Admin message

Due to a large amount of spam we do not allow new users to create repositories, they are "external" users. If you are a new user and want to create a repository, for example for forking GHC, open a new issue on ghc/ghc using the "get-verified" issue template

CBE can eliminate the entry block, silently dropping the proc's info table (.cmm only)
```c #include "Cmm.h" INFO_TABLE_RET (zork, RET_SMALL) return () { bits64 x; x = 1 :: bits64; goto E; B: x = 1 :: bits64; goto E; E: if (x > 0 :: bits64) { goto B; } return (); } ``` ```console $ ghc-9.14.1 -c -O -dcmm-lint -I$RTS_INCLUDE EntryLose.cmm # compiles silently $ nm EntryLose.o 0000000000000000 T zork_ret # no zork_info! $ cat use.c extern char zork_info[]; int main(void){ return zork_info[0]; } $ gcc use.c EntryLose.o -o use /usr/bin/ld: ... undefined reference to `zork_info' ``` I (or rather Claude) stumbled across this while looking into #27368. Analysis from Claude: <details> If a proc's entry block is identical to another block in the same graph, common block elimination can merge the *entry* into the other block. `replaceLabels` duly redirects `g_entry`, but nothing rekeys `info_tbls`, which is keyed by the old entry label. The stale-keyed info table survives until `removeUnreachableBlocksProc` at the end of `cpsTop` prunes it together with the unreachable old entry block — so the info table that the source declares is silently never emitted. `-dcmm-lint` passes throughout. ### Why the entry loses CBE's merge order makes the block with the larger label unique the loser (`groupByInt` reverses `toBlockList` order, `groupByLabel` reverses it back, and `mergeBlockList` keeps the first block of each group as the winner). The entry block is in the losing position more often than one might think: `emitProc` allocates the entry `BlockId` *after* the body has been built (`GHC.StgToCmm.Monad`), so the entry's unique is typically the largest in the proc. In the reproducer the labels are `B = c4`, `E = c3`, entry `= c7`; CBE produces `c7 :-> c4` and `g_entry` becomes `c4`, while `info_tbls` still says `[(c7, zork_info)]`. That `emitProc` ordering applies to Haskell-generated procs as well, but Haskell-generated code cannot actually trigger the bug: every closure entry emitted by StgToCmm carries the proc's single stack check (see Note [Single stack check] in GHC.StgToCmm.Heap), whose `CmmHighStackMark` is still symbolic when CBE runs (CBE precedes stack layout) and occurs in no other block of the graph — so no sibling block can ever be CBE-equal to the entry. The bug is thus limited to hand-written Cmm (including the RTS's `.cmm` files), which gets no implicit stack check. ### Trace of the silent loss With `-ddump-cmm-verbose-by-proc`: - Parsed Cmm: `info_tbls: [(c7, ...)]`, entry `c7`. - Post CBE: entry is `c4`; `c7` unreachable (still present). - After `setInfoTableStackMap`: info table still keyed `c7`, now `rep: StackRep []` — the old entry got a stack map before becoming unreachable, so the `setInfoTableStackMap` panic (cf. #27368) does not fire. - Post control-flow optimisations (2) / `removeUnreachableBlocksProc`: `info_tbls: []` — pruned along with the unreachable block. - NCG: no info table emitted. ### Possible fixes - Rekey `info_tbls` when CBE renames labels, as the control-flow optimiser already does (`upd_info` in `cmmCfgOptsProc`, which also preserves `cit_lbl` for the entry since that label is externally visible); or - never let a block with an info table (or at least the entry block) lose a merge — e.g. skip blocks in `mapKeysSet (info_tbls)` ∪ `{g_entry}` when building CBE's substitution domain. </details> It doesn't currently seem possible to reproduce in pure Haskell, as every closure-entry proc's entry block contains the proc's stack check at CBE time, and no other block in the graph can contain the same check. So it doesn't seem easy to run into this bug by accident, but it might be worth fixing anyway.
issue