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