Simon Jakobi pushed to branch wip/sjakobi/T25450-march-native at Glasgow Haskell Compiler / GHC
Commits:
d177606f by Simon Jakobi at 2026-06-01T22:51:53+02:00
Add --print-enabled-cpu-features flag
GHC now supports a new mode flag --print-enabled-cpu-features, which
prints a JSON object describing the CPU features currently enabled for
code generation, together with a set of -m... flags that reproduce the
effective feature set for the current target. Dynamic options such as
-mavx2 and -mbmi2 are respected.
$ ghc -mavx2 --print-enabled-cpu-features
{"tag":"enabled-cpu-features","version":1,"target":"x86_64-linux-gnu",
"features":["SSE","SSE2","SSE3","SSSE3","SSE4.1","SSE4.2","AVX","AVX2"],
"as_m_flags":["-mavx2"]}
The primary purpose of this flag is for testing the -march=native option
(#25450).
Assisted-by: Claude Opus 4.8
- - - - -
4851f684 by Simon Jakobi at 2026-06-02T00:37:14+02:00
Initial PLAN.md for T25450
- - - - -
69bd97c5 by Simon Jakobi at 2026-06-02T00:37:14+02:00
Initial implementation sketch from Codex
- - - - -
58057a37 by Simon Jakobi at 2026-06-02T00:37:15+02:00
Update PLAN.md
- - - - -
2ebb98e7 by Simon Jakobi at 2026-06-02T00:37:15+02:00
Add AGENTS.md
- - - - -
dcf90d0a by Simon Jakobi at 2026-06-02T00:37:15+02:00
AGENTS.md: Add implementation playbook
- - - - -
fc53b0c8 by Simon Jakobi at 2026-06-02T00:37:15+02:00
Updates
- - - - -
d56edd5c by Simon Jakobi at 2026-06-02T00:37:15+02:00
Implement -march=native for x86/x86_64
Probe the host CPU at flag-parse time and enable the matching CPU-feature
DynFlags, so the effect applies to both the NCG and LLVM backends.
The flag handler only records a marker (DynFlags.marchNative) since handlers
are pure; the CPUID probe and feature application run in parseDynamicFlagsFull,
which is in IO. detectX86CpuFeatures is memoized via cachedX86CpuFeatures so
the probe runs once per process. Detected SSE/AVX and BMI levels are collapsed
to their maximum and folded into the existing feature DynFlags, so the features
are treated exactly like the corresponding -m... flags and reuse the existing
implication logic.
-march=native is additive: it enables host features in addition to any explicit
-m... flags, independent of order, and never disables a feature. It is rejected
for non-x86 targets and when cross-compiling (host arch /= target arch).
The effective feature set is observable via --print-enabled-cpu-features, which
the new driver tests use.
Part of #25450.
Co-Authored-By: Claude Opus 4.7
- - - - -
7aa7eb43 by Simon Jakobi at 2026-06-02T00:48:27+02:00
Add -march=native superset/additive tests; refresh PLAN.md status
Add march_native_superset and march_native_additive driver tests and
update PLAN.md to reflect the completed implementation and resolved
open questions.
Co-Authored-By: Claude Opus 4.7
- - - - -
10d359b2 by Simon Jakobi at 2026-06-02T01:26:06+02:00
Add NCG asm test that -march=native enables popcnt
Compile-and-grep test confirming that -march=native enables SSE4.2 on a
capable host, so popCount emits a popcnt instruction. Gated on the host
actually having SSE4.2 via have_cpu_feature.
Co-Authored-By: Claude Opus 4.7
- - - - -
25 changed files:
- + PLAN.md
- + changelog.d/march-native
- + changelog.d/print-enabled-cpu-features
- + compiler/GHC/Driver/CpuFeatures.hs
- compiler/GHC/Driver/DynFlags.hs
- compiler/GHC/Driver/Session.hs
- + compiler/cbits/cpu_features_x86.c
- compiler/ghc.cabal.in
- docs/users_guide/using.rst
- ghc/GHC/Driver/Session/Lint.hs
- ghc/GHC/Driver/Session/Mode.hs
- ghc/Main.hs
- testsuite/tests/codeGen/should_gen_asm/all.T
- + testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.asm
- + testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.hs
- testsuite/tests/driver/all.T
- + testsuite/tests/driver/march_native.stdout
- + testsuite/tests/driver/march_native_additive.stdout
- + testsuite/tests/driver/march_native_unsupported_arch.stderr
- + testsuite/tests/driver/print_enabled_cpu_features.stdout
- + testsuite/tests/driver/print_enabled_cpu_features_avx2.stdout
- + testsuite/tests/driver/print_enabled_cpu_features_avx512.stdout
- + testsuite/tests/driver/print_enabled_cpu_features_bmi2.stdout
- + testsuite/tests/driver/print_enabled_cpu_features_fma.stdout
- + testsuite/tests/driver/print_enabled_cpu_features_unknown_flag.stderr
Changes:
=====================================
PLAN.md
=====================================
@@ -0,0 +1,142 @@
+# Implement `-march=native` for x86/x86_64 Using `GHC.Driver.CpuFeatures`
+
+## Summary
+Add support for `-march=native` (x86/x86_64, native compiler only). The flag probes the
+host CPU at parse time via an in-process CPUID/XGETBV helper and enables the matching
+CPU-feature `DynFlags`, so the effect applies to both the NCG and LLVM backends.
+
+This branch is based on `wip/sjakobi/T25450-print-cpu`, which already provides
+`ghc --print-enabled-cpu-features` and the `enabledCpuFeatures` / `showEnabledCpuFeatures`
+machinery in `GHC.Driver.Session`. That print mode is the primary tool for verifying and
+testing `-march=native`: detection sets `DynFlags`, the print mode reports them.
+
+## Public Interface Changes
+1. New dynamic flag: `-march=native`.
+2. New users-guide docs entry under platform flags.
+3. Behavior constraints:
+ - Supported only for target `x86`/`x86_64`.
+ - Rejected for cross-compilation (host CPU is irrelevant to a different target).
+ - Additive: applied once after the whole flag fold, it enables host features
+ *in addition* to any explicit `-m...` flags (order-independent, never disables).
+
+## Module / Architecture Changes
+1. `compiler/cbits/cpu_features_x86.c` — in-process CPUID/XGETBV probe (done).
+2. `compiler/GHC/Driver/CpuFeatures.hs` — mask decoder + naming (done). Build wiring in
+ `compiler/ghc.cabal.in` (done).
+3. `compiler/GHC/Driver/DynFlags.hs` — add a marker field `marchNative :: Bool` (default
+ `False`).
+4. `compiler/GHC/Driver/Session.hs` — flag registration + the IO application step (see below).
+
+## Detailed Implementation
+
+### 1. Flag parsing — pure marker only
+- File: `compiler/GHC/Driver/Session.hs`, near the existing `-m...` flags (~line 1710).
+- `make_ord_flag defGhcFlag "march=native" (noArg (\d -> d { marchNative = True }))`.
+- IMPORTANT: flag handlers run in `DynP = EwM (CmdLineP DynFlags)`, which is **pure** — they
+ cannot run the CPUID probe. The handler only records the request via the marker field.
+- Flag-surface note: a literal `"march=native"` entry means `-march=<other>` falls through to
+ a generic "unrecognised flag" error. For v1 this is acceptable; document that only `native`
+ is supported. (Optionally parse the `=value` later to emit a targeted message.)
+
+### 2. Feature application — the IO step
+- File: `compiler/GHC/Driver/Session.hs`, inside `parseDynamicFlagsFull`.
+- `parseDynamicFlagsFull` is already `MonadIO` and already performs `liftIO` work between the
+ pure flag fold (`dflags1`) and `makeDynFlagsConsistent` (`dflags2`/`dflags3`). Insert the
+ application there: after `dflags1`, before `makeDynFlagsConsistent`.
+ - If `marchNative dflags1` is set:
+ - Validate target arch is `ArchX86`/`ArchX86_64`; otherwise hard error.
+ - Validate not cross-compiling; otherwise hard error. (See open question on detection.)
+ - `feats <- liftIO detectX86CpuFeatures` (cached — see §4).
+ - Apply `feats` to `DynFlags` via a dedicated mapping helper (see §3), producing `dflags1'`.
+ - `makeDynFlagsConsistent` then propagates feature implications — do not reimplement them.
+- This path is shared with `parseDynamicFilePragma`, so the same logic also covers
+ `{-# OPTIONS_GHC -march=native #-}`. Decide whether to permit that; if so, caching matters.
+
+### 3. Mapping detected features to `DynFlags`
+The detected list is granular (`SSE2, SSE3, …, AVX2, BMI1, BMI2, …`), but several `DynFlags`
+fields are single ordered levels, so the mapper must **collapse ladders to their maximum**:
+- SSE/AVX ladder → highest present → `sseAvxVersion :: Maybe SseAvxVersion`
+ (`DynFlags.hs:461`). The `is*Enabled` predicates (`DynFlags.hs:1621-1642`) already imply the
+ lower rungs, so only the max level needs to be set.
+- BMI1/BMI2 → highest present → `bmiVersion :: Maybe BmiVersion`.
+- `AVX512F` + per-extension features → the individual bools `avx512f`, `avx512bw`,
+ `avx512cd`, `avx512dq`, `avx512vl`.
+- `FMA` → `fma`; `GFNI` → `gfni`.
+- `SSE2` is the x86_64 baseline (determined by the platform, not a flag) — nothing to set.
+
+### 4. Probe caching
+CPUID results are constant for the lifetime of the process, but `parseDynamicFlagsFull` runs
+per command-line *and* per file pragma. Memoize the probe once (top-level CAF backed by
+`unsafePerformIO` with `{-# NOINLINE #-}`, or detect once and thread the result) so repeated
+`-march=native` uses don't re-probe.
+
+### 5. Diagnostics and errors
+- Unsupported arch: clear error that `-march=native` is x86/x86_64-only.
+- Cross mode: clear error that `-march=native` is unavailable for cross-compilation.
+- The probe is in-process and returns a `Word64`; there is no subprocess, command, or stderr
+ to report. The only anomaly is a probe returning `0` (or lacking SSE2 on x86_64, where it is
+ baseline) — treat that as an internal error with a short diagnostic. (The earlier
+ "failing command + brief stderr" wording referred to an abandoned toolchain-probe approach
+ and no longer applies.)
+
+### 6. Backend behavior
+- NCG and LLVM are both driven by the same CPU-feature `DynFlags`, so no backend wiring
+ changes are needed — populating the fields in §3 is sufficient.
+
+### 7. Verification via `--print-enabled-cpu-features`
+- The base branch already prints effective enabled features as single-line JSON:
+ `{"tag":"enabled-cpu-features","version":1,"target":<triple>,"features":[…],"as_m_flags":[…]}`
+ with conventional names (`SSE4.2`, `AVX2`, `BMI2`) in canonical order.
+- `-march=native` must update the same `DynFlags` fields this mode reads, so its effect is
+ observable through `ghc -march=native --print-enabled-cpu-features`.
+
+### 8. Documentation
+- File: `docs/users_guide/using.rst`, alongside the other `-m...` options.
+- Describe: host CPU detection, x86/x86_64 only, non-cross only, positional, equivalent to
+ enabling the matching `-m...` options automatically.
+- Add a `changelog.d/` entry (the base branch already adds `changelog.d/print-enabled-cpu-features`).
+
+## Tests and Scenarios
+Lead with `--print-enabled-cpu-features` for deterministic, host-independent assertions.
+
+1. **Baseline (deterministic):** on x86_64, `ghc -march=native --print-enabled-cpu-features`
+ always lists `SSE2` in `features`.
+2. **Superset:** `features(-march=native)` ⊇ `features()` (run the print mode with and without
+ the flag and compare). Avoids hard-coding host-specific feature sets.
+3. **Additive:** an explicit `-m...` combined with `-march=native` yields the union (the
+ explicit feature is still present regardless of flag order).
+4. **Arch / cross guards:** compile-fail test for a non-x86 target and/or a cross context,
+ asserting the §5 error messages.
+5. **Optional NCG end-to-end:** one host-gated asm-grep test under
+ `testsuite/tests/codeGen/should_gen_asm` showing `-march=native` enables an expected x86
+ instruction path. Keep it secondary to the print-mode tests; do not rely on it as the main
+ signal. (There is no longer a meaningful probe-failure compile-fail test, since the probe
+ has no external tool to break.)
+
+## Open Questions
+1. **Cross-compilation detection.** *Resolved.* The guard compares the target arch against the
+ build-host arch: `arch == hostPlatformArch` in `applyMarchNative` (`Session.hs:3831`). A
+ mismatch raises the cross-compilation error from §5.
+2. **File-pragma scope.** *Resolved (permitted).* `applyMarchNative` runs in the shared
+ `parseDynamicFlagsFull` (`Session.hs:913`), so `-march=native` also takes effect in
+ `{-# OPTIONS_GHC #-}`. Repeated probing is harmless because the probe is memoized
+ (`cachedX86CpuFeatures`, §4).
+
+## Status
+1. Done: CPUID/XGETBV probe in `compiler/cbits/cpu_features_x86.c`.
+2. Done: decoder + naming in `compiler/GHC/Driver/CpuFeatures.hs`.
+3. Done: cabal wiring in `compiler/ghc.cabal.in`.
+4. Done (base branch): `--print-enabled-cpu-features` + `enabledCpuFeatures` reporting.
+5. Done: `marchNative` marker field in `DynFlags` (`DynFlags.hs:473,765`); flag registration
+ in `Session.hs:1752`.
+6. Done: IO application step `applyMarchNative` in `parseDynamicFlagsFull`
+ (`Session.hs:3816`) — arch guard, cross guard, and the ladder mapping in
+ `applyX86CpuFeatures` (`Session.hs:3839`).
+7. Done: probe caching via `cachedX86CpuFeatures` (top-level CAF, `NOINLINE`,
+ `CpuFeatures.hs:89`).
+8. Done: docs (`docs/users_guide/using.rst:1857`) and changelog (`changelog.d/march-native`).
+9. Tests. In `testsuite/tests/driver`: baseline SSE2 (`march_native`, §1), arch guard
+ (`march_native_unsupported_arch`, §4), superset (`march_native_superset`, §2), and additive
+ (`march_native_additive`, §3). In `testsuite/tests/codeGen/should_gen_asm`: NCG asm-grep
+ (`march-native-enables-popcnt`, §5) — gated on the host having SSE4.2 via
+ `have_cpu_feature`, so it deterministically expects a `popcnt` instruction.
=====================================
changelog.d/march-native
=====================================
@@ -0,0 +1,11 @@
+section: compiler
+synopsis: Add -march=native flag
+issues: #25450
+
+description:
+ GHC now supports ``-march=native`` on x86 and x86_64. It probes the CPU of the
+ machine running GHC and enables all of the corresponding ``-m...`` CPU-feature
+ options automatically (such as ``-msse4.2``, ``-mavx2``, ``-mbmi2`` and
+ ``-mfma``), for both the native code generator and the LLVM backend. The
+ detected features are enabled in addition to any explicitly requested feature
+ flags. The flag is rejected for non-x86 targets and when cross-compiling.
=====================================
changelog.d/print-enabled-cpu-features
=====================================
@@ -0,0 +1,16 @@
+section: compiler
+synopsis: Add --print-enabled-cpu-features flag
+issues: #25450
+mrs: !16117
+
+description:
+ GHC now supports a new mode flag ``--print-enabled-cpu-features``, which
+ prints a JSON object describing the CPU features currently enabled for code
+ generation, together with a set of ``-m...`` flags that reproduce the
+ effective feature set for the current target.
+ Dynamic options such as ``-mavx2`` and ``-mbmi2`` are respected. ::
+
+ $ ghc -mavx2 --print-enabled-cpu-features
+ {"tag":"enabled-cpu-features","version":1,"target":"x86_64-linux-gnu",
+ "features":["SSE","SSE2","SSE3","SSSE3","SSE4.1","SSE4.2","AVX","AVX2"],
+ "as_m_flags":["-mavx2"]}
=====================================
compiler/GHC/Driver/CpuFeatures.hs
=====================================
@@ -0,0 +1,115 @@
+{-# LANGUAGE CPP #-}
+
+module GHC.Driver.CpuFeatures
+ ( X86CpuFeature(..)
+ , detectX86CpuFeatureMask
+ , detectX86CpuFeatures
+ , cachedX86CpuFeatures
+ , decodeX86CpuFeatureMask
+ , x86CpuFeatureConventionalName
+ ) where
+
+import GHC.Prelude
+
+import Data.Bits (testBit)
+import Data.Word (Word64)
+import System.IO.Unsafe (unsafePerformIO)
+
+-- | x86 CPU features understood by GHC's native CPU feature probe.
+--
+-- The constructor order and decode order below are canonicalized for stable
+-- debug output.
+data X86CpuFeature
+ = SSE2
+ | SSE3
+ | SSSE3
+ | SSE4_1
+ | SSE4_2
+ | AVX
+ | AVX2
+ | AVX512F
+ | AVX512BW
+ | AVX512CD
+ | AVX512DQ
+ | AVX512VL
+ | BMI1
+ | BMI2
+ | FMA
+ | GFNI
+ deriving (Eq, Ord, Show)
+
+-- | Human-readable names used in the JSON/debug output.
+x86CpuFeatureConventionalName :: X86CpuFeature -> String
+x86CpuFeatureConventionalName feat = case feat of
+ SSE2 -> "SSE2"
+ SSE3 -> "SSE3"
+ SSSE3 -> "SSSE3"
+ SSE4_1 -> "SSE4.1"
+ SSE4_2 -> "SSE4.2"
+ AVX -> "AVX"
+ AVX2 -> "AVX2"
+ AVX512F -> "AVX512F"
+ AVX512BW -> "AVX512BW"
+ AVX512CD -> "AVX512CD"
+ AVX512DQ -> "AVX512DQ"
+ AVX512VL -> "AVX512VL"
+ BMI1 -> "BMI1"
+ BMI2 -> "BMI2"
+ FMA -> "FMA"
+ GFNI -> "GFNI"
+
+-- | Decode the bitmask returned by 'ghc_detect_x86_cpu_features'.
+--
+-- NOTE: Bit positions must match the enum in @compiler/cbits/cpu_features_x86.c@.
+decodeX86CpuFeatureMask :: Word64 -> [X86CpuFeature]
+decodeX86CpuFeatureMask mask =
+ [ feat
+ | (bit_ix, feat) <- cpuFeatureBitLayout
+ , testBit mask bit_ix
+ ]
+
+-- | Low-level FFI access to the C probe.
+detectX86CpuFeatureMask :: IO Word64
+#if defined(javascript_HOST_ARCH)
+detectX86CpuFeatureMask = pure 0
+#else
+detectX86CpuFeatureMask = c_ghc_detect_x86_cpu_features
+#endif
+
+-- | Probe host x86 CPU features and decode them into an ordered feature list.
+detectX86CpuFeatures :: IO [X86CpuFeature]
+detectX86CpuFeatures = decodeX86CpuFeatureMask <$> detectX86CpuFeatureMask
+
+-- | The host's x86 CPU features, probed once and memoized.
+--
+-- CPUID results are constant for the lifetime of the process, so probing more
+-- than once (e.g. once per @-march=native@ in a command line or file pragma)
+-- is wasteful. This is referentially transparent despite the FFI call.
+cachedX86CpuFeatures :: [X86CpuFeature]
+cachedX86CpuFeatures = unsafePerformIO detectX86CpuFeatures
+{-# NOINLINE cachedX86CpuFeatures #-}
+
+cpuFeatureBitLayout :: [(Int, X86CpuFeature)]
+cpuFeatureBitLayout =
+ [ (0, SSE2)
+ , (1, SSE3)
+ , (2, SSSE3)
+ , (3, SSE4_1)
+ , (4, SSE4_2)
+ , (5, AVX)
+ , (6, AVX2)
+ , (7, AVX512F)
+ , (8, AVX512BW)
+ , (9, AVX512CD)
+ , (10, AVX512DQ)
+ , (11, AVX512VL)
+ , (12, BMI1)
+ , (13, BMI2)
+ , (14, FMA)
+ , (15, GFNI)
+ ]
+
+#if !defined(javascript_HOST_ARCH)
+foreign import ccall unsafe "ghc_detect_x86_cpu_features"
+ c_ghc_detect_x86_cpu_features :: IO Word64
+#endif
=====================================
compiler/GHC/Driver/DynFlags.hs
=====================================
@@ -470,6 +470,8 @@ data DynFlags = DynFlags {
fma :: Bool, -- ^ Enable FMA instructions.
gfni :: Bool, -- ^ Enable GFNI Instructions.
la664 :: Bool, -- ^ Enable LA664 instructions
+ marchNative :: Bool, -- ^ @-march=native@ was requested; the host
+ -- CPU features are applied during flag parsing.
-- Constants used to control the amount of optimization done.
@@ -760,6 +762,7 @@ defaultDynFlags mySettings =
gfni = False,
-- For LoongArch, la464 is used by default.
la664 = False,
+ marchNative = False,
maxInlineAllocSize = 128,
maxInlineMemcpyInsns = 32,
@@ -1615,6 +1618,7 @@ initPromotionTickContext dflags =
-- -----------------------------------------------------------------------------
-- SSE, AVX, FMA
+-- See Note [Keeping enabledCpuFeatures in sync] in GHC.Driver.Session
isSse3Enabled :: DynFlags -> Bool
isSse3Enabled dflags = sseAvxVersion dflags >= Just SSE3 || isAvxEnabled dflags
@@ -1705,11 +1709,14 @@ We handle this as follows:
-- -----------------------------------------------------------------------------
-- LA664
+-- See Note [Keeping enabledCpuFeatures in sync] in GHC.Driver.Session
+
isLa664Enabled :: DynFlags -> Bool
isLa664Enabled dflags = la664 dflags
-- -----------------------------------------------------------------------------
-- BMI2
+-- See Note [Keeping enabledCpuFeatures in sync] in GHC.Driver.Session
isBmiEnabled :: DynFlags -> Bool
isBmiEnabled dflags = case platformArch (targetPlatform dflags) of
=====================================
compiler/GHC/Driver/Session.hs
=====================================
@@ -196,6 +196,8 @@ module GHC.Driver.Session (
-- * Compiler configuration suitable for display to the user
compilerInfo,
+ showEnabledCpuFeatures,
+ enabledCpuFeatures,
targetHasRTSWays,
@@ -243,6 +245,8 @@ import GHC.Platform
import GHC.Platform.Ways
import GHC.Platform.Profile
import GHC.Platform.ArchOS
+import GHC.Platform.Host (hostPlatformArch)
+import qualified GHC.Driver.CpuFeatures as Cpu
import GHC.Unit.Types
import GHC.Unit.Parser
@@ -277,6 +281,7 @@ import GHC.Utils.TmpFs
import GHC.Utils.Fingerprint
import GHC.Utils.Outputable
import GHC.Utils.Error (emptyDiagOpts, logInfo)
+import GHC.Utils.Json
import GHC.Settings
import GHC.CmmToAsm.CFG.Weight
import GHC.Core.Opt.CallerCC
@@ -903,8 +908,12 @@ parseDynamicFlagsFull activeFlags cmdline logger dflags0 args = do
unless (null errs) $ liftIO $ throwGhcExceptionIO $ errorsToGhcException $
map ((rdr . ppr . getLoc &&& unLoc) . errMsg) $ errs
+ -- Apply -march=native: probe the host CPU and enable the matching feature
+ -- flags. This needs IO (CPUID), so it cannot live in the pure flag handlers.
+ dflags1' <- applyMarchNative dflags1
+
-- check for disabled flags in safe haskell
- let (dflags2, sh_warns) = safeFlagCheck cmdline dflags1
+ let (dflags2, sh_warns) = safeFlagCheck cmdline dflags1'
theWays = ways dflags2
unless (allowed_combination theWays) $ liftIO $
@@ -1740,6 +1749,7 @@ dynamic_flags_deps = [
, make_ord_flag defGhcFlag "mavx512vl" (noArg (\d -> d { avx512vl = True }))
, make_ord_flag defGhcFlag "mfma" (noArg (\d -> d { fma = True }))
, make_ord_flag defGhcFlag "mgfni" (noArg (\d -> d { gfni = True }))
+ , make_ord_flag defGhcFlag "march=native" (noArg (\d -> d { marchNative = True }))
, make_ord_flag defGhcFlag "mla664" (noArg (\d -> d { la664 = True }))
@@ -3677,6 +3687,185 @@ compilerInfo dflags
queryCmdMaybe p f = expandDirectories (query (maybe "" (prgPath . p) . f))
queryFlagsMaybe p f = query (maybe "" (unwords . map escapeArg . prgFlags . p) . f)
+showEnabledCpuFeatures :: DynFlags -> String
+showEnabledCpuFeatures dflags = showSDocUnsafe $ renderJSON $ JSObject
+ [ ("tag", JSString "enabled-cpu-features")
+ -- Schema version of this JSON object; bump it whenever the shape or
+ -- meaning of the fields changes, so consumers can detect incompatibility.
+ , ("version", JSInt 1)
+ , ("target", JSString (platformMisc_targetPlatformString (platformMisc dflags)))
+ , ("features", JSArray (map JSString features))
+ -- A set of `-m...` flags that, passed to GHC for this target, reproduce
+ -- the effective feature set above. Note this need not be the flags the
+ -- user actually passed: implied features are folded in, and a feature
+ -- enabled by default may be reproduced by the empty set.
+ , ("as_m_flags", JSArray (map JSString asMFlags))
+ ]
+ where
+ (features, asMFlags) = enabledCpuFeatures dflags
+
+{- Note [Keeping enabledCpuFeatures in sync]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+`enabledCpuFeatures` must be updated whenever a new CPU feature flag is added
+to GHC. The three places to touch are, all in GHC.Driver.DynFlags:
+
+ 1. The flag registration (e.g. `make_ord_flag defGhcFlag "mnewfeat" ...`)
+ 2. The corresponding `is*Enabled` predicate
+ 3. The `enabledCpuFeatures` function below — add the feature to `features`
+ and, if it has a GHC `-m...` flag, to `as_m_flags` via the appropriate
+ architecture branch.
+
+See Note [Implications between X86 CPU feature flags] in GHC.Driver.DynFlags
+for the implication structure that `x86FeaturesAndFlags` and `x86AsMFlags`
+must respect.
+-}
+
+enabledCpuFeatures :: DynFlags -> ([String], [String])
+enabledCpuFeatures dflags = case platformArch (targetPlatform dflags) of
+ ArchX86_64 -> x86FeaturesAndFlags dflags
+ ArchX86 -> x86FeaturesAndFlags dflags
+ ArchLoongArch64 ->
+ ( fmaFeature ++ [ "LA664" | isLa664Enabled dflags ]
+ , fmaFlag ++ [ "-mla664" | isLa664Enabled dflags ]
+ )
+ _ -> (fmaFeature, fmaFlag)
+ where
+ -- `-mfma` is a cross-platform flag. On x86 it is folded into the
+ -- SSE/AVX hierarchy (handled in x86FeaturesAndFlags); on every other
+ -- architecture FMA stands on its own and gates FMA codegen via
+ -- isFmaEnabled in stgToCmmAllowFMAInstr. `fma dflags` is the source of
+ -- truth (it defaults to True on AArch64).
+ fmaFeature = [ "FMA" | fma dflags ]
+ fmaFlag = [ "-mfma" | fma dflags ]
+
+x86FeaturesAndFlags :: DynFlags -> ([String], [String])
+x86FeaturesAndFlags dflags =
+ -- SSE/SSE2 are determined by the target platform rather than a dynamic
+ -- flag, hence those predicates take Platform while the others take DynFlags.
+ ( [ "SSE" | isSseEnabled platform ]
+ ++ [ "SSE2" | isSse2Enabled platform ]
+ ++ [ "SSE3" | isSse3Enabled dflags ]
+ ++ [ "SSSE3" | isSsse3Enabled dflags ]
+ ++ [ "SSE4.1" | isSse4_1Enabled dflags ]
+ ++ [ "SSE4.2" | isSse4_2Enabled dflags ]
+ ++ [ "AVX" | isAvxEnabled dflags ]
+ ++ [ "AVX2" | isAvx2Enabled dflags ]
+ ++ [ "AVX512F" | isAvx512fEnabled dflags ]
+ ++ [ "AVX512BW" | isAvx512bwEnabled dflags ]
+ ++ [ "AVX512CD" | isAvx512cdEnabled dflags ]
+ ++ [ "AVX512DQ" | isAvx512dqEnabled dflags ]
+ ++ [ "AVX512ER" | isAvx512erEnabled dflags ]
+ ++ [ "AVX512PF" | isAvx512pfEnabled dflags ]
+ ++ [ "AVX512VL" | isAvx512vlEnabled dflags ]
+ ++ [ "BMI1" | isBmiEnabled dflags ]
+ ++ [ "BMI2" | isBmi2Enabled dflags ]
+ ++ [ "FMA" | isFmaEnabled dflags ]
+ ++ [ "GFNI" | isGfniEnabled dflags ]
+ , x86AsMFlags dflags
+ )
+ where
+ platform = targetPlatform dflags
+
+x86AsMFlags :: DynFlags -> [String]
+x86AsMFlags dflags =
+ avx512Flags
+ ++ vectorFlags
+ ++ bmiFlags
+ ++ fmaFlags
+ ++ gfniFlags
+ where
+ avx512Extensions =
+ [ ("-mavx512bw", avx512bw dflags)
+ , ("-mavx512cd", avx512cd dflags)
+ , ("-mavx512dq", avx512dq dflags)
+ , ("-mavx512er", avx512er dflags)
+ , ("-mavx512pf", avx512pf dflags)
+ , ("-mavx512vl", avx512vl dflags)
+ ]
+
+ hasAvx512Extension = any snd avx512Extensions
+ hasAvx512 = avx512f dflags || hasAvx512Extension
+
+ avx512Flags =
+ [ "-mavx512f" | avx512f dflags && not hasAvx512Extension ]
+ ++ [ flag | (flag, True) <- avx512Extensions ]
+
+ vectorFlags
+ | hasAvx512 = []
+ | otherwise =
+ case sseAvxVersion dflags of
+ Just AVX2 -> ["-mavx2"]
+ Just AVX1 -> ["-mavx"]
+ Just SSE42 -> ["-msse4.2"]
+ Just SSE4 -> ["-msse4"]
+ Just SSSE3 -> ["-mssse3"]
+ Just SSE3 -> ["-msse3"]
+ _ -> []
+
+ bmiFlags = case bmiVersion dflags of
+ Just BMI2 -> ["-mbmi2"]
+ Just BMI1 -> ["-mbmi"]
+ Nothing -> []
+
+ fmaFlags
+ | fma dflags && not hasAvx512 = ["-mfma"]
+ | otherwise = []
+
+ gfniFlags = [ "-mgfni" | gfni dflags ]
+
+-- | Apply a requested @-march=native@ by probing the host CPU and enabling the
+-- matching CPU-feature flags.
+--
+-- This runs in 'parseDynamicFlagsFull' rather than in a flag handler because the
+-- CPUID probe needs 'IO', whereas flag handlers are pure. The detected features
+-- are folded into the existing feature 'DynFlags' so that 'makeDynFlagsConsistent'
+-- and the backends treat them exactly like the corresponding @-m...@ flags.
+applyMarchNative :: MonadIO m => DynFlags -> m DynFlags
+applyMarchNative dflags
+ | not (marchNative dflags) = return dflags
+ | otherwise = do
+ let arch = platformArch (targetPlatform dflags)
+ unless (arch == ArchX86 || arch == ArchX86_64) $ liftIO $
+ throwGhcExceptionIO $ CmdLineError
+ "-march=native is only supported on x86 and x86_64 targets"
+ unless (arch == hostPlatformArch) $ liftIO $
+ throwGhcExceptionIO $ CmdLineError
+ "-march=native is not supported when cross-compiling"
+ return (applyX86CpuFeatures Cpu.cachedX86CpuFeatures dflags)
+
+-- | Enable the 'DynFlags' CPU-feature fields corresponding to a probed set of
+-- host x86 features. SSE/AVX and BMI levels are collapsed to their maximum,
+-- since 'sseAvxVersion' and 'bmiVersion' each record a single level.
+applyX86CpuFeatures :: [Cpu.X86CpuFeature] -> DynFlags -> DynFlags
+applyX86CpuFeatures feats dflags = dflags
+ { sseAvxVersion = foldr (max . Just) (sseAvxVersion dflags) sseLevels
+ , bmiVersion = foldr (max . Just) (bmiVersion dflags) bmiLevels
+ , avx512f = avx512f dflags || has Cpu.AVX512F
+ , avx512bw = avx512bw dflags || has Cpu.AVX512BW
+ , avx512cd = avx512cd dflags || has Cpu.AVX512CD
+ , avx512dq = avx512dq dflags || has Cpu.AVX512DQ
+ , avx512vl = avx512vl dflags || has Cpu.AVX512VL
+ , fma = fma dflags || has Cpu.FMA
+ , gfni = gfni dflags || has Cpu.GFNI
+ }
+ where
+ has feat = feat `elem` feats
+ sseLevels = [ lvl | feat <- feats, Just lvl <- [sseLevelOf feat] ]
+ bmiLevels = [ lvl | feat <- feats, Just lvl <- [bmiLevelOf feat] ]
+ sseLevelOf feat = case feat of
+ Cpu.SSE2 -> Just SSE2
+ Cpu.SSE3 -> Just SSE3
+ Cpu.SSSE3 -> Just SSSE3
+ Cpu.SSE4_1 -> Just SSE4
+ Cpu.SSE4_2 -> Just SSE42
+ Cpu.AVX -> Just AVX1
+ Cpu.AVX2 -> Just AVX2
+ _ -> Nothing
+ bmiLevelOf feat = case feat of
+ Cpu.BMI1 -> Just BMI1
+ Cpu.BMI2 -> Just BMI2
+ _ -> Nothing
+
-- | Query if the target RTS has the given 'Ways'. It's computed from
-- the @"RTS ways"@ field in the settings file.
targetHasRTSWays :: DynFlags -> Ways -> Bool
=====================================
compiler/cbits/cpu_features_x86.c
=====================================
@@ -0,0 +1,177 @@
+#include
+#include
+
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#include
+#include
+#endif
+
+#if !defined(_MSC_VER) && (defined(__i386__) || defined(__x86_64__))
+#include
+#endif
+
+enum {
+ GHC_X86_FEAT_SSE2 = 0,
+ GHC_X86_FEAT_SSE3,
+ GHC_X86_FEAT_SSSE3,
+ GHC_X86_FEAT_SSE4_1,
+ GHC_X86_FEAT_SSE4_2,
+ GHC_X86_FEAT_AVX,
+ GHC_X86_FEAT_AVX2,
+ GHC_X86_FEAT_AVX512F,
+ GHC_X86_FEAT_AVX512BW,
+ GHC_X86_FEAT_AVX512CD,
+ GHC_X86_FEAT_AVX512DQ,
+ GHC_X86_FEAT_AVX512VL,
+ GHC_X86_FEAT_BMI1,
+ GHC_X86_FEAT_BMI2,
+ GHC_X86_FEAT_FMA,
+ GHC_X86_FEAT_GFNI
+};
+
+#define SET_FEAT(mask, bit) ((mask) |= ((HsWord64)1ULL << (bit)))
+
+static int ghc_cpuid_count(uint32_t leaf, uint32_t subleaf,
+ uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
+{
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+ int regs[4];
+ __cpuidex(regs, (int)leaf, (int)subleaf);
+ *a = (uint32_t)regs[0];
+ *b = (uint32_t)regs[1];
+ *c = (uint32_t)regs[2];
+ *d = (uint32_t)regs[3];
+ return 1;
+#elif defined(__i386__) || defined(__x86_64__)
+ return __get_cpuid_count(leaf, subleaf, a, b, c, d);
+#else
+ (void)leaf;
+ (void)subleaf;
+ (void)a;
+ (void)b;
+ (void)c;
+ (void)d;
+ return 0;
+#endif
+}
+
+static uint64_t ghc_xgetbv0(void)
+{
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+ return (uint64_t)_xgetbv(0);
+#elif defined(__i386__) || defined(__x86_64__)
+ uint32_t eax, edx;
+ __asm__ volatile(".byte 0x0f, 0x01, 0xd0" /* xgetbv */
+ : "=a"(eax), "=d"(edx)
+ : "c"(0));
+ return ((uint64_t)edx << 32) | (uint64_t)eax;
+#else
+ return 0;
+#endif
+}
+
+HsWord64 ghc_detect_x86_cpu_features(void)
+{
+ HsWord64 feats = 0;
+
+#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
+ uint32_t a, b, c, d;
+ uint32_t max_basic = 0;
+
+ if (!ghc_cpuid_count(0, 0, &a, &b, &c, &d)) {
+ return 0;
+ }
+ max_basic = a;
+ if (max_basic < 1) {
+ return 0;
+ }
+
+ ghc_cpuid_count(1, 0, &a, &b, &c, &d);
+
+ {
+ int has_sse2 = !!(d & (1u << 26));
+ int has_sse3 = !!(c & (1u << 0));
+ int has_ssse3 = !!(c & (1u << 9));
+ int has_sse4_1 = !!(c & (1u << 19));
+ int has_sse4_2 = !!(c & (1u << 20));
+ int has_fma_hw = !!(c & (1u << 12));
+ int has_avx_hw = !!(c & (1u << 28));
+ int has_osxsave = !!(c & (1u << 27));
+
+ int avx_usable = 0;
+ int avx512_usable = 0;
+
+ if (has_osxsave) {
+ uint64_t xcr0 = ghc_xgetbv0();
+ avx_usable = ((xcr0 & 0x6u) == 0x6u); /* XMM + YMM state */
+ avx512_usable = ((xcr0 & 0xE6u) == 0xE6u); /* XMM+YMM+opmask+ZMM */
+ }
+
+ if (has_sse2) {
+ SET_FEAT(feats, GHC_X86_FEAT_SSE2);
+ }
+ if (has_sse3) {
+ SET_FEAT(feats, GHC_X86_FEAT_SSE3);
+ }
+ if (has_ssse3) {
+ SET_FEAT(feats, GHC_X86_FEAT_SSSE3);
+ }
+ if (has_sse4_1) {
+ SET_FEAT(feats, GHC_X86_FEAT_SSE4_1);
+ }
+ if (has_sse4_2) {
+ SET_FEAT(feats, GHC_X86_FEAT_SSE4_2);
+ }
+ if (has_avx_hw && avx_usable) {
+ SET_FEAT(feats, GHC_X86_FEAT_AVX);
+ }
+ if (has_fma_hw && avx_usable) {
+ SET_FEAT(feats, GHC_X86_FEAT_FMA);
+ }
+
+ if (max_basic >= 7 && ghc_cpuid_count(7, 0, &a, &b, &c, &d)) {
+ int has_bmi1 = !!(b & (1u << 3));
+ int has_avx2_hw = !!(b & (1u << 5));
+ int has_bmi2 = !!(b & (1u << 8));
+ int has_avx512f = !!(b & (1u << 16));
+ int has_avx512dq = !!(b & (1u << 17));
+ int has_avx512cd = !!(b & (1u << 28));
+ int has_avx512bw = !!(b & (1u << 30));
+ int has_avx512vl = !!(b & (1u << 31));
+ int has_gfni = !!(c & (1u << 8));
+
+ if (has_bmi1) {
+ SET_FEAT(feats, GHC_X86_FEAT_BMI1);
+ }
+ if (has_bmi2) {
+ SET_FEAT(feats, GHC_X86_FEAT_BMI2);
+ }
+ if (avx_usable && has_avx2_hw) {
+ SET_FEAT(feats, GHC_X86_FEAT_AVX2);
+ }
+
+ if (avx512_usable && has_avx512f) {
+ SET_FEAT(feats, GHC_X86_FEAT_AVX512F);
+ if (has_avx512bw) {
+ SET_FEAT(feats, GHC_X86_FEAT_AVX512BW);
+ }
+ if (has_avx512cd) {
+ SET_FEAT(feats, GHC_X86_FEAT_AVX512CD);
+ }
+ if (has_avx512dq) {
+ SET_FEAT(feats, GHC_X86_FEAT_AVX512DQ);
+ }
+ if (has_avx512vl) {
+ SET_FEAT(feats, GHC_X86_FEAT_AVX512VL);
+ }
+ }
+
+ if (has_gfni) {
+ SET_FEAT(feats, GHC_X86_FEAT_GFNI);
+ }
+ }
+ }
+#endif
+
+ return feats;
+}
=====================================
compiler/ghc.cabal.in
=====================================
@@ -187,6 +187,7 @@ Library
else
c-sources:
cbits/cutils.c
+ cbits/cpu_features_x86.c
cbits/genSym.c
cbits/keepCAFsForGHCi.c
@@ -514,6 +515,7 @@ Library
GHC.Driver.Config.StgToCmm
GHC.Driver.Config.Tidy
GHC.Driver.Config.StgToJS
+ GHC.Driver.CpuFeatures
GHC.Driver.DynFlags
GHC.Driver.IncludeSpecs
GHC.Driver.Downsweep
=====================================
docs/users_guide/using.rst
=====================================
@@ -488,6 +488,16 @@ The available mode flags are:
List the flags passed to the C compiler for the linking step
during GHC build.
+.. ghc-flag:: --print-enabled-cpu-features
+ :shortdesc: display the effective enabled CPU features for code generation
+ :type: mode
+ :category: modes
+
+ Print a JSON object describing the CPU features currently enabled for code
+ generation, together with a set of ``-m...`` flags that reproduce the
+ effective feature set for the current target.
+ Dynamic options such as ``-mavx2`` and ``-mbmi2`` are respected.
+
.. ghc-flag:: --print-debug-on
:shortdesc: print whether GHC was built with ``-DDEBUG``
:type: mode
@@ -1844,6 +1854,30 @@ Some flags only make sense for particular target platforms.
so this flag has no effect when used with the :ref:`native code generator <native-code-gen>`
or the :ref:`LLVM backend <llvm-code-gen>`.
+.. ghc-flag:: -march=native
+ :shortdesc: (x86 only) Enable all CPU features supported by the host
+ :type: dynamic
+ :category: platform-options
+
+ (x86/x86_64 only) Probe the CPU of the machine running GHC and enable all of
+ the corresponding ``-m...`` CPU-feature options automatically (for example
+ ``-msse4.2``, ``-mavx2``, ``-mbmi2``, ``-mfma``). The detected features apply
+ to both the :ref:`native code generator <native-code-gen>` and the
+ :ref:`LLVM backend <llvm-code-gen>`.
+
+ The detected features are enabled *in addition* to any CPU-feature flags you
+ pass explicitly, regardless of their order on the command line; ``-march=native``
+ never disables a feature.
+
+ .. warning::
+
+ Code compiled with ``-march=native`` may use instructions that are not
+ available on other CPUs, and is therefore not portable to a different
+ machine.
+
+ This flag is rejected when compiling for a non-x86 target or when
+ cross-compiling, since the host CPU is then unrelated to the target.
+
Haddock
-------
=====================================
ghc/GHC/Driver/Session/Lint.hs
=====================================
@@ -1,6 +1,6 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE NondecreasingIndentation #-}
-module GHC.Driver.Session.Lint (checkOptions) where
+module GHC.Driver.Session.Lint (checkOptions, unknownFlagsErr) where
import GHC.Driver.Backend
import GHC.Driver.Phases
=====================================
ghc/GHC/Driver/Session/Mode.hs
=====================================
@@ -77,6 +77,7 @@ isShowGhciUsageMode _ = False
data PostLoadMode
= ShowInterface FilePath -- ghc --show-iface
+ | PrintEnabledCpuFeatures -- ghc --print-enabled-cpu-features
| DoMkDependHS -- ghc -M
| StopBefore StopPhase -- ghc -E | -C | -S
-- StopBefore StopLn is the default
@@ -90,12 +91,13 @@ data PostLoadMode
| DoFrontend ModuleName -- ghc --frontend Plugin.Module
doMkDependHSMode, doMakeMode, doInteractiveMode, doRunMode,
- doAbiHashMode, showUnitsMode :: Mode
+ doAbiHashMode, printEnabledCpuFeaturesMode, showUnitsMode :: Mode
doMkDependHSMode = mkPostLoadMode DoMkDependHS
doMakeMode = mkPostLoadMode DoMake
doInteractiveMode = mkPostLoadMode DoInteractive
doRunMode = mkPostLoadMode DoRun
doAbiHashMode = mkPostLoadMode DoAbiHash
+printEnabledCpuFeaturesMode = mkPostLoadMode PrintEnabledCpuFeatures
showUnitsMode = mkPostLoadMode ShowPackages
showInterfaceMode :: FilePath -> Mode
@@ -203,6 +205,8 @@ mode_flags =
, defFlag "-show-options" (PassFlag (setMode showOptionsMode))
, defFlag "-supported-languages" (PassFlag (setMode showSupportedExtensionsMode))
, defFlag "-supported-extensions" (PassFlag (setMode showSupportedExtensionsMode))
+ , defFlag "-print-enabled-cpu-features"
+ (PassFlag (setMode printEnabledCpuFeaturesMode))
, defFlag "-show-packages" (PassFlag (setMode showUnitsMode))
] ++
[ defFlag k' (PassFlag (setMode (printSetting k)))
=====================================
ghc/Main.hs
=====================================
@@ -229,55 +229,64 @@ main' postLoadMode units dflags0 args flagWarnings = do
liftIO $ exitWith (ExitFailure 1)) $ do
liftIO $ printOrThrowDiagnostics logger4 (initPrintConfig dflags4) diag_opts flagWarnings'
- liftIO $ showBanner postLoadMode dflags4
-
- let (dflags5, srcs, objs) = parseTargetFiles dflags4 (map unLoc fileish_args)
-
- -- we've finished manipulating the DynFlags, update the session
- _ <- GHC.setSessionDynFlags dflags5
- dflags6 <- GHC.getSessionDynFlags
-
- -- Must do this before loading plugins
- liftIO $ initUniqSupply (initialUnique dflags6) (uniqueIncrement dflags6)
-
- -- Initialise plugins here because the plugin author might already expect this
- -- subsequent call to `getLogger` to be affected by a plugin.
- initializeSessionPlugins
- hsc_env <- getSession
- logger <- getLogger
-
-
- ---------------- Display configuration -----------
- case verbosity dflags6 of
- v | v == 4 -> liftIO $ dumpUnitsSimple hsc_env
- | v >= 5 -> liftIO $ dumpUnits hsc_env
- | otherwise -> return ()
-
- ---------------- Final sanity checking -----------
- liftIO $ checkOptions postLoadMode dflags6 srcs objs units
-
- ---------------- Do the business -----------
- handleSourceError (\e -> do
- GHC.printException e
- liftIO $ exitWith (ExitFailure 1)) $ do
- case postLoadMode of
- ShowInterface f -> liftIO $ showIface logger
- (hsc_dflags hsc_env)
- (hsc_units hsc_env)
- (hsc_NC hsc_env)
- f
- DoMake -> doMake units srcs
- DoMkDependHS -> doMkDependHS (map fst srcs)
- StopBefore p -> liftIO (oneShot hsc_env p srcs)
- DoInteractive -> ghciUI units srcs Nothing
- DoEval exprs -> ghciUI units srcs $ Just $ reverse exprs
- DoRun -> doRun units srcs args
- DoAbiHash -> abiHash (map fst srcs)
- ShowPackages -> liftIO $ showUnits hsc_env
- DoFrontend f -> doFrontend f srcs
- DoBackpack -> doBackpack (map fst srcs)
-
- liftIO $ dumpFinalStats logger
+ case postLoadMode of
+ PrintEnabledCpuFeatures -> liftIO $ do
+ -- This mode bypasses parseTargetFiles/checkOptions, so reject any
+ -- leftover flag-like arguments (e.g. a mistyped -mavx22) ourselves;
+ -- otherwise the typo would be silently ignored.
+ let unknown_opts = [ f | f@('-':_) <- map unLoc fileish_args ]
+ when (not (null unknown_opts)) (unknownFlagsErr unknown_opts)
+ putStrLn (showEnabledCpuFeatures dflags4)
+ _ -> do
+ liftIO $ showBanner postLoadMode dflags4
+
+ let (dflags5, srcs, objs) = parseTargetFiles dflags4 (map unLoc fileish_args)
+
+ -- we've finished manipulating the DynFlags, update the session
+ _ <- GHC.setSessionDynFlags dflags5
+ dflags6 <- GHC.getSessionDynFlags
+
+ -- Must do this before loading plugins
+ liftIO $ initUniqSupply (initialUnique dflags6) (uniqueIncrement dflags6)
+
+ -- Initialise plugins here because the plugin author might already expect this
+ -- subsequent call to `getLogger` to be affected by a plugin.
+ initializeSessionPlugins
+ hsc_env <- getSession
+ logger <- getLogger
+
+
+ ---------------- Display configuration -----------
+ case verbosity dflags6 of
+ v | v == 4 -> liftIO $ dumpUnitsSimple hsc_env
+ | v >= 5 -> liftIO $ dumpUnits hsc_env
+ | otherwise -> return ()
+
+ ---------------- Final sanity checking -----------
+ liftIO $ checkOptions postLoadMode dflags6 srcs objs units
+
+ ---------------- Do the business -----------
+ handleSourceError (\e -> do
+ GHC.printException e
+ liftIO $ exitWith (ExitFailure 1)) $ do
+ case postLoadMode of
+ ShowInterface f -> liftIO $ showIface logger
+ (hsc_dflags hsc_env)
+ (hsc_units hsc_env)
+ (hsc_NC hsc_env)
+ f
+ DoMake -> doMake units srcs
+ DoMkDependHS -> doMkDependHS (map fst srcs)
+ StopBefore p -> liftIO (oneShot hsc_env p srcs)
+ DoInteractive -> ghciUI units srcs Nothing
+ DoEval exprs -> ghciUI units srcs $ Just $ reverse exprs
+ DoRun -> doRun units srcs args
+ DoAbiHash -> abiHash (map fst srcs)
+ ShowPackages -> liftIO $ showUnits hsc_env
+ DoFrontend f -> doFrontend f srcs
+ DoBackpack -> doBackpack (map fst srcs)
+
+ liftIO $ dumpFinalStats logger
doRun :: [String] -> [(FilePath, Maybe Phase)] -> [Located String] -> Ghc ()
doRun units srcs args = do
@@ -515,4 +524,3 @@ abiHash strs = do
f <- fingerprintBinMem bh
putStrLn (showPpr dflags f)
-
=====================================
testsuite/tests/codeGen/should_gen_asm/all.T
=====================================
@@ -17,6 +17,11 @@ test('msse-option-order', [unless(arch('x86_64') or arch('i386'), skip),
when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-msse4.2 -msse2'])
test('mavx-should-enable-popcnt', [unless(arch('x86_64') or arch('i386'), skip),
when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-mavx'])
+# -march=native probes the host CPU, so gate on the host actually having SSE4.2
+# (have_cpu_feature reports nothing under cross, skipping the test there too).
+test('march-native-enables-popcnt',
+ [unless((arch('x86_64') or arch('i386')) and have_cpu_feature('sse4_2'), skip),
+ when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-march=native'])
test('avx512-int64-mul', [unless(arch('x86_64'), skip),
when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-mavx512dq -mavx512vl'])
test('avx512-int64-minmax', [unless(arch('x86_64'), skip),
=====================================
testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.asm
=====================================
@@ -0,0 +1 @@
+popcnt(?![0-9])
\ No newline at end of file
=====================================
testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.hs
=====================================
@@ -0,0 +1,11 @@
+-- `-march=native` enables the host's CPU features. On a host with SSE4.2
+-- (gated in all.T via have_cpu_feature) this makes popCount compile to a
+-- `popcnt` instruction rather than the SSE2-baseline software fallback.
+import Data.Bits
+
+{-# NOINLINE foo #-}
+foo :: Int -> Int
+foo x = 1 + popCount x
+
+main :: IO ()
+main = print (foo 42)
=====================================
testsuite/tests/driver/all.T
=====================================
@@ -1,3 +1,18 @@
+def normalise_enabled_cpu_target(msg):
+ return re.sub(r'"target":"[^"]+"', '"target":"TARGET"', msg)
+
+def normalise_unknown_flag(msg):
+ # Keep only the stable 'unrecognised flag' line; the program-name prefix,
+ # the suggestion list, and the usage trailer vary across configurations.
+ m = re.search(r'unrecognised flag: \S+', msg)
+ return m.group(0) + '\n' if m else msg
+
+def normalise_march_native_error(msg):
+ # Keep only the stable '-march=native ...' diagnostic; the program-name
+ # prefix and any usage trailer vary across configurations.
+ m = re.search(r'-march=native is [^\n]+', msg)
+ return m.group(0) + '\n' if m else msg
+
test('driver011', [extra_files(['A011.hs'])], makefile_test, ['test011'])
test('driver012', [extra_files(['A012.hs'])], makefile_test, ['test012'])
@@ -221,6 +236,77 @@ test('T9938B', [], makefile_test, [])
test('T9963', exit_code(1), run_command,
['{compiler} --interactive -ignore-dot-ghci --print-libdir'])
+test('print_enabled_cpu_features',
+ [unless(arch('x86_64') or arch('i386'), skip),
+ normalise_fun(normalise_enabled_cpu_target)],
+ run_command,
+ ['{compiler} --print-enabled-cpu-features'])
+
+test('print_enabled_cpu_features_avx2',
+ [unless(arch('x86_64') or arch('i386'), skip),
+ normalise_fun(normalise_enabled_cpu_target)],
+ run_command,
+ ['{compiler} -mavx2 --print-enabled-cpu-features'])
+
+test('print_enabled_cpu_features_bmi2',
+ [unless(arch('x86_64') or arch('i386'), skip),
+ normalise_fun(normalise_enabled_cpu_target)],
+ run_command,
+ ['{compiler} -mbmi2 --print-enabled-cpu-features'])
+
+test('print_enabled_cpu_features_fma',
+ [unless(arch('x86_64') or arch('i386'), skip),
+ normalise_fun(normalise_enabled_cpu_target)],
+ run_command,
+ ['{compiler} -mfma --print-enabled-cpu-features'])
+
+test('print_enabled_cpu_features_avx512',
+ [unless(arch('x86_64'), skip),
+ normalise_fun(normalise_enabled_cpu_target)],
+ run_command,
+ ['{compiler} -mavx512dq -mavx512vl --print-enabled-cpu-features'])
+
+test('print_enabled_cpu_features_unknown_flag',
+ [normalise_fun(normalise_unknown_flag), exit_code(1)],
+ run_command,
+ ['{compiler} -mavx22 --print-enabled-cpu-features'])
+
+# -march=native enables at least the x86_64 baseline (SSE2). The full feature
+# set is host-dependent, so we only assert the always-present baseline.
+test('march_native',
+ [unless(arch('x86_64') or arch('i386'), skip)],
+ run_command,
+ ['{compiler} -march=native --print-enabled-cpu-features | grep -o SSE2'])
+
+# On non-x86 targets -march=native must be rejected.
+test('march_native_unsupported_arch',
+ [when(arch('x86_64') or arch('i386'), skip),
+ normalise_fun(normalise_march_native_error), exit_code(1)],
+ run_command,
+ ['{compiler} -march=native --print-enabled-cpu-features'])
+
+# -march=native is additive: its feature set is a superset of the default set.
+# We extract the "features" arrays with and without the flag and assert that no
+# baseline feature is dropped (comm -23 prints baseline-only entries; expect none).
+# This avoids hard-coding the host-specific feature set.
+test('march_native_superset',
+ [unless(arch('x86_64') or arch('i386'), skip)],
+ run_command,
+ ['{compiler} --print-enabled-cpu-features | '
+ 'sed \'s/.*"features":\\[//;s/].*//;s/"//g\' | tr \',\' \'\\n\' | sort > base.txt && '
+ '{compiler} -march=native --print-enabled-cpu-features | '
+ 'sed \'s/.*"features":\\[//;s/].*//;s/"//g\' | tr \',\' \'\\n\' | sort > native.txt && '
+ 'comm -23 base.txt native.txt'])
+
+# -march=native is additive with explicit -m flags, regardless of order: an
+# explicitly requested feature (here AVX2, forced on independent of the host) is
+# still present whether the flag comes before or after -march=native.
+test('march_native_additive',
+ [unless(arch('x86_64') or arch('i386'), skip)],
+ run_command,
+ ['{compiler} -mavx2 -march=native --print-enabled-cpu-features | grep -o AVX2 && '
+ '{compiler} -march=native -mavx2 --print-enabled-cpu-features | grep -o AVX2'])
+
test('T10219', normal, run_command,
# `-x hspp` in make mode should work.
# Note: need to specify `-x hspp` before the filename.
=====================================
testsuite/tests/driver/march_native.stdout
=====================================
@@ -0,0 +1 @@
+SSE2
=====================================
testsuite/tests/driver/march_native_additive.stdout
=====================================
@@ -0,0 +1,2 @@
+AVX2
+AVX2
=====================================
testsuite/tests/driver/march_native_unsupported_arch.stderr
=====================================
@@ -0,0 +1 @@
+-march=native is only supported on x86 and x86_64 targets
=====================================
testsuite/tests/driver/print_enabled_cpu_features.stdout
=====================================
@@ -0,0 +1 @@
+{"tag":"enabled-cpu-features","version":1,"target":"TARGET","features":["SSE","SSE2"],"as_m_flags":[]}
=====================================
testsuite/tests/driver/print_enabled_cpu_features_avx2.stdout
=====================================
@@ -0,0 +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
=====================================
@@ -0,0 +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
=====================================
@@ -0,0 +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
=====================================
@@ -0,0 +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
=====================================
@@ -0,0 +1,6 @@
+ghc: unrecognised flag: -mavx22
+did you mean one of:
+ -mavx2
+ -mavx
+
+Usage: For basic information, try the `--help' option.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/19ca1ae48d71b92c7d56672cf406ab3...
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/19ca1ae48d71b92c7d56672cf406ab3...
You're receiving this email because of your account on gitlab.haskell.org.