Simon Jakobi pushed to branch wip/sjakobi/T27368 at Glasgow Haskell Compiler / GHC Commits: 47985b11 by Simon Jakobi at 2026-08-17T14:45:24+02:00 Cmm: only treat continuations of reachable calls as proc points callProcPoints used to fold over the whole block map, which can contain unreachable blocks: the control-flow optimiser and the common block eliminator both leave them behind, and they are only removed at the end of the Cmm pipeline (Note [unreachable blocks]). In particular, when the common block eliminator merges duplicated call blocks it leaves the losing copy in the block map, and the continuation of such a dead call can itself be unreachable. callProcPoints would still collect that continuation, attachContInfoTables would attach an info table to it, but stack layout (which only walks reachable blocks) produced no stack map for it, so setInfoTableStackMap panicked. Fix: make callProcPoints fold over revPostorder, i.e. reachable blocks only. Also: * Rewrite Note [unreachable blocks] to state the invariant. * Give elimCommonBlocks a haddock (absorbing the module's intro comment) that documents its unreachable-block behaviour, and drop the stale claim that ContFlowOpt had already removed unreachable blocks. The regression test distills the code shape that triggered the panic when compiling GHC.CmmToAsm.Dwarf.Types with -O2 on top of !16168. Fixes #27368 Assisted-by: Claude Fable 5 - - - - - 6 changed files: - + changelog.d/27368 - compiler/GHC/Cmm/CommonBlockElim.hs - compiler/GHC/Cmm/Pipeline.hs - compiler/GHC/Cmm/ProcPoint.hs - + testsuite/tests/codeGen/should_compile/T27368.hs - testsuite/tests/codeGen/should_compile/all.T Changes: ===================================== changelog.d/27368 ===================================== @@ -0,0 +1,4 @@ +section: compiler +synopsis: Fix a ``setInfoTableStackMap`` panic caused by calls in unreachable Cmm blocks. +issues: #27368 +mrs: !16169 ===================================== compiler/GHC/Cmm/CommonBlockElim.hs ===================================== @@ -30,20 +30,23 @@ import Control.Arrow (first, second) import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NE --- ----------------------------------------------------------------------------- --- Eliminate common blocks - +-- | Merge identical blocks +-- -- If two blocks are identical except for the label on the first node, -- then we can eliminate one of the blocks. To ensure that the semantics -- of the program are preserved, we have to rewrite each predecessor of the -- eliminated block to proceed with the block we keep. - +-- +-- Tolerates unreachable blocks in its input, and produces them: the +-- losing copy of a merged pair stays in the block map, now +-- unreachable. See Note [unreachable blocks] in GHC.Cmm.Pipeline. +-- -- The algorithm iterates over the blocks in the graph, -- checking whether it has seen another block that is equal modulo labels. -- If so, then it adds an entry in a map indicating that the new block -- is made redundant by the old block. -- Otherwise, it is added to the useful blocks. - +-- -- To avoid comparing every block with every other block repeatedly, we group -- them by -- * a hash of the block, ignoring labels (explained below) @@ -57,16 +60,13 @@ import qualified Data.List.NonEmpty as NE -- All in all, two blocks should never be compared if they have different -- hashes, and at most once otherwise. Previously, we were slower, and people -- rightfully complained: #10397 - --- TODO: Use optimization fuel elimCommonBlocks :: CmmGraph -> CmmGraph +-- TODO: Use optimization fuel elimCommonBlocks g = replaceLabels env $ copyTicks env g where env = iterate mapEmpty blocks_with_key - -- The order of blocks doesn't matter here. While we could use - -- revPostorder which drops unreachable blocks this is done in - -- ContFlowOpt already which runs before this pass. So we use - -- toBlockList since it is faster. + -- Block order doesn't matter here, so we use toBlockList, + -- which is faster than revPostorder. groups = groupByInt hash_block (toBlockList g) :: [[CmmBlock]] blocks_with_key = [ [ (successors b, [b]) | b <- bs] | bs <- groups] ===================================== compiler/GHC/Cmm/Pipeline.hs ===================================== @@ -353,10 +353,18 @@ _GLOBAL_OFFSET_TABLE_, regardless of which entry point we arrived via. {- Note [unreachable blocks] ~~~~~~~~~~~~~~~~~~~~~~~~~ -The control-flow optimiser sometimes leaves unreachable blocks behind -containing junk code. These aren't necessarily a problem, but -removing them is good because it might save time in the native code -generator later. +The control-flow optimiser and the common block eliminator sometimes +leave unreachable blocks behind. They are removed only here, at the +end of the pipeline, to save the cost of extra reachability passes — +so every pass in between runs on a block map that may contain +unreachable blocks, and must not let them influence the reachable +part of the graph. + +In particular, callProcPoints in GHC.Cmm.ProcPoint must only consider +reachable blocks: the continuation of a call in an unreachable +block may itself be unreachable, and must not become a proc point. +Otherwise attachContInfoTables would attach an info table to it, +resulting in a setInfoTableStackMap panic (#27368). To make unreachable blocks visible in -ddump-cmm-* output, add -dppr-debug. -} ===================================== compiler/GHC/Cmm/ProcPoint.hs ===================================== @@ -173,8 +173,12 @@ procPointLattice = DataflowLattice unreached add_to -- introduced because they're reachable from multiple proc points. -- -- Extract the set of Continuation BlockIds, see Note [Continuation BlockIds]. +-- +-- Fold over reachable blocks only: the continuation of an unreachable +-- call must not become a proc point. See Note [unreachable blocks] +-- in GHC.Cmm.Pipeline. callProcPoints :: CmmGraph -> ProcPointSet -callProcPoints g = foldlGraphBlocks add (setSingleton (g_entry g)) g +callProcPoints g = foldl' add (setSingleton (g_entry g)) (revPostorder g) where add :: LabelSet -> CmmBlock -> LabelSet add set b = case lastNode b of CmmCall {cml_cont = Just k} -> setInsert k set ===================================== testsuite/tests/codeGen/should_compile/T27368.hs ===================================== @@ -0,0 +1,20 @@ +-- Regression test for #27368: setInfoTableStackMap panicked because +-- callProcPoints collected the continuation of a call in an unreachable +-- block. The two branches below have identical suffixes from the inner +-- case onwards; common block elimination merges the duplicated call +-- blocks but leaves the losing copies in the block map, where the dead +-- call's continuation label is itself unreachable. +module T27368 (f) where + +{-# NOINLINE put #-} +put :: Int -> Int -> IO () +put h x = if h + x == 12345 then errorWithoutStackTrace "boom" else pure () + +data T = N | J Int | K + +f :: Int -> Bool -> T -> IO () +f h a t = do + if a + then do put h 1; case t of { N -> pure (); J _ -> put h 3; K -> put h 4 }; put h 0; put h 0 + else do put h 2; case t of { N -> pure (); J _ -> put h 3; K -> put h 4 }; put h 0; put h 0 + put h 0 ===================================== testsuite/tests/codeGen/should_compile/all.T ===================================== @@ -150,3 +150,5 @@ test('T16351', normal, compile, ['-O2 -ddump-simpl -dno-typeable-binds -dsuppres test('T20298a', normal, compile, ['-O2 -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques']) test('T20298b', normal, compile, ['-O2 -dno-bignum-rules -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques']) test('T20298c', normal, compile, ['-O2 -dno-builtin-rules -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques']) + +test('T27368', normal, compile, ['-O2']) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/47985b111483e8b577800676efa39498... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/47985b111483e8b577800676efa39498... You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help