Summary
Enable Java compilers to use alternate translation strategies, such as invokedynamic, in order to improve the performance of certain JDK methods designated as compiler intrinsic candidates. Specifically, intrinsify the invocation of String::format and Objects::hash.
Problem
In most cases, the JVM does an excellent job of optimizing bytecode at run time. However, for certain kinds of methods, the Java compiler's standard translation strategy results in bytecode which is hard to optimize. A prime example is String::format
, whose signature is:
public static String format(String formatString, Object... args) { ... }
The bytecode that javac
generates for an invocation of String::format
is hard to optimize, despite the best efforts of the JVM's JIT compiler. It is common to have primitive arguments; they must be boxed. A varargs array must be created and initialized with all the arguments. The format string will almost always be a constant string, but it is parsed every time by the implementation of String::format
. That implementation is, unsurprisingly, too large to inline. As a result, the bytecode is much slower than we'd like.
Methods such as String::format
and Objects::hash
(which has a similar signature) are critically important, since they are concise and reliable ways to implement toString
and hashCode
. Some developers shy away from using these methods and instead use more verbose and error-prone mechanisms purely out of performance considerations. By optimizing the invocation of String::format
and Objects::hash
, the most readable and maintainable way to implement toString
and hashCode
also becomes the most performant way.
JEP 280 replaced the translation of string concatenation with invokedynamic
, resulting in faster bytecode, less allocation churn, and more uniform optimizability. We can apply the same technique to methods such as String::format
by compiling invocations of these methods using an alternate translation strategy customizes the bytecode for each specific invocation based information available at compile time, such as the static types and values of the arguments present in the invocation.
Solution
There are two separate aspects to enabling compile-time intrinsification:
- Authorizing a Java compiler to select an alternate translation strategy for a given method invocation (that is, a strategy other than JLS 15.12.3); and
- Implementation in a specific Java compiler to enable specific alternate translation strategies for specific intrinsification candidates.
The first can be accomplished by creating a Java SE annotation @IntrinsicCandidate
which JDK library authors can use to tag suitable methods as candidates for intrinsification. A compiler is thereby authorized to select an alternate, but behavior preserving, translation for invocations of those methods. This specifies only that a compiler may do so, not how a compiler does it. JLS 13.1 would be updated to be aware of this opt-in.
We propose to accomplish the second in the JDK's javac
implementation by creating a mechanism for declaring and registering intrinsic processors. They will be invoked when the compiler encounters an invocation of an intrinsic candidate, and will instruct the compiler as to whether and how to replace the standard translation with an optimized translation. Such intrinsification is entirely optional; a compiler may choose to not intrinsify at all, or may choose to provide command-line options for enabling or disabling intrinsification.
We intend to intrinsify String::format
(and related methods, such as PrintStream::printf
) to avoid the boxing overhead, varargs overhead, and repeated analysis of constant format strings. Consider the following invocation of String::format
:
String name = ...
int age = ...
String s = String.format("%s: %d", name, age);
This results in boxing age
to an Integer
, allocating a varargs array, storing name
and the boxed age
into the varargs array, and then parsing and interpreting the format string -- on every invocation. When the format string is constant, which it almost always is, the compile-time analysis can select an alternate translation, as follows:
String s = name + ": " + Integer.toString(age);
which can be further optimized to an invokedynamic
using the mechanics from JEP 280. Note that neither name
nor age
need to be constant variables in order to select the alternate translation.
Similarly, invoking Objects::hash
has two of the three problems that String::format
has: boxing and varargs. The invocation:
int hashCode() { return Objects.hash(name, age); }
will similarly box age
and then box name
and age
into a varargs array (which many varargs methods will defensively copy). However, we could instead translate it as follows:
int hashCode() { return name.hashCode() + 31 * Integer.hashCode(age); }
which avoids these unnecessary costs.
Specification
diff -r 7c17199fa37d src/java.base/share/classes/java/io/PrintStream.java
--- a/src/java.base/share/classes/java/io/PrintStream.java Fri Feb 15 08:21:08 2019 -0500
+++ b/src/java.base/share/classes/java/io/PrintStream.java Wed Feb 20 21:59:44 2019 -0500
@@ -1000,6 +1001,19 @@
* out.format(format, args)
* }</pre>
*
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
* @param format
* A format string as described in <a
* href="../util/Formatter.html#syntax">Format string syntax</a>
@@ -1031,6 +1045,7 @@
*
* @since 1.5
*/
+ @IntrinsicCandidate
public PrintStream printf(String format, Object ... args) {
return format(format, args);
}
@@ -1047,6 +1062,19 @@
* out.format(l, format, args)
* }</pre>
*
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
* @param l
* The {@linkplain java.util.Locale locale} to apply during
* formatting. If {@code l} is {@code null} then no localization
@@ -1083,6 +1111,7 @@
*
* @since 1.5
*/
+ @IntrinsicCandidate
public PrintStream printf(Locale l, String format, Object ... args) {
return format(l, format, args);
}
@@ -1097,6 +1126,19 @@
* regardless of any previous invocations of other formatting methods on
* this object.
*
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
* @param format
* A format string as described in <a
* href="../util/Formatter.html#syntax">Format string syntax</a>
@@ -1128,6 +1170,7 @@
*
* @since 1.5
*/
+ @IntrinsicCandidate
public PrintStream format(String format, Object ... args) {
try {
synchronized (this) {
@@ -1151,6 +1194,19 @@
* Writes a formatted string to this output stream using the specified
* format string and arguments.
*
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
* @param l
* The {@linkplain java.util.Locale locale} to apply during
* formatting. If {@code l} is {@code null} then no localization
@@ -1187,6 +1243,7 @@
*
* @since 1.5
*/
+ @IntrinsicCandidate
public PrintStream format(Locale l, String format, Object ... args) {
try {
synchronized (this) {
diff -r 7c17199fa37d src/java.base/share/classes/java/io/PrintWriter.java
--- a/src/java.base/share/classes/java/io/PrintWriter.java Fri Feb 15 08:21:08 2019 -0500
+++ b/src/java.base/share/classes/java/io/PrintWriter.java Wed Feb 20 21:59:44 2019 -0500
@@ -854,6 +855,19 @@
* out.format(format, args)
* }</pre>
*
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
* @param format
* A format string as described in <a
* href="../util/Formatter.html#syntax">Format string syntax</a>.
@@ -885,6 +899,7 @@
*
* @since 1.5
*/
+ @IntrinsicCandidate
public PrintWriter printf(String format, Object ... args) {
return format(format, args);
}
@@ -902,6 +917,152 @@
* out.format(l, format, args)
* }</pre>
*
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
+ * @param l
+ * The {@linkplain java.util.Locale locale} to apply during
+ * formatting. If {@code l} is {@code null} then no localization
+ * is applied.
+ *
+ * @param format
+ * A format string as described in <a
+ * href="../util/Formatter.html#syntax">Format string syntax</a>.
+ *
+ * @param args
+ * Arguments referenced by the format specifiers in the format
+ * string. If there are more arguments than format specifiers, the
+ * extra arguments are ignored. The number of arguments is
+ * variable and may be zero. The maximum number of arguments is
+ * limited by the maximum dimension of a Java array as defined by
+ * <cite>The Java™ Virtual Machine Specification</cite>.
+ * The behaviour on a
+ * {@code null} argument depends on the <a
+ * href="../util/Formatter.html#syntax">conversion</a>.
+ *
+ *
+ * @throws java.util.IllegalFormatException
+ * If a format string contains an illegal syntax, a format
+ * specifier that is incompatible with the given arguments,
+ * insufficient arguments given the format string, or other
+ * illegal conditions. For specification of all possible
+ * formatting errors, see the <a
+ * href="../util/Formatter.html#detail">Details</a> section of the
+ * formatter class specification.
+ *
+ * @throws NullPointerException
+ * If the {@code format} is {@code null}
+ *
+ * @return This writer
+ *
+ * @since 1.5
+ */
+ @IntrinsicCandidate
+ public PrintWriter printf(Locale l, String format, Object ... args) {
+ return format(l, format, args);
+ }
+
+ /**
+ * Writes a formatted string to this writer using the specified format
+ * string and arguments. If automatic flushing is enabled, calls to this
+ * method will flush the output buffer.
+ *
+ * <p> The locale always used is the one returned by {@link
+ * java.util.Locale#getDefault() Locale.getDefault()}, regardless of any
+ * previous invocations of other formatting methods on this object.
+ *
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
+ * @param format
+ * A format string as described in <a
+ * href="../util/Formatter.html#syntax">Format string syntax</a>.
+ *
+ * @param args
+ * Arguments referenced by the format specifiers in the format
+ * string. If there are more arguments than format specifiers, the
+ * extra arguments are ignored. The number of arguments is
+ * variable and may be zero. The maximum number of arguments is
+ * limited by the maximum dimension of a Java array as defined by
+ * <cite>The Java™ Virtual Machine Specification</cite>.
+ * The behaviour on a
+ * {@code null} argument depends on the <a
+ * href="../util/Formatter.html#syntax">conversion</a>.
+ *
+ * @throws java.util.IllegalFormatException
+ * If a format string contains an illegal syntax, a format
+ * specifier that is incompatible with the given arguments,
+ * insufficient arguments given the format string, or other
+ * illegal conditions. For specification of all possible
+ * formatting errors, see the <a
+ * href="../util/Formatter.html#detail">Details</a> section of the
+ * Formatter class specification.
+ *
+ * @throws NullPointerException
+ * If the {@code format} is {@code null}
+ *
+ * @return This writer
+ *
+ * @since 1.5
+ */
+ @IntrinsicCandidate
+ public PrintWriter format(String format, Object ... args) {
+ try {
+ synchronized (lock) {
+ ensureOpen();
+ if ((formatter == null)
+ || (formatter.locale() != Locale.getDefault()))
+ formatter = new Formatter(this);
+ formatter.format(Locale.getDefault(), format, args);
+ if (autoFlush)
+ out.flush();
+ }
+ } catch (InterruptedIOException x) {
+ Thread.currentThread().interrupt();
+ } catch (IOException x) {
+ trouble = true;
+ }
+ return this;
+ }
+
+ /**
+ * Writes a formatted string to this writer using the specified format
+ * string and arguments. If automatic flushing is enabled, calls to this
+ * method will flush the output buffer.
+ *
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
* @param l
* The {@linkplain java.util.Locale locale} to apply during
* formatting. If {@code l} is {@code null} then no localization
@@ -938,110 +1099,7 @@
*
* @since 1.5
*/
- public PrintWriter printf(Locale l, String format, Object ... args) {
- return format(l, format, args);
- }
-
- /**
- * Writes a formatted string to this writer using the specified format
- * string and arguments. If automatic flushing is enabled, calls to this
- * method will flush the output buffer.
- *
- * <p> The locale always used is the one returned by {@link
- * java.util.Locale#getDefault() Locale.getDefault()}, regardless of any
- * previous invocations of other formatting methods on this object.
- *
- * @param format
- * A format string as described in <a
- * href="../util/Formatter.html#syntax">Format string syntax</a>.
- *
- * @param args
- * Arguments referenced by the format specifiers in the format
- * string. If there are more arguments than format specifiers, the
- * extra arguments are ignored. The number of arguments is
- * variable and may be zero. The maximum number of arguments is
- * limited by the maximum dimension of a Java array as defined by
- * <cite>The Java™ Virtual Machine Specification</cite>.
- * The behaviour on a
- * {@code null} argument depends on the <a
- * href="../util/Formatter.html#syntax">conversion</a>.
- *
- * @throws java.util.IllegalFormatException
- * If a format string contains an illegal syntax, a format
- * specifier that is incompatible with the given arguments,
- * insufficient arguments given the format string, or other
- * illegal conditions. For specification of all possible
- * formatting errors, see the <a
- * href="../util/Formatter.html#detail">Details</a> section of the
- * Formatter class specification.
- *
- * @throws NullPointerException
- * If the {@code format} is {@code null}
- *
- * @return This writer
- *
- * @since 1.5
- */
- public PrintWriter format(String format, Object ... args) {
- try {
- synchronized (lock) {
- ensureOpen();
- if ((formatter == null)
- || (formatter.locale() != Locale.getDefault()))
- formatter = new Formatter(this);
- formatter.format(Locale.getDefault(), format, args);
- if (autoFlush)
- out.flush();
- }
- } catch (InterruptedIOException x) {
- Thread.currentThread().interrupt();
- } catch (IOException x) {
- trouble = true;
- }
- return this;
- }
-
- /**
- * Writes a formatted string to this writer using the specified format
- * string and arguments. If automatic flushing is enabled, calls to this
- * method will flush the output buffer.
- *
- * @param l
- * The {@linkplain java.util.Locale locale} to apply during
- * formatting. If {@code l} is {@code null} then no localization
- * is applied.
- *
- * @param format
- * A format string as described in <a
- * href="../util/Formatter.html#syntax">Format string syntax</a>.
- *
- * @param args
- * Arguments referenced by the format specifiers in the format
- * string. If there are more arguments than format specifiers, the
- * extra arguments are ignored. The number of arguments is
- * variable and may be zero. The maximum number of arguments is
- * limited by the maximum dimension of a Java array as defined by
- * <cite>The Java™ Virtual Machine Specification</cite>.
- * The behaviour on a
- * {@code null} argument depends on the <a
- * href="../util/Formatter.html#syntax">conversion</a>.
- *
- * @throws java.util.IllegalFormatException
- * If a format string contains an illegal syntax, a format
- * specifier that is incompatible with the given arguments,
- * insufficient arguments given the format string, or other
- * illegal conditions. For specification of all possible
- * formatting errors, see the <a
- * href="../util/Formatter.html#detail">Details</a> section of the
- * formatter class specification.
- *
- * @throws NullPointerException
- * If the {@code format} is {@code null}
- *
- * @return This writer
- *
- * @since 1.5
- */
+ @IntrinsicCandidate
public PrintWriter format(Locale l, String format, Object ... args) {
try {
synchronized (lock) {
diff -r 7c17199fa37d src/java.base/share/classes/java/lang/String.java
--- a/src/java.base/share/classes/java/lang/String.java Fri Feb 15 08:21:08 2019 -0500
+++ b/src/java.base/share/classes/java/lang/String.java Wed Feb 20 21:59:44 2019 -0500
@@ -2956,6 +2957,19 @@
* Locale.getDefault(Locale.Category)} with
* {@link java.util.Locale.Category#FORMAT FORMAT} category specified.
*
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
* @param format
* A <a href="../util/Formatter.html#syntax">format string</a>
*
@@ -2984,6 +2998,7 @@
* @see java.util.Formatter
* @since 1.5
*/
+ @IntrinsicCandidate
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
@@ -2992,6 +3007,19 @@
* Returns a formatted string using the specified locale, format string,
* and arguments.
*
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
* @param l
* The {@linkplain java.util.Locale locale} to apply during
* formatting. If {@code l} is {@code null} then no localization
@@ -3025,11 +3053,120 @@
* @see java.util.Formatter
* @since 1.5
*/
+ @IntrinsicCandidate
public static String format(Locale l, String format, Object... args) {
return new Formatter(l).format(format, args).toString();
}
/**
+ * Returns a formatted string using this string, as the specified
+ * <a href="../util/Formatter.html#syntax">format</a>, and
+ * arguments.
+ *
+ * <p> The locale always used is the one returned by {@link
+ * java.util.Locale#getDefault(java.util.Locale.Category)
+ * Locale.getDefault(Locale.Category)} with
+ * {@link java.util.Locale.Category#FORMAT FORMAT} category specified.
+ *
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
+ * @param args
+ * Arguments referenced by the format specifiers in the format
+ * string. If there are more arguments than format specifiers, the
+ * extra arguments are ignored. The number of arguments is
+ * variable and may be zero. The maximum number of arguments is
+ * limited by the maximum dimension of a Java array as defined by
+ * <cite>The Java™ Virtual Machine Specification</cite>.
+ * The behaviour on a
+ * {@code null} argument depends on the <a
+ * href="../util/Formatter.html#syntax">conversion</a>.
+ *
+ * @throws java.util.IllegalFormatException
+ * If a format string contains an illegal syntax, a format
+ * specifier that is incompatible with the given arguments,
+ * insufficient arguments given the format string, or other
+ * illegal conditions. For specification of all possible
+ * formatting errors, see the <a
+ * href="../util/Formatter.html#detail">Details</a> section of the
+ * formatter class specification.
+ *
+ * @return A formatted string
+ *
+ * @see java.util.Formatter
+ *
+ * @since 12
+ */
+ @IntrinsicCandidate
+ public String format(Object... args) {
+ return new Formatter().format(this, args).toString();
+ }
+
+ /**
+ * Returns a formatted string using this string, as the specified
+ * <a href="../util/Formatter.html#syntax">format</a>, the specified locale,
+ * and arguments.
+ *
+ * @implNote
+ * An invocation of this method may be intrinsified {@link java.lang.compiler.IntrinsicCandidate}
+ * if the {@code format} string is a constant expression. Intrinsification replaces the method
+ * invocation with a string concatenation operation. No checks are made during intrinsification
+ * on the content of the {@code format} string, so an IllegalFormatException is possible at
+ * run time.
+ * <p>
+ * The treatment of subsequent arguments is the same as without intrinsification: arguments that
+ * are constant expressions are evaluated at compile time, and arguments that are not constant
+ * expressions are evaluated at run time. (An argument that is a nested invocation of an
+ * intrinsifiable method may be evaluated at compile time and/or run time, per the compiler's
+ * usual discretion on when and how to intrinsify.)
+ *
+ * @param l
+ * The {@linkplain java.util.Locale locale} to apply during
+ * formatting. If {@code l} is {@code null} then no localization
+ * is applied.
+ *
+ * @param args
+ * Arguments referenced by the format specifiers in the format
+ * string. If there are more arguments than format specifiers, the
+ * extra arguments are ignored. The number of arguments is
+ * variable and may be zero. The maximum number of arguments is
+ * limited by the maximum dimension of a Java array as defined by
+ * <cite>The Java™ Virtual Machine Specification</cite>.
+ * The behaviour on a
+ * {@code null} argument depends on the
+ * <a href="../util/Formatter.html#syntax">conversion</a>.
+ *
+ * @throws java.util.IllegalFormatException
+ * If a format string contains an illegal syntax, a format
+ * specifier that is incompatible with the given arguments,
+ * insufficient arguments given the format string, or other
+ * illegal conditions. For specification of all possible
+ * formatting errors, see the <a
+ * href="../util/Formatter.html#detail">Details</a> section of the
+ * formatter class specification
+ *
+ * @return A formatted string
+ *
+ * @see java.util.Formatter
+ *
+ * @since 12
+ */
+ @IntrinsicCandidate
+ public String format(Locale l, Object... args) {
+ return new Formatter(l).format(this, args).toString();
+ }
+
+ /**
* Returns the string representation of the {@code Object} argument.
*
* @param obj an {@code Object}.
diff -r 7c17199fa37d src/java.base/share/classes/java/lang/compiler/IntrinsicCandidate.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/compiler/IntrinsicCandidate.java Wed Feb 20 21:59:44 2019 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.lang.compiler;
+
+import java.lang.annotation.*;
+
+/**
+ * Indicates an <i>intrinsic candidate</i>: a method whose invocation may be <i>intrinsified</i> by the compiler in a
+ * behaviorally-compatible way. Intrinsification generally involves the compiler recognizing when constant arguments
+ * are passed to a passed to a variable-arity method, where the overhead of boxing is significant. In some cases,
+ * the compiler can fold the entire invocation into a constant at compile time. In other cases, the compiler can generate
+ * bytecode that invokes a specialized method (based on the constant arguments or based on the types of all arguments)
+ * at run time.
+ * <p>
+ * The compiler is free to optimize a given invocation in source code differently in each
+ * compilation, and to optimize adjacent invocations of the same intrinsic candidate in source code in different ways.
+ * <p>
+ * This annotation may only be applied to {@code public} methods in classes of the {@code java.base} module.
+ * Applying it to other methods is not an error, but has no effect.
+ *
+ * @apiNote
+ * This type's retention policy ensures that annotations of this type are not available through the reflection API.
+ * This prevents clients from identifying intrinsic candidates and thus prevents assumptions about the treatment of
+ * intrinsic candidates by a Java compiler. Being an intrinsic candidate is never part of a method's specification.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ ElementType.METHOD })
+public @interface IntrinsicCandidate {
+}
diff -r 7c17199fa37d src/java.base/share/classes/java/lang/invoke/IntrinsicFactory.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/IntrinsicFactory.java Wed Feb 20 21:59:44 2019 -0500
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Formatter;
+import java.util.List;
+import java.util.Locale;
+import java.util.MissingFormatArgumentException;
+import java.util.stream.IntStream;
+
+/**
+ * Bootstrapping support for Intrinsics.
+ */
+public final class IntrinsicFactory {
+ /**
+ * formatterFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite formatterFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, false);
+ }
+
+ /**
+ * formatterLocaleFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite formatterLocaleFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, true);
+ }
+
+ /**
+ * printStreamFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite printStreamFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, false);
+ }
+
+ /**
+ * printStreamLocaleFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite printStreamLocaleFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, true);
+ }
+
+ /**
+ * printStreamPrintfBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite printStreamPrintfBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, false);
+ }
+
+ /**
+ * printStreamLocalePrintfBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite printStreamLocalePrintfBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, true);
+ }
+
+ /**
+ * printWriterFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite printWriterFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, false);
+ }
+
+ /**
+ * printWriterLocaleFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite printWriterLocaleFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, true);
+ }
+
+ /**
+ * printWriterPrintfBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite printWriterPrintfBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, false);
+ }
+
+ /**
+ * printWriterLocalePrintfBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite printWriterLocalePrintfBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, false, true);
+ }
+
+ /**
+ * staticStringFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite staticStringFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, true, false);
+ }
+
+ /**
+ * staticStringLocaleFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite staticStringLocaleFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, true, true);
+ }
+
+ /**
+ * stringFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite stringFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, true, false);
+ }
+
+ /**
+ * stringLocaleFormatBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @param format Formatter format string
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite stringLocaleFormatBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType,
+ String format)
+ throws NoSuchMethodException, IllegalAccessException, StringConcatException {
+ return FormatterBootstraps.formatterBootstrap(lookup, name, methodType, format, true, true);
+ }
+
+ /**
+ * objectsHashBootstrap bootstrap.
+ * @param lookup MethodHandles lookup
+ * @param name Name of method
+ * @param methodType Method signature
+ * @throws NoSuchMethodException no such method
+ * @throws IllegalAccessException illegal access
+ * @throws StringConcatException string concat error
+ * @return Callsite for intrinsic method
+ */
+ public static CallSite objectsHashBootstrap(MethodHandles.Lookup lookup,
+ String name,
+ MethodType methodType)
+ throws NoSuchMethodException, IllegalAccessException {
+ return ObjectsBootstraps.hashBootstrap(lookup, name, methodType);
+ }
+
+}
diff -r 7c17199fa37d src/java.base/share/classes/java/util/Formatter.java
--- a/src/java.base/share/classes/java/util/Formatter.java Fri Feb 15 08:21:08 2019 -0500
+++ b/src/java.base/share/classes/java/util/Formatter.java Wed Feb 20 21:59:44 2019 -0500
@@ -2605,6 +2615,7 @@
*
* @return This formatter
*/
+ @IntrinsicCandidate
public Formatter format(String format, Object ... args) {
return format(l, format, args);
}
@@ -2644,7 +2655,14 @@
*
* @return This formatter
*/
+ @IntrinsicCandidate
public Formatter format(Locale l, String format, Object ... args) {
+ List<FormatToken> fsa = FormatString.parse(format);
+ format(l, fsa, args);
+ return this;
+ }
+
+ void format(Locale l, List<FormatToken> fsa, Object ... args) {
ensureOpen();
// index of last argument referenced
diff -r 7c17199fa37d src/java.base/share/classes/java/util/Objects.java
--- a/src/java.base/share/classes/java/util/Objects.java Fri Feb 15 08:21:08 2019 -0500
+++ b/src/java.base/share/classes/java/util/Objects.java Wed Feb 20 21:59:44 2019 -0500
@@ -28,6 +28,7 @@
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
+import java.lang.compiler.IntrinsicCandidate;
import java.util.function.Supplier;
/**
@@ -137,11 +138,21 @@
* value does not equal the hash code of that object reference.</b> This
* value can be computed by calling {@link #hashCode(Object)}.
*
+ * @implNote
+ * An invocation of this method may be intrinsified see {@link java.lang.compiler.IntrinsicCandidate}.
+ * If all the arguments are constant expressions, then intrinsification generates a hash code directly
+ * from the arguments, and uses it as the run-time value of the method invocation. If some arguments
+ * are not constant expressions, then intrinsification replaces the method invocation with code that
+ * will generate a hash code for each argument in a type-specific way at run time, then combine the
+ * results. Intrinsification is advantageous because it avoids both the boxing of primitive arguments
+ * into objects, and the boxing of all arguments into a varargs array.
+ *
* @param values the values to be hashed
* @return a hash value of the sequence of input values
* @see Arrays#hashCode(Object[])
* @see List#hashCode
*/
+ @IntrinsicCandidate
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
- csr of
-
JDK-8205637 JEP 348: Compiler Intrinsics for Java SE APIs
- Closed