< prev index next >

jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormBuilder.java

Print this page

        

@@ -23,24 +23,28 @@
  * questions.
  */
 
 package java.lang.invoke;
 
-import jdk.experimental.bytecode.AnnotationsBuilder;
+import jdk.experimental.bytecode.*;
 import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind;
 import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind;
-import jdk.experimental.bytecode.MethodBuilder;
-import jdk.experimental.bytecode.TypeTag;
 import jdk.experimental.value.MethodHandleBuilder;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Opcodes;
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
 import valhalla.shady.MinimalValueTypes_1_0;
 
 import java.lang.invoke.LambdaForm.BasicType;
 import java.lang.invoke.LambdaForm.Name;
 import java.lang.invoke.MethodHandles.Lookup;
+import java.util.Arrays;
+import java.util.stream.Stream;
 
+import static java.lang.invoke.InvokerBytecodeGenerator.isStaticallyInvocable;
+import static java.lang.invoke.InvokerBytecodeGenerator.isStaticallyNameable;
 import static java.lang.invoke.LambdaForm.BasicType.L_TYPE;
 import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
 import static java.lang.invoke.LambdaForm.BasicType.basicType;
 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getField;
 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic;

@@ -48,12 +52,14 @@
 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeSpecial;
 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeVirtual;
 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putField;
 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic;
+import static java.lang.invoke.MethodHandleStatics.PROFILE_GWT;
 import static java.lang.invoke.MethodHandleStatics.PROFILE_LEVEL;
 import static java.lang.invoke.MethodHandleStatics.newInternalError;
+import static jdk.experimental.bytecode.MacroCodeBuilder.CondKind.NE;
 
 /**
  * Utility class for spinning classfiles associated with lambda forms.
  */
 class LambdaFormBuilder extends MethodHandleBuilder {

@@ -61,10 +67,13 @@
     private static final String OBJ     = "java/lang/Object";
     private static final String CLASS_PREFIX   = "java/lang/invoke/LambdaForm$Value$";
     private static final String DEFAULT_CLASS  = "MH";
     private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
 
+    private static final String MH           = "java/lang/invoke/MethodHandle";
+    private static final String MHARY2       = "[[L" + MH + ";";
+
     static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
         String invokerName = form.lambdaName();
         int p = invokerName.indexOf('.');
         boolean overrideNames = p != -1;
         String methodName = overrideNames ? invokerName.substring(p + 1) : invokerName;

@@ -81,31 +90,93 @@
     }
 
     LambdaFormCodeBuilder builder;
     LambdaForm lambdaForm;
     MethodType invokerType;
+    int maxLocals;
+    int[] localsMap;
 
     LambdaFormBuilder(LambdaFormCodeBuilder builder, LambdaForm lambdaForm, MethodType invokerType) {
         this.builder = builder;
         this.lambdaForm = lambdaForm;
         this.invokerType = invokerType;
+        this.maxLocals = lambdaForm.names.length;
+        this.localsMap = computeLocalsMap(lambdaForm);
     }
 
+    static int[] computeLocalsMap(LambdaForm lform) {
+        int localsMapSize = lform.names.length;
+        int[] localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots
+        for (int i = 0, index = 0; i < localsMap.length; i++) {
+            localsMap[i] = index;
+            if (i < lform.names.length) {
+                BasicType type = lform.names[i].type();
+                index += type.basicTypeSlots();
+            }
+        }
+        return localsMap;
+    }
     void generateLambdaFormBody() {
         // iterate over the form's names, generating bytecode instructions for each
         // start iterating at the first name following the arguments
         Name onStack = null;
         for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
             Name name = lambdaForm.names[i];
 
             if (onStack != null && onStack.type != V_TYPE) {
                 // non-void: actually assign
-                builder.store(fromBasicType(onStack.type), onStack.index());
+                builder.store(fromBasicType(onStack.type), localsMap[onStack.index()]);
             }
             onStack = name;  // unless otherwise modified below
             MemberName member = name.function.member();
-            if (InvokerBytecodeGenerator.isStaticallyInvocable(member)) {
+            MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
+            switch (intr) {
+                 case SELECT_ALTERNATIVE: {
+                     assert lambdaForm.isSelectAlternative(i);
+                     if (PROFILE_GWT) {
+                         assert(name.arguments[0] instanceof Name &&
+                                 ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean"));
+//                          mv.visitAnnotation(INJECTEDPROFILE_SIG, true);
+                     }
+                     onStack = emitSelectAlternative(name, lambdaForm.names[i+1]);
+                     i++;  // skip MH.invokeBasic of the selectAlternative result
+                     continue;
+
+                 }
+                // case GUARD_WITH_CATCH:
+                // case TRY_FINALLY:
+
+                case LOOP: {
+                    assert lambdaForm.isLoop(i);
+                    onStack = emitLoop(i);
+                    i += 2; // jump to the end of the LOOP idiom
+                    continue;
+                }
+                case IDENTITY: {
+                    assert (name.arguments.length == 1);
+                    emitPushArguments(name, 0);
+                    continue;
+                }
+                case ZERO: {
+                    assert (name.arguments.length == 0);
+                    assert (name.type != BasicType.Q_TYPE);
+                    builder.ldc(name.type.basicTypeWrapper().zero());
+                    continue;
+                }
+                case NONE: {
+                    // no intrinsic associated
+                    break;
+                }
+                // case NEW_ARRAY: ?
+                // case ARRAY_LOAD: -
+                // case ARRAY_STORE: -
+                // case ARRAY_LENGTH: -
+                default: {
+                    throw newInternalError("Unknown intrinsic: "+intr);
+                }
+            }
+            if (isStaticallyInvocable(member)) {
                 emitStaticInvoke(member, name);
             } else {
                 emitInvoke(name);
             }
         }

@@ -127,11 +198,11 @@
         } else {
             LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
 
             // put return value on the stack if it is not already there
             if (rn != onStack) {
-                builder.load(lambdaForm.result);
+                builder.load(localsMap[lambdaForm.result]);
             }
 
             emitImplicitConversion(rtype, rclass, rn);
 
             // generate actual return statement

@@ -177,18 +248,18 @@
 
     private void emitReferenceCast(Class<?> cls, Object arg) {
         Name writeBack = null;  // local to write back result
         if (arg instanceof Name) {
             Name n = (Name) arg;
-            if (cls.isAssignableFrom(builder.typeOfLocal(n.index())))
+            if (cls.isAssignableFrom(builder.typeOfLocal(localsMap[n.index()])))
                 return;  // this cast was already performed
             if (lambdaForm.useCount(n) > 1) {
                 // This guy gets used more than once.
                 writeBack = n;
             }
         }
-        if (InvokerBytecodeGenerator.isStaticallyNameable(cls)) {
+        if (isStaticallyNameable(cls)) {
             builder.checkcast(cls);
         } else {
             builder.ldc(cls)
                     .checkcast(Class.class)
                     .swap()

@@ -197,14 +268,18 @@
                 builder.checkcast(Object[].class);
             else if (PROFILE_LEVEL > 0)
                 builder.checkcast(Object.class);
         }
         if (writeBack != null) {
-            builder.dup().astore(writeBack.index());
+            builder.dup().astore(localsMap[writeBack.index()]);
         }
     }
 
+    void emitStaticInvoke(Name name) {
+        emitStaticInvoke(name.function.member(), name);
+    }
+
     /**
      * Emit an invoke for the given name, using the MemberName directly.
      */
     void emitStaticInvoke(MemberName member, Name name) {
         assert(member.equals(name.function.member()));

@@ -278,11 +353,11 @@
 
     private void emitPushArgument(Class<?> ptype, Object arg) {
         BasicType bptype = basicType(ptype);
         if (arg instanceof Name) {
             Name n = (Name) arg;
-            builder.load(fromBasicType(n.type), n.index());
+            builder.load(fromBasicType(n.type), localsMap[n.index()]);
             emitImplicitConversion(n.type, ptype, n);
         } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
             builder.ldc(arg);
         } else {
             if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {

@@ -306,10 +381,14 @@
             default:
                 throw new InternalError("unknown type: " + type);
         }
     }
 
+    TypeTag fromClass(Class<?> cls) {
+        return fromBasicType(BasicType.basicType(cls));
+    }
+
     InvocationKind invKindFromRefKind(int refKind) {
         switch (refKind) {
             case REF_invokeVirtual:      return InvocationKind.INVOKEVIRTUAL;
             case REF_invokeStatic:       return InvocationKind.INVOKESTATIC;
             case REF_invokeSpecial:      return InvocationKind.INVOKESPECIAL;

@@ -340,6 +419,191 @@
 
         Class<?> typeOfLocal(int index) {
             return typeHelper.symbol(state.locals.get(index));
         }
     }
+
+    private Name emitLoop(int pos) {
+        Name args    = lambdaForm.names[pos];
+        Name invoker = lambdaForm.names[pos+1];
+        Name result  = lambdaForm.names[pos+2];
+
+        // 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;
+        maxLocals += loopLocalStateTypes.length + 1;
+
+        Class<?> returnType = result.function.resolvedHandle().type().returnType();
+        MethodType loopType = args.function.resolvedHandle().type()
+                .dropParameterTypes(0,1)
+                .changeReturnType(returnType);
+        MethodType loopHandleType = loopType.insertParameterTypes(0, loopLocalStateTypes);
+        MethodType predType = loopHandleType.changeReturnType(boolean.class);
+        MethodType finiType = loopHandleType;
+
+        final int nClauses = loopClauseTypes.length;
+
+        // indices to invoker arguments to load method handle arrays
+        final int inits = 1;
+        final int steps = 2;
+        final int preds = 3;
+        final int finis = 4;
+
+        // PREINIT:
+        emitPushArgument(MethodHandleImpl.LoopClauses.class, invoker.arguments[1]);
+        builder.getfield(MethodHandleImpl.LoopClauses.class, "clauses", MHARY2);
+        emitAstoreInsn(clauseDataIndex);
+
+        // INIT:
+        for (int c = 0, state = 0; c < nClauses; ++c) {
+            MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass());
+            emitLoopHandleInvoke(inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
+            if (cInitType.returnType() != void.class) {
+                builder.store(fromClass(cInitType.returnType()), localsMap[firstLoopStateIndex + state]);
+                ++state;
+            }
+        }
+
+        // LOOP:
+        builder.label("LOOP");
+
+        String val = null;
+        for (int c = 0, s = 0; c < nClauses; ++c) {
+            MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass());
+            boolean isVoid = (stepType.returnType() == void.class);
+
+            // invoke loop step
+            emitLoopHandleInvoke(steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
+            if (!isVoid) {
+                builder.store(fromClass(stepType.returnType()), localsMap[firstLoopStateIndex + s]);
+                ++s;
+            }
+
+            // invoke loop predicate
+            emitLoopHandleInvoke(preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
+            builder.emitCondJump(Opcode.IFEQ, NE, "NEXT_" + c);
+
+            // invoke fini
+            emitLoopHandleInvoke(finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
+            builder.goto_("DONE");
+
+            if (finiType.returnType() != void.class) {
+                val = builder.state().pop(); // FIXME !!! HACK !!!
+            }
+
+            // this is the beginning of the next loop clause
+            builder.label("NEXT_" + c);
+        }
+        builder.goto_("LOOP");
+
+        if (finiType.returnType() != void.class) {
+            builder.state().push(val); // FIXME !!! HACK !!!
+        }
+
+        builder.label("DONE");
+
+        return result;
+    }
+
+    private void emitLoopHandleInvoke(int handles, int clause, Name args, boolean pushLocalState,
+                                      MethodType type, Class<?>[] loopLocalStateTypes, int clauseDataSlot,
+                                      int firstLoopStateSlot) {
+        // load handle for clause
+//        emitPushClauseArray(clauseDataSlot, handles);
+        builder.load(TypeTag.A, localsMap[clauseDataSlot])
+               .ldc(handles - 1)
+               .aaload()
+//        emitIconstInsn(clause);
+               .ldc(clause)
+               .aaload();
+
+        // load loop state (preceding the other arguments)
+        if (pushLocalState) {
+            for (int s = 0; s < loopLocalStateTypes.length; ++s) {
+//                emitLoadInsn(BasicType.basicType(loopLocalStateTypes[s]), firstLoopStateSlot + s);
+                builder.load(fromClass(loopLocalStateTypes[s]), localsMap[firstLoopStateSlot + s]);
+            }
+        }
+        // load loop args (skip 0: method handle)
+        emitPushArguments(args, 1);
+        builder.invokevirtual(MethodHandle.class, "invokeBasic", type.toMethodDescriptorString(), false);
+    }
+
+    /**
+     * Emit bytecode for the selectAlternative idiom.
+     *
+     * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
+     * <blockquote><pre>{@code
+     *   Lambda(a0:L,a1:I)=>{
+     *     t2:I=foo.test(a1:I);
+     *     t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
+     *     t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
+     * }</pre></blockquote>
+     */
+    private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
+        assert isStaticallyInvocable(invokeBasicName);
+
+        Name receiver = (Name) invokeBasicName.arguments[0];
+
+        int pos = invokeBasicName.index();
+        String L_fallback = "L_fallback_" + pos;
+        String L_done     = "L_done_"+ pos;
+
+        // load test result
+        emitPushArgument(selectAlternativeName, 0);
+
+        // if_icmpne L_fallback
+        builder.emitCondJump(Opcode.IFEQ, NE, L_fallback);
+
+        // invoke selectAlternativeName.arguments[1]
+        emitPushArgument(selectAlternativeName, 1);  // get 2nd argument of selectAlternative
+        emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
+        emitStaticInvoke(invokeBasicName);
+
+        // goto L_done
+        builder.goto_(L_done);
+
+        if (invokeBasicName.type() != BasicType.V_TYPE) {
+            builder.state().pop(); // FIXME !!! HACK !!!
+        }
+
+        // L_fallback:
+        builder.label(L_fallback);
+
+        // invoke selectAlternativeName.arguments[2]
+        emitPushArgument(selectAlternativeName, 2);  // get 3rd argument of selectAlternative
+        emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
+        emitStaticInvoke(invokeBasicName);
+
+        // L_done:
+        builder.label(L_done);
+
+        return invokeBasicName;  // return what's on stack
+    }
+
+    private void emitAstoreInsn(int index) {
+        builder.store(TypeTag.A, localsMap[index]);
+    }
+
+    private int extendLocalsMap(Class<?>[] types) {
+        int firstSlot = localsMap.length - 1;
+        localsMap = Arrays.copyOf(localsMap, localsMap.length + types.length);
+        int index = localsMap[firstSlot - 1] + 1;
+        int lastSlots = 0;
+        for (int i = 0; i < types.length; ++i) {
+            localsMap[firstSlot + i] = index;
+            lastSlots = BasicType.basicType(types[i]).basicTypeSlots();
+            index += lastSlots;
+        }
+        localsMap[localsMap.length - 1] = index - lastSlots;
+        return firstSlot;
+    }
 }
< prev index next >