[Git][ghc/ghc][wip/dcoutts/io-manager-tidy] Add missing updateRemembSetPushClosure in poll I/O manager
by Duncan Coutts (@dcoutts) 20 Jun '26
by Duncan Coutts (@dcoutts) 20 Jun '26
20 Jun '26
Duncan Coutts pushed to branch wip/dcoutts/io-manager-tidy at Glasgow Haskell Compiler / GHC
Commits:
649ae993 by Duncan Coutts at 2026-06-19T10:42:12+01:00
Add missing updateRemembSetPushClosure in poll I/O manager
For the non-moving GC.
- - - - -
1 changed file:
- rts/posix/Poll.c
Changes:
=====================================
rts/posix/Poll.c
=====================================
@@ -222,6 +222,13 @@ void syncIOCancelPoll(CapIOManager *iomgr, StgTSO *tso)
* status, as that is done by removeFromQueues (in the throwTo* functions).
*/
tso->block_info.closure = (StgClosure *)END_TSO_QUEUE;
+
+ /* We are in the TSO case, where the aiop was only reachable from the TSO
+ * itself, and thus it is now no longer be reachable at all.
+ */
+ IF_NONMOVING_WRITE_BARRIER_ENABLED {
+ updateRemembSetPushClosure(cap, (StgClosure *)aiop);
+ }
}
@@ -279,7 +286,6 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop)
raiseAsync(iomgr->cap, tso,
(StgClosure *)blockedOnBadFD_closure,
false, NULL);
- break;
} else {
/* We should be guaranteed that the tso is still on the same
* cap because the tso was not on the run queue of any cap and
@@ -290,6 +296,12 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop)
tso->_link = END_TSO_QUEUE;
pushOnRunQueue(iomgr->cap, tso);
}
+ /* For the TSO case, the aiop was only reachable from the TSO
+ * itself, and thus it is now no longer be reachable at all.
+ */
+ IF_NONMOVING_WRITE_BARRIER_ENABLED {
+ updateRemembSetPushClosure(cap, (StgClosure *)aiop);
+ }
break;
}
case NotifyMVar:
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/649ae99352b9a442236b805ad0162a6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/649ae99352b9a442236b805ad0162a6…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc] Pushed new branch wip/sol/json-message-ghc-9.14-backport
by Simon Hengel (@sol) 20 Jun '26
by Simon Hengel (@sol) 20 Jun '26
20 Jun '26
Simon Hengel pushed new branch wip/sol/json-message-ghc-9.14-backport at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/sol/json-message-ghc-9.14-bac…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/ipe-return-prefer-current-mod] compiler/ipe: Prefer source ticks in current module for return frames
by Ben Gamari (@bgamari) 20 Jun '26
by Ben Gamari (@bgamari) 20 Jun '26
20 Jun '26
Ben Gamari pushed to branch wip/ipe-return-prefer-current-mod at Glasgow Haskell Compiler / GHC
Commits:
e76fce94 by Ben Gamari at 2026-06-19T18:26:19-04:00
compiler/ipe: Prefer source ticks in current module for return frames
Previously we would simply attribute return frames to the nearest
enclosing tick in the calling frame. However, this will very frequently
produce unhelpful results (e.g. pointing to `(>>)` rather than the
calling function).
- - - - -
2 changed files:
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Driver/Main/Compile.hs
Changes:
=====================================
compiler/GHC/Driver/GenerateCgIPEStub.hs
=====================================
@@ -1,7 +1,9 @@
module GHC.Driver.GenerateCgIPEStub (generateCgIPEStub, lookupEstimatedTicks) where
+import Control.Applicative ((<|>))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
+import Data.Maybe (listToMaybe)
import Data.Semigroup ((<>))
import GHC.Cmm
import GHC.Cmm.CLabel (CLabel, mkAsmTempLabel)
@@ -10,6 +12,7 @@ import GHC.Cmm.Dataflow.Block (blockSplit, blockToList)
import GHC.Cmm.Dataflow.Label
import GHC.Cmm.Info.Build (emptySRT)
import GHC.Cmm.Pipeline (cmmPipeline)
+import GHC.Data.FastString (FastString, mkFastString)
import GHC.Data.Stream (liftIO, liftEff)
import qualified GHC.Data.Stream as Stream
import GHC.Driver.Env (hsc_dflags, hsc_logger)
@@ -28,9 +31,10 @@ import GHC.StgToCmm.Utils
import GHC.StgToCmm.CgUtils (CgStream)
import GHC.Types.IPE (InfoTableProvMap (provInfoTables), IpeSourceLocation)
import GHC.Types.Name.Set (NonCaffySet)
+import GHC.Types.SrcLoc (srcSpanFile)
import GHC.Types.Tickish (GenTickish (SourceNote))
import GHC.Unit.Types (Module, moduleName)
-import GHC.Unit.Module (moduleNameString)
+import GHC.Unit.Module (moduleNameString, ModLocation, ml_hs_file)
import qualified GHC.Utils.Logger as Logger
import GHC.Utils.Outputable (ppr)
import GHC.Types.Unique.DSM
@@ -257,11 +261,12 @@ generateCgIPEStub hsc_env this_mod denv (nonCaffySet, moduleLFInfos, infoTablesW
-- performance suffered considerably as a result (see #23103).
lookupEstimatedTicks
:: HscEnv
+ -> ModLocation -- ^ location of the module being compiled, for IPE provenance
-> Map CmmInfoTable (Maybe IpeSourceLocation)
-> IPEStats
-> CmmGroupSRTs
-> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
-lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
+lookupEstimatedTicks hsc_env mod_location ipes stats cmm_group_srts =
-- Pass 2: Create an entry in the IPE map for every info table listed in
-- this CmmGroupSRTs. If the info table is a stack info table and
-- -finfo-table-map-with-stack is enabled, look up its estimated source
@@ -276,19 +281,24 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
dflags = hsc_dflags hsc_env
platform = targetPlatform dflags
- -- Pass 1: Map every label meeting the conditions described in Note
- -- [Stacktraces from Info Table Provenance Entries (IPE based stack
- -- unwinding)] to the estimated source location (also as described in the
- -- aformentioned note)
+ -- Source file of the module being compiled, used to prefer current-module
+ -- source ticks for return frames. See Note [Prefer current-module source
+ -- ticks for return frames].
+ mb_src_file = mkFastString <$> ml_hs_file mod_location
+
+ -- Pass 1: Map every label meeting the conditions described in
+ -- Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
+ -- to the estimated source location (also as described in the aformentioned
+ -- note).
--
-- Note: It's important that this remains a thunk so we do not compute this
-- map if -fno-info-table-with-stack is given
labelsToSources :: Map CLabel IpeSourceLocation
labelsToSources =
if platformTablesNextToCode platform then
- foldl' labelsToSourcesWithTNTC Map.empty cmm_group_srts
+ foldl' (labelsToSourcesWithTNTC mb_src_file) Map.empty cmm_group_srts
else
- foldl' labelsToSourcesSansTNTC Map.empty cmm_group_srts
+ foldl' (labelsToSourcesSansTNTC mb_src_file) Map.empty cmm_group_srts
collectInfoTables
:: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
@@ -331,15 +341,16 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
-- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
labelsToSourcesWithTNTC
- :: Map CLabel IpeSourceLocation
+ :: Maybe FastString -- ^ source file of the module being compiled
+ -> Map CLabel IpeSourceLocation
-> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
-> Map CLabel IpeSourceLocation
-labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
+labelsToSourcesWithTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
foldl' go acc (toBlockList cmm_graph)
where
go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
go acc block =
- case (,) <$> returnFrameLabel <*> lastTickInBlock of
+ case (,) <$> returnFrameLabel <*> bestTickInBlock of
Just (clabel, src_loc) -> Map.insert clabel src_loc acc
Nothing -> acc
where
@@ -351,36 +362,135 @@ labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
(CmmCall _ (Just l) _ _ _ _) -> Just $ mkAsmTempLabel l
_ -> Nothing
- lastTickInBlock = foldr maybeTick Nothing (blockToList middleBlock)
+ -- All SourceNotes in the block, in block order.
+ -- See Note [Prefer current-module source ticks for return frames].
+ bestTickInBlock = preferThisFile mb_src_file procFallback (blockToList middleBlock)
+
+ -- Enclosing current-module note for the whole proc (its function's own
+ -- span), used when a return frame's own block has no current-module tick.
+ procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
+labelsToSourcesWithTNTC _ acc _ = acc
+
+{-
+Note [Prefer current-module source ticks for return frames]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A return frame's source location is taken from the `SourceNote`s of the block
+that *ends* in the frame's call (see
+Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding]
+above and `labelsToSourcesWithTNTC`). At `-O`, inlining means such a
+block frequently carries `SourceNote`s for inlined library glue (`>>`, `>>=`,
+`threadDelay`, ...) and the *nearest* note — the one historically chosen — is
+often a library note rather than the user's code. The resulting IPE entry then
+points at the library (its file and label; see `toCgIPE` in
+GHC.StgToCmm.InfoTableProv, which takes the file from the note's own span), so a
+backtrace of a thread blocked in such a primop shows no user frames.
+
+For a concrete example, compile (at -O1)
+
+ -- Scan.hs
+ f3 :: IO ()
+ f3 = threadDelay 1000000 >> putStrLn "done"
+
+The body of `f3` reaches the inlined `threadDelay`'s internal `delay#` in a
+block whose notes are *all* from `Conc.IO`/`Base` — the user's `Scan.hs` tick
+for `f3` sits only in the proc's entry block:
+
+ entry: -- the proc's entry block
+ //tick src<.../Base.hs:2306:5-18>
+ //tick src<Scan.hs:13:1-43> -- the enclosing `f3` span
+ //tick src<.../Conc/IO.hs:(223,1)-(235,10)>
+ ...
+ delayBlk: -- no Scan.hs note here:
+ //tick src<.../Conc/IO.hs:232:5-13>
+ //tick src<.../Base.hs:2268:1-9>
+ //tick src<.../Conc/IO.hs:(232,25)-(235,10)> -- nearest note
+ call stg_delay#(R1) returns to delayCont, args: 8, res: 8, upd: 8;
+
+Naively taking the nearest note attributes `delayCont` to `Conc/IO.hs:232`,
+i.e. an internal of `threadDelay`, rather than `f3`.
+
+To fix this we attribute a return frame's source location in the following
+preference order:
+
+ 1. the nearest tick in the frame's block whose file is that of the module
+ being compiled - the precise user call site. (When the user makes a
+ blocking call directly, e.g. `f v = takeMVar v`, such a note is present in
+ the call's block and this rule suffices; the `delayBlk` above has none.)
+ 2. failing that, the proc's *enclosing* current-module note (the outermost
+ current-module `SourceNote` in the proc, i.e. its function's own span).
+ For `f3` this is `src<Scan.hs:13:1-43>`, so `delayCont` is attributed to
+ `f3` rather than to `threadDelay`'s internals.
+ 3. failing that, the nearest note of any module (the historical behaviour).
+
+This mirrors the same-file preference the DWARF path uses in
+`GHC.Cmm.DebugBlock.bestSrcTick` and that `GHC.Stg.Debug.quickSourcePos` uses
+for closures.
+-}
- maybeTick :: CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation
- maybeTick _ s@(Just _) = s
- maybeTick (CmmTick (SourceNote span name)) Nothing = Just (span, name)
- maybeTick _ _ = Nothing
-labelsToSourcesWithTNTC acc _ = acc
+-- | Pick the 'IpeSourceLocation' to attribute to a return frame from the
+-- source-note-bearing nodes of its block (in block order).
+--
+-- See Note [Prefer current-module source ticks for return frames].
+preferThisFile :: Maybe FastString -> Maybe IpeSourceLocation -> [CmmNode O O] -> Maybe IpeSourceLocation
+preferThisFile mb_src_file procFallback nodes =
+ nearest fromThisFile <|> procFallback <|> nearest sourceNotes
+ where
+ sourceNotes = [ (span, name) | CmmTick (SourceNote span name) <- nodes ]
+ fromThisFile = case mb_src_file of
+ Just f -> filter ((== f) . srcSpanFile . fst) sourceNotes
+ Nothing -> []
+ nearest = listToMaybe . reverse
+
+-- | The outermost 'SourceNote' from the module being compiled across a proc's
+-- blocks (in 'toBlockList' order, so the entry block's note — the function's own
+-- span — comes first). Used as a fallback so inlined cross-module code is still
+-- labelled with the enclosing user function. 'Nothing' when the proc has no
+-- current-module note (e.g. when compiling the library itself).
+enclosingThisFileTick :: Maybe FastString -> [CmmBlock] -> Maybe IpeSourceLocation
+enclosingThisFileTick mb_src_file blocks =
+ listToMaybe
+ [ (span, name)
+ | b <- blocks
+ , let (_, mid, _) = blockSplit b
+ , CmmTick (SourceNote span name) <- blockToList mid
+ , Just (srcSpanFile span) == mb_src_file ]
-- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
labelsToSourcesSansTNTC
- :: Map CLabel IpeSourceLocation
+ :: Maybe FastString -- ^ source file of the module being compiled
+ -> Map CLabel IpeSourceLocation
-> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
-> Map CLabel IpeSourceLocation
-labelsToSourcesSansTNTC acc (CmmProc _ _ _ cmm_graph) =
+labelsToSourcesSansTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
foldl' go acc (toBlockList cmm_graph)
where
+ -- See 'enclosingThisFileTick'.
+ procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
+
go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
- go acc block = fst $ foldl' collectLabels (acc, Nothing) (blockToList middleBlock)
+ go acc block = fst $ foldl' collectLabels (acc, (Nothing, Nothing)) (blockToList middleBlock)
where
(_, middleBlock, _) = blockSplit block
+ -- We track the nearest preceding SourceNote from the module being
+ -- compiled and the nearest of any module, and prefer the former (then
+ -- the proc's enclosing current-module note) when attributing a return
+ -- frame. See Note [Prefer current-module source ticks for return frames].
collectLabels
- :: (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
+ :: (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
-> CmmNode O O
- -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
- collectLabels (!acc, lastTick) b =
- case (b, lastTick) of
- (CmmStore _ (CmmLit (CmmLabel l)) _, Just src_loc) ->
- (Map.insert l src_loc acc, Nothing)
- (CmmTick (SourceNote span name), _) ->
- (acc, Just (span, name))
- _ -> (acc, lastTick)
-labelsToSourcesSansTNTC acc _ = acc
+ -> (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
+ collectLabels (!acc, st@(lastThis, lastAny)) b =
+ case b of
+ CmmStore _ (CmmLit (CmmLabel l)) _ ->
+ case lastThis <|> procFallback <|> lastAny of
+ Just src_loc -> (Map.insert l src_loc acc, (Nothing, Nothing))
+ Nothing -> (acc, st)
+ CmmTick (SourceNote span name) ->
+ let tick = (span, name)
+ lastThis'
+ | Just (srcSpanFile span) == mb_src_file = Just tick
+ | otherwise = lastThis
+ in (acc, (lastThis', Just tick))
+ _ -> (acc, st)
+labelsToSourcesSansTNTC _ acc _ = acc
=====================================
compiler/GHC/Driver/Main/Compile.hs
=====================================
@@ -699,7 +699,7 @@ hscGenHardCode hsc_env cgguts mod_loc output_filename = do
_ ->
do
cmms <- {-# SCC "StgToCmm" #-}
- doCodeGen hsc_env this_mod denv tycons
+ doCodeGen hsc_env this_mod mod_loc denv tycons
cost_centre_info
stg_binds
@@ -956,14 +956,17 @@ This reduces residency towards the end of the CodeGen phase significantly
(5-10%).
-}
-doCodeGen :: HscEnv -> Module -> InfoTableProvMap -> [TyCon]
+doCodeGen :: HscEnv -> Module
+ -> ModLocation -- ^ location of the module being compiled, used to
+ -- prefer current-module IPE source locations
+ -> InfoTableProvMap -> [TyCon]
-> CollectedCCs
-> [CgStgTopBinding] -- ^ Bindings come already annotated with fvs
-> IO (CgStream CmmGroupSRTs CmmCgInfos)
-- Note we produce a 'Stream' of CmmGroups, so that the
-- backend can be run incrementally. Otherwise it generates all
-- the C-- up front, which has a significant space cost.
-doCodeGen hsc_env this_mod denv tycons
+doCodeGen hsc_env this_mod mod_location denv tycons
cost_centre_info stg_binds_w_fvs = do
let dflags = hsc_dflags hsc_env
logger = hsc_logger hsc_env
@@ -1032,7 +1035,7 @@ doCodeGen hsc_env this_mod denv tycons
-- Positions] in GHC.Stg.Debug.
(ipes', stats') <-
if (gopt Opt_InfoTableMap dflags) then
- liftIO $ lookupEstimatedTicks hsc_env ipes stats cmm_srts
+ liftIO $ lookupEstimatedTicks hsc_env mod_location ipes stats cmm_srts
else
return (ipes, stats)
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/e76fce946e90fa2cbb8dd94222715a5…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/e76fce946e90fa2cbb8dd94222715a5…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/ipe-return-prefer-current-mod] compiler/ipe: Prefer source ticks in current module for return frames
by Ben Gamari (@bgamari) 20 Jun '26
by Ben Gamari (@bgamari) 20 Jun '26
20 Jun '26
Ben Gamari pushed to branch wip/ipe-return-prefer-current-mod at Glasgow Haskell Compiler / GHC
Commits:
22b12ced by Ben Gamari at 2026-06-19T18:24:08-04:00
compiler/ipe: Prefer source ticks in current module for return frames
Previously we would simply attribute return frames to the nearest
enclosing tick in the calling frame. However, this will very frequently
produce unhelpful results (e.g. pointing to `(>>)` rather than the
calling function).
- - - - -
2 changed files:
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Driver/Main/Compile.hs
Changes:
=====================================
compiler/GHC/Driver/GenerateCgIPEStub.hs
=====================================
@@ -1,7 +1,9 @@
module GHC.Driver.GenerateCgIPEStub (generateCgIPEStub, lookupEstimatedTicks) where
+import Control.Applicative ((<|>))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
+import Data.Maybe (listToMaybe)
import Data.Semigroup ((<>))
import GHC.Cmm
import GHC.Cmm.CLabel (CLabel, mkAsmTempLabel)
@@ -10,6 +12,7 @@ import GHC.Cmm.Dataflow.Block (blockSplit, blockToList)
import GHC.Cmm.Dataflow.Label
import GHC.Cmm.Info.Build (emptySRT)
import GHC.Cmm.Pipeline (cmmPipeline)
+import GHC.Data.FastString (FastString, mkFastString)
import GHC.Data.Stream (liftIO, liftEff)
import qualified GHC.Data.Stream as Stream
import GHC.Driver.Env (hsc_dflags, hsc_logger)
@@ -28,9 +31,10 @@ import GHC.StgToCmm.Utils
import GHC.StgToCmm.CgUtils (CgStream)
import GHC.Types.IPE (InfoTableProvMap (provInfoTables), IpeSourceLocation)
import GHC.Types.Name.Set (NonCaffySet)
+import GHC.Types.SrcLoc (srcSpanFile)
import GHC.Types.Tickish (GenTickish (SourceNote))
import GHC.Unit.Types (Module, moduleName)
-import GHC.Unit.Module (moduleNameString)
+import GHC.Unit.Module (moduleNameString, ModLocation, ml_hs_file)
import qualified GHC.Utils.Logger as Logger
import GHC.Utils.Outputable (ppr)
import GHC.Types.Unique.DSM
@@ -257,11 +261,12 @@ generateCgIPEStub hsc_env this_mod denv (nonCaffySet, moduleLFInfos, infoTablesW
-- performance suffered considerably as a result (see #23103).
lookupEstimatedTicks
:: HscEnv
+ -> ModLocation -- ^ location of the module being compiled, for IPE provenance
-> Map CmmInfoTable (Maybe IpeSourceLocation)
-> IPEStats
-> CmmGroupSRTs
-> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
-lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
+lookupEstimatedTicks hsc_env mod_location ipes stats cmm_group_srts =
-- Pass 2: Create an entry in the IPE map for every info table listed in
-- this CmmGroupSRTs. If the info table is a stack info table and
-- -finfo-table-map-with-stack is enabled, look up its estimated source
@@ -276,6 +281,11 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
dflags = hsc_dflags hsc_env
platform = targetPlatform dflags
+ -- Source file of the module being compiled, used to prefer current-module
+ -- source ticks for return frames. See Note [Prefer current-module source
+ -- ticks for return frames].
+ mb_src_file = mkFastString <$> ml_hs_file mod_location
+
-- Pass 1: Map every label meeting the conditions described in Note
-- [Stacktraces from Info Table Provenance Entries (IPE based stack
-- unwinding)] to the estimated source location (also as described in the
@@ -286,9 +296,9 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
labelsToSources :: Map CLabel IpeSourceLocation
labelsToSources =
if platformTablesNextToCode platform then
- foldl' labelsToSourcesWithTNTC Map.empty cmm_group_srts
+ foldl' (labelsToSourcesWithTNTC mb_src_file) Map.empty cmm_group_srts
else
- foldl' labelsToSourcesSansTNTC Map.empty cmm_group_srts
+ foldl' (labelsToSourcesSansTNTC mb_src_file) Map.empty cmm_group_srts
collectInfoTables
:: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
@@ -331,15 +341,16 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
-- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
labelsToSourcesWithTNTC
- :: Map CLabel IpeSourceLocation
+ :: Maybe FastString -- ^ source file of the module being compiled
+ -> Map CLabel IpeSourceLocation
-> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
-> Map CLabel IpeSourceLocation
-labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
+labelsToSourcesWithTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
foldl' go acc (toBlockList cmm_graph)
where
go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
go acc block =
- case (,) <$> returnFrameLabel <*> lastTickInBlock of
+ case (,) <$> returnFrameLabel <*> bestTickInBlock of
Just (clabel, src_loc) -> Map.insert clabel src_loc acc
Nothing -> acc
where
@@ -351,36 +362,134 @@ labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
(CmmCall _ (Just l) _ _ _ _) -> Just $ mkAsmTempLabel l
_ -> Nothing
- lastTickInBlock = foldr maybeTick Nothing (blockToList middleBlock)
+ -- All SourceNotes in the block, in block order.
+ -- See Note [Prefer current-module source ticks for return frames].
+ bestTickInBlock = preferThisFile mb_src_file procFallback (blockToList middleBlock)
+
+ -- Enclosing current-module note for the whole proc (its function's own
+ -- span), used when a return frame's own block has no current-module tick.
+ procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
+labelsToSourcesWithTNTC _ acc _ = acc
+
+{-
+Note [Prefer current-module source ticks for return frames]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A return frame's source location is taken from the `SourceNote`s of the block
+that *ends* in the frame's call (see Note [Stacktraces from Info Table Provenance
+Entries...] above and `labelsToSourcesWithTNTC`). At `-O`, inlining means such a
+block frequently carries `SourceNote`s for inlined library glue (`>>`, `>>=`,
+`threadDelay`, ...) and the *nearest* note — the one historically chosen — is
+often a library note rather than the user's code. The resulting IPE entry then
+points at the library (its file and label; see `toCgIPE` in
+GHC.StgToCmm.InfoTableProv, which takes the file from the note's own span), so a
+backtrace of a thread blocked in such a primop shows no user frames.
+
+For a concrete example, compile (at -O1)
+
+ -- Scan.hs
+ f3 :: IO ()
+ f3 = threadDelay 1000000 >> putStrLn "done"
+
+The body of `f3` reaches the inlined `threadDelay`'s internal `delay#` in a
+block whose notes are *all* from `Conc.IO`/`Base` — the user's `Scan.hs` tick
+for `f3` sits only in the proc's entry block:
+
+ entry: -- the proc's entry block
+ //tick src<.../Base.hs:2306:5-18>
+ //tick src<Scan.hs:13:1-43> -- the enclosing `f3` span
+ //tick src<.../Conc/IO.hs:(223,1)-(235,10)>
+ ...
+ delayBlk: -- no Scan.hs note here:
+ //tick src<.../Conc/IO.hs:232:5-13>
+ //tick src<.../Base.hs:2268:1-9>
+ //tick src<.../Conc/IO.hs:(232,25)-(235,10)> -- nearest note
+ call stg_delay#(R1) returns to delayCont, args: 8, res: 8, upd: 8;
+
+Naively taking the nearest note attributes `delayCont` to `Conc/IO.hs:232`,
+i.e. an internal of `threadDelay`, rather than `f3`.
+
+To fix this we attribute a return frame's source location in the following
+preference order:
+
+ 1. the nearest tick in the frame's block whose file is that of the module
+ being compiled - the precise user call site. (When the user makes a
+ blocking call directly, e.g. `f v = takeMVar v`, such a note is present in
+ the call's block and this rule suffices; the `delayBlk` above has none.)
+ 2. failing that, the proc's *enclosing* current-module note (the outermost
+ current-module `SourceNote` in the proc, i.e. its function's own span).
+ For `f3` this is `src<Scan.hs:13:1-43>`, so `delayCont` is attributed to
+ `f3` rather than to `threadDelay`'s internals.
+ 3. failing that, the nearest note of any module (the historical behaviour).
+
+This mirrors the same-file preference the DWARF path uses in
+`GHC.Cmm.DebugBlock.bestSrcTick` and that `GHC.Stg.Debug.quickSourcePos` uses
+for closures.
+-}
- maybeTick :: CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation
- maybeTick _ s@(Just _) = s
- maybeTick (CmmTick (SourceNote span name)) Nothing = Just (span, name)
- maybeTick _ _ = Nothing
-labelsToSourcesWithTNTC acc _ = acc
+-- | Pick the 'IpeSourceLocation' to attribute to a return frame from the
+-- source-note-bearing nodes of its block (in block order).
+--
+-- See Note [Prefer current-module source ticks for return frames].
+preferThisFile :: Maybe FastString -> Maybe IpeSourceLocation -> [CmmNode O O] -> Maybe IpeSourceLocation
+preferThisFile mb_src_file procFallback nodes =
+ nearest fromThisFile <|> procFallback <|> nearest sourceNotes
+ where
+ sourceNotes = [ (span, name) | CmmTick (SourceNote span name) <- nodes ]
+ fromThisFile = case mb_src_file of
+ Just f -> filter ((== f) . srcSpanFile . fst) sourceNotes
+ Nothing -> []
+ nearest = listToMaybe . reverse
+
+-- | The outermost 'SourceNote' from the module being compiled across a proc's
+-- blocks (in 'toBlockList' order, so the entry block's note — the function's own
+-- span — comes first). Used as a fallback so inlined cross-module code is still
+-- labelled with the enclosing user function. 'Nothing' when the proc has no
+-- current-module note (e.g. when compiling the library itself).
+enclosingThisFileTick :: Maybe FastString -> [CmmBlock] -> Maybe IpeSourceLocation
+enclosingThisFileTick mb_src_file blocks =
+ listToMaybe
+ [ (span, name)
+ | b <- blocks
+ , let (_, mid, _) = blockSplit b
+ , CmmTick (SourceNote span name) <- blockToList mid
+ , Just (srcSpanFile span) == mb_src_file ]
-- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
labelsToSourcesSansTNTC
- :: Map CLabel IpeSourceLocation
+ :: Maybe FastString -- ^ source file of the module being compiled
+ -> Map CLabel IpeSourceLocation
-> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
-> Map CLabel IpeSourceLocation
-labelsToSourcesSansTNTC acc (CmmProc _ _ _ cmm_graph) =
+labelsToSourcesSansTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
foldl' go acc (toBlockList cmm_graph)
where
+ -- See 'enclosingThisFileTick'.
+ procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
+
go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
- go acc block = fst $ foldl' collectLabels (acc, Nothing) (blockToList middleBlock)
+ go acc block = fst $ foldl' collectLabels (acc, (Nothing, Nothing)) (blockToList middleBlock)
where
(_, middleBlock, _) = blockSplit block
+ -- We track the nearest preceding SourceNote from the module being
+ -- compiled and the nearest of any module, and prefer the former (then
+ -- the proc's enclosing current-module note) when attributing a return
+ -- frame. See Note [Prefer current-module source ticks for return frames].
collectLabels
- :: (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
+ :: (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
-> CmmNode O O
- -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
- collectLabels (!acc, lastTick) b =
- case (b, lastTick) of
- (CmmStore _ (CmmLit (CmmLabel l)) _, Just src_loc) ->
- (Map.insert l src_loc acc, Nothing)
- (CmmTick (SourceNote span name), _) ->
- (acc, Just (span, name))
- _ -> (acc, lastTick)
-labelsToSourcesSansTNTC acc _ = acc
+ -> (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
+ collectLabels (!acc, st@(lastThis, lastAny)) b =
+ case b of
+ CmmStore _ (CmmLit (CmmLabel l)) _ ->
+ case lastThis <|> procFallback <|> lastAny of
+ Just src_loc -> (Map.insert l src_loc acc, (Nothing, Nothing))
+ Nothing -> (acc, st)
+ CmmTick (SourceNote span name) ->
+ let tick = (span, name)
+ lastThis'
+ | Just (srcSpanFile span) == mb_src_file = Just tick
+ | otherwise = lastThis
+ in (acc, (lastThis', Just tick))
+ _ -> (acc, st)
+labelsToSourcesSansTNTC _ acc _ = acc
=====================================
compiler/GHC/Driver/Main/Compile.hs
=====================================
@@ -699,7 +699,7 @@ hscGenHardCode hsc_env cgguts mod_loc output_filename = do
_ ->
do
cmms <- {-# SCC "StgToCmm" #-}
- doCodeGen hsc_env this_mod denv tycons
+ doCodeGen hsc_env this_mod mod_loc denv tycons
cost_centre_info
stg_binds
@@ -956,14 +956,17 @@ This reduces residency towards the end of the CodeGen phase significantly
(5-10%).
-}
-doCodeGen :: HscEnv -> Module -> InfoTableProvMap -> [TyCon]
+doCodeGen :: HscEnv -> Module
+ -> ModLocation -- ^ location of the module being compiled, used to
+ -- prefer current-module IPE source locations
+ -> InfoTableProvMap -> [TyCon]
-> CollectedCCs
-> [CgStgTopBinding] -- ^ Bindings come already annotated with fvs
-> IO (CgStream CmmGroupSRTs CmmCgInfos)
-- Note we produce a 'Stream' of CmmGroups, so that the
-- backend can be run incrementally. Otherwise it generates all
-- the C-- up front, which has a significant space cost.
-doCodeGen hsc_env this_mod denv tycons
+doCodeGen hsc_env this_mod mod_location denv tycons
cost_centre_info stg_binds_w_fvs = do
let dflags = hsc_dflags hsc_env
logger = hsc_logger hsc_env
@@ -1032,7 +1035,7 @@ doCodeGen hsc_env this_mod denv tycons
-- Positions] in GHC.Stg.Debug.
(ipes', stats') <-
if (gopt Opt_InfoTableMap dflags) then
- liftIO $ lookupEstimatedTicks hsc_env ipes stats cmm_srts
+ liftIO $ lookupEstimatedTicks hsc_env mod_location ipes stats cmm_srts
else
return (ipes, stats)
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/22b12cedff38c154d0959199da4e5ab…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/22b12cedff38c154d0959199da4e5ab…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/ipe-return-prefer-current-mod] compiler/ipe: Prefer source ticks in current module for return frames
by Ben Gamari (@bgamari) 20 Jun '26
by Ben Gamari (@bgamari) 20 Jun '26
20 Jun '26
Ben Gamari pushed to branch wip/ipe-return-prefer-current-mod at Glasgow Haskell Compiler / GHC
Commits:
ab46ad97 by Ben Gamari at 2026-06-19T18:08:40-04:00
compiler/ipe: Prefer source ticks in current module for return frames
Previously we would simply attribute return frames to the nearest
enclosing tick in the calling frame. However, this will very frequently
produce unhelpful results (e.g. pointing to `(>>)` rather than the
calling function).
- - - - -
2 changed files:
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Driver/Main/Compile.hs
Changes:
=====================================
compiler/GHC/Driver/GenerateCgIPEStub.hs
=====================================
@@ -1,7 +1,9 @@
module GHC.Driver.GenerateCgIPEStub (generateCgIPEStub, lookupEstimatedTicks) where
+import Control.Applicative ((<|>))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
+import Data.Maybe (listToMaybe)
import Data.Semigroup ((<>))
import GHC.Cmm
import GHC.Cmm.CLabel (CLabel, mkAsmTempLabel)
@@ -10,6 +12,7 @@ import GHC.Cmm.Dataflow.Block (blockSplit, blockToList)
import GHC.Cmm.Dataflow.Label
import GHC.Cmm.Info.Build (emptySRT)
import GHC.Cmm.Pipeline (cmmPipeline)
+import GHC.Data.FastString (FastString, mkFastString)
import GHC.Data.Stream (liftIO, liftEff)
import qualified GHC.Data.Stream as Stream
import GHC.Driver.Env (hsc_dflags, hsc_logger)
@@ -28,9 +31,10 @@ import GHC.StgToCmm.Utils
import GHC.StgToCmm.CgUtils (CgStream)
import GHC.Types.IPE (InfoTableProvMap (provInfoTables), IpeSourceLocation)
import GHC.Types.Name.Set (NonCaffySet)
+import GHC.Types.SrcLoc (srcSpanFile)
import GHC.Types.Tickish (GenTickish (SourceNote))
import GHC.Unit.Types (Module, moduleName)
-import GHC.Unit.Module (moduleNameString)
+import GHC.Unit.Module (moduleNameString, ModLocation, ml_hs_file)
import qualified GHC.Utils.Logger as Logger
import GHC.Utils.Outputable (ppr)
import GHC.Types.Unique.DSM
@@ -186,6 +190,59 @@ Given a `CmmGraph`:
location.
See `labelsToSourcesSansTNTC` for the implementation of this algorithm.
+
+Note [Prefer current-module source ticks for return frames]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A return frame's source location is taken from the `SourceNote`s of the block
+that *ends* in the frame's call (see Note [Stacktraces from Info Table Provenance
+Entries...] above and `labelsToSourcesWithTNTC`). At `-O`, inlining means such a
+block frequently carries `SourceNote`s for inlined library glue (`>>`, `>>=`,
+`threadDelay`, ...) and the *nearest* note — the one historically chosen — is
+often a library note rather than the user's code. The resulting IPE entry then
+points at the library (its file and label; see `toCgIPE` in
+GHC.StgToCmm.InfoTableProv, which takes the file from the note's own span), so a
+backtrace of a thread blocked in such a primop shows no user frames.
+
+For a concrete example, compile (at -O1)
+
+ -- Scan.hs
+ f3 :: IO ()
+ f3 = threadDelay 1000000 >> putStrLn "done"
+
+The body of `f3` reaches the inlined `threadDelay`'s internal `delay#` in a
+block whose notes are *all* from `Conc.IO`/`Base` — the user's `Scan.hs` tick
+for `f3` sits only in the proc's entry block:
+
+ entry: -- the proc's entry block
+ //tick src<.../Base.hs:2305:5-18>
+ //tick src<Scan.hs:13:1-43> -- the enclosing `f3` span
+ //tick src<.../Conc/IO.hs:(223,1)-(235,10)>
+ ...
+ delayBlk: -- no Scan.hs note here:
+ //tick src<.../Conc/IO.hs:232:5-13>
+ //tick src<.../Base.hs:2268:1-9>
+ //tick src<.../Conc/IO.hs:(232,25)-(235,10)> -- nearest note
+ call stg_delay#(R1) returns to delayCont, args: 8, res: 8, upd: 8;
+
+Naively taking the nearest note attributes `delayCont` to `Conc/IO.hs:232`,
+i.e. an internal of `threadDelay`, rather than `f3`.
+
+To fix this we attribute a return frame's source location in the following
+preference order:
+
+ 1. the nearest tick in the frame's block whose file is that of the module
+ being compiled - the precise user call site. (When the user makes a
+ blocking call directly, e.g. `f v = takeMVar v`, such a note is present in
+ the call's block and this rule suffices; the `delayBlk` above has none.)
+ 2. failing that, the proc's *enclosing* current-module note (the outermost
+ current-module `SourceNote` in the proc, i.e. its function's own span).
+ For `f3` this is `src<Scan.hs:13:1-43>`, so `delayCont` is attributed to
+ `f3` rather than to `threadDelay`'s internals.
+ 3. failing that, the nearest note of any module (the historical behaviour).
+
+This mirrors the same-file preference the DWARF path uses in
+`GHC.Cmm.DebugBlock.bestSrcTick` and that `GHC.Stg.Debug.quickSourcePos` uses
+for closures.
-}
generateCgIPEStub
@@ -257,11 +314,12 @@ generateCgIPEStub hsc_env this_mod denv (nonCaffySet, moduleLFInfos, infoTablesW
-- performance suffered considerably as a result (see #23103).
lookupEstimatedTicks
:: HscEnv
+ -> ModLocation -- ^ location of the module being compiled, for IPE provenance
-> Map CmmInfoTable (Maybe IpeSourceLocation)
-> IPEStats
-> CmmGroupSRTs
-> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
-lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
+lookupEstimatedTicks hsc_env mod_location ipes stats cmm_group_srts =
-- Pass 2: Create an entry in the IPE map for every info table listed in
-- this CmmGroupSRTs. If the info table is a stack info table and
-- -finfo-table-map-with-stack is enabled, look up its estimated source
@@ -276,6 +334,11 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
dflags = hsc_dflags hsc_env
platform = targetPlatform dflags
+ -- Source file of the module being compiled, used to prefer current-module
+ -- source ticks for return frames. See Note [Prefer current-module source
+ -- ticks for return frames].
+ mb_src_file = mkFastString <$> ml_hs_file mod_location
+
-- Pass 1: Map every label meeting the conditions described in Note
-- [Stacktraces from Info Table Provenance Entries (IPE based stack
-- unwinding)] to the estimated source location (also as described in the
@@ -286,9 +349,9 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
labelsToSources :: Map CLabel IpeSourceLocation
labelsToSources =
if platformTablesNextToCode platform then
- foldl' labelsToSourcesWithTNTC Map.empty cmm_group_srts
+ foldl' (labelsToSourcesWithTNTC mb_src_file) Map.empty cmm_group_srts
else
- foldl' labelsToSourcesSansTNTC Map.empty cmm_group_srts
+ foldl' (labelsToSourcesSansTNTC mb_src_file) Map.empty cmm_group_srts
collectInfoTables
:: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
@@ -331,15 +394,16 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
-- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
labelsToSourcesWithTNTC
- :: Map CLabel IpeSourceLocation
+ :: Maybe FastString -- ^ source file of the module being compiled
+ -> Map CLabel IpeSourceLocation
-> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
-> Map CLabel IpeSourceLocation
-labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
+labelsToSourcesWithTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
foldl' go acc (toBlockList cmm_graph)
where
go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
go acc block =
- case (,) <$> returnFrameLabel <*> lastTickInBlock of
+ case (,) <$> returnFrameLabel <*> bestTickInBlock of
Just (clabel, src_loc) -> Map.insert clabel src_loc acc
Nothing -> acc
where
@@ -351,36 +415,80 @@ labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
(CmmCall _ (Just l) _ _ _ _) -> Just $ mkAsmTempLabel l
_ -> Nothing
- lastTickInBlock = foldr maybeTick Nothing (blockToList middleBlock)
+ -- All SourceNotes in the block, in block order.
+ -- See Note [Prefer current-module source ticks for return frames].
+ bestTickInBlock = preferThisFile mb_src_file procFallback (blockToList middleBlock)
+
+ -- Enclosing current-module note for the whole proc (its function's own
+ -- span), used when a return frame's own block has no current-module tick.
+ procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
+labelsToSourcesWithTNTC _ acc _ = acc
- maybeTick :: CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation
- maybeTick _ s@(Just _) = s
- maybeTick (CmmTick (SourceNote span name)) Nothing = Just (span, name)
- maybeTick _ _ = Nothing
-labelsToSourcesWithTNTC acc _ = acc
+-- | Pick the 'IpeSourceLocation' to attribute to a return frame from the
+-- source-note-bearing nodes of its block (in block order).
+--
+-- See Note [Prefer current-module source ticks for return frames].
+preferThisFile :: Maybe FastString -> Maybe IpeSourceLocation -> [CmmNode O O] -> Maybe IpeSourceLocation
+preferThisFile mb_src_file procFallback nodes =
+ nearest fromThisFile <|> procFallback <|> nearest sourceNotes
+ where
+ sourceNotes = [ (span, name) | CmmTick (SourceNote span name) <- nodes ]
+ fromThisFile = case mb_src_file of
+ Just f -> filter ((== f) . srcSpanFile . fst) sourceNotes
+ Nothing -> []
+ nearest = listToMaybe . reverse
+
+-- | The outermost 'SourceNote' from the module being compiled across a proc's
+-- blocks (in 'toBlockList' order, so the entry block's note — the function's own
+-- span — comes first). Used as a fallback so inlined cross-module code is still
+-- labelled with the enclosing user function. 'Nothing' when the proc has no
+-- current-module note (e.g. when compiling the library itself).
+enclosingThisFileTick :: Maybe FastString -> [CmmBlock] -> Maybe IpeSourceLocation
+enclosingThisFileTick mb_src_file blocks =
+ listToMaybe
+ [ (span, name)
+ | b <- blocks
+ , let (_, mid, _) = blockSplit b
+ , CmmTick (SourceNote span name) <- blockToList mid
+ , Just (srcSpanFile span) == mb_src_file ]
-- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
labelsToSourcesSansTNTC
- :: Map CLabel IpeSourceLocation
+ :: Maybe FastString -- ^ source file of the module being compiled
+ -> Map CLabel IpeSourceLocation
-> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
-> Map CLabel IpeSourceLocation
-labelsToSourcesSansTNTC acc (CmmProc _ _ _ cmm_graph) =
+labelsToSourcesSansTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
foldl' go acc (toBlockList cmm_graph)
where
+ -- See 'enclosingThisFileTick'.
+ procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
+
go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
- go acc block = fst $ foldl' collectLabels (acc, Nothing) (blockToList middleBlock)
+ go acc block = fst $ foldl' collectLabels (acc, (Nothing, Nothing)) (blockToList middleBlock)
where
(_, middleBlock, _) = blockSplit block
+ -- We track the nearest preceding SourceNote from the module being
+ -- compiled and the nearest of any module, and prefer the former (then
+ -- the proc's enclosing current-module note) when attributing a return
+ -- frame. See 'preferThisFile' and
+ -- Note [Prefer current-module source ticks for return frames].
collectLabels
- :: (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
+ :: (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
-> CmmNode O O
- -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
- collectLabels (!acc, lastTick) b =
- case (b, lastTick) of
- (CmmStore _ (CmmLit (CmmLabel l)) _, Just src_loc) ->
- (Map.insert l src_loc acc, Nothing)
- (CmmTick (SourceNote span name), _) ->
- (acc, Just (span, name))
- _ -> (acc, lastTick)
-labelsToSourcesSansTNTC acc _ = acc
+ -> (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
+ collectLabels (!acc, st@(lastThis, lastAny)) b =
+ case b of
+ CmmStore _ (CmmLit (CmmLabel l)) _ ->
+ case lastThis <|> procFallback <|> lastAny of
+ Just src_loc -> (Map.insert l src_loc acc, (Nothing, Nothing))
+ Nothing -> (acc, st)
+ CmmTick (SourceNote span name) ->
+ let tick = (span, name)
+ lastThis'
+ | Just (srcSpanFile span) == mb_src_file = Just tick
+ | otherwise = lastThis
+ in (acc, (lastThis', Just tick))
+ _ -> (acc, st)
+labelsToSourcesSansTNTC _ acc _ = acc
=====================================
compiler/GHC/Driver/Main/Compile.hs
=====================================
@@ -699,7 +699,7 @@ hscGenHardCode hsc_env cgguts mod_loc output_filename = do
_ ->
do
cmms <- {-# SCC "StgToCmm" #-}
- doCodeGen hsc_env this_mod denv tycons
+ doCodeGen hsc_env this_mod mod_loc denv tycons
cost_centre_info
stg_binds
@@ -956,14 +956,17 @@ This reduces residency towards the end of the CodeGen phase significantly
(5-10%).
-}
-doCodeGen :: HscEnv -> Module -> InfoTableProvMap -> [TyCon]
+doCodeGen :: HscEnv -> Module
+ -> ModLocation -- ^ location of the module being compiled, used to
+ -- prefer current-module IPE source locations
+ -> InfoTableProvMap -> [TyCon]
-> CollectedCCs
-> [CgStgTopBinding] -- ^ Bindings come already annotated with fvs
-> IO (CgStream CmmGroupSRTs CmmCgInfos)
-- Note we produce a 'Stream' of CmmGroups, so that the
-- backend can be run incrementally. Otherwise it generates all
-- the C-- up front, which has a significant space cost.
-doCodeGen hsc_env this_mod denv tycons
+doCodeGen hsc_env this_mod mod_location denv tycons
cost_centre_info stg_binds_w_fvs = do
let dflags = hsc_dflags hsc_env
logger = hsc_logger hsc_env
@@ -1032,7 +1035,7 @@ doCodeGen hsc_env this_mod denv tycons
-- Positions] in GHC.Stg.Debug.
(ipes', stats') <-
if (gopt Opt_InfoTableMap dflags) then
- liftIO $ lookupEstimatedTicks hsc_env ipes stats cmm_srts
+ liftIO $ lookupEstimatedTicks hsc_env mod_location ipes stats cmm_srts
else
return (ipes, stats)
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ab46ad977c7ce6090523033597de082…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ab46ad977c7ce6090523033597de082…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/ipe-return-prefer-current-mod] compiler/ipe: Prefer source ticks in current module for return frames
by Ben Gamari (@bgamari) 19 Jun '26
by Ben Gamari (@bgamari) 19 Jun '26
19 Jun '26
Ben Gamari pushed to branch wip/ipe-return-prefer-current-mod at Glasgow Haskell Compiler / GHC
Commits:
33e82215 by Ben Gamari at 2026-06-19T17:46:43-04:00
compiler/ipe: Prefer source ticks in current module for return frames
Previously we would simply attribute return frames to the nearest
enclosing tick in the calling frame. However, this will very frequently
produce unhelpful results (e.g. pointing to `(>>)` rather than the
calling function).
- - - - -
2 changed files:
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Driver/Main/Compile.hs
Changes:
=====================================
compiler/GHC/Driver/GenerateCgIPEStub.hs
=====================================
@@ -1,7 +1,9 @@
module GHC.Driver.GenerateCgIPEStub (generateCgIPEStub, lookupEstimatedTicks) where
+import Control.Applicative ((<|>))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
+import Data.Maybe (listToMaybe)
import Data.Semigroup ((<>))
import GHC.Cmm
import GHC.Cmm.CLabel (CLabel, mkAsmTempLabel)
@@ -10,6 +12,7 @@ import GHC.Cmm.Dataflow.Block (blockSplit, blockToList)
import GHC.Cmm.Dataflow.Label
import GHC.Cmm.Info.Build (emptySRT)
import GHC.Cmm.Pipeline (cmmPipeline)
+import GHC.Data.FastString (FastString, mkFastString)
import GHC.Data.Stream (liftIO, liftEff)
import qualified GHC.Data.Stream as Stream
import GHC.Driver.Env (hsc_dflags, hsc_logger)
@@ -28,9 +31,10 @@ import GHC.StgToCmm.Utils
import GHC.StgToCmm.CgUtils (CgStream)
import GHC.Types.IPE (InfoTableProvMap (provInfoTables), IpeSourceLocation)
import GHC.Types.Name.Set (NonCaffySet)
+import GHC.Types.SrcLoc (srcSpanFile)
import GHC.Types.Tickish (GenTickish (SourceNote))
import GHC.Unit.Types (Module, moduleName)
-import GHC.Unit.Module (moduleNameString)
+import GHC.Unit.Module (moduleNameString, ModLocation, ml_hs_file)
import qualified GHC.Utils.Logger as Logger
import GHC.Utils.Outputable (ppr)
import GHC.Types.Unique.DSM
@@ -186,6 +190,60 @@ Given a `CmmGraph`:
location.
See `labelsToSourcesSansTNTC` for the implementation of this algorithm.
+
+Note [Prefer current-module source ticks for return frames]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A block may carry several nested `SourceNote`s before its terminating call.
+For example, when `>>`/`>>=` (and their continuations) are inlined into a user
+`do`-block at `-O`, the case continuation of a blocking primop like `takeMVar#`
+is wrapped in notes for both the combinator's definition (e.g.
+`GHC.Internal.Base.thenIO`, `Base.hs`) and the user's call site (the `do`
+statement). Naively taking the *nearest* note attributes the return frame to the
+library combinator, so IPE/`-finfo-table-map` backtraces of a thread blocked in
+such a primop show no user frames at all.
+
+Concretely, when compiling (at -O1)
+
+ -- Main.hs
+ main = do { v <- newEmptyMVar; takeMVar v; putStrLn "world" }
+
+the proc for `main` begins with `main`'s own span and reaches `takeMVar#`
+in a block carrying only the inlined `>>` combinator's ticks, while the next
+tick of `main` itself lands in the *continuation* block:
+
+ cEntry:
+ //tick src<Main.hs:(2,1)-(2,58)> -- (Tick 2) the enclosing `main` span
+ ... newEmptyMVar# etc. ...
+ cBlk:
+ //tick src<.../Base.hs:2336:37> -- only library ticks in this block:
+ //tick src<.../Base.hs:2336:67-71> -- the inlined `>>` (`thenIO`)
+ call stg_takeMVar#(R1) returns to cCont, args: 8, res: 8, upd: 8;
+ cCont: -- the takeMVar continuation body
+ //tick src<.../Base.hs:2336:60-71>
+ //tick src<Main.hs:2:34-43> -- (Tick 1) the user tick, in a
+ -- *sibling* block
+
+A return frame is attributed to the ticks of the block that *ends* in its call
+(see `labelsToSourcesWithTNTC`). So `cCont` is sourced from `cBlk`, which has no
+`Main.hs` tick at all. This is unhelpful to the user.
+
+To fix this we attribute a return frame's source location in the following
+preference order:
+
+ 1. the nearest tick in frame's block whose file is that of the module being
+ compiled — the precise user call site. `cBlk` has none.
+ 2. failing that, the proc's *enclosing* current-module note (its function's own
+ span, i.e. the outermost user `SourceNote` in the proc) — here
+ `src<Main.hs:(2,1)-(2,58)>`, named `main`. Without this, `cCont` would
+ take the nearest note `Base.hs:2336:67-71` (named `thenIO`) and, due to
+ rule (3) below, attribute it to the module being compiled. This would result
+ in a reference to `Main.thenIO`, a binding which does not exist.
+ does not exist. In the example above, (T2) labels it `Main.main` instead.
+ 3. failing that, the nearest note of any module.
+
+This mirrors the same-file preference the DWARF path uses in
+`GHC.Cmm.DebugBlock.bestSrcTick` and that `GHC.Stg.Debug.quickSourcePos` uses for
+closures.
-}
generateCgIPEStub
@@ -257,11 +315,12 @@ generateCgIPEStub hsc_env this_mod denv (nonCaffySet, moduleLFInfos, infoTablesW
-- performance suffered considerably as a result (see #23103).
lookupEstimatedTicks
:: HscEnv
+ -> ModLocation -- ^ location of the module being compiled, for IPE provenance
-> Map CmmInfoTable (Maybe IpeSourceLocation)
-> IPEStats
-> CmmGroupSRTs
-> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
-lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
+lookupEstimatedTicks hsc_env mod_location ipes stats cmm_group_srts =
-- Pass 2: Create an entry in the IPE map for every info table listed in
-- this CmmGroupSRTs. If the info table is a stack info table and
-- -finfo-table-map-with-stack is enabled, look up its estimated source
@@ -276,6 +335,11 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
dflags = hsc_dflags hsc_env
platform = targetPlatform dflags
+ -- Source file of the module being compiled, used to prefer current-module
+ -- source ticks for return frames. See Note [Prefer current-module source
+ -- ticks for return frames].
+ mb_src_file = mkFastString <$> ml_hs_file mod_location
+
-- Pass 1: Map every label meeting the conditions described in Note
-- [Stacktraces from Info Table Provenance Entries (IPE based stack
-- unwinding)] to the estimated source location (also as described in the
@@ -286,9 +350,9 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
labelsToSources :: Map CLabel IpeSourceLocation
labelsToSources =
if platformTablesNextToCode platform then
- foldl' labelsToSourcesWithTNTC Map.empty cmm_group_srts
+ foldl' (labelsToSourcesWithTNTC mb_src_file) Map.empty cmm_group_srts
else
- foldl' labelsToSourcesSansTNTC Map.empty cmm_group_srts
+ foldl' (labelsToSourcesSansTNTC mb_src_file) Map.empty cmm_group_srts
collectInfoTables
:: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
@@ -331,15 +395,16 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
-- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
labelsToSourcesWithTNTC
- :: Map CLabel IpeSourceLocation
+ :: Maybe FastString -- ^ source file of the module being compiled
+ -> Map CLabel IpeSourceLocation
-> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
-> Map CLabel IpeSourceLocation
-labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
+labelsToSourcesWithTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
foldl' go acc (toBlockList cmm_graph)
where
go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
go acc block =
- case (,) <$> returnFrameLabel <*> lastTickInBlock of
+ case (,) <$> returnFrameLabel <*> bestTickInBlock of
Just (clabel, src_loc) -> Map.insert clabel src_loc acc
Nothing -> acc
where
@@ -351,36 +416,84 @@ labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
(CmmCall _ (Just l) _ _ _ _) -> Just $ mkAsmTempLabel l
_ -> Nothing
- lastTickInBlock = foldr maybeTick Nothing (blockToList middleBlock)
-
- maybeTick :: CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation
- maybeTick _ s@(Just _) = s
- maybeTick (CmmTick (SourceNote span name)) Nothing = Just (span, name)
- maybeTick _ _ = Nothing
-labelsToSourcesWithTNTC acc _ = acc
+ -- All SourceNotes in the block, in block order (so the last is the one
+ -- nearest the terminating call). Prefer a note from the module being
+ -- compiled, so that e.g. continuations of inlined library combinators
+ -- (>>, >>=) point at the user's call site rather than the combinator's
+ -- definition. See Note [Prefer current-module source ticks for return frames].
+ -- This mirrors GHC.Cmm.DebugBlock.bestSrcTick.
+ bestTickInBlock = preferThisFile mb_src_file procFallback (blockToList middleBlock)
+
+ -- Enclosing current-module note for the whole proc (its function's own
+ -- span), used when a return frame's own block has no current-module tick.
+ procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
+labelsToSourcesWithTNTC _ acc _ = acc
+
+-- | Pick the 'IpeSourceLocation' to attribute to a return frame from the
+-- source-note-bearing nodes of its block (in block order).
+--
+-- See Note [Prefer current-module source ticks for return frames].
+preferThisFile :: Maybe FastString -> Maybe IpeSourceLocation -> [CmmNode O O] -> Maybe IpeSourceLocation
+preferThisFile mb_src_file procFallback nodes =
+ nearest fromThisFile <|> procFallback <|> nearest sourceNotes
+ where
+ sourceNotes = [ (span, name) | CmmTick (SourceNote span name) <- nodes ]
+ fromThisFile = case mb_src_file of
+ Just f -> filter ((== f) . srcSpanFile . fst) sourceNotes
+ Nothing -> []
+ nearest = listToMaybe . reverse
+
+-- | The outermost 'SourceNote' from the module being compiled across a proc's
+-- blocks (in 'toBlockList' order, so the entry block's note — the function's own
+-- span — comes first). Used as a fallback so inlined cross-module code is still
+-- labelled with the enclosing user function. 'Nothing' when the proc has no
+-- current-module note (e.g. when compiling the library itself).
+enclosingThisFileTick :: Maybe FastString -> [CmmBlock] -> Maybe IpeSourceLocation
+enclosingThisFileTick mb_src_file blocks =
+ listToMaybe
+ [ (span, name)
+ | b <- blocks
+ , let (_, mid, _) = blockSplit b
+ , CmmTick (SourceNote span name) <- blockToList mid
+ , Just (srcSpanFile span) == mb_src_file ]
-- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
labelsToSourcesSansTNTC
- :: Map CLabel IpeSourceLocation
+ :: Maybe FastString -- ^ source file of the module being compiled
+ -> Map CLabel IpeSourceLocation
-> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
-> Map CLabel IpeSourceLocation
-labelsToSourcesSansTNTC acc (CmmProc _ _ _ cmm_graph) =
+labelsToSourcesSansTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
foldl' go acc (toBlockList cmm_graph)
where
+ -- See 'enclosingThisFileTick'.
+ procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
+
go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
- go acc block = fst $ foldl' collectLabels (acc, Nothing) (blockToList middleBlock)
+ go acc block = fst $ foldl' collectLabels (acc, (Nothing, Nothing)) (blockToList middleBlock)
where
(_, middleBlock, _) = blockSplit block
+ -- We track the nearest preceding SourceNote from the module being
+ -- compiled and the nearest of any module, and prefer the former (then
+ -- the proc's enclosing current-module note) when attributing a return
+ -- frame. See 'preferThisFile' and
+ -- Note [Prefer current-module source ticks for return frames].
collectLabels
- :: (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
+ :: (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
-> CmmNode O O
- -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
- collectLabels (!acc, lastTick) b =
- case (b, lastTick) of
- (CmmStore _ (CmmLit (CmmLabel l)) _, Just src_loc) ->
- (Map.insert l src_loc acc, Nothing)
- (CmmTick (SourceNote span name), _) ->
- (acc, Just (span, name))
- _ -> (acc, lastTick)
-labelsToSourcesSansTNTC acc _ = acc
+ -> (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
+ collectLabels (!acc, st@(lastThis, lastAny)) b =
+ case b of
+ CmmStore _ (CmmLit (CmmLabel l)) _ ->
+ case lastThis <|> procFallback <|> lastAny of
+ Just src_loc -> (Map.insert l src_loc acc, (Nothing, Nothing))
+ Nothing -> (acc, st)
+ CmmTick (SourceNote span name) ->
+ let tick = (span, name)
+ lastThis'
+ | Just (srcSpanFile span) == mb_src_file = Just tick
+ | otherwise = lastThis
+ in (acc, (lastThis', Just tick))
+ _ -> (acc, st)
+labelsToSourcesSansTNTC _ acc _ = acc
=====================================
compiler/GHC/Driver/Main/Compile.hs
=====================================
@@ -699,7 +699,7 @@ hscGenHardCode hsc_env cgguts mod_loc output_filename = do
_ ->
do
cmms <- {-# SCC "StgToCmm" #-}
- doCodeGen hsc_env this_mod denv tycons
+ doCodeGen hsc_env this_mod mod_loc denv tycons
cost_centre_info
stg_binds
@@ -956,14 +956,17 @@ This reduces residency towards the end of the CodeGen phase significantly
(5-10%).
-}
-doCodeGen :: HscEnv -> Module -> InfoTableProvMap -> [TyCon]
+doCodeGen :: HscEnv -> Module
+ -> ModLocation -- ^ location of the module being compiled, used to
+ -- prefer current-module IPE source locations
+ -> InfoTableProvMap -> [TyCon]
-> CollectedCCs
-> [CgStgTopBinding] -- ^ Bindings come already annotated with fvs
-> IO (CgStream CmmGroupSRTs CmmCgInfos)
-- Note we produce a 'Stream' of CmmGroups, so that the
-- backend can be run incrementally. Otherwise it generates all
-- the C-- up front, which has a significant space cost.
-doCodeGen hsc_env this_mod denv tycons
+doCodeGen hsc_env this_mod mod_location denv tycons
cost_centre_info stg_binds_w_fvs = do
let dflags = hsc_dflags hsc_env
logger = hsc_logger hsc_env
@@ -1032,7 +1035,7 @@ doCodeGen hsc_env this_mod denv tycons
-- Positions] in GHC.Stg.Debug.
(ipes', stats') <-
if (gopt Opt_InfoTableMap dflags) then
- liftIO $ lookupEstimatedTicks hsc_env ipes stats cmm_srts
+ liftIO $ lookupEstimatedTicks hsc_env mod_location ipes stats cmm_srts
else
return (ipes, stats)
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/33e8221517c3049a3bf93bad08b31da…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/33e8221517c3049a3bf93bad08b31da…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc] Pushed new branch wip/ipe-return-prefer-current-mod
by Ben Gamari (@bgamari) 19 Jun '26
by Ben Gamari (@bgamari) 19 Jun '26
19 Jun '26
Ben Gamari pushed new branch wip/ipe-return-prefer-current-mod at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/ipe-return-prefer-current-mod
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/stm-mvar-deadlock-backtrace] 5 commits: testsuite: Add LoopBacktrace test
by Ben Gamari (@bgamari) 19 Jun '26
by Ben Gamari (@bgamari) 19 Jun '26
19 Jun '26
Ben Gamari pushed to branch wip/stm-mvar-deadlock-backtrace at Glasgow Haskell Compiler / GHC
Commits:
0e661958 by Ben Gamari at 2026-06-19T17:16:44-04:00
testsuite: Add LoopBacktrace test
- - - - -
030ac4b4 by Ben Gamari at 2026-06-19T17:16:44-04:00
Throw nontermination exceptions via `throw`
This ensures that the exception that results gets the usual backtrace
annotations.
- - - - -
0d15e5c0 by Ben Gamari at 2026-06-19T17:16:44-04:00
ghc-heap: Decode OrigThunkInfo frames
- - - - -
f6f6314a by Ben Gamari at 2026-06-19T17:17:22-04:00
compiler: Report backtraces in MVar and STM dealocks
Apply the same treatment previously given to Nontermination exceptions
to MVar and STM deadlock exceptions, using `throw` instead of ad-hoc
throwing with `throwToSingleThread` to ensure that the usual backtrace
machinery is involved.
- - - - -
cba8d183 by Ben Gamari at 2026-06-19T17:17:22-04:00
testsuite: Add tests for deadlock backtraces
- - - - -
22 changed files:
- libraries/base/src/Control/Exception/Base.hs
- libraries/base/src/GHC/IO/Exception.hs
- libraries/ghc-internal/cbits/Stack.cmm
- libraries/ghc-internal/include/RtsIfaceSymbols.h
- libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs
- libraries/ghc-internal/src/GHC/Internal/Heap/Closures.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/Stack/Decode.hs
- rts/Prelude.h
- rts/RaiseAsync.c
- rts/RaiseAsync.h
- rts/RtsStartup.c
- rts/Schedule.c
- rts/include/rts/RtsToHsIface.h
- + testsuite/tests/rts/LoopBacktrace.hs
- + testsuite/tests/rts/LoopBacktrace.stderr
- + testsuite/tests/rts/LoopBacktrace.stdout
- + testsuite/tests/rts/MVarDeadlockBacktrace.hs
- + testsuite/tests/rts/MVarDeadlockBacktrace.stderr
- + testsuite/tests/rts/STMDeadlockBacktrace.hs
- + testsuite/tests/rts/STMDeadlockBacktrace.stderr
- testsuite/tests/rts/all.T
Changes:
=====================================
libraries/base/src/Control/Exception/Base.hs
=====================================
@@ -85,7 +85,6 @@ module Control.Exception.Base
patError,
noMethodBindingError,
typeError,
- nonTermination,
nestedAtomically,
noMatchingContinuationPrompt
) where
=====================================
libraries/base/src/GHC/IO/Exception.hs
=====================================
@@ -19,8 +19,8 @@
--
module GHC.IO.Exception (
- BlockedIndefinitelyOnMVar(..), blockedIndefinitelyOnMVar,
- BlockedIndefinitelyOnSTM(..), blockedIndefinitelyOnSTM,
+ BlockedIndefinitelyOnMVar(..),
+ BlockedIndefinitelyOnSTM(..),
Deadlock(..),
AllocationLimitExceeded(..), allocationLimitExceeded,
AssertionFailed(..),
=====================================
libraries/ghc-internal/cbits/Stack.cmm
=====================================
@@ -3,6 +3,10 @@
#include "Cmm.h"
+#if !defined(UnregisterisedCompiler)
+import CLOSURE stg_orig_thunk_info_frame_info;
+#endif
+
// StgStack_marking was not available in the Stage0 compiler at the time of
// writing. Because, it has been added to derivedConstants when Stack.cmm was
// developed.
@@ -168,6 +172,18 @@ getStackInfoTableAddrzh(P_ stack) {
return (info);
}
+// (StgInfoTable*) getOrigThunkInfoPtrzh(StgStack* stack, StgWord offsetWords)
+getOrigThunkInfoPtrzh(P_ stack, W_ offsetWords) {
+ P_ p;
+ p = StgStack_sp(stack) + WDS(offsetWords);
+ ASSERT(LOOKS_LIKE_CLOSURE_PTR(p));
+ if (%INFO_PTR(UNTAG(p)) == stg_orig_thunk_info_frame_info) {
+ return (StgOrigThunkInfoFrame_info_ptr(UNTAG(p)));
+ } else {
+ return (NULL);
+ }
+}
+
// (StgClosure*) getStackClosurezh(StgStack* stack, StgWord offsetWords)
getStackClosurezh(P_ stack, W_ offsetWords) {
P_ ptr;
=====================================
libraries/ghc-internal/include/RtsIfaceSymbols.h
=====================================
@@ -15,12 +15,12 @@ CLOSURE(GHCziInternalziWeakziFinalizze, runFinalizzerBatch_closure)
CLOSURE(GHCziInternalziIOziException, stackOverflow_closure)
CLOSURE(GHCziInternalziIOziException, heapOverflow_closure)
CLOSURE(GHCziInternalziIOziException, allocationLimitExceeded_closure)
-CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnMVar_closure)
-CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnSTM_closure)
+CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnMVarError_closure)
+CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnSTMError_closure)
CLOSURE(GHCziInternalziIOziException, cannotCompactFunction_closure)
CLOSURE(GHCziInternalziIOziException, cannotCompactPinned_closure)
CLOSURE(GHCziInternalziIOziException, cannotCompactMutable_closure)
-CLOSURE(GHCziInternalziControlziExceptionziBase, nonTermination_closure)
+CLOSURE(GHCziInternalziControlziExceptionziBase, nonTerminationError_closure)
CLOSURE(GHCziInternalziControlziExceptionziBase, nestedAtomically_closure)
CLOSURE(GHCziInternalziControlziExceptionziBase, noMatchingContinuationPrompt_closure)
#if defined(mingw32_HOST_OS)
=====================================
libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs
=====================================
@@ -108,7 +108,7 @@ module GHC.Internal.Control.Exception.Base (
impossibleError, impossibleConstraintError,
nonExhaustiveGuardsError, patError, noMethodBindingError,
typeError,
- nonTermination, nestedAtomically, noMatchingContinuationPrompt,
+ nonTerminationError, nestedAtomically, noMatchingContinuationPrompt,
) where
import GHC.Internal.Base (
@@ -448,8 +448,9 @@ impossibleConstraintError s = errorWithoutStackTrace (unpackCStringUtf8# s)
-- GHC's RTS calls this
-nonTermination :: SomeException
-nonTermination = toException NonTermination
+nonTerminationError :: IO ()
+nonTerminationError = throwIO NonTermination
+
-- GHC's RTS calls this
nestedAtomically :: SomeException
=====================================
libraries/ghc-internal/src/GHC/Internal/Heap/Closures.hs
=====================================
@@ -568,6 +568,17 @@ data GenStackFrame b =
, stack_payload :: ![GenStackField b]
}
+ -- | An @stg_orig_thunk_info_frame@ pushed by @-forig-thunk-info@. It records
+ -- the original info table of the thunk being updated (in 'orig_info_tbl'),
+ -- which is otherwise lost when the thunk is blackholed.
+ -- See @Note [Original thunk info table frames]@ in "GHC.StgToCmm.Bind".
+ | OrigThunkInfo
+ { info_tbl :: !StgInfoTable
+ -- ^ the frame's own (RTS) info table
+ , orig_info_tbl :: !(Ptr StgInfoTable)
+ -- ^ the original info table of the thunk being updated
+ }
+
| RetFun
{ info_tbl :: !StgInfoTable
, retFunSize :: !Word
=====================================
libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
=====================================
@@ -24,8 +24,8 @@
-----------------------------------------------------------------------------
module GHC.Internal.IO.Exception (
- BlockedIndefinitelyOnMVar(..), blockedIndefinitelyOnMVar,
- BlockedIndefinitelyOnSTM(..), blockedIndefinitelyOnSTM,
+ BlockedIndefinitelyOnMVar(..), blockedIndefinitelyOnMVarError,
+ BlockedIndefinitelyOnSTM(..), blockedIndefinitelyOnSTMError,
Deadlock(..),
AllocationLimitExceeded(..), allocationLimitExceeded,
AssertionFailed(..),
@@ -84,8 +84,8 @@ instance Exception BlockedIndefinitelyOnMVar
instance Show BlockedIndefinitelyOnMVar where
showsPrec _ BlockedIndefinitelyOnMVar = showString "thread blocked indefinitely in an MVar operation"
-blockedIndefinitelyOnMVar :: SomeException -- for the RTS
-blockedIndefinitelyOnMVar = toException BlockedIndefinitelyOnMVar
+blockedIndefinitelyOnMVarError :: IO () -- for the RTS
+blockedIndefinitelyOnMVarError = throwIO BlockedIndefinitelyOnMVar
-----
@@ -100,8 +100,8 @@ instance Exception BlockedIndefinitelyOnSTM
instance Show BlockedIndefinitelyOnSTM where
showsPrec _ BlockedIndefinitelyOnSTM = showString "thread blocked indefinitely in an STM transaction"
-blockedIndefinitelyOnSTM :: SomeException -- for the RTS
-blockedIndefinitelyOnSTM = toException BlockedIndefinitelyOnSTM
+blockedIndefinitelyOnSTMError :: IO () -- for the RTS
+blockedIndefinitelyOnSTMError = throwIO BlockedIndefinitelyOnSTM
-----
=====================================
libraries/ghc-internal/src/GHC/Internal/Stack/Decode.hs
=====================================
@@ -192,6 +192,16 @@ foreign import prim "getInfoTableAddrszh" getInfoTableAddrs# :: StackSnapshot# -
foreign import prim "getStackInfoTableAddrzh" getStackInfoTableAddr# :: StackSnapshot# -> Addr#
+foreign import prim "getOrigThunkInfoPtrzh" getOrigThunkInfoPtr# :: StackSnapshot# -> Word# -> Addr#
+
+-- | @'Just' itbl@ if the frame at the given offset is an
+-- @stg_orig_thunk_info_frame@ (where @itbl@ is the recorded original thunk info
+-- table), otherwise 'Nothing'. See @Note [Decoding orig_thunk_info frames]@.
+origThunkInfoFrame :: StackSnapshot# -> WordOffset -> Maybe (Ptr StgInfoTable)
+origThunkInfoFrame stackSnapshot# index =
+ let p = Ptr (getOrigThunkInfoPtr# stackSnapshot# (wordOffsetToWord# index))
+ in if p == nullPtr then Nothing else Just p
+
-- | Get the 'StgInfoTable' of the stack frame.
-- Additionally, provides 'InfoProv' for the 'StgInfoTable' if there is any.
getInfoTableOnStack :: StackSnapshot# -> WordOffset -> IO (StgInfoTable, Maybe InfoProv)
@@ -380,6 +390,15 @@ unpackStackFrameTo (StackSnapshot stackSnapshot#, index) unpackUnderflowFrame fi
bco = bco',
bcoArgs = bcoArgs'
}
+ RET_SMALL
+ | Just orig_itbl <- origThunkInfoFrame stackSnapshot# index -> do
+ orig_info_prov <- lookupIPE (castPtr orig_itbl)
+ finaliseStackFrame
+ OrigThunkInfo
+ { info_tbl = info,
+ orig_info_tbl = orig_itbl
+ }
+ orig_info_prov
RET_SMALL ->
let payload' = decodeSmallBitmap getSmallBitmap# stackSnapshot# index offsetStgClosurePayload
in
=====================================
rts/Prelude.h
=====================================
@@ -53,12 +53,12 @@ extern StgClosure ZCMain_main_closure;
#define stackOverflow_closure ghc_hs_iface->stackOverflow_closure
#define heapOverflow_closure ghc_hs_iface->heapOverflow_closure
#define allocationLimitExceeded_closure ghc_hs_iface->allocationLimitExceeded_closure
-#define blockedIndefinitelyOnMVar_closure ghc_hs_iface->blockedIndefinitelyOnMVar_closure
-#define blockedIndefinitelyOnSTM_closure ghc_hs_iface->blockedIndefinitelyOnSTM_closure
+#define blockedIndefinitelyOnMVarError_closure ghc_hs_iface->blockedIndefinitelyOnMVarError_closure
+#define blockedIndefinitelyOnSTMError_closure ghc_hs_iface->blockedIndefinitelyOnSTMError_closure
#define cannotCompactFunction_closure ghc_hs_iface->cannotCompactFunction_closure
#define cannotCompactPinned_closure ghc_hs_iface->cannotCompactPinned_closure
#define cannotCompactMutable_closure ghc_hs_iface->cannotCompactMutable_closure
-#define nonTermination_closure ghc_hs_iface->nonTermination_closure
+#define nonTerminationError_closure ghc_hs_iface->nonTerminationError_closure
#define nestedAtomically_closure ghc_hs_iface->nestedAtomically_closure
#define absentSumFieldError_closure ghc_hs_iface->absentSumFieldError_closure
#define underflowException_closure ghc_hs_iface->underflowException_closure
=====================================
rts/RaiseAsync.c
=====================================
@@ -87,6 +87,55 @@ suspendComputation (Capability *cap, StgTSO *tso, StgUpdateFrame *stop_here)
throwToSingleThreaded__ (cap, tso, NULL, false, stop_here);
}
+/* -----------------------------------------------------------------------------
+ scheduleRaiseViaIO
+
+ Schedule `tso` to raise an exception by running `io_action`, an IO () that
+ performs `throwIO`. Unlike throwToSingleThreaded (which injects an exception
+ *value* via raiseAsync), the exception is raised by throwIO *within* the
+ thread, so it acquires a backtrace of the thread's stack. This is used by
+ resurrectThreads to deliver the "blocked indefinitely" exceptions
+ (BlockedIndefinitelyOnMVar, BlockedIndefinitelyOnSTM, NonTermination).
+
+ We push a "run this IO action" frame on top of the thread's existing
+ (suspended) stack and make it runnable; when the thread runs, throwIO raises
+ the exception and its own stack unwinding handles any CATCH_FRAME /
+ ATOMICALLY_FRAME (e.g. aborting a blocked STM transaction).
+
+ removeFromQueues takes care of unlinking the thread from any blocking queue
+ (notably the MVar blocked queue) and appends it to the run queue. As with
+ throwToSingleThreaded, the caller must own the TSO (e.g. hold all
+ capabilities during GC); in particular this relies on the thread not being
+ scheduled between removeFromQueues' enqueue and our stack push.
+ -------------------------------------------------------------------------- */
+
+void
+scheduleRaiseViaIO (Capability *cap, StgTSO *tso, StgClosure *io_action)
+{
+ // Thread already dead?
+ if (tso->what_next == ThreadComplete || tso->what_next == ThreadKilled) {
+ return;
+ }
+
+ // Unlink from any blocking queues; sets why_blocked = NotBlocked and
+ // appends the thread to the run queue.
+ removeFromQueues(cap, tso);
+
+ StgStack *stack = tso->stackobj;
+
+ // We are about to mutate the stack, so dirty it for the GC write barrier
+ // (resurrectThreads runs right after GC).
+ dirty_TSO(cap, tso);
+ dirty_STACK(cap, stack);
+
+ // Push a frame that enters `io_action` and applies the resulting IO action
+ // to the void (RealWorld) argument: [stg_enter_info, io_action, stg_ap_v_info]
+ stack->sp -= 3;
+ stack->sp[0] = (W_)&stg_enter_info;
+ stack->sp[1] = (W_)io_action;
+ stack->sp[2] = (W_)&stg_ap_v_info;
+}
+
/* -----------------------------------------------------------------------------
throwToSelf
=====================================
rts/RaiseAsync.h
=====================================
@@ -38,6 +38,10 @@ void suspendComputation (Capability *cap,
StgTSO *tso,
StgUpdateFrame *stop_here);
+void scheduleRaiseViaIO (Capability *cap,
+ StgTSO *tso,
+ StgClosure *io_action);
+
MessageThrowTo *throwTo (Capability *cap, // the Capability we hold
StgTSO *source,
StgTSO *target,
=====================================
rts/RtsStartup.c
=====================================
@@ -192,9 +192,9 @@ static void initBuiltinGcRoots(void)
getStablePtr((StgPtr)stackOverflow_closure);
getStablePtr((StgPtr)heapOverflow_closure);
getStablePtr((StgPtr)unpackCString_closure);
- getStablePtr((StgPtr)blockedIndefinitelyOnMVar_closure);
- getStablePtr((StgPtr)nonTermination_closure);
- getStablePtr((StgPtr)blockedIndefinitelyOnSTM_closure);
+ getStablePtr((StgPtr)blockedIndefinitelyOnMVarError_closure);
+ getStablePtr((StgPtr)nonTerminationError_closure);
+ getStablePtr((StgPtr)blockedIndefinitelyOnSTMError_closure);
getStablePtr((StgPtr)allocationLimitExceeded_closure);
getStablePtr((StgPtr)cannotCompactFunction_closure);
getStablePtr((StgPtr)cannotCompactPinned_closure);
=====================================
rts/Schedule.c
=====================================
@@ -3309,16 +3309,16 @@ resurrectThreads (StgTSO *threads)
case BlockedOnMVar:
case BlockedOnMVarRead:
/* Called by GC - sched_mutex lock is currently held. */
- throwToSingleThreaded(cap, tso,
- (StgClosure *)blockedIndefinitelyOnMVar_closure);
+ scheduleRaiseViaIO(cap, tso,
+ (StgClosure *)blockedIndefinitelyOnMVarError_closure);
break;
case BlockedOnBlackHole:
- throwToSingleThreaded(cap, tso,
- (StgClosure *)nonTermination_closure);
+ scheduleRaiseViaIO(cap, tso,
+ (StgClosure *)nonTerminationError_closure);
break;
case BlockedOnSTM:
- throwToSingleThreaded(cap, tso,
- (StgClosure *)blockedIndefinitelyOnSTM_closure);
+ scheduleRaiseViaIO(cap, tso,
+ (StgClosure *)blockedIndefinitelyOnSTMError_closure);
break;
case NotBlocked:
/* This might happen if the thread was blocked on a black hole
=====================================
rts/include/rts/RtsToHsIface.h
=====================================
@@ -20,12 +20,12 @@ typedef struct {
StgClosure *stackOverflow_closure; // GHC.Internal.IO.Exception.stackOverflow_closure
StgClosure *heapOverflow_closure; // GHC.Internal.IO.Exception.heapOverflow_closure
StgClosure *allocationLimitExceeded_closure; // GHC.Internal.IO.Exception.allocationLimitExceeded_closure
- StgClosure *blockedIndefinitelyOnMVar_closure; // GHC.Internal.IO.Exception.blockedIndefinitelyOnMVar_closure
- StgClosure *blockedIndefinitelyOnSTM_closure; // GHC.Internal.IO.Exception.blockedIndefinitelyOnSTM_closure
+ StgClosure *blockedIndefinitelyOnMVarError_closure; // GHC.Internal.IO.Exception.blockedIndefinitelyOnMVarError_closure
+ StgClosure *blockedIndefinitelyOnSTMError_closure; // GHC.Internal.IO.Exception.blockedIndefinitelyOnSTMError_closure
StgClosure *cannotCompactFunction_closure; // GHC.Internal.IO.Exception.cannotCompactFunction_closure
StgClosure *cannotCompactPinned_closure; // GHC.Internal.IO.Exception.cannotCompactPinned_closure
StgClosure *cannotCompactMutable_closure; // GHC.Internal.IO.Exception.cannotCompactMutable_closure
- StgClosure *nonTermination_closure; // GHC.Internal.Control.Exception.Base.nonTermination_closure
+ StgClosure *nonTerminationError_closure; // GHC.Internal.Control.Exception.Base.nonTerminationError_closure
StgClosure *nestedAtomically_closure; // GHC.Internal.Control.Exception.Base.nestedAtomically_closure
StgClosure *noMatchingContinuationPrompt_closure; // GHC.Internal.Control.Exception.Base.noMatchingContinuationPrompt_closure
StgClosure *blockedOnBadFD_closure; // GHC.Internal.Event.Thread.blockedOnBadFD_closure
=====================================
testsuite/tests/rts/LoopBacktrace.hs
=====================================
@@ -0,0 +1,17 @@
+{-# OPTIONS_GHC -finfo-table-map -forig-thunk-info #-}
+
+import GHC.Exception.Backtrace.Experimental
+
+x :: Integer
+x = x + 1
+
+testing :: IO ()
+testing = do
+ putStrLn "hello"
+ print x
+ putStrLn "world"
+
+main :: IO ()
+main = do
+ setBacktraceMechanismState IPEBacktrace True
+ testing
=====================================
testsuite/tests/rts/LoopBacktrace.stderr
=====================================
@@ -0,0 +1,21 @@
+LoopBacktrace: Uncaught exception ghc-internal:GHC.Internal.Control.Exception.Base.NonTermination:
+
+<<loop>>
+
+IPE backtrace:
+ GHC.Internal.Exception.Backtrace.collectBacktraces' (libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs:(179,1)-(202,25))
+ GHC.Internal.Exception.Backtrace.collectBacktraces (libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs:174:39-56)
+ GHC.Internal.Exception.toExceptionWithBacktrace (libraries/ghc-internal/src/GHC/Internal/Exception.hs:(179,26)-(181,53))
+ GHC.Internal.IO.throwIO (libraries/ghc-internal/src/GHC/Internal/IO.hs:293:36)
+ Cmm$rts/HeapStackCheck.cmm. (:)
+ GHC.Internal.Bignum.Integer.integerAdd (libraries/ghc-internal/src/GHC/Internal/Bignum/Integer.hs:(547,1)-(571,52))
+ Cmm$rts/Updates.cmm. (:)
+ Main.x (LoopBacktrace.hs:6:1-9)
+ GHC.Internal.Show.show (libraries/ghc-internal/src/GHC/Internal/Show.hs:497:10-21)
+ GHC.Internal.IO.Handle.Text.hPutStr' (libraries/ghc-internal/src/GHC/Internal/IO/Handle/Text.hs:667:29-37)
+ GHC.Internal.Base.thenIO (libraries/ghc-internal/src/GHC/Internal/Base.hs:2337:1-72)
+ Cmm$rts/Exception.cmm. (:)
+ Cmm$rts/StgStartup.cmm. (:)
+HasCallStack backtrace:
+ throwIO, called at libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs:452:23 in ghc-internal:GHC.Internal.Control.Exception.Base
+
=====================================
testsuite/tests/rts/LoopBacktrace.stdout
=====================================
@@ -0,0 +1 @@
+hello
=====================================
testsuite/tests/rts/MVarDeadlockBacktrace.hs
=====================================
@@ -0,0 +1,13 @@
+{-# OPTIONS_GHC -finfo-table-map #-}
+
+-- | Check that a @BlockedIndefinitelyOnMVar@ deadlock exception carries a
+-- backtrace mentioning the blocking site in this module.
+import Control.Concurrent.MVar
+import GHC.Exception.Backtrace.Experimental
+
+main :: IO ()
+main = do
+ setBacktraceMechanismState IPEBacktrace True
+ mv <- newEmptyMVar :: IO (MVar ())
+ x <- takeMVar mv
+ print x
=====================================
testsuite/tests/rts/MVarDeadlockBacktrace.stderr
=====================================
@@ -0,0 +1,2 @@
+MVarDeadlockBacktrace: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnMVar:
+ Main.main (MVarDeadlockBacktrace.hs:16:3-36)
=====================================
testsuite/tests/rts/STMDeadlockBacktrace.hs
=====================================
@@ -0,0 +1,12 @@
+{-# OPTIONS_GHC -finfo-table-map #-}
+
+-- | Check that a @BlockedIndefinitelyOnSTM@ deadlock exception carries a
+-- backtrace mentioning the blocking site in this module.
+import GHC.Conc (atomically, retry)
+import GHC.Exception.Backtrace.Experimental
+
+main :: IO ()
+main = do
+ setBacktraceMechanismState IPEBacktrace True
+ x <- atomically retry :: IO ()
+ print x
=====================================
testsuite/tests/rts/STMDeadlockBacktrace.stderr
=====================================
@@ -0,0 +1,2 @@
+STMDeadlockBacktrace: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnSTM:
+ Main.main (STMDeadlockBacktrace.hs:16:3-32)
=====================================
testsuite/tests/rts/all.T
=====================================
@@ -687,3 +687,11 @@ test('ClosureTable',
['-debug -O0 ClosureTable_c.c -I{top}/../rts -I{top}/../rts/include'])
test('resizeMutableByteArrayInPlace', [req_cmm, extra_ways(['optasm', 'sanity']), only_ways(['optasm', 'sanity'])], compile_and_run, [''])
+
+test('LoopBacktrace', [exit_code(1)], compile_and_run, [''])
+
+deadlock_backtrace_norm = grep_errmsg(r'(Uncaught exception|Main\.)')
+test('MVarDeadlockBacktrace', [exit_code(1), only_ways(['normal']), deadlock_backtrace_norm],
+ compile_and_run, ['-O'])
+test('STMDeadlockBacktrace', [exit_code(1), only_ways(['normal']), deadlock_backtrace_norm],
+ compile_and_run, ['-O'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/aa8026fe0480e1539a21b2ae89db40…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/aa8026fe0480e1539a21b2ae89db40…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/loop-backtrace] 3 commits: testsuite: Add LoopBacktrace test
by Ben Gamari (@bgamari) 19 Jun '26
by Ben Gamari (@bgamari) 19 Jun '26
19 Jun '26
Ben Gamari pushed to branch wip/loop-backtrace at Glasgow Haskell Compiler / GHC
Commits:
0e661958 by Ben Gamari at 2026-06-19T17:16:44-04:00
testsuite: Add LoopBacktrace test
- - - - -
030ac4b4 by Ben Gamari at 2026-06-19T17:16:44-04:00
Throw nontermination exceptions via `throw`
This ensures that the exception that results gets the usual backtrace
annotations.
- - - - -
0d15e5c0 by Ben Gamari at 2026-06-19T17:16:44-04:00
ghc-heap: Decode OrigThunkInfo frames
- - - - -
14 changed files:
- libraries/base/src/Control/Exception/Base.hs
- libraries/ghc-internal/cbits/Stack.cmm
- libraries/ghc-internal/include/RtsIfaceSymbols.h
- libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs
- libraries/ghc-internal/src/GHC/Internal/Heap/Closures.hs
- libraries/ghc-internal/src/GHC/Internal/Stack/Decode.hs
- rts/Prelude.h
- rts/RtsStartup.c
- rts/Schedule.c
- rts/include/rts/RtsToHsIface.h
- + testsuite/tests/rts/LoopBacktrace.hs
- + testsuite/tests/rts/LoopBacktrace.stderr
- + testsuite/tests/rts/LoopBacktrace.stdout
- testsuite/tests/rts/all.T
Changes:
=====================================
libraries/base/src/Control/Exception/Base.hs
=====================================
@@ -85,7 +85,6 @@ module Control.Exception.Base
patError,
noMethodBindingError,
typeError,
- nonTermination,
nestedAtomically,
noMatchingContinuationPrompt
) where
=====================================
libraries/ghc-internal/cbits/Stack.cmm
=====================================
@@ -3,6 +3,10 @@
#include "Cmm.h"
+#if !defined(UnregisterisedCompiler)
+import CLOSURE stg_orig_thunk_info_frame_info;
+#endif
+
// StgStack_marking was not available in the Stage0 compiler at the time of
// writing. Because, it has been added to derivedConstants when Stack.cmm was
// developed.
@@ -168,6 +172,18 @@ getStackInfoTableAddrzh(P_ stack) {
return (info);
}
+// (StgInfoTable*) getOrigThunkInfoPtrzh(StgStack* stack, StgWord offsetWords)
+getOrigThunkInfoPtrzh(P_ stack, W_ offsetWords) {
+ P_ p;
+ p = StgStack_sp(stack) + WDS(offsetWords);
+ ASSERT(LOOKS_LIKE_CLOSURE_PTR(p));
+ if (%INFO_PTR(UNTAG(p)) == stg_orig_thunk_info_frame_info) {
+ return (StgOrigThunkInfoFrame_info_ptr(UNTAG(p)));
+ } else {
+ return (NULL);
+ }
+}
+
// (StgClosure*) getStackClosurezh(StgStack* stack, StgWord offsetWords)
getStackClosurezh(P_ stack, W_ offsetWords) {
P_ ptr;
=====================================
libraries/ghc-internal/include/RtsIfaceSymbols.h
=====================================
@@ -20,7 +20,7 @@ CLOSURE(GHCziInternalziIOziException, blockedIndefinitelyOnSTM_closure)
CLOSURE(GHCziInternalziIOziException, cannotCompactFunction_closure)
CLOSURE(GHCziInternalziIOziException, cannotCompactPinned_closure)
CLOSURE(GHCziInternalziIOziException, cannotCompactMutable_closure)
-CLOSURE(GHCziInternalziControlziExceptionziBase, nonTermination_closure)
+CLOSURE(GHCziInternalziControlziExceptionziBase, nonTerminationError_closure)
CLOSURE(GHCziInternalziControlziExceptionziBase, nestedAtomically_closure)
CLOSURE(GHCziInternalziControlziExceptionziBase, noMatchingContinuationPrompt_closure)
#if defined(mingw32_HOST_OS)
=====================================
libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs
=====================================
@@ -108,7 +108,7 @@ module GHC.Internal.Control.Exception.Base (
impossibleError, impossibleConstraintError,
nonExhaustiveGuardsError, patError, noMethodBindingError,
typeError,
- nonTermination, nestedAtomically, noMatchingContinuationPrompt,
+ nonTerminationError, nestedAtomically, noMatchingContinuationPrompt,
) where
import GHC.Internal.Base (
@@ -448,8 +448,9 @@ impossibleConstraintError s = errorWithoutStackTrace (unpackCStringUtf8# s)
-- GHC's RTS calls this
-nonTermination :: SomeException
-nonTermination = toException NonTermination
+nonTerminationError :: IO ()
+nonTerminationError = throwIO NonTermination
+
-- GHC's RTS calls this
nestedAtomically :: SomeException
=====================================
libraries/ghc-internal/src/GHC/Internal/Heap/Closures.hs
=====================================
@@ -568,6 +568,17 @@ data GenStackFrame b =
, stack_payload :: ![GenStackField b]
}
+ -- | An @stg_orig_thunk_info_frame@ pushed by @-forig-thunk-info@. It records
+ -- the original info table of the thunk being updated (in 'orig_info_tbl'),
+ -- which is otherwise lost when the thunk is blackholed.
+ -- See @Note [Original thunk info table frames]@ in "GHC.StgToCmm.Bind".
+ | OrigThunkInfo
+ { info_tbl :: !StgInfoTable
+ -- ^ the frame's own (RTS) info table
+ , orig_info_tbl :: !(Ptr StgInfoTable)
+ -- ^ the original info table of the thunk being updated
+ }
+
| RetFun
{ info_tbl :: !StgInfoTable
, retFunSize :: !Word
=====================================
libraries/ghc-internal/src/GHC/Internal/Stack/Decode.hs
=====================================
@@ -192,6 +192,16 @@ foreign import prim "getInfoTableAddrszh" getInfoTableAddrs# :: StackSnapshot# -
foreign import prim "getStackInfoTableAddrzh" getStackInfoTableAddr# :: StackSnapshot# -> Addr#
+foreign import prim "getOrigThunkInfoPtrzh" getOrigThunkInfoPtr# :: StackSnapshot# -> Word# -> Addr#
+
+-- | @'Just' itbl@ if the frame at the given offset is an
+-- @stg_orig_thunk_info_frame@ (where @itbl@ is the recorded original thunk info
+-- table), otherwise 'Nothing'. See @Note [Decoding orig_thunk_info frames]@.
+origThunkInfoFrame :: StackSnapshot# -> WordOffset -> Maybe (Ptr StgInfoTable)
+origThunkInfoFrame stackSnapshot# index =
+ let p = Ptr (getOrigThunkInfoPtr# stackSnapshot# (wordOffsetToWord# index))
+ in if p == nullPtr then Nothing else Just p
+
-- | Get the 'StgInfoTable' of the stack frame.
-- Additionally, provides 'InfoProv' for the 'StgInfoTable' if there is any.
getInfoTableOnStack :: StackSnapshot# -> WordOffset -> IO (StgInfoTable, Maybe InfoProv)
@@ -380,6 +390,15 @@ unpackStackFrameTo (StackSnapshot stackSnapshot#, index) unpackUnderflowFrame fi
bco = bco',
bcoArgs = bcoArgs'
}
+ RET_SMALL
+ | Just orig_itbl <- origThunkInfoFrame stackSnapshot# index -> do
+ orig_info_prov <- lookupIPE (castPtr orig_itbl)
+ finaliseStackFrame
+ OrigThunkInfo
+ { info_tbl = info,
+ orig_info_tbl = orig_itbl
+ }
+ orig_info_prov
RET_SMALL ->
let payload' = decodeSmallBitmap getSmallBitmap# stackSnapshot# index offsetStgClosurePayload
in
=====================================
rts/Prelude.h
=====================================
@@ -58,7 +58,7 @@ extern StgClosure ZCMain_main_closure;
#define cannotCompactFunction_closure ghc_hs_iface->cannotCompactFunction_closure
#define cannotCompactPinned_closure ghc_hs_iface->cannotCompactPinned_closure
#define cannotCompactMutable_closure ghc_hs_iface->cannotCompactMutable_closure
-#define nonTermination_closure ghc_hs_iface->nonTermination_closure
+#define nonTerminationError_closure ghc_hs_iface->nonTerminationError_closure
#define nestedAtomically_closure ghc_hs_iface->nestedAtomically_closure
#define absentSumFieldError_closure ghc_hs_iface->absentSumFieldError_closure
#define underflowException_closure ghc_hs_iface->underflowException_closure
=====================================
rts/RtsStartup.c
=====================================
@@ -193,7 +193,7 @@ static void initBuiltinGcRoots(void)
getStablePtr((StgPtr)heapOverflow_closure);
getStablePtr((StgPtr)unpackCString_closure);
getStablePtr((StgPtr)blockedIndefinitelyOnMVar_closure);
- getStablePtr((StgPtr)nonTermination_closure);
+ getStablePtr((StgPtr)nonTerminationError_closure);
getStablePtr((StgPtr)blockedIndefinitelyOnSTM_closure);
getStablePtr((StgPtr)allocationLimitExceeded_closure);
getStablePtr((StgPtr)cannotCompactFunction_closure);
=====================================
rts/Schedule.c
=====================================
@@ -3276,6 +3276,17 @@ findAtomicallyFrameHelper (Capability *cap, StgTSO *tso)
}
}
+static void throwNontermination(Capability *cap, StgTSO *tso) {
+ StgStack *stack = tso->stackobj;
+ stack->sp -= 3;
+ stack->sp[0] = (W_)&stg_enter_info;
+ stack->sp[1] = (W_)nonTerminationError_closure;
+ stack->sp[2] = (W_)&stg_ap_v_info;
+ tso->why_blocked = NotBlocked;
+ appendToRunQueue(cap,tso);
+}
+
+
/* -----------------------------------------------------------------------------
resurrectThreads is called after garbage collection on the list of
threads found to be garbage. Each of these threads will be woken
@@ -3313,8 +3324,7 @@ resurrectThreads (StgTSO *threads)
(StgClosure *)blockedIndefinitelyOnMVar_closure);
break;
case BlockedOnBlackHole:
- throwToSingleThreaded(cap, tso,
- (StgClosure *)nonTermination_closure);
+ throwNontermination(cap, tso);
break;
case BlockedOnSTM:
throwToSingleThreaded(cap, tso,
=====================================
rts/include/rts/RtsToHsIface.h
=====================================
@@ -25,7 +25,7 @@ typedef struct {
StgClosure *cannotCompactFunction_closure; // GHC.Internal.IO.Exception.cannotCompactFunction_closure
StgClosure *cannotCompactPinned_closure; // GHC.Internal.IO.Exception.cannotCompactPinned_closure
StgClosure *cannotCompactMutable_closure; // GHC.Internal.IO.Exception.cannotCompactMutable_closure
- StgClosure *nonTermination_closure; // GHC.Internal.Control.Exception.Base.nonTermination_closure
+ StgClosure *nonTerminationError_closure; // GHC.Internal.Control.Exception.Base.nonTerminationError_closure
StgClosure *nestedAtomically_closure; // GHC.Internal.Control.Exception.Base.nestedAtomically_closure
StgClosure *noMatchingContinuationPrompt_closure; // GHC.Internal.Control.Exception.Base.noMatchingContinuationPrompt_closure
StgClosure *blockedOnBadFD_closure; // GHC.Internal.Event.Thread.blockedOnBadFD_closure
=====================================
testsuite/tests/rts/LoopBacktrace.hs
=====================================
@@ -0,0 +1,17 @@
+{-# OPTIONS_GHC -finfo-table-map -forig-thunk-info #-}
+
+import GHC.Exception.Backtrace.Experimental
+
+x :: Integer
+x = x + 1
+
+testing :: IO ()
+testing = do
+ putStrLn "hello"
+ print x
+ putStrLn "world"
+
+main :: IO ()
+main = do
+ setBacktraceMechanismState IPEBacktrace True
+ testing
=====================================
testsuite/tests/rts/LoopBacktrace.stderr
=====================================
@@ -0,0 +1,21 @@
+LoopBacktrace: Uncaught exception ghc-internal:GHC.Internal.Control.Exception.Base.NonTermination:
+
+<<loop>>
+
+IPE backtrace:
+ GHC.Internal.Exception.Backtrace.collectBacktraces' (libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs:(179,1)-(202,25))
+ GHC.Internal.Exception.Backtrace.collectBacktraces (libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs:174:39-56)
+ GHC.Internal.Exception.toExceptionWithBacktrace (libraries/ghc-internal/src/GHC/Internal/Exception.hs:(179,26)-(181,53))
+ GHC.Internal.IO.throwIO (libraries/ghc-internal/src/GHC/Internal/IO.hs:293:36)
+ Cmm$rts/HeapStackCheck.cmm. (:)
+ GHC.Internal.Bignum.Integer.integerAdd (libraries/ghc-internal/src/GHC/Internal/Bignum/Integer.hs:(547,1)-(571,52))
+ Cmm$rts/Updates.cmm. (:)
+ Main.x (LoopBacktrace.hs:6:1-9)
+ GHC.Internal.Show.show (libraries/ghc-internal/src/GHC/Internal/Show.hs:497:10-21)
+ GHC.Internal.IO.Handle.Text.hPutStr' (libraries/ghc-internal/src/GHC/Internal/IO/Handle/Text.hs:667:29-37)
+ GHC.Internal.Base.thenIO (libraries/ghc-internal/src/GHC/Internal/Base.hs:2337:1-72)
+ Cmm$rts/Exception.cmm. (:)
+ Cmm$rts/StgStartup.cmm. (:)
+HasCallStack backtrace:
+ throwIO, called at libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs:452:23 in ghc-internal:GHC.Internal.Control.Exception.Base
+
=====================================
testsuite/tests/rts/LoopBacktrace.stdout
=====================================
@@ -0,0 +1 @@
+hello
=====================================
testsuite/tests/rts/all.T
=====================================
@@ -687,3 +687,5 @@ test('ClosureTable',
['-debug -O0 ClosureTable_c.c -I{top}/../rts -I{top}/../rts/include'])
test('resizeMutableByteArrayInPlace', [req_cmm, extra_ways(['optasm', 'sanity']), only_ways(['optasm', 'sanity'])], compile_and_run, [''])
+
+test('LoopBacktrace', [exit_code(1)], compile_and_run, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4bffc3c883983cf9b02d33efabdd3f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4bffc3c883983cf9b02d33efabdd3f…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/source-note-module] 47 commits: Hadrian: avoid response files when command line is short enough
by Ben Gamari (@bgamari) 19 Jun '26
by Ben Gamari (@bgamari) 19 Jun '26
19 Jun '26
Ben Gamari pushed to branch wip/source-note-module at Glasgow Haskell Compiler / GHC
Commits:
498bb21a by David Eichmann at 2026-06-09T18:02:39-04:00
Hadrian: avoid response files when command line is short enough
This replaces the logic of always using response files on Windows.
With the new condition based on command line lenght, reponse files
can be avoided in many more cases (on windows).
Now that response files are only used in a small number of cases,
response files are always kept and the -r / --keep-response-files
command line options have been removed
The response file paths are nolonger randomized. They are placed in the
`_build/rsp` directory. This ensures they are ignored by git and we
that Hadrian reuses response file paths when rebuilding rather than
leaving stale response files around.
Update user guide putting response files in its own section
- - - - -
87f510a5 by Simon Hengel at 2026-06-09T18:03:25-04:00
Don't use non-breaking spaces
- - - - -
41a19379 by David Eichmann at 2026-06-09T18:04:11-04:00
Hadrian: remove unused wrapper scripts from windows bindist
These wrapper scripts are only installed on non-relocatable builds
which are not generally supported on windows.
- - - - -
ce01ccb6 by sheaf at 2026-06-10T05:08:48-04:00
Don't drop ticks around variables of type `IO ()`
GHC.Core.Utils.mkTick is responsible for placing a tick on a Core
expression. It contains logic for dropping SCCs (non-counting profiling
ticks) around non-function variables, as such variables cannot
meaningfully contribute to profiles. However, the logic for what counts
as a function was incorrect: it used `isFunTy` which returns 'False' for
types such as 'IO ()' where the function arrow is hidden under a
newtype.
We now use 'mightBeFunTy' instead of 'isFunTy'. This ensures we don't
drop ticks in cases we aren't sure.
On the way, we improve the documentation of 'isFunTy', 'isPiTy' and
'mightBeFunTy', and update the latter's implementation to consistently
handle unary classes.
Fixes #27225
-------------------------
Metric Decrease:
T5642
-------------------------
- - - - -
d311c4f1 by Simon Jakobi at 2026-06-10T05:09:32-04:00
testsuite: Add regression test for #4081
Check that a strict constructor field is unboxed once outside an
enclosing loop, not re-inspected each iteration (the float-out
case-floating from 9cb20b488). Uses simonpj's `data T a = T !a` example
from the ticket; T4081.stderr captures the expected Core.
Co-Authored-By: Claude Opus 4.7 <noreply(a)anthropic.com>
- - - - -
333df444 by sheaf at 2026-06-10T05:10:25-04:00
Check for cabal-install >= 3.12 upfront
Starting with commit 8cb99552f607f6bc4000e45ab32532d50c8bb996, Hadrian
requires cabal-install >= 3.12 in order to use the 'cabal path' command
that was introduced in version 3.12, as per
https://github.com/haskell/cabal/blob/a51c4ee1556d816ad86e90db7e6330dd51b0b…
This was not reflected in the Hadrian build script, causing a delayed
build failure instead of enforcing the version requirement upfront,
which this patch does.
Fixes #27317
- - - - -
98c20394 by sheaf at 2026-06-10T05:11:09-04:00
Fix crash in Data.Data instance for HsCtxt
The Data.Data instance for HsCtxt contained an error for the 'toConstr'
method, which could trigger for example when looking at -ddump-tc-ast
traces. Replace it with the 'abstractConstr' pattern used in the rest of
the codebase.
- - - - -
5ac9ce7d by Zubin Duggal at 2026-06-10T21:26:32+05:30
hadrian: Remove old package.conf files when generating new ones
Old package.conf files might exists with different hashes, causing issues like #26661
Fixes #26661
- - - - -
c9015f09 by sheaf at 2026-06-11T12:40:28-04:00
Fix AArch64 clobbering bug for MUL2
On AArch64, the code generator could clobber one of the input operands
when computing the lower bits of a MUL2 operation. This rendered invalid
the subsequent computation of the high bits.
This commit fixes that by using a temporary register. The register
allocator can remove the redundant move in the common case when the
registers do not conflict.
Fixes #27046
- - - - -
7ab90288 by Rodrigo Mesquita at 2026-06-11T12:41:11-04:00
fix: make T27131 less flaky
It seems that T27131 fails flakily in a race where we check the flag
before the capability had the chance to process the mailbox which sets
the flag. This seemingly should only happen if the capability ends up
being the same for setting and checking the flag.
- - - - -
8965cb76 by Marc Scholten at 2026-06-12T04:53:22-04:00
haddock: render modules concurrently
- - - - -
8cc0b64a by Duncan Coutts at 2026-06-12T04:54:06-04:00
Promote HAVE_PREEMPTION from Timer.c to OSThreads.h
We will want to know about HAVE_PREEMPTION in more places.
HAVE_PREEMPTION tells us that we do have OS threads available,
irrespective of whether THREADED is defined. In particular,
HAVE_PREEMPTION is defined on all proper OSs, but not on WASM (and
hyopthetically may not be true on some other platforms like
micro-controllers, RTOSs, VM hypervisors etc).
- - - - -
cce574ed by Duncan Coutts at 2026-06-12T04:54:06-04:00
Define ACQUIRE_LOCK_ALWAYS and friends
Fix issue #27335
Like the atomic _ALWAYS variants, these lock actions are always defined,
rather than being dependent on whether we are in the THREADED case. All
the "normal" LOCK macros are defined to be no-ops when !THREADED.
The use case for the _ALWAYS variants is where we are using OS threads
even in the non-threaded RTS. This includes everything to do with the
timer/ticker thread, which is used in the non-threaded RTS too.
In particular, we will want to use this for eventlog things, because the
timer thread performs eventlogging concurrently with the main
capability, even in the non-threaded RTS.
- - - - -
1f28d1f6 by Duncan Coutts at 2026-06-12T04:54:06-04:00
Use ACQUIRE/RELEASE_LOCK_ALWAYS with eventBufMutex
Even in the non-threaded RTS the eventBufMutex is needed by both the
main capability and the timer/ticker thread, so always use the mutex.
This should fix #25165 which is about the main capability and the timer
thread posting events to the eventlog buffer concurrently and thereby
corrupting the buffer data.
- - - - -
0ff29782 by Duncan Coutts at 2026-06-12T04:54:06-04:00
Expose eventBufMutex in the EventLog interface/header
We will need it in forkProcess to ensure we don't write to the global
eventlog buffer concurrently with trying to flush eventlog buffers and
do the fork().
- - - - -
7a688395 by Duncan Coutts at 2026-06-12T04:54:07-04:00
Split flushAllCapsEventsBufs into safe and unlocked version
Following the convention that unlocked versions have a trailing _
underscore in their name. This one requires the caller to hold the
eventlog global buffer mutex. We will need this in forkProcess.
- - - - -
341ed474 by Duncan Coutts at 2026-06-12T04:54:07-04:00
Remove redundant use of stopTimer in setNumCapabilities
Historically, the comment here was:
We must stop the interval timer while we are changing the
capabilities array lest handle_tick may try to context switch
an old capability. See #17289.
and
We must disable the timer while we do this since the tick handler may
call contextSwitchAllCapabilities, which may see the capabilities array
as we free it.
What this refers to is that historically, when changing the number of
capabilities, the array of capabilities was reallocated to a new size,
allocating new ones and freeing the old ones, thus invalidating all
existing capbility pointers.
Strangely, for good measure the code used to call stopTimer twice (hence
the two similar comments above).
However, since commit a3eccf06292dd666b24606251a52da2b466a9612, the
capabilities array is no longer reallocated. Instead the array is
allcoated once on RTS startup to the maximum size it could ever be
allowed to be, and then capabilities get enabled/disabled at runtime. So
the capability pointers never become invalid anymore. At worst, they may
point to capabilities that are disabled.
Thus we no longer need to stop the timer (twice) while we change the
number of enabled capabilities. This also partially solves issue #27105,
which notes that stopTimer is being used as if it were synchronous, when
it is not. At least for this case, the solution is that stopTimer is not
needed at all!
- - - - -
674858e3 by Duncan Coutts at 2026-06-12T04:54:07-04:00
Remove redundant use of stopTimer in forkProcess
but replace it with taking the eventlog buffer lock during the fork.
Fixes issue #27105
The original reason to block the timer during a fork was that
historically the timer was implemented using a periodic timer signal,
and the signal itself would interrupt the fork system call (returning
EINTR). For large processes (where fork() takes a while) this could
permanently livelock: the timer always would go off before the fork
could complete, which got retried in a loop forever.
The timer is no longer implemented as a unix signal, but uses threads.
Thus the original problem no longer exists. The only remaining reason to
block the timer tick is to prevent actions taken by the tick from
interfering with the delicate process involved in fork (taking a load of
locks and pausing everything).
The only thing we need to do is to prevent the eventlog from being
written to or flushed while the fork is taking place. To achieve this
all we need to do is hold the mutex for the global eventlog buffer.
This removes the last use of stopTimer that expects stopTimer to work
synchronously (which it was not) and thus solves issue #27105. To be
clear, we solve issue #27105 not by making stopTimer synchronous, but by
eliminating the use sites that expected it to be synchronous.
- - - - -
40764930 by sheaf at 2026-06-12T14:54:43-04:00
Add type family performance test for #26426
Some GHC versions produced large numbers of coercions after typechecking
and desugaring when compiling the program in #26426:
Version | Typechecker time | Typechecker allocations | Coercions
-------:|-----------------:|------------------------:|---------:
9.6 | 47 ms | 48 MB | 110k
9.8 | 1000 ms | 486 MB | 10,437k
9.10 | 922 ms | 489 MB | 10,436k
9.12 | 906 ms | 482 MB | 10,437k
9.14 | 63 ms | 55 MB | 333k
10.0 | 47 ms | 64 MB | 35k
The improvement 9.12 -> 9.14 was due to commit 22d11fa818fae2c95c494fc0fac1f8cb4c6e7cb6,
while the improvement 9.14 -> 10.0 was due to commit 0b7df6db9e46df40e86fbff1a66dc10440b99db5.
As the behaviour of GHC seems better than it's ever been on this program,
we declare victory, adding this performance test to ensure we don't
regress on this program.
On the way, we update Note [Combining equalities] in GHC.Tc.SolveR.Equality
with the explanation of the 9.12 -> 9.14 improvement (getting rid of an
exponential blowup in coercion sizes), and we update
Note [Exploiting closed type families] in GHC.Tc.Solver.FunDeps with
the explanation of the 9.14 -> 10.0 improvement (bringing down coercion
size growth from cubic to quadratic).
- - - - -
0f3d0a71 by Zubin Duggal at 2026-06-12T14:55:30-04:00
compiler: mark tool messages as errors/warnings depending on the exit code
Fixes #27370
- - - - -
d9ea2d76 by mangoiv at 2026-06-13T04:41:51-04:00
libraries/process: bump submodule to v1.6.30.0
- bump the submodule to the appropriate tag
- suppress benign warning resulting from the change
- - - - -
6ebaaba3 by David Eichmann at 2026-06-13T04:42:37-04:00
ghc-toolchain: don't throw when candidate executables are not found
Fixes #27369
- - - - -
6c65e1e1 by David Eichmann at 2026-06-13T04:43:23-04:00
CI: lint-changelog checks for no-changelog label in script instead of rules
- - - - -
bab37cc6 by konsumlamm at 2026-06-13T19:10:21+02:00
Implement CLC proposal #378
Add `Data.Double` and `Data.Float` modules
Document that GHC uses IEEE 754
- - - - -
fb5246ad by fendor at 2026-06-15T18:07:23-04:00
Drop `preloadClosure` from `UnitState`
It is always hard-coded to the same value.
Backpack Unit instantiation isn't using it any more.
Allows us to simplify the API and get rid of `improveUnit`.
- - - - -
291ce3aa by ARATA Mizuki at 2026-06-15T18:08:26-04:00
RISC-V NCG: Zero-extend the result of castFloatToWord32
According to the ISA manual, FMV.X.W sign-extends the result.
We need to truncate the result to avoid creating an exotic Word32 value.
Fixes #27300
- - - - -
011be91f by ARATA Mizuki at 2026-06-15T18:08:26-04:00
RISC-V NCG: Treat d28-d31 (ft8-ft11) as caller-saved
According to the calling convention, the registers d28-d31 (ft8-ft11) are caller-saved.
Fixes #27306
- - - - -
e8a54713 by ARATA Mizuki at 2026-06-15T18:08:26-04:00
RISC-V NCG: Set rounding mode when emitting `truncate`
If we omit the rounding mode for `fcvt`, `dyn` will be used.
We do not want that for `truncate`, so we set `rtz`.
In other places, we set `rne` because we do not use the dynamic rounding mode.
Fixes #27303
- - - - -
9438bec7 by Zubin Duggal at 2026-06-15T18:09:11-04:00
rts: fix validate build with gcc 16. `__attribute__((regparm(1)))` is ignored on x86_64 and now
gcc warns that it is ignored:
rts/sm/Evac.h:35:1: error:
error: ‘regparm’ attribute ignored [-Werror=attributes]
See https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=ccead81bbc39668376eb5cf47066a…
Fixes #27366
- - - - -
893e6133 by Andrew Lelechenko at 2026-06-15T23:55:36+01:00
base: more NonEmpty zips
CLC proposal: https://github.com/haskell/core-libraries-committee/issues/409
- - - - -
1314f2fd by David Eichmann at 2026-06-16T05:46:52-04:00
Hadrian: fix ghc-internal .def file name
- - - - -
7f72bcb3 by mangoiv at 2026-06-16T05:47:39-04:00
compiler: ignore camelCase and Eta reduce hlint hints
These do not cohere with the style used in GHC. After disabling them,
hlint lints are much less noisy again.
- - - - -
842bef9f by Alan Zimmerman at 2026-06-16T05:48:25-04:00
EPA: Use standard type family declaration for Anno
- - - - -
f6d30767 by Wolfgang Jeltsch at 2026-06-16T15:32:34-04:00
Fix two issues in the documentation of pipeline interruption
One issue is a typo (“interreuptible”), the other one the lack of an end
of a sentence, which has been reconstructed from the message of
633bbc1fd4762a2bb73ba1c5b9e0c279f1dd3c40, the commit that introduced
said documentation.
- - - - -
a3fa10e0 by Christian Georgii at 2026-06-16T15:33:26-04:00
Find plugins in sibling home units in multiple-home-unit sessions
In a multiple-home-unit session (e.g. `cabal repl --enable-multi-repl` or HLS), enabling a plugin with -fplugin that is defined in (or reexported by) another home unit failed with a "hidden package" error. The plugin module finder only searched the current home unit and the registered external packages, never the sibling home units.
findPluginModuleNoHsc now searches the home units that the current home unit depends on, following module reexports and respecting hidden modules, exactly as ordinary import resolution does in findImportedModuleNoHsc. To avoid two divergent copies of this logic, the shared home-unit search (the current home unit first, then its dependencies in priority order, with the accompanying ordering invariant) is extracted into findHomeModuleAmongDeps, which both findImportedModuleNoHsc and findPluginModuleNoHsc now call.
Add testsuite/tests/driver/multipleHomeUnits/plugin01, which loads a plugin as byte-code from a sibling home unit, and plugin02, in which the consumer enables a plugin reexported by a sibling home unit without depending on the plugin's own home unit directly.
Fixes #27349
- - - - -
d216412b by Ian-Woo Kim at 2026-06-16T20:25:57-04:00
Make the order of usages deterministic
It has been observed that the ordering of usages can be non-determinstic
in parallel builds. Therefore, this contribution introduces sorting of
usages based on a platform- and race-independent sorting criterion.
Resolves #26877.
Co-authored-by: Wolfgang Jeltsch <wolfgang(a)well-typed.com>
- - - - -
8e1cc105 by Wolfgang Jeltsch at 2026-06-16T20:25:57-04:00
Change the descriptions of two existing changelog entries
The descriptions now describe the changes in a user-friendly manner, as
opposed to describing the contributions that led to these changes in a
developer-friendly manner.
- - - - -
636c1c7a by Ian Duncan at 2026-06-16T20:26:50-04:00
AArch64: use SXTH, not SXTW, for W32 signExtendReg
signExtendReg was using SXTH (sign-extend halfword, 16-bit) for
W32-to-W64 sign extension. This should be SXTW (sign-extend word,
32-bit). SXTH only sign-extends the lower 16 bits, producing wrong
results for 32-bit values whose bit 15 differs from bit 31.
Other fixes:
- At sub-W64, code gen for MO_S_Mul2 should use W32 registers for
SMULL source operands as per the ARM spec (SMULL Xd, Wn, Wm),
and not W64.
- Ensure signExtendReg uses the source width for the source operand
in SXTW/SXTH/SXTB instructions. GNU as requires sxtw Xd,Wn (not
sxtw Xd,Xn), while LLVM's integrated assembler on macOS is lenient.
- Fix overflow flag computation for `MO_S_Mul2`. The overflow bit
was exactly inverted for sub-W64 operands.
Fixes #26978 and #27047
- - - - -
b734c75d by Igor Ranieri at 2026-06-16T20:27:32-04:00
haddock: Update CONTRIBUTING with missing step, add missing test
dependency
- - - - -
7fe4f2ec by Luite Stegeman at 2026-06-17T05:35:09-04:00
tag inference: don't confuse functions with their return values
inferTagRhs was mixing up taggedness for closures and return values
for function closures. We really shouldn't assign TagTuple to a
properly tagged function returning a tuple.
We fix this by keeping track of functions (TagFun) separately from
values (TagVal) and keeping track of their return value. TagFun is
also used for join points.
fixes #27005
- - - - -
4671c126 by Sebastian Graf at 2026-06-17T05:35:55-04:00
Seed the simplifier's in-scope set for open expressions
simplifyExpr simplifies expressions typed at the GHCi prompt and the
results of Template Haskell splices. Such an expression may be open: at a
GHCi debugger breakpoint its free variables include RuntimeUnk skolems
standing for as-yet-unknown types.
The simplifier began with an in-scope set holding only the wildcard
binder, so when it instantiated the unsafeCoerce# wrapper that GHCi
builds around a result, it formed a substitution whose range mentioned a
free skolem that was not in scope. That breaks the substitution invariant
and, in a compiler built with assertions, trips substTy's sanity check.
Seed the initial in-scope set with the free variables of the expression.
For a closed expression this adds nothing.
See Note [Seed the in-scope set for open expressions].
Fixes #17833 and its duplicate #21118.
- - - - -
67d41299 by Sebastian Graf at 2026-06-18T05:18:24-04:00
Desugar a `case` scrutinee only once (#27383, #20251)
In `dsExpr` for `HsCase` we desugared the scrutinee /twice/: once to
build the Core `case` itself, and again inside `matchWrapper`, which
re-desugared the source scrutinee (via `addHsScrutTmCs`) purely to
record long-distance information for the pattern-match checker.
For a single `case` that is merely wasteful. But for nested cases it
is catastrophic. Consider
case (case (case e of ... ) of ... ) of ...
Desugaring the outer scrutinee desugars the middle `case` twice, each
of which desugars the inner `case` twice, and so on. The work doubles
at every level, so desugaring takes O(2^n) time in the nesting depth.
That is the blowup reported in #27383; it is also what makes the
machine-generated program in #20251 take an age to compile.
The fix is simple. `matchWrapper` is handed the scrutinee anyway, so
we give it the Core expression we have /already/ desugared, and record
the long-distance term constraint with `addCoreScrutTmCs` instead of
re-desugaring from source. This is just what `matchSinglePatVar`
already does for single-pattern matches.
So:
* `matchWrapper` now takes `Maybe [CoreExpr]` rather than
`Maybe [LHsExpr GhcTc]`.
* The `HsCase` equation of `dsExpr` passes the already-desugared
`core_discrim`; the arrow desugarer passes its match variables.
* `addHsScrutTmCs` had no other use, so it is gone.
Desugaring is now linear in the nesting depth. (The coverage checker
still runs `simpleOptExpr` over each scrutinee, which leaves the total
at O(n^2); that is ample.) The long-distance information itself is
unchanged: the checker sees precisely the Core that backs the
generated code.
Test: deSugar/should_compile/T27383
- - - - -
fa5defde by Rodrigo Mesquita at 2026-06-18T05:19:11-04:00
fix: Save FastStrings in the PMC
There is no point in adding the unique to the occurrence FastString we
create, since it is part of the Id anyway.
Adding it to the FastString, meant each FastString was unique
unnecessarily!
In a separate branch, running the compiler on test `InstanceMatching`
observed 30000 `FastString`s created by this code path.
Plus, `fsLit "pm"` follows the existing pattern in `mkPmId`.
- - - - -
4efb4a66 by Alan Zimmerman at 2026-06-18T14:41:14-04:00
TTG: Add extension points to HsConDetails
Extend HsConDetails as
data HsConDetails p arg rec
= PrefixCon !(XPrefixCon p) [arg] -- C @t1 @t2 p1 p2 p3
| RecCon !(XRecCon p) rec -- C { x = p1, y = p2 }
| InfixCon !(XInfixCon p) arg arg -- p1 `C` p2
| XHsConDetails !(XXHsConDetails p)
type family XPrefixCon p
type family XRecCon p
type family XInfixCon p
type family XXHsConDetails p
- - - - -
c8d27dd4 by Simon Jakobi at 2026-06-18T14:41:59-04:00
CI: quiet submodule clean output in after_script and setup
The clean and cleanup_submodules functions ran 'git submodule foreach
git clean -xdf', flooding the job log with 'Entering ...' and
'Removing ...' lines. Pass --quiet to 'git submodule' and -q to 'git
clean' to drop the success output; errors are still reported.
Co-Authored-By: Claude Opus 4.8 <noreply(a)anthropic.com>
- - - - -
a1fee982 by Ben Gamari at 2026-06-19T16:23:13-04:00
compiler/ipe: Prefer source ticks in current module for return frames
Previously we would simply attribute return frames to the nearest
enclosing tick in the calling frame. However, this will very frequently
produce unhelpful results (e.g. pointing to `(>>)` rather than the
calling function).
- - - - -
d0e7152c by Ben Gamari at 2026-06-19T16:23:13-04:00
compiler: Include ModuleNames in source notes
- - - - -
235 changed files:
- .gitlab-ci.yml
- .gitlab/ci.sh
- + changelog.d/T17833
- + changelog.d/T26978
- + changelog.d/T27046
- + changelog.d/T27047
- + changelog.d/T27225
- + changelog.d/T27308
- + changelog.d/T27317
- + changelog.d/T27359
- + changelog.d/deterministic-usage-order
- + changelog.d/fix-exponential-case-desugar-27383
- + changelog.d/fix-plugin-finder-multi-home-unit.md
- changelog.d/hadrian-response-files.md
- + changelog.d/hadrian-stale-package-confs-26661
- changelog.d/module-graph-reuse-in-downsweep
- changelog.d/more-efficient-home-unit-imports-finding
- + changelog.d/tag-inference-27005
- + changelog.d/tool-messages-27370
- compiler/.hlint.yaml
- compiler/GHC/Cmm/DebugBlock.hs
- compiler/GHC/Cmm/Parser.y
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
- compiler/GHC/CmmToAsm/AArch64/Instr.hs
- compiler/GHC/CmmToAsm/AArch64/Ppr.hs
- compiler/GHC/CmmToAsm/Dwarf.hs
- compiler/GHC/CmmToAsm/LA64/CodeGen.hs
- compiler/GHC/CmmToAsm/PPC/CodeGen.hs
- compiler/GHC/CmmToAsm/RV64/CodeGen.hs
- compiler/GHC/CmmToAsm/RV64/Instr.hs
- compiler/GHC/CmmToAsm/RV64/Ppr.hs
- compiler/GHC/CmmToAsm/RV64/Regs.hs
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/Core/LateCC/OverloadedCalls.hs
- compiler/GHC/Core/Opt/Simplify.hs
- compiler/GHC/Core/Ppr.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/CoreToStg.hs
- compiler/GHC/CoreToStg/AddImplicitBinds.hs
- compiler/GHC/Data/List/NonEmpty.hs
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Hs/Binds.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Arrows.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/Match.hs
- compiler/GHC/HsToCore/Match.hs-boot
- compiler/GHC/HsToCore/Match/Constructor.hs
- compiler/GHC/HsToCore/Pmc.hs
- compiler/GHC/HsToCore/Pmc/Desugar.hs
- compiler/GHC/HsToCore/Pmc/Solver.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/Bind.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Runtime/Debugger/Breakpoints.hs
- compiler/GHC/Runtime/Eval/Types.hs
- compiler/GHC/Runtime/Heap/Inspect.hs
- compiler/GHC/Stg/Debug.hs
- compiler/GHC/Stg/EnforceEpt.hs
- compiler/GHC/Stg/EnforceEpt/Rewrite.hs
- compiler/GHC/Stg/EnforceEpt/TagSig.hs
- compiler/GHC/Stg/EnforceEpt/Types.hs
- compiler/GHC/StgToCmm/Expr.hs
- compiler/GHC/StgToJS/Expr.hs
- compiler/GHC/SysTools/Process.hs
- compiler/GHC/Tc/Deriv/Generate.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Solver/Equality.hs
- compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/TyCl/Utils.hs
- compiler/GHC/Tc/Zonk/Type.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/RepType.hs
- compiler/GHC/Types/Tickish.hs
- compiler/GHC/Unit.hs
- compiler/GHC/Unit/Finder.hs
- compiler/GHC/Unit/Module/Deps.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Unit/Types.hs
- compiler/GHC/Utils/Logger.hs
- compiler/Language/Haskell/Syntax/Binds.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- compiler/Language/Haskell/Syntax/Pat.hs
- compiler/Language/Haskell/Syntax/Type.hs
- docs/users_guide/bugs.rst
- docs/users_guide/using.rst
- hadrian/build-cabal
- hadrian/src/Builder.hs
- hadrian/src/CommandLine.hs
- hadrian/src/Hadrian/Builder/Ar.hs
- hadrian/src/Hadrian/Haskell/Cabal/Parse.hs
- hadrian/src/Hadrian/Utilities.hs
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/Rts.hs
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- + libraries/base/src/Data/Double.hs
- + libraries/base/src/Data/Float.hs
- libraries/base/src/Data/List/NonEmpty.hs
- libraries/ghc-internal/src/GHC/Internal/Float.hs
- libraries/ghc-internal/src/GHC/Internal/Lexeme.hs
- libraries/process
- rts/Capability.c
- rts/Schedule.c
- rts/Timer.c
- rts/eventlog/EventLog.c
- rts/eventlog/EventLog.h
- rts/include/rts/OSThreads.h
- rts/sm/Evac.h
- testsuite/driver/testlib.py
- + testsuite/tests/codeGen/should_gen_asm/aarch64-sxth-mul2.asm
- + testsuite/tests/codeGen/should_gen_asm/aarch64-sxth-mul2.cmm
- + testsuite/tests/codeGen/should_gen_asm/aarch64-sxtw.asm
- + testsuite/tests/codeGen/should_gen_asm/aarch64-sxtw.cmm
- testsuite/tests/codeGen/should_gen_asm/all.T
- testsuite/tests/codeGen/should_run/T16617.hs
- testsuite/tests/codeGen/should_run/T16617.stdout
- + testsuite/tests/codeGen/should_run/T27046.hs
- + testsuite/tests/codeGen/should_run/T27046_cmm.cmm
- + testsuite/tests/codeGen/should_run/aarch64-sxtw-cmm.cmm
- + testsuite/tests/codeGen/should_run/aarch64-sxtw-run.hs
- + testsuite/tests/codeGen/should_run/aarch64-sxtw-run.stdout
- testsuite/tests/codeGen/should_run/all.T
- testsuite/tests/core-to-stg/T14895.stderr
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/deSugar/should_compile/T27383.hs
- testsuite/tests/deSugar/should_compile/all.T
- + testsuite/tests/driver/T27370/Makefile
- + testsuite/tests/driver/T27370/T27370.hs
- + testsuite/tests/driver/T27370/T27370.pp
- + testsuite/tests/driver/T27370/T27370.stderr
- + testsuite/tests/driver/T27370/all.T
- + testsuite/tests/driver/multipleHomeUnits/plugin01/all.T
- + testsuite/tests/driver/multipleHomeUnits/plugin01/appunit
- + testsuite/tests/driver/multipleHomeUnits/plugin01/p/MyPlugin.hs
- + testsuite/tests/driver/multipleHomeUnits/plugin01/pluginunit
- + testsuite/tests/driver/multipleHomeUnits/plugin01/q/App.hs
- + testsuite/tests/driver/multipleHomeUnits/plugin02/all.T
- + testsuite/tests/driver/multipleHomeUnits/plugin02/appunit
- + testsuite/tests/driver/multipleHomeUnits/plugin02/p/MyPlugin.hs
- + testsuite/tests/driver/multipleHomeUnits/plugin02/pluginunit
- + testsuite/tests/driver/multipleHomeUnits/plugin02/q/App.hs
- + testsuite/tests/driver/multipleHomeUnits/plugin02/r/RexLib.hs
- + testsuite/tests/driver/multipleHomeUnits/plugin02/reexportunit
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghci.debugger/scripts/all.T
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- + testsuite/tests/perf/compiler/T26426.hs
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/printer/Test24533.stdout
- + testsuite/tests/profiling/should_run/T27225.hs
- + testsuite/tests/profiling/should_run/T27225.stdout
- + testsuite/tests/profiling/should_run/T27225b.hs
- + testsuite/tests/profiling/should_run/T27225b.stdout
- testsuite/tests/profiling/should_run/all.T
- testsuite/tests/profiling/should_run/caller-cc/CallerCc1.prof.sample
- testsuite/tests/profiling/should_run/callstack001.stdout
- testsuite/tests/profiling/should_run/scc001.prof.sample
- testsuite/tests/rts/T27131.hs
- testsuite/tests/rts/T27131.stdout
- + testsuite/tests/simplCore/should_compile/T4081.hs
- + testsuite/tests/simplCore/should_compile/T4081.stderr
- testsuite/tests/simplCore/should_compile/T4201.stdout
- testsuite/tests/simplCore/should_compile/all.T
- + testsuite/tests/simplCore/should_run/T27005.hs
- + testsuite/tests/simplCore/should_run/T27005.stdout
- + testsuite/tests/simplCore/should_run/T27005_aux.hs
- testsuite/tests/simplCore/should_run/all.T
- testsuite/tests/simplStg/should_compile/T24806.hs
- testsuite/tests/simplStg/should_compile/T24806.stderr
- + testsuite/tests/simplStg/should_compile/T27005b.hs
- + testsuite/tests/simplStg/should_compile/T27005b.stderr
- testsuite/tests/simplStg/should_compile/all.T
- testsuite/tests/simplStg/should_compile/inferTags004.hs
- testsuite/tests/simplStg/should_compile/inferTags004.stderr
- + testsuite/tests/simplStg/should_run/T27005a.hs
- + testsuite/tests/simplStg/should_run/T27005a.stdout
- testsuite/tests/simplStg/should_run/all.T
- utils/check-exact/ExactPrint.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Program.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Utils.hs
- utils/haddock/CONTRIBUTING.md
- utils/haddock/haddock-api/haddock-api.cabal
- utils/haddock/haddock-api/src/Haddock.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hyperlinker.hs
- utils/haddock/haddock-api/src/Haddock/Backends/LaTeX.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Xhtml.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Xhtml/Decl.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/GhcUtils.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Create.hs
- utils/haddock/haddock-api/src/Haddock/Interface/LexParseRn.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Options.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
- utils/haddock/haddock-api/src/Haddock/Utils.hs
- utils/haddock/haddock-test/haddock-test.cabal
- utils/haddock/html-test/ref/Hash.html
- utils/haddock/html-test/ref/Test.html
- utils/haddock/html-test/ref/TypeFamilies3.html
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/34bdacc9033d864d2afb5258db59fb…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/34bdacc9033d864d2afb5258db59fb…
You're receiving this email because of your account on gitlab.haskell.org.
1
0