[Git][ghc/ghc][master] x86 NCG SIMD: Lower packFloatX4#, insertFloatX4# and broadcastFloatX4# to SSE1 instructions
Marge Bot (@marge-bot)
gitlab at gitlab.haskell.org
Sat Nov 16 21:23:27 UTC 2024
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
a0e168ec by ARATA Mizuki at 2024-11-16T16:22:40-05:00
x86 NCG SIMD: Lower packFloatX4#, insertFloatX4# and broadcastFloatX4# to SSE1 instructions
Fixes #25441
Co-authored-by: sheaf <sam.derbyshire at gmail.com>
- - - - -
12 changed files:
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/CmmToAsm/X86/Ppr.hs
- testsuite/tests/simd/should_run/all.T
- + testsuite/tests/simd/should_run/simd_insert.hs
- + testsuite/tests/simd/should_run/simd_insert.stdout
- + testsuite/tests/simd/should_run/simd_insert_array.hs
- + testsuite/tests/simd/should_run/simd_insert_array.stdout
- + testsuite/tests/simd/should_run/simd_insert_array_baseline.hs
- + testsuite/tests/simd/should_run/simd_insert_array_baseline.stdout
- + testsuite/tests/simd/should_run/simd_insert_baseline.hs
- + testsuite/tests/simd/should_run/simd_insert_baseline.stdout
Changes:
=====================================
compiler/GHC/CmmToAsm/X86/CodeGen.hs
=====================================
@@ -971,7 +971,6 @@ getRegister' _ _ (CmmMachOp mop []) =
pprPanic "getRegister(x86): nullary MachOp" (text $ show mop)
getRegister' platform is32Bit (CmmMachOp mop [x]) = do -- unary MachOps
- sse4_1 <- sse4_1Enabled
avx <- avxEnabled
case mop of
MO_F_Neg w -> sse2NegCode w x
@@ -1068,10 +1067,7 @@ getRegister' platform is32Bit (CmmMachOp mop [x]) = do -- unary MachOps
| avx
-> vector_float_broadcast_avx l w x
| otherwise
- -> case w of
- W32 | not sse4_1
- -> sorry "32-bit float broadcast requires -msse4 or -fllvm."
- _ -> vector_float_broadcast_sse l w x
+ -> vector_float_broadcast_sse l w x
MO_V_Broadcast l w
-> vector_int_broadcast l w x
@@ -1217,6 +1213,7 @@ getRegister' platform is32Bit (CmmMachOp mop [x]) = do -- unary MachOps
-----------------------
+ -- TODO: we could use VBROADCASTSS/SD when AVX2 is available.
vector_float_broadcast_avx :: Length
-> Width
-> CmmExpr
@@ -1224,11 +1221,8 @@ getRegister' platform is32Bit (CmmMachOp mop [x]) = do -- unary MachOps
vector_float_broadcast_avx len w expr = do
(dst, exp) <- getSomeReg expr
let fmt = VecFormat len (floatScalarFormat w)
- code = case w of
- W64 -> unitOL $ VSHUF fmt (ImmInt 0) (OpReg dst) dst dst
- _ -> toOL [ INSERTPS fmt (ImmInt 0b00_10_0000) (OpReg dst) dst
- , VSHUF fmt (ImmInt 0) (OpReg dst) dst dst ]
- return $ Fixed fmt dst (exp `appOL` code)
+ code = VSHUF fmt (ImmInt 0) (OpReg dst) dst dst
+ return $ Fixed fmt dst (exp `snocOL` code)
vector_float_broadcast_sse :: Length
-> Width
@@ -1237,11 +1231,8 @@ getRegister' platform is32Bit (CmmMachOp mop [x]) = do -- unary MachOps
vector_float_broadcast_sse len w expr = do
(dst, exp) <- getSomeReg expr
let fmt = VecFormat len (floatScalarFormat w)
- code = case w of
- W64 -> unitOL $ SHUF fmt (ImmInt 0) (OpReg dst) dst
- _ -> toOL [ INSERTPS fmt (ImmInt 0b00_10_0000) (OpReg dst) dst
- , SHUF fmt (ImmInt 0) (OpReg dst) dst ]
- return $ Fixed fmt dst (exp `appOL` code)
+ code = SHUF fmt (ImmInt 0) (OpReg dst) dst
+ return $ Fixed fmt dst (exp `snocOL` code)
vector_int_broadcast :: Length
-> Width
@@ -1801,9 +1792,12 @@ getRegister' platform _is32Bit (CmmMachOp mop [x, y, z]) = do -- ternary MachOps
-> genFMA3Code l w var x y z
-- Ternary vector operations
- MO_VF_Insert l W32 | sse4_1 -> vector_float_insert_sse l x y z
- | otherwise
- -> sorry "FloatX4# operations require either -msse4 or -fllvm"
+ MO_VF_Insert l W32 | l == 4 -> vector_floatx4_insert_sse sse4_1 x y z
+ | otherwise ->
+ sorry $ "FloatX" ++ show l ++ "# insert operations require -fllvm"
+ -- SIMD NCG TODO:
+ --
+ -- - add support for FloatX8, FloatX16.
MO_VF_Insert l W64 -> vector_double_insert avx l x y z
MO_V_Insert l W64 -> vector_int_insert_sse l W64 x y z
@@ -1814,31 +1808,59 @@ getRegister' platform _is32Bit (CmmMachOp mop [x, y, z]) = do -- ternary MachOps
-- SIMD NCG TODO:
--
-- - add support for FloatX8, FloatX16.
- vector_float_insert_sse :: Length
- -> CmmExpr
- -> CmmExpr
- -> CmmExpr
- -> NatM Register
- -- FloatX4
- vector_float_insert_sse len at 4 vecExpr valExpr (CmmLit (CmmInt offset _))
- = do
- (r, exp) <- getNonClobberedReg valExpr
- fn <- getAnyReg vecExpr
- let fmt = VecFormat len FmtFloat
- imm = litToImm (CmmInt (offset `shiftL` 4) W32)
- code dst = exp `appOL`
- (fn dst) `snocOL`
- (INSERTPS fmt imm (OpReg r) dst)
- in return $ Any fmt code
- vector_float_insert_sse len _ _ offset
+ vector_floatx4_insert_sse :: Bool
+ -> CmmExpr
+ -> CmmExpr
+ -> CmmExpr
+ -> NatM Register
+ vector_floatx4_insert_sse sse4_1 vecExpr valExpr (CmmLit (CmmInt offset _))
+ | sse4_1 = do
+ (r, exp) <- getNonClobberedReg valExpr
+ fn <- getAnyReg vecExpr
+ let fmt = VecFormat 4 FmtFloat
+ imm = litToImm (CmmInt (offset `shiftL` 4) W32)
+ code dst = exp `appOL`
+ (fn dst) `snocOL`
+ (INSERTPS fmt imm (OpReg r) dst)
+ in return $ Any fmt code
+ | otherwise = do -- SSE <= 3
+ (r, exp) <- getNonClobberedReg valExpr
+ fn <- getAnyReg vecExpr
+ let fmt = VecFormat 4 FmtFloat
+ tmp <- getNewRegNat fmt
+ let code dst
+ = case offset of
+ 0 -> exp `appOL`
+ (fn dst) `snocOL`
+ -- The following MOV compiles to MOVSS instruction and merges two vectors
+ (MOV fmt (OpReg r) (OpReg dst)) -- dst <- (r[0],dst[1],dst[2],dst[3])
+ 1 -> exp `appOL`
+ (fn dst) `snocOL`
+ (MOVU fmt (OpReg dst) (OpReg tmp)) `snocOL` -- tmp <- dst
+ (UNPCKL fmt (OpReg r) dst) `snocOL` -- dst <- (dst[0],r[0],dst[1],r[1])
+ (SHUF fmt (ImmInt 0xe4) (OpReg tmp) dst) -- dst <- (dst[0],dst[1],tmp[2],tmp[3])
+ 2 -> exp `appOL`
+ (fn dst) `snocOL`
+ (MOVU fmt (OpReg dst) (OpReg tmp)) `snocOL` -- tmp <- dst
+ (MOV fmt (OpReg r) (OpReg tmp)) `snocOL` -- tmp <- (r[0],tmp[1],tmp[2],tmp[3]) with MOVSS
+ (SHUF fmt (ImmInt 0xc4) (OpReg tmp) dst) -- dst <- (dst[0],dst[1],tmp[0],tmp[3])
+ 3 -> exp `appOL`
+ (fn dst) `snocOL`
+ (MOVU fmt (OpReg dst) (OpReg tmp)) `snocOL` -- tmp <- dst
+ (MOV fmt (OpReg r) (OpReg tmp)) `snocOL` -- tmp <- (r[0],tmp[1],tmp[2],tmp[3]) with MOVSS
+ (SHUF fmt (ImmInt 0x24) (OpReg tmp) dst) -- dst <- (dst[0],dst[1],tmp[2],tmp[0])
+ _ -> panic "MO_VF_Insert FloatX4: unsupported offset"
+ in return $ Any fmt code
+ vector_floatx4_insert_sse _ _ _ offset
= pprPanic "Unsupported vector insert operation" $
vcat
- [ text "FloatX" <> ppr len <> text "#"
+ [ text "FloatX4#"
, text "offset:" <+> pdoc platform offset ]
+
-- SIMD NCG TODO:
--
- -- - add support for FloatX8, FloatX16.
+ -- - add support for DoubleX4#, DoubleX8#.
vector_double_insert :: Bool
-> Length
-> CmmExpr
@@ -1857,6 +1879,7 @@ getRegister' platform _is32Bit (CmmMachOp mop [x, y, z]) = do -- ternary MachOps
CmmInt 0 _ -> valExp `appOL`
vecExp `snocOL`
(movu (VecFormat 2 FmtDouble) (OpReg vecReg) (OpReg dst)) `snocOL`
+ -- The following MOV compiles to MOVSD instruction and merges two vectors
(MOV (VecFormat 2 FmtDouble) (OpReg valReg) (OpReg dst))
CmmInt 1 _ -> valExp `appOL`
vecExp `snocOL`
=====================================
compiler/GHC/CmmToAsm/X86/Instr.hs
=====================================
@@ -325,6 +325,7 @@ data Instr
-- | Move two 32-bit floats from the high part of an xmm register
-- to the low part of another xmm register.
| MOVHLPS Format Reg Reg
+ | UNPCKL Format Operand Reg
| PUNPCKLQDQ Format Operand Reg
-- Shift
@@ -524,6 +525,8 @@ regUsageOfInstr platform instr
MOVHLPS fmt src dst
-> mkRU [mk fmt src] [mk fmt dst]
+ UNPCKL fmt src dst
+ -> mkRU (use_R fmt src [mk fmt dst]) [mk fmt dst]
PUNPCKLQDQ fmt src dst
-> mkRU (use_R fmt src [mk fmt dst]) [mk fmt dst]
@@ -765,6 +768,8 @@ patchRegsOfInstr platform instr env
MOVHLPS fmt src dst
-> MOVHLPS fmt (env src) (env dst)
+ UNPCKL fmt src dst
+ -> UNPCKL fmt (patchOp src) (env dst)
PUNPCKLQDQ fmt src dst
-> PUNPCKLQDQ fmt (patchOp src) (env dst)
=====================================
compiler/GHC/CmmToAsm/X86/Ppr.hs
=====================================
@@ -1041,6 +1041,8 @@ pprInstr platform i = case i of
MOVHLPS format from to
-> pprOpReg (text "movhlps") format (OpReg from) to
+ UNPCKL format src dst
+ -> pprFormatOpReg (text "unpckl") format src dst
PUNPCKLQDQ format from to
-> pprOpReg (text "punpcklqdq") format from to
=====================================
testsuite/tests/simd/should_run/all.T
=====================================
@@ -12,13 +12,18 @@ setTestOpts(
, when(unregisterised(), skip)
, when(arch('wasm32'), skip)
, js_skip
+ ])
+
+test('simd_insert_baseline', [], compile_and_run, [''])
+test('simd_insert_array_baseline', [], compile_and_run, [''])
- # Ensure we set the CPU features we have available.
- #
- # This is especially important with the LLVM backend, as LLVM can otherwise
- # produce ABI-incompatible code, e.g. when compiling usage of YMM registers
- # with or without -mavx2.
- , when(have_cpu_feature('sse4_1'), extra_hc_opts('-msse4'))
+# Ensure we set the CPU features we have available.
+#
+# This is especially important with the LLVM backend, as LLVM can otherwise
+# produce ABI-incompatible code, e.g. when compiling usage of YMM registers
+# with or without -mavx2.
+setTestOpts(
+ [ when(have_cpu_feature('sse4_1'), extra_hc_opts('-msse4'))
, when(have_cpu_feature('avx'), extra_hc_opts('-mavx'))
, when(have_cpu_feature('avx2'), extra_hc_opts('-mavx2'))
, when(have_cpu_feature('avx512f'), extra_hc_opts('-mavx512f'))
@@ -55,6 +60,9 @@ test('simd014',
# register on non-x86 architectures.
compile_and_run, ['simd014Cmm.cmm'])
+test('simd_insert', [], compile_and_run, [''])
+test('simd_insert_array', [], compile_and_run, [''])
+
test('T22187', [],compile,[''])
test('T22187_run', [],compile_and_run,[''])
test('T25062_V16', [], compile_and_run, [''])
=====================================
testsuite/tests/simd/should_run/simd_insert.hs
=====================================
@@ -0,0 +1,37 @@
+{-# LANGUAGE MagicHash, UnboxedTuples #-}
+import GHC.Exts
+
+unpackFloatX4 :: FloatX4# -> (Float, Float, Float, Float)
+unpackFloatX4 v = case unpackFloatX4# v of
+ (# a0, a1, a2, a3 #) -> (F# a0, F# a1, F# a2, F# a3)
+
+unpackDoubleX2 :: DoubleX2# -> (Double, Double)
+unpackDoubleX2 v = case unpackDoubleX2# v of
+ (# a0, a1 #) -> (D# a0, D# a1)
+
+testFloatX4 :: IO ()
+testFloatX4 = do
+ let v = packFloatX4# (# 0.1#, 1.0#, 2.0#, 3.0# #)
+ print $ unpackFloatX4 v
+ let w = insertFloatX4# v 7.0# 0#
+ print $ unpackFloatX4 w
+ let x = insertFloatX4# v 7.0# 1#
+ print $ unpackFloatX4 x
+ let y = insertFloatX4# v 7.0# 2#
+ print $ unpackFloatX4 y
+ let z = insertFloatX4# v 7.0# 3#
+ print $ unpackFloatX4 z
+
+testDoubleX2 :: IO ()
+testDoubleX2 = do
+ let v = packDoubleX2# (# 0.1##, 1.0## #)
+ print $ unpackDoubleX2 v
+ let w = insertDoubleX2# v 7.0## 0#
+ print $ unpackDoubleX2 w
+ let x = insertDoubleX2# v 7.0## 1#
+ print $ unpackDoubleX2 x
+
+main :: IO ()
+main = do
+ testFloatX4
+ testDoubleX2
=====================================
testsuite/tests/simd/should_run/simd_insert.stdout
=====================================
@@ -0,0 +1,8 @@
+(0.1,1.0,2.0,3.0)
+(7.0,1.0,2.0,3.0)
+(0.1,7.0,2.0,3.0)
+(0.1,1.0,7.0,3.0)
+(0.1,1.0,2.0,7.0)
+(0.1,1.0)
+(7.0,1.0)
+(0.1,7.0)
=====================================
testsuite/tests/simd/should_run/simd_insert_array.hs
=====================================
@@ -0,0 +1,49 @@
+{-# LANGUAGE MagicHash, UnboxedTuples #-}
+import Control.Monad
+import Data.Array.Base
+import GHC.Exts
+
+unpackFloatX4 :: FloatX4# -> (Float, Float, Float, Float)
+unpackFloatX4 v = case unpackFloatX4# v of
+ (# a0, a1, a2, a3 #) -> (F# a0, F# a1, F# a2, F# a3)
+
+unpackDoubleX2 :: DoubleX2# -> (Double, Double)
+unpackDoubleX2 v = case unpackDoubleX2# v of
+ (# a0, a1 #) -> (D# a0, D# a1)
+
+indexFloatArrayAsFloatX4 :: UArray Int Float -> Int -> FloatX4#
+indexFloatArrayAsFloatX4 (UArray l u n ba) i = case i - l of I# i# -> indexFloatArrayAsFloatX4# ba i#
+
+indexDoubleArrayAsDoubleX2 :: UArray Int Double -> Int -> DoubleX2#
+indexDoubleArrayAsDoubleX2 (UArray l u n ba) i = case i - l of I# i# -> indexDoubleArrayAsDoubleX2# ba i#
+
+someFloatArray :: UArray Int Float
+someFloatArray = listArray (0, 7) [111.0, 222.0, 333.0, 444.0, 555.0, 666.0, 777.0, 888.0]
+
+someDoubleArray :: UArray Int Double
+someDoubleArray = listArray (0, 7) [111.0, 222.0, 333.0, 444.0, 555.0, 666.0, 777.0, 888.0]
+
+testFloatX4 :: IO ()
+testFloatX4 = forM_ [0,4] $ \i -> do
+ let v = indexFloatArrayAsFloatX4 someFloatArray i
+ let w = insertFloatX4# v 123.45# 0#
+ print $ unpackFloatX4 w
+ let x = insertFloatX4# v 123.45# 1#
+ print $ unpackFloatX4 x
+ let y = insertFloatX4# v 123.45# 2#
+ print $ unpackFloatX4 y
+ let z = insertFloatX4# v 123.45# 3#
+ print $ unpackFloatX4 z
+
+testDoubleX2 :: IO ()
+testDoubleX2 = forM_ [0,2,4,6] $ \i -> do
+ let v = indexDoubleArrayAsDoubleX2 someDoubleArray i
+ let w = insertDoubleX2# v 123.45## 0#
+ print $ unpackDoubleX2 w
+ let x = insertDoubleX2# v 123.45## 1#
+ print $ unpackDoubleX2 x
+
+main :: IO ()
+main = do
+ testFloatX4
+ testDoubleX2
=====================================
testsuite/tests/simd/should_run/simd_insert_array.stdout
=====================================
@@ -0,0 +1,16 @@
+(123.45,222.0,333.0,444.0)
+(111.0,123.45,333.0,444.0)
+(111.0,222.0,123.45,444.0)
+(111.0,222.0,333.0,123.45)
+(123.45,666.0,777.0,888.0)
+(555.0,123.45,777.0,888.0)
+(555.0,666.0,123.45,888.0)
+(555.0,666.0,777.0,123.45)
+(123.45,222.0)
+(111.0,123.45)
+(123.45,444.0)
+(333.0,123.45)
+(123.45,666.0)
+(555.0,123.45)
+(123.45,888.0)
+(777.0,123.45)
=====================================
testsuite/tests/simd/should_run/simd_insert_array_baseline.hs
=====================================
@@ -0,0 +1,49 @@
+{-# LANGUAGE MagicHash, UnboxedTuples #-}
+import Control.Monad
+import Data.Array.Base
+import GHC.Exts
+
+unpackFloatX4 :: FloatX4# -> (Float, Float, Float, Float)
+unpackFloatX4 v = case unpackFloatX4# v of
+ (# a0, a1, a2, a3 #) -> (F# a0, F# a1, F# a2, F# a3)
+
+unpackDoubleX2 :: DoubleX2# -> (Double, Double)
+unpackDoubleX2 v = case unpackDoubleX2# v of
+ (# a0, a1 #) -> (D# a0, D# a1)
+
+indexFloatArrayAsFloatX4 :: UArray Int Float -> Int -> FloatX4#
+indexFloatArrayAsFloatX4 (UArray l u n ba) i = case i - l of I# i# -> indexFloatArrayAsFloatX4# ba i#
+
+indexDoubleArrayAsDoubleX2 :: UArray Int Double -> Int -> DoubleX2#
+indexDoubleArrayAsDoubleX2 (UArray l u n ba) i = case i - l of I# i# -> indexDoubleArrayAsDoubleX2# ba i#
+
+someFloatArray :: UArray Int Float
+someFloatArray = listArray (0, 7) [111.0, 222.0, 333.0, 444.0, 555.0, 666.0, 777.0, 888.0]
+
+someDoubleArray :: UArray Int Double
+someDoubleArray = listArray (0, 7) [111.0, 222.0, 333.0, 444.0, 555.0, 666.0, 777.0, 888.0]
+
+testFloatX4 :: IO ()
+testFloatX4 = forM_ [0,4] $ \i -> do
+ let v = indexFloatArrayAsFloatX4 someFloatArray i
+ let w = insertFloatX4# v 123.45# 0#
+ print $ unpackFloatX4 w
+ let x = insertFloatX4# v 123.45# 1#
+ print $ unpackFloatX4 x
+ let y = insertFloatX4# v 123.45# 2#
+ print $ unpackFloatX4 y
+ let z = insertFloatX4# v 123.45# 3#
+ print $ unpackFloatX4 z
+
+testDoubleX2 :: IO ()
+testDoubleX2 = forM_ [0,2,4,6] $ \i -> do
+ let v = indexDoubleArrayAsDoubleX2 someDoubleArray i
+ let w = insertDoubleX2# v 123.45## 0#
+ print $ unpackDoubleX2 w
+ let x = insertDoubleX2# v 123.45## 1#
+ print $ unpackDoubleX2 x
+
+main :: IO ()
+main = do
+ testFloatX4
+ testDoubleX2
=====================================
testsuite/tests/simd/should_run/simd_insert_array_baseline.stdout
=====================================
@@ -0,0 +1,16 @@
+(123.45,222.0,333.0,444.0)
+(111.0,123.45,333.0,444.0)
+(111.0,222.0,123.45,444.0)
+(111.0,222.0,333.0,123.45)
+(123.45,666.0,777.0,888.0)
+(555.0,123.45,777.0,888.0)
+(555.0,666.0,123.45,888.0)
+(555.0,666.0,777.0,123.45)
+(123.45,222.0)
+(111.0,123.45)
+(123.45,444.0)
+(333.0,123.45)
+(123.45,666.0)
+(555.0,123.45)
+(123.45,888.0)
+(777.0,123.45)
=====================================
testsuite/tests/simd/should_run/simd_insert_baseline.hs
=====================================
@@ -0,0 +1,37 @@
+{-# LANGUAGE MagicHash, UnboxedTuples #-}
+import GHC.Exts
+
+unpackFloatX4 :: FloatX4# -> (Float, Float, Float, Float)
+unpackFloatX4 v = case unpackFloatX4# v of
+ (# a0, a1, a2, a3 #) -> (F# a0, F# a1, F# a2, F# a3)
+
+unpackDoubleX2 :: DoubleX2# -> (Double, Double)
+unpackDoubleX2 v = case unpackDoubleX2# v of
+ (# a0, a1 #) -> (D# a0, D# a1)
+
+testFloatX4 :: IO ()
+testFloatX4 = do
+ let v = packFloatX4# (# 0.1#, 1.0#, 2.0#, 3.0# #)
+ print $ unpackFloatX4 v
+ let w = insertFloatX4# v 7.0# 0#
+ print $ unpackFloatX4 w
+ let x = insertFloatX4# v 7.0# 1#
+ print $ unpackFloatX4 x
+ let y = insertFloatX4# v 7.0# 2#
+ print $ unpackFloatX4 y
+ let z = insertFloatX4# v 7.0# 3#
+ print $ unpackFloatX4 z
+
+testDoubleX2 :: IO ()
+testDoubleX2 = do
+ let v = packDoubleX2# (# 0.1##, 1.0## #)
+ print $ unpackDoubleX2 v
+ let w = insertDoubleX2# v 7.0## 0#
+ print $ unpackDoubleX2 w
+ let x = insertDoubleX2# v 7.0## 1#
+ print $ unpackDoubleX2 x
+
+main :: IO ()
+main = do
+ testFloatX4
+ testDoubleX2
=====================================
testsuite/tests/simd/should_run/simd_insert_baseline.stdout
=====================================
@@ -0,0 +1,8 @@
+(0.1,1.0,2.0,3.0)
+(7.0,1.0,2.0,3.0)
+(0.1,7.0,2.0,3.0)
+(0.1,1.0,7.0,3.0)
+(0.1,1.0,2.0,7.0)
+(0.1,1.0)
+(7.0,1.0)
+(0.1,7.0)
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a0e168ec0b6f18ffeddaf8a5dfc68e84563630b8
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a0e168ec0b6f18ffeddaf8a5dfc68e84563630b8
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20241116/780e7ad3/attachment-0001.html>
More information about the ghc-commits
mailing list