Repository navigation
Conversation
…targets musl libc implements memcmp as a portable 1-byte scalar loop, making large ByteString, ShortByteString, Data.Text, and ByteArray# (compareByteArrays# / MO_Memcmp) comparisons ~21x slower than glibc on x86_64 and AArch64 Linux, while small fixed-size MO_Memcmp calls incur an out-of-line C call. Append ghc-9.6.7-simd-memcmp.patch (haskell.nix#2640, upstream ghc!16986) on top of the existing haskell.nix 9.6.7 patch stack for musl targets only, unrolling constant 0..64B MO_Memcmp inline in X86_64 and AArch64 NCGs and routing larger/dynamic comparisons and Stage1+ bytestring to SIMD hs_memcmp while leaving native glibc, macOS, and Windows derivations unchanged.
angerman
force-pushed
the
angerman/musl-ghc967-simd-memcmp
branch
from
November 13, 2026 05:14
70fd027 to
0eb3c85
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
musllibc implementsmemcmp(src/string/memcmp.c) as a portable 1-byte scalar loop:In static
muslLinux builds (hydraJobs.*.musl.*,projectCross.musl64,projectCross.aarch64-multiplatform-musl), largeData.ByteString,ShortByteString,Data.Text, andByteArray#(compareByteArrays#/MO_Memcmp) comparisons run ~21× slower than nativeglibcbuilds that use SIMDmemcmp, while small fixed-sizeMO_Memcmpcomparisons incur an out-of-line C call.Until
haskell.nixis bumped past ican-group/haskell.nix#2640 (upstream GHC MR: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16986; see also ican-group/haskell.nix#2639), this PR appliesghc-9.6.7-simd-memcmp.patchtohaskell-nix.compiler.ghc967onmusltargets innix/pkgs.nix:MO_Memcmpin X86_64 & AArch64 NCGs (0..64bytes): Unrolls constant-sizeMO_Memcmpinline inGHC.CmmToAsm.X86.CodeGen(XORfor0;MOVZxL+SUBfor1; branchless load +BSWAP+CMP+SETCC+SUBfor2, 4, 8; equality scan + shared cold mismatch block with X86CFGedge updates for3, 5..7, 9..64) andGHC.CmmToAsm.AArch64.{CodeGen,Instr,Ppr}(LDR/REV/CMP/CSET/SUB), gated byncgInlineThresholdMemcpy(-fmax-inline-memcpy-insns, default32, preserving the9.6.7DynFlags/NCGConfigABI). All loads stay strictly within[0, n).hs_memcmp+ staticMuslMemcmp.o): Exportshs_memcmpinlibHSrtswith SSE2 + runtime CPUID/XGETBV AVX2 (4×32Bloop +vzeroupper) onx86_64non-glibc/non-Bionic/non-uClibc Linux, ARMv8-A NEON (64Bloop) on little-endianaarch64non-glibc/non-Bionic/non-uClibc Linux, a bounded 64-bit word fallback on other non-glibc Linux architectures, and ann == 0-guarded delegation to platformmemcmpelsewhere. All loads stay strictly within[0, n). Static RTS archives (!DYNAMIC && !SHARED) also provideMuslMemcmp.o(memcmp -> hs_memcmp) before-lcso C FFI callers resolve tohs_memcmpwithout extracting scalarlibc.a(memcmp.lo).MO_Memcmp& Stage 1+bytestring: Lowers dynamic/largeMO_Memcmp(compareByteArrays#) tohs_memcmpacross NCG and LLVM backends (while preserving inline0forn == 0and"memcmp"for constant1..32in LLVM soSimplifyLibCallsunrolls them), and compiles Stage 1+libraries/bytestringwith-DHS_MEMCMP_IN_RTSsoData.ByteStringcallshs_memcmpdirectly while Stage 0 bootstraps cleanly against the unpatched host RTS.musltargets & content-addressed: Applied vialib.optionalAttrs final.stdenv.targetPlatform.isMuslon top of the existinghaskell.nix9.6.7patch stack (12 patches onx86_64-musl, 16 patches onaarch64-musl), wrapped inbuiltins.pathso unrelated repository commits do not invalidate themuslcompiler store path. Stage 0 bootstrap compilers and allnativeLinux, macOS, and Windows (ucrt64) derivations anddevShellsremain bit-for-bit hash-identical.Verification
noGitRev.drvPath):hydraJobs.x86_64-linux.native.cardano-node.passthru.noGitRev.drvPathandpackages.aarch64-darwin.cardano-node.passthru.noGitRev.drvPathare unchanged before and after this commit.ghc-9.6.7for bothpkgsCross.musl64(13 patches total) andpkgsCross.aarch64-multiplatform-musl(17 patches total).MO_Memcmp& end-to-end staticmuslbuild (x86_64+aarch64):-dasm-lintenabled) and AArch64 NCG inlineMO_Memcmplowering and live execution across constant sizes0..65, unaligned offsets, high-bit unsigned bytes, and back-to-back calls in the same Cmm basic block.hydraJobs.x86_64-linux.musl.cardano-node(cardano-node 11.2.1 - linux-x86_64 - ghc-9.6, statically linked) andhydraJobs.x86_64-linux.musl.cardano-cli(cardano-cli 11.7.0.0), confirminghs_memcmp -> hs_memcmp_simdandmemcmp -> hs_memcmpin the resulting static ELF binaries.x86_64-linux, AMD Ryzen 5 3600, single core, stock unpatchedmusl):GHC 9.6.7 -O2,13,466 × 64 KiBcomparisons)Data.ByteStringShortByteString/compareByteArrays#glibc14.061 ms(1.00×)13.779 ms(1.00×)musl298.485 ms(21.23×slower)299.354 ms(21.73×slower)musl(this PR)14.147 ms(21.10×speedup,1.01×glibc)13.834 ms(21.64×speedup,1.00×glibc)Checklist
CHANGELOG.mdfor affected package (N/A: Nix toolchain patch only).cabalfiles are updated (N/A)