--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Fri Sep 23 03:15:00 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Fri Sep 23 15:20:49 2016 +0200
@@ -39,14 +39,14 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
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.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 @@
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 @@
* 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 @@
* 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 @@
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 @@
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 @@
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 @@
}
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 @@
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;