8161211: better inlining support for loop bytecode intrinsics
Reviewed-by: jrose, vlivanov, redestad
This commit is contained in:
parent
8469097dde
commit
281862a6aa
@ -869,5 +869,4 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
static SpeciesData speciesData_LLL() { return checkCache(3, "LLL"); }
|
||||
static SpeciesData speciesData_LLLL() { return checkCache(4, "LLLL"); }
|
||||
static SpeciesData speciesData_LLLLL() { return checkCache(5, "LLLLL"); }
|
||||
static SpeciesData speciesData_LLLLLL() { return checkCache(6, "LLLLLL"); }
|
||||
}
|
||||
|
@ -39,14 +39,14 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.LambdaForm.Kind.*;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
@ -65,6 +65,9 @@ class InvokerBytecodeGenerator {
|
||||
private static final String OBJ = "java/lang/Object";
|
||||
private static final String OBJARY = "[Ljava/lang/Object;";
|
||||
|
||||
private static final String LOOP_CLAUSES = MHI + "$LoopClauses";
|
||||
private static final String MHARY2 = "[[L" + MH + ";";
|
||||
|
||||
private static final String LF_SIG = "L" + LF + ";";
|
||||
private static final String LFN_SIG = "L" + LFN + ";";
|
||||
private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
|
||||
@ -1319,38 +1322,43 @@ class InvokerBytecodeGenerator {
|
||||
* The pattern looks like (Cf. MethodHandleImpl.loop):
|
||||
* <blockquote><pre>{@code
|
||||
* // a0: BMH
|
||||
* // a1: inits, a2: steps, a3: preds, a4: finis
|
||||
* // a5: box, a6: unbox
|
||||
* // a7 (and following): arguments
|
||||
* loop=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
|
||||
* t8:L=MethodHandle.invokeBasic(a5:L,a7:L); // box the arguments into an Object[]
|
||||
* t9:L=MethodHandleImpl.loop(bt:L,a1:L,a2:L,a3:L,a4:L,t8:L); // call the loop executor (with supplied types in bt)
|
||||
* t10:L=MethodHandle.invokeBasic(a6:L,t9:L);t10:L} // unbox the result; return the result
|
||||
* // a1: LoopClauses (containing an array of arrays: inits, steps, preds, finis)
|
||||
* // a2: box, a3: unbox
|
||||
* // a4 (and following): arguments
|
||||
* loop=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L)=>{
|
||||
* t5:L=MethodHandle.invokeBasic(a2:L,a4:L); // box the arguments into an Object[]
|
||||
* t6:L=MethodHandleImpl.loop(bt:L,a1:L,t5:L); // call the loop executor (with supplied types in bt)
|
||||
* t7:L=MethodHandle.invokeBasic(a3:L,t6:L);t7:L} // unbox the result; return the result
|
||||
* }</pre></blockquote>
|
||||
* <p>
|
||||
* It is compiled into bytecode equivalent to the code seen in {@link MethodHandleImpl#loop(BasicType[],
|
||||
* MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], Object...)}, with the difference that no arrays
|
||||
* MethodHandleImpl.LoopClauses, Object...)}, with the difference that no arrays
|
||||
* will be used for local state storage. Instead, the local state will be mapped to actual stack slots.
|
||||
* <p>
|
||||
* Bytecode generation applies an unrolling scheme to enable better bytecode generation regarding local state type
|
||||
* handling. The generated bytecode will have the following form ({@code void} types are ignored for convenience).
|
||||
* Assume there are {@code C} clauses in the loop.
|
||||
* <blockquote><pre>{@code
|
||||
* INIT: (INIT_SEQ for clause 1)
|
||||
* ...
|
||||
* (INIT_SEQ for clause C)
|
||||
* LOOP: (LOOP_SEQ for clause 1)
|
||||
* ...
|
||||
* (LOOP_SEQ for clause C)
|
||||
* GOTO LOOP
|
||||
* DONE: ...
|
||||
* PREINIT: ALOAD_1
|
||||
* CHECKCAST LoopClauses
|
||||
* GETFIELD LoopClauses.clauses
|
||||
* ASTORE clauseDataIndex // place the clauses 2-dimensional array on the stack
|
||||
* INIT: (INIT_SEQ for clause 1)
|
||||
* ...
|
||||
* (INIT_SEQ for clause C)
|
||||
* LOOP: (LOOP_SEQ for clause 1)
|
||||
* ...
|
||||
* (LOOP_SEQ for clause C)
|
||||
* GOTO LOOP
|
||||
* DONE: ...
|
||||
* }</pre></blockquote>
|
||||
* <p>
|
||||
* The {@code INIT_SEQ_x} sequence for clause {@code x} (with {@code x} ranging from {@code 0} to {@code C-1}) has
|
||||
* the following shape. Assume slot {@code vx} is used to hold the state for clause {@code x}.
|
||||
* <blockquote><pre>{@code
|
||||
* INIT_SEQ_x: ALOAD inits
|
||||
* CHECKCAST MethodHandle[]
|
||||
* INIT_SEQ_x: ALOAD clauseDataIndex
|
||||
* ICONST_0
|
||||
* AALOAD // load the inits array
|
||||
* ICONST x
|
||||
* AALOAD // load the init handle for clause x
|
||||
* load args
|
||||
@ -1361,24 +1369,27 @@ class InvokerBytecodeGenerator {
|
||||
* The {@code LOOP_SEQ_x} sequence for clause {@code x} (with {@code x} ranging from {@code 0} to {@code C-1}) has
|
||||
* the following shape. Again, assume slot {@code vx} is used to hold the state for clause {@code x}.
|
||||
* <blockquote><pre>{@code
|
||||
* LOOP_SEQ_x: ALOAD steps
|
||||
* CHECKCAST MethodHandle[]
|
||||
* LOOP_SEQ_x: ALOAD clauseDataIndex
|
||||
* ICONST_1
|
||||
* AALOAD // load the steps array
|
||||
* ICONST x
|
||||
* AALOAD // load the step handle for clause x
|
||||
* load locals
|
||||
* load args
|
||||
* INVOKEVIRTUAL MethodHandle.invokeBasic
|
||||
* store vx
|
||||
* ALOAD preds
|
||||
* CHECKCAST MethodHandle[]
|
||||
* ALOAD clauseDataIndex
|
||||
* ICONST_2
|
||||
* AALOAD // load the preds array
|
||||
* ICONST x
|
||||
* AALOAD // load the pred handle for clause x
|
||||
* load locals
|
||||
* load args
|
||||
* INVOKEVIRTUAL MethodHandle.invokeBasic
|
||||
* IFNE LOOP_SEQ_x+1 // predicate returned false -> jump to next clause
|
||||
* ALOAD finis
|
||||
* CHECKCAST MethodHandle[]
|
||||
* ALOAD clauseDataIndex
|
||||
* ICONST_3
|
||||
* AALOAD // load the finis array
|
||||
* ICONST x
|
||||
* AALOAD // load the fini handle for clause x
|
||||
* load locals
|
||||
@ -1397,8 +1408,12 @@ class InvokerBytecodeGenerator {
|
||||
BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0];
|
||||
Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes).
|
||||
filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class<?>[]::new);
|
||||
Class<?>[] localTypes = new Class<?>[loopLocalStateTypes.length + 1];
|
||||
localTypes[0] = MethodHandleImpl.LoopClauses.class;
|
||||
System.arraycopy(loopLocalStateTypes, 0, localTypes, 1, loopLocalStateTypes.length);
|
||||
|
||||
final int firstLoopStateIndex = extendLocalsMap(loopLocalStateTypes);
|
||||
final int clauseDataIndex = extendLocalsMap(localTypes);
|
||||
final int firstLoopStateIndex = clauseDataIndex + 1;
|
||||
|
||||
Class<?> returnType = result.function.resolvedHandle().type().returnType();
|
||||
MethodType loopType = args.function.resolvedHandle().type()
|
||||
@ -1420,10 +1435,16 @@ class InvokerBytecodeGenerator {
|
||||
Label lDone = new Label();
|
||||
Label lNext;
|
||||
|
||||
// PREINIT:
|
||||
emitPushArgument(MethodHandleImpl.LoopClauses.class, invoker.arguments[1]);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, LOOP_CLAUSES, "clauses", MHARY2);
|
||||
emitAstoreInsn(clauseDataIndex);
|
||||
|
||||
// INIT:
|
||||
for (int c = 0, state = 0; c < nClauses; ++c) {
|
||||
MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass());
|
||||
emitLoopHandleInvoke(invoker, inits, c, args, false, cInitType, loopLocalStateTypes, firstLoopStateIndex);
|
||||
emitLoopHandleInvoke(invoker, inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex,
|
||||
firstLoopStateIndex);
|
||||
if (cInitType.returnType() != void.class) {
|
||||
emitStoreInsn(BasicType.basicType(cInitType.returnType()), firstLoopStateIndex + state);
|
||||
++state;
|
||||
@ -1440,18 +1461,21 @@ class InvokerBytecodeGenerator {
|
||||
boolean isVoid = stepType.returnType() == void.class;
|
||||
|
||||
// invoke loop step
|
||||
emitLoopHandleInvoke(invoker, steps, c, args, true, stepType, loopLocalStateTypes, firstLoopStateIndex);
|
||||
emitLoopHandleInvoke(invoker, steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex,
|
||||
firstLoopStateIndex);
|
||||
if (!isVoid) {
|
||||
emitStoreInsn(BasicType.basicType(stepType.returnType()), firstLoopStateIndex + state);
|
||||
++state;
|
||||
}
|
||||
|
||||
// invoke loop predicate
|
||||
emitLoopHandleInvoke(invoker, preds, c, args, true, predType, loopLocalStateTypes, firstLoopStateIndex);
|
||||
emitLoopHandleInvoke(invoker, preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex,
|
||||
firstLoopStateIndex);
|
||||
mv.visitJumpInsn(Opcodes.IFNE, lNext);
|
||||
|
||||
// invoke fini
|
||||
emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, firstLoopStateIndex);
|
||||
emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex,
|
||||
firstLoopStateIndex);
|
||||
mv.visitJumpInsn(Opcodes.GOTO, lDone);
|
||||
|
||||
// this is the beginning of the next loop clause
|
||||
@ -1483,9 +1507,10 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
|
||||
private void emitLoopHandleInvoke(Name holder, int handles, int clause, Name args, boolean pushLocalState,
|
||||
MethodType type, Class<?>[] loopLocalStateTypes, int firstLoopStateSlot) {
|
||||
MethodType type, Class<?>[] loopLocalStateTypes, int clauseDataSlot,
|
||||
int firstLoopStateSlot) {
|
||||
// load handle for clause
|
||||
emitPushArgument(holder, handles);
|
||||
emitPushClauseArray(clauseDataSlot, handles);
|
||||
emitIconstInsn(clause);
|
||||
mv.visitInsn(Opcodes.AALOAD);
|
||||
// load loop state (preceding the other arguments)
|
||||
@ -1499,6 +1524,12 @@ class InvokerBytecodeGenerator {
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.toMethodDescriptorString(), false);
|
||||
}
|
||||
|
||||
private void emitPushClauseArray(int clauseDataSlot, int which) {
|
||||
emitAloadInsn(clauseDataSlot);
|
||||
emitIconstInsn(which - 1);
|
||||
mv.visitInsn(Opcodes.AALOAD);
|
||||
}
|
||||
|
||||
private void emitZero(BasicType type) {
|
||||
switch (type) {
|
||||
case I_TYPE: mv.visitInsn(Opcodes.ICONST_0); break;
|
||||
|
@ -41,7 +41,6 @@ import java.util.HashMap;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The symbolic, non-executable form of a method handle's invocation semantics.
|
||||
@ -732,9 +731,9 @@ class LambdaForm {
|
||||
boolean isLoop(int pos) {
|
||||
// loop idiom:
|
||||
// t_{n}:L=MethodHandle.invokeBasic(...)
|
||||
// t_{n+1}:L=MethodHandleImpl.loop(types, *, *, *, *, t_{n})
|
||||
// t_{n+1}:L=MethodHandleImpl.loop(types, *, t_{n})
|
||||
// t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
|
||||
return isMatchingIdiom(pos, "loop", 5);
|
||||
return isMatchingIdiom(pos, "loop", 2);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1689,8 +1689,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
NF_tryFinally = new NamedFunction(MethodHandleImpl.class
|
||||
.getDeclaredMethod("tryFinally", MethodHandle.class, MethodHandle.class, Object[].class));
|
||||
NF_loop = new NamedFunction(MethodHandleImpl.class
|
||||
.getDeclaredMethod("loop", BasicType[].class, MethodHandle[].class, MethodHandle[].class,
|
||||
MethodHandle[].class, MethodHandle[].class, Object[].class));
|
||||
.getDeclaredMethod("loop", BasicType[].class, LoopClauses.class, Object[].class));
|
||||
NF_throwException = new NamedFunction(MethodHandleImpl.class
|
||||
.getDeclaredMethod("throwException", Throwable.class));
|
||||
NF_profileBoolean = new NamedFunction(MethodHandleImpl.class
|
||||
@ -1794,12 +1793,13 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
|
||||
MethodHandle unboxResult = unboxResultHandle(tloop);
|
||||
|
||||
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLLL();
|
||||
LoopClauses clauseData =
|
||||
new LoopClauses(new MethodHandle[][]{toArray(init), toArray(step), toArray(pred), toArray(fini)});
|
||||
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
|
||||
BoundMethodHandle mh;
|
||||
try {
|
||||
mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) toArray(init),
|
||||
(Object) toArray(step), (Object) toArray(pred), (Object) toArray(fini), (Object) collectArgs,
|
||||
(Object) unboxResult);
|
||||
mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) clauseData,
|
||||
(Object) collectArgs, (Object) unboxResult);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
@ -1818,23 +1818,20 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
* {@code t12}):
|
||||
* <blockquote><pre>{@code
|
||||
* loop=Lambda(a0:L,a1:L)=>{
|
||||
* t2:L=BoundMethodHandle$Species_L6.argL0(a0:L); // array of init method handles
|
||||
* t3:L=BoundMethodHandle$Species_L6.argL1(a0:L); // array of step method handles
|
||||
* t4:L=BoundMethodHandle$Species_L6.argL2(a0:L); // array of pred method handles
|
||||
* t5:L=BoundMethodHandle$Species_L6.argL3(a0:L); // array of fini method handles
|
||||
* t6:L=BoundMethodHandle$Species_L6.argL4(a0:L); // helper handle to box the arguments into an Object[]
|
||||
* t7:L=BoundMethodHandle$Species_L6.argL5(a0:L); // helper handle to unbox the result
|
||||
* t8:L=MethodHandle.invokeBasic(t6:L,a1:L); // box the arguments into an Object[]
|
||||
* t9:L=MethodHandleImpl.loop(null,t2:L,t3:L,t4:L,t5:L,t6:L); // call the loop executor
|
||||
* t10:L=MethodHandle.invokeBasic(t7:L,t9:L);t10:L} // unbox the result; return the result
|
||||
* t2:L=BoundMethodHandle$Species_L3.argL0(a0:L); // LoopClauses holding init, step, pred, fini handles
|
||||
* t3:L=BoundMethodHandle$Species_L3.argL1(a0:L); // helper handle to box the arguments into an Object[]
|
||||
* t4:L=BoundMethodHandle$Species_L3.argL2(a0:L); // helper handle to unbox the result
|
||||
* t5:L=MethodHandle.invokeBasic(t3:L,a1:L); // box the arguments into an Object[]
|
||||
* t6:L=MethodHandleImpl.loop(null,t2:L,t3:L); // call the loop executor
|
||||
* t7:L=MethodHandle.invokeBasic(t4:L,t6:L);t7:L} // unbox the result; return the result
|
||||
* }</pre></blockquote>
|
||||
* <p>
|
||||
* {@code argL0} through {@code argL3} are the arrays of init, step, pred, and fini method handles.
|
||||
* {@code argL4} and {@code argL5} are auxiliary method handles: {@code argL2} boxes arguments and wraps them into
|
||||
* {@code Object[]} ({@code ValueConversions.array()}), and {@code argL3} unboxes the result if necessary
|
||||
* {@code argL0} is a LoopClauses instance holding, in a 2-dimensional array, the init, step, pred, and fini method
|
||||
* handles. {@code argL1} and {@code argL2} are auxiliary method handles: {@code argL1} boxes arguments and wraps
|
||||
* them into {@code Object[]} ({@code ValueConversions.array()}), and {@code argL2} unboxes the result if necessary
|
||||
* ({@code ValueConversions.unbox()}).
|
||||
* <p>
|
||||
* Having {@code t6} and {@code t7} passed in via a BMH and not hardcoded in the lambda form allows to share lambda
|
||||
* Having {@code t3} and {@code t4} passed in via a BMH and not hardcoded in the lambda form allows to share lambda
|
||||
* forms among loop combinators with the same basic type.
|
||||
* <p>
|
||||
* The above template is instantiated by using the {@link LambdaFormEditor} to replace the {@code null} argument to
|
||||
@ -1845,15 +1842,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
private static LambdaForm makeLoopForm(MethodType basicType, BasicType[] localVarTypes) {
|
||||
MethodType lambdaType = basicType.invokerType();
|
||||
|
||||
final int THIS_MH = 0; // the BMH_LLLLLL
|
||||
final int THIS_MH = 0; // the BMH_LLL
|
||||
final int ARG_BASE = 1; // start of incoming arguments
|
||||
final int ARG_LIMIT = ARG_BASE + basicType.parameterCount();
|
||||
|
||||
int nameCursor = ARG_LIMIT;
|
||||
final int GET_INITS = nameCursor++;
|
||||
final int GET_STEPS = nameCursor++;
|
||||
final int GET_PREDS = nameCursor++;
|
||||
final int GET_FINIS = nameCursor++;
|
||||
final int GET_CLAUSE_DATA = nameCursor++;
|
||||
final int GET_COLLECT_ARGS = nameCursor++;
|
||||
final int GET_UNBOX_RESULT = nameCursor++;
|
||||
final int BOXED_ARGS = nameCursor++;
|
||||
@ -1864,14 +1858,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
if (lform == null) {
|
||||
Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
|
||||
|
||||
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLLL();
|
||||
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
|
||||
names[THIS_MH] = names[THIS_MH].withConstraint(data);
|
||||
names[GET_INITS] = new Name(data.getterFunction(0), names[THIS_MH]);
|
||||
names[GET_STEPS] = new Name(data.getterFunction(1), names[THIS_MH]);
|
||||
names[GET_PREDS] = new Name(data.getterFunction(2), names[THIS_MH]);
|
||||
names[GET_FINIS] = new Name(data.getterFunction(3), names[THIS_MH]);
|
||||
names[GET_COLLECT_ARGS] = new Name(data.getterFunction(4), names[THIS_MH]);
|
||||
names[GET_UNBOX_RESULT] = new Name(data.getterFunction(5), names[THIS_MH]);
|
||||
names[GET_CLAUSE_DATA] = new Name(data.getterFunction(0), names[THIS_MH]);
|
||||
names[GET_COLLECT_ARGS] = new Name(data.getterFunction(1), names[THIS_MH]);
|
||||
names[GET_UNBOX_RESULT] = new Name(data.getterFunction(2), names[THIS_MH]);
|
||||
|
||||
// t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...);
|
||||
MethodType collectArgsType = basicType.changeReturnType(Object.class);
|
||||
@ -1881,10 +1872,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT - ARG_BASE);
|
||||
names[BOXED_ARGS] = new Name(makeIntrinsic(invokeBasic, Intrinsic.LOOP), args);
|
||||
|
||||
// t_{i+1}:L=MethodHandleImpl.loop(localTypes:L,inits:L,steps:L,preds:L,finis:L,t_{i}:L);
|
||||
// t_{i+1}:L=MethodHandleImpl.loop(localTypes:L,clauses:L,t_{i}:L);
|
||||
Object[] lArgs =
|
||||
new Object[]{null, // placeholder for BasicType[] localTypes - will be added by LambdaFormEditor
|
||||
names[GET_INITS], names[GET_STEPS], names[GET_PREDS], names[GET_FINIS], names[BOXED_ARGS]};
|
||||
names[GET_CLAUSE_DATA], names[BOXED_ARGS]};
|
||||
names[LOOP] = new Name(NF_loop, lArgs);
|
||||
|
||||
// t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
|
||||
@ -1900,22 +1891,52 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
return lform.editor().noteLoopLocalTypesForm(BOXED_ARGS, localVarTypes);
|
||||
}
|
||||
|
||||
static class LoopClauses {
|
||||
@Stable final MethodHandle[][] clauses;
|
||||
LoopClauses(MethodHandle[][] clauses) {
|
||||
assert clauses.length == 4;
|
||||
this.clauses = clauses;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer("LoopClauses -- ");
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i > 0) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append('<').append(i).append(">: ");
|
||||
MethodHandle[] hs = clauses[i];
|
||||
for (int j = 0; j < hs.length; ++j) {
|
||||
if (j > 0) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append('*').append(j).append(": ").append(hs[j]).append('\n');
|
||||
}
|
||||
}
|
||||
sb.append(" --\n");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intrinsified during LambdaForm compilation
|
||||
* (see {@link InvokerBytecodeGenerator#emitLoop(int)}).
|
||||
*/
|
||||
@LambdaForm.Hidden
|
||||
static Object loop(BasicType[] localTypes, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred,
|
||||
MethodHandle[] fini, Object... av) throws Throwable {
|
||||
static Object loop(BasicType[] localTypes, LoopClauses clauseData, Object... av) throws Throwable {
|
||||
final MethodHandle[] init = clauseData.clauses[0];
|
||||
final MethodHandle[] step = clauseData.clauses[1];
|
||||
final MethodHandle[] pred = clauseData.clauses[2];
|
||||
final MethodHandle[] fini = clauseData.clauses[3];
|
||||
int varSize = (int) Stream.of(init).filter(h -> h.type().returnType() != void.class).count();
|
||||
int nArgs = init[0].type().parameterCount();
|
||||
Object[] varsAndArgs = new Object[varSize + nArgs];
|
||||
for (int i = 0, v = 0; i < init.length; ++i) {
|
||||
if (init[i].type().returnType() == void.class) {
|
||||
init[i].asFixedArity().invokeWithArguments(av);
|
||||
MethodHandle ih = init[i];
|
||||
if (ih.type().returnType() == void.class) {
|
||||
ih.invokeWithArguments(av);
|
||||
} else {
|
||||
varsAndArgs[v++] = init[i].asFixedArity().invokeWithArguments(av);
|
||||
varsAndArgs[v++] = ih.invokeWithArguments(av);
|
||||
}
|
||||
}
|
||||
System.arraycopy(av, 0, varsAndArgs, varSize, nArgs);
|
||||
@ -1926,12 +1947,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
MethodHandle s = step[i];
|
||||
MethodHandle f = fini[i];
|
||||
if (s.type().returnType() == void.class) {
|
||||
s.asFixedArity().invokeWithArguments(varsAndArgs);
|
||||
s.invokeWithArguments(varsAndArgs);
|
||||
} else {
|
||||
varsAndArgs[v++] = s.asFixedArity().invokeWithArguments(varsAndArgs);
|
||||
varsAndArgs[v++] = s.invokeWithArguments(varsAndArgs);
|
||||
}
|
||||
if (!(boolean) p.asFixedArity().invokeWithArguments(varsAndArgs)) {
|
||||
return f.asFixedArity().invokeWithArguments(varsAndArgs);
|
||||
if (!(boolean) p.invokeWithArguments(varsAndArgs)) {
|
||||
return f.invokeWithArguments(varsAndArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2122,14 +2143,13 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
Throwable t = null;
|
||||
Object r = null;
|
||||
try {
|
||||
// Use asFixedArity() to avoid unnecessary boxing of last argument for VarargsCollector case.
|
||||
r = target.asFixedArity().invokeWithArguments(av);
|
||||
r = target.invokeWithArguments(av);
|
||||
} catch (Throwable thrown) {
|
||||
t = thrown;
|
||||
throw t;
|
||||
} finally {
|
||||
Object[] args = target.type().returnType() == void.class ? prepend(av, t) : prepend(av, t, r);
|
||||
r = cleanup.asFixedArity().invokeWithArguments(args);
|
||||
r = cleanup.invokeWithArguments(args);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
@ -4368,10 +4368,11 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
||||
}
|
||||
|
||||
// Step 4: fill in missing parameter types.
|
||||
List<MethodHandle> finit = fillParameterTypes(init, commonSuffix);
|
||||
List<MethodHandle> fstep = fillParameterTypes(step, commonParameterSequence);
|
||||
List<MethodHandle> fpred = fillParameterTypes(pred, commonParameterSequence);
|
||||
List<MethodHandle> ffini = fillParameterTypes(fini, commonParameterSequence);
|
||||
// Also convert all handles to fixed-arity handles.
|
||||
List<MethodHandle> finit = fixArities(fillParameterTypes(init, commonSuffix));
|
||||
List<MethodHandle> fstep = fixArities(fillParameterTypes(step, commonParameterSequence));
|
||||
List<MethodHandle> fpred = fixArities(fillParameterTypes(pred, commonParameterSequence));
|
||||
List<MethodHandle> ffini = fixArities(fillParameterTypes(fini, commonParameterSequence));
|
||||
|
||||
assert finit.stream().map(MethodHandle::type).map(MethodType::parameterList).
|
||||
allMatch(pl -> pl.equals(commonSuffix));
|
||||
@ -4389,6 +4390,10 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<MethodHandle> fixArities(List<MethodHandle> hs) {
|
||||
return hs.stream().map(MethodHandle::asFixedArity).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code while} loop from an initializer, a body, and a predicate. This is a convenience wrapper for
|
||||
* the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
|
||||
@ -4887,7 +4892,8 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
||||
// target parameter list.
|
||||
cleanup = dropArgumentsToMatch(cleanup, (rtype == void.class ? 1 : 2), targetParamTypes, 0);
|
||||
|
||||
return MethodHandleImpl.makeTryFinally(target, cleanup, rtype, targetParamTypes);
|
||||
// Use asFixedArity() to avoid unnecessary boxing of last argument for VarargsCollector case.
|
||||
return MethodHandleImpl.makeTryFinally(target.asFixedArity(), cleanup.asFixedArity(), rtype, targetParamTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -632,6 +632,7 @@ public class MethodHandlesTest {
|
||||
}
|
||||
|
||||
public void testFindVirtualClone0() throws Throwable {
|
||||
if (CAN_SKIP_WORKING) return;
|
||||
// test some ad hoc system methods
|
||||
testFindVirtual(false, PUBLIC, Object.class, Object.class, "clone");
|
||||
|
||||
@ -2798,11 +2799,17 @@ public class MethodHandlesTest {
|
||||
toClauseMajor(postClauses, inits, steps, usePreds, finis);
|
||||
MethodHandle pre = MethodHandles.loop(preClauses);
|
||||
MethodHandle post = MethodHandles.loop(postClauses);
|
||||
if (verbosity >= 6) {
|
||||
System.out.println("pre-handle: " + pre);
|
||||
}
|
||||
Object[] preResults = (Object[]) pre.invokeWithArguments(args);
|
||||
if (verbosity >= 4) {
|
||||
System.out.println("pre-checked: expected " + Arrays.asList(preCheckedResults[i]) + ", actual " +
|
||||
Arrays.asList(preResults));
|
||||
}
|
||||
if (verbosity >= 6) {
|
||||
System.out.println("post-handle: " + post);
|
||||
}
|
||||
Object[] postResults = (Object[]) post.invokeWithArguments(args);
|
||||
if (verbosity >= 4) {
|
||||
System.out.println("post-checked: expected " + Arrays.asList(postCheckedResults[i]) + ", actual " +
|
||||
|
Loading…
Reference in New Issue
Block a user