< prev index next >

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

Print this page

        

*** 23,46 **** * 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; import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic; --- 23,50 ---- * 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 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,59 **** --- 52,65 ---- 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 **** --- 67,79 ---- 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,111 **** } LambdaFormCodeBuilder builder; LambdaForm lambdaForm; MethodType invokerType; LambdaFormBuilder(LambdaFormCodeBuilder builder, LambdaForm lambdaForm, MethodType invokerType) { this.builder = builder; this.lambdaForm = lambdaForm; this.invokerType = invokerType; } 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()); } onStack = name; // unless otherwise modified below MemberName member = name.function.member(); ! if (InvokerBytecodeGenerator.isStaticallyInvocable(member)) { emitStaticInvoke(member, name); } else { emitInvoke(name); } } --- 90,182 ---- } 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), 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")); ! // 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,137 **** } 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); } emitImplicitConversion(rtype, rclass, rn); // generate actual return statement --- 198,208 ---- } 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(localsMap[lambdaForm.result]); } emitImplicitConversion(rtype, rclass, rn); // generate actual return statement
*** 177,194 **** 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()))) return; // this cast was already performed if (lambdaForm.useCount(n) > 1) { // This guy gets used more than once. writeBack = n; } } ! if (InvokerBytecodeGenerator.isStaticallyNameable(cls)) { builder.checkcast(cls); } else { builder.ldc(cls) .checkcast(Class.class) .swap() --- 248,265 ---- 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(localsMap[n.index()]))) return; // this cast was already performed if (lambdaForm.useCount(n) > 1) { // This guy gets used more than once. writeBack = n; } } ! if (isStaticallyNameable(cls)) { builder.checkcast(cls); } else { builder.ldc(cls) .checkcast(Class.class) .swap()
*** 197,210 **** builder.checkcast(Object[].class); else if (PROFILE_LEVEL > 0) builder.checkcast(Object.class); } if (writeBack != null) { ! builder.dup().astore(writeBack.index()); } } /** * Emit an invoke for the given name, using the MemberName directly. */ void emitStaticInvoke(MemberName member, Name name) { assert(member.equals(name.function.member())); --- 268,285 ---- builder.checkcast(Object[].class); else if (PROFILE_LEVEL > 0) builder.checkcast(Object.class); } if (writeBack != null) { ! 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,288 **** 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()); 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) { --- 353,363 ---- private void emitPushArgument(Class<?> ptype, Object arg) { BasicType bptype = basicType(ptype); if (arg instanceof Name) { Name n = (Name) arg; ! 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,315 **** --- 381,394 ---- 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,345 **** --- 419,609 ---- 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 >