Java -version output
openjdk version "25.0.4.1" 2026-08-18 LTS
IBM Semeru Runtime Open Edition 25.0.4.15 (build 25.0.4.1+1-LTS)
Eclipse OpenJ9 VM 25.0.4.15 (build 25.0.4.1+1-openj9-0.61.0, JRE 25 Mac OS X aarch64-64-Bit 20260818_330 (JIT enabled, AOT enabled)
OpenJ9 - a1daf225f5
OMR - ebd02d912
JCL - cdc2304e39f based on jdk-25.0.4.1+1)
macOS 15, aarch64
Summary of problem
int var1 = (Integer) Integer.valueOf(Integer.MIN_VALUE); followed by if ((var1 * 2) < len) b[var1 * 2] = true; must never throw: Integer.MIN_VALUE * 2 wraps to 0 (JLS 15.17.2), so the guard 0 < len holds and the array index is 0. Under -Xjit:count=0 (compile on first invocation), the OpenJ9 JIT evaluates the boxed-cast path with a wrong value range — the bounds check is miscompiled for the very first iteration of the loop — and the program throws ArrayIndexOutOfBoundsException where none can legally occur.
The reproducer contains an in-program control path that performs the identical computation with a plain int literal (no cast) and runs it in the same JVM: the control path is correct in every configuration, which isolates the defect to the redundant unboxing-cast path (MATCH=diff). An L=7 deep-chain variant reproduces identically.
Affected version matrix (macOS 15, aarch64; identical repro):
| Semeru build |
-Xjit:count=0 |
default |
-Xint |
| 11.0.32 |
spurious AIOOBE on first iteration |
correct |
correct |
| 17.0.20 |
spurious AIOOBE (5/5 reruns identical) |
correct |
correct |
| 21.0.12 |
spurious AIOOBE |
correct |
correct |
| 25.0.4 |
spurious AIOOBE |
correct |
correct |
Controls: Temurin 17/21/25 in every mode tested (default, -Xcomp, -Xint) are correct; -Xjit:optlevel=cold / -Xjit:optlevel=hot on OpenJ9 are also correct — the defect requires the compile-on-first-invocation path (initial-compilation level code), not merely a low optimization level.
Violated specification: JLS 15.17.2 (integer multiplication wraps — Integer.MIN_VALUE * 2 == 0), and the general requirement that JIT-compiled code be observationally consistent with interpreted code of the same JVM. The interpreted execution (default, below the compilation threshold for a single invocation) runs the same method without error.
This issue was identified via differential testing across multiple JVM implementations (Temurin and IBM Semeru/OpenJ9, JDK 11–25).
Steps to reproduce
javac repro.java
java -Xjit:count=0 repro → CASTITERS=-1 CASTEX=java.lang.ArrayIndexOutOfBoundsException MATCH=diff (exception on the very first iteration; the no-cast control prints NOCASTITERS=99999 ... NOCASTEX=none)
java repro → CASTITERS=99999 MATCH=same (correct)
Expected result
CASTITERS=99999
CASTHITS=100000
CASTEX=none
NOCASTITERS=99999
NOCASTHITS=100000
NOCASTEX=none
MATCH=same
on every conforming JVM in every execution mode (int overflow wraps per JLS 15.17.2, guard holds, index 0 valid).
Actual result
$ java -Xjit:count=0 repro
CHAINL=3
CASTITERS=-1
CASTHITS=0
CASTEX=java.lang.ArrayIndexOutOfBoundsException
NOCASTITERS=99999
NOCASTHITS=100000
NOCASTEX=none
MATCH=diff
Deterministic: byte-for-byte identical across 5/5 independent reruns (agent 3/3 plus main-session 2/2 on 17.0.20; single-run confirmation on 11.0.32 / 21.0.12 / 25.0.4).
Source code / Diagnostic files
// Explicit (Integer) cast unboxing of Integer.MIN_VALUE; loop guards array
// access with (var1 * 2) < len. MIN_VALUE*2 wraps to 0 so the guard must hold.
// An equivalent no-cast control path runs in the same program; both paths must
// agree (MATCH=same).
public class repro {
static int len = 100;
static int lastIters;
static int lastHits;
static boolean lastExc;
static void runCast() {
Integer boxed = Integer.valueOf(Integer.MIN_VALUE);
int var1 = (Integer) boxed; // redundant cast + auto-unbox
int iters = -1;
int hits = 0;
try {
for (int i = 0; i < 100000; i++) {
boolean[] b = new boolean[100];
if ((var1 * 2) < len) { // MIN_VALUE*2 wraps to 0 -> 0 < 100
b[var1 * 2] = true;
hits++;
}
iters = i;
}
} catch (ArrayIndexOutOfBoundsException e) {
lastExc = true;
}
lastIters = iters;
lastHits = hits;
}
static void runNoCast() {
int var1 = Integer.MIN_VALUE; // direct literal, no cast
int iters = -1;
int hits = 0;
try {
for (int i = 0; i < 100000; i++) {
boolean[] b = new boolean[100];
if ((var1 * 2) < len) {
b[var1 * 2] = true;
hits++;
}
iters = i;
}
} catch (ArrayIndexOutOfBoundsException e) {
lastExc = true;
}
lastIters = iters;
lastHits = hits;
}
static void realMain() {
System.out.println("CHAINL=3");
lastExc = false;
runCast();
int ci = lastIters;
int ch = lastHits;
boolean ce = lastExc;
lastExc = false;
runNoCast();
int ni = lastIters;
int nh = lastHits;
boolean ne = lastExc;
System.out.println("CASTITERS=" + ci);
System.out.println("CASTHITS=" + ch);
System.out.println("CASTEX=" + (ce ? "java.lang.ArrayIndexOutOfBoundsException" : "none"));
System.out.println("NOCASTITERS=" + ni);
System.out.println("NOCASTHITS=" + nh);
System.out.println("NOCASTEX=" + (ne ? "java.lang.ArrayIndexOutOfBoundsException" : "none"));
System.out.println("MATCH=" + ((ci == ni && ch == nh && ce == ne) ? "same" : "diff"));
}
public static void main(String[] args) {
try {
realMain();
} catch (Throwable t) {
System.err.println(t.getClass().getName() + ": " + t.getMessage());
System.exit(1);
}
}
}
Java -version output
macOS 15, aarch64
Summary of problem
int var1 = (Integer) Integer.valueOf(Integer.MIN_VALUE);followed byif ((var1 * 2) < len) b[var1 * 2] = true;must never throw:Integer.MIN_VALUE * 2wraps to 0 (JLS 15.17.2), so the guard0 < lenholds and the array index is 0. Under-Xjit:count=0(compile on first invocation), the OpenJ9 JIT evaluates the boxed-cast path with a wrong value range — the bounds check is miscompiled for the very first iteration of the loop — and the program throwsArrayIndexOutOfBoundsExceptionwhere none can legally occur.The reproducer contains an in-program control path that performs the identical computation with a plain
intliteral (no cast) and runs it in the same JVM: the control path is correct in every configuration, which isolates the defect to the redundant unboxing-cast path (MATCH=diff). An L=7 deep-chain variant reproduces identically.Affected version matrix (macOS 15, aarch64; identical repro):
-Xjit:count=0-XintControls: Temurin 17/21/25 in every mode tested (default,
-Xcomp,-Xint) are correct;-Xjit:optlevel=cold/-Xjit:optlevel=hoton OpenJ9 are also correct — the defect requires the compile-on-first-invocation path (initial-compilation level code), not merely a low optimization level.Violated specification: JLS 15.17.2 (integer multiplication wraps —
Integer.MIN_VALUE * 2 == 0), and the general requirement that JIT-compiled code be observationally consistent with interpreted code of the same JVM. The interpreted execution (default, below the compilation threshold for a single invocation) runs the same method without error.This issue was identified via differential testing across multiple JVM implementations (Temurin and IBM Semeru/OpenJ9, JDK 11–25).
Steps to reproduce
javac repro.javajava -Xjit:count=0 repro→CASTITERS=-1 CASTEX=java.lang.ArrayIndexOutOfBoundsException MATCH=diff(exception on the very first iteration; the no-cast control printsNOCASTITERS=99999 ... NOCASTEX=none)java repro→CASTITERS=99999 MATCH=same(correct)Expected result
on every conforming JVM in every execution mode (int overflow wraps per JLS 15.17.2, guard holds, index 0 valid).
Actual result
Deterministic: byte-for-byte identical across 5/5 independent reruns (agent 3/3 plus main-session 2/2 on 17.0.20; single-run confirmation on 11.0.32 / 21.0.12 / 25.0.4).
Source code / Diagnostic files