8341272: Factory to create wide iinc instruction with small arguments

Reviewed-by: liach, asotona
This commit is contained in:
Trevor Bond 2026-01-12 07:05:52 +00:00 committed by Adam Sotona
parent 336894857b
commit 669977f7c4
4 changed files with 58 additions and 5 deletions

View File

@ -31,6 +31,8 @@ import java.lang.classfile.Instruction;
import java.lang.classfile.Opcode;
import jdk.internal.classfile.impl.AbstractInstruction;
import jdk.internal.classfile.impl.BytecodeHelpers;
import jdk.internal.classfile.impl.Util;
/**
* Models a local variable increment instruction in the {@code code} array of a
@ -82,6 +84,37 @@ public sealed interface IncrementInstruction extends Instruction
* @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range
*/
static IncrementInstruction of(int slot, int constant) {
return new AbstractInstruction.UnboundIncrementInstruction(slot, constant);
var opcode = BytecodeHelpers.validateAndIsWideIinc(slot, constant) ? Opcode.IINC_W: Opcode.IINC;
return new AbstractInstruction.UnboundIncrementInstruction(opcode, slot, constant);
}
/**
* {@return an increment instruction}
* <p>
* {@code slot} must be {@link java.lang.classfile##u1 u1} and
* {@code constant} must be within {@code [-128, 127]} for
* {@link Opcode#IINC iinc}, or {@code slot} must be
* {@link java.lang.classfile##u2 u2} and {@code constant} must be
* within {@code [-32768, 32767]} for {@link Opcode#IINC_W wide iinc}.
*
* @apiNote
* The explicit {@code op} argument allows creating {@code wide} or
* regular increment instructions when {@code slot} and
* {@code constant} can be encoded with more optimized
* increment instructions.
*
* @param op the opcode for the specific type of increment instruction,
* which must be of kind {@link Opcode.Kind#INCREMENT}
* @param slot the local variable slot to increment
* @param constant the increment constant
* @throws IllegalArgumentException if the opcode kind is not
* {@link Opcode.Kind#INCREMENT} or {@code slot} or
* {@code constant} is out of range
* @since 27
*/
static IncrementInstruction of(Opcode op, int slot, int constant) {
Util.checkKind(op, Opcode.Kind.INCREMENT);
BytecodeHelpers.validateIncrement(op, slot, constant);
return new AbstractInstruction.UnboundIncrementInstruction(op, slot, constant);
}
}

View File

@ -832,10 +832,8 @@ public abstract sealed class AbstractInstruction
final int slot;
final int constant;
public UnboundIncrementInstruction(int slot, int constant) {
super(BytecodeHelpers.validateAndIsWideIinc(slot, constant)
? Opcode.IINC_W
: Opcode.IINC);
public UnboundIncrementInstruction(Opcode op, int slot, int constant) {
super(op);
this.slot = slot;
this.constant = constant;
}

View File

@ -450,6 +450,13 @@ public class BytecodeHelpers {
return ret;
}
public static void validateIncrement(Opcode opcode, int slot, int constant) {
if (validateAndIsWideIinc(slot, constant) && opcode != Opcode.IINC_W) {
throw new IllegalArgumentException(
"IINC: operands require wide encoding for %s".formatted(opcode));
}
}
public static void validateRet(Opcode opcode, int slot) {
if (opcode == Opcode.RET && (slot & ~0xFF) == 0 ||
opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0)

View File

@ -195,6 +195,7 @@ class InstructionValidationTest {
ensureFailFast(i, cob -> cob.iinc(i, 1));
}
check(fails, () -> IncrementInstruction.of(i, 1));
check(fails, () -> IncrementInstruction.of(IINC_W, i, 1));
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(i));
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET_W, i));
check(fails, () -> LocalVariable.of(i, "test", CD_Object, dummyLabel, dummyLabel));
@ -208,6 +209,7 @@ class InstructionValidationTest {
check(fails, () -> LoadInstruction.of(u1Op, i));
for (var u1Op : List.of(ASTORE, ISTORE, LSTORE, FSTORE, DSTORE))
check(fails, () -> StoreInstruction.of(u1Op, i));
check(fails, () -> IncrementInstruction.of(IINC, i, 1));
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET, i));
}
@ -250,6 +252,13 @@ class InstructionValidationTest {
IncrementInstruction.of(0, 2);
IncrementInstruction.of(0, Short.MAX_VALUE);
IncrementInstruction.of(0, Short.MIN_VALUE);
IncrementInstruction.of(IINC, 0, 2);
IncrementInstruction.of(IINC, 0, Byte.MIN_VALUE);
IncrementInstruction.of(IINC, 0, Byte.MAX_VALUE);
IncrementInstruction.of(IINC_W, 0, 2);
IncrementInstruction.of(IINC_W, 0, Short.MIN_VALUE);
IncrementInstruction.of(IINC_W, 0, Short.MAX_VALUE);
for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) {
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, i));
TestUtil.runCodeHandler(cob -> {
@ -257,6 +266,12 @@ class InstructionValidationTest {
cob.return_();
});
}
for (int i : new int[] {Byte.MIN_VALUE - 1, Byte.MAX_VALUE + 1}) {
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(IINC, 0, i));
}
for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) {
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(IINC_W, 0, i));
}
}
@Test