8339642: Reduce overheads in InvokerBytecodeGenerator

Reviewed-by: liach
This commit is contained in:
Claes Redestad 2024-09-06 12:37:48 +00:00
parent cb00333d6a
commit d2b36f0907
2 changed files with 43 additions and 45 deletions

View File

@ -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());
}

View File

@ -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;
}