oracle/graal · warning · RetryableBailoutException

FullUnroll : Graph seems to grow out of proportion

Error message

FullUnroll : Graph seems to grow out of proportion

What it means

During full loop unrolling, LoopTransformations duplicates the loop body once per iteration; after each peeling step it checks both the graph-size budget (node count > initialNodeCount + MaximumDesiredSize * 2) and the iteration budget (peelings > FullUnrollMaxIterations). Violating either throws RetryableBailoutException, a bailout class the compiler treats as 'retry this compilation without the failing optimization', not a permanent failure.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/loop/phases/LoopTransformations.java:185

                 * (without simplification) all floating nodes changed during peeling but only
                 * simplify new (in the peeled iteration) ones.
                 */
                EconomicSetNodeEventListener peeledListener = new EconomicSetNodeEventListener();
                try (NodeEventScope peeledScope = graph.trackNodeEvents(peeledListener)) {
                    LoopTransformations.peel(loop);
                }
                c.applyIncremental(graph, context, peeledListener.getNodes());
                loop.invalidateFragmentsAndIVs();
                for (Node n : graph.getNewNodes(newNodes)) {
                    if (n.isAlive() && (n instanceof IfNode || n instanceof SwitchNode || n instanceof FixedGuardNode || n instanceof BeginNode)) {
                        Simplifiable s = (Simplifiable) n;
                        s.simplify(defaultSimplifier);
                        graph.getOptimizationLog().report(LoopTransformations.class, "LoopFullUnrollCfgSimplification", n);
                    }
                }
                if (graph.getNodeCount() > initialNodeCount + MaximumDesiredSize.getValue(graph.getOptions()) * 2 ||
                                peelings > DefaultLoopPolicies.Options.FullUnrollMaxIterations.getValue(graph.getOptions())) {
                    throw new RetryableBailoutException("FullUnroll : Graph seems to grow out of proportion");
                }
                peelings++;
            }
        }
        // Canonicalize with the original canonicalizer to capture all simplifications
        canonicalizer.applyIncremental(graph, context, l.getNodes());
        loop.loopBegin().graph().getOptimizationLog().report(LoopTransformations.class, "LoopFullUnroll", loop.loopBegin());
    }

    public static void unswitch(Loop loop, List<ControlSplitNode> controlSplitNodeSet, boolean isTrivialUnswitch) {
        final ControlSplitNode firstNode = controlSplitNodeSet.iterator().next();
        final StructuredGraph graph = firstNode.graph();

        graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "Before unswitching %s", controlSplitNodeSet);

        LoopFragmentWhole originalLoop = loop.whole();

        if (!isTrivialUnswitch) {

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Nothing is broken: the compiler retries without full unrolling — verify the compilation ultimately succeeds; only act if performance of the resulting code matters
  2. Make the loop ineligible for full unroll (non-constant trip count) or smaller so unrolling fits the budget
  3. Tune budgets if you really need the unroll: raise -Dgraal.MaximumDesiredSize and/or -Dgraal.FullUnrollMaxIterations
  4. Split the loop body so the duplicated portion is smaller

Example fix

# before: loop body large, trips 1000 -> unroll blows budget
for (int i = 0; i < 1000; i++) { /* 200 nodes */ }

# after: precompute/extract so the unrolled body is small or trip count non-constant
for (int i = 0; i < n; i++) { doOneStep(i); }
Defensive patterns

Strategy: retry

Try / catch

// The compiler itself retries on RetryableBailoutException; only observe it in harnesses:
try { graph = compile(m); } catch (RetryableBailoutException e) { graph = compileWithoutFullUnroll(m); }

Prevention

When it happens

Trigger: Full-unrolling a loop whose body is large or whose iteration count is high, so repeated duplication exceeds MaximumDesiredSize*2 extra nodes, or where the unroller keeps peeling more than FullUnrollMaxIterations times (e.g., miscomputed trip counts causing repeated re-peeling of the same loop).

Common situations: Compiling hot loops with high constant trip counts; lowering MaximumDesiredSize too aggressively in tuning; huge loop bodies that were borderline-eligible for full unroll; Truffle/eager compilation of loop-heavy interpreters.

Related errors


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