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,23 +23,23 @@
* 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 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,12 +48,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 +63,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;
@@ -118,10 +123,56 @@
// 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,11 +309,10 @@
/**
* 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);
@@ -323,10 +373,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;
@@ -344,10 +398,13 @@
}
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,7 +413,188 @@
}
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;
}
}