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:
Claes Redestad 2024-08-26 15:58:25 +00:00
parent e63418ee01
commit 3f00da84b3

View file

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