    {-# LANGUAGE GADTs         #-}
    {-# LANGUAGE TupleSections #-}

    module GHC.Driver.GenerateCgIPEStub (generateCgIPEStub, lookupEstimatedTicks) where

    import Data.Map.Strict (Map)
    import qualified Data.Map.Strict as Map
    import Data.Semigroup ((<>))
    import GHC.Cmm
    import GHC.Cmm.CLabel (CLabel, mkAsmTempLabel)
    import GHC.Cmm.Dataflow (O)
    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.Stream (Stream, liftIO)
    import qualified GHC.Data.Stream as Stream
    import GHC.Driver.Env (hsc_dflags, hsc_logger)
    import GHC.Driver.Env.Types (HscEnv)
    import GHC.Driver.Flags (GeneralFlag (..), DumpFlag(Opt_D_ipe_stats))
    import GHC.Driver.DynFlags (gopt, targetPlatform)
    import GHC.Driver.Config.StgToCmm
    import GHC.Driver.Config.Cmm
    import GHC.Prelude
    import GHC.Runtime.Heap.Layout (isStackRep)
    import GHC.Settings (platformTablesNextToCode)
    import GHC.StgToCmm.Monad (getCmm, initC, runC, initFCodeState)
    import GHC.StgToCmm.Prof (initInfoTableProv)
    import GHC.StgToCmm.Types (CmmCgInfos (..), ModuleLFInfos)
    import GHC.StgToCmm.Utils
    import GHC.Types.IPE (InfoTableProvMap (provInfoTables), IpeSourceLocation)
    import GHC.Types.Name.Set (NonCaffySet)
    import GHC.Types.Tickish (GenTickish (SourceNote))
    import GHC.Unit.Types (Module, moduleName)
    import GHC.Unit.Module (moduleNameString)
    import qualified GHC.Utils.Logger as Logger
    import GHC.Utils.Outputable (ppr)

    {-
    Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Stacktraces can be created from return frames as they are pushed to stack for every case scrutinee.
    But to make them readable / meaningful, one needs to know the source location of each return frame.

    Every return frame has a distinct info table and thus a distinct code pointer (for tables next to
    code) or at least a distinct address itself. Info Table Provenance Entries (IPEs) are searchable by
    this pointer and contain a source location.

    The info table / info table code pointer to source location map is described in:
    Note [Mapping Info Tables to Source Positions]

    To be able to lookup IPEs for return frames one needs to emit them during compile time. This is done
    by `generateCgIPEStub`.

    This leads to the question: How to figure out the source location of a return frame?

    The algorithm for determining source locations for stack info tables is implemented in
    `lookupEstimatedTicks` as two passes over every 'CmmGroupSRTs'. The first pass generates estimated
    source locations for any labels potentially corresponding to stack info tables in the Cmm code. The
    second pass walks over the Cmm decls and creates an entry in the IPE map for every info table,
    looking up source locations for stack info tables in the map generated during the first pass.

    The rest of this note will document exactly how the first pass generates the map from labels to
    estimated source positions. The algorithms are different depending on whether tables-next-to-code
    is on or off. Both algorithms have in common that we are looking for a `CmmNode.CmmTick`
    (containing a `SourceNote`) that is near what we estimate to be the label of a return stack frame.

    With tables-next-to-code
    ~~~~~~~~~~~~~~~~~~~~~~~~

    Let's consider this example:
    ```
     Main.returnFrame_entry() { //  [R2]
             { info_tbls: [(c18g,
                            label: block_c18g_info
                            rep: StackRep []
                            srt: Just GHC.CString.unpackCString#_closure),
                           (c18r,
                            label: Main.returnFrame_info
                            rep: HeapRep static { Fun {arity: 1 fun_type: ArgSpec 5} }
                            srt: Nothing)]
               stack_info: arg_space: 8
             }
         {offset

          [...]

           c18u: // global
               //tick src<Main.hs:(7,1)-(16,15)>
               I64[Hp - 16] = sat_s16B_info;
               P64[Hp] = _s16r::P64;
               _c17j::P64 = Hp - 16;
               //tick src<Main.hs:8:25-39>
               I64[Sp - 8] = c18g;
               R3 = _c17j::P64;
               R2 = GHC.IO.Unsafe.unsafePerformIO_closure;
               R1 = GHC.Base.$_closure;
               Sp = Sp - 8;
               call stg_ap_pp_fast(R3,
                                   R2,
                                   R1) returns to c18g, args: 8, res: 8, upd: 8;
    ```

    The return frame `block_c18g_info` has the label `c18g` which is used in the call to `stg_ap_pp_fast`
    (`returns to c18g`) as continuation (`cml_cont`). The source location we're after, is the nearest
    `//tick` before the call (`//tick src<Main.hs:8:25-39>`).

    In code the Cmm program is represented as a Hoopl graph. Hoopl distinguishes nodes by defining if they
    are open or closed on entry (one can fallthrough to them from the previous instruction) and if they are
    open or closed on exit (one can fallthrough from them to the next node).

    Please refer to the paper "Hoopl: A Modular, Reusable Library for Dataflow Analysis and Transformation"
    for a detailed explanation.

    Here we use the fact, that calls (represented by `CmmNode.CmmCall`) are always closed on exit
    (`CmmNode O C`, `O` means open, `C` closed). In other words, they are always at the end of a block.

    So, given a `CmmGraph`:
      - Look at the end of every block: If it is a `CmmNode.CmmCall` returning to some label, lookup
        the nearest `CmmNode.CmmTick` by traversing the middle part of the block backwards (from end to
        beginning).
      - Take the first `CmmNode.CmmTick` that contains a `Tickish.SourceNote` and map the label we
        found to it's payload as an `IpeSourceLocation`. (There are other `Tickish` constructors like
        `ProfNote` or `HpcTick`, these are ignored.)

    See `labelsToSourcesWithTNTC` for the implementation of this algorithm.

    Without tables-next-to-code
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~

    When tables-next-to-code is off, there is no return frame / continuation label in calls. The continuation (i.e. return
    frame) is set in an explicit Cmm assignment. Thus the tick lookup algorithm has to be slightly different.

    ```
     sat_s16G_entry() { //  [R1]
             { info_tbls: [(c18O,
                            label: sat_s16G_info
                            rep: HeapRep { Thunk }
                            srt: Just _u18Z_srt)]
               stack_info: arg_space: 0
             }
         {offset
           c18O: // global
               _s16G::P64 = R1;
               if ((Sp + 8) - 40 < SpLim) (likely: False) goto c18P; else goto c18Q;
           c18P: // global
               R1 = _s16G::P64;
               call (stg_gc_enter_1)(R1) args: 8, res: 0, upd: 8;
           c18Q: // global
               I64[Sp - 16] = stg_upd_frame_info;
               P64[Sp - 8] = _s16G::P64;
               //tick src<Main.hs:20:9-13>
               I64[Sp - 24] = block_c18M_info;
               R1 = GHC.Show.$fShow[]_closure;
               P64[Sp - 32] = GHC.Show.$fShowChar_closure;
               Sp = Sp - 32;
               call stg_ap_p_fast(R1) args: 16, res: 8, upd: 24;
         }
     },
     _blk_c18M() { //  [R1]
             { info_tbls: [(c18M,
                            label: block_c18M_info
                            rep: StackRep []
                            srt: Just System.IO.print_closure)]
               stack_info: arg_space: 0
             }
         {offset
           c18M: // global
               _s16F::P64 = R1;
               R1 = System.IO.print_closure;
               P64[Sp] = _s16F::P64;
               call stg_ap_p_fast(R1) args: 32, res: 0, upd: 24;
         }
     },
    ```

    In this example we have to lookup `//tick src<Main.hs:20:9-13>` for the return frame `c18M`.
    Notice, that this cannot be done with the `Label` `c18M`, but with the `CLabel` `block_c18M_info`
    (`label: block_c18M_info` is actually a `CLabel`).

    Given a `CmmGraph`:
      - Check every `CmmBlock` from top (first) to bottom (last).
      - If a `CmmTick` holding a `SourceNote` is found, remember the source location in the tick.
      - If an assignment of the form `... = block_c18M_info;` (a `CmmStore` whose RHS is a
        `CmmLit (CmmLabel l)`) is found, map that label to the most recently visited source note's
        location.

    See `labelsToSourcesSansTNTC` for the implementation of this algorithm.
    -}

    generateCgIPEStub
      :: HscEnv
      -> Module
      -> InfoTableProvMap
      -> ( NonCaffySet
         , ModuleLFInfos
         , Map CmmInfoTable (Maybe IpeSourceLocation)
         , IPEStats
         )
      -> Stream IO CmmGroupSRTs CmmCgInfos
    generateCgIPEStub :: HscEnv
    -> Module
    -> InfoTableProvMap
    -> (NonCaffySet, ModuleLFInfos,
        Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> Stream IO CmmGroupSRTs CmmCgInfos
    generateCgIPEStub HscEnv
    hsc_env Module
    this_mod InfoTableProvMap
    denv (NonCaffySet
    nonCaffySet, ModuleLFInfos
    moduleLFInfos, Map CmmInfoTable (Maybe IpeSourceLocation)
    infoTablesWithTickishes, IPEStats
    initStats) = do
      let dflags :: DynFlags
    dflags   = HscEnv -> DynFlags
    hsc_dflags HscEnv
    hsc_env
          platform :: Platform
    platform = DynFlags -> Platform
    targetPlatform DynFlags
    dflags
          logger :: Logger
    logger   = HscEnv -> Logger
    hsc_logger HscEnv
    hsc_env
          fstate :: FCodeState
    fstate   = Platform -> FCodeState
    initFCodeState Platform
    platform
          cmm_cfg :: CmmConfig
    cmm_cfg  = DynFlags -> CmmConfig
    initCmmConfig DynFlags
    dflags
      cgState <- IO CgState -> Stream IO CmmGroupSRTs CgState
    forall a. IO a -> Stream IO CmmGroupSRTs a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO CgState
    initC

      -- Yield Cmm for Info Table Provenance Entries (IPEs)
      let denv' = InfoTableProvMap
    denv {provInfoTables = Map.mapKeys cit_lbl infoTablesWithTickishes}
          ((mIpeStub, ipeCmmGroup), _) = runC (initStgToCmmConfig dflags this_mod) fstate cgState $ getCmm (initInfoTableProv initStats (Map.keys infoTablesWithTickishes) denv')

      (_, ipeCmmGroupSRTs) <- liftIO $ cmmPipeline logger cmm_cfg (emptySRT this_mod) ipeCmmGroup
      Stream.yield ipeCmmGroupSRTs

      ipeStub <-
        case mIpeStub of
          Just (IPEStats
    stats, CStub
    stub) -> do
            -- Print ipe stats if requested
            IO () -> Stream IO CmmGroupSRTs ()
    forall a. IO a -> Stream IO CmmGroupSRTs a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> Stream IO CmmGroupSRTs ())
    -> IO () -> Stream IO CmmGroupSRTs ()
    forall a b. (a -> b) -> a -> b
    $
              Logger -> DumpFlag -> String -> DumpFormat -> SDoc -> IO ()
    Logger.putDumpFileMaybe Logger
    logger
                DumpFlag
    Opt_D_ipe_stats
                (String
    "IPE Stats for module " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ (ModuleName -> String
    moduleNameString (ModuleName -> String) -> ModuleName -> String
    forall a b. (a -> b) -> a -> b
    $ Module -> ModuleName
    forall unit. GenModule unit -> ModuleName
    moduleName Module
    this_mod))
                DumpFormat
    Logger.FormatText
                (IPEStats -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr IPEStats
    stats)
            CStub -> Stream IO CmmGroupSRTs CStub
    forall a. a -> Stream IO CmmGroupSRTs a
    forall (m :: * -> *) a. Monad m => a -> m a
    return CStub
    stub
          Maybe (IPEStats, CStub)
    Nothing -> CStub -> Stream IO CmmGroupSRTs CStub
    forall a. a -> Stream IO CmmGroupSRTs a
    forall (m :: * -> *) a. Monad m => a -> m a
    return CStub
    forall a. Monoid a => a
    mempty

      return CmmCgInfos {cgNonCafs = nonCaffySet, cgLFInfos = moduleLFInfos, cgIPEStub = ipeStub}

    -- | Given:
    --   * an initial mapping from info tables to possible source locations,
    --   * initial 'IPEStats',
    --   * a 'CmmGroupSRTs',
    --
    -- map every info table listed in the 'CmmProc's of the group to their possible
    -- source locations and update 'IPEStats' for skipped stack info tables (in case
    -- both -finfo-table-map and -fno-info-table-map-with-stack were given). See:
    -- Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
    --
    -- Note: While it would be cleaner if we could keep the recursion and
    -- accumulation internal to this function, this cannot be done without
    -- separately traversing stream of 'CmmGroupSRTs' in 'GHC.Driver.Main'. The
    -- initial implementation of this logic did such a thing, and code generation
    -- performance suffered considerably as a result (see #23103).
    lookupEstimatedTicks
      :: HscEnv
      -> Map CmmInfoTable (Maybe IpeSourceLocation)
      -> IPEStats
      -> CmmGroupSRTs
      -> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    lookupEstimatedTicks :: HscEnv
    -> Map CmmInfoTable (Maybe IpeSourceLocation)
    -> IPEStats
    -> CmmGroupSRTs
    -> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    lookupEstimatedTicks HscEnv
    hsc_env Map CmmInfoTable (Maybe IpeSourceLocation)
    ipes IPEStats
    stats CmmGroupSRTs
    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
        -- location in the map generate during Pass 1. If the info table is a stack
        -- info table and -finfo-table-map-with-stack is not enabled, skip the table
        -- and note it as skipped in the IPE stats. If the info table is not a stack
        -- info table, insert into the IPE map with no source location information
        -- (for now; see `convertInfoProvMap` in GHC.StgToCmm.Utils to see how source
        -- locations for these tables get filled in)
        (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    forall a. a -> IO a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ((Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
     -> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats))
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> IO (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    forall a b. (a -> b) -> a -> b
    $ ((Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
     -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
     -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats))
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> CmmGroupSRTs
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    collectInfoTables (Map CmmInfoTable (Maybe IpeSourceLocation)
    ipes, IPEStats
    stats) CmmGroupSRTs
    cmm_group_srts
      where
        dflags :: DynFlags
    dflags = HscEnv -> DynFlags
    hsc_dflags HscEnv
    hsc_env
        platform :: Platform
    platform = DynFlags -> Platform
    targetPlatform DynFlags
    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)
        --
        -- 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 :: Map CLabel IpeSourceLocation
    labelsToSources =
          if Platform -> Bool
    platformTablesNextToCode Platform
    platform then
            (Map CLabel IpeSourceLocation
     -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
     -> Map CLabel IpeSourceLocation)
    -> Map CLabel IpeSourceLocation
    -> CmmGroupSRTs
    -> Map CLabel IpeSourceLocation
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' Map CLabel IpeSourceLocation
    -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    -> Map CLabel IpeSourceLocation
    labelsToSourcesWithTNTC Map CLabel IpeSourceLocation
    forall k a. Map k a
    Map.empty CmmGroupSRTs
    cmm_group_srts
          else
            (Map CLabel IpeSourceLocation
     -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
     -> Map CLabel IpeSourceLocation)
    -> Map CLabel IpeSourceLocation
    -> CmmGroupSRTs
    -> Map CLabel IpeSourceLocation
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' Map CLabel IpeSourceLocation
    -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    -> Map CLabel IpeSourceLocation
    labelsToSourcesSansTNTC Map CLabel IpeSourceLocation
    forall k a. Map k a
    Map.empty CmmGroupSRTs
    cmm_group_srts

        collectInfoTables
          :: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
          -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
          -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
        collectInfoTables :: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    collectInfoTables (!Map CmmInfoTable (Maybe IpeSourceLocation)
    acc, !IPEStats
    stats) (CmmProc CmmTopInfo
    h CLabel
    _ [GlobalReg]
    _ CmmGraph
    _) =
            ((Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
     -> Label
     -> CmmInfoTable
     -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats))
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> LabelMap CmmInfoTable
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    forall t b. (t -> Label -> b -> t) -> t -> LabelMap b -> t
    mapFoldlWithKey (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> Label
    -> CmmInfoTable
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    go (Map CmmInfoTable (Maybe IpeSourceLocation)
    acc, IPEStats
    stats) (CmmTopInfo -> LabelMap CmmInfoTable
    info_tbls CmmTopInfo
    h)
          where
            go :: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
               -> Label
               -> CmmInfoTable
               -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
            go :: (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    -> Label
    -> CmmInfoTable
    -> (Map CmmInfoTable (Maybe IpeSourceLocation), IPEStats)
    go (!Map CmmInfoTable (Maybe IpeSourceLocation)
    acc, !IPEStats
    stats) Label
    lbl' CmmInfoTable
    tbl =
              let
                lbl :: CLabel
    lbl =
                  if Platform -> Bool
    platformTablesNextToCode Platform
    platform then
                    -- TNTC case, the mapped CLabel will be the result of
                    -- mkAsmTempLabel on the info table label
                    Label -> CLabel
    forall a. Uniquable a => a -> CLabel
    mkAsmTempLabel Label
    lbl'
                  else
                    -- Non-TNTC case, the mapped CLabel will be the CLabel of the
                    -- info table itself
                    CmmInfoTable -> CLabel
    cit_lbl CmmInfoTable
    tbl
              in
                if (SMRep -> Bool
    isStackRep (SMRep -> Bool) -> (CmmInfoTable -> SMRep) -> CmmInfoTable -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . CmmInfoTable -> SMRep
    cit_rep) CmmInfoTable
    tbl then
                  if GeneralFlag -> DynFlags -> Bool
    gopt GeneralFlag
    Opt_InfoTableMapWithStack DynFlags
    dflags then
                    -- This is a stack info table and we DO want to put it in the
                    -- info table map
                    (CmmInfoTable
    -> Maybe IpeSourceLocation
    -> Map CmmInfoTable (Maybe IpeSourceLocation)
    -> Map CmmInfoTable (Maybe IpeSourceLocation)
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    Map.insert CmmInfoTable
    tbl (CLabel -> Map CLabel IpeSourceLocation -> Maybe IpeSourceLocation
    forall k a. Ord k => k -> Map k a -> Maybe a
    Map.lookup CLabel
    lbl Map CLabel IpeSourceLocation
    labelsToSources) Map CmmInfoTable (Maybe IpeSourceLocation)
    acc, IPEStats
    stats)
                  else
                    -- This is a stack info table but we DO NOT want to put it in
                    -- the info table map (-fno-info-table-map-with-stack was
                    -- given), track it as skipped
                    (Map CmmInfoTable (Maybe IpeSourceLocation)
    acc, IPEStats
    stats IPEStats -> IPEStats -> IPEStats
    forall a. Semigroup a => a -> a -> a
    <> IPEStats
    skippedIpeStats)
                else
                  -- This is not a stack info table, so put it in the map with no
                  -- source location (for now)
                  (CmmInfoTable
    -> Maybe IpeSourceLocation
    -> Map CmmInfoTable (Maybe IpeSourceLocation)
    -> Map CmmInfoTable (Maybe IpeSourceLocation)
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    Map.insert CmmInfoTable
    tbl Maybe IpeSourceLocation
    forall a. Maybe a
    Nothing Map CmmInfoTable (Maybe IpeSourceLocation)
    acc, IPEStats
    stats)
        collectInfoTables (!Map CmmInfoTable (Maybe IpeSourceLocation)
    acc, !IPEStats
    stats) GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    _ = (Map CmmInfoTable (Maybe IpeSourceLocation)
    acc, IPEStats
    stats)

    -- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
    labelsToSourcesWithTNTC
      :: Map CLabel IpeSourceLocation
      -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
      -> Map CLabel IpeSourceLocation
    labelsToSourcesWithTNTC :: Map CLabel IpeSourceLocation
    -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    -> Map CLabel IpeSourceLocation
    labelsToSourcesWithTNTC Map CLabel IpeSourceLocation
    acc (CmmProc CmmTopInfo
    _ CLabel
    _ [GlobalReg]
    _ CmmGraph
    cmm_graph) =
        (Map CLabel IpeSourceLocation
     -> CmmBlock -> Map CLabel IpeSourceLocation)
    -> Map CLabel IpeSourceLocation
    -> [CmmBlock]
    -> Map CLabel IpeSourceLocation
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' Map CLabel IpeSourceLocation
    -> CmmBlock -> Map CLabel IpeSourceLocation
    go Map CLabel IpeSourceLocation
    acc (CmmGraph -> [CmmBlock]
    toBlockList CmmGraph
    cmm_graph)
      where
        go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
        go :: Map CLabel IpeSourceLocation
    -> CmmBlock -> Map CLabel IpeSourceLocation
    go Map CLabel IpeSourceLocation
    acc CmmBlock
    block =
            case (,) (CLabel -> IpeSourceLocation -> (CLabel, IpeSourceLocation))
    -> Maybe CLabel
    -> Maybe (IpeSourceLocation -> (CLabel, IpeSourceLocation))
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe CLabel
    returnFrameLabel Maybe (IpeSourceLocation -> (CLabel, IpeSourceLocation))
    -> Maybe IpeSourceLocation -> Maybe (CLabel, IpeSourceLocation)
    forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Maybe IpeSourceLocation
    lastTickInBlock of
              Just (CLabel
    clabel, IpeSourceLocation
    src_loc) -> CLabel
    -> IpeSourceLocation
    -> Map CLabel IpeSourceLocation
    -> Map CLabel IpeSourceLocation
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    Map.insert CLabel
    clabel IpeSourceLocation
    src_loc Map CLabel IpeSourceLocation
    acc
              Maybe (CLabel, IpeSourceLocation)
    Nothing -> Map CLabel IpeSourceLocation
    acc
          where
            (CmmNode C O
    _, Block CmmNode O O
    middleBlock, CmmNode O C
    endBlock) = CmmBlock -> (CmmNode C O, Block CmmNode O O, CmmNode O C)
    forall (n :: Extensibility -> Extensibility -> *).
    Block n C C -> (n C O, Block n O O, n O C)
    blockSplit CmmBlock
    block

            returnFrameLabel :: Maybe CLabel
            returnFrameLabel :: Maybe CLabel
    returnFrameLabel =
              case CmmNode O C
    endBlock of
                (CmmCall CmmExpr
    _ (Just Label
    l) [GlobalReg]
    _ ByteOff
    _ ByteOff
    _ ByteOff
    _) -> CLabel -> Maybe CLabel
    forall a. a -> Maybe a
    Just (CLabel -> Maybe CLabel) -> CLabel -> Maybe CLabel
    forall a b. (a -> b) -> a -> b
    $ Label -> CLabel
    forall a. Uniquable a => a -> CLabel
    mkAsmTempLabel Label
    l
                CmmNode O C
    _ -> Maybe CLabel
    forall a. Maybe a
    Nothing

            lastTickInBlock :: Maybe IpeSourceLocation
    lastTickInBlock = (CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation)
    -> Maybe IpeSourceLocation
    -> [CmmNode O O]
    -> Maybe IpeSourceLocation
    forall a b. (a -> b -> b) -> b -> [a] -> b
    forall (t :: * -> *) a b.
    Foldable t =>
    (a -> b -> b) -> b -> t a -> b
    foldr CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation
    maybeTick Maybe IpeSourceLocation
    forall a. Maybe a
    Nothing (Block CmmNode O O -> [CmmNode O O]
    forall (n :: Extensibility -> Extensibility -> *).
    Block n O O -> [n O O]
    blockToList Block CmmNode O O
    middleBlock)

            maybeTick :: CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation
            maybeTick :: CmmNode O O -> Maybe IpeSourceLocation -> Maybe IpeSourceLocation
    maybeTick CmmNode O O
    _ s :: Maybe IpeSourceLocation
    s@(Just IpeSourceLocation
    _) = Maybe IpeSourceLocation
    s
            maybeTick (CmmTick (SourceNote RealSrcSpan
    span LexicalFastString
    name)) Maybe IpeSourceLocation
    Nothing = IpeSourceLocation -> Maybe IpeSourceLocation
    forall a. a -> Maybe a
    Just (RealSrcSpan
    span, LexicalFastString
    name)
            maybeTick CmmNode O O
    _ Maybe IpeSourceLocation
    _ = Maybe IpeSourceLocation
    forall a. Maybe a
    Nothing
    labelsToSourcesWithTNTC Map CLabel IpeSourceLocation
    acc GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    _ = Map CLabel IpeSourceLocation
    acc

    -- | See Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
    labelsToSourcesSansTNTC
      :: Map CLabel IpeSourceLocation
      -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
      -> Map CLabel IpeSourceLocation
    labelsToSourcesSansTNTC :: Map CLabel IpeSourceLocation
    -> GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    -> Map CLabel IpeSourceLocation
    labelsToSourcesSansTNTC Map CLabel IpeSourceLocation
    acc (CmmProc CmmTopInfo
    _ CLabel
    _ [GlobalReg]
    _ CmmGraph
    cmm_graph) =
        (Map CLabel IpeSourceLocation
     -> CmmBlock -> Map CLabel IpeSourceLocation)
    -> Map CLabel IpeSourceLocation
    -> [CmmBlock]
    -> Map CLabel IpeSourceLocation
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' Map CLabel IpeSourceLocation
    -> CmmBlock -> Map CLabel IpeSourceLocation
    go Map CLabel IpeSourceLocation
    acc (CmmGraph -> [CmmBlock]
    toBlockList CmmGraph
    cmm_graph)
      where
        go :: Map CLabel IpeSourceLocation -> CmmBlock -> Map CLabel IpeSourceLocation
        go :: Map CLabel IpeSourceLocation
    -> CmmBlock -> Map CLabel IpeSourceLocation
    go Map CLabel IpeSourceLocation
    acc CmmBlock
    block = (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    -> Map CLabel IpeSourceLocation
    forall a b. (a, b) -> a
    fst ((Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
     -> Map CLabel IpeSourceLocation)
    -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    -> Map CLabel IpeSourceLocation
    forall a b. (a -> b) -> a -> b
    $ ((Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
     -> CmmNode O O
     -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation))
    -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    -> [CmmNode O O]
    -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    -> CmmNode O O
    -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    collectLabels (Map CLabel IpeSourceLocation
    acc, Maybe IpeSourceLocation
    forall a. Maybe a
    Nothing) (Block CmmNode O O -> [CmmNode O O]
    forall (n :: Extensibility -> Extensibility -> *).
    Block n O O -> [n O O]
    blockToList Block CmmNode O O
    middleBlock)
          where
            (CmmNode C O
    _, Block CmmNode O O
    middleBlock, CmmNode O C
    _) = CmmBlock -> (CmmNode C O, Block CmmNode O O, CmmNode O C)
    forall (n :: Extensibility -> Extensibility -> *).
    Block n C C -> (n C O, Block n O O, n O C)
    blockSplit CmmBlock
    block

            collectLabels
              :: (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
              -> CmmNode O O
              -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
            collectLabels :: (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    -> CmmNode O O
    -> (Map CLabel IpeSourceLocation, Maybe IpeSourceLocation)
    collectLabels (!Map CLabel IpeSourceLocation
    acc, Maybe IpeSourceLocation
    lastTick) CmmNode O O
    b =
              case (CmmNode O O
    b, Maybe IpeSourceLocation
    lastTick) of
                (CmmStore CmmExpr
    _ (CmmLit (CmmLabel CLabel
    l)) AlignmentSpec
    _, Just IpeSourceLocation
    src_loc) ->
                  (CLabel
    -> IpeSourceLocation
    -> Map CLabel IpeSourceLocation
    -> Map CLabel IpeSourceLocation
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    Map.insert CLabel
    l IpeSourceLocation
    src_loc Map CLabel IpeSourceLocation
    acc, Maybe IpeSourceLocation
    forall a. Maybe a
    Nothing)
                (CmmTick (SourceNote RealSrcSpan
    span LexicalFastString
    name), Maybe IpeSourceLocation
    _) ->
                  (Map CLabel IpeSourceLocation
    acc, IpeSourceLocation -> Maybe IpeSourceLocation
    forall a. a -> Maybe a
    Just (RealSrcSpan
    span, LexicalFastString
    name))
                (CmmNode O O, Maybe IpeSourceLocation)
    _ -> (Map CLabel IpeSourceLocation
    acc, Maybe IpeSourceLocation
    lastTick)
    labelsToSourcesSansTNTC Map CLabel IpeSourceLocation
    acc GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph
    _ = Map CLabel IpeSourceLocation
    acc