[Git][ghc/ghc][wip/sjakobi/T27368-cbe-compress] Cmm: lint that call continuations are reachable
Simon Jakobi pushed to branch wip/sjakobi/T27368-cbe-compress at Glasgow Haskell Compiler / GHC Commits: cbe671f4 by Simon Jakobi at 2026-08-17T19:52:52+02:00 Cmm: lint that call continuations are reachable The breakage behind #27368 was invisible to -dcmm-lint: the common block eliminator never deletes the blocks it eliminates, they merely become unreachable, so the stranded continuation label still named a block in the map and the existing "Branch to nonexistent id" check passed at every stage. The panic only surfaced four passes later, in setInfoTableStackMap. Check the violated invariant directly: a call continuation must be reachable even when the call itself sits in unreachable code. See Note [unreachable blocks] in GHC.Cmm.Pipeline. Without the preceding fix, this check turns the #27368 panic into a lint error located at "Post common block elimination". Assisted-by: Claude Fable 5 - - - - - 2 changed files: - compiler/GHC/Cmm/Lint.hs - compiler/GHC/Cmm/Pipeline.hs Changes: ===================================== compiler/GHC/Cmm/Lint.hs ===================================== @@ -68,18 +68,19 @@ lintCmmGraph g = do let blocks = toBlockList g labels = setFromList (map entryLabel blocks) - cmmLocalLiveness platform g `seq` mapM_ (lintCmmBlock labels) blocks + reachable = setFromList (map entryLabel (revPostorder g)) + cmmLocalLiveness platform g `seq` mapM_ (lintCmmBlock labels reachable) blocks -- cmmLiveness throws an error if there are registers -- live on entry to the graph (i.e. undefined -- variables) -lintCmmBlock :: LabelSet -> CmmBlock -> CmmLint () -lintCmmBlock labels block +lintCmmBlock :: LabelSet -> LabelSet -> CmmBlock -> CmmLint () +lintCmmBlock labels reachable block = addLintInfo (text "in basic block " <> ppr (entryLabel block)) $ do let (_, middle, last) = blockSplit block mapM_ lintCmmMiddle (blockToList middle) - lintCmmLast labels last + lintCmmLast labels reachable last -- ----------------------------------------------------------------------------- -- lintCmmExpr @@ -188,8 +189,8 @@ lintCmmMiddle node = case node of lintTarget arg_tys target -lintCmmLast :: LabelSet -> CmmNode O C -> CmmLint () -lintCmmLast labels node = case node of +lintCmmLast :: LabelSet -> LabelSet -> CmmNode O C -> CmmLint () +lintCmmLast labels reachable node = case node of CmmBranch id -> checkTarget id CmmCondBranch e t f _ -> do @@ -208,7 +209,7 @@ lintCmmLast labels node = case node of CmmCall { cml_target = target, cml_cont = cont } -> do _ <- lintCmmExpr target - maybe (return ()) checkTarget cont + maybe (return ()) checkCont cont CmmForeignCall tgt _ args succ _ _ _ -> do let lintArg expr = do @@ -222,12 +223,22 @@ lintCmmLast labels node = case node of lintCmmExpr expr arg_tys <- mapM lintArg args lintTarget arg_tys tgt - checkTarget succ + checkCont succ where checkTarget id | setMember id labels = return () | otherwise = cmmLintErr (text "Branch to nonexistent id" <+> ppr id) + -- A call continuation must be reachable even when the call itself is + -- in an unreachable block: callProcPoints collects continuations from + -- the whole block map, but only reachable blocks get stack maps + -- (#27368). See Note [unreachable blocks] in GHC.Cmm.Pipeline. + checkCont id = do + checkTarget id + unless (setMember id reachable) $ + cmmLintErr (text "Call continuation" <+> ppr id + <+> text "is not reachable") + lintTarget :: [CmmType] -> ForeignTarget -> CmmLint () lintTarget _arg_tys (ForeignTarget e _) = do mayNotMentionCallerSavedRegs (text "foreign target") e ===================================== compiler/GHC/Cmm/Pipeline.hs ===================================== @@ -368,6 +368,9 @@ but no stack map, and setInfoTableStackMap would panic (#27368). The common block eliminator therefore ensures that the labels in its unreachable leftovers coincide with labels of reachable code, see Note [Resolving the CBE substitution] in GHC.Cmm.CommonBlockElim. +-dcmm-lint checks the invariant: a call continuation must be +reachable even when the call itself is not (see checkCont in +GHC.Cmm.Lint). To make unreachable blocks visible in -ddump-cmm-* output, add -dppr-debug. -} View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/cbe671f40f376ff7a5bae61d69e9e492... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/cbe671f40f376ff7a5bae61d69e9e492... 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
participants (1)
-
Simon Jakobi (@sjakobi)