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

  1. Raise -Dgraal.MaxDuplicationFactor above 1.0 so the duplication pass can restructure the irreducible loop (subject to the cap that guards against duplication explosion).
  2. Recompile the offending class with structured loops (normal Java break/continue) to eliminate irreducible flow.
  3. 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

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


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/58f500004fa679c3. Report an issue: GitHub.