8339642: Reduce overheads in InvokerBytecodeGenerator
Reviewed-by: liach
This commit is contained in:
parent
cb00333d6a
commit
d2b36f0907
@ -57,6 +57,8 @@ import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
import static jdk.internal.constant.ConstantUtils.concat;
|
||||
import static jdk.internal.constant.ConstantUtils.validateInternalClassName;
|
||||
|
||||
/**
|
||||
* Code generation backend for LambdaForm.
|
||||
@ -67,6 +69,7 @@ class InvokerBytecodeGenerator {
|
||||
/** Define class names for convenience. */
|
||||
private static final ClassDesc CD_CasesHolder = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl$CasesHolder;");
|
||||
private static final ClassDesc CD_DirectMethodHandle = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/DirectMethodHandle;");
|
||||
private static final ClassDesc CD_MemberName = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MemberName;");
|
||||
private static final ClassDesc CD_MethodHandleImpl = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl;");
|
||||
private static final ClassDesc CD_LambdaForm = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm;");
|
||||
private static final ClassDesc CD_LambdaForm_Name = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm$Name;");
|
||||
@ -126,7 +129,8 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
this.name = name;
|
||||
this.className = CLASS_PREFIX.concat(name);
|
||||
this.classDesc = ClassDesc.ofInternalName(className);
|
||||
validateInternalClassName(name);
|
||||
this.classDesc = ReferenceClassDescImpl.ofValidated(concat("L", className, ";"));
|
||||
this.lambdaForm = lambdaForm;
|
||||
this.invokerName = invokerName;
|
||||
this.invokerType = invokerType;
|
||||
@ -142,8 +146,8 @@ class InvokerBytecodeGenerator {
|
||||
// Create an array to map name indexes to locals indexes.
|
||||
localsMap[0] = 0; // localsMap has at least one element
|
||||
for (int i = 1, index = 0; i < localsMap.length; i++) {
|
||||
Wrapper w = Wrapper.forBasicType(mt.parameterType(i - 1));
|
||||
index += w.stackSlots();
|
||||
Class<?> cl = mt.parameterType(i - 1);
|
||||
index += (cl == long.class || cl == double.class) ? 2 : 1;
|
||||
localsMap[i] = index;
|
||||
}
|
||||
}
|
||||
@ -223,6 +227,7 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
// unique static variable name
|
||||
String name;
|
||||
List<ClassData> classData = this.classData;
|
||||
if (dumper().isEnabled()) {
|
||||
Class<?> c = arg.getClass();
|
||||
while (c.isArray()) {
|
||||
@ -232,8 +237,7 @@ class InvokerBytecodeGenerator {
|
||||
} else {
|
||||
name = "_D_" + classData.size();
|
||||
}
|
||||
ClassData cd = new ClassData(name, desc, arg);
|
||||
classData.add(cd);
|
||||
classData.add(new ClassData(name, desc, arg));
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -292,15 +296,16 @@ class InvokerBytecodeGenerator {
|
||||
*/
|
||||
private Object classDataValues() {
|
||||
final List<ClassData> cd = classData;
|
||||
return switch (cd.size()) {
|
||||
int size = cd.size();
|
||||
return switch (size) {
|
||||
case 0 -> null; // special case (classData is not used by <clinit>)
|
||||
case 1 -> cd.get(0).value; // special case (single object)
|
||||
case 2 -> List.of(cd.get(0).value, cd.get(1).value);
|
||||
case 3 -> List.of(cd.get(0).value, cd.get(1).value, cd.get(2).value);
|
||||
case 4 -> List.of(cd.get(0).value, cd.get(1).value, cd.get(2).value, cd.get(3).value);
|
||||
default -> {
|
||||
Object[] data = new Object[classData.size()];
|
||||
for (int i = 0; i < classData.size(); i++) {
|
||||
Object[] data = new Object[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
data[i] = classData.get(i).value;
|
||||
}
|
||||
yield List.of(data);
|
||||
@ -316,18 +321,17 @@ class InvokerBytecodeGenerator {
|
||||
if (classData.isEmpty())
|
||||
return;
|
||||
|
||||
for (ClassData p : classData) {
|
||||
// add the static field
|
||||
clb.withField(p.name, p.desc, ACC_STATIC | ACC_FINAL);
|
||||
}
|
||||
|
||||
clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<>() {
|
||||
@Override
|
||||
public void accept(CodeBuilder cob) {
|
||||
cob.loadConstant(classDesc)
|
||||
.invokestatic(CD_MethodHandles, "classData", MTD_Object_Class);
|
||||
if (classData.size() == 1) {
|
||||
int size = classData.size();
|
||||
if (size == 1) {
|
||||
ClassData p = classData.get(0);
|
||||
// add the static field
|
||||
clb.withField(p.name, p.desc, ACC_STATIC | ACC_FINAL);
|
||||
|
||||
cob.checkcast(p.desc)
|
||||
.putstatic(classDesc, p.name, p.desc);
|
||||
} else {
|
||||
@ -335,7 +339,10 @@ class InvokerBytecodeGenerator {
|
||||
.astore(0);
|
||||
int index = 0;
|
||||
var listGet = cob.constantPool().interfaceMethodRefEntry(CD_List, "get", MTD_Object_int);
|
||||
for (ClassData p : classData) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
ClassData p = classData.get(i);
|
||||
// add the static field
|
||||
clb.withField(p.name, p.desc, ACC_STATIC | ACC_FINAL);
|
||||
// initialize the static field
|
||||
cob.aload(0)
|
||||
.loadConstant(index++)
|
||||
@ -538,6 +545,11 @@ class InvokerBytecodeGenerator {
|
||||
static final Annotation INJECTEDPROFILE = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/InjectedProfile;"));
|
||||
static final Annotation LF_COMPILED = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm$Compiled;"));
|
||||
|
||||
// Suppress method in backtraces displayed to the user, mark this method as
|
||||
// a compiled LambdaForm, then either force or prohibit inlining.
|
||||
public static final RuntimeVisibleAnnotationsAttribute LF_DONTINLINE_ANNOTATIONS = RuntimeVisibleAnnotationsAttribute.of(HIDDEN, LF_COMPILED, DONTINLINE);
|
||||
public static final RuntimeVisibleAnnotationsAttribute LF_FORCEINLINE_ANNOTATIONS = RuntimeVisibleAnnotationsAttribute.of(HIDDEN, LF_COMPILED, FORCEINLINE);
|
||||
|
||||
/**
|
||||
* Generate an invoker method for the passed {@link LambdaForm}.
|
||||
*/
|
||||
@ -558,21 +570,11 @@ class InvokerBytecodeGenerator {
|
||||
@Override
|
||||
public void accept(MethodBuilder mb) {
|
||||
|
||||
List<Annotation> annotations = new ArrayList<>(3);
|
||||
|
||||
// Suppress this method in backtraces displayed to the user.
|
||||
annotations.add(HIDDEN);
|
||||
|
||||
// Mark this method as a compiled LambdaForm
|
||||
annotations.add(LF_COMPILED);
|
||||
|
||||
if (lambdaForm.forceInline) {
|
||||
// Force inlining of this invoker method.
|
||||
annotations.add(FORCEINLINE);
|
||||
mb.accept(LF_FORCEINLINE_ANNOTATIONS);
|
||||
} else {
|
||||
annotations.add(DONTINLINE);
|
||||
mb.accept(LF_DONTINLINE_ANNOTATIONS);
|
||||
}
|
||||
mb.accept(RuntimeVisibleAnnotationsAttribute.of(annotations));
|
||||
|
||||
classData(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled.
|
||||
|
||||
@ -1674,10 +1676,12 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
static ClassDesc classDesc(Class<?> cls) {
|
||||
// assert(VerifyAccess.isTypeVisible(cls, Object.class)) : cls.getName();
|
||||
return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor()
|
||||
: cls == MethodHandle.class ? CD_MethodHandle
|
||||
return cls == MethodHandle.class ? CD_MethodHandle
|
||||
: cls == DirectMethodHandle.class ? CD_DirectMethodHandle
|
||||
: cls == Object.class ? CD_Object
|
||||
: cls == MemberName.class ? CD_MemberName
|
||||
: cls == MethodType.class ? CD_MethodType
|
||||
: cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor()
|
||||
: ReferenceClassDescImpl.ofValidated(cls.descriptorString());
|
||||
}
|
||||
|
||||
|
@ -1555,6 +1555,7 @@ class LambdaForm {
|
||||
* Return -1 if the name is not used.
|
||||
*/
|
||||
int lastUseIndex(Name n) {
|
||||
Object[] arguments = this.arguments;
|
||||
if (arguments == null) return -1;
|
||||
for (int i = arguments.length; --i >= 0; ) {
|
||||
if (arguments[i] == n) return i;
|
||||
@ -1562,21 +1563,6 @@ class LambdaForm {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Return the number of occurrences of n in the argument array.
|
||||
* Return 0 if the name is not used.
|
||||
*/
|
||||
int useCount(Name n) {
|
||||
int count = 0;
|
||||
if (arguments != null) {
|
||||
for (Object argument : arguments) {
|
||||
if (argument == n) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean equals(Name that) {
|
||||
if (this == that) return true;
|
||||
if (isParam())
|
||||
@ -1618,8 +1604,16 @@ class LambdaForm {
|
||||
int useCount(Name n) {
|
||||
int count = (result == n.index) ? 1 : 0;
|
||||
int i = Math.max(n.index + 1, arity);
|
||||
Name[] names = this.names;
|
||||
while (i < names.length) {
|
||||
count += names[i++].useCount(n);
|
||||
Object[] arguments = names[i++].arguments;
|
||||
if (arguments != null) {
|
||||
for (Object argument : arguments) {
|
||||
if (argument == n) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user