Skip to content

Commit 3623abb

Browse files
committed
8263087: Add a MethodHandle combinator that switches over a set of MethodHandles
Reviewed-by: redestad
1 parent 85f6165 commit 3623abb

File tree

10 files changed

+974
-26
lines changed

10 files changed

+974
-26
lines changed

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,12 @@ void addMethod() {
864864
onStack = emitTryFinally(i);
865865
i += 2; // jump to the end of the TF idiom
866866
continue;
867+
case TABLE_SWITCH:
868+
assert lambdaForm.isTableSwitch(i);
869+
int numCases = (Integer) name.function.intrinsicData();
870+
onStack = emitTableSwitch(i, numCases);
871+
i += 2; // jump to the end of the TS idiom
872+
continue;
867873
case LOOP:
868874
assert lambdaForm.isLoop(i);
869875
onStack = emitLoop(i);
@@ -1389,6 +1395,58 @@ private static int popInsnOpcode(BasicType type) {
13891395
}
13901396
}
13911397

1398+
private Name emitTableSwitch(int pos, int numCases) {
1399+
Name args = lambdaForm.names[pos];
1400+
Name invoker = lambdaForm.names[pos + 1];
1401+
Name result = lambdaForm.names[pos + 2];
1402+
1403+
Class<?> returnType = result.function.resolvedHandle().type().returnType();
1404+
MethodType caseType = args.function.resolvedHandle().type()
1405+
.dropParameterTypes(0, 1) // drop collector
1406+
.changeReturnType(returnType);
1407+
String caseDescriptor = caseType.basicType().toMethodDescriptorString();
1408+
1409+
emitPushArgument(invoker, 2); // push cases
1410+
mv.visitFieldInsn(Opcodes.GETFIELD, "java/lang/invoke/MethodHandleImpl$CasesHolder", "cases",
1411+
"[Ljava/lang/invoke/MethodHandle;");
1412+
int casesLocal = extendLocalsMap(new Class<?>[] { MethodHandle[].class });
1413+
emitStoreInsn(L_TYPE, casesLocal);
1414+
1415+
Label endLabel = new Label();
1416+
Label defaultLabel = new Label();
1417+
Label[] caseLabels = new Label[numCases];
1418+
for (int i = 0; i < caseLabels.length; i++) {
1419+
caseLabels[i] = new Label();
1420+
}
1421+
1422+
emitPushArgument(invoker, 0); // push switch input
1423+
mv.visitTableSwitchInsn(0, numCases - 1, defaultLabel, caseLabels);
1424+
1425+
mv.visitLabel(defaultLabel);
1426+
emitPushArgument(invoker, 1); // push default handle
1427+
emitPushArguments(args, 1); // again, skip collector
1428+
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", caseDescriptor, false);
1429+
mv.visitJumpInsn(Opcodes.GOTO, endLabel);
1430+
1431+
for (int i = 0; i < numCases; i++) {
1432+
mv.visitLabel(caseLabels[i]);
1433+
// Load the particular case:
1434+
emitLoadInsn(L_TYPE, casesLocal);
1435+
emitIconstInsn(i);
1436+
mv.visitInsn(Opcodes.AALOAD);
1437+
1438+
// invoke it:
1439+
emitPushArguments(args, 1); // again, skip collector
1440+
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", caseDescriptor, false);
1441+
1442+
mv.visitJumpInsn(Opcodes.GOTO, endLabel);
1443+
}
1444+
1445+
mv.visitLabel(endLabel);
1446+
1447+
return result;
1448+
}
1449+
13921450
/**
13931451
* Emit bytecode for the loop idiom.
13941452
* <p>

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

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ enum Kind {
314314
GET_DOUBLE_VOLATILE("getDoubleVolatile"),
315315
PUT_DOUBLE_VOLATILE("putDoubleVolatile"),
316316
TRY_FINALLY("tryFinally"),
317+
TABLE_SWITCH("tableSwitch"),
317318
COLLECT("collect"),
318319
COLLECTOR("collector"),
319320
CONVERT("convert"),
@@ -707,6 +708,32 @@ boolean isTryFinally(int pos) {
707708
return isMatchingIdiom(pos, "tryFinally", 2);
708709
}
709710

711+
/**
712+
* Check if i-th name is a start of the tableSwitch idiom.
713+
*/
714+
boolean isTableSwitch(int pos) {
715+
// tableSwitch idiom:
716+
// t_{n}:L=MethodHandle.invokeBasic(...) // args
717+
// t_{n+1}:L=MethodHandleImpl.tableSwitch(*, *, *, t_{n})
718+
// t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
719+
if (pos + 2 >= names.length) return false;
720+
721+
final int POS_COLLECT_ARGS = pos;
722+
final int POS_TABLE_SWITCH = pos + 1;
723+
final int POS_UNBOX_RESULT = pos + 2;
724+
725+
Name collectArgs = names[POS_COLLECT_ARGS];
726+
Name tableSwitch = names[POS_TABLE_SWITCH];
727+
Name unboxResult = names[POS_UNBOX_RESULT];
728+
return tableSwitch.refersTo(MethodHandleImpl.class, "tableSwitch") &&
729+
collectArgs.isInvokeBasic() &&
730+
unboxResult.isInvokeBasic() &&
731+
tableSwitch.lastUseIndex(collectArgs) == 3 && // t_{n+1}:L=MethodHandleImpl.<invoker>(*, *, *, t_{n});
732+
lastUseIndex(collectArgs) == POS_TABLE_SWITCH && // t_{n} is local: used only in t_{n+1}
733+
unboxResult.lastUseIndex(tableSwitch) == 1 && // t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
734+
lastUseIndex(tableSwitch) == POS_UNBOX_RESULT; // t_{n+1} is local: used only in t_{n+2}
735+
}
736+
710737
/**
711738
* Check if i-th name is a start of the loop idiom.
712739
*/
@@ -1067,24 +1094,13 @@ static class NamedFunction {
10671094
final MemberName member;
10681095
private @Stable MethodHandle resolvedHandle;
10691096
@Stable MethodHandle invoker;
1070-
private final MethodHandleImpl.Intrinsic intrinsicName;
10711097

10721098
NamedFunction(MethodHandle resolvedHandle) {
1073-
this(resolvedHandle.internalMemberName(), resolvedHandle, MethodHandleImpl.Intrinsic.NONE);
1074-
}
1075-
NamedFunction(MethodHandle resolvedHandle, MethodHandleImpl.Intrinsic intrinsic) {
1076-
this(resolvedHandle.internalMemberName(), resolvedHandle, intrinsic);
1099+
this(resolvedHandle.internalMemberName(), resolvedHandle);
10771100
}
10781101
NamedFunction(MemberName member, MethodHandle resolvedHandle) {
1079-
this(member, resolvedHandle, MethodHandleImpl.Intrinsic.NONE);
1080-
}
1081-
NamedFunction(MemberName member, MethodHandle resolvedHandle, MethodHandleImpl.Intrinsic intrinsic) {
10821102
this.member = member;
10831103
this.resolvedHandle = resolvedHandle;
1084-
this.intrinsicName = intrinsic;
1085-
assert(resolvedHandle == null ||
1086-
resolvedHandle.intrinsicName() == MethodHandleImpl.Intrinsic.NONE ||
1087-
resolvedHandle.intrinsicName() == intrinsic) : resolvedHandle.intrinsicName() + " != " + intrinsic;
10881104
// The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
10891105
//assert(!isInvokeBasic(member));
10901106
}
@@ -1097,7 +1113,6 @@ static class NamedFunction {
10971113
// necessary to pass BigArityTest
10981114
this.member = Invokers.invokeBasicMethod(basicInvokerType);
10991115
}
1100-
this.intrinsicName = MethodHandleImpl.Intrinsic.NONE;
11011116
assert(isInvokeBasic(member));
11021117
}
11031118

@@ -1250,7 +1265,15 @@ public boolean isConstantZero() {
12501265
}
12511266

12521267
public MethodHandleImpl.Intrinsic intrinsicName() {
1253-
return intrinsicName;
1268+
return resolvedHandle != null
1269+
? resolvedHandle.intrinsicName()
1270+
: MethodHandleImpl.Intrinsic.NONE;
1271+
}
1272+
1273+
public Object intrinsicData() {
1274+
return resolvedHandle != null
1275+
? resolvedHandle.intrinsicData()
1276+
: null;
12541277
}
12551278
}
12561279

@@ -1732,15 +1755,15 @@ private static void createFormsFor(BasicType type) {
17321755
Name[] idNames = new Name[] { argument(0, L_TYPE), argument(1, type) };
17331756
idForm = new LambdaForm(2, idNames, 1, Kind.IDENTITY);
17341757
idForm.compileToBytecode();
1735-
idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm),
1736-
MethodHandleImpl.Intrinsic.IDENTITY);
1758+
idFun = new NamedFunction(idMem, MethodHandleImpl.makeIntrinsic(SimpleMethodHandle.make(idMem.getInvocationType(), idForm),
1759+
MethodHandleImpl.Intrinsic.IDENTITY));
17371760

17381761
Object zeValue = Wrapper.forBasicType(btChar).zero();
17391762
Name[] zeNames = new Name[] { argument(0, L_TYPE), new Name(idFun, zeValue) };
17401763
zeForm = new LambdaForm(1, zeNames, 1, Kind.ZERO);
17411764
zeForm.compileToBytecode();
1742-
zeFun = new NamedFunction(zeMem, SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm),
1743-
MethodHandleImpl.Intrinsic.ZERO);
1765+
zeFun = new NamedFunction(zeMem, MethodHandleImpl.makeIntrinsic(SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm),
1766+
MethodHandleImpl.Intrinsic.ZERO));
17441767
}
17451768

17461769
LF_zero[ord] = zeForm;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import static java.lang.invoke.LambdaForm.BasicType.*;
3939
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
4040
import static java.lang.invoke.MethodHandleImpl.NF_loop;
41+
import static java.lang.invoke.MethodHandleImpl.makeIntrinsic;
4142

4243
/** Transforms on LFs.
4344
* A lambda-form editor can derive new LFs from its base LF.
@@ -619,7 +620,7 @@ LambdaForm spreadArgumentsForm(int pos, Class<?> arrayType, int arrayLength) {
619620
// adjust the arguments
620621
MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType);
621622
for (int i = 0; i < arrayLength; i++) {
622-
Name loadArgument = new Name(new NamedFunction(aload, Intrinsic.ARRAY_LOAD), spreadParam, i);
623+
Name loadArgument = new Name(new NamedFunction(makeIntrinsic(aload, Intrinsic.ARRAY_LOAD)), spreadParam, i);
623624
buf.insertExpression(exprPos + i, loadArgument);
624625
buf.replaceParameterByCopy(pos + i, exprPos + i);
625626
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,6 +1679,11 @@ MethodHandleImpl.Intrinsic intrinsicName() {
16791679
return MethodHandleImpl.Intrinsic.NONE;
16801680
}
16811681

1682+
/*non-public*/
1683+
Object intrinsicData() {
1684+
return null;
1685+
}
1686+
16821687
/*non-public*/
16831688
MethodHandle withInternalMemberName(MemberName member, boolean isInvokeSpecial) {
16841689
if (member != null) {

0 commit comments

Comments
 (0)