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

Print this page
rev 17257 : imported patch 8183130.patch
rev 17253 : imported patch 8183131.patch
rev 17240 : Dump classes from LambdaFormBuilder is triggered using
MethodHandleStatics.DUMP_CLASS_FILES. Class names are
extended by the LF invoker name (which should be unique)
when debugging (dumping triggers debugging).
rev 17235 : Add support for Q-types to lambda forms
Note: support is optional, and can be enabled using the flag:
-Dvalhalla.enableValueLambdaForms=true

*** 23,45 **** * questions. */ package java.lang.invoke; ! import jdk.experimental.bytecode.AnnotationsBuilder; 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 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 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; --- 23,45 ---- * questions. */ package java.lang.invoke; ! import jdk.experimental.bytecode.*; import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind; import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind; import jdk.experimental.value.MethodHandleBuilder; 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.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;
*** 48,59 **** --- 48,61 ---- 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,70 **** --- 63,75 ---- 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;
*** 118,127 **** --- 123,178 ---- // non-void: actually assign builder.store(fromBasicType(onStack.type), localsMap[onStack.index()]); } onStack = name; // unless otherwise modified below MemberName member = name.function.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")); + builder.method().withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, InvokerBytecodeGenerator.INJECTEDPROFILE_SIG); + } + onStack = emitSelectAlternative(name, lambdaForm.names[i+1]); + i++; // skip MH.invokeBasic of the selectAlternative result + continue; + + } + // TODO: case GUARD_WITH_CATCH: + // TODO: 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; + } + // TODO: case NEW_ARRAY: + // TODO: case ARRAY_LOAD: + // TODO: case ARRAY_STORE: + // TODO: case ARRAY_LENGTH: + default: { + throw newInternalError("Unknown intrinsic: "+intr); + } + } if (InvokerBytecodeGenerator.isStaticallyInvocable(member)) { emitStaticInvoke(member, name); } else { emitInvoke(name); }
*** 258,268 **** /** * Emit an invoke for the given name. */ void emitInvoke(Name name) { - //assert(!isLinkerMethodInvoke(name)); // should use the static path for these if (true) { // push receiver MethodHandle target = name.function.resolvedHandle(); assert(target != null) : name.exprString(); builder.ldc(target); --- 309,318 ----
*** 323,332 **** --- 373,386 ---- 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;
*** 344,353 **** --- 398,410 ---- } throw new InternalError("refKind="+refKind); } static class LambdaFormCodeBuilder extends MethodHandleCodeBuilder { + + private MethodBuilder<Class<?>, String, byte[]> methodBuilder; + public LambdaFormCodeBuilder(LambdaForm form, MethodBuilder<Class<?>, String, byte[]> methodBuilder) { super(methodBuilder); if (form.forceInline) { methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljdk/internal/vm/annotation/ForceInline;"); }
*** 356,362 **** --- 413,600 ---- } Class<?> typeOfLocal(int index) { return typeHelper.symbol(state.locals.get(index)); } + + MethodBuilder<Class<?>, String, byte[]> method() { + return methodBuilder; + } + } + + 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"); + + // this is the beginning of the next loop clause + builder.label("NEXT_" + c); + } + builder.goto_("LOOP"); + + + 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 + builder.load(TypeTag.A, localsMap[clauseDataSlot]) + .ldc(handles - 1) + .aaload() + .ldc(clause) + .aaload(); + + // load loop state (preceding the other arguments) + if (pushLocalState) { + for (int s = 0; s < loopLocalStateTypes.length; ++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 InvokerBytecodeGenerator.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); + + + + // 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]); + } + + void emitStaticInvoke(Name name) { + emitStaticInvoke(name.function.member(), name); + } + + 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; } }