oracle/graal · warning · RetryableBailoutException

Graph too large to safely compile in reasonable time. Graph

Error message

Graph too large to safely compile in reasonable time. Graph contains more than %d basic blocks

What it means

ControlFlowGraph.identifyBlocksImpl counts one HIRBlock per AbstractBeginNode; block indices are stored in bounded data structures, so exceeding AbstractControlFlowGraph.LAST_VALID_BLOCK_INDEX triggers a RetryableBailoutException stating the graph is too large to compile safely. Retryable means the compilation is re-attempted with a degraded configuration (e.g., less inlining) rather than permanently failing.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/cfg/ControlFlowGraph.java:409

        }
    }

    @SuppressWarnings("try")
    private void identifyBlocks(boolean makeModifiable) {
        try (DebugCloseable a = CFG_Build.start(graph.getDebug())) {
            identifyBlocksImpl(makeModifiable);
        }
    }

    private void identifyBlocksImpl(boolean makeModifiable) {
        int numBlocks = 0;
        for (AbstractBeginNode begin : graph.getNodes(AbstractBeginNode.TYPE)) {
            GraalError.guarantee(begin.predecessor() != null || (begin instanceof AbstractMergeNode || begin instanceof StartNode), "Disconnected control flow %s encountered", begin);
            HIRBlock block = makeModifiable ? new HIRBlock.ModifiableBlock(begin, this) : new HIRBlock.UnmodifiableBlock(begin, this);
            identifyBlock(block);
            numBlocks++;
            if (numBlocks > AbstractControlFlowGraph.LAST_VALID_BLOCK_INDEX) {
                throw new RetryableBailoutException("Graph too large to safely compile in reasonable time. Graph contains more than %d basic blocks",
                                AbstractControlFlowGraph.LAST_VALID_BLOCK_INDEX);
            }
        }
        reversePostOrder = ReversePostOrder.identifyBlocks(this, numBlocks);
        buildConfig.modifiableBlocks = makeModifiable;
    }

    @Override
    public int getNumberOfLoops() {
        return loops.size();
    }

    public double localLoopFrequency(LoopBeginNode lb) {
        return localLoopFrequencyData.get(lb).getLoopFrequency();
    }

    public ProfileSource localLoopFrequencySource(LoopBeginNode lb) {
        return localLoopFrequencyData.get(lb).getProfileSource();

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Confirm the retry path succeeds: a RetryableBailout normally recompiles with more conservative settings — only intervene if compilation ultimately fails
  2. Reduce graph growth: less inlining (smaller MaximumInlineSize / more conservative profiles), less loop explosion, smaller methods
  3. Split the giant method into smaller compilation units
  4. If compiling generated code, cap the size of the largest generated methods

Example fix

# before: unbounded eager inlining grows one graph past the block limit
-Dgraal.MaximumInlineSize=1000

# after: keep graphs within the limit
-Dgraal.MaximumInlineSize=200  # plus split the largest generated methods
Defensive patterns

Strategy: retry

Try / catch

// Compiler retries internally on RetryableBailoutException; harness-level fallback:
try { g = compile(m); } catch (RetryableBailoutException e) { g = compileWithConservativeInlining(m); }

Prevention

When it happens

Trigger: A graph with more than the hard block-index limit (LAST_VALID_BLOCK_INDEX) basic blocks: gigantic methods, explosive inlining, or loop explosion/full unrolling that multiplies branch structure past the limit.

Common situations: AOT-compiling huge generated dispatch methods; eager inlining of interpreter loops in Truffle; raising inlining/unrolling budgets to extremes; machine-generated state machines.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/3d66f9d8695d26d7. Report an issue: GitHub.