mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8247681: Improve bootstrapping of unary concatenations
Reviewed-by: jlaskey, psandoz
This commit is contained in:
parent
1d87958ead
commit
34c79640e7
6 changed files with 149 additions and 19 deletions
|
@ -426,6 +426,22 @@ final class StringConcatHelper {
|
|||
return newString(buf, indexCoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a String from a concatenation of single argument, which we
|
||||
* end up using for trivial concatenations like {@code "" + arg}.
|
||||
*
|
||||
* This will always create a new Object to comply with JLS 15.18.1:
|
||||
* "The String object is newly created unless the expression is a
|
||||
* compile-time constant expression".
|
||||
*
|
||||
* @param arg the only argument
|
||||
* @return String resulting string
|
||||
*/
|
||||
@ForceInline
|
||||
static String newStringOf(Object arg) {
|
||||
return new String(stringOf(arg));
|
||||
}
|
||||
|
||||
/**
|
||||
* We need some additional conversion for Objects in general, because
|
||||
* {@code String.valueOf(Object)} may return null. String conversion rules
|
||||
|
|
|
@ -447,9 +447,18 @@ public final class StringConcatFactory {
|
|||
*/
|
||||
private static MethodHandle generateMHInlineCopy(MethodType mt, List<String> elements) {
|
||||
|
||||
// Fast-path two-argument Object + Object concatenations
|
||||
// Fast-path unary concatenations
|
||||
if (elements.size() == 1) {
|
||||
String s0 = elements.get(0);
|
||||
if (s0 == null) {
|
||||
return unaryConcat(mt.parameterType(0));
|
||||
} else {
|
||||
return MethodHandles.insertArguments(unaryConcat(Object.class), 0, s0);
|
||||
}
|
||||
}
|
||||
// Fast-path binary concatenations
|
||||
if (elements.size() == 2) {
|
||||
// Two object arguments
|
||||
// Two arguments
|
||||
String s0 = elements.get(0);
|
||||
String s1 = elements.get(1);
|
||||
|
||||
|
@ -459,20 +468,22 @@ public final class StringConcatFactory {
|
|||
s0 == null &&
|
||||
s1 == null) {
|
||||
return simpleConcat();
|
||||
} else if (mt.parameterCount() == 1 &&
|
||||
!mt.parameterType(0).isPrimitive()) {
|
||||
|
||||
// One Object argument, one constant
|
||||
MethodHandle mh = simpleConcat();
|
||||
|
||||
if (s0 != null && s1 == null) {
|
||||
// First recipe element is a constant
|
||||
return MethodHandles.insertArguments(mh, 0, s0);
|
||||
|
||||
} else if (s1 != null && s0 == null) {
|
||||
// Second recipe element is a constant
|
||||
return MethodHandles.insertArguments(mh, 1, s1);
|
||||
|
||||
} else if (mt.parameterCount() == 1) {
|
||||
// One argument, one constant
|
||||
String constant;
|
||||
int constIdx;
|
||||
if (s1 == null) {
|
||||
constant = s0;
|
||||
constIdx = 0;
|
||||
} else {
|
||||
constant = s1;
|
||||
constIdx = 1;
|
||||
}
|
||||
if (constant.isEmpty()) {
|
||||
return unaryConcat(mt.parameterType(0));
|
||||
} else if (!mt.parameterType(0).isPrimitive()) {
|
||||
// Non-primitive argument
|
||||
return MethodHandles.insertArguments(simpleConcat(), constIdx, constant);
|
||||
}
|
||||
}
|
||||
// else... fall-through to slow-path
|
||||
|
@ -732,6 +743,76 @@ public final class StringConcatFactory {
|
|||
return mh;
|
||||
}
|
||||
|
||||
private @Stable static MethodHandle INT_STRINGIFIER;
|
||||
private static MethodHandle intStringifier() {
|
||||
MethodHandle mh = INT_STRINGIFIER;
|
||||
if (mh == null) {
|
||||
INT_STRINGIFIER = mh =
|
||||
lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
|
||||
private @Stable static MethodHandle LONG_STRINGIFIER;
|
||||
private static MethodHandle longStringifier() {
|
||||
MethodHandle mh = LONG_STRINGIFIER;
|
||||
if (mh == null) {
|
||||
LONG_STRINGIFIER = mh =
|
||||
lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, long.class);
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
|
||||
private @Stable static MethodHandle CHAR_STRINGIFIER;
|
||||
private static MethodHandle charStringifier() {
|
||||
MethodHandle mh = CHAR_STRINGIFIER;
|
||||
if (mh == null) {
|
||||
CHAR_STRINGIFIER = mh =
|
||||
lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, char.class);
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
|
||||
private @Stable static MethodHandle BOOLEAN_STRINGIFIER;
|
||||
private static MethodHandle booleanStringifier() {
|
||||
MethodHandle mh = BOOLEAN_STRINGIFIER;
|
||||
if (mh == null) {
|
||||
BOOLEAN_STRINGIFIER = mh =
|
||||
lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
|
||||
private @Stable static MethodHandle NEW_STRINGIFIER;
|
||||
private static MethodHandle newStringifier() {
|
||||
MethodHandle mh = NEW_STRINGIFIER;
|
||||
if (mh == null) {
|
||||
NEW_STRINGIFIER = mh = JLA.stringConcatHelper("newStringOf",
|
||||
methodType(String.class, Object.class));
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static MethodHandle unaryConcat(Class<?> cl) {
|
||||
if (!cl.isPrimitive()) {
|
||||
return newStringifier();
|
||||
} else if (cl == int.class || cl == short.class || cl == byte.class) {
|
||||
return intStringifier();
|
||||
} else if (cl == long.class) {
|
||||
return longStringifier();
|
||||
} else if (cl == char.class) {
|
||||
return charStringifier();
|
||||
} else if (cl == boolean.class) {
|
||||
return booleanStringifier();
|
||||
} else if (cl == float.class) {
|
||||
return floatStringifier();
|
||||
} else if (cl == double.class) {
|
||||
return doubleStringifier();
|
||||
} else {
|
||||
throw new InternalError("Unhandled type for unary concatenation: " + cl);
|
||||
}
|
||||
}
|
||||
|
||||
private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
|
||||
private static final ConcurrentMap<Class<?>, MethodHandle> NULL_PREPENDERS;
|
||||
private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
/**
|
||||
* @test
|
||||
* @summary Test implicit String concatenations, multiple shapes.
|
||||
* @bug 8148483 8245959
|
||||
* @bug 8148483 8245959 8247681
|
||||
*
|
||||
* @compile ImplicitStringConcatShapes.java
|
||||
* @run main/othervm -Xverify:all ImplicitStringConcatShapes
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
/**
|
||||
* @test
|
||||
* @summary Test implicit String concatenations, multiple shapes.
|
||||
* @bug 8148483 8245959
|
||||
* @bug 8148483 8245959 8247681
|
||||
*
|
||||
* @compile ImplicitStringConcatShapes.java
|
||||
* @run main/othervm -Xverify:all ImplicitStringConcatShapes
|
||||
|
|
|
@ -28,7 +28,7 @@ import java.util.concurrent.Callable;
|
|||
/**
|
||||
* @test
|
||||
* @summary Test input invariants for StringConcatFactory
|
||||
* @bug 8246152
|
||||
* @bug 8246152 8247681
|
||||
*
|
||||
* @compile StringConcatFactoryInvariants.java
|
||||
*
|
||||
|
@ -46,6 +46,7 @@ public class StringConcatFactoryInvariants {
|
|||
MethodType mt = MethodType.methodType(String.class, String.class, int.class);
|
||||
String recipe = "" + TAG_ARG + TAG_ARG + TAG_CONST;
|
||||
Object[][] constants = new Object[][] {
|
||||
new String[] { "" },
|
||||
new String[] { "bar" },
|
||||
new Integer[] { 1 },
|
||||
new Short[] { 2 },
|
||||
|
@ -60,6 +61,7 @@ public class StringConcatFactoryInvariants {
|
|||
// The string representation that should end up if the corresponding
|
||||
// Object[] in constants is used as an argument to makeConcatWithConstants
|
||||
String[] constantString = new String[] {
|
||||
"",
|
||||
"bar",
|
||||
"1",
|
||||
"2",
|
||||
|
@ -116,6 +118,21 @@ public class StringConcatFactoryInvariants {
|
|||
}
|
||||
}
|
||||
|
||||
// Check unary expressions with pre- and postfix constants
|
||||
{
|
||||
String constArgRecipe = "" + TAG_CONST + TAG_ARG;
|
||||
String argConstRecipe = "" + TAG_ARG + TAG_CONST;
|
||||
MethodType unaryMt = MethodType.methodType(String.class, String.class);
|
||||
|
||||
for (int i = 0; i < constants.length; i++) {
|
||||
CallSite prefixCS = StringConcatFactory.makeConcatWithConstants(lookup, methodName, unaryMt, constArgRecipe, constants[i]);
|
||||
test(constantString[i].concat("foo"), (String) prefixCS.getTarget().invokeExact("foo"));
|
||||
|
||||
CallSite postfixCS = StringConcatFactory.makeConcatWithConstants(lookup, methodName, unaryMt, argConstRecipe, constants[i]);
|
||||
test("foo".concat(constantString[i]), (String) postfixCS.getTarget().invokeExact("foo"));
|
||||
}
|
||||
}
|
||||
|
||||
// Simple factory, check for nulls:
|
||||
failNPE("Lookup is null",
|
||||
() -> StringConcatFactory.makeConcat(null, methodName, mt));
|
||||
|
|
|
@ -24,11 +24,14 @@ package org.openjdk.bench.java.lang;
|
|||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -38,6 +41,9 @@ import java.util.concurrent.TimeUnit;
|
|||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Thread)
|
||||
@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Fork(3)
|
||||
public class StringConcat {
|
||||
|
||||
@Param("4711")
|
||||
|
@ -73,6 +79,16 @@ public class StringConcat {
|
|||
return emptyString + stringValue;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String concatEmptyConstInt() {
|
||||
return "" + intValue;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String concatEmptyConstString() {
|
||||
return "" + stringValue;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String concatMethodConstString() {
|
||||
return "string".concat(stringValue);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue