oracle/graal · warning · PermanentBailoutException
Irreducible
Error message
Irreducible
What it means
Thrown during BciBlockMapping when control flow re-enters a loop that is not currently active — irreducible control flow — and -Dgraal.MaxDuplicationFactor is <= 1.0, so block duplication (the mechanism that would make the loop reducible) is disabled. Graal's frontend only handles reducible loops; with duplication off, irreducible re-entry is a permanent bailout.
Source
Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BciBlockMapping.java:1840
makeLoopHeader(successor);
}
loopBits = (BitSet) successor.loops.clone();
} else {
// re-reaching control-flow through new path.
// Find loop bits
loopBits = (BitSet) successor.loops.clone();
if (successor.isLoopHeader) {
// this is a forward edge
loopBits.clear(successor.loopId);
}
// Check if we are re-entering a loop in an irreducible way
BitSet checkBits = loopBits;
int outermostInactiveLoopId = -1;
for (int pos = -1; (pos = checkBits.nextSetBit(pos + 1)) >= 0;) {
int id = pos;
if (!loopHeaders[id].active) {
if (Options.MaxDuplicationFactor.getValue(debug.getOptions()) <= 1.0D) {
throw new PermanentBailoutException("Irreducible");
} else if (outermostInactiveLoopId == -1 || !loopHeaders[id].loops.get(outermostInactiveLoopId)) {
outermostInactiveLoopId = id;
}
}
}
if (outermostInactiveLoopId != -1) {
assert !(step instanceof DuplicationTraversalStep) : step;
// we need to duplicate until we can merge with this loop's header
successor.predecessorCount--;
BciBlock duplicate = successor.duplicate();
duplicate.predecessorCount++;
block.successors.set(step.currentSuccessorIndex, duplicate);
DuplicationTraversalStep duplicationStep = new DuplicationTraversalStep(step, duplicate, loopHeaders[outermostInactiveLoopId]);
workStack.push(duplicationStep);
debug.log("Starting duplication @ %s", duplicate);
debug.dump(DebugContext.DETAILED_LEVEL, this, "Starting duplication @ %s", duplicate);
duplicationStep.duplicationMap.put(successor, duplicate);
newDuplicateBlocks++;View on GitHub (pinned to a66e9ccd1d)
Solutions
- Raise -Dgraal.MaxDuplicationFactor above 1.0 so the duplication pass can restructure the irreducible loop (subject to the cap that guards against duplication explosion).
- Recompile the offending class with structured loops (normal Java break/continue) to eliminate irreducible flow.
- If the bytecode is produced by a tool (obfuscator/DSL compiler), configure it to emit reducible control flow.
Example fix
# before java -Dgraal.MaxDuplicationFactor=1 MyApp # after java -Dgraal.MaxDuplicationFactor=2 MyApp
Defensive patterns
Strategy: validation
Validate before calling
// if you must compile irreducible-flow bytecode, check the flag first
double factor = jdk.graal.compiler.options.OptionValues
.thenApply(v -> MaxDuplicationFactor.getValue(v));
if (factor <= 1.0D) {
// raise the flag or restructure the bytecode; parsing will bail out otherwise
} Prevention
- Keep -Dgraal.MaxDuplicationFactor > 1.0 (default) if you run obfuscated or goto-heavy bytecode.
- Prefer structured, reducible loops in generated code.
When it happens
Trigger: Parsing a method whose BCI graph re-enters an inactive loop (a jump into the middle of a loop from outside it) while Options.MaxDuplicationFactor <= 1.0D (the check in the loop over loopBits finding !loopHeaders[id].active).
Common situations: Methods with irreducible control flow from bytecode obfuscators, goto-heavy generated code, or languages compiling to class files with unstructured jumps; users who explicitly set -Dgraal.MaxDuplicationFactor=1 to shrink compilation time/memory and then compile such a method.
Related errors
- Non-reducible loop requires too much duplication. Setting %s
- Can not duplicate block with JSR data
- Too many loops in method
- Block that is reached by a fall through end of code is reach
- cannot link call from %s
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/58f500004fa679c3.
Report an issue: GitHub.