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
- Nothing is broken: the compiler retries without full unrolling — verify the compilation ultimately succeeds; only act if performance of the resulting code matters
- Make the loop ineligible for full unroll (non-constant trip count) or smaller so unrolling fits the budget
- Tune budgets if you really need the unroll: raise -Dgraal.MaximumDesiredSize and/or -Dgraal.FullUnrollMaxIterations
- 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
- Treat this as informational: the retry path compiles without full unroll
- Keep loop bodies compact if you want full unroll to succeed
- Know the budgets: MaximumDesiredSize and FullUnrollMaxIterations bound this optimization
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
- OSR entry point wasn't parsed
- Graph too large to safely compile in reasonable time. Graph
- LogFile substitution %s cannot be combined with any other ch
- Set the JVMCI_VERSION_CHECK environment variable to "ignore"
- Unknown argument: %s
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/957602d2f9143724.
Report an issue: GitHub.