Ben Gamari pushed to branch wip/ipe-return-prefer-current-mod at Glasgow Haskell Compiler / GHC

Commits:

2 changed files:

Changes:

  • compiler/GHC/Driver/GenerateCgIPEStub.hs
    1 1
     module GHC.Driver.GenerateCgIPEStub (generateCgIPEStub, lookupEstimatedTicks) where
    
    2 2
     
    
    3
    +import Control.Applicative ((<|>))
    
    3 4
     import Data.Map.Strict (Map)
    
    4 5
     import qualified Data.Map.Strict as Map
    
    6
    +import Data.Maybe (listToMaybe)
    
    5 7
     import Data.Semigroup ((<>))
    
    6 8
     import GHC.Cmm
    
    7 9
     import GHC.Cmm.CLabel (CLabel, mkAsmTempLabel)
    
    ... ... @@ -10,6 +12,7 @@ import GHC.Cmm.Dataflow.Block (blockSplit, blockToList)
    10 12
     import GHC.Cmm.Dataflow.Label
    
    11 13
     import GHC.Cmm.Info.Build (emptySRT)
    
    12 14
     import GHC.Cmm.Pipeline (cmmPipeline)
    
    15
    +import GHC.Data.FastString (FastString, mkFastString)
    
    13 16
     import GHC.Data.Stream (liftIO, liftEff)
    
    14 17
     import qualified GHC.Data.Stream as Stream
    
    15 18
     import GHC.Driver.Env (hsc_dflags, hsc_logger)
    
    ... ... @@ -28,9 +31,10 @@ import GHC.StgToCmm.Utils
    28 31
     import GHC.StgToCmm.CgUtils (CgStream)
    
    29 32
     import GHC.Types.IPE (InfoTableProvMap (provInfoTables), IpeSourceLocation)
    
    30 33
     import GHC.Types.Name.Set (NonCaffySet)
    
    34
    +import GHC.Types.SrcLoc (srcSpanFile)
    
    31 35
     import GHC.Types.Tickish (GenTickish (SourceNote))
    
    32 36
     import GHC.Unit.Types (Module, moduleName)
    
    33
    -import GHC.Unit.Module (moduleNameString)
    
    37
    +import GHC.Unit.Module (moduleNameString, ModLocation, ml_hs_file)
    
    34 38
     import qualified GHC.Utils.Logger as Logger
    
    35 39
     import GHC.Utils.Outputable (ppr)
    
    36 40
     import GHC.Types.Unique.DSM
    
    ... ... @@ -186,6 +190,59 @@ Given a `CmmGraph`:
    186 190
         location.
    
    187 191
     
    
    188 192
     See `labelsToSourcesSansTNTC` for the implementation of this algorithm.
    
    193
    +
    
    194
    +Note [Prefer current-module source ticks for return frames]
    
    195
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    196
    +A return frame's source location is taken from the `SourceNote`s of the block
    
    197
    +that *ends* in the frame's call (see Note [Stacktraces from Info Table Provenance
    
    198
    +Entries...] above and `labelsToSourcesWithTNTC`). At `-O`, inlining means such a
    
    199
    +block frequently carries `SourceNote`s for inlined library glue (`>>`, `>>=`,
    
    200
    +`threadDelay`, ...) and the *nearest* note โ€” the one historically chosen โ€” is
    
    201
    +often a library note rather than the user's code. The resulting IPE entry then
    
    202
    +points at the library (its file and label; see `toCgIPE` in
    
    203
    +GHC.StgToCmm.InfoTableProv, which takes the file from the note's own span), so a
    
    204
    +backtrace of a thread blocked in such a primop shows no user frames.
    
    205
    +
    
    206
    +For a concrete example, compile (at -O1)
    
    207
    +
    
    208
    +    -- Scan.hs
    
    209
    +    f3 :: IO ()
    
    210
    +    f3 = threadDelay 1000000 >> putStrLn "done"
    
    211
    +
    
    212
    +The body of `f3` reaches the inlined `threadDelay`'s internal `delay#` in a
    
    213
    +block whose notes are *all* from `Conc.IO`/`Base` โ€” the user's `Scan.hs` tick
    
    214
    +for `f3` sits only in the proc's entry block:
    
    215
    +
    
    216
    +    entry:                                    -- the proc's entry block
    
    217
    +        //tick src<.../Base.hs:2305:5-18>
    
    218
    +        //tick src<Scan.hs:13:1-43>           -- the enclosing `f3` span
    
    219
    +        //tick src<.../Conc/IO.hs:(223,1)-(235,10)>
    
    220
    +        ...
    
    221
    +    delayBlk:                                 -- no Scan.hs note here:
    
    222
    +        //tick src<.../Conc/IO.hs:232:5-13>
    
    223
    +        //tick src<.../Base.hs:2268:1-9>
    
    224
    +        //tick src<.../Conc/IO.hs:(232,25)-(235,10)>   -- nearest note
    
    225
    +        call stg_delay#(R1) returns to delayCont, args: 8, res: 8, upd: 8;
    
    226
    +
    
    227
    +Naively taking the nearest note attributes `delayCont` to `Conc/IO.hs:232`,
    
    228
    +i.e. an internal of `threadDelay`, rather than `f3`.
    
    229
    +
    
    230
    +To fix this we attribute a return frame's source location in the following
    
    231
    +preference order:
    
    232
    +
    
    233
    +  1. the nearest tick in the frame's block whose file is that of the module
    
    234
    +     being compiled - the precise user call site. (When the user makes a
    
    235
    +     blocking call directly, e.g. `f v = takeMVar v`, such a note is present in
    
    236
    +     the call's block and this rule suffices; the `delayBlk` above has none.)
    
    237
    +  2. failing that, the proc's *enclosing* current-module note (the outermost
    
    238
    +     current-module `SourceNote` in the proc, i.e. its function's own span).
    
    239
    +     For `f3` this is `src<Scan.hs:13:1-43>`, so `delayCont` is attributed to
    
    240
    +     `f3` rather than to `threadDelay`'s internals.
    
    241
    +  3. failing that, the nearest note of any module (the historical behaviour).
    
    242
    +
    
    243
    +This mirrors the same-file preference the DWARF path uses in
    
    244
    +`GHC.Cmm.DebugBlock.bestSrcTick` and that `GHC.Stg.Debug.quickSourcePos` uses
    
    245
    +for closures.
    
    189 246
     -}
    
    190 247
     
    
    191 248
     generateCgIPEStub
    
    ... ... @@ -257,11 +314,12 @@ generateCgIPEStub hsc_env this_mod denv (nonCaffySet, moduleLFInfos, infoTablesW
    257 314
     -- performance suffered considerably as a result (see #23103).
    
    258 315
     lookupEstimatedTicks
    
    259 316
       :: HscEnv
    
    317
    +  -> ModLocation -- ^ location of the module being compiled, for IPE provenance
    
    260 318
       -> Map CmmInfoTable (Maybe IpeSourceLocation)
    
    261 319
       -> IPEStats
    
    262 320
       -> CmmGroupSRTs
    
    263 321
       -> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    
    264
    -lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
    
    322
    +lookupEstimatedTicks hsc_env mod_location ipes stats cmm_group_srts =
    
    265 323
         -- Pass 2: Create an entry in the IPE map for every info table listed in
    
    266 324
         -- this CmmGroupSRTs. If the info table is a stack info table and
    
    267 325
         -- -finfo-table-map-with-stack is enabled, look up its estimated source
    
    ... ... @@ -276,6 +334,11 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
    276 334
         dflags = hsc_dflags hsc_env
    
    277 335
         platform = targetPlatform dflags
    
    278 336
     
    
    337
    +    -- Source file of the module being compiled, used to prefer current-module
    
    338
    +    -- source ticks for return frames. See Note [Prefer current-module source
    
    339
    +    -- ticks for return frames].
    
    340
    +    mb_src_file = mkFastString <$> ml_hs_file mod_location
    
    341
    +
    
    279 342
         -- Pass 1: Map every label meeting the conditions described in Note
    
    280 343
         -- [Stacktraces from Info Table Provenance Entries (IPE based stack
    
    281 344
         -- unwinding)] to the estimated source location (also as described in the
    
    ... ... @@ -286,9 +349,9 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
    286 349
         labelsToSources :: Map CLabel IpeSourceLocation
    
    287 350
         labelsToSources =
    
    288 351
           if platformTablesNextToCode platform then
    
    289
    -        foldl' labelsToSourcesWithTNTC Map.empty cmm_group_srts
    
    352
    +        foldl' (labelsToSourcesWithTNTC mb_src_file) Map.empty cmm_group_srts
    
    290 353
           else
    
    291
    -        foldl' labelsToSourcesSansTNTC Map.empty cmm_group_srts
    
    354
    +        foldl' (labelsToSourcesSansTNTC mb_src_file) Map.empty cmm_group_srts
    
    292 355
     
    
    293 356
         collectInfoTables
    
    294 357
           :: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    
    ... ... @@ -331,15 +394,16 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
    331 394
     
    
    332 395
     -- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
    
    333 396
     labelsToSourcesWithTNTC
    
    334
    -  :: Map CLabel IpeSourceLocation
    
    397
    +  :: Maybe FastString -- ^ source file of the module being compiled
    
    398
    +  -> Map CLabel IpeSourceLocation
    
    335 399
       -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    
    336 400
       -> Map CLabel IpeSourceLocation
    
    337
    -labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
    
    401
    +labelsToSourcesWithTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
    
    338 402
         foldl' go acc (toBlockList cmm_graph)
    
    339 403
       where
    
    340 404
         go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
    
    341 405
         go acc block =
    
    342
    -        case (,) <$> returnFrameLabel <*> lastTickInBlock of
    
    406
    +        case (,) <$> returnFrameLabel <*> bestTickInBlock of
    
    343 407
               Just (clabel, src_loc) -> Map.insert clabel src_loc acc
    
    344 408
               Nothing -> acc
    
    345 409
           where
    
    ... ... @@ -351,36 +415,80 @@ labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
    351 415
                 (CmmCall _ (Just l) _ _ _ _) -> Just $ mkAsmTempLabel l
    
    352 416
                 _ -> Nothing
    
    353 417
     
    
    354
    -        lastTickInBlock = foldr maybeTick Nothing (blockToList middleBlock)
    
    418
    +        -- All SourceNotes in the block, in block order.
    
    419
    +        -- See Note [Prefer current-module source ticks for return frames].
    
    420
    +        bestTickInBlock = preferThisFile mb_src_file procFallback (blockToList middleBlock)
    
    421
    +
    
    422
    +    -- Enclosing current-module note for the whole proc (its function's own
    
    423
    +    -- span), used when a return frame's own block has no current-module tick.
    
    424
    +    procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
    
    425
    +labelsToSourcesWithTNTC _ acc _ = acc
    
    355 426
     
    
    356
    -        maybeTick :: CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation
    
    357
    -        maybeTick _ s@(Just _) = s
    
    358
    -        maybeTick (CmmTick (SourceNote span name)) Nothing = Just (span, name)
    
    359
    -        maybeTick _ _ = Nothing
    
    360
    -labelsToSourcesWithTNTC acc _ = acc
    
    427
    +-- | Pick the 'IpeSourceLocation' to attribute to a return frame from the
    
    428
    +-- source-note-bearing nodes of its block (in block order).
    
    429
    +--
    
    430
    +-- See Note [Prefer current-module source ticks for return frames].
    
    431
    +preferThisFile :: Maybe FastString -> Maybe IpeSourceLocation -> [CmmNode O O] -> Maybe IpeSourceLocation
    
    432
    +preferThisFile mb_src_file procFallback nodes =
    
    433
    +    nearest fromThisFile <|> procFallback <|> nearest sourceNotes
    
    434
    +  where
    
    435
    +    sourceNotes = [ (span, name) | CmmTick (SourceNote span name) <- nodes ]
    
    436
    +    fromThisFile = case mb_src_file of
    
    437
    +      Just f  -> filter ((== f) . srcSpanFile . fst) sourceNotes
    
    438
    +      Nothing -> []
    
    439
    +    nearest = listToMaybe . reverse
    
    440
    +
    
    441
    +-- | The outermost 'SourceNote' from the module being compiled across a proc's
    
    442
    +-- blocks (in 'toBlockList' order, so the entry block's note โ€” the function's own
    
    443
    +-- span โ€” comes first). Used as a fallback so inlined cross-module code is still
    
    444
    +-- labelled with the enclosing user function. 'Nothing' when the proc has no
    
    445
    +-- current-module note (e.g. when compiling the library itself).
    
    446
    +enclosingThisFileTick :: Maybe FastString -> [CmmBlock] -> Maybe IpeSourceLocation
    
    447
    +enclosingThisFileTick mb_src_file blocks =
    
    448
    +    listToMaybe
    
    449
    +      [ (span, name)
    
    450
    +      | b <- blocks
    
    451
    +      , let (_, mid, _) = blockSplit b
    
    452
    +      , CmmTick (SourceNote span name) <- blockToList mid
    
    453
    +      , Just (srcSpanFile span) == mb_src_file ]
    
    361 454
     
    
    362 455
     -- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
    
    363 456
     labelsToSourcesSansTNTC
    
    364
    -  :: Map CLabel IpeSourceLocation
    
    457
    +  :: Maybe FastString -- ^ source file of the module being compiled
    
    458
    +  -> Map CLabel IpeSourceLocation
    
    365 459
       -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    
    366 460
       -> Map CLabel IpeSourceLocation
    
    367
    -labelsToSourcesSansTNTC acc (CmmProc _ _ _ cmm_graph) =
    
    461
    +labelsToSourcesSansTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
    
    368 462
         foldl' go acc (toBlockList cmm_graph)
    
    369 463
       where
    
    464
    +    -- See 'enclosingThisFileTick'.
    
    465
    +    procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
    
    466
    +
    
    370 467
         go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
    
    371
    -    go acc block = fst $ foldl' collectLabels (acc, Nothing) (blockToList middleBlock)
    
    468
    +    go acc block = fst $ foldl' collectLabels (acc, (Nothing, Nothing)) (blockToList middleBlock)
    
    372 469
           where
    
    373 470
             (_, middleBlock, _) = blockSplit block
    
    374 471
     
    
    472
    +        -- We track the nearest preceding SourceNote from the module being
    
    473
    +        -- compiled and the nearest of any module, and prefer the former (then
    
    474
    +        -- the proc's enclosing current-module note) when attributing a return
    
    475
    +        -- frame. See 'preferThisFile' and
    
    476
    +        -- Note [Prefer current-module source ticks for return frames].
    
    375 477
             collectLabels
    
    376
    -          :: (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    
    478
    +          :: (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
    
    377 479
               -> CmmNode O O
    
    378
    -          -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    
    379
    -        collectLabels (!acc, lastTick) b =
    
    380
    -          case (b, lastTick) of
    
    381
    -            (CmmStore _ (CmmLit (CmmLabel l)) _, Just src_loc) ->
    
    382
    -              (Map.insert l src_loc acc, Nothing)
    
    383
    -            (CmmTick (SourceNote span name), _) ->
    
    384
    -              (acc, Just (span, name))
    
    385
    -            _ -> (acc, lastTick)
    
    386
    -labelsToSourcesSansTNTC acc _ = acc
    480
    +          -> (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
    
    481
    +        collectLabels (!acc, st@(lastThis, lastAny)) b =
    
    482
    +          case b of
    
    483
    +            CmmStore _ (CmmLit (CmmLabel l)) _ ->
    
    484
    +              case lastThis <|> procFallback <|> lastAny of
    
    485
    +                Just src_loc -> (Map.insert l src_loc acc, (Nothing, Nothing))
    
    486
    +                Nothing      -> (acc, st)
    
    487
    +            CmmTick (SourceNote span name) ->
    
    488
    +              let tick = (span, name)
    
    489
    +                  lastThis'
    
    490
    +                    | Just (srcSpanFile span) == mb_src_file = Just tick
    
    491
    +                    | otherwise                              = lastThis
    
    492
    +              in (acc, (lastThis', Just tick))
    
    493
    +            _ -> (acc, st)
    
    494
    +labelsToSourcesSansTNTC _ acc _ = acc

  • compiler/GHC/Driver/Main/Compile.hs
    ... ... @@ -699,7 +699,7 @@ hscGenHardCode hsc_env cgguts mod_loc output_filename = do
    699 699
                 _          ->
    
    700 700
                   do
    
    701 701
                   cmms <- {-# SCC "StgToCmm" #-}
    
    702
    -                doCodeGen hsc_env this_mod denv tycons
    
    702
    +                doCodeGen hsc_env this_mod mod_loc denv tycons
    
    703 703
                     cost_centre_info
    
    704 704
                     stg_binds
    
    705 705
     
    
    ... ... @@ -956,14 +956,17 @@ This reduces residency towards the end of the CodeGen phase significantly
    956 956
     (5-10%).
    
    957 957
     -}
    
    958 958
     
    
    959
    -doCodeGen :: HscEnv -> Module -> InfoTableProvMap -> [TyCon]
    
    959
    +doCodeGen :: HscEnv -> Module
    
    960
    +          -> ModLocation -- ^ location of the module being compiled, used to
    
    961
    +                         -- prefer current-module IPE source locations
    
    962
    +          -> InfoTableProvMap -> [TyCon]
    
    960 963
               -> CollectedCCs
    
    961 964
               -> [CgStgTopBinding] -- ^ Bindings come already annotated with fvs
    
    962 965
               -> IO (CgStream CmmGroupSRTs CmmCgInfos)
    
    963 966
              -- Note we produce a 'Stream' of CmmGroups, so that the
    
    964 967
              -- backend can be run incrementally.  Otherwise it generates all
    
    965 968
              -- the C-- up front, which has a significant space cost.
    
    966
    -doCodeGen hsc_env this_mod denv tycons
    
    969
    +doCodeGen hsc_env this_mod mod_location denv tycons
    
    967 970
                   cost_centre_info stg_binds_w_fvs = do
    
    968 971
         let dflags     = hsc_dflags hsc_env
    
    969 972
             logger     = hsc_logger hsc_env
    
    ... ... @@ -1032,7 +1035,7 @@ doCodeGen hsc_env this_mod denv tycons
    1032 1035
               -- Positions] in GHC.Stg.Debug.
    
    1033 1036
               (ipes', stats') <-
    
    1034 1037
                 if (gopt Opt_InfoTableMap dflags) then
    
    1035
    -              liftIO $ lookupEstimatedTicks hsc_env ipes stats cmm_srts
    
    1038
    +              liftIO $ lookupEstimatedTicks hsc_env mod_location ipes stats cmm_srts
    
    1036 1039
                 else
    
    1037 1040
                   return (ipes, stats)
    
    1038 1041