{"record":{"id":"6932db344a6b0a2b","repo":"oracle/graal","slug":"graal-implementation-restriction-method-with-s-l","errorCode":null,"errorMessage":"Graal implementation restriction: Method with %s loop explosion must not have more than one top-level loop","messagePattern":"Graal implementation restriction: Method with (.+?) loop explosion must not have more than one top-level loop","errorType":"exception","errorClass":"PermanentBailoutException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphDecoder.java","lineNumber":1743,"sourceCode":"                for (EndNode predecessor : predecessors) {\n                    existingState.merge.addForwardEnd(predecessor);\n                }\n                return;\n            }\n        }\n\n        /*\n         * Merge explosion: When doing merge-explode PE loops are detected after partial evaluation\n         * in a dedicated steps. Therefore, we create merge nodes instead of loop begins and loop\n         * exits and later replace them with the detected loop begin and loop exit nodes.\n         */\n        MergeNode merge = graph.add(new MergeNode());\n        methodScope.loopExplosionMerges.add(merge);\n\n        if (methodScope.loopExplosion.mergeLoops()) {\n            if (loopScope.iterationStates.size() == 0 && loopScope.loopDepth == 1) {\n                if (methodScope.loopExplosionHead != null) {\n                    throw new PermanentBailoutException(\"Graal implementation restriction: Method with %s loop explosion must not have more than one top-level loop\",\n                                    LoopExplosionPlugin.LoopExplosionKind.MERGE_EXPLODE);\n                }\n                methodScope.loopExplosionHead = merge;\n            }\n        }\n\n        loopBegin.replaceAtUsagesAndDelete(merge);\n        merge.setStateAfter(frameState);\n        merge.setNext(successor);\n        for (EndNode predecessor : predecessors) {\n            merge.addForwardEnd(predecessor);\n        }\n\n        if (methodScope.loopExplosion.mergeLoops()) {\n            LoopExplosionState explosionState = new LoopExplosionState(frameState, merge);\n            loopScope.iterationStates.put(explosionState, explosionState);\n        }\n    }","sourceCodeStart":1725,"sourceCodeEnd":1761,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphDecoder.java#L1725-L1761","documentation":"GraphDecoder handles merge-explode loop explosion by converting loop begins into plain merges and reconstructing loops later; it supports at most one top-level loop per method (tracked via loopExplosionHead). When a second top-level loop is encountered during MERGE_EXPLODE decoding, it throws a documented Graal implementation restriction as a PermanentBailoutException.","triggerScenarios":"Partial evaluation of a method annotated (or registered via LoopExplosionPlugin) with LoopExplosionKind.MERGE_EXPLODE whose encoded graph contains more than one top-level loop (loopScope.loopDepth == 1 with an already-set loopExplosionHead).","commonSituations":"Truffle interpreters or substitution graphs where a helper method got MERGE_EXPLODE semantics (annotation, plugin, or @LoopExplosionKind inheritance) but the method body contains two sibling loops; refactoring exploded methods by adding a second loop.","solutions":["Move each top-level loop into its own method so no MERGE_EXPLODE method has more than one top-level loop","Switch that method to a different LoopExplosionKind (FULL_UNROLL, DUP_WITHOUT_EXIT, ...) that does not carry the single-loop restriction","Drop the explode annotation from the offending method if the explosion is not essential"],"exampleFix":"// before\n@LoopExplosionKind(MERGE_EXPLODE)\nstatic int run(VMState s) {\n    while (s.step1()) { }\n    while (s.step2()) { }   // second top-level loop -> bailout\n}\n\n// after\n@LoopExplosionKind(MERGE_EXPLODE) static int run1(VMState s) { while (s.step1()) { } return 0; }\n@LoopExplosionKind(MERGE_EXPLODE) static int run2(VMState s) { while (s.step2()) { } return 0; }\nstatic int run(VMState s) { run1(s); return run2(s); }","handlingStrategy":"validation","validationCode":"// Static check before annotating/running PE: count top-level loops in the method\nlong topLevelLoops = countTopLevelLoops(methodBytecode);\nif (kind == MERGE_EXPLODE && topLevelLoops > 1) {\n    throw new IllegalArgumentException(\"MERGE_EXPLODE supports only one top-level loop: \" + method);\n}","typeGuard":null,"tryCatchPattern":"try { pe(method); } catch (PermanentBailoutException e) { /* restructure method or change explosion kind */ throw e; }","preventionTips":["Keep exactly one top-level loop per MERGE_EXPLODE method; extract siblings into helpers","Prefer FULL_UNROLL or DUP_WITHOUT_EXIT when a method must contain several loops","Add an architectural test asserting exploded interpreter methods stay single-loop"],"tags":["graalvm","partial-evaluation","loop-explosion","graph-decoder","permanent-bailout"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}