Simon Jakobi pushed to branch wip/sjakobi/cmmsink-touch at Glasgow Haskell Compiler / GHC
Commits:
-
1394f191
by Simon Jakobi at 2026-09-07T14:25:58+02:00
6 changed files:
- compiler/GHC/Cmm/MachOp.hs
- compiler/GHC/Cmm/Parser.y
- compiler/GHC/Cmm/Sink.hs
- + testsuite/tests/codeGen/should_gen_asm/T27782.asm
- + testsuite/tests/codeGen/should_gen_asm/T27782.cmm
- testsuite/tests/codeGen/should_gen_asm/all.T
Changes:
| ... | ... | @@ -18,6 +18,7 @@ module GHC.Cmm.MachOp |
| 18 | 18 | |
| 19 | 19 | -- CallishMachOp
|
| 20 | 20 | , CallishMachOp(..), callishMachOpHints
|
| 21 | + , callishMachOpClobbersMemory
|
|
| 21 | 22 | , pprCallishMachOp
|
| 22 | 23 | , machOpMemcpyishAlign
|
| 23 | 24 | , callishMachOpArgTys
|
| ... | ... | @@ -876,6 +877,18 @@ callishMachOpHints op = case op of |
| 876 | 877 | _ -> ([],[])
|
| 877 | 878 | -- empty lists indicate NoHint
|
| 878 | 879 | |
| 880 | +-- | May this operation write to memory, or otherwise act as a memory
|
|
| 881 | +-- barrier? When in doubt, answer 'True'.
|
|
| 882 | +--
|
|
| 883 | +-- Used by the sinking pass to decide whether a memory read may be moved
|
|
| 884 | +-- past a call of this operation; see Note [Foreign calls clobber heap]
|
|
| 885 | +-- in GHC.Cmm.Sink.
|
|
| 886 | +callishMachOpClobbersMemory :: CallishMachOp -> Bool
|
|
| 887 | +callishMachOpClobbersMemory op = case op of
|
|
| 888 | + MO_Touch -> False -- pure liveness marker, no instructions
|
|
| 889 | + MO_Prefetch_Data _ -> False -- reads memory, writes nothing
|
|
| 890 | + _ -> True
|
|
| 891 | + |
|
| 879 | 892 | -- | The alignment of a 'memcpy'-ish operation.
|
| 880 | 893 | machOpMemcpyishAlign :: CallishMachOp -> Maybe Int
|
| 881 | 894 | machOpMemcpyishAlign op = case op of
|
| ... | ... | @@ -1203,6 +1203,8 @@ callishMachOps platform = listToUFM $ |
| 1203 | 1203 | ( "prefetch2", (MO_Prefetch_Data 2,)),
|
| 1204 | 1204 | ( "prefetch3", (MO_Prefetch_Data 3,)),
|
| 1205 | 1205 | |
| 1206 | + ( "touch", (MO_Touch,) ),
|
|
| 1207 | + |
|
| 1206 | 1208 | ( "bswap16", (MO_BSwap W16,) ),
|
| 1207 | 1209 | ( "bswap32", (MO_BSwap W32,) ),
|
| 1208 | 1210 | ( "bswap64", (MO_BSwap W64,) )
|
| ... | ... | @@ -719,7 +719,10 @@ conflicts platform assig@(r, rhs, addr) node |
| 719 | 719 | = True
|
| 720 | 720 | |
| 721 | 721 | -- (c) foreign calls clobber heap: see Note [Foreign calls clobber heap]
|
| 722 | - | CmmUnsafeForeignCall{} <- node, memConflicts addr AnyMem = True
|
|
| 722 | + | CmmUnsafeForeignCall target _ _ <- node
|
|
| 723 | + , foreignTargetClobbersMemory target
|
|
| 724 | + , memConflicts addr AnyMem
|
|
| 725 | + = True
|
|
| 723 | 726 | |
| 724 | 727 | -- (d) native calls clobber any memory
|
| 725 | 728 | | CmmCall{} <- node, memConflicts addr AnyMem = True
|
| ... | ... | @@ -732,6 +735,12 @@ conflicts platform assig@(r, rhs, addr) node |
| 732 | 735 | |
| 733 | 736 | | otherwise = False
|
| 734 | 737 | |
| 738 | +-- | May this foreign-call target write to memory, or otherwise act as a
|
|
| 739 | +-- memory barrier? See Note [Foreign calls clobber heap].
|
|
| 740 | +foreignTargetClobbersMemory :: ForeignTarget -> Bool
|
|
| 741 | +foreignTargetClobbersMemory (PrimTarget op) = callishMachOpClobbersMemory op
|
|
| 742 | +foreignTargetClobbersMemory (ForeignTarget {}) = True
|
|
| 743 | + |
|
| 735 | 744 | {- Note [Inlining foldRegsDefd]
|
| 736 | 745 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 737 | 746 | foldRegsDefd is, after optimization, *not* a small function so
|
| ... | ... | @@ -874,6 +883,16 @@ data AbsMem |
| 874 | 883 | -- `suspendThread` releases the capability used by the thread, hence we mustn't
|
| 875 | 884 | -- float accesses to heap, stack or virtual global registers stored in the
|
| 876 | 885 | -- capability (e.g. with unregisterised build, see #19237).
|
| 886 | +--
|
|
| 887 | +-- Conversely, a few CallishMachOps neither write memory nor imply a
|
|
| 888 | +-- barrier, and needn't block sinking ('callishMachOpClobbersMemory' in
|
|
| 889 | +-- GHC.Cmm.MachOp classifies them):
|
|
| 890 | +--
|
|
| 891 | +-- * MO_Touch is a pure liveness marker: no backend emits any code for it.
|
|
| 892 | +-- Sinking is intra-block, so the Core-level touch# reordering hazard
|
|
| 893 | +-- of #18040 does not arise.
|
|
| 894 | +--
|
|
| 895 | +-- * MO_Prefetch_Data reads memory but writes nothing.
|
|
| 877 | 896 | |
| 878 | 897 | |
| 879 | 898 | bothMems :: AbsMem -> AbsMem -> AbsMem
|
| 1 | +loadTouch:
|
|
| 2 | + movzbl
|
|
| 3 | + jmp
|
|
| 4 | +loadPrefetch:
|
|
| 5 | + prefetchnta
|
|
| 6 | + movzbl
|
|
| 7 | + jmp |
| 1 | +#include "Cmm.h"
|
|
| 2 | + |
|
| 3 | +// A load must sink past touch# and prefetch, which don't clobber memory,
|
|
| 4 | +// so that it can be folded into the instruction that uses it.
|
|
| 5 | + |
|
| 6 | +loadTouch (W_ p, P_ o) {
|
|
| 7 | + bits8 b;
|
|
| 8 | + b = bits8[p];
|
|
| 9 | + prim %touch(o);
|
|
| 10 | + return (%zx64(b));
|
|
| 11 | +}
|
|
| 12 | + |
|
| 13 | +loadPrefetch (W_ p, W_ q) {
|
|
| 14 | + bits8 b;
|
|
| 15 | + b = bits8[p];
|
|
| 16 | + prim %prefetch0(q);
|
|
| 17 | + return (%zx64(b));
|
|
| 18 | +} |
| ... | ... | @@ -27,6 +27,7 @@ test('T25233', [unless(arch('x86_64'), skip), |
| 27 | 27 | when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-O'])
|
| 28 | 28 | test('T25233b', [unless(arch('x86_64'), skip),
|
| 29 | 29 | when(unregisterised(), skip)], compile_grep_asm, ['cmm', True, ''])
|
| 30 | +test('T27782', is_amd64_codegen, compile_cmp_asm, ['cmm', ''])
|
|
| 30 | 31 | is_aarch64_codegen = [
|
| 31 | 32 | unless(arch('aarch64'), skip),
|
| 32 | 33 | when(unregisterised(), skip),
|