mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8338906: Avoid passing EnumDescs and extra classes to type switch methods that don't use them
Reviewed-by: liach, jlahoda
This commit is contained in:
parent
e63418ee01
commit
3f00da84b3
1 changed files with 85 additions and 51 deletions
|
@ -43,13 +43,14 @@ import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Stream;
|
|
||||||
import jdk.internal.access.SharedSecrets;
|
import jdk.internal.access.SharedSecrets;
|
||||||
import java.lang.classfile.ClassFile;
|
import java.lang.classfile.ClassFile;
|
||||||
import java.lang.classfile.Label;
|
import java.lang.classfile.Label;
|
||||||
import java.lang.classfile.instruction.SwitchCase;
|
import java.lang.classfile.instruction.SwitchCase;
|
||||||
|
|
||||||
import jdk.internal.constant.ConstantUtils;
|
import jdk.internal.constant.ConstantUtils;
|
||||||
|
import jdk.internal.constant.MethodTypeDescImpl;
|
||||||
import jdk.internal.constant.ReferenceClassDescImpl;
|
import jdk.internal.constant.ReferenceClassDescImpl;
|
||||||
import jdk.internal.misc.PreviewFeatures;
|
import jdk.internal.misc.PreviewFeatures;
|
||||||
import jdk.internal.vm.annotation.Stable;
|
import jdk.internal.vm.annotation.Stable;
|
||||||
|
@ -81,19 +82,27 @@ public class SwitchBootstraps {
|
||||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||||
private static final boolean previewEnabled = PreviewFeatures.isEnabled();
|
private static final boolean previewEnabled = PreviewFeatures.isEnabled();
|
||||||
|
|
||||||
|
private static final ClassDesc CD_BiPredicate = ReferenceClassDescImpl.ofValidated("Ljava/util/function/BiPredicate;");
|
||||||
|
private static final ClassDesc CD_Objects = ReferenceClassDescImpl.ofValidated("Ljava/util/Objects;");
|
||||||
|
|
||||||
private static final MethodType TYPES_SWITCH_TYPE = MethodType.methodType(int.class,
|
private static final MethodTypeDesc CHECK_INDEX_DESCRIPTOR =
|
||||||
|
MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int, ConstantDescs.CD_int, ConstantDescs.CD_int);
|
||||||
|
private static final MethodTypeDesc MTD_TYPE_SWITCH = MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int,
|
||||||
|
ConstantDescs.CD_Object,
|
||||||
|
ConstantDescs.CD_int);
|
||||||
|
private static final MethodTypeDesc MTD_TYPE_SWITCH_EXTRA = MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int,
|
||||||
|
ConstantDescs.CD_Object,
|
||||||
|
ConstantDescs.CD_int,
|
||||||
|
CD_BiPredicate,
|
||||||
|
ConstantDescs.CD_List);
|
||||||
|
private static final MethodType MT_TYPE_SWITCH_EXTRA = MethodType.methodType(int.class,
|
||||||
Object.class,
|
Object.class,
|
||||||
int.class,
|
int.class,
|
||||||
BiPredicate.class,
|
BiPredicate.class,
|
||||||
List.class);
|
List.class);
|
||||||
|
private static final MethodType MT_TYPE_SWITCH = MethodType.methodType(int.class,
|
||||||
private static final MethodTypeDesc TYPES_SWITCH_DESCRIPTOR =
|
Object.class,
|
||||||
MethodTypeDesc.ofDescriptor("(Ljava/lang/Object;ILjava/util/function/BiPredicate;Ljava/util/List;)I");
|
int.class);
|
||||||
private static final MethodTypeDesc CHECK_INDEX_DESCRIPTOR =
|
|
||||||
MethodTypeDesc.ofDescriptor("(II)I");
|
|
||||||
|
|
||||||
private static final ClassDesc CD_Objects = ReferenceClassDescImpl.ofValidated("Ljava/util/Objects;");
|
|
||||||
|
|
||||||
private static class StaticHolders {
|
private static class StaticHolders {
|
||||||
private static final MethodHandle MAPPED_ENUM_SWITCH;
|
private static final MethodHandle MAPPED_ENUM_SWITCH;
|
||||||
|
@ -180,7 +189,7 @@ public class SwitchBootstraps {
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodHandle target = generateTypeSwitch(lookup, selectorType, labels);
|
MethodHandle target = generateTypeSwitch(lookup, selectorType, labels);
|
||||||
|
target = target.asType(invocationType);
|
||||||
return new ConstantCallSite(target);
|
return new ConstantCallSite(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,9 +281,8 @@ public class SwitchBootstraps {
|
||||||
|| !invocationType.parameterType(0).isEnum()
|
|| !invocationType.parameterType(0).isEnum()
|
||||||
|| !invocationType.parameterType(1).equals(int.class))
|
|| !invocationType.parameterType(1).equals(int.class))
|
||||||
throw new IllegalArgumentException("Illegal invocation type " + invocationType);
|
throw new IllegalArgumentException("Illegal invocation type " + invocationType);
|
||||||
requireNonNull(labels);
|
|
||||||
|
|
||||||
labels = labels.clone();
|
labels = labels.clone(); // implicit null check
|
||||||
|
|
||||||
Class<?> enumClass = invocationType.parameterType(0);
|
Class<?> enumClass = invocationType.parameterType(0);
|
||||||
boolean constantsOnly = true;
|
boolean constantsOnly = true;
|
||||||
|
@ -301,7 +309,6 @@ public class SwitchBootstraps {
|
||||||
} else {
|
} else {
|
||||||
target = generateTypeSwitch(lookup, invocationType.parameterType(0), labels);
|
target = generateTypeSwitch(lookup, invocationType.parameterType(0), labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
target = target.asType(invocationType);
|
target = target.asType(invocationType);
|
||||||
|
|
||||||
return new ConstantCallSite(target);
|
return new ConstantCallSite(target);
|
||||||
|
@ -434,6 +441,33 @@ public class SwitchBootstraps {
|
||||||
public MethodHandle generatedSwitch;
|
public MethodHandle generatedSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the labelConstants can be converted statically to bytecode, or
|
||||||
|
* whether we'll need to compute and pass in extra information at the call site.
|
||||||
|
*/
|
||||||
|
private static boolean needsExtraInfo(Class<?> selectorType, Object[] labelConstants) {
|
||||||
|
for (int idx = labelConstants.length - 1; idx >= 0; idx--) {
|
||||||
|
Object currentLabel = labelConstants[idx];
|
||||||
|
if (currentLabel instanceof Class<?> classLabel) {
|
||||||
|
// No extra info needed for exact matches or primitives
|
||||||
|
if (unconditionalExactnessCheck(selectorType, classLabel) || classLabel.isPrimitive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Hidden classes - or arrays thereof - can't be nominally
|
||||||
|
// represented. Passed in as arguments.
|
||||||
|
while (classLabel.isArray()) {
|
||||||
|
classLabel = classLabel.getComponentType();
|
||||||
|
}
|
||||||
|
if (classLabel.isHidden()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (currentLabel instanceof EnumDesc<?>) {
|
||||||
|
// EnumDescs labels needs late binding
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Construct test chains for labels inside switch, to handle switch repeats:
|
* Construct test chains for labels inside switch, to handle switch repeats:
|
||||||
* switch (idx) {
|
* switch (idx) {
|
||||||
|
@ -467,9 +501,10 @@ public class SwitchBootstraps {
|
||||||
}
|
}
|
||||||
cb.iload(RESTART_IDX);
|
cb.iload(RESTART_IDX);
|
||||||
Label dflt = cb.newLabel();
|
Label dflt = cb.newLabel();
|
||||||
record Element(Label target, Label next, Object caseLabel) { }
|
Label[] caseTargets = new Label[labelConstants.length];
|
||||||
List<Element> cases = new ArrayList<>();
|
Label[] caseNext = new Label[labelConstants.length];
|
||||||
List<SwitchCase> switchCases = new ArrayList<>();
|
Object[] caseLabels = new Object[labelConstants.length];
|
||||||
|
SwitchCase[] switchCases = new SwitchCase[labelConstants.length];
|
||||||
Object lastLabel = null;
|
Object lastLabel = null;
|
||||||
for (int idx = labelConstants.length - 1; idx >= 0; idx--) {
|
for (int idx = labelConstants.length - 1; idx >= 0; idx--) {
|
||||||
Object currentLabel = labelConstants[idx];
|
Object currentLabel = labelConstants[idx];
|
||||||
|
@ -478,22 +513,22 @@ public class SwitchBootstraps {
|
||||||
if (lastLabel == null) {
|
if (lastLabel == null) {
|
||||||
next = dflt;
|
next = dflt;
|
||||||
} else if (lastLabel.equals(currentLabel)) {
|
} else if (lastLabel.equals(currentLabel)) {
|
||||||
next = cases.getLast().next();
|
next = caseNext[idx + 1];
|
||||||
} else {
|
} else {
|
||||||
next = cases.getLast().target();
|
next = caseTargets[idx + 1];
|
||||||
}
|
}
|
||||||
lastLabel = currentLabel;
|
lastLabel = currentLabel;
|
||||||
cases.add(new Element(target, next, currentLabel));
|
caseTargets[idx] = target;
|
||||||
switchCases.add(SwitchCase.of(idx, target));
|
caseNext[idx] = next;
|
||||||
|
caseLabels[idx] = currentLabel;
|
||||||
|
switchCases[idx] = SwitchCase.of(idx, target);
|
||||||
}
|
}
|
||||||
cases = cases.reversed();
|
cb.tableswitch(0, labelConstants.length - 1, dflt, Arrays.asList(switchCases));
|
||||||
switchCases = switchCases.reversed();
|
for (int idx = 0; idx < labelConstants.length; idx++) {
|
||||||
cb.tableswitch(0, labelConstants.length - 1, dflt, switchCases);
|
Label next = caseNext[idx];
|
||||||
for (int idx = 0; idx < cases.size(); idx++) {
|
Object caseLabel = caseLabels[idx];
|
||||||
Element element = cases.get(idx);
|
cb.labelBinding(caseTargets[idx]);
|
||||||
Label next = element.next();
|
if (caseLabel instanceof Class<?> classLabel) {
|
||||||
cb.labelBinding(element.target());
|
|
||||||
if (element.caseLabel() instanceof Class<?> classLabel) {
|
|
||||||
if (unconditionalExactnessCheck(selectorType, classLabel)) {
|
if (unconditionalExactnessCheck(selectorType, classLabel)) {
|
||||||
//nothing - unconditionally use this case
|
//nothing - unconditionally use this case
|
||||||
} else if (classLabel.isPrimitive()) {
|
} else if (classLabel.isPrimitive()) {
|
||||||
|
@ -577,7 +612,7 @@ public class SwitchBootstraps {
|
||||||
extraClassLabels.add(classLabel);
|
extraClassLabels.add(classLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (element.caseLabel() instanceof EnumDesc<?> enumLabel) {
|
} else if (caseLabel instanceof EnumDesc<?> enumLabel) {
|
||||||
int enumIdx = enumDescs.size();
|
int enumIdx = enumDescs.size();
|
||||||
enumDescs.add(enumLabel);
|
enumDescs.add(enumLabel);
|
||||||
cb.aload(ENUM_CACHE);
|
cb.aload(ENUM_CACHE);
|
||||||
|
@ -587,13 +622,13 @@ public class SwitchBootstraps {
|
||||||
MethodTypeDesc.of(ConstantDescs.CD_Integer,
|
MethodTypeDesc.of(ConstantDescs.CD_Integer,
|
||||||
ConstantDescs.CD_int));
|
ConstantDescs.CD_int));
|
||||||
cb.aload(SELECTOR_OBJ);
|
cb.aload(SELECTOR_OBJ);
|
||||||
cb.invokeinterface(referenceClassDesc(BiPredicate.class),
|
cb.invokeinterface(CD_BiPredicate,
|
||||||
"test",
|
"test",
|
||||||
MethodTypeDesc.of(ConstantDescs.CD_boolean,
|
MethodTypeDesc.of(ConstantDescs.CD_boolean,
|
||||||
ConstantDescs.CD_Object,
|
ConstantDescs.CD_Object,
|
||||||
ConstantDescs.CD_Object));
|
ConstantDescs.CD_Object));
|
||||||
cb.ifeq(next);
|
cb.ifeq(next);
|
||||||
} else if (element.caseLabel() instanceof String stringLabel) {
|
} else if (caseLabel instanceof String stringLabel) {
|
||||||
cb.ldc(stringLabel);
|
cb.ldc(stringLabel);
|
||||||
cb.aload(SELECTOR_OBJ);
|
cb.aload(SELECTOR_OBJ);
|
||||||
cb.invokevirtual(ConstantDescs.CD_Object,
|
cb.invokevirtual(ConstantDescs.CD_Object,
|
||||||
|
@ -601,7 +636,7 @@ public class SwitchBootstraps {
|
||||||
MethodTypeDesc.of(ConstantDescs.CD_boolean,
|
MethodTypeDesc.of(ConstantDescs.CD_boolean,
|
||||||
ConstantDescs.CD_Object));
|
ConstantDescs.CD_Object));
|
||||||
cb.ifeq(next);
|
cb.ifeq(next);
|
||||||
} else if (element.caseLabel() instanceof Integer integerLabel) {
|
} else if (caseLabel instanceof Integer integerLabel) {
|
||||||
Label compare = cb.newLabel();
|
Label compare = cb.newLabel();
|
||||||
Label notNumber = cb.newLabel();
|
Label notNumber = cb.newLabel();
|
||||||
cb.aload(SELECTOR_OBJ);
|
cb.aload(SELECTOR_OBJ);
|
||||||
|
@ -626,16 +661,16 @@ public class SwitchBootstraps {
|
||||||
|
|
||||||
cb.ldc(integerLabel);
|
cb.ldc(integerLabel);
|
||||||
cb.if_icmpne(next);
|
cb.if_icmpne(next);
|
||||||
} else if ((element.caseLabel() instanceof Long ||
|
} else if ((caseLabel instanceof Long ||
|
||||||
element.caseLabel() instanceof Float ||
|
caseLabel instanceof Float ||
|
||||||
element.caseLabel() instanceof Double ||
|
caseLabel instanceof Double ||
|
||||||
element.caseLabel() instanceof Boolean)) {
|
caseLabel instanceof Boolean)) {
|
||||||
if (element.caseLabel() instanceof Boolean c) {
|
if (caseLabel instanceof Boolean c) {
|
||||||
cb.loadConstant(c ? 1 : 0);
|
cb.loadConstant(c ? 1 : 0);
|
||||||
} else {
|
} else {
|
||||||
cb.loadConstant((ConstantDesc) element.caseLabel());
|
cb.loadConstant((ConstantDesc) caseLabel);
|
||||||
}
|
}
|
||||||
var caseLabelWrapper = Wrapper.forWrapperType(element.caseLabel().getClass());
|
var caseLabelWrapper = Wrapper.forWrapperType(caseLabel.getClass());
|
||||||
cb.invokestatic(caseLabelWrapper.wrapperClassDescriptor(),
|
cb.invokestatic(caseLabelWrapper.wrapperClassDescriptor(),
|
||||||
"valueOf",
|
"valueOf",
|
||||||
MethodTypeDesc.of(caseLabelWrapper.wrapperClassDescriptor(),
|
MethodTypeDesc.of(caseLabelWrapper.wrapperClassDescriptor(),
|
||||||
|
@ -648,13 +683,13 @@ public class SwitchBootstraps {
|
||||||
cb.ifeq(next);
|
cb.ifeq(next);
|
||||||
} else {
|
} else {
|
||||||
throw new InternalError("Unsupported label type: " +
|
throw new InternalError("Unsupported label type: " +
|
||||||
element.caseLabel().getClass());
|
caseLabel.getClass());
|
||||||
}
|
}
|
||||||
cb.loadConstant(idx);
|
cb.loadConstant(idx);
|
||||||
cb.ireturn();
|
cb.ireturn();
|
||||||
}
|
}
|
||||||
cb.labelBinding(dflt);
|
cb.labelBinding(dflt);
|
||||||
cb.loadConstant(cases.size());
|
cb.loadConstant(labelConstants.length);
|
||||||
cb.ireturn();
|
cb.ireturn();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -663,14 +698,15 @@ public class SwitchBootstraps {
|
||||||
* Construct the method handle that represents the method int typeSwitch(Object, int, BiPredicate, List)
|
* Construct the method handle that represents the method int typeSwitch(Object, int, BiPredicate, List)
|
||||||
*/
|
*/
|
||||||
private static MethodHandle generateTypeSwitch(MethodHandles.Lookup caller, Class<?> selectorType, Object[] labelConstants) {
|
private static MethodHandle generateTypeSwitch(MethodHandles.Lookup caller, Class<?> selectorType, Object[] labelConstants) {
|
||||||
List<EnumDesc<?>> enumDescs = new ArrayList<>();
|
boolean addExtraInfo = needsExtraInfo(selectorType, labelConstants);
|
||||||
List<Class<?>> extraClassLabels = new ArrayList<>();
|
List<EnumDesc<?>> enumDescs = addExtraInfo ? new ArrayList<>() : null;
|
||||||
|
List<Class<?>> extraClassLabels = addExtraInfo ? new ArrayList<>() : null;
|
||||||
|
|
||||||
byte[] classBytes = ClassFile.of().build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())),
|
byte[] classBytes = ClassFile.of().build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())),
|
||||||
clb -> {
|
clb -> {
|
||||||
clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
|
clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
|
||||||
.withMethodBody("typeSwitch",
|
.withMethodBody("typeSwitch",
|
||||||
TYPES_SWITCH_DESCRIPTOR,
|
addExtraInfo ? MTD_TYPE_SWITCH_EXTRA : MTD_TYPE_SWITCH,
|
||||||
ClassFile.ACC_FINAL | ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
|
ClassFile.ACC_FINAL | ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
|
||||||
generateTypeSwitchSkeleton(selectorType, labelConstants, enumDescs, extraClassLabels));
|
generateTypeSwitchSkeleton(selectorType, labelConstants, enumDescs, extraClassLabels));
|
||||||
});
|
});
|
||||||
|
@ -681,13 +717,11 @@ public class SwitchBootstraps {
|
||||||
lookup = caller.defineHiddenClass(classBytes, true, NESTMATE, STRONG);
|
lookup = caller.defineHiddenClass(classBytes, true, NESTMATE, STRONG);
|
||||||
MethodHandle typeSwitch = lookup.findStatic(lookup.lookupClass(),
|
MethodHandle typeSwitch = lookup.findStatic(lookup.lookupClass(),
|
||||||
"typeSwitch",
|
"typeSwitch",
|
||||||
TYPES_SWITCH_TYPE);
|
addExtraInfo ? MT_TYPE_SWITCH_EXTRA : MT_TYPE_SWITCH);
|
||||||
typeSwitch = MethodHandles.insertArguments(typeSwitch, 2, new ResolvedEnumLabels(caller, enumDescs.toArray(new EnumDesc<?>[0])),
|
if (addExtraInfo) {
|
||||||
List.copyOf(extraClassLabels));
|
typeSwitch = MethodHandles.insertArguments(typeSwitch, 2, new ResolvedEnumLabels(caller, enumDescs.toArray(new EnumDesc<?>[0])),
|
||||||
typeSwitch = MethodHandles.explicitCastArguments(typeSwitch,
|
List.copyOf(extraClassLabels));
|
||||||
MethodType.methodType(int.class,
|
}
|
||||||
selectorType,
|
|
||||||
int.class));
|
|
||||||
return typeSwitch;
|
return typeSwitch;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
throw new IllegalArgumentException(t);
|
throw new IllegalArgumentException(t);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue