Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC Commits: a2208b21 by Simon Jakobi at 2026-11-03T07:02:05-05:00 Cmm: exclude the entry block from common block elimination a1d8139054c had fixed #27722 only partially, because it didn't prevent elimination of the entry block in later rounds, where the entry block is no longer guaranteed to come first. To close this loophole, we exclude the entry block from the CBE merge logic. See Note [Retain entry block during common block elimination] for details. This also deletes a misleading comment regarding unreachable blocks: the control-flow optimiser doesn't actually guarantee that no unreachable blocks reach CBE. Fixes #27722. Assisted-by: Claude Fable 5.1 - - - - - 5 changed files: - changelog.d/T27722-cbe-entry-block.md - compiler/GHC/Cmm/CommonBlockElim.hs - + testsuite/tests/cmm/should_run/T27722.hs - + testsuite/tests/cmm/should_run/T27722_cmm.cmm - testsuite/tests/cmm/should_run/all.T Changes: ===================================== changelog.d/T27722-cbe-entry-block.md ===================================== @@ -1,5 +1,5 @@ section: cmm issues: #27722 -mrs: !16592 +mrs: !16592 !16861 synopsis: Fix common block elimination dropping entry block info table in hand written cmm. ===================================== compiler/GHC/Cmm/CommonBlockElim.hs ===================================== @@ -66,25 +66,23 @@ elimCommonBlocks g = where g' = replaceLabels env $ copyTicks env g 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. - -- One exception: The entry block most come first or we risk eliminating it - -- in favour of another block. See Note [Retain entry block during common block elimination.] - groups = groupByInt hash_block (toBlockListEntryFirst g) :: [[CmmBlock]] blocks_with_key = [ [ (successors b, [b]) | b <- bs] | bs <- groups] - --- Note [Retain entry block during common block elimination.] --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- At the stage we run common block elimination (CBE) we only have one info --- table for the entry label. Which means we can get away without applying the --- block label substitution to the info table *as long as we keep the first block*. --- When combining blocks the first block in the list of blocks is kept, and the later --- one eliminated, so we can achieve this by simply using toBlockListEntryFirst. + groups = groupByInt hash_block candidates :: [[CmmBlock]] + -- See Note [Retain entry block during common block elimination] + candidates = filter ((/= g_entry g) . entryLabel) (toBlockList g) + +-- Note [Retain entry block during common block elimination] +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-- When CBE runs, only the entry label has an info table, so we can get away +-- without applying the block label substitution to info tables, as long as we +-- preserve the entry block (#27722). We achieve this by simply keeping it out +-- of the list of candidates for elimination. -- --- If we don't we end up with #27722 where the entry block was eliminated in favour --- of another block. +-- The downside is that any duplicates of the entry block stay unmerged. This +-- should be very rare though, and only affects hand-written Cmm: the entry +-- block of any proc generated from STG contains the stack check (see +-- entryHeapCheck in GHC.StgToCmm.Heap) and is therefore distinct from any +-- other block in the same proc. -- Invariant: The blocks in the list are pairwise distinct -- (so avoid comparing them again) ===================================== testsuite/tests/cmm/should_run/T27722.hs ===================================== @@ -0,0 +1,8 @@ +import Control.Monad +import Foreign.Ptr +import System.Exit + +foreign import ccall "&zork_info" zork_info :: Ptr () + +main :: IO () +main = when (zork_info == nullPtr) exitFailure ===================================== testsuite/tests/cmm/should_run/T27722_cmm.cmm ===================================== @@ -0,0 +1,29 @@ +#include "Cmm.h" + +// Regression test for #27722: common block elimination (CBE) must not +// eliminate the entry block, because the proc's info table refers to it. +// With the bug, zork_info is never emitted and T27722.hs fails to link. +// +// The entry block only becomes a duplicate in the third round of CBE: +// round 1 merges the two then-blocks (both duplicates of the entry +// block), round 2 merges E1 and E2, and only then does the entry block +// match the surviving then-block. +// +// The then-blocks are braced if-bodies so that, like the entry block, +// they carry two source notes (braces and statement): CBE's block hash +// counts tick nodes (#27877), and blocks with different hashes are never +// compared. +INFO_TABLE_RET (zork, RET_SMALL) + return () +{ + bits64 x; + x = 1 :: bits64; + goto E1; +E1: + if (x > 0 :: bits64) { x = 1 :: bits64; goto E2; } else { goto D; } +E2: + if (x > 0 :: bits64) { x = 1 :: bits64; goto E2; } else { goto D; } +D: + if (x > 1 :: bits64) goto E1; + goto E2; +} ===================================== testsuite/tests/cmm/should_run/all.T ===================================== @@ -60,3 +60,6 @@ test('T27447', ], multi_compile_and_run, ['T27447', [('T27447_cmm.cmm', '')], '']) + +test('T27722', [req_cmm], multi_compile_and_run, + ['T27722', [('T27722_cmm.cmm', '')], '-O']) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a2208b21fcb5b417e7dd535295eb823e... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a2208b21fcb5b417e7dd535295eb823e... 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