{"record":{"id":"fc4801a00ee4b0bd","repo":"oracle/graal","slug":"too-many-iterations-in-computegloballivesets","errorCode":null,"errorMessage":"too many iterations in computeGlobalLiveSets","messagePattern":"too many iterations in computeGlobalLiveSets","errorType":"exception","errorClass":"PermanentBailoutException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java","lineNumber":416,"sourceCode":"                                debug.log(\"block %d: livein = %s,  liveout = %s\", block.getId(), liveIn, blockSets.liveOut);\n                            }\n                            int predecessorCount = block.getPredecessorCount();\n                            for (int p = 0; p < predecessorCount; p++) {\n                                BasicBlock<? extends BasicBlock<?>> predecessor = block.getPredecessorAt(p);\n                                BlockData predBlockSet = allocator.getBlockData(predecessor);\n                                // Process this block on the next iteration\n                                predBlockSet.dirty = true;\n                            }\n                        }\n                    }\n                    iterationCount++;\n\n                    if (changeOccurred && iterationCount > 50) {\n                        /*\n                         * Very unlikely, should never happen: If it happens we cannot guarantee it\n                         * won't happen again.\n                         */\n                        throw new PermanentBailoutException(\"too many iterations in computeGlobalLiveSets\");\n                    }\n                }\n            } while (changeOccurred);\n\n            assert verifyLiveness();\n\n            computeGlobalLiveSetsBlocksProcessed.add(allocator.debug, blocksProcessed);\n            computeGlobalLiveSetsBlocks.add(allocator.debug, numBlocks);\n\n            // check that the liveIn set of the first block is empty\n            BasicBlock<?> startBlock = allocator.getLIR().getControlFlowGraph().getStartBlock();\n            if (allocator.getBlockData(startBlock).liveIn.cardinality() != 0) {\n                if (allocator.isDetailedAsserts()) {\n                    reportFailure(numBlocks);\n                }\n                SparseBitSet bs = allocator.getBlockData(startBlock).liveIn;\n                StringBuilder sb = new StringBuilder();\n                for (int i = bs.iterateValues(0); i >= 0; i = bs.iterateValues(i + 1)) {","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java#L398-L434","documentation":"Thrown by LinearScan register allocation when computing global liveness sets via fixpoint iteration over the CFG. The do/while loop keeps iterating while changes occur; if changes are still occurring after more than 50 iterations, the algorithm is considered non-convergent and a PermanentBailoutException aborts the compilation. The comment in the source explicitly says this 'should never happen', so it indicates a CFG pathology (e.g., self-modifying pred sets or extremely deep loop nesting) rather than normal compiler behavior.","triggerScenarios":"Compiling a method whose control-flow graph causes the liveIn/liveOut backward dataflow in LinearScanLifetimeAnalysisPhase.computeGlobalLiveSets to still be changing after iteration 51 (iterationCount > 50 && changeOccurred). Typically requires very deeply nested loops or an unusual CFG shape produced by bytecode instrumentation or complex loop transformations.","commonSituations":"JIT compiling or AOT-image-building an unusually large method with many nested loops; generated/instrumented bytecode with irregular CFGs; rarely, a Graal version regression in liveness handling. Mostly seen as a one-off on a specific method.","solutions":["Capture the failure with -Dgraal.Dump=:1 and file a GraalVM issue including the method and dump, since this is an internal invariant that should never fire","Exclude the offending method from compilation: add it to the compilation blacklist (e.g., -Dgraal.CompileTheStoreBlack.../TruffleBoundary or mx/--Dgraal.MethodFilter to isolate, then use compilation-exclusion options) so it stays on the baseline/interpreter path","If the method is generated code, restructure it to reduce loop-nesting depth or split it into smaller methods","Retry the build on a newer GraalVM release, as fixpoint-handling bugs in LSRA liveness have historically been fixed"],"exampleFix":"// before: one giant method with 12 nested loops -> triggers non-convergent liveness\nvoid process(Matrix m) { for(...) { for(...) { ... } } }\n\n// after: split into smaller methods so each compiled unit has a shallow CFG\nvoid process(Matrix m) { for (Row r : m.rows()) processRow(r); }\nvoid processRow(Row r) { for (Cell c : r.cells()) processCell(c); }","handlingStrategy":"try-catch","validationCode":"// No meaningful pre-validation: convergence is an internal property.\n// Optionally cap compiled-method complexity before submitting it.\nif (methodBytecodeSize > 8000 || loopNestingDepth(method) > 8) {\n    skipCompilationAndUseBaseline(method);\n}","typeGuard":null,"tryCatchPattern":"try {\n    compile(method);\n} catch (PermanentBailoutException e) {\n    // non-retryable: fall back to baseline/interpreter for this method, log and report\n    fallbackToBaseline(method, e);\n}","preventionTips":["Keep compiled methods modest in size and loop-nesting depth; split generated code","Enable -Dgraal.Dump in CI for one-off Graal failures so bailouts like this are diagnosable","Report occurrences upstream: this path is documented as 'should never happen'"],"tags":["graalvm","register-allocation","lsra","liveness","permanent-bailout"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}