< prev index next >

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

Print this page
rev 15686 : 8161211: better inlining support for loop bytecode intrinsics
Reviewed-by: jrose, vlivanov

*** 37,54 **** 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.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.Kind.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; /** * Code generation backend for LambdaForm. --- 37,54 ---- import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Modifier; import java.util.ArrayList; + import java.util.Arrays; import java.util.HashMap; import java.util.stream.Stream; ! import static java.lang.invoke.LambdaForm.BasicType; import static java.lang.invoke.LambdaForm.BasicType.*; ! import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; /** * Code generation backend for LambdaForm.
*** 63,72 **** --- 63,75 ---- private static final String LFN = "java/lang/invoke/LambdaForm$Name"; private static final String CLS = "java/lang/Class"; 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 + ";"; private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V"; private static final String CLASS_PREFIX = LF + "$";
*** 1317,1343 **** * Emit bytecode for the loop idiom. * <p> * 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 * }</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 * 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) * ... --- 1320,1350 ---- * Emit bytecode for the loop idiom. * <p> * The pattern looks like (Cf. MethodHandleImpl.loop): * <blockquote><pre>{@code * // a0: BMH ! * // 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[], ! * 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 + * 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) * ...
*** 1347,1386 **** * }</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[] * ICONST x * AALOAD // load the init handle for clause x * load args * INVOKEVIRTUAL MethodHandle.invokeBasic * store vx * }</pre></blockquote> * <p> * 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[] * ICONST x * AALOAD // load the step handle for clause x * load locals * load args * INVOKEVIRTUAL MethodHandle.invokeBasic * store vx ! * ALOAD preds ! * CHECKCAST MethodHandle[] * 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[] * ICONST x * AALOAD // load the fini handle for clause x * load locals * load args * INVOKEVIRTUAL MethodHandle.invokeBasic --- 1354,1397 ---- * }</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 clauseDataIndex ! * ICONST_0 ! * AALOAD // load the inits array * ICONST x * AALOAD // load the init handle for clause x * load args * INVOKEVIRTUAL MethodHandle.invokeBasic * store vx * }</pre></blockquote> * <p> * 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 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 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 clauseDataIndex ! * ICONST_3 ! * AALOAD // load the finis array * ICONST x * AALOAD // load the fini handle for clause x * load locals * load args * INVOKEVIRTUAL MethodHandle.invokeBasic
*** 1395,1406 **** // extract clause and loop-local state types // find the type info in the loop invocation BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0]; Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes). filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class<?>[]::new); ! final int firstLoopStateIndex = extendLocalsMap(loopLocalStateTypes); Class<?> returnType = result.function.resolvedHandle().type().returnType(); MethodType loopType = args.function.resolvedHandle().type() .dropParameterTypes(0,1) .changeReturnType(returnType); --- 1406,1421 ---- // extract clause and loop-local state types // find the type info in the loop invocation 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 clauseDataIndex = extendLocalsMap(localTypes); ! final int firstLoopStateIndex = clauseDataIndex + 1; Class<?> returnType = result.function.resolvedHandle().type().returnType(); MethodType loopType = args.function.resolvedHandle().type() .dropParameterTypes(0,1) .changeReturnType(returnType);
*** 1418,1431 **** Label lLoop = new Label(); Label lDone = new Label(); Label lNext; // 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); if (cInitType.returnType() != void.class) { emitStoreInsn(BasicType.basicType(cInitType.returnType()), firstLoopStateIndex + state); ++state; } } --- 1433,1452 ---- Label lLoop = new Label(); 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, clauseDataIndex, ! firstLoopStateIndex); if (cInitType.returnType() != void.class) { emitStoreInsn(BasicType.basicType(cInitType.returnType()), firstLoopStateIndex + state); ++state; } }
*** 1438,1459 **** MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass()); boolean isVoid = stepType.returnType() == void.class; // invoke loop step ! emitLoopHandleInvoke(invoker, steps, c, args, true, stepType, loopLocalStateTypes, firstLoopStateIndex); if (!isVoid) { emitStoreInsn(BasicType.basicType(stepType.returnType()), firstLoopStateIndex + state); ++state; } // invoke loop predicate ! emitLoopHandleInvoke(invoker, preds, c, args, true, predType, loopLocalStateTypes, firstLoopStateIndex); mv.visitJumpInsn(Opcodes.IFNE, lNext); // invoke fini ! emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, firstLoopStateIndex); mv.visitJumpInsn(Opcodes.GOTO, lDone); // this is the beginning of the next loop clause mv.visitLabel(lNext); } --- 1459,1483 ---- MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass()); boolean isVoid = stepType.returnType() == void.class; // invoke loop step ! 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, clauseDataIndex, ! firstLoopStateIndex); mv.visitJumpInsn(Opcodes.IFNE, lNext); // invoke fini ! emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, ! firstLoopStateIndex); mv.visitJumpInsn(Opcodes.GOTO, lDone); // this is the beginning of the next loop clause mv.visitLabel(lNext); }
*** 1481,1493 **** localsMap[localsMap.length - 1] = index - lastSlots; return firstSlot; } private void emitLoopHandleInvoke(Name holder, int handles, int clause, Name args, boolean pushLocalState, ! MethodType type, Class<?>[] loopLocalStateTypes, int firstLoopStateSlot) { // load handle for clause ! emitPushArgument(holder, handles); emitIconstInsn(clause); mv.visitInsn(Opcodes.AALOAD); // load loop state (preceding the other arguments) if (pushLocalState) { for (int s = 0; s < loopLocalStateTypes.length; ++s) { --- 1505,1518 ---- localsMap[localsMap.length - 1] = index - lastSlots; return firstSlot; } private void emitLoopHandleInvoke(Name holder, int handles, int clause, Name args, boolean pushLocalState, ! MethodType type, Class<?>[] loopLocalStateTypes, int clauseDataSlot, ! int firstLoopStateSlot) { // load handle for clause ! emitPushClauseArray(clauseDataSlot, handles); emitIconstInsn(clause); mv.visitInsn(Opcodes.AALOAD); // load loop state (preceding the other arguments) if (pushLocalState) { for (int s = 0; s < loopLocalStateTypes.length; ++s) {
*** 1497,1506 **** --- 1522,1537 ---- // load loop args (skip 0: method handle) emitPushArguments(args, 1); 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; case J_TYPE: mv.visitInsn(Opcodes.LCONST_0); break; case F_TYPE: mv.visitInsn(Opcodes.FCONST_0); break;
< prev index next >