[Git][ghc/ghc][master] Introduce UniqueSet and use it to replace 'UniqSet Unique'
Marge Bot (@marge-bot)
gitlab at gitlab.haskell.org
Fri May 31 19:53:37 UTC 2024
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
5f1afdf7 by Sylvain Henry at 2024-05-31T15:52:52-04:00
Introduce UniqueSet and use it to replace 'UniqSet Unique'
'UniqSet Unique' represents a set of uniques as a 'Map Unique Unique',
which is wasting space (associated key/value are always the same).
Fix #23572 and #23605
- - - - -
13 changed files:
- compiler/GHC/Builtin/Types.hs
- compiler/GHC/Cmm/LRegSet.hs
- compiler/GHC/Cmm/Liveness.hs
- compiler/GHC/Cmm/Sink.hs
- compiler/GHC/CmmToAsm/Wasm/Asm.hs
- compiler/GHC/CmmToAsm/Wasm/FromCmm.hs
- compiler/GHC/CmmToAsm/Wasm/Types.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Data/Word64Set/Internal.hs
- compiler/GHC/HsToCore/Pmc/Solver.hs
- compiler/GHC/Types/Unique/Set.hs
Changes:
=====================================
compiler/GHC/Builtin/Types.hs
=====================================
@@ -1040,13 +1040,13 @@ cTupleTyConName a = tyConName (cTupleTyCon a)
cTupleTyConNames :: [Name]
cTupleTyConNames = map cTupleTyConName (0 : [2..mAX_CTUPLE_SIZE])
-cTupleTyConKeys :: UniqSet Unique
-cTupleTyConKeys = mkUniqSet $ map getUnique cTupleTyConNames
+cTupleTyConKeys :: UniqueSet
+cTupleTyConKeys = fromListUniqueSet $ map getUnique cTupleTyConNames
isCTupleTyConName :: Name -> Bool
isCTupleTyConName n
= assertPpr (isExternalName n) (ppr n) $
- getUnique n `elementOfUniqSet` cTupleTyConKeys
+ getUnique n `memberUniqueSet` cTupleTyConKeys
-- | If the given name is that of a constraint tuple, return its arity.
cTupleTyConNameArity_maybe :: Name -> Maybe Arity
=====================================
compiler/GHC/Cmm/LRegSet.hs
=====================================
@@ -3,7 +3,6 @@
module GHC.Cmm.LRegSet (
LRegSet,
- LRegKey,
emptyLRegSet,
nullLRegSet,
@@ -13,42 +12,52 @@ module GHC.Cmm.LRegSet (
deleteFromLRegSet,
sizeLRegSet,
- plusLRegSet,
+ unionLRegSet,
+ unionsLRegSet,
elemsLRegSet
) where
import GHC.Prelude
import GHC.Types.Unique
+import GHC.Types.Unique.Set
import GHC.Cmm.Expr
-import GHC.Word
-
-import GHC.Data.Word64Set as Word64Set
-- Compact sets for membership tests of local variables.
-type LRegSet = Word64Set.Word64Set
-type LRegKey = Word64
+type LRegSet = UniqueSet
+{-# INLINE emptyLRegSet #-}
emptyLRegSet :: LRegSet
-emptyLRegSet = Word64Set.empty
+emptyLRegSet = emptyUniqueSet
+{-# INLINE nullLRegSet #-}
nullLRegSet :: LRegSet -> Bool
-nullLRegSet = Word64Set.null
+nullLRegSet = nullUniqueSet
+{-# INLINE insertLRegSet #-}
insertLRegSet :: LocalReg -> LRegSet -> LRegSet
-insertLRegSet l = Word64Set.insert (getKey (getUnique l))
+insertLRegSet l = insertUniqueSet (getUnique l)
+{-# INLINE elemLRegSet #-}
elemLRegSet :: LocalReg -> LRegSet -> Bool
-elemLRegSet l = Word64Set.member (getKey (getUnique l))
+elemLRegSet l = memberUniqueSet (getUnique l)
+{-# INLINE deleteFromLRegSet #-}
deleteFromLRegSet :: LRegSet -> LocalReg -> LRegSet
-deleteFromLRegSet set reg = Word64Set.delete (getKey . getUnique $ reg) set
+deleteFromLRegSet set reg = deleteUniqueSet (getUnique reg) set
+
+{-# INLINE sizeLRegSet #-}
+sizeLRegSet :: LRegSet -> Int
+sizeLRegSet = sizeUniqueSet
-sizeLRegSet :: Word64Set -> Int
-sizeLRegSet = Word64Set.size
+{-# INLINE unionLRegSet #-}
+unionLRegSet :: LRegSet -> LRegSet -> LRegSet
+unionLRegSet = unionUniqueSet
-plusLRegSet :: Word64Set -> Word64Set -> Word64Set
-plusLRegSet = Word64Set.union
+{-# INLINE unionsLRegSet #-}
+unionsLRegSet :: [LRegSet] -> LRegSet
+unionsLRegSet = unionsUniqueSet
-elemsLRegSet :: Word64Set -> [Word64]
-elemsLRegSet = Word64Set.toList
+{-# INLINE elemsLRegSet #-}
+elemsLRegSet :: LRegSet -> [Unique]
+elemsLRegSet = elemsUniqueSet
=====================================
compiler/GHC/Cmm/Liveness.hs
=====================================
@@ -26,8 +26,6 @@ import GHC.Data.Maybe
import GHC.Utils.Outputable
import GHC.Utils.Panic
-import GHC.Types.Unique
-
-----------------------------------------------------------------------------
-- Calculating what variables are live on entry to a basic block
-----------------------------------------------------------------------------
@@ -112,7 +110,7 @@ liveLatticeL :: DataflowLattice LRegSet
liveLatticeL = DataflowLattice emptyLRegSet add
where
add (OldFact old) (NewFact new) =
- let !join = plusLRegSet old new
+ let !join = unionLRegSet old new
in changedIf (sizeLRegSet join > sizeLRegSet old) join
@@ -132,7 +130,7 @@ noLiveOnEntryL bid in_fact x =
where
-- We convert the int's to uniques so that the printing matches that
-- of registers.
- reg_uniques = map mkUniqueGrimily $ elemsLRegSet in_fact
+ reg_uniques = elemsLRegSet in_fact
=====================================
compiler/GHC/Cmm/Sink.hs
=====================================
@@ -23,7 +23,6 @@ import GHC.Types.Unique.FM
import GHC.Types.Unique.Supply
import GHC.Cmm.Config
-import qualified GHC.Data.Word64Set as Word64Set
import Data.List (partition)
import Data.Maybe
@@ -186,8 +185,8 @@ cmmSink cfg graph = ofBlockList (g_entry graph) <$> sink mapEmpty blocks
live_sets' | should_drop = live_sets
| otherwise = map upd live_sets
- upd set | r `elemLRegSet` set = set `Word64Set.union` live_rhs
- | otherwise = set
+ upd set | r `elemLRegSet` set = set `unionLRegSet` live_rhs
+ | otherwise = set
live_rhs = foldRegsUsed platform (flip insertLRegSet) emptyLRegSet rhs
@@ -208,7 +207,7 @@ cmmSink cfg graph = ofBlockList (g_entry graph) <$> sink mapEmpty blocks
-- Annotate the middle nodes with the registers live *after*
-- the node. This will help us decide whether we can inline
-- an assignment in the current node or not.
- live = Word64Set.unions (map getLive succs)
+ live = unionsLRegSet (map getLive succs)
live_middle = gen_killL platform last live
ann_middles = annotate platform live_middle (blockToList middle)
@@ -216,7 +215,7 @@ cmmSink cfg graph = ofBlockList (g_entry graph) <$> sink mapEmpty blocks
-- one predecessor), so identify the join points and the set
-- of registers live in them.
(joins, nonjoins) = partition (`mapMember` join_pts) succs
- live_in_joins = Word64Set.unions (map getLive joins)
+ live_in_joins = unionsLRegSet (map getLive joins)
-- We do not want to sink an assignment into multiple branches,
-- so identify the set of registers live in multiple successors.
=====================================
compiler/GHC/CmmToAsm/Wasm/Asm.hs
=====================================
@@ -15,7 +15,6 @@ import Data.ByteString.Builder
import qualified Data.ByteString.Char8 as BS8
import Data.Coerce
import Data.Foldable
-import qualified GHC.Data.Word64Set as WS
import Data.Maybe
import Data.Semigroup
import GHC.Cmm
@@ -30,6 +29,7 @@ import GHC.Settings.Config (cProjectVersion)
import GHC.Types.Basic
import GHC.Types.Unique
import GHC.Types.Unique.Map
+import GHC.Types.Unique.Set
import GHC.Utils.Monad.State.Strict
import GHC.Utils.Outputable hiding ((<>))
import GHC.Utils.Panic (panic)
@@ -183,9 +183,9 @@ asmTellSectionHeader :: Builder -> WasmAsmM ()
asmTellSectionHeader k = asmTellTabLine $ ".section " <> k <> ",\"\",@"
asmTellDataSection ::
- WasmTypeTag w -> WS.Word64Set -> SymName -> DataSection -> WasmAsmM ()
+ WasmTypeTag w -> UniqueSet -> SymName -> DataSection -> WasmAsmM ()
asmTellDataSection ty_word def_syms sym DataSection {..} = do
- when (getKey (getUnique sym) `WS.member` def_syms) $ asmTellDefSym sym
+ when (getUnique sym `memberUniqueSet` def_syms) $ asmTellDefSym sym
asmTellSectionHeader sec_name
asmTellAlign dataSectionAlignment
asmTellTabLine asm_size
@@ -422,12 +422,12 @@ asmTellWasmControl ty_word c = case c of
asmTellFunc ::
WasmTypeTag w ->
- WS.Word64Set ->
+ UniqueSet ->
SymName ->
(([SomeWasmType], [SomeWasmType]), FuncBody w) ->
WasmAsmM ()
asmTellFunc ty_word def_syms sym (func_ty, FuncBody {..}) = do
- when (getKey (getUnique sym) `WS.member` def_syms) $ asmTellDefSym sym
+ when (getUnique sym `memberUniqueSet` def_syms) $ asmTellDefSym sym
asmTellSectionHeader $ ".text." <> asm_sym
asmTellLine $ asm_sym <> ":"
asmTellFuncType sym func_ty
=====================================
compiler/GHC/CmmToAsm/Wasm/FromCmm.hs
=====================================
@@ -26,7 +26,6 @@ import Control.Monad
import qualified Data.ByteString as BS
import Data.Foldable
import Data.Functor
-import qualified GHC.Data.Word64Set as WS
import Data.Semigroup
import Data.String
import Data.Traversable
@@ -48,6 +47,7 @@ import GHC.Types.ForeignCall
import GHC.Types.Unique
import GHC.Types.Unique.FM
import GHC.Types.Unique.Map
+import GHC.Types.Unique.Set
import GHC.Types.Unique.Supply
import GHC.Utils.Outputable hiding ((<>))
import GHC.Utils.Panic
@@ -1600,8 +1600,8 @@ onTopSym lbl = case sym_vis of
SymDefault -> wasmModifyM $ \s ->
s
{ defaultSyms =
- WS.insert
- (getKey $ getUnique sym)
+ insertUniqueSet
+ (getUnique sym)
$ defaultSyms s
}
_ -> pure ()
=====================================
compiler/GHC/CmmToAsm/Wasm/Types.hs
=====================================
@@ -52,7 +52,6 @@ import Control.Applicative
import Data.ByteString (ByteString)
import Data.Coerce
import Data.Functor
-import qualified GHC.Data.Word64Set as WS
import Data.Kind
import Data.String
import Data.Type.Equality
@@ -66,6 +65,7 @@ import GHC.Types.Basic
import GHC.Types.Unique
import GHC.Types.Unique.FM
import GHC.Types.Unique.Map
+import GHC.Types.Unique.Set
import GHC.Types.Unique.Supply
import GHC.Utils.Monad.State.Strict
import GHC.Utils.Outputable hiding ((<>))
@@ -197,7 +197,7 @@ data DataSection = DataSection
type SymMap = UniqMap SymName
-- | No need to remember the symbols.
-type SymSet = WS.Word64Set
+type SymSet = UniqueSet
type GlobalInfo = (SymName, SomeWasmType)
@@ -427,7 +427,7 @@ initialWasmCodeGenState platform us =
WasmCodeGenState
{ wasmPlatform =
platform,
- defaultSyms = WS.empty,
+ defaultSyms = emptyUniqueSet,
funcTypes = emptyUniqMap,
funcBodies =
emptyUniqMap,
=====================================
compiler/GHC/CmmToAsm/X86/Instr.hs
=====================================
@@ -1083,7 +1083,7 @@ shortcutStatics fn (align, CmmStaticsRaw lbl statics)
shortcutLabel :: (BlockId -> Maybe JumpDest) -> CLabel -> CLabel
shortcutLabel fn lab
- | Just blkId <- maybeLocalBlockLabel lab = shortBlockId fn emptyUniqSet blkId
+ | Just blkId <- maybeLocalBlockLabel lab = shortBlockId fn emptyUniqueSet blkId
| otherwise = lab
shortcutStatic :: (BlockId -> Maybe JumpDest) -> CmmStatic -> CmmStatic
@@ -1098,15 +1098,15 @@ shortcutStatic _ other_static
shortBlockId
:: (BlockId -> Maybe JumpDest)
- -> UniqSet Unique
+ -> UniqueSet
-> BlockId
-> CLabel
shortBlockId fn seen blockid =
- case (elementOfUniqSet uq seen, fn blockid) of
+ case (memberUniqueSet uq seen, fn blockid) of
(True, _) -> blockLbl blockid
(_, Nothing) -> blockLbl blockid
- (_, Just (DestBlockId blockid')) -> shortBlockId fn (addOneToUniqSet seen uq) blockid'
+ (_, Just (DestBlockId blockid')) -> shortBlockId fn (insertUniqueSet uq seen) blockid'
(_, Just (DestImm (ImmCLbl lbl))) -> lbl
(_, _other) -> panic "shortBlockId"
where uq = getUnique blockid
=====================================
compiler/GHC/Core/DataCon.hs
=====================================
@@ -1248,12 +1248,12 @@ freshNames avoids
, let uniq = mkAlphaTyVarUnique n
occ = mkTyVarOccFS (mkFastString ('x' : show n))
- , not (uniq `elementOfUniqSet` avoid_uniqs)
+ , not (uniq `memberUniqueSet` avoid_uniqs)
, not (occ `elemOccSet` avoid_occs) ]
where
- avoid_uniqs :: UniqSet Unique
- avoid_uniqs = mkUniqSet (map getUnique avoids)
+ avoid_uniqs :: UniqueSet
+ avoid_uniqs = fromListUniqueSet (map getUnique avoids)
avoid_occs :: OccSet
avoid_occs = mkOccSet (map getOccName avoids)
=====================================
compiler/GHC/Core/TyCon.hs
=====================================
@@ -2331,12 +2331,12 @@ isKindName = isKindUniquable
-- | The workhorse for 'isKindTyCon' and 'isKindName'.
isKindUniquable :: Uniquable a => a -> Bool
-isKindUniquable thing = getUnique thing `elementOfUniqSet` kindTyConKeys
+isKindUniquable thing = getUnique thing `memberUniqueSet` kindTyConKeys
-- | These TyCons should be allowed at the kind level, even without
-- -XDataKinds.
-kindTyConKeys :: UniqSet Unique
-kindTyConKeys = unionManyUniqSets
+kindTyConKeys :: UniqueSet
+kindTyConKeys = fromListUniqueSet $
-- Make sure to keep this in sync with the following:
--
-- - The Overview section in docs/users_guide/exts/data_kinds.rst in the GHC
@@ -2344,10 +2344,10 @@ kindTyConKeys = unionManyUniqSets
--
-- - The typecheck/should_compile/T22141f.hs test case, which ensures that all
-- of these can successfully be used without DataKinds.
- ( mkUniqSet [ liftedTypeKindTyConKey, liftedRepTyConKey, constraintKindTyConKey, tYPETyConKey, cONSTRAINTTyConKey ]
- : map (mkUniqSet . tycon_with_datacons) [ runtimeRepTyCon, levityTyCon
- , multiplicityTyCon
- , vecCountTyCon, vecElemTyCon ] )
+ [ liftedTypeKindTyConKey, liftedRepTyConKey, constraintKindTyConKey, tYPETyConKey, cONSTRAINTTyConKey ]
+ ++ concatMap tycon_with_datacons [ runtimeRepTyCon, levityTyCon
+ , multiplicityTyCon
+ , vecCountTyCon, vecElemTyCon ]
where
tycon_with_datacons tc = getUnique tc : map getUnique (tyConDataCons tc)
=====================================
compiler/GHC/Data/Word64Set/Internal.hs
=====================================
@@ -205,7 +205,6 @@ import Text.Read
import qualified GHC.Exts
#endif
-import qualified Data.Foldable as Foldable
import Data.Functor.Identity (Identity(..))
infixl 9 \\{-This comment teaches CPP correct behaviour -}
@@ -519,10 +518,10 @@ alterF f k s = fmap choose (f member_)
Union
--------------------------------------------------------------------}
-- | The union of a list of sets.
-unions :: Foldable f => f Word64Set -> Word64Set
-unions xs
- = Foldable.foldl' union empty xs
+{-# INLINABLE unions #-}
+unions :: [Word64Set] -> Word64Set
+unions = List.foldl' union empty
-- | \(O(n+m)\). The union of two sets.
union :: Word64Set -> Word64Set -> Word64Set
@@ -1183,9 +1182,9 @@ foldlFB = foldl
-- | \(O(n \min(n,W))\). Create a set from a list of integers.
+{-# INLINABLE fromList #-}
fromList :: [Key] -> Word64Set
-fromList xs
- = Foldable.foldl' ins empty xs
+fromList = List.foldl' ins empty
where
ins t x = insert x t
=====================================
compiler/GHC/HsToCore/Pmc/Solver.hs
=====================================
@@ -1466,11 +1466,11 @@ dataConMightBeUnliftedFieldTys =
filter mightBeUnliftedType . map scaledThing . dataConOrigArgTys
isTyConTriviallyInhabited :: TyCon -> Bool
-isTyConTriviallyInhabited tc = elementOfUniqSet (getUnique tc) triviallyInhabitedTyConKeys
+isTyConTriviallyInhabited tc = memberUniqueSet (getUnique tc) triviallyInhabitedTyConKeys
-- | All these types are trivially inhabited
-triviallyInhabitedTyConKeys :: UniqSet Unique
-triviallyInhabitedTyConKeys = mkUniqSet [
+triviallyInhabitedTyConKeys :: UniqueSet
+triviallyInhabitedTyConKeys = fromListUniqueSet [
charTyConKey, doubleTyConKey, floatTyConKey,
intTyConKey, int8TyConKey, int16TyConKey, int32TyConKey, int64TyConKey,
intPrimTyConKey, int8PrimTyConKey, int16PrimTyConKey, int32PrimTyConKey, int64PrimTyConKey,
=====================================
compiler/GHC/Types/Unique/Set.hs
=====================================
@@ -44,6 +44,26 @@ module GHC.Types.Unique.Set (
nonDetEltsUniqSet,
nonDetKeysUniqSet,
nonDetStrictFoldUniqSet,
+
+ -- UniqueSet
+ UniqueSet(..),
+ nullUniqueSet,
+ sizeUniqueSet,
+ memberUniqueSet,
+ emptyUniqueSet,
+ singletonUniqueSet,
+ insertUniqueSet,
+ deleteUniqueSet,
+ differenceUniqueSet,
+ unionUniqueSet,
+ unionsUniqueSet,
+ intersectionUniqueSet,
+ isSubsetOfUniqueSet,
+ filterUniqueSet,
+ foldlUniqueSet,
+ foldrUniqueSet,
+ elemsUniqueSet,
+ fromListUniqueSet,
) where
import GHC.Prelude
@@ -56,6 +76,7 @@ import GHC.Utils.Outputable
import Data.Data
import qualified Data.Semigroup as Semi
import Control.DeepSeq
+import qualified GHC.Data.Word64Set as S
-- Note [UniqSet invariant]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -64,6 +85,7 @@ import Control.DeepSeq
-- It means that to implement mapUniqSet you have to update
-- both the keys and the values.
+-- | Set of Uniquable values
newtype UniqSet a = UniqSet {getUniqSet' :: UniqFM a a}
deriving (Data, Semi.Semigroup, Monoid)
@@ -205,6 +227,83 @@ pprUniqSet :: (a -> SDoc) -> UniqSet a -> SDoc
-- pretty-printing.
pprUniqSet f = braces . pprWithCommas f . nonDetEltsUniqSet
-
forceUniqSet :: (a -> ()) -> UniqSet a -> ()
forceUniqSet f (UniqSet fm) = seqEltsUFM f fm
+
+--------------------------------------------------------
+-- UniqueSet
+--------------------------------------------------------
+
+-- | Set of Unique values
+--
+-- Similar to 'UniqSet Unique' but with a more compact representation.
+newtype UniqueSet = US { unUniqueSet :: S.Word64Set }
+ deriving (Eq, Ord, Show, Semigroup, Monoid)
+
+{-# INLINE nullUniqueSet #-}
+nullUniqueSet :: UniqueSet -> Bool
+nullUniqueSet (US s) = S.null s
+
+{-# INLINE sizeUniqueSet #-}
+sizeUniqueSet :: UniqueSet -> Int
+sizeUniqueSet (US s) = S.size s
+
+{-# INLINE memberUniqueSet #-}
+memberUniqueSet :: Unique -> UniqueSet -> Bool
+memberUniqueSet k (US s) = S.member (getKey k) s
+
+{-# INLINE emptyUniqueSet #-}
+emptyUniqueSet :: UniqueSet
+emptyUniqueSet = US S.empty
+
+{-# INLINE singletonUniqueSet #-}
+singletonUniqueSet :: Unique -> UniqueSet
+singletonUniqueSet k = US (S.singleton (getKey k))
+
+{-# INLINE insertUniqueSet #-}
+insertUniqueSet :: Unique -> UniqueSet -> UniqueSet
+insertUniqueSet k (US s) = US (S.insert (getKey k) s)
+
+{-# INLINE deleteUniqueSet #-}
+deleteUniqueSet :: Unique -> UniqueSet -> UniqueSet
+deleteUniqueSet k (US s) = US (S.delete (getKey k) s)
+
+{-# INLINE unionUniqueSet #-}
+unionUniqueSet :: UniqueSet -> UniqueSet -> UniqueSet
+unionUniqueSet (US x) (US y) = US (S.union x y)
+
+{-# INLINE unionsUniqueSet #-}
+unionsUniqueSet :: [UniqueSet] -> UniqueSet
+unionsUniqueSet xs = US (S.unions (map unUniqueSet xs))
+
+{-# INLINE differenceUniqueSet #-}
+differenceUniqueSet :: UniqueSet -> UniqueSet -> UniqueSet
+differenceUniqueSet (US x) (US y) = US (S.difference x y)
+
+{-# INLINE intersectionUniqueSet #-}
+intersectionUniqueSet :: UniqueSet -> UniqueSet -> UniqueSet
+intersectionUniqueSet (US x) (US y) = US (S.intersection x y)
+
+{-# INLINE isSubsetOfUniqueSet #-}
+isSubsetOfUniqueSet :: UniqueSet -> UniqueSet -> Bool
+isSubsetOfUniqueSet (US x) (US y) = S.isSubsetOf x y
+
+{-# INLINE filterUniqueSet #-}
+filterUniqueSet :: (Unique -> Bool) -> UniqueSet -> UniqueSet
+filterUniqueSet f (US s) = US (S.filter (f . mkUniqueGrimily) s)
+
+{-# INLINE foldlUniqueSet #-}
+foldlUniqueSet :: (a -> Unique -> a) -> a -> UniqueSet -> a
+foldlUniqueSet k z (US s) = S.foldl' (\a b -> k a (mkUniqueGrimily b)) z s
+
+{-# INLINE foldrUniqueSet #-}
+foldrUniqueSet :: (Unique -> b -> b) -> b -> UniqueSet -> b
+foldrUniqueSet k z (US s) = S.foldr (k . mkUniqueGrimily) z s
+
+{-# INLINE elemsUniqueSet #-}
+elemsUniqueSet :: UniqueSet -> [Unique]
+elemsUniqueSet (US s) = map mkUniqueGrimily (S.elems s)
+
+{-# INLINE fromListUniqueSet #-}
+fromListUniqueSet :: [Unique] -> UniqueSet
+fromListUniqueSet ks = US (S.fromList (map getKey ks))
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5f1afdf73ab7857c41a923a1435ffa081bf099fe
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5f1afdf73ab7857c41a923a1435ffa081bf099fe
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/20240531/3b400b4d/attachment-0001.html>
More information about the ghc-commits
mailing list