{"record":{"id":"ef9c84f1a552d7fe","repo":"oracle/graal","slug":"early-inlining-exceeded-the-maximum-depth-of-s-fo","errorCode":null,"errorMessage":"Early inlining exceeded the maximum depth of %s for method %s.","messagePattern":"Early inlining exceeded the maximum depth of (.+?) for method (.+?)\\.","errorType":"exception","errorClass":"PermanentBailoutException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/TruffleEarlyInliningPhase.java","lineNumber":128,"sourceCode":"    protected void run(StructuredGraph graph) {\n        EconomicSet<Node> canonicalizableNodes = EconomicSet.create();\n        boolean progress = true;\n        int depth = 0;\n        while (progress) {\n            progress = false;\n            if (depth > maxDepth) {\n                /*\n                 * We intentionally use a simple iteration/depth limit instead of managing an\n                 * explicit inlining tree or recursion detection.\n                 *\n                 * Hitting this limit typically indicates an unintended recursive early-inlining\n                 * pattern, or an early-inlined helper that expands into calls that are again\n                 * annotated for early inlining without a clear bound.\n                 *\n                 * If recursion in early-inline methods is a valid use case, this phase needs to be\n                 * extended with proper cycle detection or a more precise inlining policy.\n                 */\n                throw new PermanentBailoutException(\"Early inlining exceeded the maximum depth of %s for method %s.\", maxDepth, graph.method());\n            }\n            List<Invoke> workList = new ArrayList<>();\n            for (Node node : graph.getNodes()) {\n                if (!(node instanceof Invoke invoke)) {\n                    continue;\n                }\n                if (shouldInline(invoke)) {\n                    workList.add(invoke);\n                }\n            }\n            for (Invoke invoke : workList) {\n                if (shouldInline(invoke)) {\n                    inlineCall(canonicalizableNodes, graph, invoke);\n                    progress = true;\n                }\n            }\n\n            if (!canonicalizableNodes.isEmpty()) {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/phases/TruffleEarlyInliningPhase.java#L110-L146","documentation":"PermanentBailoutException thrown by the Truffle early-inlining phase when the recursive walk over @Inlineinvoke-annotated calls exceeds the configured maximum depth. The phase deliberately uses a simple depth counter instead of building an inlining tree or cycle detection, so unbounded chains of early-inlineable methods (typically unintended recursion where an early-inlined helper again calls early-inline-annotated methods) hit this guard. Being 'permanent', it aborts this compilation without expecting a retry to help.","triggerScenarios":"Calling TruffleEarlyInliningPhase.runOnGraph on a graph where shouldInline(invoke) keeps returning true along a call chain deeper than maxDepth (derived from the early-inlining depth option), e.g. method A -> B -> C -> ... where every call target is annotated for early inlining and the chain is recursive or unbounded.","commonSituations":"A Truffle node helper annotated @Inline invokes another helper that (transitively) invokes back, creating a cycle with no explicit bound; adding a new early-inlined utility that fans out into many further early-inline calls; enabling aggressive early-inlining options after a language refactor.","solutions":["Find the recursion: the exception message names graph.method() and maxDepth; dump the graph before the phase (-Dgraal.Dump=TruffleEarlyInlining) and trace which @Inline-annotated calls form the cycle.","Break the cycle by removing the early-inlining annotation (@Inline in Truffle) from one call in the loop, or make the recursive helper a normal (non-early-inlined) call.","Add an explicit base case so the annotated call chain is bounded below maxDepth.","Only if recursion in early-inline methods is a legitimate use case: raise the depth limit option, and consider extending the phase with real cycle detection (as its comment notes) rather than relying on the depth counter."],"exampleFix":"// before: mutual early-inlined recursion\n@Inline\nstatic Value evalA(Node n) { return evalB(n); }\n@Inline\nstatic Value evalB(Node n) { return evalA(n); }\n\n// after: break the cycle — one side stays a普通 call\n@Inline\nstatic Value evalA(Node n) { return evalB(n); }\nstatic Value evalB(Node n) { /* plain call, not early-inlined */ return evalA(n); }","handlingStrategy":"validation","validationCode":"// Before enabling early inlining, sanity-check the call graph for cycles among @Inline methods\nboolean cyclic = hasCycleAmongEarlyInlineMethods(entryMethod, /*maxProbes*/ maxDepth);\nif (cyclic) {\n    throw new IllegalStateException(\"early-inline recursion detected; fix annotations before compiling\");\n}","typeGuard":null,"tryCatchPattern":"// Permanent bailout: do NOT transparently retry the same compilation\ntry {\n    compileWithEarlyInlining(graph);\n} catch (PermanentBailoutException e) {\n    if (e.getMessage().contains(\"Early inlining exceeded\")) {\n        logAndDisableEarlyInliningFor(graph.method()); // fall back to normal compilation path\n    } else {\n        throw e;\n    }\n}","preventionTips":["Keep @Inline annotations off recursive and mutually recursive helpers.","Add a static-analysis or test pass that flags cycles among early-inline-annotated methods.","Treat hitting the depth limit as a code smell to fix, not a limit to raise."],"tags":["truffle","inlining","bailout","recursion","graalvm"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}