Skip to content

Commit bd1c53e

Browse files
committed
8354899: Reduce overhead associated with type switches
Reviewed-by: asotona
1 parent ffe6a4f commit bd1c53e

File tree

2 files changed

+45
-13
lines changed

2 files changed

+45
-13
lines changed

src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
import java.lang.Enum.EnumDesc;
2929
import java.lang.classfile.CodeBuilder;
30+
import java.lang.classfile.attribute.StackMapFrameInfo;
31+
import java.lang.classfile.attribute.StackMapTableAttribute;
3032
import java.lang.constant.ClassDesc;
3133
import java.lang.constant.ConstantDesc;
3234
import java.lang.constant.MethodTypeDesc;
@@ -48,6 +50,7 @@
4850
import java.lang.classfile.Label;
4951
import java.lang.classfile.instruction.SwitchCase;
5052

53+
import jdk.internal.classfile.impl.DirectCodeBuilder;
5154
import jdk.internal.constant.ClassOrInterfaceDescImpl;
5255
import jdk.internal.constant.ConstantUtils;
5356
import jdk.internal.constant.MethodTypeDescImpl;
@@ -103,6 +106,13 @@ private SwitchBootstraps() {}
103106
private static final MethodType MT_TYPE_SWITCH = MethodType.methodType(int.class,
104107
Object.class,
105108
int.class);
109+
private static final List<StackMapFrameInfo.VerificationTypeInfo> TYPE_SWITCH_LOCALS = List.of(
110+
StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_Object), StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER
111+
);
112+
private static final List<StackMapFrameInfo.VerificationTypeInfo> TYPE_SWITCH_EXTRA_LOCALS = List.of(
113+
StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_Object), StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER,
114+
StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_BiPredicate), StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_List)
115+
);
106116

107117
private static class StaticHolders {
108118
private static final MethodHandle MAPPED_ENUM_SWITCH;
@@ -482,8 +492,11 @@ private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selecto
482492
int ENUM_CACHE = 2;
483493
int EXTRA_CLASS_LABELS = 3;
484494

495+
var locals = enumDescs == null && extraClassLabels == null ? TYPE_SWITCH_LOCALS : TYPE_SWITCH_EXTRA_LOCALS;
496+
485497
return cb -> {
486498
// Objects.checkIndex(RESTART_IDX, labelConstants + 1)
499+
var stackMapFrames = new ArrayList<StackMapFrameInfo>(labelConstants.length * 2);
487500
cb.iload(RESTART_IDX)
488501
.loadConstant(labelConstants.length + 1)
489502
.invokestatic(CD_Objects, "checkIndex", CHECK_INDEX_DESCRIPTOR)
@@ -494,9 +507,12 @@ private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selecto
494507
.iconst_m1()
495508
.ireturn()
496509
.labelBinding(nonNullLabel);
510+
stackMapFrames.add(StackMapFrameInfo.of(nonNullLabel, locals, List.of()));
497511
if (labelConstants.length == 0) {
498512
cb.loadConstant(0)
499-
.ireturn();
513+
.ireturn()
514+
.with(StackMapTableAttribute.of(stackMapFrames));
515+
DirectCodeBuilder.withMaxs(cb, 2, locals.size()); // checkIndex uses 2
500516
return;
501517
}
502518
cb.iload(RESTART_IDX);
@@ -509,6 +525,7 @@ private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selecto
509525
for (int idx = labelConstants.length - 1; idx >= 0; idx--) {
510526
Object currentLabel = labelConstants[idx];
511527
Label target = cb.newLabel();
528+
stackMapFrames.add(StackMapFrameInfo.of(target, locals, List.of()));
512529
Label next;
513530
if (lastLabel == null) {
514531
next = dflt;
@@ -541,7 +558,7 @@ private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selecto
541558
} else if (!unconditionalExactnessCheck(Wrapper.asPrimitiveType(selectorType), classLabel)) {
542559
// Integer i = ... or int i = ...
543560
// o instanceof float
544-
Label notNumber = cb.newLabel();
561+
Label notNumber = cb.newLabel(); // this label may end up unbound
545562
cb.aload(SELECTOR_OBJ)
546563
.instanceOf(CD_Number);
547564
if (selectorType == long.class || selectorType == float.class || selectorType == double.class ||
@@ -570,8 +587,9 @@ private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selecto
570587
"intValue",
571588
MethodTypeDesc.of(CD_int))
572589
.goto_(compare)
573-
.labelBinding(notNumber)
574-
.aload(SELECTOR_OBJ)
590+
.labelBinding(notNumber);
591+
stackMapFrames.add(StackMapFrameInfo.of(notNumber, locals, List.of()));
592+
cb.aload(SELECTOR_OBJ)
575593
.instanceOf(CD_Character)
576594
.ifeq(next)
577595
.aload(SELECTOR_OBJ)
@@ -580,6 +598,7 @@ private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selecto
580598
"charValue",
581599
MethodTypeDesc.of(CD_char))
582600
.labelBinding(compare);
601+
stackMapFrames.add(StackMapFrameInfo.of(compare, locals, List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER)));
583602
}
584603

585604
TypePairs typePair = TypePairs.of(Wrapper.asPrimitiveType(selectorType), classLabel);
@@ -648,18 +667,19 @@ private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selecto
648667
"intValue",
649668
MethodTypeDesc.of(CD_int))
650669
.goto_(compare)
651-
.labelBinding(notNumber)
652-
.aload(SELECTOR_OBJ)
670+
.labelBinding(notNumber);
671+
stackMapFrames.add(StackMapFrameInfo.of(notNumber, locals, List.of()));
672+
cb.aload(SELECTOR_OBJ)
653673
.instanceOf(CD_Character)
654674
.ifeq(next)
655675
.aload(SELECTOR_OBJ)
656676
.checkcast(CD_Character)
657677
.invokevirtual(CD_Character,
658678
"charValue",
659679
MethodTypeDesc.of(CD_char))
660-
.labelBinding(compare)
661-
662-
.loadConstant(integerLabel)
680+
.labelBinding(compare);
681+
stackMapFrames.add(StackMapFrameInfo.of(compare, locals, List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER)));
682+
cb.loadConstant(integerLabel)
663683
.if_icmpne(next);
664684
} else if ((caseLabel instanceof Long ||
665685
caseLabel instanceof Float ||
@@ -688,9 +708,12 @@ private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selecto
688708
cb.loadConstant(idx)
689709
.ireturn();
690710
}
711+
stackMapFrames.add(StackMapFrameInfo.of(dflt, locals, List.of()));
691712
cb.labelBinding(dflt)
692713
.loadConstant(labelConstants.length)
693-
.ireturn();
714+
.ireturn()
715+
.with(StackMapTableAttribute.of(stackMapFrames));
716+
DirectCodeBuilder.withMaxs(cb, 3, locals.size()); // enum labels use 3 stack, others use 2
694717
};
695718
}
696719

@@ -702,7 +725,7 @@ private static MethodHandle generateTypeSwitch(MethodHandles.Lookup caller, Clas
702725
List<EnumDesc<?>> enumDescs = addExtraInfo ? new ArrayList<>() : null;
703726
List<Class<?>> extraClassLabels = addExtraInfo ? new ArrayList<>() : null;
704727

705-
byte[] classBytes = ClassFile.of().build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())),
728+
byte[] classBytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())),
706729
clb -> {
707730
clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
708731
.withMethodBody("typeSwitch",

src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ public final class DirectCodeBuilder
4848
extends AbstractDirectBuilder<CodeModel>
4949
implements TerminalCodeBuilder {
5050
private static final CharacterRange[] EMPTY_CHARACTER_RANGE = {};
51-
private static final DeferredLabel[] EMPTY_LABEL_ARRAY = {};
5251
private static final LocalVariable[] EMPTY_LOCAL_VARIABLE_ARRAY = {};
5352
private static final LocalVariableType[] EMPTY_LOCAL_VARIABLE_TYPE_ARRAY = {};
54-
private static final AbstractPseudoInstruction.ExceptionCatchImpl[] EMPTY_HANDLER_ARRAY = {};
5553
private static final DeferredLabel[] EMPTY_DEFERRED_LABEL_ARRAY = {};
5654

5755
final List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers = new ArrayList<>();
@@ -74,6 +72,9 @@ public final class DirectCodeBuilder
7472
private DeferredLabel[] deferredLabels = EMPTY_DEFERRED_LABEL_ARRAY;
7573
private int deferredLabelsCount = 0;
7674

75+
private int maxStackHint = -1;
76+
private int maxLocalsHint = -1;
77+
7778
/* Locals management
7879
lazily computed maxLocal = -1
7980
first time: derive count from methodType descriptor (for new methods) & ACC_STATIC,
@@ -173,6 +174,12 @@ public MethodInfo methodInfo() {
173174
return methodInfo;
174175
}
175176

177+
public static void withMaxs(CodeBuilder cob, int stacks, int locals) {
178+
var dcb = (DirectCodeBuilder) cob;
179+
dcb.maxStackHint = stacks;
180+
dcb.maxLocalsHint = locals;
181+
}
182+
176183
private UnboundAttribute<CodeAttribute> content = null;
177184

178185
private void writeExceptionHandlers(BufWriterImpl buf) {
@@ -319,6 +326,8 @@ private void writeCounters(boolean codeMatch, BufWriterImpl buf) {
319326
if (codeMatch) {
320327
var originalAttribute = (CodeImpl) original;
321328
buf.writeU2U2(originalAttribute.maxStack(), originalAttribute.maxLocals());
329+
} else if (maxLocalsHint >= 0 && maxStackHint >= 0) {
330+
buf.writeU2U2(maxStackHint, maxLocalsHint);
322331
} else {
323332
StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf);
324333
buf.writeU2U2(cntr.maxStack(), cntr.maxLocals());

0 commit comments

Comments
 (0)