[Git][ghc/ghc][wip/decode_cloned_stack] 6 commits: Formatting
Sven Tennie (@supersven)
gitlab at gitlab.haskell.org
Sun Feb 26 16:11:37 UTC 2023
Sven Tennie pushed to branch wip/decode_cloned_stack at Glasgow Haskell Compiler / GHC
Commits:
0a353217 by Sven Tennie at 2023-02-25T15:27:03+00:00
Formatting
- - - - -
5b81493a by Sven Tennie at 2023-02-25T15:45:47+00:00
Refactor
- - - - -
98b7bb9d by Sven Tennie at 2023-02-26T08:49:24+00:00
Formatting
- - - - -
ae371c02 by Sven Tennie at 2023-02-26T09:16:04+00:00
Limit scopes in Decode
- - - - -
aa2a4812 by Sven Tennie at 2023-02-26T10:43:09+00:00
Scopes
- - - - -
cc51bcc9 by Sven Tennie at 2023-02-26T10:49:31+00:00
Formatting
- - - - -
3 changed files:
- libraries/ghc-heap/GHC/Exts/Stack/Constants.hsc
- libraries/ghc-heap/GHC/Exts/Stack/Decode.hs
- libraries/ghc-heap/tests/TestUtils.hs
Changes:
=====================================
libraries/ghc-heap/GHC/Exts/Stack/Constants.hsc
=====================================
@@ -3,7 +3,6 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module GHC.Exts.Stack.Constants where
--- TODO: Better expression to allow is only for the latest (this branch) GHC?
#if MIN_TOOL_VERSION_ghc(9,7,0)
import Prelude
@@ -21,59 +20,74 @@ newtype WordOffset = WordOffset { offsetInWords :: Int }
deriving newtype (Eq, Show, Integral, Real, Num, Enum, Ord)
offsetStgCatchFrameHandler :: WordOffset
-offsetStgCatchFrameHandler = byteOffsetToWordOffset $ (#const OFFSET_StgCatchFrame_handler) + (#size StgHeader)
+offsetStgCatchFrameHandler = byteOffsetToWordOffset $
+ (#const OFFSET_StgCatchFrame_handler) + (#size StgHeader)
offsetStgCatchFrameExceptionsBlocked :: WordOffset
-offsetStgCatchFrameExceptionsBlocked = byteOffsetToWordOffset $ (#const OFFSET_StgCatchFrame_exceptions_blocked) + (#size StgHeader)
+offsetStgCatchFrameExceptionsBlocked = byteOffsetToWordOffset $
+ (#const OFFSET_StgCatchFrame_exceptions_blocked) + (#size StgHeader)
sizeStgCatchFrame :: Int
-sizeStgCatchFrame = bytesToWords $ (#const SIZEOF_StgCatchFrame_NoHdr) + (#size StgHeader)
+sizeStgCatchFrame = bytesToWords $
+ (#const SIZEOF_StgCatchFrame_NoHdr) + (#size StgHeader)
offsetStgCatchSTMFrameCode :: WordOffset
-offsetStgCatchSTMFrameCode = byteOffsetToWordOffset $ (#const OFFSET_StgCatchSTMFrame_code) + (#size StgHeader)
+offsetStgCatchSTMFrameCode = byteOffsetToWordOffset $
+ (#const OFFSET_StgCatchSTMFrame_code) + (#size StgHeader)
offsetStgCatchSTMFrameHandler :: WordOffset
-offsetStgCatchSTMFrameHandler = byteOffsetToWordOffset $ (#const OFFSET_StgCatchSTMFrame_handler) + (#size StgHeader)
+offsetStgCatchSTMFrameHandler = byteOffsetToWordOffset $
+ (#const OFFSET_StgCatchSTMFrame_handler) + (#size StgHeader)
sizeStgCatchSTMFrame :: Int
-sizeStgCatchSTMFrame = bytesToWords $ (#const SIZEOF_StgCatchSTMFrame_NoHdr) + (#size StgHeader)
+sizeStgCatchSTMFrame = bytesToWords $
+ (#const SIZEOF_StgCatchSTMFrame_NoHdr) + (#size StgHeader)
offsetStgUpdateFrameUpdatee :: WordOffset
-offsetStgUpdateFrameUpdatee = byteOffsetToWordOffset $ (#const OFFSET_StgUpdateFrame_updatee) + (#size StgHeader)
+offsetStgUpdateFrameUpdatee = byteOffsetToWordOffset $
+ (#const OFFSET_StgUpdateFrame_updatee) + (#size StgHeader)
sizeStgUpdateFrame :: Int
-sizeStgUpdateFrame = bytesToWords $ (#const SIZEOF_StgUpdateFrame_NoHdr) + (#size StgHeader)
+sizeStgUpdateFrame = bytesToWords $
+ (#const SIZEOF_StgUpdateFrame_NoHdr) + (#size StgHeader)
offsetStgAtomicallyFrameCode :: WordOffset
-offsetStgAtomicallyFrameCode = byteOffsetToWordOffset $ (#const OFFSET_StgAtomicallyFrame_code) + (#size StgHeader)
+offsetStgAtomicallyFrameCode = byteOffsetToWordOffset $
+ (#const OFFSET_StgAtomicallyFrame_code) + (#size StgHeader)
offsetStgAtomicallyFrameResult :: WordOffset
-offsetStgAtomicallyFrameResult = byteOffsetToWordOffset $ (#const OFFSET_StgAtomicallyFrame_result) + (#size StgHeader)
+offsetStgAtomicallyFrameResult = byteOffsetToWordOffset $
+ (#const OFFSET_StgAtomicallyFrame_result) + (#size StgHeader)
sizeStgAtomicallyFrame :: Int
-sizeStgAtomicallyFrame = bytesToWords $ (#const SIZEOF_StgAtomicallyFrame_NoHdr) + (#size StgHeader)
+sizeStgAtomicallyFrame = bytesToWords $
+ (#const SIZEOF_StgAtomicallyFrame_NoHdr) + (#size StgHeader)
offsetStgCatchRetryFrameRunningAltCode :: WordOffset
-offsetStgCatchRetryFrameRunningAltCode = byteOffsetToWordOffset $ (#const OFFSET_StgCatchRetryFrame_running_alt_code) + (#size StgHeader)
+offsetStgCatchRetryFrameRunningAltCode = byteOffsetToWordOffset $
+ (#const OFFSET_StgCatchRetryFrame_running_alt_code) + (#size StgHeader)
offsetStgCatchRetryFrameRunningFirstCode :: WordOffset
-offsetStgCatchRetryFrameRunningFirstCode = byteOffsetToWordOffset $ (#const OFFSET_StgCatchRetryFrame_first_code) + (#size StgHeader)
+offsetStgCatchRetryFrameRunningFirstCode = byteOffsetToWordOffset $
+ (#const OFFSET_StgCatchRetryFrame_first_code) + (#size StgHeader)
offsetStgCatchRetryFrameAltCode :: WordOffset
-offsetStgCatchRetryFrameAltCode = byteOffsetToWordOffset $ (#const OFFSET_StgCatchRetryFrame_alt_code) + (#size StgHeader)
+offsetStgCatchRetryFrameAltCode = byteOffsetToWordOffset $
+ (#const OFFSET_StgCatchRetryFrame_alt_code) + (#size StgHeader)
sizeStgCatchRetryFrame :: Int
-sizeStgCatchRetryFrame = bytesToWords $ (#const SIZEOF_StgCatchRetryFrame_NoHdr) + (#size StgHeader)
+sizeStgCatchRetryFrame = bytesToWords $
+ (#const SIZEOF_StgCatchRetryFrame_NoHdr) + (#size StgHeader)
offsetStgRetFunFrameSize :: WordOffset
-- StgRetFun has no header, but only a pointer to the info table at the beginning.
-offsetStgRetFunFrameSize = byteOffsetToWordOffset $ (#const OFFSET_StgRetFun_size)
+offsetStgRetFunFrameSize = byteOffsetToWordOffset (#const OFFSET_StgRetFun_size)
offsetStgRetFunFrameFun :: WordOffset
-offsetStgRetFunFrameFun = byteOffsetToWordOffset $ (#const OFFSET_StgRetFun_fun)
+offsetStgRetFunFrameFun = byteOffsetToWordOffset (#const OFFSET_StgRetFun_fun)
offsetStgRetFunFramePayload :: WordOffset
-offsetStgRetFunFramePayload = byteOffsetToWordOffset $ (#const OFFSET_StgRetFun_payload)
+offsetStgRetFunFramePayload = byteOffsetToWordOffset (#const OFFSET_StgRetFun_payload)
sizeStgRetFunFrame :: Int
sizeStgRetFunFrame = bytesToWords (#const SIZEOF_StgRetFun)
@@ -94,7 +108,8 @@ offsetStgBCOFrameSize :: ByteOffset
offsetStgBCOFrameSize = (#const OFFSET_StgBCO_size) + (#size StgHeader)
offsetStgClosurePayload :: WordOffset
-offsetStgClosurePayload = byteOffsetToWordOffset $ (#const OFFSET_StgClosure_payload) + (#size StgHeader)
+offsetStgClosurePayload = byteOffsetToWordOffset $
+ (#const OFFSET_StgClosure_payload) + (#size StgHeader)
sizeStgClosure :: Int
sizeStgClosure = bytesToWords (#size StgHeader)
=====================================
libraries/ghc-heap/GHC/Exts/Stack/Decode.hs
=====================================
@@ -19,7 +19,6 @@ module GHC.Exts.Stack.Decode
)
where
-
import Data.Array.Byte
import Data.Bits
import Data.Maybe
@@ -110,12 +109,6 @@ Technical details
This keeps the code very portable.
-}
-type WordGetter = StackSnapshot# -> Word# -> State# RealWorld -> (# State# RealWorld, Word# #)
-
-type LargeBitmapGetter = StackSnapshot# -> Word# -> State# RealWorld -> (# State# RealWorld, ByteArray#, Word# #)
-
-type SmallBitmapGetter = StackSnapshot# -> Word# -> State# RealWorld -> (# State# RealWorld, Word#, Word# #)
-
foreign import prim "getUnderflowFrameNextChunkzh" getUnderflowFrameNextChunk# :: StackSnapshot# -> Word# -> State# RealWorld -> (# State# RealWorld, StackSnapshot# #)
getUnderflowFrameNextChunk :: StackFrameIter -> IO StackSnapshot
@@ -143,6 +136,8 @@ getWord (SfiClosure {..}) relativeOffset = IO $ \s ->
(# s1, w# #) -> (# s1, W# w# #)
getWord sfi _ = error $ "Unexpected StackFrameIter type: " ++ show sfi
+type WordGetter = StackSnapshot# -> Word# -> State# RealWorld -> (# State# RealWorld, Word# #)
+
foreign import prim "getRetFunTypezh" getRetFunType# :: WordGetter
getRetFunType :: StackFrameIter -> IO RetFunType
@@ -158,18 +153,20 @@ getRetFunType (SfiClosure {..}) =
)
getRetFunType sfi = error $ "Unexpected StackFrameIter type: " ++ show sfi
+type LargeBitmapGetter = StackSnapshot# -> Word# -> State# RealWorld -> (# State# RealWorld, ByteArray#, Word# #)
+
foreign import prim "getLargeBitmapzh" getLargeBitmap# :: LargeBitmapGetter
foreign import prim "getBCOLargeBitmapzh" getBCOLargeBitmap# :: LargeBitmapGetter
foreign import prim "getRetFunLargeBitmapzh" getRetFunLargeBitmap# :: LargeBitmapGetter
+type SmallBitmapGetter = StackSnapshot# -> Word# -> State# RealWorld -> (# State# RealWorld, Word#, Word# #)
+
foreign import prim "getSmallBitmapzh" getSmallBitmap# :: SmallBitmapGetter
foreign import prim "getRetFunSmallBitmapzh" getRetFunSmallBitmap# :: SmallBitmapGetter
-foreign import prim "advanceStackFrameIterzh" advanceStackFrameIter# :: StackSnapshot# -> Word# -> (# StackSnapshot#, Word#, Int# #)
-
foreign import prim "getInfoTableAddrzh" getInfoTableAddr# :: StackSnapshot# -> Word# -> Addr#
foreign import prim "getStackInfoTableAddrzh" getStackInfoTableAddr# :: StackSnapshot# -> Addr#
@@ -198,6 +195,12 @@ getStackFields sfi = error $ "Unexpected StackFrameIter type: " ++ show sfi
stackHead :: StackSnapshot -> StackFrameIter
stackHead (StackSnapshot s) = SfiClosure s 0 -- GHC stacks are never empty
+-- | Advance to the next stack frame (if any)
+--
+-- The last `Int#` in the result tuple is meant to be treated as bool
+-- (has_next).
+foreign import prim "advanceStackFrameIterzh" advanceStackFrameIter# :: StackSnapshot# -> Word# -> (# StackSnapshot#, Word#, Int# #)
+
-- | Advance iterator to the next stack frame (if any)
advanceStackFrameIter :: StackFrameIter -> Maybe StackFrameIter
advanceStackFrameIter (SfiClosure {..}) =
@@ -205,58 +208,10 @@ advanceStackFrameIter (SfiClosure {..}) =
in if I# hasNext > 0
then Just $ SfiClosure s' (primWordToWordOffset i')
else Nothing
-advanceStackFrameIter sfi = error $ "Unexpected StackFrameIter type: " ++ show sfi
-
-primWordToWordOffset :: Word# -> WordOffset
-primWordToWordOffset w# = fromIntegral (W# w#)
-
-wordsToBitmapEntries :: StackFrameIter -> [Word] -> Word -> [StackFrameIter]
-wordsToBitmapEntries _ [] 0 = []
-wordsToBitmapEntries _ [] i = error $ "Invalid state: Empty list, size " ++ show i
-wordsToBitmapEntries _ l 0 = error $ "Invalid state: Size 0, list " ++ show l
-wordsToBitmapEntries sfi (b : bs) bitmapSize =
- let entries = toBitmapEntries sfi b (min bitmapSize (fromIntegral wORD_SIZE_IN_BITS))
- mbLastFrame = (listToMaybe . reverse) entries
- in case mbLastFrame of
- Just (SfiClosure {..}) ->
- entries
- ++ wordsToBitmapEntries
- ( SfiClosure stackSnapshot# (index + 1)
- )
- bs
- (subtractDecodedBitmapWord bitmapSize)
- Just (SfiPrimitive {..}) ->
- entries
- ++ wordsToBitmapEntries
- ( SfiClosure stackSnapshot# (index + 1)
- )
- bs
- (subtractDecodedBitmapWord bitmapSize)
- _ -> error "This should never happen! Recursion ended not in base case."
where
- subtractDecodedBitmapWord :: Word -> Word
- subtractDecodedBitmapWord bSize =
- fromIntegral $
- max 0 (fromIntegral bSize - wORD_SIZE_IN_BITS)
-
-toBitmapEntries :: StackFrameIter -> Word -> Word -> [StackFrameIter]
-toBitmapEntries _ _ 0 = []
-toBitmapEntries (SfiClosure {..}) bitmapWord bSize =
- ( if (bitmapWord .&. 1) /= 0
- then SfiPrimitive stackSnapshot# index
- else SfiClosure stackSnapshot# index
- )
- : toBitmapEntries
- ( SfiClosure stackSnapshot# (index + 1)
- )
- (bitmapWord `shiftR` 1)
- (bSize - 1)
-toBitmapEntries sfi _ _ = error $ "Unexpected StackFrameIter type: " ++ show sfi
-
-toBitmapPayload :: StackFrameIter -> IO Box
-toBitmapPayload sfi at SfiPrimitive {} = pure (StackFrameBox sfi)
-toBitmapPayload sfi at SfiClosure {} = getClosure sfi 0
-toBitmapPayload sfi = error $ "Unexpected StackFrameIter type: " ++ show sfi
+ primWordToWordOffset :: Word# -> WordOffset
+ primWordToWordOffset w# = fromIntegral (W# w#)
+advanceStackFrameIter sfi = error $ "Unexpected StackFrameIter type: " ++ show sfi
getClosure :: StackFrameIter -> WordOffset -> IO Box
getClosure SfiClosure {..} relativeOffset =
@@ -276,12 +231,66 @@ decodeLargeBitmap getterFun# sfi@(SfiClosure {..}) relativePayloadOffset = do
(# s1, ba#, s# #) -> (# s1, (ByteArray ba#, W# s#) #)
let bitmapWords :: [Word] = byteArrayToList bitmapArray
decodeBitmaps sfi relativePayloadOffset bitmapWords size
+ where
+ byteArrayToList :: ByteArray -> [Word]
+ byteArrayToList (ByteArray bArray) = go 0
+ where
+ go i
+ | i < maxIndex = W# (indexWordArray# bArray (toInt# i)) : go (i + 1)
+ | otherwise = []
+ maxIndex = sizeofByteArray bArray `quot` sizeOf (undefined :: Word)
+
+ sizeofByteArray :: ByteArray# -> Int
+ sizeofByteArray arr# = I# (sizeofByteArray# arr#)
decodeLargeBitmap _ sfi _ = error $ "Unexpected StackFrameIter type: " ++ show sfi
decodeBitmaps :: StackFrameIter -> WordOffset -> [Word] -> Word -> IO [Box]
decodeBitmaps (SfiClosure {..}) relativePayloadOffset bitmapWords size =
- let bes = wordsToBitmapEntries (SfiClosure stackSnapshot# (index + relativePayloadOffset)) bitmapWords size
+ let bes = wordsToBitmapEntries (index + relativePayloadOffset) bitmapWords size
in mapM toBitmapPayload bes
+ where
+ toBitmapPayload :: StackFrameIter -> IO Box
+ toBitmapPayload sfi at SfiPrimitive {} = pure (StackFrameBox sfi)
+ toBitmapPayload sfi at SfiClosure {} = getClosure sfi 0
+ toBitmapPayload sfi = error $ "Unexpected StackFrameIter type: " ++ show sfi
+
+ wordsToBitmapEntries :: WordOffset -> [Word] -> Word -> [StackFrameIter]
+ wordsToBitmapEntries _ [] 0 = []
+ wordsToBitmapEntries _ [] i = error $ "Invalid state: Empty list, size " ++ show i
+ wordsToBitmapEntries _ l 0 = error $ "Invalid state: Size 0, list " ++ show l
+ wordsToBitmapEntries index' (b : bs) bitmapSize =
+ let entries = toBitmapEntries index' b (min bitmapSize (fromIntegral wORD_SIZE_IN_BITS))
+ mbLastFrame = (listToMaybe . reverse) entries
+ in case mbLastFrame of
+ Just sfi' ->
+ entries
+ ++ wordsToBitmapEntries
+ ((getIndex sfi') + 1)
+ bs
+ subtractDecodedBitmapWord
+ _ -> error "This should never happen! Recursion ended not in base case."
+ where
+ subtractDecodedBitmapWord :: Word
+ subtractDecodedBitmapWord =
+ fromIntegral $
+ max 0 (fromIntegral bitmapSize - wORD_SIZE_IN_BITS)
+
+ toBitmapEntries :: WordOffset -> Word -> Word -> [StackFrameIter]
+ toBitmapEntries _ _ 0 = []
+ toBitmapEntries i bitmapWord bSize =
+ ( if (bitmapWord .&. 1) /= 0
+ then SfiPrimitive stackSnapshot# i
+ else SfiClosure stackSnapshot# i
+ )
+ : toBitmapEntries
+ (i + 1)
+ (bitmapWord `shiftR` 1)
+ (bSize - 1)
+
+ getIndex :: StackFrameIter -> WordOffset
+ getIndex (SfiClosure _ i) = i
+ getIndex (SfiPrimitive _ i) = i
+ getIndex sfi' = error $ "Has no index : " ++ show sfi'
decodeBitmaps sfi _ _ _ = error $ "Unexpected StackFrameIter type: " ++ show sfi
decodeSmallBitmap :: SmallBitmapGetter -> StackFrameIter -> WordOffset -> IO [Box]
@@ -296,27 +305,17 @@ decodeSmallBitmap _ sfi _ =
error $
"Unexpected StackFrameIter type: " ++ show sfi
-byteArrayToList :: ByteArray -> [Word]
-byteArrayToList (ByteArray bArray) = go 0
- where
- go i
- | i < maxIndex = W# (indexWordArray# bArray (toInt# i)) : go (i + 1)
- | otherwise = []
- maxIndex = sizeofByteArray bArray `quot` sizeOf (undefined :: Word)
-
-wordOffsetToWord# :: WordOffset -> Word#
-wordOffsetToWord# wo = intToWord# (fromIntegral wo)
-
+-- | Decode `StackFrameIter` to `Closure`
unpackStackFrameIter :: StackFrameIter -> IO Closure
unpackStackFrameIter sfi@(SfiPrimitive {}) =
UnknownTypeWordSizedPrimitive
<$> getWord sfi 0
-unpackStackFrameIter sfi@(SfiStackClosure {}) = do
+unpackStackFrameIter sfi@(SfiStackClosure {..}) = do
info <- getInfoTable sfi
(stack_size', stack_dirty', stack_marking') <- getStackFields sfi
case tipe info of
STACK -> do
- let stack' = decodeStack' (StackSnapshot (stackSnapshot# sfi))
+ let stack' = decodeStackToBoxes (StackSnapshot stackSnapshot#)
pure $
StackClosure
{ info = info,
@@ -326,6 +325,15 @@ unpackStackFrameIter sfi@(SfiStackClosure {}) = do
stack = stack'
}
_ -> error $ "Expected STACK closure, got " ++ show info
+ where
+ decodeStackToBoxes :: StackSnapshot -> [Box]
+ decodeStackToBoxes s =
+ StackFrameBox (stackHead s)
+ : go (advanceStackFrameIter (stackHead s))
+ where
+ go :: Maybe StackFrameIter -> [Box]
+ go Nothing = []
+ go (Just sfi') = StackFrameBox sfi' : go (advanceStackFrameIter sfi')
unpackStackFrameIter sfi@(SfiClosure {}) = do
info <- getInfoTable sfi
unpackStackFrameIter' info
@@ -337,7 +345,7 @@ unpackStackFrameIter sfi@(SfiClosure {}) = do
bco' <- getClosure sfi offsetStgClosurePayload
-- The arguments begin directly after the payload's one element
bcoArgs' <- decodeLargeBitmap getBCOLargeBitmap# sfi (offsetStgClosurePayload + 1)
- pure $
+ pure
RetBCO
{ info = info,
bco = bco',
@@ -428,33 +436,26 @@ unpackStackFrameIter sfi@(SfiClosure {}) = do
}
x -> error $ "Unexpected closure type on stack: " ++ show x
--- | Size of the byte array in bytes.
--- Copied from `primitive`
-sizeofByteArray :: ByteArray# -> Int
-{-# INLINE sizeofByteArray #-}
-sizeofByteArray arr# = I# (sizeofByteArray# arr#)
-
-- | Unbox 'Int#' from 'Int'
toInt# :: Int -> Int#
toInt# (I# i) = i
+-- | Convert `Int` to `Word#`
intToWord# :: Int -> Word#
intToWord# i = int2Word# (toInt# i)
+wordOffsetToWord# :: WordOffset -> Word#
+wordOffsetToWord# wo = intToWord# (fromIntegral wo)
+
+-- | Decode `StackSnapshot` to a Closure
+--
+-- Due to the use of `Box` this decoding is lazy. The first decoded closure is
+-- the representation of the @StgStack@ itself.
decodeStack :: StackSnapshot -> IO Closure
decodeStack (StackSnapshot stack#) =
unpackStackFrameIter $
SfiStackClosure stack#
-decodeStack' :: StackSnapshot -> [Box]
-decodeStack' s =
- StackFrameBox (stackHead s)
- : go (advanceStackFrameIter (stackHead s))
- where
- go :: Maybe StackFrameIter -> [Box]
- go Nothing = []
- go (Just sfi) = StackFrameBox sfi : go (advanceStackFrameIter sfi)
-
#else
module GHC.Exts.Stack.Decode where
#endif
=====================================
libraries/ghc-heap/tests/TestUtils.hs
=====================================
@@ -17,9 +17,9 @@ import Data.Array.Byte
import Data.Foldable
import Debug.Trace
import GHC.Exts
-import GHC.Exts.Stack.Decode
import GHC.Exts.Heap
import GHC.Exts.Heap.Closures
+import GHC.Exts.Stack.Decode
import GHC.Records
import GHC.Stack (HasCallStack)
import GHC.Stack.CloneStack
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b7dfa91d32a4333860520cd8fc3f1d3173dc3fa3...cc51bcc9c967d4fa70719dc8643348eeff2749be
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b7dfa91d32a4333860520cd8fc3f1d3173dc3fa3...cc51bcc9c967d4fa70719dc8643348eeff2749be
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/20230226/3b88fd1e/attachment-0001.html>
More information about the ghc-commits
mailing list