88239e06
by Andreas Klebinger at 2026-11-03T15:41:02+01:00
x86 ncg: Track the current block in NatM to keep the CFG in sync
The x86 NCG maintains a CFG that is used for code layout. Whenever
instruction selection introduces new basic blocks (for instance for
word-to-float conversions, cmpxchg based atomic operations, ctz on
i386 or 64-bit shifts on i386) it must update this CFG, and the code
that follows must use the new block as its origin.
Before this patch the current block was threaded explicitly through
some statement-level functions only. Block-introducing primops that
were dispatched as "simple" (word2Float#, #27915) and the 64-bit shifts
in iselExpr64 did not update the CFG at all. This resulted in an
out-of-sync CFG, bad code layout and the "Can't find weight for edge
that should have one" panic once the register allocator inserted
fixup blocks (#27694, #27915).
We now keep the current block in the NatM state and provide a few
combinators (continueInNewBlock, addCondBlock, addDiamondFlow,
addColdSelfLoop, increaseEdgeWeight) that update the CFG and the
current block at the same time. See Note [Updating the CFG during
CodeGen] in GHC.CmmToAsm.Monad.
The cfg sanity check (-dasm-lint) now also checks that the branches in
the generated code and the edges in the CFG match both ways (call edges
excepted), which catches this class of bug early. It immediately found
another desync: when the register allocator needs more stack than
preallocated, allocMoreStack moves the body of every entry block into a
new block and retargets the local jumps to it, but the CFG only recorded
the new block as the immediate successor of the entry block. The new
addPreentryBlock also redirects the jump edges (and keeps the call
edges, since returns still go through the entry block).
The CFG records the blocks that the code of an expression introduces in
generation order, so a store must emit the code of its address before
the code of its value. For this to be safe the amode must survive the
value code (a quotient index, fixed in eax, could be clobbered by a
64-bit conversion on i386): the stores and the atomic operations now
take their address from the new getNonClobberedAmode. Test T27694b
covers both stores.
The x86 code generator now keeps the CFG in sync on i386 too, so
backendMaintainsCfg is enabled for it: i386 gets CFG based code layout,
dead code elimination and the -dasm-lint check.
Fixes #27694, #27915
Co-authored-by: Sylvain Henry <sylvain@haskus.fr>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>