8164483: Generate field lambda forms at link time

Reviewed-by: vlivanov
This commit is contained in:
Claes Redestad 2016-08-24 16:09:34 +02:00
parent 338343e0e1
commit 5431436909
4 changed files with 167 additions and 29 deletions

@ -492,7 +492,7 @@ class DirectMethodHandle extends MethodHandle {
}
// Caching machinery for field accessors:
private static final byte
static final byte
AF_GETFIELD = 0,
AF_PUTFIELD = 1,
AF_GETSTATIC = 2,
@ -502,7 +502,7 @@ class DirectMethodHandle extends MethodHandle {
AF_LIMIT = 6;
// Enumerate the different field kinds using Wrapper,
// with an extra case added for checked references.
private static final int
static final int
FT_LAST_WRAPPER = Wrapper.COUNT-1,
FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(),
FT_CHECKED_REF = FT_LAST_WRAPPER+1,
@ -515,7 +515,7 @@ class DirectMethodHandle extends MethodHandle {
@Stable
private static final LambdaForm[] ACCESSOR_FORMS
= new LambdaForm[afIndex(AF_LIMIT, false, 0)];
private static int ftypeKind(Class<?> ftype) {
static int ftypeKind(Class<?> ftype) {
if (ftype.isPrimitive())
return Wrapper.forPrimitiveType(ftype).ordinal();
else if (VerifyType.isNullReferenceConversion(Object.class, ftype))
@ -566,7 +566,64 @@ class DirectMethodHandle extends MethodHandle {
private static final Wrapper[] ALL_WRAPPERS = Wrapper.values();
private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
private static Kind getFieldKind(boolean isGetter, boolean isVolatile, Wrapper wrapper) {
if (isGetter) {
if (isVolatile) {
switch (wrapper) {
case BOOLEAN: return GET_BOOLEAN_VOLATILE;
case BYTE: return GET_BYTE_VOLATILE;
case SHORT: return GET_SHORT_VOLATILE;
case CHAR: return GET_CHAR_VOLATILE;
case INT: return GET_INT_VOLATILE;
case LONG: return GET_LONG_VOLATILE;
case FLOAT: return GET_FLOAT_VOLATILE;
case DOUBLE: return GET_DOUBLE_VOLATILE;
case OBJECT: return GET_OBJECT_VOLATILE;
}
} else {
switch (wrapper) {
case BOOLEAN: return GET_BOOLEAN;
case BYTE: return GET_BYTE;
case SHORT: return GET_SHORT;
case CHAR: return GET_CHAR;
case INT: return GET_INT;
case LONG: return GET_LONG;
case FLOAT: return GET_FLOAT;
case DOUBLE: return GET_DOUBLE;
case OBJECT: return GET_OBJECT;
}
}
} else {
if (isVolatile) {
switch (wrapper) {
case BOOLEAN: return PUT_BOOLEAN_VOLATILE;
case BYTE: return PUT_BYTE_VOLATILE;
case SHORT: return PUT_SHORT_VOLATILE;
case CHAR: return PUT_CHAR_VOLATILE;
case INT: return PUT_INT_VOLATILE;
case LONG: return PUT_LONG_VOLATILE;
case FLOAT: return PUT_FLOAT_VOLATILE;
case DOUBLE: return PUT_DOUBLE_VOLATILE;
case OBJECT: return PUT_OBJECT_VOLATILE;
}
} else {
switch (wrapper) {
case BOOLEAN: return PUT_BOOLEAN;
case BYTE: return PUT_BYTE;
case SHORT: return PUT_SHORT;
case CHAR: return PUT_CHAR;
case INT: return PUT_INT;
case LONG: return PUT_LONG;
case FLOAT: return PUT_FLOAT;
case DOUBLE: return PUT_DOUBLE;
case OBJECT: return PUT_OBJECT;
}
}
}
throw new AssertionError("Invalid arguments");
}
static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1);
boolean isStatic = (formOp >= AF_GETSTATIC);
boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
@ -576,24 +633,14 @@ class DirectMethodHandle extends MethodHandle {
assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
// getObject, putIntVolatile, etc.
StringBuilder nameBuilder = new StringBuilder();
if (isGetter) {
nameBuilder.append("get");
} else {
nameBuilder.append("put");
}
nameBuilder.append(fw.primitiveSimpleName());
nameBuilder.setCharAt(3, Character.toUpperCase(nameBuilder.charAt(3)));
if (isVolatile) {
nameBuilder.append("Volatile");
}
Kind kind = getFieldKind(isGetter, isVolatile, fw);
MethodType linkerType;
if (isGetter)
linkerType = MethodType.methodType(ft, Object.class, long.class);
else
linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
MemberName linker = new MemberName(Unsafe.class, nameBuilder.toString(), linkerType, REF_invokeVirtual);
MemberName linker = new MemberName(Unsafe.class, kind.methodName, linkerType, REF_invokeVirtual);
try {
linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
} catch (ReflectiveOperationException ex) {
@ -620,6 +667,7 @@ class DirectMethodHandle extends MethodHandle {
final int F_HOLDER = (isStatic ? nameCursor++ : -1); // static base if any
final int F_OFFSET = nameCursor++; // Either static offset or field offset.
final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
final int U_HOLDER = nameCursor++; // UNSAFE holder
final int INIT_BAR = (needsInit ? nameCursor++ : -1);
final int PRE_CAST = (needsCast && !isGetter ? nameCursor++ : -1);
final int LINKER_CALL = nameCursor++;
@ -632,7 +680,7 @@ class DirectMethodHandle extends MethodHandle {
names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
Object[] outArgs = new Object[1 + linkerType.parameterCount()];
assert(outArgs.length == (isGetter ? 3 : 4));
outArgs[0] = UNSAFE;
outArgs[0] = names[U_HOLDER] = new Name(NF_UNSAFE);
if (isStatic) {
outArgs[1] = names[F_HOLDER] = new Name(NF_staticBase, names[DMH_THIS]);
outArgs[2] = names[F_OFFSET] = new Name(NF_staticOffset, names[DMH_THIS]);
@ -650,6 +698,7 @@ class DirectMethodHandle extends MethodHandle {
for (Name n : names) assert(n != null);
// add some detail to the lambdaForm debugname,
// significant only for debugging
StringBuilder nameBuilder = new StringBuilder(kind.methodName);
if (isStatic) {
nameBuilder.append("Static");
} else {
@ -657,7 +706,12 @@ class DirectMethodHandle extends MethodHandle {
}
if (needsCast) nameBuilder.append("Cast");
if (needsInit) nameBuilder.append("Init");
return new LambdaForm(nameBuilder.toString(), ARG_LIMIT, names, RESULT);
if (needsCast || needsInit) {
// can't use the pre-generated form when casting and/or initializing
return new LambdaForm(nameBuilder.toString(), ARG_LIMIT, names, RESULT);
} else {
return new LambdaForm(nameBuilder.toString(), ARG_LIMIT, names, RESULT, kind);
}
}
/**
@ -674,7 +728,8 @@ class DirectMethodHandle extends MethodHandle {
NF_staticOffset,
NF_checkCast,
NF_allocateInstance,
NF_constructorMethod;
NF_constructorMethod,
NF_UNSAFE;
static {
try {
NamedFunction nfs[] = {
@ -697,7 +752,9 @@ class DirectMethodHandle extends MethodHandle {
NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("allocateInstance", Object.class)),
NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
.getDeclaredMethod("constructorMethod", Object.class))
.getDeclaredMethod("constructorMethod", Object.class)),
NF_UNSAFE = new NamedFunction(new MemberName(MethodHandleStatics.class
.getDeclaredField("UNSAFE")))
};
// Each nf must be statically invocable or we get tied up in our bootstraps.
assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));

@ -28,9 +28,11 @@ package java.lang.invoke;
import java.util.Map;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;
import java.util.ArrayList;
import java.util.HashSet;
import sun.invoke.util.Wrapper;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
/**
* Helper class to assist the GenerateJLIClassesPlugin to get access to
@ -66,14 +68,38 @@ class GenerateJLIClassesHelper {
static byte[] generateDirectMethodHandleHolderClassBytes(String className,
MethodType[] methodTypes, int[] types) {
LambdaForm[] forms = new LambdaForm[methodTypes.length];
String[] names = new String[methodTypes.length];
for (int i = 0; i < forms.length; i++) {
forms[i] = DirectMethodHandle.makePreparedLambdaForm(methodTypes[i],
types[i]);
names[i] = forms[i].kind.defaultLambdaName;
ArrayList<LambdaForm> forms = new ArrayList<>();
ArrayList<String> names = new ArrayList<>();
for (int i = 0; i < methodTypes.length; i++) {
LambdaForm form = DirectMethodHandle
.makePreparedLambdaForm(methodTypes[i], types[i]);
forms.add(form);
names.add(form.kind.defaultLambdaName);
}
return generateCodeBytesForLFs(className, names, forms);
for (Wrapper wrapper : Wrapper.values()) {
if (wrapper == Wrapper.VOID) {
continue;
}
for (byte b = DirectMethodHandle.AF_GETFIELD; b < DirectMethodHandle.AF_LIMIT; b++) {
int ftype = DirectMethodHandle.ftypeKind(wrapper.primitiveType());
LambdaForm form = DirectMethodHandle
.makePreparedFieldLambdaForm(b, /*isVolatile*/false, ftype);
if (form.kind != LambdaForm.Kind.GENERIC) {
forms.add(form);
names.add(form.kind.defaultLambdaName);
}
// volatile
form = DirectMethodHandle
.makePreparedFieldLambdaForm(b, /*isVolatile*/true, ftype);
if (form.kind != LambdaForm.Kind.GENERIC) {
forms.add(form);
names.add(form.kind.defaultLambdaName);
}
}
}
return generateCodeBytesForLFs(className,
names.toArray(new String[0]),
forms.toArray(new LambdaForm[0]));
}
static byte[] generateDelegatingMethodHandleHolderClassBytes(String className,
@ -166,4 +192,5 @@ class GenerateJLIClassesHelper {
BoundMethodHandle.Factory.generateConcreteBMHClassBytes(
shortTypes, types, className));
}
}

@ -629,6 +629,24 @@ class InvokerBytecodeGenerator {
name = name + "_" + form.returnType().basicTypeChar();
return resolveFrom(name, invokerType, LambdaForm.Holder.class);
}
case GET_OBJECT: // fall-through
case GET_BOOLEAN: // fall-through
case GET_BYTE: // fall-through
case GET_CHAR: // fall-through
case GET_SHORT: // fall-through
case GET_INT: // fall-through
case GET_LONG: // fall-through
case GET_FLOAT: // fall-through
case GET_DOUBLE: // fall-through
case PUT_OBJECT: // fall-through
case PUT_BOOLEAN: // fall-through
case PUT_BYTE: // fall-through
case PUT_CHAR: // fall-through
case PUT_SHORT: // fall-through
case PUT_INT: // fall-through
case PUT_LONG: // fall-through
case PUT_FLOAT: // fall-through
case PUT_DOUBLE: // fall-through
case DIRECT_INVOKE_INTERFACE: // fall-through
case DIRECT_INVOKE_SPECIAL: // fall-through
case DIRECT_INVOKE_STATIC: // fall-through

@ -280,7 +280,43 @@ class LambdaForm {
DIRECT_INVOKE_STATIC("DMH.invokeStatic"),
DIRECT_NEW_INVOKE_SPECIAL("DMH.newInvokeSpecial"),
DIRECT_INVOKE_INTERFACE("DMH.invokeInterface"),
DIRECT_INVOKE_STATIC_INIT("DMH.invokeStaticInit");
DIRECT_INVOKE_STATIC_INIT("DMH.invokeStaticInit"),
GET_OBJECT("getObject"),
PUT_OBJECT("putObject"),
GET_OBJECT_VOLATILE("getObjectVolatile"),
PUT_OBJECT_VOLATILE("putObjectVolatile"),
GET_INT("getInt"),
PUT_INT("putInt"),
GET_INT_VOLATILE("getIntVolatile"),
PUT_INT_VOLATILE("putIntVolatile"),
GET_BOOLEAN("getBoolean"),
PUT_BOOLEAN("putBoolean"),
GET_BOOLEAN_VOLATILE("getBooleanVolatile"),
PUT_BOOLEAN_VOLATILE("putBooleanVolatile"),
GET_BYTE("getByte"),
PUT_BYTE("putByte"),
GET_BYTE_VOLATILE("getByteVolatile"),
PUT_BYTE_VOLATILE("putByteVolatile"),
GET_CHAR("getChar"),
PUT_CHAR("putChar"),
GET_CHAR_VOLATILE("getCharVolatile"),
PUT_CHAR_VOLATILE("putCharVolatile"),
GET_SHORT("getShort"),
PUT_SHORT("putShort"),
GET_SHORT_VOLATILE("getShortVolatile"),
PUT_SHORT_VOLATILE("putShortVolatile"),
GET_LONG("getLong"),
PUT_LONG("putLong"),
GET_LONG_VOLATILE("getLongVolatile"),
PUT_LONG_VOLATILE("putLongVolatile"),
GET_FLOAT("getFloat"),
PUT_FLOAT("putFloat"),
GET_FLOAT_VOLATILE("getFloatVolatile"),
PUT_FLOAT_VOLATILE("putFloatVolatile"),
GET_DOUBLE("getDouble"),
PUT_DOUBLE("putDouble"),
GET_DOUBLE_VOLATILE("getDoubleVolatile"),
PUT_DOUBLE_VOLATILE("putDoubleVolatile");
final String defaultLambdaName;
final String methodName;