Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
-
f8c24b51
by Teo Camarasu at 2026-03-12T01:26:31-04:00
-
e1b8e539
by Sylvain Henry at 2026-03-12T01:27:01-04:00
-
25ae85a8
by Sylvain Henry at 2026-03-12T01:27:07-04:00
-
62fe65ba
by Cheng Shao at 2026-03-12T01:27:07-04:00
-
6af6ba92
by Brandon Simmons at 2026-03-12T01:27:19-04:00
25 changed files:
- compiler/GHC/Stg/Pipeline.hs
- compiler/GHC/Stg/Unarise.hs
- libraries/base/src/GHC/Stats.hs
- libraries/base/src/GHC/Weak.hs
- libraries/base/src/GHC/Weak/Finalize.hs
- − libraries/base/src/GHC/Weak/Finalizehs
- libraries/base/src/System/Mem/Weak.hs
- libraries/base/tests/IO/T17912.hs
- libraries/base/tests/IO/all.T
- libraries/ghc-internal/CHANGELOG.md
- − libraries/ghc-internal/src/GHC/Internal/Conc/Sync.hs-boot
- − libraries/ghc-internal/src/GHC/Internal/IO/Handle/Text.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Stats.hsc
- libraries/ghc-internal/src/GHC/Internal/TopHandler.hs
- libraries/ghc-internal/src/GHC/Internal/Weak.hs
- libraries/ghc-internal/src/GHC/Internal/Weak/Finalize.hs
- libraries/template-haskell/Language/Haskell/TH/Quote.hs
- rts/Stats.c
- rts/include/RtsAPI.h
- testsuite/tests/codeGen/should_compile/Makefile
- + testsuite/tests/codeGen/should_compile/T25650.hs
- + testsuite/tests/codeGen/should_compile/T25650.stdout
- testsuite/tests/codeGen/should_compile/all.T
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout-mingw32
Changes:
| ... | ... | @@ -143,7 +143,7 @@ stg2stg logger extra_vars opts this_mod binds |
| 143 | 143 | StgUnarise -> do
|
| 144 | 144 | us <- getUniqueSupplyM
|
| 145 | 145 | liftIO (stg_linter False "Pre-unarise" binds)
|
| 146 | - let binds' = {-# SCC "StgUnarise" #-} unarise us (stgPipeline_allowTopLevelConApp opts this_mod) binds
|
|
| 146 | + let binds' = {-# SCC "StgUnarise" #-} unarise (stgPlatform opts) us (stgPipeline_allowTopLevelConApp opts this_mod) binds
|
|
| 147 | 147 | liftIO (dump_when Opt_D_dump_stg_unarised "Unarised STG:" binds')
|
| 148 | 148 | liftIO (stg_linter True "Unarise" binds')
|
| 149 | 149 | return binds'
|
| ... | ... | @@ -413,6 +413,7 @@ import Data.Maybe (mapMaybe) |
| 413 | 413 | import qualified Data.IntMap as IM
|
| 414 | 414 | import GHC.Builtin.PrimOps
|
| 415 | 415 | import GHC.Builtin.PrimOps.Casts
|
| 416 | +import GHC.Platform
|
|
| 416 | 417 | import Data.List (mapAccumL)
|
| 417 | 418 | |
| 418 | 419 | -- import GHC.Utils.Trace
|
| ... | ... | @@ -441,12 +442,13 @@ import Data.List (mapAccumL) |
| 441 | 442 | -- (i.e. no unboxed tuples, sums or voids)
|
| 442 | 443 | --
|
| 443 | 444 | data UnariseEnv = UnariseEnv
|
| 444 | - { ue_rho :: (VarEnv UnariseVal)
|
|
| 445 | + { ue_platform :: !Platform
|
|
| 446 | + , ue_rho :: VarEnv UnariseVal
|
|
| 445 | 447 | , ue_allow_static_conapp :: DataCon -> [StgArg] -> Bool
|
| 446 | 448 | }
|
| 447 | 449 | |
| 448 | -initUnariseEnv :: VarEnv UnariseVal -> (DataCon -> [StgArg] -> Bool) -> UnariseEnv
|
|
| 449 | -initUnariseEnv = UnariseEnv
|
|
| 450 | +initUnariseEnv :: Platform -> VarEnv UnariseVal -> (DataCon -> [StgArg] -> Bool) -> UnariseEnv
|
|
| 451 | +initUnariseEnv platform rho is_dll = UnariseEnv platform rho is_dll
|
|
| 450 | 452 | |
| 451 | 453 | data UnariseVal
|
| 452 | 454 | = MultiVal [OutStgArg] -- MultiVal to tuple. Can be empty list (void).
|
| ... | ... | @@ -479,8 +481,8 @@ lookupRho env v = lookupVarEnv (ue_rho env) v |
| 479 | 481 | |
| 480 | 482 | --------------------------------------------------------------------------------
|
| 481 | 483 | |
| 482 | -unarise :: UniqSupply -> (DataCon -> [StgArg] -> Bool) -> [StgTopBinding] -> [StgTopBinding]
|
|
| 483 | -unarise us is_dll_con_app binds = initUs_ us (mapM (unariseTopBinding (initUnariseEnv emptyVarEnv is_dll_con_app)) binds)
|
|
| 484 | +unarise :: Platform -> UniqSupply -> (DataCon -> [StgArg] -> Bool) -> [StgTopBinding] -> [StgTopBinding]
|
|
| 485 | +unarise platform us is_dll_con_app binds = initUs_ us (mapM (unariseTopBinding (initUnariseEnv platform emptyVarEnv is_dll_con_app)) binds)
|
|
| 484 | 486 | |
| 485 | 487 | unariseTopBinding :: UnariseEnv -> StgTopBinding -> UniqSM StgTopBinding
|
| 486 | 488 | unariseTopBinding rho (StgTopLifted bind)
|
| ... | ... | @@ -627,7 +629,7 @@ unariseUbxSumOrTupleArgs rho us dc args ty_args |
| 627 | 629 | |
| 628 | 630 | | isUnboxedSumDataCon dc
|
| 629 | 631 | , let args1 = assert (isSingleton args) (unariseConArgs rho args)
|
| 630 | - = let (args2, cast_wrapper) = mkUbxSum dc ty_args args1 us
|
|
| 632 | + = let (args2, cast_wrapper) = mkUbxSum (ue_platform rho) dc ty_args args1 us
|
|
| 631 | 633 | in (args2, Just cast_wrapper)
|
| 632 | 634 | |
| 633 | 635 | | otherwise
|
| ... | ... | @@ -848,29 +850,29 @@ mapSumIdBinders alt_bndr args rhs rho0 |
| 848 | 850 | -- right type.
|
| 849 | 851 | -- Select only the args which contain parts of the current field.
|
| 850 | 852 | id_arg_exprs = [ args !! i | i <- layout1 ]
|
| 851 | - id_vars = [v | StgVarArg v <- id_arg_exprs]
|
|
| 852 | 853 | |
| 853 | - typed_id_arg_input = assert (equalLength id_vars fld_reps) $
|
|
| 854 | - zip3 id_vars fld_reps uss
|
|
| 855 | - |
|
| 856 | - mkCastInput :: (Id,PrimRep,UniqSupply) -> ([(PrimOp,Type,Unique)],Id,Id)
|
|
| 857 | - mkCastInput (id,rep,bndr_us) =
|
|
| 858 | - let (ops,types) = unzip $ getCasts (typePrimRepU $ idType id) rep
|
|
| 854 | + typed_id_arg_input = assert (equalLength id_arg_exprs fld_reps) $
|
|
| 855 | + zip3 id_arg_exprs fld_reps uss
|
|
| 856 | + |
|
| 857 | + -- Process each (arg, target rep, unique supply) to produce
|
|
| 858 | + -- (rhs wrapper, typed arg). Handles both literal and variable args.
|
|
| 859 | + -- Literal args can arise after constant-folding in mkUbxSum
|
|
| 860 | + -- (see Note [Constant-folding during unarisation]).
|
|
| 861 | + mkCastArg :: (StgArg, PrimRep, UniqSupply) -> (StgExpr -> StgExpr, StgArg)
|
|
| 862 | + mkCastArg (StgLitArg lit, rep, _us)
|
|
| 863 | + | Just lit' <- castLiteralArg (ue_platform rho0) rep lit
|
|
| 864 | + = (id, StgLitArg lit')
|
|
| 865 | + | otherwise = pprPanic "mapSumIdBinders: cannot cast literal" (ppr lit $$ ppr rep)
|
|
| 866 | + mkCastArg (StgVarArg v, rep, bndr_us) =
|
|
| 867 | + let (ops,types) = unzip $ getCasts (typePrimRepU $ idType v) rep
|
|
| 859 | 868 | cst_opts = zip3 ops types $ uniqsFromSupply bndr_us
|
| 860 | 869 | out_id = case cst_opts of
|
| 861 | - [] -> id
|
|
| 862 | - _ -> let (_,ty,uq) = last cst_opts
|
|
| 863 | - in mkCastVar uq ty
|
|
| 864 | - in (cst_opts,id,out_id)
|
|
| 865 | - |
|
| 866 | - cast_inputs = map mkCastInput typed_id_arg_input
|
|
| 867 | - (rhs_with_casts,typed_ids) = mapAccumL cast_arg (\x->x) cast_inputs
|
|
| 868 | - where
|
|
| 869 | - cast_arg rhs_in (cast_ops,in_id,out_id) =
|
|
| 870 | - let rhs_out = castArgRename cast_ops (StgVarArg in_id)
|
|
| 871 | - in (rhs_in . rhs_out, out_id)
|
|
| 870 | + [] -> v
|
|
| 871 | + _ -> let (_,ty,uq) = last cst_opts in mkCastVar uq ty
|
|
| 872 | + in (castArgRename cst_opts (StgVarArg v), StgVarArg out_id)
|
|
| 872 | 873 | |
| 873 | - typed_id_args = map StgVarArg typed_ids
|
|
| 874 | + (wrappers, typed_id_args) = unzip $ map mkCastArg typed_id_arg_input
|
|
| 875 | + rhs_with_casts = foldr (.) id wrappers
|
|
| 874 | 876 | |
| 875 | 877 | if isMultiValBndr alt_bndr
|
| 876 | 878 | then return (extendRho rho0 alt_bndr (MultiVal typed_id_args), rhs_with_casts rhs)
|
| ... | ... | @@ -913,14 +915,15 @@ mkCast arg_in cast_op out_id out_ty in_rhs = |
| 913 | 915 | --
|
| 914 | 916 | mkUbxSum
|
| 915 | 917 | :: HasDebugCallStack
|
| 916 | - => DataCon -- Sum data con
|
|
| 918 | + => Platform -- For compile-time constant-folding
|
|
| 919 | + -> DataCon -- Sum data con
|
|
| 917 | 920 | -> [[PrimRep]] -- Representations of type arguments of the sum data con
|
| 918 | 921 | -> [OutStgArg] -- Actual arguments of the alternative.
|
| 919 | 922 | -> UniqSupply
|
| 920 | 923 | -> ([OutStgArg] -- Final tuple arguments
|
| 921 | 924 | ,(StgExpr->StgExpr) -- We might need to cast the args first
|
| 922 | 925 | )
|
| 923 | -mkUbxSum dc ty_args args0 us
|
|
| 926 | +mkUbxSum platform dc ty_args args0 us
|
|
| 924 | 927 | = let
|
| 925 | 928 | tag_slot :| sum_slots = ubxSumRepType ty_args
|
| 926 | 929 | -- drop tag slot
|
| ... | ... | @@ -961,6 +964,11 @@ mkUbxSum dc ty_args args0 us |
| 961 | 964 | , ubxSumRubbishArg slot)
|
| 962 | 965 | |
| 963 | 966 | castArg :: UniqSupply -> SlotTy -> StgArg -> Maybe (StgArg,UniqSupply,StgExpr -> StgExpr)
|
| 967 | + castArg us slot_ty arg@(StgLitArg lit)
|
|
| 968 | + -- See Note [Constant-folding during unarisation]
|
|
| 969 | + | slotPrimRep slot_ty /= stgArgRepU arg
|
|
| 970 | + , Just lit' <- castLiteralArg platform (slotPrimRep slot_ty) lit
|
|
| 971 | + = Just (StgLitArg lit', us, id)
|
|
| 964 | 972 | castArg us slot_ty arg
|
| 965 | 973 | -- Cast the argument to the type of the slot if required
|
| 966 | 974 | | slotPrimRep slot_ty /= stgArgRepU arg
|
| ... | ... | @@ -1006,6 +1014,101 @@ ubxSumRubbishArg DoubleSlot = StgLitArg (LitDouble 0) |
| 1006 | 1014 | ubxSumRubbishArg (VecSlot n e) = StgLitArg (LitRubbish TypeLike vec_rep)
|
| 1007 | 1015 | where vec_rep = primRepToRuntimeRep (VecRep n e)
|
| 1008 | 1016 | |
| 1017 | +{-
|
|
| 1018 | +Note [Constant-folding during unarisation]
|
|
| 1019 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1020 | +See #25650.
|
|
| 1021 | + |
|
| 1022 | +Goal: ensure that top-level bindings whose unboxed-sum fields are literals
|
|
| 1023 | +become statically allocated closures (i.e. compile-time constants in the
|
|
| 1024 | +object file) rather than CAFs.
|
|
| 1025 | + |
|
| 1026 | +Background: A top-level RHS is statically allocated when it is a plain
|
|
| 1027 | +`StgRhsCon`: a data constructor applied to arguments with no surrounding
|
|
| 1028 | +expression. Any `StgCase` wrapper, even one that is a no-op at runtime, turns
|
|
| 1029 | +the RHS into a CAF.
|
|
| 1030 | + |
|
| 1031 | +The problem: When `mkUbxSum` builds an unboxed sum whose argument PrimRep does
|
|
| 1032 | +not match the slot PrimRep, the general `castArg` path emits a runtime conversion
|
|
| 1033 | +wrapper:
|
|
| 1034 | + |
|
| 1035 | + case <conversion_primop> arg of x' -> <rhs using x'>
|
|
| 1036 | + |
|
| 1037 | +For a *variable* argument this is unavoidable, the value is not known at
|
|
| 1038 | +compile time. For a *literal* argument, however, the conversion can be performed
|
|
| 1039 | +at compile time, avoiding the `StgCase` wrapper entirely.
|
|
| 1040 | + |
|
| 1041 | +Example
|
|
| 1042 | +~~~~~~~
|
|
| 1043 | +Consider:
|
|
| 1044 | + |
|
| 1045 | + data A = MkA (# Int16# | Int32# #)
|
|
| 1046 | + foo = MkA (# 10#Int16 | #)
|
|
| 1047 | + |
|
| 1048 | +By the time this gets to the end of the Simplifier pipeline, this still looks
|
|
| 1049 | +like:
|
|
| 1050 | + foo = MkA (# 10#Int16 | #)
|
|
| 1051 | +That is: the worker for the data constructor takes an unboxed sum as its
|
|
| 1052 | +argument.
|
|
| 1053 | + |
|
| 1054 | +The Unarise pass, which works on STG, decides that
|
|
| 1055 | + (# 10#Int16 | #) :: (# Int16# | Int32# #)
|
|
| 1056 | +should be represented as an pair of an integer tag (of type `Int8#`) and a payload
|
|
| 1057 | +value (of type `Word32#`). But to do that it has to convert `10#Int16` into
|
|
| 1058 | +`Word32#`, and that conversion is not a no-op. So without constant-folding we
|
|
| 1059 | +get:
|
|
| 1060 | + |
|
| 1061 | + foo =
|
|
| 1062 | + \u []
|
|
| 1063 | + case int16ToWord16# [10#Int16] of cst_sum_gio {
|
|
| 1064 | + __DEFAULT ->
|
|
| 1065 | + case word16ToWord# [cst_sum_gio] of cst_sum_gip {
|
|
| 1066 | + __DEFAULT -> MkA [1# cst_sum_gip];
|
|
| 1067 | + };
|
|
| 1068 | + };
|
|
| 1069 | + |
|
| 1070 | +Note that in the output of the unarise pass, the worker `MkA` takes two
|
|
| 1071 | +arguments: the tag and the payload of our unboxed sum..
|
|
| 1072 | + |
|
| 1073 | +However it's a bit silly to generate a CAF here because with some
|
|
| 1074 | +constant-folding we can easily avoid this thunk and generate a static datacon
|
|
| 1075 | +instead. That's why the literal clause of `castArg` intercepts `Int16# 10`,
|
|
| 1076 | +calls `castLiteralArg` to compute `Word32# 10` at compile time, and returns the
|
|
| 1077 | +identity wrapper. The result is:
|
|
| 1078 | + |
|
| 1079 | + foo = MkA! [1#Word8 10#Word32];
|
|
| 1080 | + |
|
| 1081 | + |
|
| 1082 | +Note that `castLiteralArg` uses `mkLitNumberWrap`, which matches the
|
|
| 1083 | +semantics of GHC's integer-conversion primops (zero/sign extension to the target
|
|
| 1084 | +width) — exactly the same transformation the runtime conversion would have
|
|
| 1085 | +performed.
|
|
| 1086 | + |
|
| 1087 | +-}
|
|
| 1088 | + |
|
| 1089 | +-- | Try to convert a numeric literal to a new PrimRep at compile time.
|
|
| 1090 | +-- Uses wrapping semantics (same as GHC's integer conversion primops).
|
|
| 1091 | +-- Returns Nothing for non-numeric literals or unsupported PrimReps.
|
|
| 1092 | +-- See Note [Constant-folding during unarisation].
|
|
| 1093 | +castLiteralArg :: Platform -> PrimRep -> Literal -> Maybe Literal
|
|
| 1094 | +castLiteralArg platform to_rep (LitNumber _ n)
|
|
| 1095 | + | Just to_ty <- litNumTypeFromPrimRep to_rep
|
|
| 1096 | + = Just (mkLitNumberWrap platform to_ty n)
|
|
| 1097 | +castLiteralArg _ _ _ = Nothing
|
|
| 1098 | + |
|
| 1099 | +litNumTypeFromPrimRep :: PrimRep -> Maybe LitNumType
|
|
| 1100 | +litNumTypeFromPrimRep WordRep = Just LitNumWord
|
|
| 1101 | +litNumTypeFromPrimRep Word8Rep = Just LitNumWord8
|
|
| 1102 | +litNumTypeFromPrimRep Word16Rep = Just LitNumWord16
|
|
| 1103 | +litNumTypeFromPrimRep Word32Rep = Just LitNumWord32
|
|
| 1104 | +litNumTypeFromPrimRep Word64Rep = Just LitNumWord64
|
|
| 1105 | +litNumTypeFromPrimRep IntRep = Just LitNumInt
|
|
| 1106 | +litNumTypeFromPrimRep Int8Rep = Just LitNumInt8
|
|
| 1107 | +litNumTypeFromPrimRep Int16Rep = Just LitNumInt16
|
|
| 1108 | +litNumTypeFromPrimRep Int32Rep = Just LitNumInt32
|
|
| 1109 | +litNumTypeFromPrimRep Int64Rep = Just LitNumInt64
|
|
| 1110 | +litNumTypeFromPrimRep _ = Nothing
|
|
| 1111 | + |
|
| 1009 | 1112 | --------------------------------------------------------------------------------
|
| 1010 | 1113 | |
| 1011 | 1114 | {-
|
| ... | ... | @@ -26,6 +26,7 @@ |
| 26 | 26 | -- proposal
|
| 27 | 27 | -- #289](https://github.com/haskell/core-libraries-committee/issues/289). These
|
| 28 | 28 | -- declarations are now instead available from the @ghc-experimental@ package.
|
| 29 | +-- @ghc-experimental@ contains additional metrics not added to the API here.
|
|
| 29 | 30 | |
| 30 | 31 | module GHC.Stats
|
| 31 | 32 | ( -- * Runtime statistics
|
| ... | ... | @@ -29,3 +29,5 @@ module GHC.Weak |
| 29 | 29 | ) where
|
| 30 | 30 | |
| 31 | 31 | import GHC.Internal.Weak
|
| 32 | +import GHC.Internal.Weak.Finalize
|
|
| 33 | +import GHC.Weak.Finalize |
| ... | ... | @@ -14,9 +14,14 @@ module GHC.Weak.Finalize |
| 14 | 14 | |
| 15 | 15 | import GHC.Internal.Weak.Finalize
|
| 16 | 16 | |
| 17 | --- These imports can be removed once runFinalizerBatch is removed,
|
|
| 18 | --- as can MagicHash above.
|
|
| 19 | -import GHC.Internal.Base (Int, Array#, IO, State#, RealWorld)
|
|
| 17 | +import GHC.Internal.Base
|
|
| 18 | +import GHC.Internal.Exception
|
|
| 19 | +import GHC.Internal.IORef
|
|
| 20 | +import GHC.Internal.Conc.Sync (labelThreadByteArray#, myThreadId)
|
|
| 21 | +import GHC.Internal.IO (catchException, unsafePerformIO)
|
|
| 22 | +import GHC.Internal.IO.Handle.Types (Handle)
|
|
| 23 | +import GHC.Internal.IO.Handle.Text (hPutStrLn)
|
|
| 24 | +import GHC.Internal.Encoding.UTF8 (utf8EncodeByteArray#)
|
|
| 20 | 25 | |
| 21 | 26 | |
| 22 | 27 | {-# DEPRECATED runFinalizerBatch
|
| ... | ... | @@ -36,3 +41,13 @@ runFinalizerBatch :: Int |
| 36 | 41 | -> Array# (State# RealWorld -> State# RealWorld)
|
| 37 | 42 | -> IO ()
|
| 38 | 43 | runFinalizerBatch = GHC.Internal.Weak.Finalize.runFinalizerBatch
|
| 44 | + |
|
| 45 | +-- | An exception handler for 'Handle' finalization that prints the error to
|
|
| 46 | +-- the given 'Handle', but doesn't rethrow it.
|
|
| 47 | +--
|
|
| 48 | +-- @since base-4.18.0.0
|
|
| 49 | +printToHandleFinalizerExceptionHandler :: Handle -> SomeException -> IO ()
|
|
| 50 | +printToHandleFinalizerExceptionHandler hdl se =
|
|
| 51 | + hPutStrLn hdl msg `catchException` (\(SomeException _) -> return ())
|
|
| 52 | + where
|
|
| 53 | + msg = "Exception during weak pointer finalization (ignored): " ++ displayException se ++ "\n" |
| ... | ... | @@ -91,6 +91,7 @@ module System.Mem.Weak ( |
| 91 | 91 | |
| 92 | 92 | import Prelude
|
| 93 | 93 | import GHC.Internal.Weak
|
| 94 | +import GHC.Weak
|
|
| 94 | 95 | |
| 95 | 96 | -- | A specialised version of 'mkWeak', where the key and the value are
|
| 96 | 97 | -- the same object:
|
| ... | ... | @@ -6,6 +6,7 @@ import Control.Exception |
| 6 | 6 | import System.IO
|
| 7 | 7 | import System.Exit
|
| 8 | 8 | import System.Process
|
| 9 | +import GHC.Conc (threadStatus, ThreadStatus(..), BlockReason(..))
|
|
| 9 | 10 | import GHC.IO.Handle.FD
|
| 10 | 11 | |
| 11 | 12 | main = do
|
| ... | ... | @@ -22,7 +23,14 @@ main = do |
| 22 | 23 | putMVar passed True
|
| 23 | 24 | else print e
|
| 24 | 25 | throwIO e
|
| 25 | - threadDelay 1000
|
|
| 26 | + let waitUntilBlocked = do
|
|
| 27 | + st <- threadStatus opener
|
|
| 28 | + case st of
|
|
| 29 | + ThreadBlocked BlockedOnForeignCall -> return ()
|
|
| 30 | + ThreadFinished -> return ()
|
|
| 31 | + ThreadDied -> return ()
|
|
| 32 | + _ -> threadDelay 100 >> waitUntilBlocked
|
|
| 33 | + waitUntilBlocked
|
|
| 26 | 34 | forkIO $ killThread opener
|
| 27 | 35 | forkIO $ do
|
| 28 | 36 | threadDelay (10^6)
|
| ... | ... | @@ -182,7 +182,7 @@ test('T17414', |
| 182 | 182 | compile_and_run, [''])
|
| 183 | 183 | test('T17510', expect_broken(17510), compile_and_run, [''])
|
| 184 | 184 | test('bytestringread001', extra_run_opts('test.data'), compile_and_run, [''])
|
| 185 | -test('T17912', [only_ways(['threaded1']), when(opsys('mingw32'),expect_broken(1))], compile_and_run, [''])
|
|
| 185 | +test('T17912', [only_ways(['threaded1']), when(opsys('mingw32'),expect_broken(17912))], compile_and_run, [''])
|
|
| 186 | 186 | test('T18832', only_ways(['threaded1']), compile_and_run, [''])
|
| 187 | 187 | |
| 188 | 188 | test('mkdirExists', [exit_code(1), when(opsys('mingw32'), ignore_stderr)], compile_and_run, [''])
|
| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | ## 9.1401.0 -- yyyy-mm-dd
|
| 4 | 4 | |
| 5 | 5 | * Introduce `dataToCodeQ` and `liftDataTyped`, typed variants of `dataToExpQ` and `liftData` respectively.
|
| 6 | +* Add new `gc_sync_elapsed_ns` counter to GHC.Internal.Stats
|
|
| 6 | 7 | |
| 7 | 8 | ## 9.1001.0 -- 2024-05-01
|
| 8 | 9 |
| 1 | -{-# LANGUAGE MagicHash, NoImplicitPrelude #-}
|
|
| 2 | -{-# OPTIONS_HADDOCK not-home #-}
|
|
| 3 | - |
|
| 4 | ------------------------------------------------------------------------------
|
|
| 5 | --- |
|
|
| 6 | --- Module : GHC.Internal.Conc.Sync [boot]
|
|
| 7 | --- Copyright : (c) The University of Glasgow, 1994-2002
|
|
| 8 | --- License : see libraries/base/LICENSE
|
|
| 9 | ---
|
|
| 10 | --- Maintainer : ghc-devs@haskell.org
|
|
| 11 | --- Stability : internal
|
|
| 12 | --- Portability : non-portable (GHC extensions)
|
|
| 13 | ---
|
|
| 14 | --- Basic concurrency stuff.
|
|
| 15 | ---
|
|
| 16 | ------------------------------------------------------------------------------
|
|
| 17 | - |
|
| 18 | -module GHC.Internal.Conc.Sync
|
|
| 19 | - ( forkIO,
|
|
| 20 | - ThreadId(..),
|
|
| 21 | - myThreadId,
|
|
| 22 | - showThreadId,
|
|
| 23 | - ThreadStatus(..),
|
|
| 24 | - threadStatus,
|
|
| 25 | - sharedCAF,
|
|
| 26 | - labelThreadByteArray#
|
|
| 27 | - ) where
|
|
| 28 | - |
|
| 29 | -import GHC.Internal.Base
|
|
| 30 | -import GHC.Internal.Ptr
|
|
| 31 | - |
|
| 32 | -forkIO :: IO () -> IO ThreadId
|
|
| 33 | - |
|
| 34 | -data ThreadId = ThreadId ThreadId#
|
|
| 35 | - |
|
| 36 | -data BlockReason
|
|
| 37 | - = BlockedOnMVar
|
|
| 38 | - -- ^blocked on 'MVar'
|
|
| 39 | - {- possibly (see 'threadstatus' below):
|
|
| 40 | - | BlockedOnMVarRead
|
|
| 41 | - -- ^blocked on reading an empty 'MVar'
|
|
| 42 | - -}
|
|
| 43 | - | BlockedOnBlackHole
|
|
| 44 | - -- ^blocked on a computation in progress by another thread
|
|
| 45 | - | BlockedOnException
|
|
| 46 | - -- ^blocked in 'throwTo'
|
|
| 47 | - | BlockedOnSTM
|
|
| 48 | - -- ^blocked in 'retry' in an STM transaction
|
|
| 49 | - | BlockedOnForeignCall
|
|
| 50 | - -- ^currently in a foreign call
|
|
| 51 | - | BlockedOnOther
|
|
| 52 | - -- ^blocked on some other resource. Without @-threaded@,
|
|
| 53 | - -- I\/O and 'threadDelay' show up as 'BlockedOnOther', with @-threaded@
|
|
| 54 | - -- they show up as 'BlockedOnMVar'.
|
|
| 55 | - |
|
| 56 | -data ThreadStatus
|
|
| 57 | - = ThreadRunning
|
|
| 58 | - -- ^the thread is currently runnable or running
|
|
| 59 | - | ThreadFinished
|
|
| 60 | - -- ^the thread has finished
|
|
| 61 | - | ThreadBlocked BlockReason
|
|
| 62 | - -- ^the thread is blocked on some resource
|
|
| 63 | - | ThreadDied
|
|
| 64 | - -- ^the thread received an uncaught exception
|
|
| 65 | - |
|
| 66 | -myThreadId :: IO ThreadId
|
|
| 67 | -showThreadId :: ThreadId -> String
|
|
| 68 | -threadStatus :: ThreadId -> IO ThreadStatus
|
|
| 69 | -sharedCAF :: a -> (Ptr a -> IO (Ptr a)) -> IO a
|
|
| 70 | -labelThreadByteArray# :: ThreadId -> ByteArray# -> IO () |
| 1 | -{-# LANGUAGE NoImplicitPrelude #-}
|
|
| 2 | - |
|
| 3 | -module GHC.Internal.IO.Handle.Text ( hPutStrLn ) where
|
|
| 4 | - |
|
| 5 | -import GHC.Internal.Base (String, IO)
|
|
| 6 | -import {-# SOURCE #-} GHC.Internal.IO.Handle.Types (Handle)
|
|
| 7 | - |
|
| 8 | -hPutStrLn :: Handle -> String -> IO () |
| ... | ... | @@ -111,6 +111,8 @@ data RTSStats = RTSStats { |
| 111 | 111 | , gc_cpu_ns :: RtsTime
|
| 112 | 112 | -- | Total elapsed time used by the GC
|
| 113 | 113 | , gc_elapsed_ns :: RtsTime
|
| 114 | + -- | Total elapsed time used during GC synchronization
|
|
| 115 | + , gc_sync_elapsed_ns :: RtsTime
|
|
| 114 | 116 | -- | Total CPU time (at the previous GC)
|
| 115 | 117 | , cpu_ns :: RtsTime
|
| 116 | 118 | -- | Total elapsed time (at the previous GC)
|
| ... | ... | @@ -234,6 +236,7 @@ getRTSStats = do |
| 234 | 236 | mutator_elapsed_ns <- (# peek RTSStats, mutator_elapsed_ns) p
|
| 235 | 237 | gc_cpu_ns <- (# peek RTSStats, gc_cpu_ns) p
|
| 236 | 238 | gc_elapsed_ns <- (# peek RTSStats, gc_elapsed_ns) p
|
| 239 | + gc_sync_elapsed_ns <- (# peek RTSStats, gc_sync_elapsed_ns) p
|
|
| 237 | 240 | cpu_ns <- (# peek RTSStats, cpu_ns) p
|
| 238 | 241 | elapsed_ns <- (# peek RTSStats, elapsed_ns) p
|
| 239 | 242 | nonmoving_gc_sync_cpu_ns <- (# peek RTSStats, nonmoving_gc_sync_cpu_ns) p
|
| ... | ... | @@ -50,6 +50,8 @@ import GHC.Internal.IO.Handle |
| 50 | 50 | import GHC.Internal.IO.StdHandles
|
| 51 | 51 | import GHC.Internal.IO.Exception
|
| 52 | 52 | import GHC.Internal.Weak
|
| 53 | +import GHC.Internal.Weak.Finalize
|
|
| 54 | +import GHC.Internal.IO.Handle.Types ()
|
|
| 53 | 55 | |
| 54 | 56 | #if defined(mingw32_HOST_OS)
|
| 55 | 57 | import GHC.Internal.ConsoleHandler as GHC.ConsoleHandler
|
| ... | ... | @@ -24,19 +24,9 @@ module GHC.Internal.Weak ( |
| 24 | 24 | mkWeak,
|
| 25 | 25 | deRefWeak,
|
| 26 | 26 | finalize,
|
| 27 | - |
|
| 28 | - -- * Handling exceptions
|
|
| 29 | - -- | When an exception is thrown by a finalizer called by the
|
|
| 30 | - -- garbage collector, GHC calls a global handler which can be set with
|
|
| 31 | - -- 'setFinalizerExceptionHandler'. Note that any exceptions thrown by
|
|
| 32 | - -- this handler will be ignored.
|
|
| 33 | - setFinalizerExceptionHandler,
|
|
| 34 | - getFinalizerExceptionHandler,
|
|
| 35 | - printToHandleFinalizerExceptionHandler
|
|
| 36 | 27 | ) where
|
| 37 | 28 | |
| 38 | 29 | import GHC.Internal.Base
|
| 39 | -import GHC.Internal.Weak.Finalize
|
|
| 40 | 30 | |
| 41 | 31 | {-|
|
| 42 | 32 | A weak pointer object with a key and a value. The value has type @v@.
|
| ... | ... | @@ -4,26 +4,17 @@ |
| 4 | 4 | {-# LANGUAGE Unsafe #-}
|
| 5 | 5 | |
| 6 | 6 | module GHC.Internal.Weak.Finalize
|
| 7 | - ( -- * Handling exceptions
|
|
| 8 | - -- | When an exception is thrown by a finalizer called by the
|
|
| 9 | - -- garbage collector, GHC calls a global handler which can be set with
|
|
| 10 | - -- 'setFinalizerExceptionHandler'. Note that any exceptions thrown by
|
|
| 11 | - -- this handler will be ignored.
|
|
| 12 | - setFinalizerExceptionHandler
|
|
| 13 | - , getFinalizerExceptionHandler
|
|
| 14 | - , printToHandleFinalizerExceptionHandler
|
|
| 15 | - -- * Internal
|
|
| 7 | + ( getFinalizerExceptionHandler
|
|
| 8 | + , setFinalizerExceptionHandler
|
|
| 16 | 9 | , runFinalizerBatch
|
| 17 | 10 | ) where
|
| 18 | 11 | |
| 19 | 12 | import GHC.Internal.Base
|
| 20 | -import GHC.Internal.Exception
|
|
| 21 | -import GHC.Internal.IORef
|
|
| 22 | -import {-# SOURCE #-} GHC.Internal.Conc.Sync (labelThreadByteArray#, myThreadId)
|
|
| 23 | -import GHC.Internal.IO (catchException, unsafePerformIO)
|
|
| 24 | -import {-# SOURCE #-} GHC.Internal.IO.Handle.Types (Handle)
|
|
| 25 | -import {-# SOURCE #-} GHC.Internal.IO.Handle.Text (hPutStrLn)
|
|
| 26 | -import GHC.Internal.Encoding.UTF8 (utf8EncodeByteArray#)
|
|
| 13 | +import GHC.Internal.Conc.Sync ( labelThreadByteArray#, myThreadId )
|
|
| 14 | +import GHC.Internal.Encoding.UTF8 ( utf8EncodeByteArray# )
|
|
| 15 | +import GHC.Internal.Exception ( SomeException(..) )
|
|
| 16 | +import GHC.Internal.IO ( catchException, unsafePerformIO )
|
|
| 17 | +import GHC.Internal.IORef ( IORef, newIORef, readIORef, writeIORef )
|
|
| 27 | 18 | |
| 28 | 19 | data ByteArray = ByteArray ByteArray#
|
| 29 | 20 | |
| ... | ... | @@ -82,13 +73,3 @@ getFinalizerExceptionHandler = readIORef finalizerExceptionHandler |
| 82 | 73 | -- @since base-4.18.0.0
|
| 83 | 74 | setFinalizerExceptionHandler :: (SomeException -> IO ()) -> IO ()
|
| 84 | 75 | setFinalizerExceptionHandler = writeIORef finalizerExceptionHandler |
| 85 | - |
|
| 86 | --- | An exception handler for 'Handle' finalization that prints the error to
|
|
| 87 | --- the given 'Handle', but doesn't rethrow it.
|
|
| 88 | ---
|
|
| 89 | --- @since base-4.18.0.0
|
|
| 90 | -printToHandleFinalizerExceptionHandler :: Handle -> SomeException -> IO ()
|
|
| 91 | -printToHandleFinalizerExceptionHandler hdl se =
|
|
| 92 | - hPutStrLn hdl msg `catchException` (\(SomeException _) -> return ())
|
|
| 93 | - where
|
|
| 94 | - msg = "Exception during weak pointer finalization (ignored): " ++ displayException se ++ "\n" |
| ... | ... | @@ -23,7 +23,6 @@ module Language.Haskell.TH.Quote |
| 23 | 23 | ) where
|
| 24 | 24 | |
| 25 | 25 | import GHC.Boot.TH.Monad
|
| 26 | -import GHC.Boot.TH.Quote
|
|
| 27 | 26 | import Language.Haskell.TH.Syntax (dataToQa, dataToExpQ, dataToPatQ)
|
| 28 | 27 | |
| 29 | 28 |
| ... | ... | @@ -163,6 +163,7 @@ initStats0(void) |
| 163 | 163 | .mutator_elapsed_ns = 0,
|
| 164 | 164 | .gc_cpu_ns = 0,
|
| 165 | 165 | .gc_elapsed_ns = 0,
|
| 166 | + .gc_sync_elapsed_ns = 0,
|
|
| 166 | 167 | .cpu_ns = 0,
|
| 167 | 168 | .elapsed_ns = 0,
|
| 168 | 169 | .nonmoving_gc_cpu_ns = 0,
|
| ... | ... | @@ -288,6 +289,8 @@ stat_endExit(void) |
| 288 | 289 | RELEASE_LOCK(&stats_mutex);
|
| 289 | 290 | }
|
| 290 | 291 | |
| 292 | +// This is only called in the threaded RTS. On non-threaded RTS `gc_sync_start_elapsed`
|
|
| 293 | +// is conditonally set in `stat_startGC`.
|
|
| 291 | 294 | void
|
| 292 | 295 | stat_startGCSync (gc_thread *gct)
|
| 293 | 296 | {
|
| ... | ... | @@ -433,6 +436,11 @@ stat_startGC (Capability *cap, gc_thread *gct) |
| 433 | 436 | }
|
| 434 | 437 | |
| 435 | 438 | gct->gc_start_elapsed = getProcessElapsedTime();
|
| 439 | +#if !defined(THREADED_RTS)
|
|
| 440 | + // Non-threaded RTS has no sync phase. Initializing in this way makes the
|
|
| 441 | + // calculated statistics correctly read zero.
|
|
| 442 | + gct->gc_sync_start_elapsed = gct->gc_start_elapsed;
|
|
| 443 | +#endif
|
|
| 436 | 444 | |
| 437 | 445 | // Post EVENT_GC_START with the same timestamp as used for stats
|
| 438 | 446 | // (though converted from Time=StgInt64 to EventTimestamp=StgWord64).
|
| ... | ... | @@ -548,6 +556,7 @@ stat_endGC (Capability *cap, gc_thread *initiating_gct, W_ live, W_ copied, W_ s |
| 548 | 556 | }
|
| 549 | 557 | stats.gc_cpu_ns += stats.gc.cpu_ns;
|
| 550 | 558 | stats.gc_elapsed_ns += stats.gc.elapsed_ns;
|
| 559 | + stats.gc_sync_elapsed_ns += stats.gc.sync_elapsed_ns;
|
|
| 551 | 560 | |
| 552 | 561 | if (gen == RtsFlags.GcFlags.generations-1) { // major GC?
|
| 553 | 562 | stats.major_gcs++;
|
| ... | ... | @@ -915,6 +924,8 @@ static void report_summary(const RTSSummaryStats* sum) |
| 915 | 924 | statsPrintf(" GC time %7.3fs (%7.3fs elapsed)\n",
|
| 916 | 925 | TimeToSecondsDbl(stats.gc_cpu_ns),
|
| 917 | 926 | TimeToSecondsDbl(stats.gc_elapsed_ns));
|
| 927 | + statsPrintf(" GC SYNC time (%7.3fs elapsed)\n",
|
|
| 928 | + TimeToSecondsDbl(stats.gc_sync_elapsed_ns));
|
|
| 918 | 929 | if (RtsFlags.GcFlags.useNonmoving) {
|
| 919 | 930 | statsPrintf(
|
| 920 | 931 | " CONC GC time %7.3fs (%7.3fs elapsed)\n",
|
| ... | ... | @@ -1069,6 +1080,7 @@ static void report_machine_readable (const RTSSummaryStats * sum) |
| 1069 | 1080 | TimeToSecondsDbl(stats.mutator_elapsed_ns));
|
| 1070 | 1081 | MR_STAT("GC_cpu_seconds", "f", TimeToSecondsDbl(stats.gc_cpu_ns));
|
| 1071 | 1082 | MR_STAT("GC_wall_seconds", "f", TimeToSecondsDbl(stats.gc_elapsed_ns));
|
| 1083 | + MR_STAT("GC_sync_wall_seconds", "f", TimeToSecondsDbl(stats.gc_sync_elapsed_ns));
|
|
| 1072 | 1084 | |
| 1073 | 1085 | // end backward compatibility
|
| 1074 | 1086 |
| ... | ... | @@ -240,6 +240,8 @@ typedef struct _RTSStats { |
| 240 | 240 | Time gc_cpu_ns;
|
| 241 | 241 | // Total elapsed time used by the GC
|
| 242 | 242 | Time gc_elapsed_ns;
|
| 243 | + // Total elapsed time used during GC synchronization
|
|
| 244 | + Time gc_sync_elapsed_ns;
|
|
| 243 | 245 | // Total CPU time (at the previous GC)
|
| 244 | 246 | Time cpu_ns;
|
| 245 | 247 | // Total elapsed time (at the previous GC)
|
| ... | ... | @@ -80,3 +80,6 @@ T17648: |
| 80 | 80 | |
| 81 | 81 | T25166:
|
| 82 | 82 | '$(TEST_HC)' $(TEST_HC_OPTS) -O2 -dno-typeable-binds -ddump-cmm T25166.hs | awk '/foo_closure/{flag=1}/}]/{flag=0}flag'
|
| 83 | + |
|
| 84 | +T25650:
|
|
| 85 | + '$(TEST_HC)' $(TEST_HC_OPTS) -O2 -dno-typeable-binds -ddump-cmm T25650.hs | awk '/baz_foo_closure|baz_bar_closure/{flag=1}/}]/{flag=0}flag' |
| 1 | +module T25650 (baz_foo, baz_bar) where
|
|
| 2 | + |
|
| 3 | +import Data.Word
|
|
| 4 | + |
|
| 5 | +data A
|
|
| 6 | + = A1 {-# UNPACK #-} !Word32
|
|
| 7 | + | A2 {-# UNPACK #-} !B
|
|
| 8 | + |
|
| 9 | +data B = B1 | B2
|
|
| 10 | + |
|
| 11 | +foo = A1 10
|
|
| 12 | +bar = A2 B2
|
|
| 13 | + |
|
| 14 | +data C = C {-# UNPACK #-} !A
|
|
| 15 | + |
|
| 16 | +baz_foo = C foo
|
|
| 17 | +baz_bar = C bar |
| 1 | +[section ""data" . T25650.baz_foo_closure" {
|
|
| 2 | + T25650.baz_foo_closure:
|
|
| 3 | + const T25650.C_con_info;
|
|
| 4 | + const 10 :: W32;
|
|
| 5 | + const 1 :: W8;
|
|
| 6 | + const 0 :: W8;
|
|
| 7 | + const 0 :: W16;
|
|
| 8 | +[section ""data" . T25650.baz_bar_closure" {
|
|
| 9 | + T25650.baz_bar_closure:
|
|
| 10 | + const T25650.C_con_info;
|
|
| 11 | + const 2 :: W32;
|
|
| 12 | + const 2 :: W8;
|
|
| 13 | + const 0 :: W8;
|
|
| 14 | + const 0 :: W16; |
| ... | ... | @@ -140,6 +140,7 @@ test('callee-no-local', [ |
| 140 | 140 | )
|
| 141 | 141 | |
| 142 | 142 | test('T25166', [req_cmm], makefile_test, [])
|
| 143 | +test('T25650', [req_cmm], makefile_test, [])
|
|
| 143 | 144 | |
| 144 | 145 | # dump Core to ensure that d is defined as: d = D 10## RUBBISH(IntRep)
|
| 145 | 146 | test('T25177', normal, compile, ['-O2 -dno-typeable-binds -ddump-simpl -dsuppress-all -dsuppress-uniques -v0'])
|
| ... | ... | @@ -6587,6 +6587,7 @@ module GHC.Stats.Experimental where |
| 6587 | 6587 | mutator_elapsed_ns :: RtsTime,
|
| 6588 | 6588 | gc_cpu_ns :: RtsTime,
|
| 6589 | 6589 | gc_elapsed_ns :: RtsTime,
|
| 6590 | + gc_sync_elapsed_ns :: RtsTime,
|
|
| 6590 | 6591 | cpu_ns :: RtsTime,
|
| 6591 | 6592 | elapsed_ns :: RtsTime,
|
| 6592 | 6593 | nonmoving_gc_sync_cpu_ns :: RtsTime,
|
| ... | ... | @@ -6590,6 +6590,7 @@ module GHC.Stats.Experimental where |
| 6590 | 6590 | mutator_elapsed_ns :: RtsTime,
|
| 6591 | 6591 | gc_cpu_ns :: RtsTime,
|
| 6592 | 6592 | gc_elapsed_ns :: RtsTime,
|
| 6593 | + gc_sync_elapsed_ns :: RtsTime,
|
|
| 6593 | 6594 | cpu_ns :: RtsTime,
|
| 6594 | 6595 | elapsed_ns :: RtsTime,
|
| 6595 | 6596 | nonmoving_gc_sync_cpu_ns :: RtsTime,
|