Skip to content

Commit 4d597de

Browse files
wenshaocl4es
authored andcommitted
8338930: StringConcatFactory hardCoded string concatenation strategy
Reviewed-by: redestad, liach
1 parent ad10493 commit 4d597de

File tree

3 files changed

+130
-49
lines changed

3 files changed

+130
-49
lines changed

src/java.base/share/classes/java/lang/System.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2647,6 +2647,10 @@ public byte stringInitCoder() {
26472647
return String.COMPACT_STRINGS ? String.LATIN1 : String.UTF16;
26482648
}
26492649

2650+
public byte stringCoder(String str) {
2651+
return str.coder();
2652+
}
2653+
26502654
public int getCharsLatin1(long i, int index, byte[] buf) {
26512655
return StringLatin1.getChars(i, index, buf);
26522656
}

src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java

Lines changed: 121 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,16 @@
119119
*/
120120
public final class StringConcatFactory {
121121
private static final int HIGH_ARITY_THRESHOLD;
122+
private static final int CACHE_THRESHOLD;
122123
private static final int FORCE_INLINE_THRESHOLD;
123124

124125
static {
125126
String highArity = VM.getSavedProperty("java.lang.invoke.StringConcat.highArityThreshold");
126127
HIGH_ARITY_THRESHOLD = highArity != null ? Integer.parseInt(highArity) : 0;
127128

129+
String cacheThreshold = VM.getSavedProperty("java.lang.invoke.StringConcat.cacheThreshold");
130+
CACHE_THRESHOLD = cacheThreshold != null ? Integer.parseInt(cacheThreshold) : 256;
131+
128132
String inlineThreshold = VM.getSavedProperty("java.lang.invoke.StringConcat.inlineThreshold");
129133
FORCE_INLINE_THRESHOLD = inlineThreshold != null ? Integer.parseInt(inlineThreshold) : 16;
130134
}
@@ -1179,17 +1183,21 @@ private static MethodType erasedArgs(MethodType args) {
11791183
* int arg0, long arg1, boolean arg2, char arg3, String arg5)
11801184
* </pre></blockquote>
11811185
*/
1182-
private static MethodTypeDesc prependArgs(MethodType concatArgs) {
1186+
private static MethodTypeDesc prependArgs(MethodType concatArgs, boolean staticConcat) {
11831187
int parameterCount = concatArgs.parameterCount();
1184-
var paramTypes = new ClassDesc[parameterCount + 4];
1188+
int prefixArgs = staticConcat ? 3 : 4;
1189+
var paramTypes = new ClassDesc[parameterCount + prefixArgs];
11851190
paramTypes[0] = CD_int; // length
11861191
paramTypes[1] = CD_byte; // coder
11871192
paramTypes[2] = CD_Array_byte; // buff
1188-
paramTypes[3] = CD_Array_String; // constants
1193+
1194+
if (!staticConcat) {
1195+
paramTypes[3] = CD_Array_String; // constants
1196+
}
11891197

11901198
for (int i = 0; i < parameterCount; i++) {
11911199
var cl = concatArgs.parameterType(i);
1192-
paramTypes[i + 4] = needStringOf(cl) ? CD_String : ConstantUtils.classDesc(cl);
1200+
paramTypes[i + prefixArgs] = needStringOf(cl) ? CD_String : ConstantUtils.classDesc(cl);
11931201
}
11941202
return MethodTypeDescImpl.ofValidated(CD_int, paramTypes);
11951203
}
@@ -1254,32 +1262,42 @@ private static MethodHandle generate(Lookup lookup, MethodType args, String[] co
12541262
return handle.bindTo(concat1);
12551263
}
12561264

1257-
var weakConstructorHandle = CACHE.get(concatArgs);
1258-
if (weakConstructorHandle != null) {
1259-
MethodHandlePair handlePair = weakConstructorHandle.get();
1260-
if (handlePair != null) {
1261-
try {
1262-
var instance = handlePair.constructor.invokeBasic((Object)constants);
1263-
return handlePair.concatenator.bindTo(instance);
1264-
} catch (Throwable e) {
1265-
throw new StringConcatException("Exception while utilizing the hidden class", e);
1265+
boolean forceInline = concatArgs.parameterCount() < FORCE_INLINE_THRESHOLD;
1266+
boolean staticConcat = concatArgs.parameterCount() >= CACHE_THRESHOLD;
1267+
1268+
if (!staticConcat) {
1269+
var weakConstructorHandle = CACHE.get(concatArgs);
1270+
if (weakConstructorHandle != null) {
1271+
MethodHandlePair handlePair = weakConstructorHandle.get();
1272+
if (handlePair != null) {
1273+
try {
1274+
var instance = handlePair.constructor.invokeBasic((Object)constants);
1275+
return handlePair.concatenator.bindTo(instance);
1276+
} catch (Throwable e) {
1277+
throw new StringConcatException("Exception while utilizing the hidden class", e);
1278+
}
12661279
}
12671280
}
12681281
}
1282+
12691283
MethodTypeDesc lengthArgs = lengthArgs(concatArgs),
12701284
coderArgs = coderArgsIfMaybeUTF16(concatArgs),
1271-
prependArgs = prependArgs(concatArgs);
1285+
prependArgs = prependArgs(concatArgs, staticConcat);
12721286

12731287
byte[] classBytes = ClassFile.of().build(CD_CONCAT,
12741288
new Consumer<ClassBuilder>() {
1275-
final boolean forceInline = concatArgs.parameterCount() < FORCE_INLINE_THRESHOLD;
1276-
12771289
@Override
12781290
public void accept(ClassBuilder clb) {
1279-
clb.withSuperclass(CD_StringConcatBase)
1280-
.withFlags(ACC_FINAL | ACC_SUPER | ACC_SYNTHETIC)
1281-
.withMethodBody(INIT_NAME, MTD_INIT, 0, CONSTRUCTOR_BUILDER)
1282-
.withMethod("length",
1291+
if (staticConcat) {
1292+
clb.withSuperclass(CD_Object)
1293+
.withFlags(ACC_ABSTRACT | ACC_SUPER | ACC_SYNTHETIC);
1294+
} else {
1295+
clb.withSuperclass(CD_StringConcatBase)
1296+
.withFlags(ACC_FINAL | ACC_SUPER | ACC_SYNTHETIC)
1297+
.withMethodBody(INIT_NAME, MTD_INIT, 0, CONSTRUCTOR_BUILDER);
1298+
}
1299+
1300+
clb.withMethod("length",
12831301
lengthArgs,
12841302
ACC_STATIC | ACC_PRIVATE,
12851303
new Consumer<MethodBuilder>() {
@@ -1298,18 +1316,20 @@ public void accept(MethodBuilder mb) {
12981316
if (forceInline) {
12991317
mb.with(FORCE_INLINE);
13001318
}
1301-
mb.withCode(generatePrependMethod(prependArgs));
1319+
mb.withCode(generatePrependMethod(prependArgs, staticConcat, constants));
13021320
}
13031321
})
13041322
.withMethod(METHOD_NAME,
13051323
ConstantUtils.methodTypeDesc(concatArgs),
1306-
ACC_FINAL,
1324+
staticConcat ? ACC_STATIC | ACC_FINAL : ACC_FINAL,
13071325
new Consumer<MethodBuilder>() {
13081326
public void accept(MethodBuilder mb) {
13091327
if (forceInline) {
13101328
mb.with(FORCE_INLINE);
13111329
}
13121330
mb.withCode(generateConcatMethod(
1331+
staticConcat,
1332+
constants,
13131333
CD_CONCAT,
13141334
concatArgs,
13151335
lengthArgs,
@@ -1335,6 +1355,11 @@ public void accept(MethodBuilder mb) {
13351355
try {
13361356
var hiddenClass = lookup.makeHiddenClassDefiner(CLASS_NAME, classBytes, Set.of(), DUMPER)
13371357
.defineClass(true, null);
1358+
1359+
if (staticConcat) {
1360+
return lookup.findStatic(hiddenClass, METHOD_NAME, concatArgs);
1361+
}
1362+
13381363
var constructor = lookup.findConstructor(hiddenClass, CONSTRUCTOR_METHOD_TYPE);
13391364
var concatenator = lookup.findVirtual(hiddenClass, METHOD_NAME, concatArgs);
13401365
CACHE.put(concatArgs, new SoftReference<>(new MethodHandlePair(constructor, concatenator)));
@@ -1410,6 +1435,8 @@ public void accept(MethodBuilder mb) {
14101435
* </pre></blockquote>
14111436
*/
14121437
private static Consumer<CodeBuilder> generateConcatMethod(
1438+
boolean staticConcat,
1439+
String[] constants,
14131440
ClassDesc concatClass,
14141441
MethodType concatArgs,
14151442
MethodTypeDesc lengthArgs,
@@ -1421,7 +1448,7 @@ private static Consumer<CodeBuilder> generateConcatMethod(
14211448
public void accept(CodeBuilder cb) {
14221449
// Compute parameter variable slots
14231450
int paramCount = concatArgs.parameterCount(),
1424-
thisSlot = cb.receiverSlot(),
1451+
thisSlot = staticConcat ? 0 : cb.receiverSlot(),
14251452
lengthSlot = cb.allocateLocal(TypeKind.INT),
14261453
coderSlot = cb.allocateLocal(TypeKind.BYTE),
14271454
bufSlot = cb.allocateLocal(TypeKind.REFERENCE),
@@ -1457,11 +1484,30 @@ public void accept(CodeBuilder cb) {
14571484
}
14581485
}
14591486

1487+
int coder = JLA.stringInitCoder(),
1488+
length = 0;
1489+
if (staticConcat) {
1490+
for (var constant : constants) {
1491+
coder |= JLA.stringCoder(constant);
1492+
length += constant.length();
1493+
}
1494+
}
1495+
14601496
/*
14611497
* coder = coder(this.coder, arg0, arg1, ... argN);
14621498
*/
1463-
cb.aload(thisSlot)
1464-
.getfield(concatClass, "coder", CD_byte);
1499+
if (staticConcat) {
1500+
// coder can only be 0 or 1
1501+
if (coder == 0) {
1502+
cb.iconst_0();
1503+
} else {
1504+
cb.iconst_1();
1505+
}
1506+
} else {
1507+
cb.aload(thisSlot)
1508+
.getfield(concatClass, "coder", CD_byte);
1509+
}
1510+
14651511
if (coderArgs != null) {
14661512
for (int i = 0; i < paramCount; i++) {
14671513
var cl = concatArgs.parameterType(i);
@@ -1480,8 +1526,13 @@ public void accept(CodeBuilder cb) {
14801526
/*
14811527
* length = length(this.length, arg0, arg1, ..., argN);
14821528
*/
1483-
cb.aload(thisSlot)
1484-
.getfield(concatClass, "length", CD_int);
1529+
if (staticConcat) {
1530+
cb.ldc(length);
1531+
} else {
1532+
cb.aload(thisSlot)
1533+
.getfield(concatClass, "length", CD_int);
1534+
}
1535+
14851536
for (int i = 0; i < paramCount; i++) {
14861537
var cl = concatArgs.parameterType(i);
14871538
int paramSlot = cb.parameterSlot(i);
@@ -1498,25 +1549,35 @@ public void accept(CodeBuilder cb) {
14981549
* suffix = constants[paramCount];
14991550
* length -= suffix.length();
15001551
*/
1501-
cb.aload(thisSlot)
1502-
.getfield(concatClass, "constants", CD_Array_String)
1503-
.dup()
1504-
.astore(constantsSlot)
1505-
.ldc(paramCount)
1506-
.aaload()
1507-
.dup()
1508-
.astore(suffixSlot)
1509-
.invokevirtual(CD_String, "length", MTD_int)
1510-
.isub()
1511-
.istore(lengthSlot);
1552+
if (staticConcat) {
1553+
cb.ldc(constants[paramCount].length())
1554+
.isub()
1555+
.istore(lengthSlot);
1556+
} else {
1557+
cb.aload(thisSlot)
1558+
.getfield(concatClass, "constants", CD_Array_String)
1559+
.dup()
1560+
.astore(constantsSlot)
1561+
.ldc(paramCount)
1562+
.aaload()
1563+
.dup()
1564+
.astore(suffixSlot)
1565+
.invokevirtual(CD_String, "length", MTD_int)
1566+
.isub()
1567+
.istore(lengthSlot);
1568+
}
15121569

15131570
/*
15141571
* Allocate buffer :
15151572
*
15161573
* buf = newArrayWithSuffix(suffix, length, coder)
15171574
*/
1518-
cb.aload(suffixSlot)
1519-
.iload(lengthSlot)
1575+
if (staticConcat) {
1576+
cb.ldc(constants[paramCount]);
1577+
} else {
1578+
cb.aload(suffixSlot);
1579+
}
1580+
cb.iload(lengthSlot)
15201581
.iload(coderSlot)
15211582
.invokestatic(CD_StringConcatHelper, "newArrayWithSuffix", MTD_NEW_ARRAY_SUFFIX)
15221583
.astore(bufSlot);
@@ -1526,8 +1587,10 @@ public void accept(CodeBuilder cb) {
15261587
*/
15271588
cb.iload(lengthSlot)
15281589
.iload(coderSlot)
1529-
.aload(bufSlot)
1530-
.aload(constantsSlot);
1590+
.aload(bufSlot);
1591+
if (!staticConcat) {
1592+
cb.aload(constantsSlot);
1593+
}
15311594
for (int i = 0; i < paramCount; i++) {
15321595
var cl = concatArgs.parameterType(i);
15331596
int paramSlot = cb.parameterSlot(i);
@@ -1651,7 +1714,10 @@ public void accept(CodeBuilder cb) {
16511714
* }
16521715
* </pre></blockquote>
16531716
*/
1654-
private static Consumer<CodeBuilder> generatePrependMethod(MethodTypeDesc prependArgs) {
1717+
private static Consumer<CodeBuilder> generatePrependMethod(
1718+
MethodTypeDesc prependArgs,
1719+
boolean staticConcat, String[] constants
1720+
) {
16551721
return new Consumer<CodeBuilder>() {
16561722
@Override
16571723
public void accept(CodeBuilder cb) {
@@ -1670,7 +1736,7 @@ public void accept(CodeBuilder cb) {
16701736
* buf, arg1, constant[1]), buf, arg0, constant[0]);
16711737
*/
16721738
cb.iload(lengthSlot);
1673-
for (int i = prependArgs.parameterCount() - 1; i >= 4; i--) {
1739+
for (int i = prependArgs.parameterCount() - 1, end = staticConcat ? 3 : 4; i >= end; i--) {
16741740
var cl = prependArgs.parameterType(i);
16751741
var kind = TypeKind.from(cl);
16761742

@@ -1691,11 +1757,17 @@ public void accept(CodeBuilder cb) {
16911757

16921758
cb.iload(coderSlot)
16931759
.aload(bufSlot)
1694-
.loadLocal(kind, cb.parameterSlot(i))
1695-
.aload(constantsSlot)
1696-
.ldc(i - 4)
1697-
.aaload()
1698-
.invokestatic(CD_StringConcatHelper, "prepend", methodTypeDesc);
1760+
.loadLocal(kind, cb.parameterSlot(i));
1761+
1762+
if (staticConcat) {
1763+
cb.ldc(constants[i - 3]);
1764+
} else {
1765+
cb.aload(constantsSlot)
1766+
.ldc(i - 4)
1767+
.aaload();
1768+
}
1769+
1770+
cb.invokestatic(CD_StringConcatHelper, "prepend", methodTypeDesc);
16991771
}
17001772
cb.ireturn();
17011773
}

src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,11 @@ public interface JavaLangAccess {
465465
*/
466466
byte stringInitCoder();
467467

468+
/**
469+
* Get the Coder of String, which is used by StringConcatFactory to calculate the initCoder of constants
470+
*/
471+
byte stringCoder(String str);
472+
468473
/**
469474
* Join strings
470475
*/

0 commit comments

Comments
 (0)