[Git][ghc/ghc][wip/sjakobi/T25450-march-native] Vendor x86 CPU feature detection from LLVM's compiler-rt
by Simon Jakobi (@sjakobi2) 10 Jun '26
by Simon Jakobi (@sjakobi2) 10 Jun '26
10 Jun '26
Simon Jakobi pushed to branch wip/sjakobi/T25450-march-native at Glasgow Haskell Compiler / GHC
Commits:
7bab9752 by Simon Jakobi at 2026-06-10T02:05:57+02:00
Vendor x86 CPU feature detection from LLVM's compiler-rt
Following review feedback, replace the hand-rolled CPUID/XGETBV probe in
cpu_features_x86.c with code vendored from compiler-rt's plain-C CPU
model implementation:
compiler-rt/lib/builtins/cpu_model/x86.c at tag llvmorg-20.1.0
This is the runtime behind clang's __builtin_cpu_supports and
__builtin_cpu_is; upstream notes it is a copy of
llvm/lib/TargetParser/Host.cpp (the code behind clang -march=native) and
that the two files are kept in sync. The file header references the
upstream source so the code can be diffed against it when debugging or
updating.
The vendored code is byte-for-byte upstream: enum ProcessorFeatures, the
CPUID/XGETBV helpers (getX86CpuIDAndInfo, getX86CpuIDAndInfoEx,
getX86XCR0) and the entire getAvailableFeatures function are verbatim.
The single GHC deviation is a separate marked block that clears
FEATURE_FMA again when OS AVX-state support is missing -- FMA
instructions are VEX-encoded and unusable without it -- matching
Host.cpp and GCC's cpuinfo.h, where upstream x86.c uses the raw CPUID
bit alone.
To make this possible, cpuFeatureBitLayout in GHC.Driver.CpuFeatures
adopts LLVM/GCC's stable __builtin_cpu_supports feature numbering
(BMI1 = upstream's FEATURE_BMI). ghc_detect_x86_cpu_features stands in
for __cpu_indicator_init as the driver, returning the first 64 feature
bits of that numbering as a mask (every feature GHC decodes is below
bit 64; GFNI is the highest at 32). Features decoded by upstream code
beyond GHC's set are simply ignored on the Haskell side, so updating
the vendored code is a pure copy-paste and adding a new feature to GHC
only requires a Haskell-side change.
Behavioural changes from adopting LLVM's logic:
* On x86_64 macOS we no longer query sysctl(hw.optional.avx512f) to work
around the kernel's lazy AVX-512 XSAVE enablement. Like LLVM, we trust
that Darwin will save the AVX-512 context on first use and rely on the
CPUID leaf-7 bits alone (HasAVX512Save = true on __APPLE__). This also
drops the sys/sysctl.h dependency.
* The AVX-512 sub-features (BW/CD/DQ/VL) are each gated individually on
OS support for the AVX-512 context save, instead of being nested under
an AVX512F check.
Co-Authored-By: Claude Fable 5 <noreply(a)anthropic.com>
- - - - -
2 changed files:
- compiler/GHC/Driver/CpuFeatures.hs
- compiler/cbits/cpu_features_x86.c
Changes:
=====================================
compiler/GHC/Driver/CpuFeatures.hs
=====================================
@@ -32,7 +32,10 @@ data X86CpuFeature
-- | Decode the bitmask returned by 'ghc_detect_x86_cpu_features'.
--
--- NOTE: Bit positions must match the enum in @compiler/cbits/cpu_features_x86.c@.
+-- NOTE: Bit positions are LLVM\/GCC's @__builtin_cpu_supports@ feature
+-- numbering, i.e. @enum ProcessorFeatures@ vendored in
+-- @compiler/cbits/cpu_features_x86.c@. The C side returns the first 64
+-- feature bits of that numbering.
decodeX86CpuFeatureMask :: Word64 -> [X86CpuFeature]
decodeX86CpuFeatureMask mask =
[ feat
@@ -61,24 +64,27 @@ cachedX86CpuFeatures :: [X86CpuFeature]
cachedX86CpuFeatures = unsafePerformIO detectX86CpuFeatures
{-# NOINLINE cachedX86CpuFeatures #-}
+-- | See the NOTE on 'decodeX86CpuFeatureMask' for where these bit positions
+-- come from. The constant names on the C side are upstream's: @FEATURE_SSE2@,
+-- @FEATURE_BMI@ (= 'BMI1'), etc.
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)
+ [ (4, SSE2) -- FEATURE_SSE2
+ , (5, SSE3) -- FEATURE_SSE3
+ , (6, SSSE3) -- FEATURE_SSSE3
+ , (7, SSE4_1) -- FEATURE_SSE4_1
+ , (8, SSE4_2) -- FEATURE_SSE4_2
+ , (9, AVX) -- FEATURE_AVX
+ , (10, AVX2) -- FEATURE_AVX2
+ , (14, FMA) -- FEATURE_FMA
+ , (15, AVX512F) -- FEATURE_AVX512F
+ , (16, BMI1) -- FEATURE_BMI
+ , (17, BMI2) -- FEATURE_BMI2
+ , (20, AVX512VL) -- FEATURE_AVX512VL
+ , (21, AVX512BW) -- FEATURE_AVX512BW
+ , (22, AVX512DQ) -- FEATURE_AVX512DQ
+ , (23, AVX512CD) -- FEATURE_AVX512CD
+ , (32, GFNI) -- FEATURE_GFNI
]
#if !defined(javascript_HOST_ARCH)
=====================================
compiler/cbits/cpu_features_x86.c
=====================================
@@ -1,208 +1,586 @@
+/* Host x86 CPU feature detection, used to implement -march=native.
+ *
+ * The detection code is vendored from LLVM's compiler-rt, where it implements
+ * the runtime support for __builtin_cpu_supports/__builtin_cpu_is
+ * (__cpu_model, __cpu_indicator_init):
+ *
+ * compiler-rt/lib/builtins/cpu_model/x86.c
+ * at tag llvmorg-20.1.0 (LLVM 20.1.0)
+ * https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/bu…
+ *
+ * LLVM is licensed under Apache-2.0 WITH LLVM-exception. Upstream notes that
+ * this file is itself a copy of llvm/lib/TargetParser/Host.cpp -- the code
+ * behind clang's -march=native -- and that the two are kept in sync.
+ *
+ * Vendored verbatim: enum ProcessorFeatures, getX86CpuIDAndInfo,
+ * getX86CpuIDAndInfoEx, getX86XCR0 and getAvailableFeatures, the latter with
+ * a single marked GHC deviation that additionally gates FEATURE_FMA on OS
+ * support for saving the AVX register state.
+ *
+ * Adaptations around the vendored code:
+ *
+ * - ghc_detect_x86_cpu_features stands in for __cpu_indicator_init as the
+ * driver: it performs the same CPUID call sequence, but returns the first
+ * 64 feature bits as a mask -- which covers every feature GHC decodes,
+ * see GHC.Driver.CpuFeatures -- instead of filling in the __cpu_model
+ * globals.
+ * - On non-x86 hosts the driver compiles to a stub returning 0 instead of
+ * upstream's "#error This file is intended only for x86-based targets".
+ */
+
#include <HsFFI.h>
-#include <stdint.h>
+#include <stdbool.h>
-#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
-#include <immintrin.h>
-#include <intrin.h>
+#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || \
+ defined(_M_X64)
+#define GHC_HOST_IS_X86 1
#endif
-#if !defined(_MSC_VER) && (defined(__i386__) || defined(__x86_64__))
+#if defined(GHC_HOST_IS_X86)
+
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(_MSC_VER)
#include <cpuid.h>
#endif
-#if defined(__APPLE__) && (defined(__i386__) || defined(__x86_64__))
-#include <sys/sysctl.h>
+#ifdef _MSC_VER
+#include <intrin.h>
#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
+/* NOTE: The feature bit positions below are LLVM/GCC's stable
+ * __builtin_cpu_supports feature numbering. cpuFeatureBitLayout in
+ * GHC.Driver.CpuFeatures must use the same values. */
+enum ProcessorFeatures {
+ FEATURE_CMOV = 0,
+ FEATURE_MMX,
+ FEATURE_POPCNT,
+ FEATURE_SSE,
+ FEATURE_SSE2,
+ FEATURE_SSE3,
+ FEATURE_SSSE3,
+ FEATURE_SSE4_1,
+ FEATURE_SSE4_2,
+ FEATURE_AVX,
+ FEATURE_AVX2,
+ FEATURE_SSE4_A,
+ FEATURE_FMA4,
+ FEATURE_XOP,
+ FEATURE_FMA,
+ FEATURE_AVX512F,
+ FEATURE_BMI,
+ FEATURE_BMI2,
+ FEATURE_AES,
+ FEATURE_PCLMUL,
+ FEATURE_AVX512VL,
+ FEATURE_AVX512BW,
+ FEATURE_AVX512DQ,
+ FEATURE_AVX512CD,
+ FEATURE_AVX512ER,
+ FEATURE_AVX512PF,
+ FEATURE_AVX512VBMI,
+ FEATURE_AVX512IFMA,
+ FEATURE_AVX5124VNNIW,
+ FEATURE_AVX5124FMAPS,
+ FEATURE_AVX512VPOPCNTDQ,
+ FEATURE_AVX512VBMI2,
+ FEATURE_GFNI,
+ FEATURE_VPCLMULQDQ,
+ FEATURE_AVX512VNNI,
+ FEATURE_AVX512BITALG,
+ FEATURE_AVX512BF16,
+ FEATURE_AVX512VP2INTERSECT,
+ // FIXME: Below Features has some missings comparing to gcc, it's because gcc
+ // has some not one-to-one mapped in llvm.
+ // FEATURE_3DNOW,
+ // FEATURE_3DNOWP,
+ FEATURE_ADX = 40,
+ // FEATURE_ABM,
+ FEATURE_CLDEMOTE = 42,
+ FEATURE_CLFLUSHOPT,
+ FEATURE_CLWB,
+ FEATURE_CLZERO,
+ FEATURE_CMPXCHG16B,
+ // FIXME: Not adding FEATURE_CMPXCHG8B is a workaround to make 'generic' as
+ // a cpu string with no X86_FEATURE_COMPAT features, which is required in
+ // current implementantion of cpu_specific/cpu_dispatch FMV feature.
+ // FEATURE_CMPXCHG8B,
+ FEATURE_ENQCMD = 48,
+ FEATURE_F16C,
+ FEATURE_FSGSBASE,
+ // FEATURE_FXSAVE,
+ // FEATURE_HLE,
+ // FEATURE_IBT,
+ FEATURE_LAHF_LM = 54,
+ FEATURE_LM,
+ FEATURE_LWP,
+ FEATURE_LZCNT,
+ FEATURE_MOVBE,
+ FEATURE_MOVDIR64B,
+ FEATURE_MOVDIRI,
+ FEATURE_MWAITX,
+ // FEATURE_OSXSAVE,
+ FEATURE_PCONFIG = 63,
+ FEATURE_PKU,
+ FEATURE_PREFETCHWT1,
+ FEATURE_PRFCHW,
+ FEATURE_PTWRITE,
+ FEATURE_RDPID,
+ FEATURE_RDRND,
+ FEATURE_RDSEED,
+ FEATURE_RTM,
+ FEATURE_SERIALIZE,
+ FEATURE_SGX,
+ FEATURE_SHA,
+ FEATURE_SHSTK,
+ FEATURE_TBM,
+ FEATURE_TSXLDTRK,
+ FEATURE_VAES,
+ FEATURE_WAITPKG,
+ FEATURE_WBNOINVD,
+ FEATURE_XSAVE,
+ FEATURE_XSAVEC,
+ FEATURE_XSAVEOPT,
+ FEATURE_XSAVES,
+ FEATURE_AMX_TILE,
+ FEATURE_AMX_INT8,
+ FEATURE_AMX_BF16,
+ FEATURE_UINTR,
+ FEATURE_HRESET,
+ FEATURE_KL,
+ // FEATURE_AESKLE,
+ FEATURE_WIDEKL = 92,
+ FEATURE_AVXVNNI,
+ FEATURE_AVX512FP16,
+ FEATURE_X86_64_BASELINE,
+ FEATURE_X86_64_V2,
+ FEATURE_X86_64_V3,
+ FEATURE_X86_64_V4,
+ FEATURE_AVXIFMA,
+ FEATURE_AVXVNNIINT8,
+ FEATURE_AVXNECONVERT,
+ FEATURE_CMPCCXADD,
+ FEATURE_AMX_FP16,
+ FEATURE_PREFETCHI,
+ FEATURE_RAOINT,
+ FEATURE_AMX_COMPLEX,
+ FEATURE_AVXVNNIINT16,
+ FEATURE_SM3,
+ FEATURE_SHA512,
+ FEATURE_SM4,
+ FEATURE_APXF,
+ FEATURE_USERMSR,
+ FEATURE_AVX10_1_256,
+ FEATURE_AVX10_1_512,
+ FEATURE_AVX10_2_256,
+ FEATURE_AVX10_2_512,
+ FEATURE_MOVRS,
+ CPU_FEATURE_MAX
};
-#define SET_FEAT(mask, bit) ((mask) |= ((HsWord64)1ULL << (bit)))
+// This code is copied from lib/Support/Host.cpp.
+// Changes to either file should be mirrored in the other.
-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);
+/// getX86CpuIDAndInfo - Execute the specified cpuid and return the 4 values in
+/// the specified arguments. If we can't run cpuid on the host, return true.
+static bool getX86CpuIDAndInfo(unsigned value, unsigned *rEAX, unsigned *rEBX,
+ unsigned *rECX, unsigned *rEDX) {
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(_MSC_VER)
+ return !__get_cpuid(value, rEAX, rEBX, rECX, rEDX);
+#elif defined(_MSC_VER)
+ // The MSVC intrinsic is portable across x86 and x64.
+ int registers[4];
+ __cpuid(registers, value);
+ *rEAX = registers[0];
+ *rEBX = registers[1];
+ *rECX = registers[2];
+ *rEDX = registers[3];
+ return false;
#else
- (void)leaf;
- (void)subleaf;
- (void)a;
- (void)b;
- (void)c;
- (void)d;
- return 0;
+ return true;
#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;
+/// getX86CpuIDAndInfoEx - Execute the specified cpuid with subleaf and return
+/// the 4 values in the specified arguments. If we can't run cpuid on the host,
+/// return true.
+static bool getX86CpuIDAndInfoEx(unsigned value, unsigned subleaf,
+ unsigned *rEAX, unsigned *rEBX, unsigned *rECX,
+ unsigned *rEDX) {
+ // TODO(boomanaiden154): When the minimum toolchain versions for gcc and clang
+ // are such that __cpuidex is defined within cpuid.h for both, we can remove
+ // the __get_cpuid_count function and share the MSVC implementation between
+ // all three.
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(_MSC_VER)
+ return !__get_cpuid_count(value, subleaf, rEAX, rEBX, rECX, rEDX);
+#elif defined(_MSC_VER)
+ int registers[4];
+ __cpuidex(registers, value, subleaf);
+ *rEAX = registers[0];
+ *rEBX = registers[1];
+ *rECX = registers[2];
+ *rEDX = registers[3];
+ return false;
#else
- return 0;
+ return true;
#endif
}
-#if defined(__APPLE__) && (defined(__i386__) || defined(__x86_64__))
-/* Query a macOS CPU-capability sysctl, e.g. "hw.optional.avx512f". */
-static int ghc_macos_sysctl_flag(const char *name)
-{
- int result = 0;
- size_t len = sizeof(result);
- if (sysctlbyname(name, &result, &len, NULL, 0) != 0) {
- return 0;
- }
- return result != 0;
-}
+// Read control register 0 (XCR0). Used to detect features such as AVX.
+static bool getX86XCR0(unsigned *rEAX, unsigned *rEDX) {
+ // TODO(boomanaiden154): When the minimum toolchain versions for gcc and clang
+ // are such that _xgetbv is supported by both, we can unify the implementation
+ // with MSVC and remove all inline assembly.
+#if defined(__GNUC__) || defined(__clang__)
+ // Check xgetbv; this uses a .byte sequence instead of the instruction
+ // directly because older assemblers do not include support for xgetbv and
+ // there is no easy way to conditionally compile based on the assembler used.
+ __asm__(".byte 0x0f, 0x01, 0xd0" : "=a"(*rEAX), "=d"(*rEDX) : "c"(0));
+ return false;
+#elif defined(_MSC_FULL_VER) && defined(_XCR_XFEATURE_ENABLED_MASK)
+ unsigned long long Result = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
+ *rEAX = Result;
+ *rEDX = Result >> 32;
+ return false;
+#else
+ return true;
#endif
+}
-HsWord64 ghc_detect_x86_cpu_features(void)
-{
- HsWord64 feats = 0;
+static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf,
+ unsigned *Features) {
+ unsigned EAX = 0, EBX = 0;
-#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
- uint32_t a, b, c, d;
- uint32_t max_basic = 0;
+#define hasFeature(F) ((Features[F / 32] >> (F % 32)) & 1)
+#define setFeature(F) Features[F / 32] |= 1U << (F % 32)
- if (!ghc_cpuid_count(0, 0, &a, &b, &c, &d)) {
- return 0;
- }
- max_basic = a;
- if (max_basic < 1) {
- return 0;
- }
+ if ((EDX >> 15) & 1)
+ setFeature(FEATURE_CMOV);
+ if ((EDX >> 23) & 1)
+ setFeature(FEATURE_MMX);
+ if ((EDX >> 25) & 1)
+ setFeature(FEATURE_SSE);
+ if ((EDX >> 26) & 1)
+ setFeature(FEATURE_SSE2);
- 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 ((ECX >> 0) & 1)
+ setFeature(FEATURE_SSE3);
+ if ((ECX >> 1) & 1)
+ setFeature(FEATURE_PCLMUL);
+ if ((ECX >> 9) & 1)
+ setFeature(FEATURE_SSSE3);
+ if ((ECX >> 12) & 1)
+ setFeature(FEATURE_FMA);
+ if ((ECX >> 13) & 1)
+ setFeature(FEATURE_CMPXCHG16B);
+ if ((ECX >> 19) & 1)
+ setFeature(FEATURE_SSE4_1);
+ if ((ECX >> 20) & 1)
+ setFeature(FEATURE_SSE4_2);
+ if ((ECX >> 22) & 1)
+ setFeature(FEATURE_MOVBE);
+ if ((ECX >> 23) & 1)
+ setFeature(FEATURE_POPCNT);
+ if ((ECX >> 25) & 1)
+ setFeature(FEATURE_AES);
+ if ((ECX >> 29) & 1)
+ setFeature(FEATURE_F16C);
+ if ((ECX >> 30) & 1)
+ setFeature(FEATURE_RDRND);
+ // If CPUID indicates support for XSAVE, XRESTORE and AVX, and XGETBV
+ // indicates that the AVX registers will be saved and restored on context
+ // switch, then we have full AVX support.
+ const unsigned AVXBits = (1 << 27) | (1 << 28);
+ bool HasAVXSave = ((ECX & AVXBits) == AVXBits) && !getX86XCR0(&EAX, &EDX) &&
+ ((EAX & 0x6) == 0x6);
#if defined(__APPLE__)
- /* On x86_64 macOS the kernel enables AVX-512 XSAVE state lazily: XCR0
- reads back with the opmask/ZMM bits clear until a process first faults
- on an AVX-512 instruction, so the XCR0 check above is a false negative
- on AVX-512-capable Macs. Use the OS feature query instead. Checking
- AVX512F alone suffices here; the AVX-512 sub-features (BW/CD/DQ/VL) are
- still decoded from CPUID leaf 7 below.
-
- Refs:
- https://zenn.dev/mod_poppo/articles/detect-processor-features-x86?locale=en…
- https://github.com/minoki/haskell-cpu-features */
- avx512_usable = ghc_macos_sysctl_flag("hw.optional.avx512f");
+ // Darwin lazily saves the AVX512 context on first use: trust that the OS will
+ // save the AVX512 context if we use AVX512 instructions, even the bit is not
+ // set right now.
+ bool HasAVX512Save = true;
+#else
+ // AVX512 requires additional context to be saved by the OS.
+ bool HasAVX512Save = HasAVXSave && ((EAX & 0xe0) == 0xe0);
#endif
+ // AMX requires additional context to be saved by the OS.
+ const unsigned AMXBits = (1 << 17) | (1 << 18);
+ bool HasXSave = ((ECX >> 27) & 1) && !getX86XCR0(&EAX, &EDX);
+ bool HasAMXSave = HasXSave && ((EAX & AMXBits) == AMXBits);
- 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);
+ // GHC deviation from upstream: FMA instructions are VEX-encoded and
+ // unusable unless the OS saves the AVX register state, so FEATURE_FMA (set
+ // from the raw CPUID bit above) is cleared again when full AVX support is
+ // missing. This matches llvm/lib/TargetParser/Host.cpp ("fma") and GCC's
+ // gcc/common/config/i386/cpuinfo.h (FEATURE_FMA under avx_usable).
+ if (!HasAVXSave)
+ Features[FEATURE_FMA / 32] &= ~(1U << (FEATURE_FMA % 32));
+
+ if (HasAVXSave)
+ setFeature(FEATURE_AVX);
+
+ if (((ECX >> 26) & 1) && HasAVXSave)
+ setFeature(FEATURE_XSAVE);
+
+ bool HasLeaf7 =
+ MaxLeaf >= 0x7 && !getX86CpuIDAndInfoEx(0x7, 0x0, &EAX, &EBX, &ECX, &EDX);
+
+ if (HasLeaf7 && ((EBX >> 0) & 1))
+ setFeature(FEATURE_FSGSBASE);
+ if (HasLeaf7 && ((EBX >> 2) & 1))
+ setFeature(FEATURE_SGX);
+ if (HasLeaf7 && ((EBX >> 3) & 1))
+ setFeature(FEATURE_BMI);
+ if (HasLeaf7 && ((EBX >> 5) & 1) && HasAVXSave)
+ setFeature(FEATURE_AVX2);
+ if (HasLeaf7 && ((EBX >> 8) & 1))
+ setFeature(FEATURE_BMI2);
+ if (HasLeaf7 && ((EBX >> 11) & 1))
+ setFeature(FEATURE_RTM);
+ if (HasLeaf7 && ((EBX >> 16) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512F);
+ if (HasLeaf7 && ((EBX >> 17) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512DQ);
+ if (HasLeaf7 && ((EBX >> 18) & 1))
+ setFeature(FEATURE_RDSEED);
+ if (HasLeaf7 && ((EBX >> 19) & 1))
+ setFeature(FEATURE_ADX);
+ if (HasLeaf7 && ((EBX >> 21) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512IFMA);
+ if (HasLeaf7 && ((EBX >> 24) & 1))
+ setFeature(FEATURE_CLWB);
+ if (HasLeaf7 && ((EBX >> 26) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512PF);
+ if (HasLeaf7 && ((EBX >> 27) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512ER);
+ if (HasLeaf7 && ((EBX >> 28) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512CD);
+ if (HasLeaf7 && ((EBX >> 29) & 1))
+ setFeature(FEATURE_SHA);
+ if (HasLeaf7 && ((EBX >> 30) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512BW);
+ if (HasLeaf7 && ((EBX >> 31) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512VL);
+
+ if (HasLeaf7 && ((ECX >> 0) & 1))
+ setFeature(FEATURE_PREFETCHWT1);
+ if (HasLeaf7 && ((ECX >> 1) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512VBMI);
+ if (HasLeaf7 && ((ECX >> 4) & 1))
+ setFeature(FEATURE_PKU);
+ if (HasLeaf7 && ((ECX >> 5) & 1))
+ setFeature(FEATURE_WAITPKG);
+ if (HasLeaf7 && ((ECX >> 6) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512VBMI2);
+ if (HasLeaf7 && ((ECX >> 7) & 1))
+ setFeature(FEATURE_SHSTK);
+ if (HasLeaf7 && ((ECX >> 8) & 1))
+ setFeature(FEATURE_GFNI);
+ if (HasLeaf7 && ((ECX >> 9) & 1) && HasAVXSave)
+ setFeature(FEATURE_VAES);
+ if (HasLeaf7 && ((ECX >> 10) & 1) && HasAVXSave)
+ setFeature(FEATURE_VPCLMULQDQ);
+ if (HasLeaf7 && ((ECX >> 11) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512VNNI);
+ if (HasLeaf7 && ((ECX >> 12) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512BITALG);
+ if (HasLeaf7 && ((ECX >> 14) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512VPOPCNTDQ);
+ if (HasLeaf7 && ((ECX >> 22) & 1))
+ setFeature(FEATURE_RDPID);
+ if (HasLeaf7 && ((ECX >> 23) & 1))
+ setFeature(FEATURE_KL);
+ if (HasLeaf7 && ((ECX >> 25) & 1))
+ setFeature(FEATURE_CLDEMOTE);
+ if (HasLeaf7 && ((ECX >> 27) & 1))
+ setFeature(FEATURE_MOVDIRI);
+ if (HasLeaf7 && ((ECX >> 28) & 1))
+ setFeature(FEATURE_MOVDIR64B);
+ if (HasLeaf7 && ((ECX >> 29) & 1))
+ setFeature(FEATURE_ENQCMD);
+
+ if (HasLeaf7 && ((EDX >> 2) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX5124VNNIW);
+ if (HasLeaf7 && ((EDX >> 3) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX5124FMAPS);
+ if (HasLeaf7 && ((EDX >> 5) & 1))
+ setFeature(FEATURE_UINTR);
+ if (HasLeaf7 && ((EDX >> 8) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512VP2INTERSECT);
+ if (HasLeaf7 && ((EDX >> 14) & 1))
+ setFeature(FEATURE_SERIALIZE);
+ if (HasLeaf7 && ((EDX >> 16) & 1))
+ setFeature(FEATURE_TSXLDTRK);
+ if (HasLeaf7 && ((EDX >> 18) & 1))
+ setFeature(FEATURE_PCONFIG);
+ if (HasLeaf7 && ((EDX >> 22) & 1) && HasAMXSave)
+ setFeature(FEATURE_AMX_BF16);
+ if (HasLeaf7 && ((EDX >> 23) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512FP16);
+ if (HasLeaf7 && ((EDX >> 24) & 1) && HasAMXSave)
+ setFeature(FEATURE_AMX_TILE);
+ if (HasLeaf7 && ((EDX >> 25) & 1) && HasAMXSave)
+ setFeature(FEATURE_AMX_INT8);
+
+ // EAX from subleaf 0 is the maximum subleaf supported. Some CPUs don't
+ // return all 0s for invalid subleaves so check the limit.
+ bool HasLeaf7Subleaf1 =
+ HasLeaf7 && EAX >= 1 &&
+ !getX86CpuIDAndInfoEx(0x7, 0x1, &EAX, &EBX, &ECX, &EDX);
+ if (HasLeaf7Subleaf1 && ((EAX >> 0) & 1))
+ setFeature(FEATURE_SHA512);
+ if (HasLeaf7Subleaf1 && ((EAX >> 1) & 1))
+ setFeature(FEATURE_SM3);
+ if (HasLeaf7Subleaf1 && ((EAX >> 2) & 1))
+ setFeature(FEATURE_SM4);
+ if (HasLeaf7Subleaf1 && ((EAX >> 3) & 1))
+ setFeature(FEATURE_RAOINT);
+ if (HasLeaf7Subleaf1 && ((EAX >> 4) & 1) && HasAVXSave)
+ setFeature(FEATURE_AVXVNNI);
+ if (HasLeaf7Subleaf1 && ((EAX >> 5) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512BF16);
+ if (HasLeaf7Subleaf1 && ((EAX >> 7) & 1))
+ setFeature(FEATURE_CMPCCXADD);
+ if (HasLeaf7Subleaf1 && ((EAX >> 21) & 1) && HasAMXSave)
+ setFeature(FEATURE_AMX_FP16);
+ if (HasLeaf7Subleaf1 && ((EAX >> 22) & 1))
+ setFeature(FEATURE_HRESET);
+ if (HasLeaf7Subleaf1 && ((EAX >> 23) & 1) && HasAVXSave)
+ setFeature(FEATURE_AVXIFMA);
+ if (HasLeaf7Subleaf1 && ((EAX >> 31) & 1))
+ setFeature(FEATURE_MOVRS);
+
+ if (HasLeaf7Subleaf1 && ((EDX >> 4) & 1) && HasAVXSave)
+ setFeature(FEATURE_AVXVNNIINT8);
+ if (HasLeaf7Subleaf1 && ((EDX >> 5) & 1) && HasAVXSave)
+ setFeature(FEATURE_AVXNECONVERT);
+ if (HasLeaf7Subleaf1 && ((EDX >> 8) & 1) && HasAMXSave)
+ setFeature(FEATURE_AMX_COMPLEX);
+ if (HasLeaf7Subleaf1 && ((EDX >> 10) & 1) && HasAVXSave)
+ setFeature(FEATURE_AVXVNNIINT16);
+ if (HasLeaf7Subleaf1 && ((EDX >> 14) & 1))
+ setFeature(FEATURE_PREFETCHI);
+ if (HasLeaf7Subleaf1 && ((EDX >> 15) & 1))
+ setFeature(FEATURE_USERMSR);
+ if (HasLeaf7Subleaf1 && ((EDX >> 21) & 1))
+ setFeature(FEATURE_APXF);
+
+ unsigned MaxLevel = 0;
+ getX86CpuIDAndInfo(0, &MaxLevel, &EBX, &ECX, &EDX);
+ bool HasLeafD = MaxLevel >= 0xd &&
+ !getX86CpuIDAndInfoEx(0xd, 0x1, &EAX, &EBX, &ECX, &EDX);
+ if (HasLeafD && ((EAX >> 0) & 1) && HasAVXSave)
+ setFeature(FEATURE_XSAVEOPT);
+ if (HasLeafD && ((EAX >> 1) & 1) && HasAVXSave)
+ setFeature(FEATURE_XSAVEC);
+ if (HasLeafD && ((EAX >> 3) & 1) && HasAVXSave)
+ setFeature(FEATURE_XSAVES);
+
+ bool HasLeaf24 =
+ MaxLevel >= 0x24 && !getX86CpuIDAndInfo(0x24, &EAX, &EBX, &ECX, &EDX);
+ if (HasLeaf7Subleaf1 && ((EDX >> 19) & 1) && HasLeaf24) {
+ bool Has512Len = (EBX >> 18) & 1;
+ int AVX10Ver = EBX & 0xff;
+ if (AVX10Ver >= 2) {
+ setFeature(FEATURE_AVX10_2_256);
+ if (Has512Len)
+ setFeature(FEATURE_AVX10_2_512);
}
- if (has_fma_hw && avx_usable) {
- SET_FEAT(feats, GHC_X86_FEAT_FMA);
+ if (AVX10Ver >= 1) {
+ setFeature(FEATURE_AVX10_1_256);
+ if (Has512Len)
+ setFeature(FEATURE_AVX10_1_512);
}
+ }
- 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);
- }
+ unsigned MaxExtLevel = 0;
+ getX86CpuIDAndInfo(0x80000000, &MaxExtLevel, &EBX, &ECX, &EDX);
- 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);
- }
- }
+ bool HasExtLeaf1 = MaxExtLevel >= 0x80000001 &&
+ !getX86CpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX);
+ if (HasExtLeaf1) {
+ if (ECX & 1)
+ setFeature(FEATURE_LAHF_LM);
+ if ((ECX >> 5) & 1)
+ setFeature(FEATURE_LZCNT);
+ if (((ECX >> 6) & 1))
+ setFeature(FEATURE_SSE4_A);
+ if (((ECX >> 8) & 1))
+ setFeature(FEATURE_PRFCHW);
+ if (((ECX >> 11) & 1))
+ setFeature(FEATURE_XOP);
+ if (((ECX >> 15) & 1))
+ setFeature(FEATURE_LWP);
+ if (((ECX >> 16) & 1))
+ setFeature(FEATURE_FMA4);
+ if (((ECX >> 21) & 1))
+ setFeature(FEATURE_TBM);
+ if (((ECX >> 29) & 1))
+ setFeature(FEATURE_MWAITX);
+
+ if (((EDX >> 29) & 1))
+ setFeature(FEATURE_LM);
+ }
+
+ bool HasExtLeaf8 = MaxExtLevel >= 0x80000008 &&
+ !getX86CpuIDAndInfo(0x80000008, &EAX, &EBX, &ECX, &EDX);
+ if (HasExtLeaf8 && ((EBX >> 0) & 1))
+ setFeature(FEATURE_CLZERO);
+ if (HasExtLeaf8 && ((EBX >> 9) & 1))
+ setFeature(FEATURE_WBNOINVD);
+
+ bool HasLeaf14 = MaxLevel >= 0x14 &&
+ !getX86CpuIDAndInfoEx(0x14, 0x0, &EAX, &EBX, &ECX, &EDX);
+ if (HasLeaf14 && ((EBX >> 4) & 1))
+ setFeature(FEATURE_PTWRITE);
- if (has_gfni) {
- SET_FEAT(feats, GHC_X86_FEAT_GFNI);
+ bool HasLeaf19 =
+ MaxLevel >= 0x19 && !getX86CpuIDAndInfo(0x19, &EAX, &EBX, &ECX, &EDX);
+ if (HasLeaf7 && HasLeaf19 && ((EBX >> 2) & 1))
+ setFeature(FEATURE_WIDEKL);
+
+ if (hasFeature(FEATURE_LM) && hasFeature(FEATURE_SSE2)) {
+ setFeature(FEATURE_X86_64_BASELINE);
+ if (hasFeature(FEATURE_CMPXCHG16B) && hasFeature(FEATURE_POPCNT) &&
+ hasFeature(FEATURE_LAHF_LM) && hasFeature(FEATURE_SSE4_2)) {
+ setFeature(FEATURE_X86_64_V2);
+ if (hasFeature(FEATURE_AVX2) && hasFeature(FEATURE_BMI) &&
+ hasFeature(FEATURE_BMI2) && hasFeature(FEATURE_F16C) &&
+ hasFeature(FEATURE_FMA) && hasFeature(FEATURE_LZCNT) &&
+ hasFeature(FEATURE_MOVBE)) {
+ setFeature(FEATURE_X86_64_V3);
+ if (hasFeature(FEATURE_AVX512BW) && hasFeature(FEATURE_AVX512CD) &&
+ hasFeature(FEATURE_AVX512DQ) && hasFeature(FEATURE_AVX512VL))
+ setFeature(FEATURE_X86_64_V4);
}
}
}
-#endif
- return feats;
+#undef hasFeature
+#undef setFeature
+}
+
+#endif /* GHC_HOST_IS_X86 */
+
+/* The driver, modeled on upstream's __cpu_indicator_init: same CPUID call
+ * sequence, but the first 64 feature bits are returned to the caller (see
+ * GHC.Driver.CpuFeatures) instead of being stored in the __cpu_model
+ * globals. */
+HsWord64 ghc_detect_x86_cpu_features(void)
+{
+#if defined(GHC_HOST_IS_X86)
+ unsigned EAX = 0, EBX = 0, ECX = 0, EDX = 0;
+ unsigned MaxLeaf = 5;
+ unsigned Features[(CPU_FEATURE_MAX + 31) / 32] = {0};
+
+ if (getX86CpuIDAndInfo(0, &MaxLeaf, &EBX, &ECX, &EDX) || MaxLeaf < 1)
+ return 0;
+
+ getX86CpuIDAndInfo(1, &EAX, &EBX, &ECX, &EDX);
+
+ // Find available features.
+ getAvailableFeatures(ECX, EDX, MaxLeaf, &Features[0]);
+
+ return ((HsWord64)Features[1] << 32) | (HsWord64)Features[0];
+#else
+ return 0;
+#endif
}
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/7bab9752985efd921aa0198e2c3cd2a…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/7bab9752985efd921aa0198e2c3cd2a…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 7 commits: Hadrian: avoid response files when command line is short enough
by Marge Bot (@marge-bot) 10 Jun '26
by Marge Bot (@marge-bot) 10 Jun '26
10 Jun '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
498bb21a by David Eichmann at 2026-06-09T18:02:39-04:00
Hadrian: avoid response files when command line is short enough
This replaces the logic of always using response files on Windows.
With the new condition based on command line lenght, reponse files
can be avoided in many more cases (on windows).
Now that response files are only used in a small number of cases,
response files are always kept and the -r / --keep-response-files
command line options have been removed
The response file paths are nolonger randomized. They are placed in the
`_build/rsp` directory. This ensures they are ignored by git and we
that Hadrian reuses response file paths when rebuilding rather than
leaving stale response files around.
Update user guide putting response files in its own section
- - - - -
87f510a5 by Simon Hengel at 2026-06-09T18:03:25-04:00
Don't use non-breaking spaces
- - - - -
41a19379 by David Eichmann at 2026-06-09T18:04:11-04:00
Hadrian: remove unused wrapper scripts from windows bindist
These wrapper scripts are only installed on non-relocatable builds
which are not generally supported on windows.
- - - - -
947a7f91 by sheaf at 2026-06-09T18:36:45-04:00
Don't drop ticks around variables of type `IO ()`
GHC.Core.Utils.mkTick is responsible for placing a tick on a Core
expression. It contains logic for dropping SCCs (non-counting profiling
ticks) around non-function variables, as such variables cannot
meaningfully contribute to profiles. However, the logic for what counts
as a function was incorrect: it used `isFunTy` which returns 'False' for
types such as 'IO ()' where the function arrow is hidden under a
newtype.
We now use 'mightBeFunTy' instead of 'isFunTy'. This ensures we don't
drop ticks in cases we aren't sure.
On the way, we improve the documentation of 'isFunTy', 'isPiTy' and
'mightBeFunTy', and update the latter's implementation to consistently
handle unary classes.
Fixes #27225
-------------------------
Metric Decrease:
T5642
-------------------------
- - - - -
1d1f248b by Simon Jakobi at 2026-06-09T18:36:46-04:00
testsuite: Add regression test for #4081
Check that a strict constructor field is unboxed once outside an
enclosing loop, not re-inspected each iteration (the float-out
case-floating from 9cb20b488). Uses simonpj's `data T a = T !a` example
from the ticket; T4081.stderr captures the expected Core.
Co-Authored-By: Claude Opus 4.7 <noreply(a)anthropic.com>
- - - - -
a562349b by sheaf at 2026-06-09T18:36:50-04:00
Check for cabal-install >= 3.12 upfront
Starting with commit 8cb99552f607f6bc4000e45ab32532d50c8bb996, Hadrian
requires cabal-install >= 3.12 in order to use the 'cabal path' command
that was introduced in version 3.12, as per
https://github.com/haskell/cabal/blob/a51c4ee1556d816ad86e90db7e6330dd51b0b…
This was not reflected in the Hadrian build script, causing a delayed
build failure instead of enforcing the version requirement upfront,
which this patch does.
Fixes #27317
- - - - -
085e8e31 by sheaf at 2026-06-09T18:36:51-04:00
Fix crash in Data.Data instance for HsCtxt
The Data.Data instance for HsCtxt contained an error for the 'toConstr'
method, which could trigger for example when looking at -ddump-tc-ast
traces. Replace it with the 'abstractConstr' pattern used in the rest of
the codebase.
- - - - -
35 changed files:
- + changelog.d/T27225
- + changelog.d/T27317
- + changelog.d/T27359
- changelog.d/hadrian-response-files.md
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Runtime/Debugger/Breakpoints.hs
- compiler/GHC/Runtime/Eval/Types.hs
- compiler/GHC/Runtime/Heap/Inspect.hs
- compiler/GHC/Types/RepType.hs
- compiler/GHC/Utils/Logger.hs
- docs/users_guide/using.rst
- hadrian/build-cabal
- hadrian/src/Builder.hs
- hadrian/src/CommandLine.hs
- hadrian/src/Hadrian/Builder/Ar.hs
- hadrian/src/Hadrian/Utilities.hs
- hadrian/src/Rules/BinaryDist.hs
- libraries/ghc-internal/src/GHC/Internal/Lexeme.hs
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/profiling/should_run/T27225.hs
- + testsuite/tests/profiling/should_run/T27225.stdout
- + testsuite/tests/profiling/should_run/T27225b.hs
- + testsuite/tests/profiling/should_run/T27225b.stdout
- testsuite/tests/profiling/should_run/all.T
- testsuite/tests/profiling/should_run/caller-cc/CallerCc1.prof.sample
- testsuite/tests/profiling/should_run/callstack001.stdout
- testsuite/tests/profiling/should_run/scc001.prof.sample
- + testsuite/tests/simplCore/should_compile/T4081.hs
- + testsuite/tests/simplCore/should_compile/T4081.stderr
- testsuite/tests/simplCore/should_compile/all.T
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5d75f13ca133594386b4366aae2063…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5d75f13ca133594386b4366aae2063…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][master] Hadrian: remove unused wrapper scripts from windows bindist
by Marge Bot (@marge-bot) 10 Jun '26
by Marge Bot (@marge-bot) 10 Jun '26
10 Jun '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
41a19379 by David Eichmann at 2026-06-09T18:04:11-04:00
Hadrian: remove unused wrapper scripts from windows bindist
These wrapper scripts are only installed on non-relocatable builds
which are not generally supported on windows.
- - - - -
1 changed file:
- hadrian/src/Rules/BinaryDist.hs
Changes:
=====================================
hadrian/src/Rules/BinaryDist.hs
=====================================
@@ -290,23 +290,25 @@ bindistRules = do
copyFile ("hadrian" -/- "cfg" -/- "default.target.in") (bindistFilesDir -/- "default.target.in")
copyFile ("hadrian" -/- "cfg" -/- "default.host.target.in") (bindistFilesDir -/- "default.host.target.in")
- -- todo: do we need these wrappers on windows
- forM_ bin_targets $ \(pkg, _) -> do
- needed_wrappers <- pkgToWrappers pkg
- forM_ needed_wrappers $ \wrapper_name -> do
- let suffix = if useGhcPrefix pkg
- then "ghc-" ++ version
- else version
- wrapper_content <- wrapper wrapper_name
- let unversioned_wrapper_path = bindistFilesDir -/- "wrappers" -/- wrapper_name
- versioned_wrapper = wrapper_name ++ "-" ++ suffix
- versioned_wrapper_path = bindistFilesDir -/- "wrappers" -/- versioned_wrapper
- -- Write the wrapper to the versioned path
- writeFile' versioned_wrapper_path wrapper_content
- -- Create a symlink from the non-versioned to the versioned.
- liftIO $ do
- IO.removeFile unversioned_wrapper_path <|> return ()
- IO.createFileLink versioned_wrapper unversioned_wrapper_path
+ -- These wrapper scripts are only necessary in the configure/install
+ -- workflow which is not supported on windows.
+ unless windowsHost $ do
+ forM_ bin_targets $ \(pkg, _) -> do
+ needed_wrappers <- pkgToWrappers pkg
+ forM_ needed_wrappers $ \wrapper_name -> do
+ let suffix = if useGhcPrefix pkg
+ then "ghc-" ++ version
+ else version
+ wrapper_content <- wrapper wrapper_name
+ let unversioned_wrapper_path = bindistFilesDir -/- "wrappers" -/- wrapper_name
+ versioned_wrapper = wrapper_name ++ "-" ++ suffix
+ versioned_wrapper_path = bindistFilesDir -/- "wrappers" -/- versioned_wrapper
+ -- Write the wrapper to the versioned path
+ writeFile' versioned_wrapper_path wrapper_content
+ -- Create a symlink from the non-versioned to the versioned.
+ liftIO $ do
+ IO.removeFile unversioned_wrapper_path <|> return ()
+ IO.createFileLink versioned_wrapper unversioned_wrapper_path
let buildBinDist compressor = do
win_target <- isWinTarget
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/41a19379fa97bc6c944526814fd644e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/41a19379fa97bc6c944526814fd644e…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
10 Jun '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
87f510a5 by Simon Hengel at 2026-06-09T18:03:25-04:00
Don't use non-breaking spaces
- - - - -
7 changed files:
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Runtime/Debugger/Breakpoints.hs
- compiler/GHC/Runtime/Eval/Types.hs
- compiler/GHC/Runtime/Heap/Inspect.hs
- compiler/GHC/Utils/Logger.hs
- libraries/ghc-internal/src/GHC/Internal/Lexeme.hs
Changes:
=====================================
compiler/GHC/Driver/Backend.hs
=====================================
@@ -184,7 +184,7 @@ import GHC.Platform
-- about enumerating them. Just one set of error messages has been
-- ported to have an open-world assumption: these are the error
-- messages associated with type checking of foreign imports and
--- exports. To allow other errors to be issued with an open-world
+-- exports. To allow other errors to be issued with an open-world
-- assumption, use functions `backendValidityOfCImport` and
-- `backendValidityOfCExport` as models, and have a look at how the
-- 'expected back ends' are used in modules "GHC.Tc.Gen.Foreign" and
@@ -225,11 +225,11 @@ platformJSSupported platform
| otherwise = False
--- | A value of type @Backend@ represents one of GHC's back ends.
+-- | A value of type @Backend@ represents one of GHC's back ends.
-- The set of back ends cannot be extended except by modifying the
-- definition of @Backend@ in this module.
--
--- The @Backend@ type is abstract; that is, its value constructors are
+-- The @Backend@ type is abstract; that is, its value constructors are
-- not exported. It's crucial that they not be exported, because a
-- value of type @Backend@ carries only the back end's /name/, not its
-- behavior or properties. If @Backend@ were not abstract, then code
=====================================
compiler/GHC/Parser/PostProcess/Haddock.hs
=====================================
@@ -289,7 +289,7 @@ instance HasHaddock (Located (HsModule GhcPs)) where
pure $ L l_mod $
mod { hsmodExports = hsmodExports'
, hsmodDecls = hsmodDecls'
- , hsmodExt = (hsmodExt mod) { hsmodHaddockModHeader = headerDocs } }
+ , hsmodExt = (hsmodExt mod) { hsmodHaddockModHeader = headerDocs } }
lexHsDocString :: HsDocString -> HsDoc GhcPs
lexHsDocString = lexHsDoc parseIdentifier
=====================================
compiler/GHC/Runtime/Debugger/Breakpoints.hs
=====================================
@@ -177,7 +177,7 @@ resolveFunctionBreakpoint inp = do
-- for
-- (a) this binder only (it maybe a top-level or a nested declaration)
-- (b) that do not have an enclosing breakpoint
-findBreakForBind :: String {-^ Name of bind to break at -} -> ModBreaks -> [(BreakTickIndex, RealSrcSpan)]
+findBreakForBind :: String {-^ Name of bind to break at -} -> ModBreaks -> [(BreakTickIndex, RealSrcSpan)]
findBreakForBind str_name modbreaks = filter (not . enclosed) ticks
where
ticks = [ (index, span)
@@ -226,7 +226,7 @@ getModBreak m = do
-- source breakpoint, it means all *ocurrences* of that breakpoint across
-- modules should be stopped at -- hence we keep a trie from BreakpointId to
-- the list of internal break ids using it.
--- See also Note [Breakpoint identifiers]
+-- See also Note [Breakpoint identifiers]
type BreakpointOccurrences = ModuleEnv (IntMap.IntMap [InternalBreakpointId])
-- | Lookup all InternalBreakpointIds matching the given BreakpointId
=====================================
compiler/GHC/Runtime/Eval/Types.hs
=====================================
@@ -75,7 +75,7 @@ enableGhcStepMode _ = EvalStepSingle
-- and the SrcSpan of a breakpoint we hit, return @True@ if we should stop at
-- this breakpoint.
--
--- In particular, this will always be @False@ for @'RunToCompletion'@ and
+-- In particular, this will always be @False@ for @'RunToCompletion'@ and
-- @'RunAndLogSteps'@. We'd need further information e.g. about the user
-- breakpoints to determine whether to break in those modes.
breakHere :: Bool -- ^ Was this breakpoint explicitly active (in the @BreakArray@s)?
=====================================
compiler/GHC/Runtime/Heap/Inspect.hs
=====================================
@@ -293,22 +293,22 @@ ppr_termM1 Prim{valRaw=words, ty=ty} =
return $ repPrim (tyConAppTyCon ty) words
ppr_termM1 Suspension{ty=ty, bound_to=Nothing, infoprov=mipe} =
return $ hcat $
- [ char '_'
- , whenPprDebug $
+ [ char '_'
+ , whenPprDebug $
space <>
dcolon <>
pprSigmaType ty
- ] ++
- [ whenPprDebug $
+ ] ++
+ [ whenPprDebug $
space <>
char '<' <>
text (ipSrcFile ipe) <>
char ':' <>
text (ipSrcSpan ipe) <>
char '>'
- | Just ipe <- [mipe]
+ | Just ipe <- [mipe]
, not $ null $ ipSrcFile ipe
- ]
+ ]
ppr_termM1 Suspension{ty=ty, bound_to=Just n}
| otherwise = return$ parens$ ppr n <> dcolon <> pprSigmaType ty
ppr_termM1 Term{} = panic "ppr_termM1 - Term"
=====================================
compiler/GHC/Utils/Logger.hs
=====================================
@@ -413,7 +413,7 @@ defaultLogAction = defaultLogActionWithHandles stdout stderr
-- | The default 'LogAction' parametrized over the standard output and standard error handles.
-- Allows clients to replicate the log message formatting of GHC with custom handles.
-defaultLogActionWithHandles :: Handle {-^ Handle for standard output -} -> Handle {-^ Handle for standard errors -} -> LogAction
+defaultLogActionWithHandles :: Handle {-^ Handle for standard output -} -> Handle {-^ Handle for standard errors -} -> LogAction
defaultLogActionWithHandles out err logflags msg_class srcSpan msg
| log_dopt Opt_D_dump_json logflags = jsonLogActionWithHandle out logflags msg_class srcSpan msg
| otherwise = case msg_class of
=====================================
libraries/ghc-internal/src/GHC/Internal/Lexeme.hs
=====================================
@@ -44,7 +44,7 @@ okSymChar c
_ -> False
startsVarSym, startsVarId, startsConSym, startsConId :: Char -> Bool
-startsVarSym c = okSymChar c && c /= ':' -- Infix Ids
+startsVarSym c = okSymChar c && c /= ':' -- Infix Ids
startsConSym c = c == ':' -- Infix data constructors
startsVarId c = c == '_' || case generalCategory c of -- Ordinary Ids
LowercaseLetter -> True
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/87f510a5ee1f7f08baceebfcd79c3e3…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/87f510a5ee1f7f08baceebfcd79c3e3…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][master] Hadrian: avoid response files when command line is short enough
by Marge Bot (@marge-bot) 10 Jun '26
by Marge Bot (@marge-bot) 10 Jun '26
10 Jun '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
498bb21a by David Eichmann at 2026-06-09T18:02:39-04:00
Hadrian: avoid response files when command line is short enough
This replaces the logic of always using response files on Windows.
With the new condition based on command line lenght, reponse files
can be avoided in many more cases (on windows).
Now that response files are only used in a small number of cases,
response files are always kept and the -r / --keep-response-files
command line options have been removed
The response file paths are nolonger randomized. They are placed in the
`_build/rsp` directory. This ensures they are ignored by git and we
that Hadrian reuses response file paths when rebuilding rather than
leaving stale response files around.
Update user guide putting response files in its own section
- - - - -
6 changed files:
- changelog.d/hadrian-response-files.md
- docs/users_guide/using.rst
- hadrian/src/Builder.hs
- hadrian/src/CommandLine.hs
- hadrian/src/Hadrian/Builder/Ar.hs
- hadrian/src/Hadrian/Utilities.hs
Changes:
=====================================
changelog.d/hadrian-response-files.md
=====================================
@@ -1,9 +1,15 @@
section: packaging
-synopsis: Add a flag to tell Hadrian to keep response files
-issues: #27184
-mrs: !15906
+synopsis: Improved Hadrian's use of response files
+issues: #27230
+mrs: !15906 !16134
description:
- Hadrian can now be instructed to keep response files with the new
- --keep-response-files command line flag. This is helpful when debugging a
- build failure, as it allows re-running the failing command line invocation
- without an error due to a missing response file.
+ Response files are files that contain command-line arguments. Hadrian uses
+ response files to shorten command-line lengths. This is important on Windows
+ where command-line lengths are limited.
+
+ Hadrian now supports response files when invoking GHC. In order to support
+ manually rerunning commands issued by Hadrian, response files are no longer
+ deleted. Instead they are stored under `_build/rsp`. Response files are now
+ only used when the corresponding command-line is too long for the host
+ platform. This greatly reduces the use of response files and avoids excessive
+ file usage. Response files are overwritten on subsequent Hadrian builds.
=====================================
docs/users_guide/using.rst
=====================================
@@ -85,17 +85,6 @@ all files; you cannot, for example, invoke
``ghc -c -O1 Foo.hs -O2 Bar.hs`` to apply different optimisation levels
to the files ``Foo.hs`` and ``Bar.hs``.
-In addition to passing arguments via the command-line, arguments can be passed
-via GNU-style response files. For instance,
-
-.. code-block:: bash
-
- $ cat response-file
- -O1
- Hello.hs
- -o Hello
- $ ghc @response-file
-
.. note::
.. index::
@@ -118,9 +107,24 @@ via GNU-style response files. For instance,
``-fspecialise`` will not be enabled, since the ``-fno-specialise``
overrides the ``-fspecialise`` implied by ``-O1``.
+
+Command-line arguments in response files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In addition to passing arguments via the command-line, arguments can be passed
+via GNU-style response files. For instance,
+
+.. code-block:: bash
+
+ $ cat response-file
+ -O1
+ Hello.hs
+ -o Hello
+ $ ghc @response-file
+
.. _source-file-options:
-Command line options in source files
+Command-line options in source files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. index::
=====================================
hadrian/src/Builder.hs
=====================================
@@ -304,7 +304,7 @@ instance H.Builder Builder where
case builder of
Ar Pack stg -> do
useTempFile <- arSupportsAtFile stg
- if useTempFile then runAr path buildArgs buildInputs buildOptions
+ if useTempFile then runAr output path buildArgs buildInputs buildOptions
else runArWithoutTempFile path buildArgs buildInputs buildOptions
Ar Unpack _ -> cmd' [Cwd output] [path] buildArgs buildOptions
@@ -343,7 +343,7 @@ instance H.Builder Builder where
Exit _ <- cmd' [path] (buildArgs ++ [input]) buildOptions
return ()
- Haddock BuildPackage -> runHaddock path buildArgs buildInputs
+ Haddock BuildPackage -> runHaddock output path buildArgs buildInputs
Ghc _ _ ->
-- Use a response file for ghc invocations to avoid issues with command line
@@ -351,9 +351,11 @@ instance H.Builder Builder where
-- NB: we can't put the buildArgs in a response file, because some flags require
-- empty arguments (such as the -dep-suffix flag), but that isn't supported
-- yet due to #26560.
- withResponseFileOnWindows
- (\buildInputs' -> cmd [path] buildArgs buildInputs' buildOptions)
+ withResponseFileIfLongCmd
+ output
+ (toCmdArgument [path] <> toCmdArgument buildArgs)
buildInputs
+ (toCmdArgument buildOptions)
HsCpp -> captureStdout
@@ -389,13 +391,16 @@ instance H.Builder Builder where
-- | Invoke @haddock@ given a path to it and a list of arguments. On Windows,
-- the input file arguments are passed as a response file.
-runHaddock :: FilePath -- ^ path to @haddock@
+runHaddock :: FilePath -- ^ base name to use for response file
+ -> FilePath -- ^ path to @haddock@
-> [String]
-> [FilePath] -- ^ input file paths
-> Action ()
-runHaddock haddockPath flagArgs fileInputs = withResponseFileOnWindows
- (cmd [haddockPath] flagArgs)
+runHaddock outputFilePath haddockPath flagArgs fileInputs = withResponseFileIfLongCmd
+ outputFilePath
+ (toCmdArgument [haddockPath] <> toCmdArgument flagArgs)
fileInputs
+ (CmdArgument [])
-- TODO: Some builders are required only on certain platforms. For example,
-- 'Objdump' is only required on OpenBSD and AIX. Add support for platform
=====================================
hadrian/src/CommandLine.hs
=====================================
@@ -3,8 +3,7 @@ module CommandLine (
lookupBignum,
cmdBignum, cmdProgressInfo, cmdCompleteSetting,
cmdDocsArgs, cmdUnitIdHash, lookupBuildRoot, TestArgs(..), TestSpeed(..), defaultTestArgs,
- cmdPrefix, cmdChangelogVersion, DocArgs(..), defaultDocArgs,
- cmdKeepResponseFiles
+ cmdPrefix, cmdChangelogVersion, DocArgs(..), defaultDocArgs
) where
import Data.Either
@@ -12,7 +11,7 @@ import qualified Data.HashMap.Strict as Map
import Data.List.Extra
import Development.Shake hiding (Normal)
import Flavour (DocTargets, DocTarget(..))
-import Hadrian.Utilities hiding (buildRoot, keepResponseFiles)
+import Hadrian.Utilities hiding (buildRoot)
import Settings.Parser
import System.Console.GetOpt
import System.Environment
@@ -37,7 +36,6 @@ data CommandLineArgs = CommandLineArgs
, testArgs :: TestArgs
, docsArgs :: DocArgs
, docTargets :: DocTargets
- , keepResponseFiles :: Bool
, prefix :: Maybe FilePath
, changelogVersion :: Maybe String
, completeStg :: Maybe String }
@@ -58,7 +56,6 @@ defaultCommandLineArgs = CommandLineArgs
, testArgs = defaultTestArgs
, docsArgs = defaultDocArgs
, docTargets = Set.fromList [minBound..maxBound]
- , keepResponseFiles = False
, prefix = Nothing
, changelogVersion = Nothing
, completeStg = Nothing }
@@ -141,9 +138,6 @@ readFreeze1 = Right $ \flags -> flags { freeze1 = True }
readFreeze2 = Right $ \flags -> flags { freeze1 = True, freeze2 = True }
readSkipDepends = Right $ \flags -> flags { skipDepends = True }
-readKeepResponseFiles :: Either String (CommandLineArgs -> CommandLineArgs)
-readKeepResponseFiles = Right $ \flags -> flags { keepResponseFiles = True }
-
readUnitIdHash :: Either String (CommandLineArgs -> CommandLineArgs)
readUnitIdHash = Right $ \flags ->
trace "--hash-unit-ids is deprecated. It is enabled by release flavour or +hash_unit_ids flavour transformer" $
@@ -302,8 +296,6 @@ optDescrs =
"Progress info style (None, Brief, Normal or Unicorn)."
, Option [] ["docs"] (ReqArg readDocsArg "TARGET")
"Strip down docs targets (none, no-haddocks, no-sphinx[-{html, pdfs, man}]."
- , Option ['r'] ["keep-response-files"] (NoArg readKeepResponseFiles)
- "Keep response files created during the build (for debugging)."
, Option ['k'] ["keep-test-files"] (NoArg readTestKeepFiles)
"Keep all the files generated when running the testsuite."
, Option [] ["test-compiler"] (ReqArg readTestCompiler "TEST_COMPILER")
@@ -382,7 +374,6 @@ cmdLineArgsMap = do
return $ insertExtra (progressInfo args) -- Accessed by Hadrian.Utilities
$ insertExtra (buildRoot args) -- Accessed by Hadrian.Utilities
- $ insertExtra (KeepResponseFiles $ keepResponseFiles args) -- Accessed by Hadrian.Utilities
$ insertExtra (testArgs args) -- Accessed by Settings.Builders.RunTest
$ insertExtra (docsArgs args) -- Accessed by Rules.Documentation
$ insertExtra allSettings -- Accessed by Settings
@@ -424,9 +415,6 @@ cmdUnitIdHash = unitIdHash <$> cmdLineArgs
cmdBignum :: Action (Maybe String)
cmdBignum = bignum <$> cmdLineArgs
-cmdKeepResponseFiles :: Action Bool
-cmdKeepResponseFiles = keepResponseFiles <$> cmdLineArgs
-
cmdProgressInfo :: Action ProgressInfo
cmdProgressInfo = progressInfo <$> cmdLineArgs
=====================================
hadrian/src/Hadrian/Builder/Ar.hs
=====================================
@@ -35,14 +35,16 @@ instance NFData ArMode
-- to be archived is passed via a temporary response file. Passing arguments
-- via a response file is not supported by some versions of @ar@, in which
-- case you should use 'runArWithoutTempFile' instead.
-runAr :: FilePath -- ^ path to @ar@
+runAr :: FilePath -- ^ base name to use for response files
+ -> FilePath -- ^ path to @ar@
-> [String] -- ^ other arguments
-> [FilePath] -- ^ input file paths
-> [CmdOption] -- ^ Additional options
-> Action ()
-runAr arPath flagArgs fileArgs buildOptions = withResponseFile $ \tmp -> do
- writeFile' tmp $ unwords fileArgs
- cmd [arPath] flagArgs ('@' : tmp) buildOptions
+runAr outputFilePath arPath flagArgs fileArgs buildOptions = do
+ rspFile <- responseFilePath outputFilePath
+ writeFile' rspFile $ unwords fileArgs
+ cmd [arPath] flagArgs ('@' : rspFile) buildOptions
-- | Invoke @ar@ given a path to it and a list of arguments. Note that @ar@
-- will be called multiple times if the list of files to be archived is too
=====================================
hadrian/src/Hadrian/Utilities.hs
=====================================
@@ -1,4 +1,6 @@
+{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE TypeFamilies #-}
+
module Hadrian.Utilities (
-- * List manipulation
fromSingleton, replaceEq, minusOrd, intersectOrd, lookupAll, chunksOfSize,
@@ -14,7 +16,7 @@ module Hadrian.Utilities (
-- * Paths
BuildRoot (..), buildRoot, buildRootRules, isGeneratedSource,
- KeepResponseFiles (..), keepResponseFiles, withResponseFile, withResponseFileOnWindows,
+ withResponseFileIfLongCmd, responseFilePath,
-- * File system operations
copyFile, copyFileUntracked, createFileLink, fixFile,
@@ -47,11 +49,10 @@ import Data.Maybe
import Data.Typeable (TypeRep, typeOf)
import Development.Shake hiding (Normal)
import Development.Shake.Classes
+import Development.Shake.Command (CmdArgument (..), IsCmdArgument (toCmdArgument))
import Development.Shake.FilePath
import GHC.ResponseFile (escapeArgs)
import System.Environment (lookupEnv)
-import System.Info.Extra (isWindows)
-import System.IO (hClose, openTempFile)
import System.IO.Error (isPermissionError)
import qualified Data.ByteString as BS
@@ -255,13 +256,13 @@ infix 1 %%>
-- library, they can reach 2MB! Some operating systems do not support command
-- lines of such length, and this function can be used to obtain a reasonable
-- approximation of the limit. On Windows, it is theoretically 32768 characters
--- (since Windows 7). In practice we use 31000 to leave some breathing space for
+-- (since Windows 7). In practice we use 30000 to leave some breathing space for
-- the builder path & name, auxiliary flags, and other overheads. On Mac OS X,
-- ARG_MAX is 262144, yet when using @xargs@ on OSX this is reduced by over
-- 20000. Hence, 200000 seems like a sensible limit. On other operating systems
-- we currently use the 4194304 setting.
cmdLineLengthLimit :: Int
-cmdLineLengthLimit | IO.isWindows = 31000
+cmdLineLengthLimit | IO.isWindows = 30000
| IO.isMac = 200000
| otherwise = 4194304
@@ -321,53 +322,35 @@ buildRootRules = do
isGeneratedSource :: FilePath -> Action Bool
isGeneratedSource file = buildRoot <&> (`isPrefixOf` file)
-newtype KeepResponseFiles = KeepResponseFiles Bool deriving (Eq, Show)
-
--- | Whether to retain response files after the build action that created them
--- completes. Mainly useful for debugging.
-keepResponseFiles :: Action Bool
-keepResponseFiles = do
- KeepResponseFiles keep <- userSetting (KeepResponseFiles False)
- return keep
-
--- | Run an action either with command arguments direcly or by, on Windows,
--- placing those arguments into a response file escaped with @GHC.ResponseFile.escapeArgs@.
---
--- With @--keep-response-files@, the file is left on disk (if used)
-withResponseFileOnWindows ::
- ([String] -> Action a) -- ^ Action to perform given arguments (of the form @["\@reponseFilePath"]@ on Windows)
- -> [String] -- ^ Command arguments
- -> Action a
-withResponseFileOnWindows action commandArgs = do
- if isWindows
- then withResponseFile $ \tmp -> do
- writeFile' tmp (escapeArgs commandArgs)
- action ['@' : tmp]
- else action commandArgs
-
--- | Run an action with a response file path.
---
--- With @--keep-response-files@, the file is left on disk.
-withResponseFile :: (FilePath -> Action a) -> Action a
-withResponseFile action = do
- keep <- keepResponseFiles
- let putVerboseResponseFile tmp = do
- verbosity <- getVerbosity
- when (verbosity >= Verbose) $ do
- tmpContent <- liftIO (readFile tmp)
- putVerbose (tmp <> " (use hadrian flag --keep-response-files to keep this file):\n" <> tmpContent)
- if keep
- then do
- (tmp, h) <- liftIO $ openTempFile "." "hadrian-rsp"
- liftIO $ hClose h
- putInfo $ "Keeping response file: " ++ tmp
- result <- action tmp
- putVerboseResponseFile tmp
- return result
- else withTempFile $ \tmp -> do
- result <- action tmp
- putVerboseResponseFile tmp
- return result
+-- | Run an command with the given arguments. If the command is too long then the
+-- response file arguments are placed into a response file and escaped with @GHC.ResponseFile.escapeArgs@.
+withResponseFileIfLongCmd ::
+ CmdResult c
+ => FilePath -- ^ Response base name. The reponse file is placed in @_build/rsp/\<Response base name\>@.
+ -> CmdArgument -- ^ Command and arguments before the response file arguments.
+ -> [String] -- ^ Response file aruguments.
+ -> CmdArgument -- ^ Command arguments after the response file arguments.
+ -> Action c
+withResponseFileIfLongCmd outputFilePath argsPre argsResp argsPost = do
+ let cmdLineLengh = sum
+ [ 1 + length arg -- add one to account for space inbetween arguments
+ | let CmdArgument args = argsPre <> toCmdArgument argsResp <> argsPost
+ , Right arg <- args
+ ]
+ if cmdLineLengh < cmdLineLengthLimit
+ then cmd argsPre argsResp argsPost
+ else do
+ rspFile <- responseFilePath outputFilePath
+ writeFile' rspFile (escapeArgs argsResp)
+ cmd argsPre ['@' : rspFile] argsPost
+
+-- | Convert a command's output file path to a response file path to be used for that command.
+-- Response files are placed in a dedicated @rps@ directory under the build directory. This avoids
+-- clutering the work tree or interfearing with other build directories.
+responseFilePath :: FilePath -> Action FilePath
+responseFilePath outputFilePath = do
+ buildDir <- buildRoot
+ return $ buildDir </> "rsp" </> outputFilePath
-- | Link a file tracking the link target. Create the target directory if
-- missing.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/498bb21afd3852bd99d001ac5b20eeb…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/498bb21afd3852bd99d001ac5b20eeb…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
09 Jun '26
Duncan Coutts deleted branch wip/dcoutts/issue-26867 at Glasgow Haskell Compiler / GHC
--
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/hsctxt-data-instance] Fix crash in Data.Data instance for HsCtxt
by sheaf (@sheaf) 09 Jun '26
by sheaf (@sheaf) 09 Jun '26
09 Jun '26
sheaf pushed to branch wip/hsctxt-data-instance at Glasgow Haskell Compiler / GHC
Commits:
a8ac1a02 by sheaf at 2026-06-09T17:57:41+02:00
Fix crash in Data.Data instance for HsCtxt
The Data.Data instance for HsCtxt contained an error for the 'toConstr'
method, which could trigger for example when looking at -ddump-tc-ast
traces. Replace it with the 'abstractConstr' pattern used in the rest of
the codebase.
- - - - -
2 changed files:
- + changelog.d/T27359
- compiler/GHC/Hs/Instances.hs
Changes:
=====================================
changelog.d/T27359
=====================================
@@ -0,0 +1,9 @@
+section: compiler
+issues: #27359
+mrs: !16142
+synopsis:
+ Fix ``Data.Data`` instance of ``HsCtxt`` to avoid a crash
+description:
+ The ``toConstr`` method of the ``Data.Data`` instance of ``HsCtxt`` now
+ uses ``abstractConstr`` instead of ``error``, which avoids it triggering
+ runtime crashes (e.g. when using ``-ddump-tc-ast``).
=====================================
compiler/GHC/Hs/Instances.hs
=====================================
@@ -46,6 +46,7 @@ import GHC.Hs.ImpExp
import GHC.Parser.Annotation
import GHC.Types.Name.Reader (WithUserRdr(..))
import GHC.Types.InlinePragma (ActivationGhc)
+import GHC.Utils.Misc (abstractConstr)
import GHC.Data.BooleanFormula (BooleanFormula(..))
import Language.Haskell.Syntax.Decls
import Language.Haskell.Syntax.Decls.Foreign (CType(..), Header(..))
@@ -673,7 +674,7 @@ deriving instance Eq (IE GhcTc)
instance Data HsCtxt where
gunfold _ _ _ = error "no gunfold for HsCtxt"
gfoldl _ k z = k z
- toConstr = error "no toConstr for HsCtxt"
+ toConstr _ = abstractConstr "HsCtxt"
dataTypeOf = error "no dataTypeOf for HsCtxt"
deriving instance Data XXExprGhcRn
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a8ac1a02090cd95c7f11e3c62c68076…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a8ac1a02090cd95c7f11e3c62c68076…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 3 commits: Hadrian: avoid response files when command line is short enough
by Marge Bot (@marge-bot) 09 Jun '26
by Marge Bot (@marge-bot) 09 Jun '26
09 Jun '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
23a13665 by David Eichmann at 2026-06-09T11:22:05-04:00
Hadrian: avoid response files when command line is short enough
This replaces the logic of always using response files on Windows.
With the new condition based on command line lenght, reponse files
can be avoided in many more cases (on windows).
Now that response files are only used in a small number of cases,
response files are always kept and the -r / --keep-response-files
command line options have been removed
The response file paths are nolonger randomized. They are placed in the
`_build/rsp` directory. This ensures they are ignored by git and we
that Hadrian reuses response file paths when rebuilding rather than
leaving stale response files around.
Update user guide putting response files in its own section
- - - - -
648aa8e5 by Simon Hengel at 2026-06-09T11:22:06-04:00
Don't use non-breaking spaces
- - - - -
5d75f13c by David Eichmann at 2026-06-09T11:22:06-04:00
Hadrian: remove unused wrapper scripts from windows bindist
These wrapper scripts are only installed on non-relocatable builds
which are not generally supported on windows.
- - - - -
14 changed files:
- changelog.d/hadrian-response-files.md
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Runtime/Debugger/Breakpoints.hs
- compiler/GHC/Runtime/Eval/Types.hs
- compiler/GHC/Runtime/Heap/Inspect.hs
- compiler/GHC/Utils/Logger.hs
- docs/users_guide/using.rst
- hadrian/src/Builder.hs
- hadrian/src/CommandLine.hs
- hadrian/src/Hadrian/Builder/Ar.hs
- hadrian/src/Hadrian/Utilities.hs
- hadrian/src/Rules/BinaryDist.hs
- libraries/ghc-internal/src/GHC/Internal/Lexeme.hs
Changes:
=====================================
changelog.d/hadrian-response-files.md
=====================================
@@ -1,9 +1,15 @@
section: packaging
-synopsis: Add a flag to tell Hadrian to keep response files
-issues: #27184
-mrs: !15906
+synopsis: Improved Hadrian's use of response files
+issues: #27230
+mrs: !15906 !16134
description:
- Hadrian can now be instructed to keep response files with the new
- --keep-response-files command line flag. This is helpful when debugging a
- build failure, as it allows re-running the failing command line invocation
- without an error due to a missing response file.
+ Response files are files that contain command-line arguments. Hadrian uses
+ response files to shorten command-line lengths. This is important on Windows
+ where command-line lengths are limited.
+
+ Hadrian now supports response files when invoking GHC. In order to support
+ manually rerunning commands issued by Hadrian, response files are no longer
+ deleted. Instead they are stored under `_build/rsp`. Response files are now
+ only used when the corresponding command-line is too long for the host
+ platform. This greatly reduces the use of response files and avoids excessive
+ file usage. Response files are overwritten on subsequent Hadrian builds.
=====================================
compiler/GHC/Driver/Backend.hs
=====================================
@@ -184,7 +184,7 @@ import GHC.Platform
-- about enumerating them. Just one set of error messages has been
-- ported to have an open-world assumption: these are the error
-- messages associated with type checking of foreign imports and
--- exports. To allow other errors to be issued with an open-world
+-- exports. To allow other errors to be issued with an open-world
-- assumption, use functions `backendValidityOfCImport` and
-- `backendValidityOfCExport` as models, and have a look at how the
-- 'expected back ends' are used in modules "GHC.Tc.Gen.Foreign" and
@@ -225,11 +225,11 @@ platformJSSupported platform
| otherwise = False
--- | A value of type @Backend@ represents one of GHC's back ends.
+-- | A value of type @Backend@ represents one of GHC's back ends.
-- The set of back ends cannot be extended except by modifying the
-- definition of @Backend@ in this module.
--
--- The @Backend@ type is abstract; that is, its value constructors are
+-- The @Backend@ type is abstract; that is, its value constructors are
-- not exported. It's crucial that they not be exported, because a
-- value of type @Backend@ carries only the back end's /name/, not its
-- behavior or properties. If @Backend@ were not abstract, then code
=====================================
compiler/GHC/Parser/PostProcess/Haddock.hs
=====================================
@@ -289,7 +289,7 @@ instance HasHaddock (Located (HsModule GhcPs)) where
pure $ L l_mod $
mod { hsmodExports = hsmodExports'
, hsmodDecls = hsmodDecls'
- , hsmodExt = (hsmodExt mod) { hsmodHaddockModHeader = headerDocs } }
+ , hsmodExt = (hsmodExt mod) { hsmodHaddockModHeader = headerDocs } }
lexHsDocString :: HsDocString -> HsDoc GhcPs
lexHsDocString = lexHsDoc parseIdentifier
=====================================
compiler/GHC/Runtime/Debugger/Breakpoints.hs
=====================================
@@ -177,7 +177,7 @@ resolveFunctionBreakpoint inp = do
-- for
-- (a) this binder only (it maybe a top-level or a nested declaration)
-- (b) that do not have an enclosing breakpoint
-findBreakForBind :: String {-^ Name of bind to break at -} -> ModBreaks -> [(BreakTickIndex, RealSrcSpan)]
+findBreakForBind :: String {-^ Name of bind to break at -} -> ModBreaks -> [(BreakTickIndex, RealSrcSpan)]
findBreakForBind str_name modbreaks = filter (not . enclosed) ticks
where
ticks = [ (index, span)
@@ -226,7 +226,7 @@ getModBreak m = do
-- source breakpoint, it means all *ocurrences* of that breakpoint across
-- modules should be stopped at -- hence we keep a trie from BreakpointId to
-- the list of internal break ids using it.
--- See also Note [Breakpoint identifiers]
+-- See also Note [Breakpoint identifiers]
type BreakpointOccurrences = ModuleEnv (IntMap.IntMap [InternalBreakpointId])
-- | Lookup all InternalBreakpointIds matching the given BreakpointId
=====================================
compiler/GHC/Runtime/Eval/Types.hs
=====================================
@@ -75,7 +75,7 @@ enableGhcStepMode _ = EvalStepSingle
-- and the SrcSpan of a breakpoint we hit, return @True@ if we should stop at
-- this breakpoint.
--
--- In particular, this will always be @False@ for @'RunToCompletion'@ and
+-- In particular, this will always be @False@ for @'RunToCompletion'@ and
-- @'RunAndLogSteps'@. We'd need further information e.g. about the user
-- breakpoints to determine whether to break in those modes.
breakHere :: Bool -- ^ Was this breakpoint explicitly active (in the @BreakArray@s)?
=====================================
compiler/GHC/Runtime/Heap/Inspect.hs
=====================================
@@ -293,22 +293,22 @@ ppr_termM1 Prim{valRaw=words, ty=ty} =
return $ repPrim (tyConAppTyCon ty) words
ppr_termM1 Suspension{ty=ty, bound_to=Nothing, infoprov=mipe} =
return $ hcat $
- [ char '_'
- , whenPprDebug $
+ [ char '_'
+ , whenPprDebug $
space <>
dcolon <>
pprSigmaType ty
- ] ++
- [ whenPprDebug $
+ ] ++
+ [ whenPprDebug $
space <>
char '<' <>
text (ipSrcFile ipe) <>
char ':' <>
text (ipSrcSpan ipe) <>
char '>'
- | Just ipe <- [mipe]
+ | Just ipe <- [mipe]
, not $ null $ ipSrcFile ipe
- ]
+ ]
ppr_termM1 Suspension{ty=ty, bound_to=Just n}
| otherwise = return$ parens$ ppr n <> dcolon <> pprSigmaType ty
ppr_termM1 Term{} = panic "ppr_termM1 - Term"
=====================================
compiler/GHC/Utils/Logger.hs
=====================================
@@ -413,7 +413,7 @@ defaultLogAction = defaultLogActionWithHandles stdout stderr
-- | The default 'LogAction' parametrized over the standard output and standard error handles.
-- Allows clients to replicate the log message formatting of GHC with custom handles.
-defaultLogActionWithHandles :: Handle {-^ Handle for standard output -} -> Handle {-^ Handle for standard errors -} -> LogAction
+defaultLogActionWithHandles :: Handle {-^ Handle for standard output -} -> Handle {-^ Handle for standard errors -} -> LogAction
defaultLogActionWithHandles out err logflags msg_class srcSpan msg
| log_dopt Opt_D_dump_json logflags = jsonLogActionWithHandle out logflags msg_class srcSpan msg
| otherwise = case msg_class of
=====================================
docs/users_guide/using.rst
=====================================
@@ -85,17 +85,6 @@ all files; you cannot, for example, invoke
``ghc -c -O1 Foo.hs -O2 Bar.hs`` to apply different optimisation levels
to the files ``Foo.hs`` and ``Bar.hs``.
-In addition to passing arguments via the command-line, arguments can be passed
-via GNU-style response files. For instance,
-
-.. code-block:: bash
-
- $ cat response-file
- -O1
- Hello.hs
- -o Hello
- $ ghc @response-file
-
.. note::
.. index::
@@ -118,9 +107,24 @@ via GNU-style response files. For instance,
``-fspecialise`` will not be enabled, since the ``-fno-specialise``
overrides the ``-fspecialise`` implied by ``-O1``.
+
+Command-line arguments in response files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In addition to passing arguments via the command-line, arguments can be passed
+via GNU-style response files. For instance,
+
+.. code-block:: bash
+
+ $ cat response-file
+ -O1
+ Hello.hs
+ -o Hello
+ $ ghc @response-file
+
.. _source-file-options:
-Command line options in source files
+Command-line options in source files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. index::
=====================================
hadrian/src/Builder.hs
=====================================
@@ -304,7 +304,7 @@ instance H.Builder Builder where
case builder of
Ar Pack stg -> do
useTempFile <- arSupportsAtFile stg
- if useTempFile then runAr path buildArgs buildInputs buildOptions
+ if useTempFile then runAr output path buildArgs buildInputs buildOptions
else runArWithoutTempFile path buildArgs buildInputs buildOptions
Ar Unpack _ -> cmd' [Cwd output] [path] buildArgs buildOptions
@@ -343,7 +343,7 @@ instance H.Builder Builder where
Exit _ <- cmd' [path] (buildArgs ++ [input]) buildOptions
return ()
- Haddock BuildPackage -> runHaddock path buildArgs buildInputs
+ Haddock BuildPackage -> runHaddock output path buildArgs buildInputs
Ghc _ _ ->
-- Use a response file for ghc invocations to avoid issues with command line
@@ -351,9 +351,11 @@ instance H.Builder Builder where
-- NB: we can't put the buildArgs in a response file, because some flags require
-- empty arguments (such as the -dep-suffix flag), but that isn't supported
-- yet due to #26560.
- withResponseFileOnWindows
- (\buildInputs' -> cmd [path] buildArgs buildInputs' buildOptions)
+ withResponseFileIfLongCmd
+ output
+ (toCmdArgument [path] <> toCmdArgument buildArgs)
buildInputs
+ (toCmdArgument buildOptions)
HsCpp -> captureStdout
@@ -389,13 +391,16 @@ instance H.Builder Builder where
-- | Invoke @haddock@ given a path to it and a list of arguments. On Windows,
-- the input file arguments are passed as a response file.
-runHaddock :: FilePath -- ^ path to @haddock@
+runHaddock :: FilePath -- ^ base name to use for response file
+ -> FilePath -- ^ path to @haddock@
-> [String]
-> [FilePath] -- ^ input file paths
-> Action ()
-runHaddock haddockPath flagArgs fileInputs = withResponseFileOnWindows
- (cmd [haddockPath] flagArgs)
+runHaddock outputFilePath haddockPath flagArgs fileInputs = withResponseFileIfLongCmd
+ outputFilePath
+ (toCmdArgument [haddockPath] <> toCmdArgument flagArgs)
fileInputs
+ (CmdArgument [])
-- TODO: Some builders are required only on certain platforms. For example,
-- 'Objdump' is only required on OpenBSD and AIX. Add support for platform
=====================================
hadrian/src/CommandLine.hs
=====================================
@@ -3,8 +3,7 @@ module CommandLine (
lookupBignum,
cmdBignum, cmdProgressInfo, cmdCompleteSetting,
cmdDocsArgs, cmdUnitIdHash, lookupBuildRoot, TestArgs(..), TestSpeed(..), defaultTestArgs,
- cmdPrefix, cmdChangelogVersion, DocArgs(..), defaultDocArgs,
- cmdKeepResponseFiles
+ cmdPrefix, cmdChangelogVersion, DocArgs(..), defaultDocArgs
) where
import Data.Either
@@ -12,7 +11,7 @@ import qualified Data.HashMap.Strict as Map
import Data.List.Extra
import Development.Shake hiding (Normal)
import Flavour (DocTargets, DocTarget(..))
-import Hadrian.Utilities hiding (buildRoot, keepResponseFiles)
+import Hadrian.Utilities hiding (buildRoot)
import Settings.Parser
import System.Console.GetOpt
import System.Environment
@@ -37,7 +36,6 @@ data CommandLineArgs = CommandLineArgs
, testArgs :: TestArgs
, docsArgs :: DocArgs
, docTargets :: DocTargets
- , keepResponseFiles :: Bool
, prefix :: Maybe FilePath
, changelogVersion :: Maybe String
, completeStg :: Maybe String }
@@ -58,7 +56,6 @@ defaultCommandLineArgs = CommandLineArgs
, testArgs = defaultTestArgs
, docsArgs = defaultDocArgs
, docTargets = Set.fromList [minBound..maxBound]
- , keepResponseFiles = False
, prefix = Nothing
, changelogVersion = Nothing
, completeStg = Nothing }
@@ -141,9 +138,6 @@ readFreeze1 = Right $ \flags -> flags { freeze1 = True }
readFreeze2 = Right $ \flags -> flags { freeze1 = True, freeze2 = True }
readSkipDepends = Right $ \flags -> flags { skipDepends = True }
-readKeepResponseFiles :: Either String (CommandLineArgs -> CommandLineArgs)
-readKeepResponseFiles = Right $ \flags -> flags { keepResponseFiles = True }
-
readUnitIdHash :: Either String (CommandLineArgs -> CommandLineArgs)
readUnitIdHash = Right $ \flags ->
trace "--hash-unit-ids is deprecated. It is enabled by release flavour or +hash_unit_ids flavour transformer" $
@@ -302,8 +296,6 @@ optDescrs =
"Progress info style (None, Brief, Normal or Unicorn)."
, Option [] ["docs"] (ReqArg readDocsArg "TARGET")
"Strip down docs targets (none, no-haddocks, no-sphinx[-{html, pdfs, man}]."
- , Option ['r'] ["keep-response-files"] (NoArg readKeepResponseFiles)
- "Keep response files created during the build (for debugging)."
, Option ['k'] ["keep-test-files"] (NoArg readTestKeepFiles)
"Keep all the files generated when running the testsuite."
, Option [] ["test-compiler"] (ReqArg readTestCompiler "TEST_COMPILER")
@@ -382,7 +374,6 @@ cmdLineArgsMap = do
return $ insertExtra (progressInfo args) -- Accessed by Hadrian.Utilities
$ insertExtra (buildRoot args) -- Accessed by Hadrian.Utilities
- $ insertExtra (KeepResponseFiles $ keepResponseFiles args) -- Accessed by Hadrian.Utilities
$ insertExtra (testArgs args) -- Accessed by Settings.Builders.RunTest
$ insertExtra (docsArgs args) -- Accessed by Rules.Documentation
$ insertExtra allSettings -- Accessed by Settings
@@ -424,9 +415,6 @@ cmdUnitIdHash = unitIdHash <$> cmdLineArgs
cmdBignum :: Action (Maybe String)
cmdBignum = bignum <$> cmdLineArgs
-cmdKeepResponseFiles :: Action Bool
-cmdKeepResponseFiles = keepResponseFiles <$> cmdLineArgs
-
cmdProgressInfo :: Action ProgressInfo
cmdProgressInfo = progressInfo <$> cmdLineArgs
=====================================
hadrian/src/Hadrian/Builder/Ar.hs
=====================================
@@ -35,14 +35,16 @@ instance NFData ArMode
-- to be archived is passed via a temporary response file. Passing arguments
-- via a response file is not supported by some versions of @ar@, in which
-- case you should use 'runArWithoutTempFile' instead.
-runAr :: FilePath -- ^ path to @ar@
+runAr :: FilePath -- ^ base name to use for response files
+ -> FilePath -- ^ path to @ar@
-> [String] -- ^ other arguments
-> [FilePath] -- ^ input file paths
-> [CmdOption] -- ^ Additional options
-> Action ()
-runAr arPath flagArgs fileArgs buildOptions = withResponseFile $ \tmp -> do
- writeFile' tmp $ unwords fileArgs
- cmd [arPath] flagArgs ('@' : tmp) buildOptions
+runAr outputFilePath arPath flagArgs fileArgs buildOptions = do
+ rspFile <- responseFilePath outputFilePath
+ writeFile' rspFile $ unwords fileArgs
+ cmd [arPath] flagArgs ('@' : rspFile) buildOptions
-- | Invoke @ar@ given a path to it and a list of arguments. Note that @ar@
-- will be called multiple times if the list of files to be archived is too
=====================================
hadrian/src/Hadrian/Utilities.hs
=====================================
@@ -1,4 +1,6 @@
+{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE TypeFamilies #-}
+
module Hadrian.Utilities (
-- * List manipulation
fromSingleton, replaceEq, minusOrd, intersectOrd, lookupAll, chunksOfSize,
@@ -14,7 +16,7 @@ module Hadrian.Utilities (
-- * Paths
BuildRoot (..), buildRoot, buildRootRules, isGeneratedSource,
- KeepResponseFiles (..), keepResponseFiles, withResponseFile, withResponseFileOnWindows,
+ withResponseFileIfLongCmd, responseFilePath,
-- * File system operations
copyFile, copyFileUntracked, createFileLink, fixFile,
@@ -47,11 +49,10 @@ import Data.Maybe
import Data.Typeable (TypeRep, typeOf)
import Development.Shake hiding (Normal)
import Development.Shake.Classes
+import Development.Shake.Command (CmdArgument (..), IsCmdArgument (toCmdArgument))
import Development.Shake.FilePath
import GHC.ResponseFile (escapeArgs)
import System.Environment (lookupEnv)
-import System.Info.Extra (isWindows)
-import System.IO (hClose, openTempFile)
import System.IO.Error (isPermissionError)
import qualified Data.ByteString as BS
@@ -255,13 +256,13 @@ infix 1 %%>
-- library, they can reach 2MB! Some operating systems do not support command
-- lines of such length, and this function can be used to obtain a reasonable
-- approximation of the limit. On Windows, it is theoretically 32768 characters
--- (since Windows 7). In practice we use 31000 to leave some breathing space for
+-- (since Windows 7). In practice we use 30000 to leave some breathing space for
-- the builder path & name, auxiliary flags, and other overheads. On Mac OS X,
-- ARG_MAX is 262144, yet when using @xargs@ on OSX this is reduced by over
-- 20000. Hence, 200000 seems like a sensible limit. On other operating systems
-- we currently use the 4194304 setting.
cmdLineLengthLimit :: Int
-cmdLineLengthLimit | IO.isWindows = 31000
+cmdLineLengthLimit | IO.isWindows = 30000
| IO.isMac = 200000
| otherwise = 4194304
@@ -321,53 +322,35 @@ buildRootRules = do
isGeneratedSource :: FilePath -> Action Bool
isGeneratedSource file = buildRoot <&> (`isPrefixOf` file)
-newtype KeepResponseFiles = KeepResponseFiles Bool deriving (Eq, Show)
-
--- | Whether to retain response files after the build action that created them
--- completes. Mainly useful for debugging.
-keepResponseFiles :: Action Bool
-keepResponseFiles = do
- KeepResponseFiles keep <- userSetting (KeepResponseFiles False)
- return keep
-
--- | Run an action either with command arguments direcly or by, on Windows,
--- placing those arguments into a response file escaped with @GHC.ResponseFile.escapeArgs@.
---
--- With @--keep-response-files@, the file is left on disk (if used)
-withResponseFileOnWindows ::
- ([String] -> Action a) -- ^ Action to perform given arguments (of the form @["\@reponseFilePath"]@ on Windows)
- -> [String] -- ^ Command arguments
- -> Action a
-withResponseFileOnWindows action commandArgs = do
- if isWindows
- then withResponseFile $ \tmp -> do
- writeFile' tmp (escapeArgs commandArgs)
- action ['@' : tmp]
- else action commandArgs
-
--- | Run an action with a response file path.
---
--- With @--keep-response-files@, the file is left on disk.
-withResponseFile :: (FilePath -> Action a) -> Action a
-withResponseFile action = do
- keep <- keepResponseFiles
- let putVerboseResponseFile tmp = do
- verbosity <- getVerbosity
- when (verbosity >= Verbose) $ do
- tmpContent <- liftIO (readFile tmp)
- putVerbose (tmp <> " (use hadrian flag --keep-response-files to keep this file):\n" <> tmpContent)
- if keep
- then do
- (tmp, h) <- liftIO $ openTempFile "." "hadrian-rsp"
- liftIO $ hClose h
- putInfo $ "Keeping response file: " ++ tmp
- result <- action tmp
- putVerboseResponseFile tmp
- return result
- else withTempFile $ \tmp -> do
- result <- action tmp
- putVerboseResponseFile tmp
- return result
+-- | Run an command with the given arguments. If the command is too long then the
+-- response file arguments are placed into a response file and escaped with @GHC.ResponseFile.escapeArgs@.
+withResponseFileIfLongCmd ::
+ CmdResult c
+ => FilePath -- ^ Response base name. The reponse file is placed in @_build/rsp/\<Response base name\>@.
+ -> CmdArgument -- ^ Command and arguments before the response file arguments.
+ -> [String] -- ^ Response file aruguments.
+ -> CmdArgument -- ^ Command arguments after the response file arguments.
+ -> Action c
+withResponseFileIfLongCmd outputFilePath argsPre argsResp argsPost = do
+ let cmdLineLengh = sum
+ [ 1 + length arg -- add one to account for space inbetween arguments
+ | let CmdArgument args = argsPre <> toCmdArgument argsResp <> argsPost
+ , Right arg <- args
+ ]
+ if cmdLineLengh < cmdLineLengthLimit
+ then cmd argsPre argsResp argsPost
+ else do
+ rspFile <- responseFilePath outputFilePath
+ writeFile' rspFile (escapeArgs argsResp)
+ cmd argsPre ['@' : rspFile] argsPost
+
+-- | Convert a command's output file path to a response file path to be used for that command.
+-- Response files are placed in a dedicated @rps@ directory under the build directory. This avoids
+-- clutering the work tree or interfearing with other build directories.
+responseFilePath :: FilePath -> Action FilePath
+responseFilePath outputFilePath = do
+ buildDir <- buildRoot
+ return $ buildDir </> "rsp" </> outputFilePath
-- | Link a file tracking the link target. Create the target directory if
-- missing.
=====================================
hadrian/src/Rules/BinaryDist.hs
=====================================
@@ -290,23 +290,25 @@ bindistRules = do
copyFile ("hadrian" -/- "cfg" -/- "default.target.in") (bindistFilesDir -/- "default.target.in")
copyFile ("hadrian" -/- "cfg" -/- "default.host.target.in") (bindistFilesDir -/- "default.host.target.in")
- -- todo: do we need these wrappers on windows
- forM_ bin_targets $ \(pkg, _) -> do
- needed_wrappers <- pkgToWrappers pkg
- forM_ needed_wrappers $ \wrapper_name -> do
- let suffix = if useGhcPrefix pkg
- then "ghc-" ++ version
- else version
- wrapper_content <- wrapper wrapper_name
- let unversioned_wrapper_path = bindistFilesDir -/- "wrappers" -/- wrapper_name
- versioned_wrapper = wrapper_name ++ "-" ++ suffix
- versioned_wrapper_path = bindistFilesDir -/- "wrappers" -/- versioned_wrapper
- -- Write the wrapper to the versioned path
- writeFile' versioned_wrapper_path wrapper_content
- -- Create a symlink from the non-versioned to the versioned.
- liftIO $ do
- IO.removeFile unversioned_wrapper_path <|> return ()
- IO.createFileLink versioned_wrapper unversioned_wrapper_path
+ -- These wrapper scripts are only necessary in the configure/install
+ -- workflow which is not supported on windows.
+ unless windowsHost $ do
+ forM_ bin_targets $ \(pkg, _) -> do
+ needed_wrappers <- pkgToWrappers pkg
+ forM_ needed_wrappers $ \wrapper_name -> do
+ let suffix = if useGhcPrefix pkg
+ then "ghc-" ++ version
+ else version
+ wrapper_content <- wrapper wrapper_name
+ let unversioned_wrapper_path = bindistFilesDir -/- "wrappers" -/- wrapper_name
+ versioned_wrapper = wrapper_name ++ "-" ++ suffix
+ versioned_wrapper_path = bindistFilesDir -/- "wrappers" -/- versioned_wrapper
+ -- Write the wrapper to the versioned path
+ writeFile' versioned_wrapper_path wrapper_content
+ -- Create a symlink from the non-versioned to the versioned.
+ liftIO $ do
+ IO.removeFile unversioned_wrapper_path <|> return ()
+ IO.createFileLink versioned_wrapper unversioned_wrapper_path
let buildBinDist compressor = do
win_target <- isWinTarget
=====================================
libraries/ghc-internal/src/GHC/Internal/Lexeme.hs
=====================================
@@ -44,7 +44,7 @@ okSymChar c
_ -> False
startsVarSym, startsVarId, startsConSym, startsConId :: Char -> Bool
-startsVarSym c = okSymChar c && c /= ':' -- Infix Ids
+startsVarSym c = okSymChar c && c /= ':' -- Infix Ids
startsConSym c = c == ':' -- Infix data constructors
startsVarId c = c == '_' || case generalCategory c of -- Ordinary Ids
LowercaseLetter -> True
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/3465c065ea45c44c866005d0db3d1c…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/3465c065ea45c44c866005d0db3d1c…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/sjakobi/T27296-stable-simpl] Stabilise anonymous float ordering in untidied Core dumps
by Simon Jakobi (@sjakobi2) 09 Jun '26
by Simon Jakobi (@sjakobi2) 09 Jun '26
09 Jun '26
Simon Jakobi pushed to branch wip/sjakobi/T27296-stable-simpl at Glasgow Haskell Compiler / GHC
Commits:
f5eb81e6 by Simon Jakobi at 2026-06-09T16:40:04+02:00
Stabilise anonymous float ordering in untidied Core dumps
Anonymous floats are all built with OccName "lvl" and noSrcSpan
(newLvlVar), so the source-span/name sort key is identical for every
one of them; sortOn then falls back to the unique-driven input order --
the very churn -dstable-core-dump-order is meant to remove. (Tidied
dumps like -ddump-simpl are unaffected, as tidy gives the floats
distinct names lvl, lvl1, ...)
Add a content-based, unique-independent tie-break (rhsKey): the floated
literal, if any, then the RHS size statistics.
Add test T27296b pinning the float ordering in an untidied
-ddump-float-out dump. It is a makefile_test that seds the dump down to
just the bindings (collapsing each pass header to a bare "Float out"
separator and dropping the FOS config / size lines), so the six lvl
floats are asserted to come out ordered by literal value.
Co-Authored-By: Claude Opus 4.7 <noreply(a)anthropic.com>
- - - - -
5 changed files:
- compiler/GHC/Core/Ppr.hs
- testsuite/tests/simplCore/should_compile/Makefile
- + testsuite/tests/simplCore/should_compile/T27296b.hs
- + testsuite/tests/simplCore/should_compile/T27296b.stdout
- testsuite/tests/simplCore/should_compile/all.T
Changes:
=====================================
compiler/GHC/Core/Ppr.hs
=====================================
@@ -28,10 +28,10 @@ module GHC.Core.Ppr (
import GHC.Prelude
import GHC.Core
-import GHC.Core.Stats (exprStats)
+import GHC.Core.Stats (CoreStats(..), exprStats)
import GHC.Types.Fixity (LexicalFixity(..))
-import GHC.Types.Literal( pprLiteral )
-import GHC.Types.Name( pprInfixName, pprPrefixName, getOccString, getSrcSpan )
+import GHC.Types.Literal( Literal, pprLiteral )
+import GHC.Types.Name( getOccString, getSrcSpan, pprInfixName, pprPrefixName )
import GHC.Types.Var
import GHC.Types.Id
import GHC.Types.Id.Info
@@ -46,8 +46,8 @@ import GHC.Types.Basic
import GHC.Utils.Misc
import GHC.Utils.Outputable
import GHC.Utils.Panic (panic)
-import GHC.Types.SrcLoc ( SrcSpan(..), srcSpanStartLine, srcSpanStartCol
- , pprUserRealSpan )
+import GHC.Types.SrcLoc ( SrcSpan(..), pprUserRealSpan, srcSpanStartCol
+ , srcSpanStartLine )
import GHC.Types.Tickish
import Data.List ( sortOn )
@@ -99,7 +99,18 @@ Uniques*, so two dumps line up across rebuilds. The sort key is:
whether the OccName *contains* a '$', which marks a derived binder: a worker
is @$wfoo@, but a call-site specialisation is tidied to @bar_$sfoo@ (no
leading '$'), so a leading-'$' test would miss it.
- 3. the OccName string, as a final lexical, deterministic tie-break.
+ 3. the OccName string, as a lexical, deterministic tie-break.
+ 4. a content-based tie-break on the right-hand side ('rhsKey'): the floated
+ literal, if any, then the RHS size statistics. This matters for the
+ anonymous floats: 'newLvlVar' builds them all with OccName "lvl" and
+ noSrcSpan, so keys 1-3 are identical and without it their order would fall
+ back to the Unique-driven input order -- the churn we set out to remove.
+ (Tidied dumps like -ddump-simpl give the floats distinct names lvl,
+ lvl1, ...; this additionally stabilises untidied dumps such as
+ -ddump-simpl-iterations.) It is only a best-effort tie-break -- RHSs
+ agreeing on both components keep their input order -- and Unique-independent
+ for the numeric CAFs we target (a rubbish literal is the exception: its
+ 'cmpLit' falls back to the Unique-dependent 'nonDetCmpType').
Recursive groups are never split: a 'Rec' is one 'CoreBind', placed as a unit by
its earliest-source member, with its members sorted by the same key.
@@ -114,24 +125,36 @@ suffer the cross-module churn this flag addresses.
useful for debugging the compiler itself.
-}
+-- | The sort key for one top-level binder. The trailing 'RhsKey' is a
+-- content-based tiebreak, used only when two binders agree on everything
+-- before it. See Note [Stable Core dump order].
+type DumpSortKey =
+ ( Int -- source-span bucket: 0 = real span, 1 = noSrcSpan (sorts last)
+ , Int -- source-span start line
+ , Int -- source-span start column
+ , Int -- dollar-rank: 0 = derived ($w/$s) binder, 1 = its origin
+ , String -- the OccName string, a lexical tiebreak
+ , RhsKey -- content-based tiebreak (see 'rhsKey')
+ )
+
-- | Reorder a 'CoreProgram' into a stable, source-location-driven order for
-- dumping. See Note [Stable Core dump order]. Used by 'dumpPassResult' when
-- -dstable-core-dump-order is enabled.
sortCoreBindingsForDump :: CoreProgram -> CoreProgram
sortCoreBindingsForDump = sortOn bindKey . map sortRecMembers
where
- sortRecMembers (Rec prs) = Rec (sortOn (bndrKey . fst) prs)
+ sortRecMembers (Rec prs) = Rec (sortOn (uncurry elemKey) prs)
sortRecMembers b = b
- -- 'sortRecMembers' runs first, so a 'Rec' is already sorted by 'bndrKey'
+ -- 'sortRecMembers' runs first, so a 'Rec' is already sorted by 'elemKey'
-- when 'bindKey' sees it; its first member is therefore the minimum key.
- bindKey :: CoreBind -> (Int, Int, Int, Int, String)
- bindKey (NonRec b _) = bndrKey b
- bindKey (Rec ((b,_):_)) = bndrKey b
- bindKey (Rec []) = panic "sortCoreBindingsForDump: empty Rec"
+ bindKey :: CoreBind -> DumpSortKey
+ bindKey (NonRec b rhs) = elemKey b rhs
+ bindKey (Rec ((b,rhs):_)) = elemKey b rhs
+ bindKey (Rec []) = panic "sortCoreBindingsForDump: empty Rec"
- bndrKey :: CoreBndr -> (Int, Int, Int, Int, String)
- bndrKey b = (bucket, line, col, dollar_rank, s)
+ elemKey :: CoreBndr -> CoreExpr -> DumpSortKey
+ elemKey b rhs = (bucket, line, col, dollar_rank, s, rhsKey rhs)
where
s = getOccString b
(bucket, line, col) = case getSrcSpan b of
@@ -145,6 +168,23 @@ sortCoreBindingsForDump = sortOn bindKey . map sortRecMembers
dollar_rank | '$' `elem` s = 0
| otherwise = 1
+-- | A content-based tie-break on a binder's right-hand side: see point 4 of
+-- Note [Stable Core dump order].
+type RhsKey =
+ ( Maybe Literal -- the floated literal, if any (Nothing sorts first)
+ , (Int, Int, Int, Int, Int) -- exprStats counts: terms, types, coercions, value binds, join binds
+ )
+
+rhsKey :: CoreExpr -> RhsKey
+rhsKey rhs = (litOf rhs, statsTuple (exprStats rhs))
+ where
+ statsTuple (CS tm ty co vb jb) = (tm, ty, co, vb, jb)
+ litOf (Lit l) = Just l
+ litOf (App f a) = case a of { Lit l -> Just l; _ -> litOf f }
+ litOf (Cast e _) = litOf e
+ litOf (Tick _ e) = litOf e
+ litOf _ = Nothing
+
instance OutputableBndr b => Outputable (Bind b) where
ppr bind = ppr_bind noAnn bind
=====================================
testsuite/tests/simplCore/should_compile/Makefile
=====================================
@@ -316,3 +316,13 @@ T27296:
-dstable-core-dump-order T27296.hs 2> /dev/null \
| sed -nE 's/^(\$$fEqKey|\$$fOrdKey|\$$fOrdKey_\$$ccompare|size|findI_\$$slookupG|lookupG|member|findI|\$$wrotate|rotate|insertG|insertManyI|insertTwoI|weight|balance|ratios|fromAscI)( .*)?$$/\1/p' \
| uniq
+
+# See T27296b.hs for what this pins and why. The six floated "lvl" constants
+# are scrambled in source order; grep them out and the dump coming out
+# 1000..6000 confirms the stable, value-ordered float ordering.
+T27296b:
+ $(RM) -f T27296b.o T27296b.hi
+ '$(TEST_HC)' $(TEST_HC_OPTS) -O -c -ddump-float-out -dsuppress-uniques \
+ -dsuppress-idinfo -dsuppress-module-prefixes -dno-typeable-binds \
+ -dstable-core-dump-order T27296b.hs 2> /dev/null \
+ | grep '^lvl = I#'
=====================================
testsuite/tests/simplCore/should_compile/T27296b.hs
=====================================
@@ -0,0 +1,21 @@
+-- See Note [Stable Core dump order] in GHC.Core.Ppr.
+--
+-- Companion to T27296 that pins the ordering of *anonymous* top-level floats.
+-- Under -O the boxed Int constants in sel's branches are floated to top level
+-- as separate CAFs, all of which the compiler names "lvl" with noSrcSpan (see
+-- newLvlVar). Before -dstable-core-dump-order their dump order was the
+-- unique-driven processing order; the flag's content-based tie-break (rhsKey)
+-- now orders them by literal value -- here 1000..6000, despite the scrambled
+-- source order. This dump is intentionally *untidied* (-ddump-float-out), the
+-- only place the "lvl" collision is observable; tidied dumps like -ddump-simpl
+-- already give the floats distinct names (lvl, lvl1, ...).
+module T27296b (sel) where
+
+{-# NOINLINE sel #-}
+sel :: Int -> Int
+sel 0 = 5000
+sel 1 = 1000
+sel 2 = 4000
+sel 3 = 2000
+sel 4 = 3000
+sel _ = 6000
=====================================
testsuite/tests/simplCore/should_compile/T27296b.stdout
=====================================
@@ -0,0 +1,6 @@
+lvl = I# 1000#
+lvl = I# 2000#
+lvl = I# 3000#
+lvl = I# 4000#
+lvl = I# 5000#
+lvl = I# 6000#
=====================================
testsuite/tests/simplCore/should_compile/all.T
=====================================
@@ -603,3 +603,4 @@ test('T25718c', normal, compile, ['-O -ddump-simpl -dsuppress-uniques -dsuppress
test('T19166', normal, compile, ['-O -ddump-simpl -dsuppress-uniques -dsuppress-all -dno-typeable-binds'])
test('T27261', [extra_files(['T27261_aux.hs'])], multimod_compile, ['T27261', '-v0 -O'])
test('T27296', [], makefile_test, ['T27296'])
+test('T27296b', [], makefile_test, ['T27296b'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f5eb81e6745870feb1a1a2cdc3adec7…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f5eb81e6745870feb1a1a2cdc3adec7…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/sjakobi/multi-caret] 13 commits: compiler: use nubOrd from containers
by Simon Jakobi (@sjakobi2) 09 Jun '26
by Simon Jakobi (@sjakobi2) 09 Jun '26
09 Jun '26
Simon Jakobi pushed to branch wip/sjakobi/multi-caret at Glasgow Haskell Compiler / GHC
Commits:
576987d0 by Simon Jakobi at 2026-06-02T04:53:36-04:00
compiler: use nubOrd from containers
Address #27103 by replacing GHC.Utils.Misc.ordNub[On] with
Data.Containers.ListUtils.nubOrd[On].
Note that nubOrd suffers from a small inefficiency, a fix for which
will be included in the next containers release:
https://github.com/haskell/containers/issues/1202
- - - - -
deea53c3 by David Eichmann at 2026-06-02T04:54:22-04:00
Hadrian: disable response files for GHC/Haddock builders on non-Windows
This makes debugging build errors easier on non-windows hosts.
See issue #27230
- - - - -
f2f5c6ba by Nikita Efremov at 2026-06-02T16:04:54+00:00
fix typo : compete with performance, not complete
- - - - -
5524ea0e by Wolfgang Jeltsch at 2026-06-03T08:01:26-04:00
Make the current `base` buildable with GHC 9.14
This comprises the following changes:
* Disable some imports into `GHC.Base` for GHC 9.14
* Disable some imports into `Prelude` for GHC 9.14
* Disable separate `ArrowLoop` import for GHC 9.14
* Disable `GHC.Internal.STM` import for GHC 9.14
* Disable `GHC.Internal.Unicode.Version` import for GHC 9.14
* Disable `GHC.Internal.TH.Monad` import for GHC 9.14
* Add alternative `fixIO` import for GHC 9.14
* Add alternative `unsafeCodeCoerce` import for GHC 9.14
* Disable hiding of imported SIMD operations for GHC 9.14
* Disable use of GHC 9.14’s `printToHandleFinalizerExceptionHandler`
* Enable use of `getFileHash` from `ghc-internal` for GHC 9.14
* Make `thenA` available for GHC 9.14
* Make `thenM` available for GHC 9.14
* Disable translation of `IoManagerFlagPoll` for GHC 9.14
* Add `hGetNewlineMode` for GHC 9.14
- - - - -
d3438055 by Enrico Maria De Angelis at 2026-06-03T08:02:17-04:00
Fix #27067 - Clarify haddocks on `minusNaturalMaybe`
- - - - -
f9bcfac2 by sheaf at 2026-06-03T14:47:19-04:00
Avoid mkTick in Core Prep breaking ANF
As discovered in #27182, mkTick can break ANF. This patch introduces a
variant of mkTick that skips the single optimisation that could break
ANF. This is preferrable over switching to the raw Tick constructor,
as the latter may introduce spurious cost centres in profiling reports.
This is a temporary measure until we more thoroughly refactor how
mkTick works (see #27141).
See Note [mkTick breaks ANF] in GHC.CoreToStg.Prep.
Fixes #27182
- - - - -
cf1fd661 by Artem Pelenitsyn at 2026-06-03T14:48:09-04:00
clarify comment for getSizeofMutableByteArray#: we get the size in bytes, not "elements"
- - - - -
a3b431f3 by David Eichmann at 2026-06-04T10:10:19+00:00
Hadrian: convert env variable ACLOCAL_PATH to unix paths.
Convert ACLOCAL_PATH to a unix style path when invoking autoreconf.
Autoreconf doesn't handle windows paths.
See Note [Autoreconf unix paths from ACLOCAL_PATH].
Fixes #27311
- - - - -
18f6138a by Simon Jakobi at 2026-06-04T20:20:31-04:00
testsuite: Deduplicate --only test names
config.only is assumed to be a set, but supplying --only overwrote it
with the (list) argparse result, which can contain duplicates. When a
test ran, config.only.remove(name) dropped only the first occurrence,
so a duplicated name lingered and was later misreported as a
"test not found" framework failure. Store it as a set instead.
Fixes #27322
Co-Authored-By: Claude Opus 4.7 <noreply(a)anthropic.com>
- - - - -
2f3cc9ff by Simon Jakobi at 2026-06-08T07:55:49-04:00
testsuite: detect fast bignum via ghc-internal, not removed ghc-bignum
The ghc-bignum package was merged into ghc-internal, so the BIGNUM_GMP
probe in test.mk ran `ghc-pkg field ghc-bignum exposed-modules`, which
fails with "cannot find package ghc-bignum". That error went to stderr
and leaked into the captured stderr of every makefile_test, causing
spurious [bad stderr] failures across the suite. The probe also silently
returned empty, so config.have_fast_bignum was wrongly False even on GMP
builds.
Probe ghc-internal's extra-libraries for the gmp library instead: the
GMP backend module is an other-module (not exposed), but GMP_LIBS adds
gmp to extra-libraries only on a GMP build, so this distinguishes the
backends. Redirect stderr to keep any future missing-package error off
the harness's stderr.
This also removes a stale comment as per suggestion from hsyl20.
Co-Authored-By: Claude Opus 4.7 <noreply(a)anthropic.com>
- - - - -
eb3bf6e7 by Alan Zimmerman at 2026-06-08T07:56:32-04:00
EPA: Rename Transform.anchorEof to addModuleCommentOrigDeltas
This now matches what it actually does.
- - - - -
f578388e by Simon Jakobi at 2026-06-09T13:22:39+02:00
Add support for related locations in Diagnostic
Diagnostics can now carry "related locations" -- additional source spans
beyond the primary error span. See the new Note [The source span model for
diagnostics] for the model and its correspondence to the LSP Diagnostic /
relatedInformation interfaces. This prepares the ground for #22637; the
diagnosticRelatedLocations name is inspired by #23414.
Implementation:
* Related spans are carried through the MCDiagnostic message class, the
single source of truth; decorateDiagnostic reads them from there.
* getCaretDiagnostics takes the show-caret flag and returns the real spans
that got no caret (sorted and deduped), splitting each span into
'Either SDoc RealSrcSpan' via partitionEithers; decorateDiagnostic lists
those under the "At:" heading. Spans are deduped by real span, ignoring
the BufSpan, so a duplicate export's related span (which often differs
from the primary only in its BufSpan) is not listed twice. The single-span
getCaretDiagnostic lost its only caller and is removed along with its
exports and .hs-boot declaration.
JSON output:
* Emit related spans as a new "relatedSpans" field and bump the JSON schema
version 1.2 -> 1.3 (additive, backwards compatible).
Diagnostics converted:
* TcRnBindingNameConflict, TcRnDuplicateDecls, TcRnDuplicateExport and
TcRnDuplicateNamedDefaultExport carry related locations. The now-redundant
location prose is removed from TcRnBindingNameConflict ("Bound at:") and
TcRnDuplicateDecls ("Declared at:"), and TcRnDuplicateNamedDefaultExport
is reworded (its two export items are always identical). check_occs threads
LIE GhcPs so export items carry their source spans. TcRnConflictingExports
and TcRnDuplicateFieldExport were tried but reverted: their export items
and name provenance already appear in the message text, so carets only
duplicated the primary span.
Tests:
* Enable -fdiagnostics-show-caret in several tests to show multi-caret
output, and add MultiCaretFallback exercising a diagnostic whose related
locations are split between carets and the "At:" fallback.
Co-Authored-By: Claude Opus 4.8 <noreply(a)anthropic.com>
- - - - -
b4d9842a by Simon Jakobi at 2026-06-09T16:04:39+02:00
WIP: Diagnostics: preserve author's order of related locations
The caret/At: renderer used to sort and dedup the diagnostic's source spans
by position, overriding whatever order the message author chose. Make order
the author's responsibility instead:
* decorateDiagnostic no longer nubs; sourceSpans is just primary :| related.
* getCaretDiagnostics deduplicates while preserving the given order, rather
than sorting first. This is now the only dedup.
The construction-site leftmost_smallest sort is thus the sole authority on
order. Note [The source span model for diagnostics] records that authors must
order related spans deterministically themselves.
To keep "Multiple declarations" output in source order under the new rule,
addDupDeclErr now reports at the first declaration (NE.head) rather than the
last, matching TcRnBindingNameConflict; the related spans become NE.tail.
Expected outputs updated accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply(a)anthropic.com>
- - - - -
130 changed files:
- boot
- + changelog.d/T27182.md
- compiler/GHC/Builtin/primops.txt.pp
- compiler/GHC/CmmToAsm/BlockLayout.hs
- compiler/GHC/Core/Opt/Monad.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Errors.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Driver/Session/Units.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Linker/Unit.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Export.hs
- compiler/GHC/Types/Error.hs
- compiler/GHC/Types/Error.hs-boot
- compiler/GHC/Unit/Info.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Utils/Error.hs
- compiler/GHC/Utils/Logger.hs
- compiler/GHC/Utils/Misc.hs
- + docs/users_guide/diagnostics-as-json-schema-1_3.json
- docs/users_guide/javascript.rst
- docs/users_guide/using.rst
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Exception.hs
- hadrian/src/Builder.hs
- hadrian/src/Hadrian/Oracles/Path.hs
- hadrian/src/Hadrian/Utilities.hs
- hadrian/src/Rules/BinaryDist.hs
- libraries/base/src/Control/Applicative.hs
- libraries/base/src/Control/Arrow.hs
- libraries/base/src/Control/Monad.hs
- libraries/base/src/Data/Array/Byte.hs
- libraries/base/src/Data/Fixed.hs
- libraries/base/src/GHC/Base.hs
- libraries/base/src/GHC/Conc.hs
- libraries/base/src/GHC/Conc/Sync.hs
- libraries/base/src/GHC/Exts.hs
- libraries/base/src/GHC/Fingerprint.hs
- libraries/base/src/GHC/IO/Handle.hs
- libraries/base/src/GHC/RTS/Flags.hs
- libraries/base/src/GHC/Unicode.hs
- libraries/base/src/GHC/Weak.hs
- libraries/base/src/GHC/Weak/Finalize.hs
- libraries/base/src/Prelude.hs
- libraries/base/src/System/IO.hs
- libraries/base/src/System/Mem/Weak.hs
- libraries/ghc-internal/src/GHC/Internal/Natural.hs
- testsuite/driver/runtests.py
- testsuite/mk/test.mk
- testsuite/tests/default/T25857.stderr
- testsuite/tests/default/all.T
- testsuite/tests/driver/T16167.stdout
- testsuite/tests/driver/json.stderr
- testsuite/tests/driver/json2.stderr
- testsuite/tests/driver/json_dump.stderr
- testsuite/tests/driver/json_warn.stderr
- testsuite/tests/ghci/scripts/T4127a.stderr
- testsuite/tests/ghci/scripts/ghci048.stderr
- testsuite/tests/haddock/haddock_examples/haddock.Test.stderr
- testsuite/tests/indexed-types/should_fail/SimpleFail6.stderr
- testsuite/tests/indexed-types/should_fail/T16110_Fail1.stderr
- testsuite/tests/mdo/should_fail/mdofail002.stderr
- testsuite/tests/mdo/should_fail/mdofail003.stderr
- testsuite/tests/module/mod128.stderr
- testsuite/tests/module/mod18.stderr
- testsuite/tests/module/mod19.stderr
- testsuite/tests/module/mod20.stderr
- testsuite/tests/module/mod21.stderr
- testsuite/tests/module/mod22.stderr
- testsuite/tests/module/mod23.stderr
- testsuite/tests/module/mod24.stderr
- testsuite/tests/module/mod38.stderr
- testsuite/tests/module/mod66.stderr
- testsuite/tests/overloadedrecflds/should_fail/DuplicateExports.stderr
- testsuite/tests/overloadedrecflds/should_fail/FieldSelectors.stderr
- testsuite/tests/overloadedrecflds/should_fail/NFSDuplicate.stderr
- testsuite/tests/overloadedrecflds/should_fail/T17965.stderr
- testsuite/tests/overloadedrecflds/should_fail/all.T
- testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail03.stderr
- testsuite/tests/patsyn/should_compile/T11959.stderr
- testsuite/tests/patsyn/should_compile/T9975a.stderr
- testsuite/tests/patsyn/should_fail/T14114.stderr
- testsuite/tests/patsyn/should_fail/all.T
- + testsuite/tests/profiling/should_compile/T27182.hs
- testsuite/tests/profiling/should_compile/all.T
- testsuite/tests/quasiquotation/qq006/qq006.stderr
- testsuite/tests/rename/should_compile/ExportWarnings6.stderr
- testsuite/tests/rename/should_compile/T23318.stderr
- + testsuite/tests/rename/should_fail/MultiCaretFallback.hs
- + testsuite/tests/rename/should_fail/MultiCaretFallback.stderr
- testsuite/tests/rename/should_fail/T22478b.stderr
- testsuite/tests/rename/should_fail/T7164.stderr
- testsuite/tests/rename/should_fail/all.T
- testsuite/tests/rename/should_fail/rn_dup.stderr
- testsuite/tests/rename/should_fail/rnfail001.stderr
- testsuite/tests/rename/should_fail/rnfail002.stderr
- testsuite/tests/rename/should_fail/rnfail003.stderr
- testsuite/tests/rename/should_fail/rnfail004.stderr
- testsuite/tests/rename/should_fail/rnfail009.stderr
- testsuite/tests/rename/should_fail/rnfail010.stderr
- testsuite/tests/rename/should_fail/rnfail011.stderr
- testsuite/tests/rename/should_fail/rnfail012.stderr
- testsuite/tests/rename/should_fail/rnfail013.stderr
- testsuite/tests/rename/should_fail/rnfail015.stderr
- testsuite/tests/rename/should_fail/rnfail043.stderr
- testsuite/tests/th/T8932.stderr
- testsuite/tests/th/TH_dupdecl.stderr
- testsuite/tests/th/TH_spliceD1.stderr
- testsuite/tests/type-data/should_fail/TDMultiple01.stderr
- testsuite/tests/type-data/should_fail/TDMultiple02.stderr
- testsuite/tests/type-data/should_fail/TDPunning.stderr
- testsuite/tests/typecheck/should_fail/T22560_fail_c.stderr
- testsuite/tests/typecheck/should_fail/TyAppPat_NonlinearMultiAppPat.stderr
- testsuite/tests/typecheck/should_fail/TyAppPat_NonlinearMultiPat.stderr
- testsuite/tests/typecheck/should_fail/TyAppPat_NonlinearSinglePat.stderr
- testsuite/tests/typecheck/should_fail/all.T
- testsuite/tests/typecheck/should_fail/tcfail038.stderr
- testsuite/tests/vdq-rta/should_fail/T22326_fail_nonlinear.stderr
- testsuite/tests/warnings/should_compile/T25901_exp_dup_wc_3.stderr
- testsuite/tests/warnings/should_compile/T25901_exp_dup_wc_4.stderr
- utils/check-exact/Main.hs
- utils/check-exact/Transform.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2f8d359fd00f968eeac09038910882…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2f8d359fd00f968eeac09038910882…
You're receiving this email because of your account on gitlab.haskell.org.
1
0