< 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 >