Simon Jakobi pushed to branch wip/sjakobi/T25450-march-native at Glasgow Haskell Compiler / GHC

Commits:

25 changed files:

Changes:

  • PLAN.md
    1
    +# Implement `-march=native` for x86/x86_64 Using `GHC.Driver.CpuFeatures`
    
    2
    +
    
    3
    +## Summary
    
    4
    +Add support for `-march=native` (x86/x86_64, native compiler only). The flag probes the
    
    5
    +host CPU at parse time via an in-process CPUID/XGETBV helper and enables the matching
    
    6
    +CPU-feature `DynFlags`, so the effect applies to both the NCG and LLVM backends.
    
    7
    +
    
    8
    +This branch is based on `wip/sjakobi/T25450-print-cpu`, which already provides
    
    9
    +`ghc --print-enabled-cpu-features` and the `enabledCpuFeatures` / `showEnabledCpuFeatures`
    
    10
    +machinery in `GHC.Driver.Session`. That print mode is the primary tool for verifying and
    
    11
    +testing `-march=native`: detection sets `DynFlags`, the print mode reports them.
    
    12
    +
    
    13
    +## Public Interface Changes
    
    14
    +1. New dynamic flag: `-march=native`.
    
    15
    +2. New users-guide docs entry under platform flags.
    
    16
    +3. Behavior constraints:
    
    17
    +   - Supported only for target `x86`/`x86_64`.
    
    18
    +   - Rejected for cross-compilation (host CPU is irrelevant to a different target).
    
    19
    +   - Additive: applied once after the whole flag fold, it enables host features
    
    20
    +     *in addition* to any explicit `-m...` flags (order-independent, never disables).
    
    21
    +
    
    22
    +## Module / Architecture Changes
    
    23
    +1. `compiler/cbits/cpu_features_x86.c` — in-process CPUID/XGETBV probe (done).
    
    24
    +2. `compiler/GHC/Driver/CpuFeatures.hs` — mask decoder + naming (done). Build wiring in
    
    25
    +   `compiler/ghc.cabal.in` (done).
    
    26
    +3. `compiler/GHC/Driver/DynFlags.hs` — add a marker field `marchNative :: Bool` (default
    
    27
    +   `False`).
    
    28
    +4. `compiler/GHC/Driver/Session.hs` — flag registration + the IO application step (see below).
    
    29
    +
    
    30
    +## Detailed Implementation
    
    31
    +
    
    32
    +### 1. Flag parsing — pure marker only
    
    33
    +- File: `compiler/GHC/Driver/Session.hs`, near the existing `-m...` flags (~line 1710).
    
    34
    +- `make_ord_flag defGhcFlag "march=native" (noArg (\d -> d { marchNative = True }))`.
    
    35
    +- IMPORTANT: flag handlers run in `DynP = EwM (CmdLineP DynFlags)`, which is **pure** — they
    
    36
    +  cannot run the CPUID probe. The handler only records the request via the marker field.
    
    37
    +- Flag-surface note: a literal `"march=native"` entry means `-march=<other>` falls through to
    
    38
    +  a generic "unrecognised flag" error. For v1 this is acceptable; document that only `native`
    
    39
    +  is supported. (Optionally parse the `=value` later to emit a targeted message.)
    
    40
    +
    
    41
    +### 2. Feature application — the IO step
    
    42
    +- File: `compiler/GHC/Driver/Session.hs`, inside `parseDynamicFlagsFull`.
    
    43
    +- `parseDynamicFlagsFull` is already `MonadIO` and already performs `liftIO` work between the
    
    44
    +  pure flag fold (`dflags1`) and `makeDynFlagsConsistent` (`dflags2`/`dflags3`). Insert the
    
    45
    +  application there: after `dflags1`, before `makeDynFlagsConsistent`.
    
    46
    +  - If `marchNative dflags1` is set:
    
    47
    +    - Validate target arch is `ArchX86`/`ArchX86_64`; otherwise hard error.
    
    48
    +    - Validate not cross-compiling; otherwise hard error. (See open question on detection.)
    
    49
    +    - `feats <- liftIO detectX86CpuFeatures` (cached — see §4).
    
    50
    +    - Apply `feats` to `DynFlags` via a dedicated mapping helper (see §3), producing `dflags1'`.
    
    51
    +  - `makeDynFlagsConsistent` then propagates feature implications — do not reimplement them.
    
    52
    +- This path is shared with `parseDynamicFilePragma`, so the same logic also covers
    
    53
    +  `{-# OPTIONS_GHC -march=native #-}`. Decide whether to permit that; if so, caching matters.
    
    54
    +
    
    55
    +### 3. Mapping detected features to `DynFlags`
    
    56
    +The detected list is granular (`SSE2, SSE3, …, AVX2, BMI1, BMI2, …`), but several `DynFlags`
    
    57
    +fields are single ordered levels, so the mapper must **collapse ladders to their maximum**:
    
    58
    +- SSE/AVX ladder → highest present → `sseAvxVersion :: Maybe SseAvxVersion`
    
    59
    +  (`DynFlags.hs:461`). The `is*Enabled` predicates (`DynFlags.hs:1621-1642`) already imply the
    
    60
    +  lower rungs, so only the max level needs to be set.
    
    61
    +- BMI1/BMI2 → highest present → `bmiVersion :: Maybe BmiVersion`.
    
    62
    +- `AVX512F` + per-extension features → the individual bools `avx512f`, `avx512bw`,
    
    63
    +  `avx512cd`, `avx512dq`, `avx512vl`.
    
    64
    +- `FMA``fma`; `GFNI``gfni`.
    
    65
    +- `SSE2` is the x86_64 baseline (determined by the platform, not a flag) — nothing to set.
    
    66
    +
    
    67
    +### 4. Probe caching
    
    68
    +CPUID results are constant for the lifetime of the process, but `parseDynamicFlagsFull` runs
    
    69
    +per command-line *and* per file pragma. Memoize the probe once (top-level CAF backed by
    
    70
    +`unsafePerformIO` with `{-# NOINLINE #-}`, or detect once and thread the result) so repeated
    
    71
    +`-march=native` uses don't re-probe.
    
    72
    +
    
    73
    +### 5. Diagnostics and errors
    
    74
    +- Unsupported arch: clear error that `-march=native` is x86/x86_64-only.
    
    75
    +- Cross mode: clear error that `-march=native` is unavailable for cross-compilation.
    
    76
    +- The probe is in-process and returns a `Word64`; there is no subprocess, command, or stderr
    
    77
    +  to report. The only anomaly is a probe returning `0` (or lacking SSE2 on x86_64, where it is
    
    78
    +  baseline) — treat that as an internal error with a short diagnostic. (The earlier
    
    79
    +  "failing command + brief stderr" wording referred to an abandoned toolchain-probe approach
    
    80
    +  and no longer applies.)
    
    81
    +
    
    82
    +### 6. Backend behavior
    
    83
    +- NCG and LLVM are both driven by the same CPU-feature `DynFlags`, so no backend wiring
    
    84
    +  changes are needed — populating the fields in §3 is sufficient.
    
    85
    +
    
    86
    +### 7. Verification via `--print-enabled-cpu-features`
    
    87
    +- The base branch already prints effective enabled features as single-line JSON:
    
    88
    +  `{"tag":"enabled-cpu-features","version":1,"target":<triple>,"features":[…],"as_m_flags":[…]}`
    
    89
    +  with conventional names (`SSE4.2`, `AVX2`, `BMI2`) in canonical order.
    
    90
    +- `-march=native` must update the same `DynFlags` fields this mode reads, so its effect is
    
    91
    +  observable through `ghc -march=native --print-enabled-cpu-features`.
    
    92
    +
    
    93
    +### 8. Documentation
    
    94
    +- File: `docs/users_guide/using.rst`, alongside the other `-m...` options.
    
    95
    +- Describe: host CPU detection, x86/x86_64 only, non-cross only, positional, equivalent to
    
    96
    +  enabling the matching `-m...` options automatically.
    
    97
    +- Add a `changelog.d/` entry (the base branch already adds `changelog.d/print-enabled-cpu-features`).
    
    98
    +
    
    99
    +## Tests and Scenarios
    
    100
    +Lead with `--print-enabled-cpu-features` for deterministic, host-independent assertions.
    
    101
    +
    
    102
    +1. **Baseline (deterministic):** on x86_64, `ghc -march=native --print-enabled-cpu-features`
    
    103
    +   always lists `SSE2` in `features`.
    
    104
    +2. **Superset:** `features(-march=native)``features()` (run the print mode with and without
    
    105
    +   the flag and compare). Avoids hard-coding host-specific feature sets.
    
    106
    +3. **Additive:** an explicit `-m...` combined with `-march=native` yields the union (the
    
    107
    +   explicit feature is still present regardless of flag order).
    
    108
    +4. **Arch / cross guards:** compile-fail test for a non-x86 target and/or a cross context,
    
    109
    +   asserting the §5 error messages.
    
    110
    +5. **Optional NCG end-to-end:** one host-gated asm-grep test under
    
    111
    +   `testsuite/tests/codeGen/should_gen_asm` showing `-march=native` enables an expected x86
    
    112
    +   instruction path. Keep it secondary to the print-mode tests; do not rely on it as the main
    
    113
    +   signal. (There is no longer a meaningful probe-failure compile-fail test, since the probe
    
    114
    +   has no external tool to break.)
    
    115
    +
    
    116
    +## Open Questions
    
    117
    +1. **Cross-compilation detection.** *Resolved.* The guard compares the target arch against the
    
    118
    +   build-host arch: `arch == hostPlatformArch` in `applyMarchNative` (`Session.hs:3831`). A
    
    119
    +   mismatch raises the cross-compilation error from §5.
    
    120
    +2. **File-pragma scope.** *Resolved (permitted).* `applyMarchNative` runs in the shared
    
    121
    +   `parseDynamicFlagsFull` (`Session.hs:913`), so `-march=native` also takes effect in
    
    122
    +   `{-# OPTIONS_GHC #-}`. Repeated probing is harmless because the probe is memoized
    
    123
    +   (`cachedX86CpuFeatures`, §4).
    
    124
    +
    
    125
    +## Status
    
    126
    +1. Done: CPUID/XGETBV probe in `compiler/cbits/cpu_features_x86.c`.
    
    127
    +2. Done: decoder + naming in `compiler/GHC/Driver/CpuFeatures.hs`.
    
    128
    +3. Done: cabal wiring in `compiler/ghc.cabal.in`.
    
    129
    +4. Done (base branch): `--print-enabled-cpu-features` + `enabledCpuFeatures` reporting.
    
    130
    +5. Done: `marchNative` marker field in `DynFlags` (`DynFlags.hs:473,765`); flag registration
    
    131
    +   in `Session.hs:1752`.
    
    132
    +6. Done: IO application step `applyMarchNative` in `parseDynamicFlagsFull`
    
    133
    +   (`Session.hs:3816`) — arch guard, cross guard, and the ladder mapping in
    
    134
    +   `applyX86CpuFeatures` (`Session.hs:3839`).
    
    135
    +7. Done: probe caching via `cachedX86CpuFeatures` (top-level CAF, `NOINLINE`,
    
    136
    +   `CpuFeatures.hs:89`).
    
    137
    +8. Done: docs (`docs/users_guide/using.rst:1857`) and changelog (`changelog.d/march-native`).
    
    138
    +9. Tests. In `testsuite/tests/driver`: baseline SSE2 (`march_native`, §1), arch guard
    
    139
    +   (`march_native_unsupported_arch`, §4), superset (`march_native_superset`, §2), and additive
    
    140
    +   (`march_native_additive`, §3). In `testsuite/tests/codeGen/should_gen_asm`: NCG asm-grep
    
    141
    +   (`march-native-enables-popcnt`, §5) — gated on the host having SSE4.2 via
    
    142
    +   `have_cpu_feature`, so it deterministically expects a `popcnt` instruction.

  • changelog.d/march-native
    1
    +section: compiler
    
    2
    +synopsis: Add -march=native flag
    
    3
    +issues: #25450
    
    4
    +
    
    5
    +description:
    
    6
    +  GHC now supports ``-march=native`` on x86 and x86_64. It probes the CPU of the
    
    7
    +  machine running GHC and enables all of the corresponding ``-m...`` CPU-feature
    
    8
    +  options automatically (such as ``-msse4.2``, ``-mavx2``, ``-mbmi2`` and
    
    9
    +  ``-mfma``), for both the native code generator and the LLVM backend. The
    
    10
    +  detected features are enabled in addition to any explicitly requested feature
    
    11
    +  flags. The flag is rejected for non-x86 targets and when cross-compiling.

  • changelog.d/print-enabled-cpu-features
    1
    +section: compiler
    
    2
    +synopsis: Add --print-enabled-cpu-features flag
    
    3
    +issues: #25450
    
    4
    +mrs: !16117
    
    5
    +
    
    6
    +description:
    
    7
    +  GHC now supports a new mode flag ``--print-enabled-cpu-features``, which
    
    8
    +  prints a JSON object describing the CPU features currently enabled for code
    
    9
    +  generation, together with a set of ``-m...`` flags that reproduce the
    
    10
    +  effective feature set for the current target.
    
    11
    +  Dynamic options such as ``-mavx2`` and ``-mbmi2`` are respected. ::
    
    12
    +
    
    13
    +    $ ghc -mavx2 --print-enabled-cpu-features
    
    14
    +    {"tag":"enabled-cpu-features","version":1,"target":"x86_64-linux-gnu",
    
    15
    +     "features":["SSE","SSE2","SSE3","SSSE3","SSE4.1","SSE4.2","AVX","AVX2"],
    
    16
    +     "as_m_flags":["-mavx2"]}

  • compiler/GHC/Driver/CpuFeatures.hs
    1
    +{-# LANGUAGE CPP #-}
    
    2
    +
    
    3
    +module GHC.Driver.CpuFeatures
    
    4
    +  ( X86CpuFeature(..)
    
    5
    +  , detectX86CpuFeatureMask
    
    6
    +  , detectX86CpuFeatures
    
    7
    +  , cachedX86CpuFeatures
    
    8
    +  , decodeX86CpuFeatureMask
    
    9
    +  , x86CpuFeatureConventionalName
    
    10
    +  ) where
    
    11
    +
    
    12
    +import GHC.Prelude
    
    13
    +
    
    14
    +import Data.Bits (testBit)
    
    15
    +import Data.Word (Word64)
    
    16
    +import System.IO.Unsafe (unsafePerformIO)
    
    17
    +
    
    18
    +-- | x86 CPU features understood by GHC's native CPU feature probe.
    
    19
    +--
    
    20
    +-- The constructor order and decode order below are canonicalized for stable
    
    21
    +-- debug output.
    
    22
    +data X86CpuFeature
    
    23
    +  = SSE2
    
    24
    +  | SSE3
    
    25
    +  | SSSE3
    
    26
    +  | SSE4_1
    
    27
    +  | SSE4_2
    
    28
    +  | AVX
    
    29
    +  | AVX2
    
    30
    +  | AVX512F
    
    31
    +  | AVX512BW
    
    32
    +  | AVX512CD
    
    33
    +  | AVX512DQ
    
    34
    +  | AVX512VL
    
    35
    +  | BMI1
    
    36
    +  | BMI2
    
    37
    +  | FMA
    
    38
    +  | GFNI
    
    39
    +  deriving (Eq, Ord, Show)
    
    40
    +
    
    41
    +-- | Human-readable names used in the JSON/debug output.
    
    42
    +x86CpuFeatureConventionalName :: X86CpuFeature -> String
    
    43
    +x86CpuFeatureConventionalName feat = case feat of
    
    44
    +  SSE2    -> "SSE2"
    
    45
    +  SSE3    -> "SSE3"
    
    46
    +  SSSE3   -> "SSSE3"
    
    47
    +  SSE4_1  -> "SSE4.1"
    
    48
    +  SSE4_2  -> "SSE4.2"
    
    49
    +  AVX     -> "AVX"
    
    50
    +  AVX2    -> "AVX2"
    
    51
    +  AVX512F -> "AVX512F"
    
    52
    +  AVX512BW -> "AVX512BW"
    
    53
    +  AVX512CD -> "AVX512CD"
    
    54
    +  AVX512DQ -> "AVX512DQ"
    
    55
    +  AVX512VL -> "AVX512VL"
    
    56
    +  BMI1    -> "BMI1"
    
    57
    +  BMI2    -> "BMI2"
    
    58
    +  FMA     -> "FMA"
    
    59
    +  GFNI    -> "GFNI"
    
    60
    +
    
    61
    +-- | Decode the bitmask returned by 'ghc_detect_x86_cpu_features'.
    
    62
    +--
    
    63
    +-- NOTE: Bit positions must match the enum in @compiler/cbits/cpu_features_x86.c@.
    
    64
    +decodeX86CpuFeatureMask :: Word64 -> [X86CpuFeature]
    
    65
    +decodeX86CpuFeatureMask mask =
    
    66
    +  [ feat
    
    67
    +  | (bit_ix, feat) <- cpuFeatureBitLayout
    
    68
    +  , testBit mask bit_ix
    
    69
    +  ]
    
    70
    +
    
    71
    +-- | Low-level FFI access to the C probe.
    
    72
    +detectX86CpuFeatureMask :: IO Word64
    
    73
    +#if defined(javascript_HOST_ARCH)
    
    74
    +detectX86CpuFeatureMask = pure 0
    
    75
    +#else
    
    76
    +detectX86CpuFeatureMask = c_ghc_detect_x86_cpu_features
    
    77
    +#endif
    
    78
    +
    
    79
    +-- | Probe host x86 CPU features and decode them into an ordered feature list.
    
    80
    +detectX86CpuFeatures :: IO [X86CpuFeature]
    
    81
    +detectX86CpuFeatures = decodeX86CpuFeatureMask <$> detectX86CpuFeatureMask
    
    82
    +
    
    83
    +-- | The host's x86 CPU features, probed once and memoized.
    
    84
    +--
    
    85
    +-- CPUID results are constant for the lifetime of the process, so probing more
    
    86
    +-- than once (e.g. once per @-march=native@ in a command line or file pragma)
    
    87
    +-- is wasteful. This is referentially transparent despite the FFI call.
    
    88
    +cachedX86CpuFeatures :: [X86CpuFeature]
    
    89
    +cachedX86CpuFeatures = unsafePerformIO detectX86CpuFeatures
    
    90
    +{-# NOINLINE cachedX86CpuFeatures #-}
    
    91
    +
    
    92
    +cpuFeatureBitLayout :: [(Int, X86CpuFeature)]
    
    93
    +cpuFeatureBitLayout =
    
    94
    +  [ (0,  SSE2)
    
    95
    +  , (1,  SSE3)
    
    96
    +  , (2,  SSSE3)
    
    97
    +  , (3,  SSE4_1)
    
    98
    +  , (4,  SSE4_2)
    
    99
    +  , (5,  AVX)
    
    100
    +  , (6,  AVX2)
    
    101
    +  , (7,  AVX512F)
    
    102
    +  , (8,  AVX512BW)
    
    103
    +  , (9,  AVX512CD)
    
    104
    +  , (10, AVX512DQ)
    
    105
    +  , (11, AVX512VL)
    
    106
    +  , (12, BMI1)
    
    107
    +  , (13, BMI2)
    
    108
    +  , (14, FMA)
    
    109
    +  , (15, GFNI)
    
    110
    +  ]
    
    111
    +
    
    112
    +#if !defined(javascript_HOST_ARCH)
    
    113
    +foreign import ccall unsafe "ghc_detect_x86_cpu_features"
    
    114
    +  c_ghc_detect_x86_cpu_features :: IO Word64
    
    115
    +#endif

  • compiler/GHC/Driver/DynFlags.hs
    ... ... @@ -470,6 +470,8 @@ data DynFlags = DynFlags {
    470 470
       fma                   :: Bool, -- ^ Enable FMA instructions.
    
    471 471
       gfni                  :: Bool, -- ^ Enable GFNI Instructions.
    
    472 472
       la664                 :: Bool, -- ^ Enable LA664 instructions
    
    473
    +  marchNative           :: Bool, -- ^ @-march=native@ was requested; the host
    
    474
    +                                 -- CPU features are applied during flag parsing.
    
    473 475
     
    
    474 476
       -- Constants used to control the amount of optimization done.
    
    475 477
     
    
    ... ... @@ -760,6 +762,7 @@ defaultDynFlags mySettings =
    760 762
             gfni = False,
    
    761 763
             -- For LoongArch, la464 is used by default.
    
    762 764
             la664 = False,
    
    765
    +        marchNative = False,
    
    763 766
     
    
    764 767
             maxInlineAllocSize = 128,
    
    765 768
             maxInlineMemcpyInsns = 32,
    
    ... ... @@ -1615,6 +1618,7 @@ initPromotionTickContext dflags =
    1615 1618
     
    
    1616 1619
     -- -----------------------------------------------------------------------------
    
    1617 1620
     -- SSE, AVX, FMA
    
    1621
    +-- See Note [Keeping enabledCpuFeatures in sync] in GHC.Driver.Session
    
    1618 1622
     
    
    1619 1623
     isSse3Enabled :: DynFlags -> Bool
    
    1620 1624
     isSse3Enabled dflags = sseAvxVersion dflags >= Just SSE3 || isAvxEnabled dflags
    
    ... ... @@ -1705,11 +1709,14 @@ We handle this as follows:
    1705 1709
     
    
    1706 1710
     -- -----------------------------------------------------------------------------
    
    1707 1711
     -- LA664
    
    1712
    +-- See Note [Keeping enabledCpuFeatures in sync] in GHC.Driver.Session
    
    1713
    +
    
    1708 1714
     isLa664Enabled :: DynFlags -> Bool
    
    1709 1715
     isLa664Enabled dflags = la664 dflags
    
    1710 1716
     
    
    1711 1717
     -- -----------------------------------------------------------------------------
    
    1712 1718
     -- BMI2
    
    1719
    +-- See Note [Keeping enabledCpuFeatures in sync] in GHC.Driver.Session
    
    1713 1720
     
    
    1714 1721
     isBmiEnabled :: DynFlags -> Bool
    
    1715 1722
     isBmiEnabled dflags = case platformArch (targetPlatform dflags) of
    

  • compiler/GHC/Driver/Session.hs
    ... ... @@ -196,6 +196,8 @@ module GHC.Driver.Session (
    196 196
     
    
    197 197
             -- * Compiler configuration suitable for display to the user
    
    198 198
             compilerInfo,
    
    199
    +        showEnabledCpuFeatures,
    
    200
    +        enabledCpuFeatures,
    
    199 201
     
    
    200 202
             targetHasRTSWays,
    
    201 203
     
    
    ... ... @@ -243,6 +245,8 @@ import GHC.Platform
    243 245
     import GHC.Platform.Ways
    
    244 246
     import GHC.Platform.Profile
    
    245 247
     import GHC.Platform.ArchOS
    
    248
    +import GHC.Platform.Host (hostPlatformArch)
    
    249
    +import qualified GHC.Driver.CpuFeatures as Cpu
    
    246 250
     
    
    247 251
     import GHC.Unit.Types
    
    248 252
     import GHC.Unit.Parser
    
    ... ... @@ -277,6 +281,7 @@ import GHC.Utils.TmpFs
    277 281
     import GHC.Utils.Fingerprint
    
    278 282
     import GHC.Utils.Outputable
    
    279 283
     import GHC.Utils.Error (emptyDiagOpts, logInfo)
    
    284
    +import GHC.Utils.Json
    
    280 285
     import GHC.Settings
    
    281 286
     import GHC.CmmToAsm.CFG.Weight
    
    282 287
     import GHC.Core.Opt.CallerCC
    
    ... ... @@ -903,8 +908,12 @@ parseDynamicFlagsFull activeFlags cmdline logger dflags0 args = do
    903 908
       unless (null errs) $ liftIO $ throwGhcExceptionIO $ errorsToGhcException $
    
    904 909
         map ((rdr . ppr . getLoc &&& unLoc) . errMsg) $ errs
    
    905 910
     
    
    911
    +  -- Apply -march=native: probe the host CPU and enable the matching feature
    
    912
    +  -- flags. This needs IO (CPUID), so it cannot live in the pure flag handlers.
    
    913
    +  dflags1' <- applyMarchNative dflags1
    
    914
    +
    
    906 915
       -- check for disabled flags in safe haskell
    
    907
    -  let (dflags2, sh_warns) = safeFlagCheck cmdline dflags1
    
    916
    +  let (dflags2, sh_warns) = safeFlagCheck cmdline dflags1'
    
    908 917
           theWays = ways dflags2
    
    909 918
     
    
    910 919
       unless (allowed_combination theWays) $ liftIO $
    
    ... ... @@ -1740,6 +1749,7 @@ dynamic_flags_deps = [
    1740 1749
       , make_ord_flag defGhcFlag "mavx512vl"    (noArg (\d -> d { avx512vl = True }))
    
    1741 1750
       , make_ord_flag defGhcFlag "mfma"         (noArg (\d -> d { fma = True }))
    
    1742 1751
       , make_ord_flag defGhcFlag "mgfni"        (noArg (\d -> d { gfni = True }))
    
    1752
    +  , make_ord_flag defGhcFlag "march=native" (noArg (\d -> d { marchNative = True }))
    
    1743 1753
     
    
    1744 1754
     
    
    1745 1755
       , make_ord_flag defGhcFlag "mla664"       (noArg (\d -> d { la664 = True }))
    
    ... ... @@ -3677,6 +3687,185 @@ compilerInfo dflags
    3677 3687
         queryCmdMaybe p f = expandDirectories (query (maybe "" (prgPath . p) . f))
    
    3678 3688
         queryFlagsMaybe p f = query (maybe "" (unwords . map escapeArg . prgFlags . p) . f)
    
    3679 3689
     
    
    3690
    +showEnabledCpuFeatures :: DynFlags -> String
    
    3691
    +showEnabledCpuFeatures dflags = showSDocUnsafe $ renderJSON $ JSObject
    
    3692
    +  [ ("tag", JSString "enabled-cpu-features")
    
    3693
    +    -- Schema version of this JSON object; bump it whenever the shape or
    
    3694
    +    -- meaning of the fields changes, so consumers can detect incompatibility.
    
    3695
    +  , ("version", JSInt 1)
    
    3696
    +  , ("target", JSString (platformMisc_targetPlatformString (platformMisc dflags)))
    
    3697
    +  , ("features", JSArray (map JSString features))
    
    3698
    +    -- A set of `-m...` flags that, passed to GHC for this target, reproduce
    
    3699
    +    -- the effective feature set above. Note this need not be the flags the
    
    3700
    +    -- user actually passed: implied features are folded in, and a feature
    
    3701
    +    -- enabled by default may be reproduced by the empty set.
    
    3702
    +  , ("as_m_flags", JSArray (map JSString asMFlags))
    
    3703
    +  ]
    
    3704
    +  where
    
    3705
    +    (features, asMFlags) = enabledCpuFeatures dflags
    
    3706
    +
    
    3707
    +{- Note [Keeping enabledCpuFeatures in sync]
    
    3708
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    3709
    +`enabledCpuFeatures` must be updated whenever a new CPU feature flag is added
    
    3710
    +to GHC. The three places to touch are, all in GHC.Driver.DynFlags:
    
    3711
    +
    
    3712
    +  1. The flag registration (e.g. `make_ord_flag defGhcFlag "mnewfeat" ...`)
    
    3713
    +  2. The corresponding `is*Enabled` predicate
    
    3714
    +  3. The `enabledCpuFeatures` function below — add the feature to `features`
    
    3715
    +     and, if it has a GHC `-m...` flag, to `as_m_flags` via the appropriate
    
    3716
    +     architecture branch.
    
    3717
    +
    
    3718
    +See Note [Implications between X86 CPU feature flags] in GHC.Driver.DynFlags
    
    3719
    +for the implication structure that `x86FeaturesAndFlags` and `x86AsMFlags`
    
    3720
    +must respect.
    
    3721
    +-}
    
    3722
    +
    
    3723
    +enabledCpuFeatures :: DynFlags -> ([String], [String])
    
    3724
    +enabledCpuFeatures dflags = case platformArch (targetPlatform dflags) of
    
    3725
    +  ArchX86_64 -> x86FeaturesAndFlags dflags
    
    3726
    +  ArchX86    -> x86FeaturesAndFlags dflags
    
    3727
    +  ArchLoongArch64 ->
    
    3728
    +    ( fmaFeature ++ [ "LA664"   | isLa664Enabled dflags ]
    
    3729
    +    , fmaFlag    ++ [ "-mla664" | isLa664Enabled dflags ]
    
    3730
    +    )
    
    3731
    +  _ -> (fmaFeature, fmaFlag)
    
    3732
    +  where
    
    3733
    +    -- `-mfma` is a cross-platform flag. On x86 it is folded into the
    
    3734
    +    -- SSE/AVX hierarchy (handled in x86FeaturesAndFlags); on every other
    
    3735
    +    -- architecture FMA stands on its own and gates FMA codegen via
    
    3736
    +    -- isFmaEnabled in stgToCmmAllowFMAInstr. `fma dflags` is the source of
    
    3737
    +    -- truth (it defaults to True on AArch64).
    
    3738
    +    fmaFeature = [ "FMA"   | fma dflags ]
    
    3739
    +    fmaFlag    = [ "-mfma" | fma dflags ]
    
    3740
    +
    
    3741
    +x86FeaturesAndFlags :: DynFlags -> ([String], [String])
    
    3742
    +x86FeaturesAndFlags dflags =
    
    3743
    +  -- SSE/SSE2 are determined by the target platform rather than a dynamic
    
    3744
    +  -- flag, hence those predicates take Platform while the others take DynFlags.
    
    3745
    +  ( [ "SSE"      | isSseEnabled platform ]
    
    3746
    + ++ [ "SSE2"     | isSse2Enabled platform ]
    
    3747
    + ++ [ "SSE3"     | isSse3Enabled dflags ]
    
    3748
    + ++ [ "SSSE3"    | isSsse3Enabled dflags ]
    
    3749
    + ++ [ "SSE4.1"   | isSse4_1Enabled dflags ]
    
    3750
    + ++ [ "SSE4.2"   | isSse4_2Enabled dflags ]
    
    3751
    + ++ [ "AVX"      | isAvxEnabled dflags ]
    
    3752
    + ++ [ "AVX2"     | isAvx2Enabled dflags ]
    
    3753
    + ++ [ "AVX512F"  | isAvx512fEnabled dflags ]
    
    3754
    + ++ [ "AVX512BW" | isAvx512bwEnabled dflags ]
    
    3755
    + ++ [ "AVX512CD" | isAvx512cdEnabled dflags ]
    
    3756
    + ++ [ "AVX512DQ" | isAvx512dqEnabled dflags ]
    
    3757
    + ++ [ "AVX512ER" | isAvx512erEnabled dflags ]
    
    3758
    + ++ [ "AVX512PF" | isAvx512pfEnabled dflags ]
    
    3759
    + ++ [ "AVX512VL" | isAvx512vlEnabled dflags ]
    
    3760
    + ++ [ "BMI1"     | isBmiEnabled dflags ]
    
    3761
    + ++ [ "BMI2"     | isBmi2Enabled dflags ]
    
    3762
    + ++ [ "FMA"      | isFmaEnabled dflags ]
    
    3763
    + ++ [ "GFNI"     | isGfniEnabled dflags ]
    
    3764
    +  , x86AsMFlags dflags
    
    3765
    +  )
    
    3766
    +  where
    
    3767
    +    platform = targetPlatform dflags
    
    3768
    +
    
    3769
    +x86AsMFlags :: DynFlags -> [String]
    
    3770
    +x86AsMFlags dflags =
    
    3771
    +     avx512Flags
    
    3772
    +  ++ vectorFlags
    
    3773
    +  ++ bmiFlags
    
    3774
    +  ++ fmaFlags
    
    3775
    +  ++ gfniFlags
    
    3776
    +  where
    
    3777
    +    avx512Extensions =
    
    3778
    +      [ ("-mavx512bw", avx512bw dflags)
    
    3779
    +      , ("-mavx512cd", avx512cd dflags)
    
    3780
    +      , ("-mavx512dq", avx512dq dflags)
    
    3781
    +      , ("-mavx512er", avx512er dflags)
    
    3782
    +      , ("-mavx512pf", avx512pf dflags)
    
    3783
    +      , ("-mavx512vl", avx512vl dflags)
    
    3784
    +      ]
    
    3785
    +
    
    3786
    +    hasAvx512Extension = any snd avx512Extensions
    
    3787
    +    hasAvx512 = avx512f dflags || hasAvx512Extension
    
    3788
    +
    
    3789
    +    avx512Flags =
    
    3790
    +      [ "-mavx512f" | avx512f dflags && not hasAvx512Extension ]
    
    3791
    +      ++ [ flag | (flag, True) <- avx512Extensions ]
    
    3792
    +
    
    3793
    +    vectorFlags
    
    3794
    +      | hasAvx512 = []
    
    3795
    +      | otherwise =
    
    3796
    +          case sseAvxVersion dflags of
    
    3797
    +            Just AVX2  -> ["-mavx2"]
    
    3798
    +            Just AVX1  -> ["-mavx"]
    
    3799
    +            Just SSE42 -> ["-msse4.2"]
    
    3800
    +            Just SSE4  -> ["-msse4"]
    
    3801
    +            Just SSSE3 -> ["-mssse3"]
    
    3802
    +            Just SSE3  -> ["-msse3"]
    
    3803
    +            _          -> []
    
    3804
    +
    
    3805
    +    bmiFlags = case bmiVersion dflags of
    
    3806
    +      Just BMI2 -> ["-mbmi2"]
    
    3807
    +      Just BMI1 -> ["-mbmi"]
    
    3808
    +      Nothing   -> []
    
    3809
    +
    
    3810
    +    fmaFlags
    
    3811
    +      | fma dflags && not hasAvx512 = ["-mfma"]
    
    3812
    +      | otherwise = []
    
    3813
    +
    
    3814
    +    gfniFlags = [ "-mgfni" | gfni dflags ]
    
    3815
    +
    
    3816
    +-- | Apply a requested @-march=native@ by probing the host CPU and enabling the
    
    3817
    +-- matching CPU-feature flags.
    
    3818
    +--
    
    3819
    +-- This runs in 'parseDynamicFlagsFull' rather than in a flag handler because the
    
    3820
    +-- CPUID probe needs 'IO', whereas flag handlers are pure. The detected features
    
    3821
    +-- are folded into the existing feature 'DynFlags' so that 'makeDynFlagsConsistent'
    
    3822
    +-- and the backends treat them exactly like the corresponding @-m...@ flags.
    
    3823
    +applyMarchNative :: MonadIO m => DynFlags -> m DynFlags
    
    3824
    +applyMarchNative dflags
    
    3825
    +  | not (marchNative dflags) = return dflags
    
    3826
    +  | otherwise = do
    
    3827
    +      let arch = platformArch (targetPlatform dflags)
    
    3828
    +      unless (arch == ArchX86 || arch == ArchX86_64) $ liftIO $
    
    3829
    +        throwGhcExceptionIO $ CmdLineError
    
    3830
    +          "-march=native is only supported on x86 and x86_64 targets"
    
    3831
    +      unless (arch == hostPlatformArch) $ liftIO $
    
    3832
    +        throwGhcExceptionIO $ CmdLineError
    
    3833
    +          "-march=native is not supported when cross-compiling"
    
    3834
    +      return (applyX86CpuFeatures Cpu.cachedX86CpuFeatures dflags)
    
    3835
    +
    
    3836
    +-- | Enable the 'DynFlags' CPU-feature fields corresponding to a probed set of
    
    3837
    +-- host x86 features. SSE/AVX and BMI levels are collapsed to their maximum,
    
    3838
    +-- since 'sseAvxVersion' and 'bmiVersion' each record a single level.
    
    3839
    +applyX86CpuFeatures :: [Cpu.X86CpuFeature] -> DynFlags -> DynFlags
    
    3840
    +applyX86CpuFeatures feats dflags = dflags
    
    3841
    +  { sseAvxVersion = foldr (max . Just) (sseAvxVersion dflags) sseLevels
    
    3842
    +  , bmiVersion    = foldr (max . Just) (bmiVersion dflags)    bmiLevels
    
    3843
    +  , avx512f  = avx512f dflags  || has Cpu.AVX512F
    
    3844
    +  , avx512bw = avx512bw dflags || has Cpu.AVX512BW
    
    3845
    +  , avx512cd = avx512cd dflags || has Cpu.AVX512CD
    
    3846
    +  , avx512dq = avx512dq dflags || has Cpu.AVX512DQ
    
    3847
    +  , avx512vl = avx512vl dflags || has Cpu.AVX512VL
    
    3848
    +  , fma      = fma dflags      || has Cpu.FMA
    
    3849
    +  , gfni     = gfni dflags     || has Cpu.GFNI
    
    3850
    +  }
    
    3851
    +  where
    
    3852
    +    has feat = feat `elem` feats
    
    3853
    +    sseLevels = [ lvl | feat <- feats, Just lvl <- [sseLevelOf feat] ]
    
    3854
    +    bmiLevels = [ lvl | feat <- feats, Just lvl <- [bmiLevelOf feat] ]
    
    3855
    +    sseLevelOf feat = case feat of
    
    3856
    +      Cpu.SSE2   -> Just SSE2
    
    3857
    +      Cpu.SSE3   -> Just SSE3
    
    3858
    +      Cpu.SSSE3  -> Just SSSE3
    
    3859
    +      Cpu.SSE4_1 -> Just SSE4
    
    3860
    +      Cpu.SSE4_2 -> Just SSE42
    
    3861
    +      Cpu.AVX    -> Just AVX1
    
    3862
    +      Cpu.AVX2   -> Just AVX2
    
    3863
    +      _          -> Nothing
    
    3864
    +    bmiLevelOf feat = case feat of
    
    3865
    +      Cpu.BMI1 -> Just BMI1
    
    3866
    +      Cpu.BMI2 -> Just BMI2
    
    3867
    +      _        -> Nothing
    
    3868
    +
    
    3680 3869
     -- | Query if the target RTS has the given 'Ways'. It's computed from
    
    3681 3870
     -- the @"RTS ways"@ field in the settings file.
    
    3682 3871
     targetHasRTSWays :: DynFlags -> Ways -> Bool
    

  • compiler/cbits/cpu_features_x86.c
    1
    +#include <HsFFI.h>
    
    2
    +#include <stdint.h>
    
    3
    +
    
    4
    +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
    
    5
    +#include <immintrin.h>
    
    6
    +#include <intrin.h>
    
    7
    +#endif
    
    8
    +
    
    9
    +#if !defined(_MSC_VER) && (defined(__i386__) || defined(__x86_64__))
    
    10
    +#include <cpuid.h>
    
    11
    +#endif
    
    12
    +
    
    13
    +enum {
    
    14
    +  GHC_X86_FEAT_SSE2 = 0,
    
    15
    +  GHC_X86_FEAT_SSE3,
    
    16
    +  GHC_X86_FEAT_SSSE3,
    
    17
    +  GHC_X86_FEAT_SSE4_1,
    
    18
    +  GHC_X86_FEAT_SSE4_2,
    
    19
    +  GHC_X86_FEAT_AVX,
    
    20
    +  GHC_X86_FEAT_AVX2,
    
    21
    +  GHC_X86_FEAT_AVX512F,
    
    22
    +  GHC_X86_FEAT_AVX512BW,
    
    23
    +  GHC_X86_FEAT_AVX512CD,
    
    24
    +  GHC_X86_FEAT_AVX512DQ,
    
    25
    +  GHC_X86_FEAT_AVX512VL,
    
    26
    +  GHC_X86_FEAT_BMI1,
    
    27
    +  GHC_X86_FEAT_BMI2,
    
    28
    +  GHC_X86_FEAT_FMA,
    
    29
    +  GHC_X86_FEAT_GFNI
    
    30
    +};
    
    31
    +
    
    32
    +#define SET_FEAT(mask, bit) ((mask) |= ((HsWord64)1ULL << (bit)))
    
    33
    +
    
    34
    +static int ghc_cpuid_count(uint32_t leaf, uint32_t subleaf,
    
    35
    +                           uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
    
    36
    +{
    
    37
    +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
    
    38
    +  int regs[4];
    
    39
    +  __cpuidex(regs, (int)leaf, (int)subleaf);
    
    40
    +  *a = (uint32_t)regs[0];
    
    41
    +  *b = (uint32_t)regs[1];
    
    42
    +  *c = (uint32_t)regs[2];
    
    43
    +  *d = (uint32_t)regs[3];
    
    44
    +  return 1;
    
    45
    +#elif defined(__i386__) || defined(__x86_64__)
    
    46
    +  return __get_cpuid_count(leaf, subleaf, a, b, c, d);
    
    47
    +#else
    
    48
    +  (void)leaf;
    
    49
    +  (void)subleaf;
    
    50
    +  (void)a;
    
    51
    +  (void)b;
    
    52
    +  (void)c;
    
    53
    +  (void)d;
    
    54
    +  return 0;
    
    55
    +#endif
    
    56
    +}
    
    57
    +
    
    58
    +static uint64_t ghc_xgetbv0(void)
    
    59
    +{
    
    60
    +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
    
    61
    +  return (uint64_t)_xgetbv(0);
    
    62
    +#elif defined(__i386__) || defined(__x86_64__)
    
    63
    +  uint32_t eax, edx;
    
    64
    +  __asm__ volatile(".byte 0x0f, 0x01, 0xd0" /* xgetbv */
    
    65
    +                   : "=a"(eax), "=d"(edx)
    
    66
    +                   : "c"(0));
    
    67
    +  return ((uint64_t)edx << 32) | (uint64_t)eax;
    
    68
    +#else
    
    69
    +  return 0;
    
    70
    +#endif
    
    71
    +}
    
    72
    +
    
    73
    +HsWord64 ghc_detect_x86_cpu_features(void)
    
    74
    +{
    
    75
    +  HsWord64 feats = 0;
    
    76
    +
    
    77
    +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
    
    78
    +  uint32_t a, b, c, d;
    
    79
    +  uint32_t max_basic = 0;
    
    80
    +
    
    81
    +  if (!ghc_cpuid_count(0, 0, &a, &b, &c, &d)) {
    
    82
    +    return 0;
    
    83
    +  }
    
    84
    +  max_basic = a;
    
    85
    +  if (max_basic < 1) {
    
    86
    +    return 0;
    
    87
    +  }
    
    88
    +
    
    89
    +  ghc_cpuid_count(1, 0, &a, &b, &c, &d);
    
    90
    +
    
    91
    +  {
    
    92
    +    int has_sse2    = !!(d & (1u << 26));
    
    93
    +    int has_sse3    = !!(c & (1u << 0));
    
    94
    +    int has_ssse3   = !!(c & (1u << 9));
    
    95
    +    int has_sse4_1  = !!(c & (1u << 19));
    
    96
    +    int has_sse4_2  = !!(c & (1u << 20));
    
    97
    +    int has_fma_hw  = !!(c & (1u << 12));
    
    98
    +    int has_avx_hw  = !!(c & (1u << 28));
    
    99
    +    int has_osxsave = !!(c & (1u << 27));
    
    100
    +
    
    101
    +    int avx_usable = 0;
    
    102
    +    int avx512_usable = 0;
    
    103
    +
    
    104
    +    if (has_osxsave) {
    
    105
    +      uint64_t xcr0 = ghc_xgetbv0();
    
    106
    +      avx_usable = ((xcr0 & 0x6u) == 0x6u);      /* XMM + YMM state */
    
    107
    +      avx512_usable = ((xcr0 & 0xE6u) == 0xE6u); /* XMM+YMM+opmask+ZMM */
    
    108
    +    }
    
    109
    +
    
    110
    +    if (has_sse2) {
    
    111
    +      SET_FEAT(feats, GHC_X86_FEAT_SSE2);
    
    112
    +    }
    
    113
    +    if (has_sse3) {
    
    114
    +      SET_FEAT(feats, GHC_X86_FEAT_SSE3);
    
    115
    +    }
    
    116
    +    if (has_ssse3) {
    
    117
    +      SET_FEAT(feats, GHC_X86_FEAT_SSSE3);
    
    118
    +    }
    
    119
    +    if (has_sse4_1) {
    
    120
    +      SET_FEAT(feats, GHC_X86_FEAT_SSE4_1);
    
    121
    +    }
    
    122
    +    if (has_sse4_2) {
    
    123
    +      SET_FEAT(feats, GHC_X86_FEAT_SSE4_2);
    
    124
    +    }
    
    125
    +    if (has_avx_hw && avx_usable) {
    
    126
    +      SET_FEAT(feats, GHC_X86_FEAT_AVX);
    
    127
    +    }
    
    128
    +    if (has_fma_hw && avx_usable) {
    
    129
    +      SET_FEAT(feats, GHC_X86_FEAT_FMA);
    
    130
    +    }
    
    131
    +
    
    132
    +    if (max_basic >= 7 && ghc_cpuid_count(7, 0, &a, &b, &c, &d)) {
    
    133
    +      int has_bmi1     = !!(b & (1u << 3));
    
    134
    +      int has_avx2_hw  = !!(b & (1u << 5));
    
    135
    +      int has_bmi2     = !!(b & (1u << 8));
    
    136
    +      int has_avx512f  = !!(b & (1u << 16));
    
    137
    +      int has_avx512dq = !!(b & (1u << 17));
    
    138
    +      int has_avx512cd = !!(b & (1u << 28));
    
    139
    +      int has_avx512bw = !!(b & (1u << 30));
    
    140
    +      int has_avx512vl = !!(b & (1u << 31));
    
    141
    +      int has_gfni     = !!(c & (1u << 8));
    
    142
    +
    
    143
    +      if (has_bmi1) {
    
    144
    +        SET_FEAT(feats, GHC_X86_FEAT_BMI1);
    
    145
    +      }
    
    146
    +      if (has_bmi2) {
    
    147
    +        SET_FEAT(feats, GHC_X86_FEAT_BMI2);
    
    148
    +      }
    
    149
    +      if (avx_usable && has_avx2_hw) {
    
    150
    +        SET_FEAT(feats, GHC_X86_FEAT_AVX2);
    
    151
    +      }
    
    152
    +
    
    153
    +      if (avx512_usable && has_avx512f) {
    
    154
    +        SET_FEAT(feats, GHC_X86_FEAT_AVX512F);
    
    155
    +        if (has_avx512bw) {
    
    156
    +          SET_FEAT(feats, GHC_X86_FEAT_AVX512BW);
    
    157
    +        }
    
    158
    +        if (has_avx512cd) {
    
    159
    +          SET_FEAT(feats, GHC_X86_FEAT_AVX512CD);
    
    160
    +        }
    
    161
    +        if (has_avx512dq) {
    
    162
    +          SET_FEAT(feats, GHC_X86_FEAT_AVX512DQ);
    
    163
    +        }
    
    164
    +        if (has_avx512vl) {
    
    165
    +          SET_FEAT(feats, GHC_X86_FEAT_AVX512VL);
    
    166
    +        }
    
    167
    +      }
    
    168
    +
    
    169
    +      if (has_gfni) {
    
    170
    +        SET_FEAT(feats, GHC_X86_FEAT_GFNI);
    
    171
    +      }
    
    172
    +    }
    
    173
    +  }
    
    174
    +#endif
    
    175
    +
    
    176
    +  return feats;
    
    177
    +}

  • compiler/ghc.cabal.in
    ... ... @@ -187,6 +187,7 @@ Library
    187 187
         else
    
    188 188
           c-sources:
    
    189 189
             cbits/cutils.c
    
    190
    +        cbits/cpu_features_x86.c
    
    190 191
             cbits/genSym.c
    
    191 192
             cbits/keepCAFsForGHCi.c
    
    192 193
     
    
    ... ... @@ -514,6 +515,7 @@ Library
    514 515
             GHC.Driver.Config.StgToCmm
    
    515 516
             GHC.Driver.Config.Tidy
    
    516 517
             GHC.Driver.Config.StgToJS
    
    518
    +        GHC.Driver.CpuFeatures
    
    517 519
             GHC.Driver.DynFlags
    
    518 520
             GHC.Driver.IncludeSpecs
    
    519 521
             GHC.Driver.Downsweep
    

  • docs/users_guide/using.rst
    ... ... @@ -488,6 +488,16 @@ The available mode flags are:
    488 488
         List the flags passed to the C compiler for the linking step
    
    489 489
         during GHC build.
    
    490 490
     
    
    491
    +.. ghc-flag:: --print-enabled-cpu-features
    
    492
    +    :shortdesc: display the effective enabled CPU features for code generation
    
    493
    +    :type: mode
    
    494
    +    :category: modes
    
    495
    +
    
    496
    +    Print a JSON object describing the CPU features currently enabled for code
    
    497
    +    generation, together with a set of ``-m...`` flags that reproduce the
    
    498
    +    effective feature set for the current target.
    
    499
    +    Dynamic options such as ``-mavx2`` and ``-mbmi2`` are respected.
    
    500
    +
    
    491 501
     .. ghc-flag:: --print-debug-on
    
    492 502
         :shortdesc: print whether GHC was built with ``-DDEBUG``
    
    493 503
         :type: mode
    
    ... ... @@ -1844,6 +1854,30 @@ Some flags only make sense for particular target platforms.
    1844 1854
         so this flag has no effect when used with the :ref:`native code generator <native-code-gen>`
    
    1845 1855
         or the :ref:`LLVM backend <llvm-code-gen>`.
    
    1846 1856
     
    
    1857
    +.. ghc-flag:: -march=native
    
    1858
    +    :shortdesc: (x86 only) Enable all CPU features supported by the host
    
    1859
    +    :type: dynamic
    
    1860
    +    :category: platform-options
    
    1861
    +
    
    1862
    +    (x86/x86_64 only) Probe the CPU of the machine running GHC and enable all of
    
    1863
    +    the corresponding ``-m...`` CPU-feature options automatically (for example
    
    1864
    +    ``-msse4.2``, ``-mavx2``, ``-mbmi2``, ``-mfma``). The detected features apply
    
    1865
    +    to both the :ref:`native code generator <native-code-gen>` and the
    
    1866
    +    :ref:`LLVM backend <llvm-code-gen>`.
    
    1867
    +
    
    1868
    +    The detected features are enabled *in addition* to any CPU-feature flags you
    
    1869
    +    pass explicitly, regardless of their order on the command line; ``-march=native``
    
    1870
    +    never disables a feature.
    
    1871
    +
    
    1872
    +    .. warning::
    
    1873
    +
    
    1874
    +        Code compiled with ``-march=native`` may use instructions that are not
    
    1875
    +        available on other CPUs, and is therefore not portable to a different
    
    1876
    +        machine.
    
    1877
    +
    
    1878
    +    This flag is rejected when compiling for a non-x86 target or when
    
    1879
    +    cross-compiling, since the host CPU is then unrelated to the target.
    
    1880
    +
    
    1847 1881
     Haddock
    
    1848 1882
     -------
    
    1849 1883
     
    

  • ghc/GHC/Driver/Session/Lint.hs
    1 1
     {-# LANGUAGE CPP #-}
    
    2 2
     {-# LANGUAGE NondecreasingIndentation #-}
    
    3
    -module GHC.Driver.Session.Lint (checkOptions) where
    
    3
    +module GHC.Driver.Session.Lint (checkOptions, unknownFlagsErr) where
    
    4 4
     
    
    5 5
     import GHC.Driver.Backend
    
    6 6
     import GHC.Driver.Phases
    

  • ghc/GHC/Driver/Session/Mode.hs
    ... ... @@ -77,6 +77,7 @@ isShowGhciUsageMode _ = False
    77 77
     
    
    78 78
     data PostLoadMode
    
    79 79
       = ShowInterface FilePath  -- ghc --show-iface
    
    80
    +  | PrintEnabledCpuFeatures -- ghc --print-enabled-cpu-features
    
    80 81
       | DoMkDependHS            -- ghc -M
    
    81 82
       | StopBefore StopPhase    -- ghc -E | -C | -S
    
    82 83
                                 -- StopBefore StopLn is the default
    
    ... ... @@ -90,12 +91,13 @@ data PostLoadMode
    90 91
       | DoFrontend ModuleName   -- ghc --frontend Plugin.Module
    
    91 92
     
    
    92 93
     doMkDependHSMode, doMakeMode, doInteractiveMode, doRunMode,
    
    93
    -  doAbiHashMode, showUnitsMode :: Mode
    
    94
    +  doAbiHashMode, printEnabledCpuFeaturesMode, showUnitsMode :: Mode
    
    94 95
     doMkDependHSMode = mkPostLoadMode DoMkDependHS
    
    95 96
     doMakeMode = mkPostLoadMode DoMake
    
    96 97
     doInteractiveMode = mkPostLoadMode DoInteractive
    
    97 98
     doRunMode = mkPostLoadMode DoRun
    
    98 99
     doAbiHashMode = mkPostLoadMode DoAbiHash
    
    100
    +printEnabledCpuFeaturesMode = mkPostLoadMode PrintEnabledCpuFeatures
    
    99 101
     showUnitsMode = mkPostLoadMode ShowPackages
    
    100 102
     
    
    101 103
     showInterfaceMode :: FilePath -> Mode
    
    ... ... @@ -203,6 +205,8 @@ mode_flags =
    203 205
       , defFlag "-show-options"         (PassFlag (setMode showOptionsMode))
    
    204 206
       , defFlag "-supported-languages"  (PassFlag (setMode showSupportedExtensionsMode))
    
    205 207
       , defFlag "-supported-extensions" (PassFlag (setMode showSupportedExtensionsMode))
    
    208
    +  , defFlag "-print-enabled-cpu-features"
    
    209
    +                                     (PassFlag (setMode printEnabledCpuFeaturesMode))
    
    206 210
       , defFlag "-show-packages"        (PassFlag (setMode showUnitsMode))
    
    207 211
       ] ++
    
    208 212
       [ defFlag k'                      (PassFlag (setMode (printSetting k)))
    

  • ghc/Main.hs
    ... ... @@ -229,55 +229,64 @@ main' postLoadMode units dflags0 args flagWarnings = do
    229 229
            liftIO $ exitWith (ExitFailure 1)) $ do
    
    230 230
              liftIO $ printOrThrowDiagnostics logger4 (initPrintConfig dflags4) diag_opts flagWarnings'
    
    231 231
     
    
    232
    -  liftIO $ showBanner postLoadMode dflags4
    
    233
    -
    
    234
    -  let (dflags5, srcs, objs) = parseTargetFiles dflags4 (map unLoc fileish_args)
    
    235
    -
    
    236
    -  -- we've finished manipulating the DynFlags, update the session
    
    237
    -  _ <- GHC.setSessionDynFlags dflags5
    
    238
    -  dflags6 <- GHC.getSessionDynFlags
    
    239
    -
    
    240
    -  -- Must do this before loading plugins
    
    241
    -  liftIO $ initUniqSupply (initialUnique dflags6) (uniqueIncrement dflags6)
    
    242
    -
    
    243
    -  -- Initialise plugins here because the plugin author might already expect this
    
    244
    -  -- subsequent call to `getLogger` to be affected by a plugin.
    
    245
    -  initializeSessionPlugins
    
    246
    -  hsc_env <- getSession
    
    247
    -  logger <- getLogger
    
    248
    -
    
    249
    -
    
    250
    -        ---------------- Display configuration -----------
    
    251
    -  case verbosity dflags6 of
    
    252
    -    v | v == 4 -> liftIO $ dumpUnitsSimple hsc_env
    
    253
    -      | v >= 5 -> liftIO $ dumpUnits       hsc_env
    
    254
    -      | otherwise -> return ()
    
    255
    -
    
    256
    -        ---------------- Final sanity checking -----------
    
    257
    -  liftIO $ checkOptions postLoadMode dflags6 srcs objs units
    
    258
    -
    
    259
    -  ---------------- Do the business -----------
    
    260
    -  handleSourceError (\e -> do
    
    261
    -       GHC.printException e
    
    262
    -       liftIO $ exitWith (ExitFailure 1)) $ do
    
    263
    -    case postLoadMode of
    
    264
    -       ShowInterface f        -> liftIO $ showIface logger
    
    265
    -                                                    (hsc_dflags hsc_env)
    
    266
    -                                                    (hsc_units  hsc_env)
    
    267
    -                                                    (hsc_NC     hsc_env)
    
    268
    -                                                    f
    
    269
    -       DoMake                 -> doMake units srcs
    
    270
    -       DoMkDependHS           -> doMkDependHS (map fst srcs)
    
    271
    -       StopBefore p           -> liftIO (oneShot hsc_env p srcs)
    
    272
    -       DoInteractive          -> ghciUI units srcs Nothing
    
    273
    -       DoEval exprs           -> ghciUI units srcs $ Just $ reverse exprs
    
    274
    -       DoRun                  -> doRun units srcs args
    
    275
    -       DoAbiHash              -> abiHash (map fst srcs)
    
    276
    -       ShowPackages           -> liftIO $ showUnits hsc_env
    
    277
    -       DoFrontend f           -> doFrontend f srcs
    
    278
    -       DoBackpack             -> doBackpack (map fst srcs)
    
    279
    -
    
    280
    -  liftIO $ dumpFinalStats logger
    
    232
    +  case postLoadMode of
    
    233
    +    PrintEnabledCpuFeatures -> liftIO $ do
    
    234
    +      -- This mode bypasses parseTargetFiles/checkOptions, so reject any
    
    235
    +      -- leftover flag-like arguments (e.g. a mistyped -mavx22) ourselves;
    
    236
    +      -- otherwise the typo would be silently ignored.
    
    237
    +      let unknown_opts = [ f | f@('-':_) <- map unLoc fileish_args ]
    
    238
    +      when (not (null unknown_opts)) (unknownFlagsErr unknown_opts)
    
    239
    +      putStrLn (showEnabledCpuFeatures dflags4)
    
    240
    +    _ -> do
    
    241
    +      liftIO $ showBanner postLoadMode dflags4
    
    242
    +
    
    243
    +      let (dflags5, srcs, objs) = parseTargetFiles dflags4 (map unLoc fileish_args)
    
    244
    +
    
    245
    +      -- we've finished manipulating the DynFlags, update the session
    
    246
    +      _ <- GHC.setSessionDynFlags dflags5
    
    247
    +      dflags6 <- GHC.getSessionDynFlags
    
    248
    +
    
    249
    +      -- Must do this before loading plugins
    
    250
    +      liftIO $ initUniqSupply (initialUnique dflags6) (uniqueIncrement dflags6)
    
    251
    +
    
    252
    +      -- Initialise plugins here because the plugin author might already expect this
    
    253
    +      -- subsequent call to `getLogger` to be affected by a plugin.
    
    254
    +      initializeSessionPlugins
    
    255
    +      hsc_env <- getSession
    
    256
    +      logger <- getLogger
    
    257
    +
    
    258
    +
    
    259
    +            ---------------- Display configuration -----------
    
    260
    +      case verbosity dflags6 of
    
    261
    +        v | v == 4 -> liftIO $ dumpUnitsSimple hsc_env
    
    262
    +          | v >= 5 -> liftIO $ dumpUnits       hsc_env
    
    263
    +          | otherwise -> return ()
    
    264
    +
    
    265
    +            ---------------- Final sanity checking -----------
    
    266
    +      liftIO $ checkOptions postLoadMode dflags6 srcs objs units
    
    267
    +
    
    268
    +      ---------------- Do the business -----------
    
    269
    +      handleSourceError (\e -> do
    
    270
    +           GHC.printException e
    
    271
    +           liftIO $ exitWith (ExitFailure 1)) $ do
    
    272
    +        case postLoadMode of
    
    273
    +           ShowInterface f        -> liftIO $ showIface logger
    
    274
    +                                                        (hsc_dflags hsc_env)
    
    275
    +                                                        (hsc_units  hsc_env)
    
    276
    +                                                        (hsc_NC     hsc_env)
    
    277
    +                                                        f
    
    278
    +           DoMake                 -> doMake units srcs
    
    279
    +           DoMkDependHS           -> doMkDependHS (map fst srcs)
    
    280
    +           StopBefore p           -> liftIO (oneShot hsc_env p srcs)
    
    281
    +           DoInteractive          -> ghciUI units srcs Nothing
    
    282
    +           DoEval exprs           -> ghciUI units srcs $ Just $ reverse exprs
    
    283
    +           DoRun                  -> doRun units srcs args
    
    284
    +           DoAbiHash              -> abiHash (map fst srcs)
    
    285
    +           ShowPackages           -> liftIO $ showUnits hsc_env
    
    286
    +           DoFrontend f           -> doFrontend f srcs
    
    287
    +           DoBackpack             -> doBackpack (map fst srcs)
    
    288
    +
    
    289
    +      liftIO $ dumpFinalStats logger
    
    281 290
     
    
    282 291
     doRun :: [String] -> [(FilePath, Maybe Phase)] -> [Located String] -> Ghc ()
    
    283 292
     doRun units srcs args = do
    
    ... ... @@ -515,4 +524,3 @@ abiHash strs = do
    515 524
       f <- fingerprintBinMem bh
    
    516 525
     
    
    517 526
       putStrLn (showPpr dflags f)
    518
    -

  • testsuite/tests/codeGen/should_gen_asm/all.T
    ... ... @@ -17,6 +17,11 @@ test('msse-option-order', [unless(arch('x86_64') or arch('i386'), skip),
    17 17
                                when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-msse4.2 -msse2'])
    
    18 18
     test('mavx-should-enable-popcnt', [unless(arch('x86_64') or arch('i386'), skip),
    
    19 19
                                        when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-mavx'])
    
    20
    +# -march=native probes the host CPU, so gate on the host actually having SSE4.2
    
    21
    +# (have_cpu_feature reports nothing under cross, skipping the test there too).
    
    22
    +test('march-native-enables-popcnt',
    
    23
    +     [unless((arch('x86_64') or arch('i386')) and have_cpu_feature('sse4_2'), skip),
    
    24
    +      when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-march=native'])
    
    20 25
     test('avx512-int64-mul', [unless(arch('x86_64'), skip),
    
    21 26
                               when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-mavx512dq -mavx512vl'])
    
    22 27
     test('avx512-int64-minmax', [unless(arch('x86_64'), skip),
    

  • testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.asm
    1
    +popcnt(?![0-9])
    \ No newline at end of file

  • testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.hs
    1
    +-- `-march=native` enables the host's CPU features. On a host with SSE4.2
    
    2
    +-- (gated in all.T via have_cpu_feature) this makes popCount compile to a
    
    3
    +-- `popcnt` instruction rather than the SSE2-baseline software fallback.
    
    4
    +import Data.Bits
    
    5
    +
    
    6
    +{-# NOINLINE foo #-}
    
    7
    +foo :: Int -> Int
    
    8
    +foo x = 1 + popCount x
    
    9
    +
    
    10
    +main :: IO ()
    
    11
    +main = print (foo 42)

  • testsuite/tests/driver/all.T
    1
    +def normalise_enabled_cpu_target(msg):
    
    2
    +    return re.sub(r'"target":"[^"]+"', '"target":"TARGET"', msg)
    
    3
    +
    
    4
    +def normalise_unknown_flag(msg):
    
    5
    +    # Keep only the stable 'unrecognised flag' line; the program-name prefix,
    
    6
    +    # the suggestion list, and the usage trailer vary across configurations.
    
    7
    +    m = re.search(r'unrecognised flag: \S+', msg)
    
    8
    +    return m.group(0) + '\n' if m else msg
    
    9
    +
    
    10
    +def normalise_march_native_error(msg):
    
    11
    +    # Keep only the stable '-march=native ...' diagnostic; the program-name
    
    12
    +    # prefix and any usage trailer vary across configurations.
    
    13
    +    m = re.search(r'-march=native is [^\n]+', msg)
    
    14
    +    return m.group(0) + '\n' if m else msg
    
    15
    +
    
    1 16
     test('driver011', [extra_files(['A011.hs'])], makefile_test, ['test011'])
    
    2 17
     
    
    3 18
     test('driver012', [extra_files(['A012.hs'])], makefile_test, ['test012'])
    
    ... ... @@ -221,6 +236,77 @@ test('T9938B', [], makefile_test, [])
    221 236
     test('T9963', exit_code(1), run_command,
    
    222 237
          ['{compiler} --interactive -ignore-dot-ghci --print-libdir'])
    
    223 238
     
    
    239
    +test('print_enabled_cpu_features',
    
    240
    +     [unless(arch('x86_64') or arch('i386'), skip),
    
    241
    +      normalise_fun(normalise_enabled_cpu_target)],
    
    242
    +     run_command,
    
    243
    +     ['{compiler} --print-enabled-cpu-features'])
    
    244
    +
    
    245
    +test('print_enabled_cpu_features_avx2',
    
    246
    +     [unless(arch('x86_64') or arch('i386'), skip),
    
    247
    +      normalise_fun(normalise_enabled_cpu_target)],
    
    248
    +     run_command,
    
    249
    +     ['{compiler} -mavx2 --print-enabled-cpu-features'])
    
    250
    +
    
    251
    +test('print_enabled_cpu_features_bmi2',
    
    252
    +     [unless(arch('x86_64') or arch('i386'), skip),
    
    253
    +      normalise_fun(normalise_enabled_cpu_target)],
    
    254
    +     run_command,
    
    255
    +     ['{compiler} -mbmi2 --print-enabled-cpu-features'])
    
    256
    +
    
    257
    +test('print_enabled_cpu_features_fma',
    
    258
    +     [unless(arch('x86_64') or arch('i386'), skip),
    
    259
    +      normalise_fun(normalise_enabled_cpu_target)],
    
    260
    +     run_command,
    
    261
    +     ['{compiler} -mfma --print-enabled-cpu-features'])
    
    262
    +
    
    263
    +test('print_enabled_cpu_features_avx512',
    
    264
    +     [unless(arch('x86_64'), skip),
    
    265
    +      normalise_fun(normalise_enabled_cpu_target)],
    
    266
    +     run_command,
    
    267
    +     ['{compiler} -mavx512dq -mavx512vl --print-enabled-cpu-features'])
    
    268
    +
    
    269
    +test('print_enabled_cpu_features_unknown_flag',
    
    270
    +     [normalise_fun(normalise_unknown_flag), exit_code(1)],
    
    271
    +     run_command,
    
    272
    +     ['{compiler} -mavx22 --print-enabled-cpu-features'])
    
    273
    +
    
    274
    +# -march=native enables at least the x86_64 baseline (SSE2). The full feature
    
    275
    +# set is host-dependent, so we only assert the always-present baseline.
    
    276
    +test('march_native',
    
    277
    +     [unless(arch('x86_64') or arch('i386'), skip)],
    
    278
    +     run_command,
    
    279
    +     ['{compiler} -march=native --print-enabled-cpu-features | grep -o SSE2'])
    
    280
    +
    
    281
    +# On non-x86 targets -march=native must be rejected.
    
    282
    +test('march_native_unsupported_arch',
    
    283
    +     [when(arch('x86_64') or arch('i386'), skip),
    
    284
    +      normalise_fun(normalise_march_native_error), exit_code(1)],
    
    285
    +     run_command,
    
    286
    +     ['{compiler} -march=native --print-enabled-cpu-features'])
    
    287
    +
    
    288
    +# -march=native is additive: its feature set is a superset of the default set.
    
    289
    +# We extract the "features" arrays with and without the flag and assert that no
    
    290
    +# baseline feature is dropped (comm -23 prints baseline-only entries; expect none).
    
    291
    +# This avoids hard-coding the host-specific feature set.
    
    292
    +test('march_native_superset',
    
    293
    +     [unless(arch('x86_64') or arch('i386'), skip)],
    
    294
    +     run_command,
    
    295
    +     ['{compiler} --print-enabled-cpu-features | '
    
    296
    +      'sed \'s/.*"features":\\[//;s/].*//;s/"//g\' | tr \',\' \'\\n\' | sort > base.txt && '
    
    297
    +      '{compiler} -march=native --print-enabled-cpu-features | '
    
    298
    +      'sed \'s/.*"features":\\[//;s/].*//;s/"//g\' | tr \',\' \'\\n\' | sort > native.txt && '
    
    299
    +      'comm -23 base.txt native.txt'])
    
    300
    +
    
    301
    +# -march=native is additive with explicit -m flags, regardless of order: an
    
    302
    +# explicitly requested feature (here AVX2, forced on independent of the host) is
    
    303
    +# still present whether the flag comes before or after -march=native.
    
    304
    +test('march_native_additive',
    
    305
    +     [unless(arch('x86_64') or arch('i386'), skip)],
    
    306
    +     run_command,
    
    307
    +     ['{compiler} -mavx2 -march=native --print-enabled-cpu-features | grep -o AVX2 && '
    
    308
    +      '{compiler} -march=native -mavx2 --print-enabled-cpu-features | grep -o AVX2'])
    
    309
    +
    
    224 310
     test('T10219', normal, run_command,
    
    225 311
          # `-x hspp` in make mode should work.
    
    226 312
          # Note: need to specify `-x hspp` before the filename.
    

  • testsuite/tests/driver/march_native.stdout
    1
    +SSE2

  • testsuite/tests/driver/march_native_additive.stdout
    1
    +AVX2
    
    2
    +AVX2

  • testsuite/tests/driver/march_native_unsupported_arch.stderr
    1
    +-march=native is only supported on x86 and x86_64 targets

  • testsuite/tests/driver/print_enabled_cpu_features.stdout
    1
    +{"tag":"enabled-cpu-features","version":1,"target":"TARGET","features":["SSE","SSE2"],"as_m_flags":[]}

  • testsuite/tests/driver/print_enabled_cpu_features_avx2.stdout
    1
    +{"tag":"enabled-cpu-features","version":1,"target":"TARGET","features":["SSE","SSE2","SSE3","SSSE3","SSE4.1","SSE4.2","AVX","AVX2"],"as_m_flags":["-mavx2"]}

  • testsuite/tests/driver/print_enabled_cpu_features_avx512.stdout
    1
    +{"tag":"enabled-cpu-features","version":1,"target":"TARGET","features":["SSE","SSE2","SSE3","SSSE3","SSE4.1","SSE4.2","AVX","AVX2","AVX512F","AVX512DQ","AVX512VL","FMA"],"as_m_flags":["-mavx512dq","-mavx512vl"]}

  • testsuite/tests/driver/print_enabled_cpu_features_bmi2.stdout
    1
    +{"tag":"enabled-cpu-features","version":1,"target":"TARGET","features":["SSE","SSE2","BMI1","BMI2"],"as_m_flags":["-mbmi2"]}

  • testsuite/tests/driver/print_enabled_cpu_features_fma.stdout
    1
    +{"tag":"enabled-cpu-features","version":1,"target":"TARGET","features":["SSE","SSE2","SSE3","SSSE3","SSE4.1","SSE4.2","AVX","FMA"],"as_m_flags":["-mfma"]}

  • testsuite/tests/driver/print_enabled_cpu_features_unknown_flag.stderr
    1
    +ghc: unrecognised flag: -mavx22
    
    2
    +did you mean one of:
    
    3
    +  -mavx2
    
    4
    +  -mavx
    
    5
    +
    
    6
    +Usage: For basic information, try the `--help' option.