ADDITIONAL SYSTEM INFORMATION :
CPU: Intel I7-9700K, 8 cores @ 4.9 GHz
Memory: 16 GB
OS: Zorin OS 16.3, x86_64 (Based on Debian Linux)
Java Runtime: OpenJDK Runtime Environment (build 21+45-2513), 64 bit
Java Compiler: javac 21 (Same distribution as Java Runtime mentioned above)
A DESCRIPTION OF THE PROBLEM :
Javac's compilation of switch statements using `instanceof`-like patterns generates sub-optimal bytecode.
For example:
class Hello {
void foo() {
Object o = "hello";
switch(o) {
case String s -> System.out.println(s);
case Integer i -> System.out.printf("int %s", i);
default -> System.out.println("huh");
}
}
}
Javac adds this header to the top of the resulting switch case:
```
3: aload_1 ; Switch input, this is the `o` variable
4: dup
5: invokestatic #9 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
8: pop
9: astore_2
10: iconst_0
11: istore_3
12: aload_2
13: iload_3
14: invokedynamic #15, 0 // InvokeDynamic #0:typeSwitch:(Ljava/lang/Object;I)I
```
This header wastes 2 entire local slots, and does a lot of unnecessary stack operations for what is basically just an invokedynamic call, with one argument wrapped in an Objects.requireNonNull.
A java representation could be written like such:
```
Objects.requireNonNull(o);
Object oCpy = o;
int startIdx = 0;
switch((int) SwitchBootstraps.typeSwitch(...).getTarget().invoke(oCpy, startIdx)) {
// ...
}
Instead, bytecode like this could be generated:
```
aload_1
invokestatic java/util/Objects.requireNonNull(Ljava/lang/Object;)Ljava/lang/Object;
iconst_0
invokedynamic java/lang/runtime/SwitchBootstraps.typeSwitch(...)... ; Continue with standard procedure
```
The above bytecode would both save 2 local slots, and save 6 instructions.
CPU: Intel I7-9700K, 8 cores @ 4.9 GHz
Memory: 16 GB
OS: Zorin OS 16.3, x86_64 (Based on Debian Linux)
Java Runtime: OpenJDK Runtime Environment (build 21+45-2513), 64 bit
Java Compiler: javac 21 (Same distribution as Java Runtime mentioned above)
A DESCRIPTION OF THE PROBLEM :
Javac's compilation of switch statements using `instanceof`-like patterns generates sub-optimal bytecode.
For example:
class Hello {
void foo() {
Object o = "hello";
switch(o) {
case String s -> System.out.println(s);
case Integer i -> System.out.printf("int %s", i);
default -> System.out.println("huh");
}
}
}
Javac adds this header to the top of the resulting switch case:
```
3: aload_1 ; Switch input, this is the `o` variable
4: dup
5: invokestatic #9 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
8: pop
9: astore_2
10: iconst_0
11: istore_3
12: aload_2
13: iload_3
14: invokedynamic #15, 0 // InvokeDynamic #0:typeSwitch:(Ljava/lang/Object;I)I
```
This header wastes 2 entire local slots, and does a lot of unnecessary stack operations for what is basically just an invokedynamic call, with one argument wrapped in an Objects.requireNonNull.
A java representation could be written like such:
```
Objects.requireNonNull(o);
Object oCpy = o;
int startIdx = 0;
switch((int) SwitchBootstraps.typeSwitch(...).getTarget().invoke(oCpy, startIdx)) {
// ...
}
Instead, bytecode like this could be generated:
```
aload_1
invokestatic java/util/Objects.requireNonNull(Ljava/lang/Object;)Ljava/lang/Object;
iconst_0
invokedynamic java/lang/runtime/SwitchBootstraps.typeSwitch(...)... ; Continue with standard procedure
```
The above bytecode would both save 2 local slots, and save 6 instructions.