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,60 @@ 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 block may carry several nested `SourceNote`s before its terminating call.
    
    197
    +For example, when `>>`/`>>=` (and their continuations) are inlined into a user
    
    198
    +`do`-block at `-O`, the case continuation of a blocking primop like `takeMVar#`
    
    199
    +is wrapped in notes for both the combinator's definition (e.g.
    
    200
    +`GHC.Internal.Base.thenIO`, `Base.hs`) and the user's call site (the `do`
    
    201
    +statement). Naively taking the *nearest* note attributes the return frame to the
    
    202
    +library combinator, so IPE/`-finfo-table-map` backtraces of a thread blocked in
    
    203
    +such a primop show no user frames at all.
    
    204
    +
    
    205
    +Concretely, when compiling (at -O1)
    
    206
    +
    
    207
    +    -- Main.hs
    
    208
    +    main = do { v <- newEmptyMVar; takeMVar v; putStrLn "world" }
    
    209
    +
    
    210
    +the proc for `main` begins with `main`'s own span and reaches `takeMVar#`
    
    211
    +in a block carrying only the inlined `>>` combinator's ticks, while the next
    
    212
    +tick of `main` itself lands in the *continuation* block:
    
    213
    +
    
    214
    +    cEntry:
    
    215
    +        //tick src<Main.hs:(2,1)-(2,58)>      -- (Tick 2) the enclosing `main` span
    
    216
    +        ... newEmptyMVar# etc. ...
    
    217
    +    cBlk:
    
    218
    +        //tick src<.../Base.hs:2336:37>       -- only library ticks in this block:
    
    219
    +        //tick src<.../Base.hs:2336:67-71>    -- the inlined `>>` (`thenIO`)
    
    220
    +        call stg_takeMVar#(R1) returns to cCont, args: 8, res: 8, upd: 8;
    
    221
    +    cCont:                                    -- the takeMVar continuation body
    
    222
    +        //tick src<.../Base.hs:2336:60-71>
    
    223
    +        //tick src<Main.hs:2:34-43>           -- (Tick 1) the user tick, in a
    
    224
    +                                              -- *sibling* block
    
    225
    +
    
    226
    +A return frame is attributed to the ticks of the block that *ends* in its call
    
    227
    +(see `labelsToSourcesWithTNTC`). So `cCont` is sourced from `cBlk`, which has no
    
    228
    +`Main.hs` tick at all. This is unhelpful to the user.
    
    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 frame's block whose file is that of the module being
    
    234
    +     compiled — the precise user call site. `cBlk` has none.
    
    235
    +  2. failing that, the proc's *enclosing* current-module note (its function's own
    
    236
    +     span, i.e. the outermost user `SourceNote` in the proc) — here
    
    237
    +     `src<Main.hs:(2,1)-(2,58)>`, named `main`. Without this, `cCont` would
    
    238
    +     take the nearest note `Base.hs:2336:67-71` (named `thenIO`) and, due to
    
    239
    +     rule (3) below, attribute it to the module being compiled. This would result
    
    240
    +     in a reference to `Main.thenIO`, a binding which does not exist.
    
    241
    +     does not exist. In the example above, (T2) labels it `Main.main` instead.
    
    242
    +  3. failing that, the nearest note of any module.
    
    243
    +
    
    244
    +This mirrors the same-file preference the DWARF path uses in
    
    245
    +`GHC.Cmm.DebugBlock.bestSrcTick` and that `GHC.Stg.Debug.quickSourcePos` uses for
    
    246
    +closures.
    
    189 247
     -}
    
    190 248
     
    
    191 249
     generateCgIPEStub
    
    ... ... @@ -257,11 +315,12 @@ generateCgIPEStub hsc_env this_mod denv (nonCaffySet, moduleLFInfos, infoTablesW
    257 315
     -- performance suffered considerably as a result (see #23103).
    
    258 316
     lookupEstimatedTicks
    
    259 317
       :: HscEnv
    
    318
    +  -> ModLocation -- ^ location of the module being compiled, for IPE provenance
    
    260 319
       -> Map CmmInfoTable (Maybe IpeSourceLocation)
    
    261 320
       -> IPEStats
    
    262 321
       -> CmmGroupSRTs
    
    263 322
       -> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    
    264
    -lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
    
    323
    +lookupEstimatedTicks hsc_env mod_location ipes stats cmm_group_srts =
    
    265 324
         -- Pass 2: Create an entry in the IPE map for every info table listed in
    
    266 325
         -- this CmmGroupSRTs. If the info table is a stack info table and
    
    267 326
         -- -finfo-table-map-with-stack is enabled, look up its estimated source
    
    ... ... @@ -276,6 +335,11 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
    276 335
         dflags = hsc_dflags hsc_env
    
    277 336
         platform = targetPlatform dflags
    
    278 337
     
    
    338
    +    -- Source file of the module being compiled, used to prefer current-module
    
    339
    +    -- source ticks for return frames. See Note [Prefer current-module source
    
    340
    +    -- ticks for return frames].
    
    341
    +    mb_src_file = mkFastString <$> ml_hs_file mod_location
    
    342
    +
    
    279 343
         -- Pass 1: Map every label meeting the conditions described in Note
    
    280 344
         -- [Stacktraces from Info Table Provenance Entries (IPE based stack
    
    281 345
         -- unwinding)] to the estimated source location (also as described in the
    
    ... ... @@ -286,9 +350,9 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
    286 350
         labelsToSources :: Map CLabel IpeSourceLocation
    
    287 351
         labelsToSources =
    
    288 352
           if platformTablesNextToCode platform then
    
    289
    -        foldl' labelsToSourcesWithTNTC Map.empty cmm_group_srts
    
    353
    +        foldl' (labelsToSourcesWithTNTC mb_src_file) Map.empty cmm_group_srts
    
    290 354
           else
    
    291
    -        foldl' labelsToSourcesSansTNTC Map.empty cmm_group_srts
    
    355
    +        foldl' (labelsToSourcesSansTNTC mb_src_file) Map.empty cmm_group_srts
    
    292 356
     
    
    293 357
         collectInfoTables
    
    294 358
           :: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    
    ... ... @@ -331,15 +395,16 @@ lookupEstimatedTicks hsc_env ipes stats cmm_group_srts =
    331 395
     
    
    332 396
     -- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
    
    333 397
     labelsToSourcesWithTNTC
    
    334
    -  :: Map CLabel IpeSourceLocation
    
    398
    +  :: Maybe FastString -- ^ source file of the module being compiled
    
    399
    +  -> Map CLabel IpeSourceLocation
    
    335 400
       -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    
    336 401
       -> Map CLabel IpeSourceLocation
    
    337
    -labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
    
    402
    +labelsToSourcesWithTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
    
    338 403
         foldl' go acc (toBlockList cmm_graph)
    
    339 404
       where
    
    340 405
         go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
    
    341 406
         go acc block =
    
    342
    -        case (,) <$> returnFrameLabel <*> lastTickInBlock of
    
    407
    +        case (,) <$> returnFrameLabel <*> bestTickInBlock of
    
    343 408
               Just (clabel, src_loc) -> Map.insert clabel src_loc acc
    
    344 409
               Nothing -> acc
    
    345 410
           where
    
    ... ... @@ -351,36 +416,84 @@ labelsToSourcesWithTNTC acc (CmmProc _ _ _ cmm_graph) =
    351 416
                 (CmmCall _ (Just l) _ _ _ _) -> Just $ mkAsmTempLabel l
    
    352 417
                 _ -> Nothing
    
    353 418
     
    
    354
    -        lastTickInBlock = foldr maybeTick Nothing (blockToList middleBlock)
    
    355
    -
    
    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
    
    419
    +        -- All SourceNotes in the block, in block order (so the last is the one
    
    420
    +        -- nearest the terminating call). Prefer a note from the module being
    
    421
    +        -- compiled, so that e.g. continuations of inlined library combinators
    
    422
    +        -- (>>, >>=) point at the user's call site rather than the combinator's
    
    423
    +        -- definition. See Note [Prefer current-module source ticks for return frames].
    
    424
    +        -- This mirrors GHC.Cmm.DebugBlock.bestSrcTick.
    
    425
    +        bestTickInBlock = preferThisFile mb_src_file procFallback (blockToList middleBlock)
    
    426
    +
    
    427
    +    -- Enclosing current-module note for the whole proc (its function's own
    
    428
    +    -- span), used when a return frame's own block has no current-module tick.
    
    429
    +    procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
    
    430
    +labelsToSourcesWithTNTC _ acc _ = acc
    
    431
    +
    
    432
    +-- | Pick the 'IpeSourceLocation' to attribute to a return frame from the
    
    433
    +-- source-note-bearing nodes of its block (in block order).
    
    434
    +--
    
    435
    +-- See Note [Prefer current-module source ticks for return frames].
    
    436
    +preferThisFile :: Maybe FastString -> Maybe IpeSourceLocation -> [CmmNode O O] -> Maybe IpeSourceLocation
    
    437
    +preferThisFile mb_src_file procFallback nodes =
    
    438
    +    nearest fromThisFile <|> procFallback <|> nearest sourceNotes
    
    439
    +  where
    
    440
    +    sourceNotes = [ (span, name) | CmmTick (SourceNote span name) <- nodes ]
    
    441
    +    fromThisFile = case mb_src_file of
    
    442
    +      Just f  -> filter ((== f) . srcSpanFile . fst) sourceNotes
    
    443
    +      Nothing -> []
    
    444
    +    nearest = listToMaybe . reverse
    
    445
    +
    
    446
    +-- | The outermost 'SourceNote' from the module being compiled across a proc's
    
    447
    +-- blocks (in 'toBlockList' order, so the entry block's note — the function's own
    
    448
    +-- span — comes first). Used as a fallback so inlined cross-module code is still
    
    449
    +-- labelled with the enclosing user function. 'Nothing' when the proc has no
    
    450
    +-- current-module note (e.g. when compiling the library itself).
    
    451
    +enclosingThisFileTick :: Maybe FastString -> [CmmBlock] -> Maybe IpeSourceLocation
    
    452
    +enclosingThisFileTick mb_src_file blocks =
    
    453
    +    listToMaybe
    
    454
    +      [ (span, name)
    
    455
    +      | b <- blocks
    
    456
    +      , let (_, mid, _) = blockSplit b
    
    457
    +      , CmmTick (SourceNote span name) <- blockToList mid
    
    458
    +      , Just (srcSpanFile span) == mb_src_file ]
    
    361 459
     
    
    362 460
     -- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
    
    363 461
     labelsToSourcesSansTNTC
    
    364
    -  :: Map CLabel IpeSourceLocation
    
    462
    +  :: Maybe FastString -- ^ source file of the module being compiled
    
    463
    +  -> Map CLabel IpeSourceLocation
    
    365 464
       -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    
    366 465
       -> Map CLabel IpeSourceLocation
    
    367
    -labelsToSourcesSansTNTC acc (CmmProc _ _ _ cmm_graph) =
    
    466
    +labelsToSourcesSansTNTC mb_src_file acc (CmmProc _ _ _ cmm_graph) =
    
    368 467
         foldl' go acc (toBlockList cmm_graph)
    
    369 468
       where
    
    469
    +    -- See 'enclosingThisFileTick'.
    
    470
    +    procFallback = enclosingThisFileTick mb_src_file (toBlockList cmm_graph)
    
    471
    +
    
    370 472
         go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
    
    371
    -    go acc block = fst $ foldl' collectLabels (acc, Nothing) (blockToList middleBlock)
    
    473
    +    go acc block = fst $ foldl' collectLabels (acc, (Nothing, Nothing)) (blockToList middleBlock)
    
    372 474
           where
    
    373 475
             (_, middleBlock, _) = blockSplit block
    
    374 476
     
    
    477
    +        -- We track the nearest preceding SourceNote from the module being
    
    478
    +        -- compiled and the nearest of any module, and prefer the former (then
    
    479
    +        -- the proc's enclosing current-module note) when attributing a return
    
    480
    +        -- frame. See 'preferThisFile' and
    
    481
    +        -- Note [Prefer current-module source ticks for return frames].
    
    375 482
             collectLabels
    
    376
    -          :: (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    
    483
    +          :: (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
    
    377 484
               -> 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
    485
    +          -> (Map CLabel IpeSourceLocation, (Maybe IpeSourceLocation, Maybe IpeSourceLocation))
    
    486
    +        collectLabels (!acc, st@(lastThis, lastAny)) b =
    
    487
    +          case b of
    
    488
    +            CmmStore _ (CmmLit (CmmLabel l)) _ ->
    
    489
    +              case lastThis <|> procFallback <|> lastAny of
    
    490
    +                Just src_loc -> (Map.insert l src_loc acc, (Nothing, Nothing))
    
    491
    +                Nothing      -> (acc, st)
    
    492
    +            CmmTick (SourceNote span name) ->
    
    493
    +              let tick = (span, name)
    
    494
    +                  lastThis'
    
    495
    +                    | Just (srcSpanFile span) == mb_src_file = Just tick
    
    496
    +                    | otherwise                              = lastThis
    
    497
    +              in (acc, (lastThis', Just tick))
    
    498
    +            _ -> (acc, st)
    
    499
    +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