{"record":{"id":"957602d2f9143724","repo":"oracle/graal","slug":"fullunroll-graph-seems-to-grow-out-of-proportion","errorCode":null,"errorMessage":"FullUnroll : Graph seems to grow out of proportion","messagePattern":"FullUnroll : Graph seems to grow out of proportion","errorType":"exception","errorClass":"RetryableBailoutException","httpStatus":null,"severity":"warning","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/loop/phases/LoopTransformations.java","lineNumber":185,"sourceCode":"                 * (without simplification) all floating nodes changed during peeling but only\n                 * simplify new (in the peeled iteration) ones.\n                 */\n                EconomicSetNodeEventListener peeledListener = new EconomicSetNodeEventListener();\n                try (NodeEventScope peeledScope = graph.trackNodeEvents(peeledListener)) {\n                    LoopTransformations.peel(loop);\n                }\n                c.applyIncremental(graph, context, peeledListener.getNodes());\n                loop.invalidateFragmentsAndIVs();\n                for (Node n : graph.getNewNodes(newNodes)) {\n                    if (n.isAlive() && (n instanceof IfNode || n instanceof SwitchNode || n instanceof FixedGuardNode || n instanceof BeginNode)) {\n                        Simplifiable s = (Simplifiable) n;\n                        s.simplify(defaultSimplifier);\n                        graph.getOptimizationLog().report(LoopTransformations.class, \"LoopFullUnrollCfgSimplification\", n);\n                    }\n                }\n                if (graph.getNodeCount() > initialNodeCount + MaximumDesiredSize.getValue(graph.getOptions()) * 2 ||\n                                peelings > DefaultLoopPolicies.Options.FullUnrollMaxIterations.getValue(graph.getOptions())) {\n                    throw new RetryableBailoutException(\"FullUnroll : Graph seems to grow out of proportion\");\n                }\n                peelings++;\n            }\n        }\n        // Canonicalize with the original canonicalizer to capture all simplifications\n        canonicalizer.applyIncremental(graph, context, l.getNodes());\n        loop.loopBegin().graph().getOptimizationLog().report(LoopTransformations.class, \"LoopFullUnroll\", loop.loopBegin());\n    }\n\n    public static void unswitch(Loop loop, List<ControlSplitNode> controlSplitNodeSet, boolean isTrivialUnswitch) {\n        final ControlSplitNode firstNode = controlSplitNodeSet.iterator().next();\n        final StructuredGraph graph = firstNode.graph();\n\n        graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, \"Before unswitching %s\", controlSplitNodeSet);\n\n        LoopFragmentWhole originalLoop = loop.whole();\n\n        if (!isTrivialUnswitch) {","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/loop/phases/LoopTransformations.java#L167-L203","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"# before: loop body large, trips 1000 -> unroll blows budget\nfor (int i = 0; i < 1000; i++) { /* 200 nodes */ }\n\n# after: precompute/extract so the unrolled body is small or trip count non-constant\nfor (int i = 0; i < n; i++) { doOneStep(i); }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// The compiler itself retries on RetryableBailoutException; only observe it in harnesses:\ntry { graph = compile(m); } catch (RetryableBailoutException e) { graph = compileWithoutFullUnroll(m); }","preventionTips":["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"],"tags":["graalvm","loop-optimization","full-unroll","retryable-bailout"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}