Matthew Pickering pushed to branch wip/stable-ipe-info at Glasgow Haskell Compiler / GHC

Commits:

18 changed files:

Changes:

  • compiler/GHC/Cmm.hs
    ... ... @@ -278,6 +278,7 @@ data SectionType
    278 278
       | InitArray           -- .init_array on ELF, .ctor on Windows
    
    279 279
       | FiniArray           -- .fini_array on ELF, .dtor on Windows
    
    280 280
       | CString
    
    281
    +  | IPE
    
    281 282
       | OtherSection String
    
    282 283
       deriving (Show)
    
    283 284
     
    
    ... ... @@ -298,6 +299,7 @@ sectionProtection (Section t _) = case t of
    298 299
         CString                 -> ReadOnlySection
    
    299 300
         Data                    -> ReadWriteSection
    
    300 301
         UninitialisedData       -> ReadWriteSection
    
    302
    +    IPE                     -> ReadWriteSection
    
    301 303
         (OtherSection _)        -> ReadWriteSection
    
    302 304
     
    
    303 305
     {-
    
    ... ... @@ -557,4 +559,5 @@ pprSectionType s = doubleQuotes $ case s of
    557 559
       InitArray               -> text "initarray"
    
    558 560
       FiniArray               -> text "finiarray"
    
    559 561
       CString                 -> text "cstring"
    
    562
    +  IPE                     -> text "ipe"
    
    560 563
       OtherSection s'         -> text s'

  • compiler/GHC/CmmToAsm/PPC/Ppr.hs
    ... ... @@ -285,6 +285,9 @@ pprAlignForSection platform seg = line $
    285 285
            Data
    
    286 286
             | ppc64          -> text ".align 3"
    
    287 287
             | otherwise      -> text ".align 2"
    
    288
    +       IPE
    
    289
    +        | ppc64          -> text ".align 3"
    
    290
    +        | otherwise      -> text ".align 2"
    
    288 291
            ReadOnlyData
    
    289 292
             | ppc64          -> text ".align 3"
    
    290 293
             | otherwise      -> text ".align 2"
    

  • compiler/GHC/CmmToAsm/Ppr.hs
    ... ... @@ -236,6 +236,10 @@ pprGNUSectionHeader config t suffix =
    236 236
             | OSMinGW32 <- platformOS platform
    
    237 237
                         -> text ".rdata"
    
    238 238
             | otherwise -> text ".rodata.str"
    
    239
    +      IPE
    
    240
    +        | OSMinGW32 <- platformOS platform
    
    241
    +                    -> text ".rdata"
    
    242
    +        | otherwise -> text ".ipe"
    
    239 243
           OtherSection _ ->
    
    240 244
             panic "PprBase.pprGNUSectionHeader: unknown section type"
    
    241 245
         flags = case t of
    
    ... ... @@ -248,6 +252,10 @@ pprGNUSectionHeader config t suffix =
    248 252
             | OSMinGW32 <- platformOS platform
    
    249 253
                         -> empty
    
    250 254
             | otherwise -> text ",\"aMS\"," <> sectionType platform "progbits" <> text ",1"
    
    255
    +      IPE
    
    256
    +        | OSMinGW32 <- platformOS platform
    
    257
    +                    -> empty
    
    258
    +        | otherwise -> text ",\"a\"," <> sectionType platform "progbits"
    
    251 259
           _ -> empty
    
    252 260
     {-# SPECIALIZE pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> SDoc #-}
    
    253 261
     {-# SPECIALIZE pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable
    
    ... ... @@ -262,6 +270,7 @@ pprXcoffSectionHeader t = case t of
    262 270
       RelocatableReadOnlyData -> text ".csect .text[PR] # RelocatableReadOnlyData"
    
    263 271
       CString                 -> text ".csect .text[PR] # CString"
    
    264 272
       UninitialisedData       -> text ".csect .data[BS]"
    
    273
    +  IPE                     -> text ".csect .text[PR] #IPE"
    
    265 274
       _                       -> panic "pprXcoffSectionHeader: unknown section type"
    
    266 275
     {-# SPECIALIZE pprXcoffSectionHeader :: SectionType -> SDoc #-}
    
    267 276
     {-# SPECIALIZE pprXcoffSectionHeader :: SectionType -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable
    
    ... ... @@ -276,6 +285,7 @@ pprDarwinSectionHeader t = case t of
    276 285
       InitArray               -> text ".section\t__DATA,__mod_init_func,mod_init_funcs"
    
    277 286
       FiniArray               -> panic "pprDarwinSectionHeader: fini not supported"
    
    278 287
       CString                 -> text ".section\t__TEXT,__cstring,cstring_literals"
    
    288
    +  IPE                     -> text ".const"
    
    279 289
       OtherSection _          -> panic "pprDarwinSectionHeader: unknown section type"
    
    280 290
     {-# SPECIALIZE pprDarwinSectionHeader :: SectionType -> SDoc #-}
    
    281 291
     {-# SPECIALIZE pprDarwinSectionHeader :: SectionType -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable

  • compiler/GHC/CmmToLlvm/Data.hs
    ... ... @@ -145,7 +145,7 @@ llvmSectionType p t = case t of
    145 145
         CString                 -> case platformOS p of
    
    146 146
                                      OSMinGW32 -> fsLit ".rdata$str"
    
    147 147
                                      _         -> fsLit ".rodata.str"
    
    148
    -
    
    148
    +    IPE                     -> fsLit ".ipe"
    
    149 149
         InitArray               -> panic "llvmSectionType: InitArray"
    
    150 150
         FiniArray               -> panic "llvmSectionType: FiniArray"
    
    151 151
         OtherSection _          -> panic "llvmSectionType: unknown section type"
    

  • compiler/GHC/HsToCore/Ticks.hs
    1
    -{-# LANGUAGE DeriveFunctor            #-}
    
    2 1
     {-# LANGUAGE NondecreasingIndentation #-}
    
    3
    -{-# LANGUAGE TypeFamilies             #-}
    
    4 2
     
    
    5 3
     {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}
    
    6 4
     
    
    7 5
     {-
    
    8 6
     (c) Galois, 2006
    
    9 7
     (c) University of Glasgow, 2007
    
    8
    +(c) Florian Ragwitz, 2025
    
    10 9
     -}
    
    11 10
     
    
    12 11
     module GHC.HsToCore.Ticks
    
    ... ... @@ -38,7 +37,9 @@ import GHC.Utils.Logger
    38 37
     import GHC.Types.SrcLoc
    
    39 38
     import GHC.Types.Basic
    
    40 39
     import GHC.Types.Id
    
    40
    +import GHC.Types.Id.Info
    
    41 41
     import GHC.Types.Var.Set
    
    42
    +import GHC.Types.Var.Env
    
    42 43
     import GHC.Types.Name.Set hiding (FreeVars)
    
    43 44
     import GHC.Types.Name
    
    44 45
     import GHC.Types.CostCentre
    
    ... ... @@ -48,6 +49,7 @@ import GHC.Types.ProfAuto
    48 49
     
    
    49 50
     import Control.Monad
    
    50 51
     import Data.List (isSuffixOf, intersperse)
    
    52
    +import Data.Foldable (toList)
    
    51 53
     
    
    52 54
     import Trace.Hpc.Mix
    
    53 55
     
    
    ... ... @@ -123,6 +125,7 @@ addTicksToBinds logger cfg
    123 125
                           , density      = mkDensity tickish $ ticks_profAuto cfg
    
    124 126
                           , this_mod     = mod
    
    125 127
                           , tickishType  = tickish
    
    128
    +                      , recSelBinds  = emptyVarEnv
    
    126 129
                           }
    
    127 130
                     (binds',_,st') = unTM (addTickLHsBinds binds) env st
    
    128 131
                 in (binds', st')
    
    ... ... @@ -224,8 +227,7 @@ addTickLHsBind :: LHsBind GhcTc -> TM (LHsBind GhcTc)
    224 227
     addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
    
    225 228
                                                      , abs_exports = abs_exports
    
    226 229
                                                      }))) =
    
    227
    -  withEnv add_exports $
    
    228
    -    withEnv add_inlines $ do
    
    230
    +  withEnv (add_rec_sels . add_inlines . add_exports) $ do
    
    229 231
           binds' <- addTickLHsBinds binds
    
    230 232
           return $ L pos $ XHsBindsLR $ bind { abs_binds = binds' }
    
    231 233
       where
    
    ... ... @@ -247,6 +249,12 @@ addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
    247 249
                           | ABE{ abe_poly = pid, abe_mono = mid } <- abs_exports
    
    248 250
                           , isInlinePragma (idInlinePragma pid) ] }
    
    249 251
     
    
    252
    +   add_rec_sels env =
    
    253
    +     env{ recSelBinds = recSelBinds env `extendVarEnvList`
    
    254
    +                          [ (abe_mono, abe_poly)
    
    255
    +                          | ABE{ abe_poly, abe_mono } <- abs_exports
    
    256
    +                          , RecSelId{} <- [idDetails abe_poly] ] }
    
    257
    +
    
    250 258
     addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches }))) = do
    
    251 259
       let name = getOccString id
    
    252 260
       decl_path <- getPathEntry
    
    ... ... @@ -261,6 +269,10 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches
    261 269
       tickish <- tickishType `liftM` getEnv
    
    262 270
       case tickish of { ProfNotes | inline -> return (L pos funBind); _ -> do
    
    263 271
     
    
    272
    +  -- See Note [Record-selector ticks]
    
    273
    +  selTick <- recSelTick id
    
    274
    +  case selTick of { Just tick -> tick_rec_sel tick; _ -> do
    
    275
    +
    
    264 276
       (fvs, mg) <-
    
    265 277
             getFreeVars $
    
    266 278
             addPathEntry name $
    
    ... ... @@ -288,7 +300,40 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches
    288 300
       let mbCons = maybe Prelude.id (:)
    
    289 301
       return $ L pos $ funBind { fun_matches = mg
    
    290 302
                                , fun_ext = second (tick `mbCons`) (fun_ext funBind) }
    
    291
    -  }
    
    303
    +  } }
    
    304
    +  where
    
    305
    +    -- See Note [Record-selector ticks]
    
    306
    +    tick_rec_sel tick =
    
    307
    +      pure $ L pos $ funBind { fun_ext = second (tick :) (fun_ext funBind) }
    
    308
    +
    
    309
    +
    
    310
    +-- Note [Record-selector ticks]
    
    311
    +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    312
    +-- Users expect (see #17834) that accessing a record field by its name using
    
    313
    +-- NamedFieldPuns or RecordWildCards will mark it as covered. This is very
    
    314
    +-- reasonable, because otherwise the use of those two language features will
    
    315
    +-- produce unnecessary noise in coverage reports, distracting from real
    
    316
    +-- coverage problems.
    
    317
    +--
    
    318
    +-- Because of that, GHC chooses to treat record selectors specially for
    
    319
    +-- coverage purposes to improve the developer experience.
    
    320
    +--
    
    321
    +-- This is done by keeping track of which 'Id's are effectively bound to
    
    322
    +-- record fields (using NamedFieldPuns or RecordWildCards) in 'TickTransEnv's
    
    323
    +-- 'recSelBinds', and making 'HsVar's corresponding to those fields tick the
    
    324
    +-- appropriate box when executed.
    
    325
    +--
    
    326
    +-- To enable that, we also treat 'FunBind's for record selector functions
    
    327
    +-- specially. We only create a TopLevelBox for the record selector function,
    
    328
    +-- skipping the ExpBox that'd normally be created. This simplifies the re-use
    
    329
    +-- of ticks for the same record selector, and is done by not recursing into
    
    330
    +-- the fun_matches match group for record selector functions.
    
    331
    +--
    
    332
    +-- This scheme could be extended further in the future, making coverage for
    
    333
    +-- constructor fields (named or even positional) mean that the field was
    
    334
    +-- accessed at run-time. For the time being, we only cover NamedFieldPuns and
    
    335
    +-- RecordWildCards binds to cover most practical use-cases while keeping it
    
    336
    +-- simple.
    
    292 337
     
    
    293 338
     -- TODO: Revisit this
    
    294 339
     addTickLHsBind (L pos (pat@(PatBind { pat_lhs = lhs
    
    ... ... @@ -471,7 +516,10 @@ addBinTickLHsExpr boxLabel e@(L pos e0)
    471 516
     -- in the addTickLHsExpr family of functions.)
    
    472 517
     
    
    473 518
     addTickHsExpr :: HsExpr GhcTc -> TM (HsExpr GhcTc)
    
    474
    -addTickHsExpr e@(HsVar _ (L _ id))  = do freeVar id; return e
    
    519
    +-- See Note [Record-selector ticks]
    
    520
    +addTickHsExpr e@(HsVar _ (L _ id)) =
    
    521
    +    freeVar id >> recSelTick id >>= pure . maybe e wrap
    
    522
    +  where wrap tick = XExpr . HsTick tick . noLocA $ e
    
    475 523
     addTickHsExpr e@(HsIPVar {})            = return e
    
    476 524
     addTickHsExpr e@(HsOverLit {})          = return e
    
    477 525
     addTickHsExpr e@(HsOverLabel{})         = return e
    
    ... ... @@ -532,7 +580,7 @@ addTickHsExpr (HsMultiIf ty alts)
    532 580
            ; alts' <- mapM (traverse $ addTickGRHS isOneOfMany False False) alts
    
    533 581
            ; return $ HsMultiIf ty alts' }
    
    534 582
     addTickHsExpr (HsLet x binds e) =
    
    535
    -        bindLocals (collectLocalBinders CollNoDictBinders binds) $ do
    
    583
    +        bindLocals binds $ do
    
    536 584
               binds' <- addTickHsLocalBinds binds -- to think about: !patterns.
    
    537 585
               e' <- addTickLHsExprLetBody e
    
    538 586
               return (HsLet x binds' e')
    
    ... ... @@ -580,6 +628,7 @@ addTickHsExpr e@(HsUntypedSplice{}) = return e
    580 628
     addTickHsExpr e@(HsGetField {})      = return e
    
    581 629
     addTickHsExpr e@(HsProjection {})    = return e
    
    582 630
     addTickHsExpr (HsProc x pat cmdtop) =
    
    631
    +      bindLocals pat $
    
    583 632
             liftM2 (HsProc x)
    
    584 633
                     (addTickLPat pat)
    
    585 634
                     (traverse (addTickHsCmdTop) cmdtop)
    
    ... ... @@ -646,19 +695,17 @@ addTickMatch :: Bool -> Bool -> Bool {-Is this Do Expansion-} -> Match GhcTc (L
    646 695
                  -> TM (Match GhcTc (LHsExpr GhcTc))
    
    647 696
     addTickMatch isOneOfMany isLambda isDoExp match@(Match { m_pats = L _ pats
    
    648 697
                                                            , m_grhss = gRHSs }) =
    
    649
    -  bindLocals (collectPatsBinders CollNoDictBinders pats) $ do
    
    698
    +  bindLocals pats $ do
    
    650 699
         gRHSs' <- addTickGRHSs isOneOfMany isLambda isDoExp gRHSs
    
    651 700
         return $ match { m_grhss = gRHSs' }
    
    652 701
     
    
    653 702
     addTickGRHSs :: Bool -> Bool -> Bool -> GRHSs GhcTc (LHsExpr GhcTc)
    
    654 703
                  -> TM (GRHSs GhcTc (LHsExpr GhcTc))
    
    655 704
     addTickGRHSs isOneOfMany isLambda isDoExp (GRHSs x guarded local_binds) =
    
    656
    -  bindLocals binders $ do
    
    705
    +  bindLocals local_binds $ do
    
    657 706
         local_binds' <- addTickHsLocalBinds local_binds
    
    658 707
         guarded' <- mapM (traverse (addTickGRHS isOneOfMany isLambda isDoExp)) guarded
    
    659 708
         return $ GRHSs x guarded' local_binds'
    
    660
    -  where
    
    661
    -    binders = collectLocalBinders CollNoDictBinders local_binds
    
    662 709
     
    
    663 710
     addTickGRHS :: Bool -> Bool -> Bool -> GRHS GhcTc (LHsExpr GhcTc)
    
    664 711
                 -> TM (GRHS GhcTc (LHsExpr GhcTc))
    
    ... ... @@ -697,7 +744,7 @@ addTickLStmts isGuard stmts = do
    697 744
     addTickLStmts' :: (Maybe (Bool -> BoxLabel)) -> [ExprLStmt GhcTc] -> TM a
    
    698 745
                    -> TM ([ExprLStmt GhcTc], a)
    
    699 746
     addTickLStmts' isGuard lstmts res
    
    700
    -  = bindLocals (collectLStmtsBinders CollNoDictBinders lstmts) $
    
    747
    +  = bindLocals lstmts $
    
    701 748
         do { lstmts' <- mapM (traverse (addTickStmt isGuard)) lstmts
    
    702 749
            ; a <- res
    
    703 750
            ; return (lstmts', a) }
    
    ... ... @@ -710,6 +757,7 @@ addTickStmt _isGuard (LastStmt x e noret ret) =
    710 757
                     (pure noret)
    
    711 758
                     (addTickSyntaxExpr hpcSrcSpan ret)
    
    712 759
     addTickStmt _isGuard (BindStmt xbs pat e) =
    
    760
    +      bindLocals pat $
    
    713 761
             liftM4 (\b f -> BindStmt $ XBindStmtTc
    
    714 762
                         { xbstc_bindOp = b
    
    715 763
                         , xbstc_boundResultType = xbstc_boundResultType xbs
    
    ... ... @@ -770,17 +818,19 @@ addTickApplicativeArg isGuard (op, arg) =
    770 818
       liftM2 (,) (addTickSyntaxExpr hpcSrcSpan op) (addTickArg arg)
    
    771 819
      where
    
    772 820
       addTickArg (ApplicativeArgOne m_fail pat expr isBody) =
    
    773
    -    ApplicativeArgOne
    
    774
    -      <$> mapM (addTickSyntaxExpr hpcSrcSpan) m_fail
    
    775
    -      <*> addTickLPat pat
    
    776
    -      <*> addTickLHsExpr expr
    
    777
    -      <*> pure isBody
    
    821
    +    bindLocals pat $
    
    822
    +      ApplicativeArgOne
    
    823
    +        <$> mapM (addTickSyntaxExpr hpcSrcSpan) m_fail
    
    824
    +        <*> addTickLPat pat
    
    825
    +        <*> addTickLHsExpr expr
    
    826
    +        <*> pure isBody
    
    778 827
       addTickArg (ApplicativeArgMany x stmts ret pat ctxt) =
    
    779
    -    (ApplicativeArgMany x)
    
    780
    -      <$> addTickLStmts isGuard stmts
    
    781
    -      <*> (unLoc <$> addTickLHsExpr (L (noAnnSrcSpan hpcSrcSpan) ret))
    
    782
    -      <*> addTickLPat pat
    
    783
    -      <*> pure ctxt
    
    828
    +    bindLocals pat $
    
    829
    +      ApplicativeArgMany x
    
    830
    +        <$> addTickLStmts isGuard stmts
    
    831
    +        <*> (unLoc <$> addTickLHsExpr (L (noAnnSrcSpan hpcSrcSpan) ret))
    
    832
    +        <*> addTickLPat pat
    
    833
    +        <*> pure ctxt
    
    784 834
     
    
    785 835
     addTickStmtAndBinders :: Maybe (Bool -> BoxLabel) -> ParStmtBlock GhcTc GhcTc
    
    786 836
                           -> TM (ParStmtBlock GhcTc GhcTc)
    
    ... ... @@ -871,7 +921,7 @@ addTickHsCmd (HsCmdIf x cnd e1 c2 c3) =
    871 921
                     (addTickLHsCmd c2)
    
    872 922
                     (addTickLHsCmd c3)
    
    873 923
     addTickHsCmd (HsCmdLet x binds c) =
    
    874
    -        bindLocals (collectLocalBinders CollNoDictBinders binds) $ do
    
    924
    +        bindLocals binds $ do
    
    875 925
               binds' <- addTickHsLocalBinds binds -- to think about: !patterns.
    
    876 926
               c' <- addTickLHsCmd c
    
    877 927
               return (HsCmdLet x binds' c')
    
    ... ... @@ -907,18 +957,16 @@ addTickCmdMatchGroup mg@(MG { mg_alts = (L l matches) }) = do
    907 957
     
    
    908 958
     addTickCmdMatch :: Match GhcTc (LHsCmd GhcTc) -> TM (Match GhcTc (LHsCmd GhcTc))
    
    909 959
     addTickCmdMatch match@(Match { m_pats = L _ pats, m_grhss = gRHSs }) =
    
    910
    -  bindLocals (collectPatsBinders CollNoDictBinders pats) $ do
    
    960
    +  bindLocals pats $ do
    
    911 961
         gRHSs' <- addTickCmdGRHSs gRHSs
    
    912 962
         return $ match { m_grhss = gRHSs' }
    
    913 963
     
    
    914 964
     addTickCmdGRHSs :: GRHSs GhcTc (LHsCmd GhcTc) -> TM (GRHSs GhcTc (LHsCmd GhcTc))
    
    915 965
     addTickCmdGRHSs (GRHSs x guarded local_binds) =
    
    916
    -  bindLocals binders $ do
    
    966
    +  bindLocals local_binds $ do
    
    917 967
         local_binds' <- addTickHsLocalBinds local_binds
    
    918 968
         guarded' <- mapM (traverse addTickCmdGRHS) guarded
    
    919 969
         return $ GRHSs x guarded' local_binds'
    
    920
    -  where
    
    921
    -    binders = collectLocalBinders CollNoDictBinders local_binds
    
    922 970
     
    
    923 971
     addTickCmdGRHS :: GRHS GhcTc (LHsCmd GhcTc) -> TM (GRHS GhcTc (LHsCmd GhcTc))
    
    924 972
     -- The *guards* are *not* Cmds, although the body is
    
    ... ... @@ -937,15 +985,14 @@ addTickLCmdStmts stmts = do
    937 985
     addTickLCmdStmts' :: [LStmt GhcTc (LHsCmd GhcTc)] -> TM a
    
    938 986
                       -> TM ([LStmt GhcTc (LHsCmd GhcTc)], a)
    
    939 987
     addTickLCmdStmts' lstmts res
    
    940
    -  = bindLocals binders $ do
    
    988
    +  = bindLocals lstmts $ do
    
    941 989
             lstmts' <- mapM (traverse addTickCmdStmt) lstmts
    
    942 990
             a <- res
    
    943 991
             return (lstmts', a)
    
    944
    -  where
    
    945
    -        binders = collectLStmtsBinders CollNoDictBinders lstmts
    
    946 992
     
    
    947 993
     addTickCmdStmt :: Stmt GhcTc (LHsCmd GhcTc) -> TM (Stmt GhcTc (LHsCmd GhcTc))
    
    948 994
     addTickCmdStmt (BindStmt x pat c) =
    
    995
    +      bindLocals pat $
    
    949 996
             liftM2 (BindStmt x)
    
    950 997
                     (addTickLPat pat)
    
    951 998
                     (addTickLHsCmd c)
    
    ... ... @@ -1006,11 +1053,13 @@ addTickArithSeqInfo (FromThenTo e1 e2 e3) =
    1006 1053
     
    
    1007 1054
     data TickTransState = TT { ticks       :: !(SizedSeq Tick)
    
    1008 1055
                              , ccIndices   :: !CostCentreState
    
    1056
    +                         , recSelTicks :: !(IdEnv CoreTickish)
    
    1009 1057
                              }
    
    1010 1058
     
    
    1011 1059
     initTTState :: TickTransState
    
    1012 1060
     initTTState = TT { ticks        = emptySS
    
    1013 1061
                      , ccIndices    = newCostCentreState
    
    1062
    +                 , recSelTicks  = emptyVarEnv
    
    1014 1063
                      }
    
    1015 1064
     
    
    1016 1065
     addMixEntry :: Tick -> TM Int
    
    ... ... @@ -1021,6 +1070,10 @@ addMixEntry ent = do
    1021 1070
            }
    
    1022 1071
       return c
    
    1023 1072
     
    
    1073
    +addRecSelTick :: Id -> CoreTickish -> TM ()
    
    1074
    +addRecSelTick sel tick =
    
    1075
    +  setState $ \s -> s{ recSelTicks = extendVarEnv (recSelTicks s) sel tick }
    
    1076
    +
    
    1024 1077
     data TickTransEnv = TTE { fileName     :: FastString
    
    1025 1078
                             , density      :: TickDensity
    
    1026 1079
                             , tte_countEntries :: !Bool
    
    ... ... @@ -1033,6 +1086,7 @@ data TickTransEnv = TTE { fileName :: FastString
    1033 1086
                             , blackList    :: Set RealSrcSpan
    
    1034 1087
                             , this_mod     :: Module
    
    1035 1088
                             , tickishType  :: TickishType
    
    1089
    +                        , recSelBinds  :: IdEnv Id
    
    1036 1090
                             }
    
    1037 1091
     
    
    1038 1092
     --      deriving Show
    
    ... ... @@ -1154,12 +1208,13 @@ ifGoodTickSrcSpan pos then_code else_code = do
    1154 1208
       good <- isGoodTickSrcSpan pos
    
    1155 1209
       if good then then_code else else_code
    
    1156 1210
     
    
    1157
    -bindLocals :: [Id] -> TM a -> TM a
    
    1158
    -bindLocals new_ids (TM m)
    
    1159
    -  = TM $ \ env st ->
    
    1160
    -                 case m env{ inScope = inScope env `extendVarSetList` new_ids } st of
    
    1161
    -                   (r, fv, st') -> (r, fv `delListFromOccEnv` occs, st')
    
    1162
    -  where occs = [ nameOccName (idName id) | id <- new_ids ]
    
    1211
    +bindLocals :: (CollectBinders bndr, CollectFldBinders bndr) => bndr -> TM a -> TM a
    
    1212
    +bindLocals from (TM m) = TM $ \env st ->
    
    1213
    +  case m (with_bnds env) st of
    
    1214
    +    (r, fv, st') -> (r, fv `delListFromOccEnv` (map (nameOccName . idName) new_bnds), st')
    
    1215
    +  where with_bnds e = e{ inScope = inScope e `extendVarSetList` new_bnds
    
    1216
    +                       , recSelBinds = recSelBinds e `plusVarEnv` collectFldBinds from }
    
    1217
    +        new_bnds = collectBinds from
    
    1163 1218
     
    
    1164 1219
     withBlackListed :: SrcSpan -> TM a -> TM a
    
    1165 1220
     withBlackListed (RealSrcSpan ss _) = withEnv (\ env -> env { blackList = Set.insert ss (blackList env) })
    
    ... ... @@ -1186,6 +1241,17 @@ allocTickBox boxLabel countEntries topOnly pos m
    1186 1241
           tickish <- mkTickish boxLabel countEntries topOnly pos fvs (declPath env)
    
    1187 1242
           return (this_loc (XExpr $ HsTick tickish $ this_loc e))
    
    1188 1243
     
    
    1244
    +recSelTick :: Id -> TM (Maybe CoreTickish)
    
    1245
    +recSelTick id = ifDensity TickForCoverage maybe_tick (pure Nothing)
    
    1246
    +  where
    
    1247
    +    maybe_tick = getEnv >>=
    
    1248
    +      maybe (pure Nothing) tick . (`lookupVarEnv` id) . recSelBinds
    
    1249
    +    tick sel = getState >>=
    
    1250
    +      maybe (alloc sel) (pure . Just) . (`lookupVarEnv` sel) . recSelTicks
    
    1251
    +    alloc sel = allocATickBox (box sel) False False (getSrcSpan sel) noFVs
    
    1252
    +                  >>= traverse (\t -> t <$ addRecSelTick sel t)
    
    1253
    +    box sel = TopLevelBox [getOccString sel]
    
    1254
    +
    
    1189 1255
     -- the tick application inherits the source position of its
    
    1190 1256
     -- expression argument to support nested box allocations
    
    1191 1257
     allocATickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> FreeVars
    
    ... ... @@ -1288,3 +1354,98 @@ matchesOneOfMany lmatches = sum (map matchCount lmatches) > 1
    1288 1354
             matchCount :: LMatch GhcTc body -> Int
    
    1289 1355
             matchCount (L _ (Match { m_grhss = GRHSs _ grhss _ }))
    
    1290 1356
               = length grhss
    
    1357
    +
    
    1358
    +-- | Convenience class used by 'bindLocals' to collect new bindings from
    
    1359
    +-- various parts of he AST. Just delegates to
    
    1360
    +-- 'collect{Pat,Pats,Local,LStmts}Binders' from 'GHC.Hs.Utils' as appropriate.
    
    1361
    +class CollectBinders a where
    
    1362
    +  collectBinds :: a -> [Id]
    
    1363
    +
    
    1364
    +-- | Variant of 'CollectBinders' which collects information on which locals
    
    1365
    +-- are bound to record fields (currently only via 'RecordWildCards' or
    
    1366
    +-- 'NamedFieldPuns') to enable better coverage support for record selectors.
    
    1367
    +--
    
    1368
    +-- See Note [Record-selector ticks].
    
    1369
    +class CollectFldBinders a where
    
    1370
    +  collectFldBinds :: a -> IdEnv Id
    
    1371
    +
    
    1372
    +instance CollectBinders (LocatedA (Pat GhcTc)) where
    
    1373
    +  collectBinds = collectPatBinders CollNoDictBinders
    
    1374
    +instance CollectBinders [LocatedA (Pat GhcTc)] where
    
    1375
    +  collectBinds = collectPatsBinders CollNoDictBinders
    
    1376
    +instance CollectBinders (HsLocalBinds GhcTc) where
    
    1377
    +  collectBinds = collectLocalBinders CollNoDictBinders
    
    1378
    +instance CollectBinders [LocatedA (Stmt GhcTc (LocatedA (HsExpr GhcTc)))] where
    
    1379
    +  collectBinds = collectLStmtsBinders CollNoDictBinders
    
    1380
    +instance CollectBinders [LocatedA (Stmt GhcTc (LocatedA (HsCmd GhcTc)))] where
    
    1381
    +  collectBinds = collectLStmtsBinders CollNoDictBinders
    
    1382
    +
    
    1383
    +instance (CollectFldBinders a) => CollectFldBinders [a] where
    
    1384
    +  collectFldBinds = foldr (flip plusVarEnv . collectFldBinds) emptyVarEnv
    
    1385
    +instance (CollectFldBinders e) => CollectFldBinders (GenLocated l e) where
    
    1386
    +  collectFldBinds = collectFldBinds . unLoc
    
    1387
    +instance CollectFldBinders (Pat GhcTc) where
    
    1388
    +  collectFldBinds ConPat{ pat_args = RecCon HsRecFields{ rec_flds, rec_dotdot } } =
    
    1389
    +    collectFldBinds rec_flds `plusVarEnv` plusVarEnvList (zipWith fld_bnds [0..] rec_flds)
    
    1390
    +    where n_explicit | Just (L _ (RecFieldsDotDot n)) <- rec_dotdot = n
    
    1391
    +                     | otherwise = length rec_flds
    
    1392
    +          fld_bnds n (L _ HsFieldBind{ hfbLHS = L _ FieldOcc{ foLabel = L _ sel }
    
    1393
    +                                     , hfbRHS = L _ (VarPat _ (L _ var))
    
    1394
    +                                     , hfbPun })
    
    1395
    +            | hfbPun || n >= n_explicit = unitVarEnv var sel
    
    1396
    +          fld_bnds _ _ = emptyVarEnv
    
    1397
    +  collectFldBinds ConPat{ pat_args = PrefixCon pats } = collectFldBinds pats
    
    1398
    +  collectFldBinds ConPat{ pat_args = InfixCon p1 p2 } = collectFldBinds [p1, p2]
    
    1399
    +  collectFldBinds (LazyPat _ pat) = collectFldBinds pat
    
    1400
    +  collectFldBinds (BangPat _ pat) = collectFldBinds pat
    
    1401
    +  collectFldBinds (AsPat _ _ pat) = collectFldBinds pat
    
    1402
    +  collectFldBinds (ViewPat _ _ pat) = collectFldBinds pat
    
    1403
    +  collectFldBinds (ParPat _ pat) = collectFldBinds pat
    
    1404
    +  collectFldBinds (ListPat _ pats) = collectFldBinds pats
    
    1405
    +  collectFldBinds (TuplePat _ pats _) = collectFldBinds pats
    
    1406
    +  collectFldBinds (SumPat _ pats _ _) = collectFldBinds pats
    
    1407
    +  collectFldBinds (SigPat _ pat _) = collectFldBinds pat
    
    1408
    +  collectFldBinds (XPat exp) = collectFldBinds exp
    
    1409
    +  collectFldBinds VarPat{} = emptyVarEnv
    
    1410
    +  collectFldBinds WildPat{} = emptyVarEnv
    
    1411
    +  collectFldBinds OrPat{} = emptyVarEnv
    
    1412
    +  collectFldBinds LitPat{} = emptyVarEnv
    
    1413
    +  collectFldBinds NPat{} = emptyVarEnv
    
    1414
    +  collectFldBinds NPlusKPat{} = emptyVarEnv
    
    1415
    +  collectFldBinds SplicePat{} = emptyVarEnv
    
    1416
    +  collectFldBinds EmbTyPat{} = emptyVarEnv
    
    1417
    +  collectFldBinds InvisPat{} = emptyVarEnv
    
    1418
    +instance (CollectFldBinders r) => CollectFldBinders (HsFieldBind l r) where
    
    1419
    +  collectFldBinds = collectFldBinds . hfbRHS
    
    1420
    +instance CollectFldBinders XXPatGhcTc where
    
    1421
    +  collectFldBinds (CoPat _ pat _) = collectFldBinds pat
    
    1422
    +  collectFldBinds (ExpansionPat _ pat) = collectFldBinds pat
    
    1423
    +instance CollectFldBinders (HsLocalBinds GhcTc) where
    
    1424
    +  collectFldBinds (HsValBinds _ bnds) = collectFldBinds bnds
    
    1425
    +  collectFldBinds HsIPBinds{} = emptyVarEnv
    
    1426
    +  collectFldBinds EmptyLocalBinds{} = emptyVarEnv
    
    1427
    +instance CollectFldBinders (HsValBinds GhcTc) where
    
    1428
    +  collectFldBinds (ValBinds _ bnds _) = collectFldBinds bnds
    
    1429
    +  collectFldBinds (XValBindsLR (NValBinds bnds _)) = collectFldBinds (map snd bnds)
    
    1430
    +instance CollectFldBinders (HsBind GhcTc) where
    
    1431
    +  collectFldBinds PatBind{ pat_lhs } = collectFldBinds pat_lhs
    
    1432
    +  collectFldBinds (XHsBindsLR AbsBinds{ abs_exports, abs_binds }) =
    
    1433
    +    mkVarEnv [ (abe_poly, sel)
    
    1434
    +             | ABE{ abe_poly, abe_mono } <- abs_exports
    
    1435
    +             , Just sel <- [lookupVarEnv monos abe_mono] ]
    
    1436
    +    where monos = collectFldBinds abs_binds
    
    1437
    +  collectFldBinds VarBind{} = emptyVarEnv
    
    1438
    +  collectFldBinds FunBind{} = emptyVarEnv
    
    1439
    +  collectFldBinds PatSynBind{} = emptyVarEnv
    
    1440
    +instance CollectFldBinders (Stmt GhcTc e) where
    
    1441
    +  collectFldBinds (BindStmt _ pat _) = collectFldBinds pat
    
    1442
    +  collectFldBinds (LetStmt _ bnds) = collectFldBinds bnds
    
    1443
    +  collectFldBinds (ParStmt _ xs _ _) = collectFldBinds [s | ParStmtBlock _ ss _ _ <- toList xs, s <- ss]
    
    1444
    +  collectFldBinds TransStmt{ trS_stmts } = collectFldBinds trS_stmts
    
    1445
    +  collectFldBinds RecStmt{ recS_stmts } = collectFldBinds recS_stmts
    
    1446
    +  collectFldBinds (XStmtLR (ApplicativeStmt _ args _)) = collectFldBinds (map snd args)
    
    1447
    +  collectFldBinds LastStmt{} = emptyVarEnv
    
    1448
    +  collectFldBinds BodyStmt{} = emptyVarEnv
    
    1449
    +instance CollectFldBinders (ApplicativeArg GhcTc) where
    
    1450
    +  collectFldBinds ApplicativeArgOne{ app_arg_pattern } = collectFldBinds app_arg_pattern
    
    1451
    +  collectFldBinds ApplicativeArgMany{ bv_pattern } = collectFldBinds bv_pattern

  • compiler/GHC/StgToCmm/InfoTableProv.hs
    ... ... @@ -66,6 +66,28 @@ construction, the 'compressed' field of each IPE buffer list node is examined.
    66 66
     If the field indicates that the data has been compressed, the entry data and
    
    67 67
     strings table are decompressed before continuing with the normal IPE map
    
    68 68
     construction.
    
    69
    +
    
    70
    +Note [IPE Stripping and magic words]
    
    71
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    72
    +
    
    73
    +For systems which support ELF executables:
    
    74
    +
    
    75
    +The metadata part of IPE info is placed into a separate ELF section (.ipe).
    
    76
    +This can then be stripped afterwards if you don't require the metadata
    
    77
    +
    
    78
    +```
    
    79
    +-- Remove the section
    
    80
    +objcopy --remove-section .ipe <your-exe>
    
    81
    +-- Repack and compress the executable
    
    82
    +upx <your-exe>
    
    83
    +```
    
    84
    +
    
    85
    +The .ipe section starts with a magic 64-bit word "IPE\nIPE\n`, encoded as ascii.
    
    86
    +
    
    87
    +The RTS checks to see if the .ipe section starts with the magic word. If the
    
    88
    +section has been stripped then it won't start with the magic word and the
    
    89
    +metadata won't be accessible for the info tables.
    
    90
    +
    
    69 91
     -}
    
    70 92
     
    
    71 93
     emitIpeBufferListNode ::
    
    ... ... @@ -124,11 +146,21 @@ emitIpeBufferListNode this_mod ents dus0 = do
    124 146
             ipe_buffer_lbl :: CLabel
    
    125 147
             ipe_buffer_lbl = mkIPELabel this_mod
    
    126 148
     
    
    149
    +        -- A magic word we can use to see if the IPE information has been stripped
    
    150
    +        -- or not
    
    151
    +        -- See Note [IPE Stripping and magic words]
    
    152
    +        -- "IPE\nIPE\n", null terminated.
    
    153
    +        ipe_header :: CmmStatic
    
    154
    +        ipe_header = CmmStaticLit (CmmInt 0x4950450049504500 W64)
    
    155
    +
    
    127 156
             ipe_buffer_node :: [CmmStatic]
    
    128 157
             ipe_buffer_node = map CmmStaticLit
    
    129 158
               [ -- 'next' field
    
    130 159
                 zeroCLit platform
    
    131 160
     
    
    161
    +            -- 'node_id' field
    
    162
    +          , zeroCLit platform
    
    163
    +
    
    132 164
                 -- 'compressed' field
    
    133 165
               , int do_compress
    
    134 166
     
    
    ... ... @@ -164,13 +196,13 @@ emitIpeBufferListNode this_mod ents dus0 = do
    164 196
     
    
    165 197
         -- Emit the strings table
    
    166 198
         emitDecl $ CmmData
    
    167
    -      (Section Data strings_lbl)
    
    168
    -      (CmmStaticsRaw strings_lbl strings)
    
    199
    +      (Section IPE strings_lbl)
    
    200
    +      (CmmStaticsRaw strings_lbl (ipe_header : strings))
    
    169 201
     
    
    170 202
         -- Emit the list of IPE buffer entries
    
    171 203
         emitDecl $ CmmData
    
    172
    -      (Section Data entries_lbl)
    
    173
    -      (CmmStaticsRaw entries_lbl entries)
    
    204
    +      (Section IPE entries_lbl)
    
    205
    +      (CmmStaticsRaw entries_lbl (ipe_header : entries))
    
    174 206
     
    
    175 207
         -- Emit the IPE buffer list node
    
    176 208
         emitDecl $ CmmData
    

  • docs/users_guide/9.14.1-notes.rst
    ... ... @@ -138,6 +138,11 @@ Compiler
    138 138
       uses of the now deprecated ``pattern`` namespace specifier in import/export
    
    139 139
       lists. See `GHC Proposal #581, section 2.3 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0581-namespace-specified-imports.rst#deprecate-use-of-pattern-in-import-export-lists>`_.
    
    140 140
     
    
    141
    +- Code coverage (:ghc-flag:`-fhpc`) now treats uses of record fields via
    
    142
    +  :extension:`RecordWildCards` or :extension:`NamedFieldPuns` as if the fields
    
    143
    +  were accessed using the generated record selector functions, marking the fields
    
    144
    +  as covered in coverage reports (:ghc-ticket:`17834`).
    
    145
    +
    
    141 146
     GHCi
    
    142 147
     ~~~~
    
    143 148
     
    

  • rts/IPE.c
    ... ... @@ -62,6 +62,22 @@ entry's containing IpeBufferListNode and its index in that node.
    62 62
     When the user looks up an IPE entry, we convert it to the user-facing
    
    63 63
     InfoProvEnt representation.
    
    64 64
     
    
    65
    +Note [Stable identifiers for IPE entries]
    
    66
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    67
    +
    
    68
    +Each IPE entry is given a stable identifier which remains the same across
    
    69
    +different runs of the executable (unlike the address of the info table).
    
    70
    +
    
    71
    +The identifier is a 64-bit word which consists of two parts.
    
    72
    +
    
    73
    +* The high 32-bits are a per-node identifier.
    
    74
    +* The low 32-bits are the index of the entry in the node.
    
    75
    +
    
    76
    +When a node is queued in the pending list by `registerInfoProvList` it is
    
    77
    +given a unique identifier from an incrementing global variable.
    
    78
    +
    
    79
    +The unique key can be computed by using the `IPE_ENTRY_KEY` macro.
    
    80
    +
    
    65 81
     */
    
    66 82
     
    
    67 83
     typedef struct {
    
    ... ... @@ -69,6 +85,13 @@ typedef struct {
    69 85
         uint32_t idx;
    
    70 86
     } IpeMapEntry;
    
    71 87
     
    
    88
    +// See Note [Stable identifiers for IPE entries]
    
    89
    +#define IPE_ENTRY_KEY(entry) \
    
    90
    +    MAKE_IPE_KEY((entry).node->node_id, (entry).idx)
    
    91
    +
    
    92
    +#define MAKE_IPE_KEY(module_id, idx) \
    
    93
    +    ((((uint64_t)(module_id)) << 32) | ((uint64_t)(idx)))
    
    94
    +
    
    72 95
     #if defined(THREADED_RTS)
    
    73 96
     static Mutex ipeMapLock;
    
    74 97
     #endif
    
    ... ... @@ -78,9 +101,22 @@ static HashTable *ipeMap = NULL;
    78 101
     // Accessed atomically
    
    79 102
     static IpeBufferListNode *ipeBufferList = NULL;
    
    80 103
     
    
    104
    +// A global counter which is used to give an IPE entry a unique value across runs.
    
    105
    +static StgWord next_module_id = 1; // Start at 1 to reserve 0 as "invalid"
    
    106
    +
    
    81 107
     static void decompressIPEBufferListNodeIfCompressed(IpeBufferListNode*);
    
    82 108
     static void updateIpeMap(void);
    
    83 109
     
    
    110
    +// Check whether the IpeBufferListNode has the relevant magic words.
    
    111
    +// See Note [IPE Stripping and magic words]
    
    112
    +static inline bool ipe_node_valid(const IpeBufferListNode *node) {
    
    113
    +    return node &&
    
    114
    +           node->entries_block &&
    
    115
    +           node->string_table_block &&
    
    116
    +           node->entries_block->magic == IPE_MAGIC_WORD &&
    
    117
    +           node->string_table_block->magic == IPE_MAGIC_WORD;
    
    118
    +}
    
    119
    +
    
    84 120
     #if defined(THREADED_RTS)
    
    85 121
     
    
    86 122
     void initIpe(void) { initMutex(&ipeMapLock); }
    
    ... ... @@ -99,11 +135,12 @@ static InfoProvEnt ipeBufferEntryToIpe(const IpeBufferListNode *node, uint32_t i
    99 135
     {
    
    100 136
         CHECK(idx < node->count);
    
    101 137
         CHECK(!node->compressed);
    
    102
    -    const char *strings = node->string_table;
    
    103
    -    const IpeBufferEntry *ent = &node->entries[idx];
    
    138
    +    const char *strings = node->string_table_block->string_table;
    
    139
    +    const IpeBufferEntry *ent = &node->entries_block->entries[idx];
    
    104 140
         return (InfoProvEnt) {
    
    105 141
                 .info = node->tables[idx],
    
    106 142
                 .prov = {
    
    143
    +                .info_prov_id  = MAKE_IPE_KEY(node->node_id, idx),
    
    107 144
                     .table_name = &strings[ent->table_name],
    
    108 145
                     .closure_desc = ent->closure_desc,
    
    109 146
                     .ty_desc = &strings[ent->ty_desc],
    
    ... ... @@ -121,19 +158,23 @@ static InfoProvEnt ipeBufferEntryToIpe(const IpeBufferListNode *node, uint32_t i
    121 158
     static void traceIPEFromHashTable(void *data STG_UNUSED, StgWord key STG_UNUSED,
    
    122 159
                                       const void *value) {
    
    123 160
         const IpeMapEntry *map_ent = (const IpeMapEntry *)value;
    
    124
    -    const InfoProvEnt ipe = ipeBufferEntryToIpe(map_ent->node, map_ent->idx);
    
    125
    -    traceIPE(&ipe);
    
    161
    +    if (ipe_node_valid(map_ent->node)){
    
    162
    +      const InfoProvEnt ipe = ipeBufferEntryToIpe(map_ent->node, map_ent->idx);
    
    163
    +      traceIPE(&ipe);
    
    164
    +    }
    
    126 165
     }
    
    127 166
     
    
    128 167
     void dumpIPEToEventLog(void) {
    
    129 168
         // Dump pending entries
    
    130 169
         IpeBufferListNode *node = RELAXED_LOAD(&ipeBufferList);
    
    131 170
         while (node != NULL) {
    
    132
    -        decompressIPEBufferListNodeIfCompressed(node);
    
    171
    +        if (ipe_node_valid(node)){
    
    172
    +          decompressIPEBufferListNodeIfCompressed(node);
    
    133 173
     
    
    134
    -        for (uint32_t i = 0; i < node->count; i++) {
    
    135
    -            const InfoProvEnt ent = ipeBufferEntryToIpe(node, i);
    
    136
    -            traceIPE(&ent);
    
    174
    +          for (uint32_t i = 0; i < node->count; i++) {
    
    175
    +              const InfoProvEnt ent = ipeBufferEntryToIpe(node, i);
    
    176
    +              traceIPE(&ent);
    
    177
    +          }
    
    137 178
             }
    
    138 179
             node = node->next;
    
    139 180
         }
    
    ... ... @@ -167,9 +208,22 @@ A performance test for IPE registration and lookup can be found here:
    167 208
     https://gitlab.haskell.org/ghc/ghc/-/merge_requests/5724#note_370806
    
    168 209
     */
    
    169 210
     void registerInfoProvList(IpeBufferListNode *node) {
    
    211
    +
    
    212
    +        // Grab a fresh module_id
    
    213
    +    uint32_t module_id;
    
    214
    +    StgWord temp_module_id;
    
    215
    +    while (true) {
    
    216
    +        temp_module_id = next_module_id;
    
    217
    +        if (cas(&next_module_id, temp_module_id, temp_module_id+1) == temp_module_id) {
    
    218
    +            module_id = (uint32_t) temp_module_id;
    
    219
    +            break;
    
    220
    +        }
    
    221
    +
    
    222
    +    }
    
    170 223
         while (true) {
    
    171 224
             IpeBufferListNode *old = RELAXED_LOAD(&ipeBufferList);
    
    172 225
             node->next = old;
    
    226
    +        node->node_id = module_id;
    
    173 227
             if (cas_ptr((volatile void **) &ipeBufferList, old, node) == (void *) old) {
    
    174 228
                 return;
    
    175 229
             }
    
    ... ... @@ -183,7 +237,7 @@ void formatClosureDescIpe(const InfoProvEnt *ipe_buf, char *str_buf) {
    183 237
     bool lookupIPE(const StgInfoTable *info, InfoProvEnt *out) {
    
    184 238
         updateIpeMap();
    
    185 239
         IpeMapEntry *map_ent = (IpeMapEntry *) lookupHashTable(ipeMap, (StgWord)info);
    
    186
    -    if (map_ent) {
    
    240
    +    if (map_ent && ipe_node_valid(map_ent->node)) {
    
    187 241
             *out = ipeBufferEntryToIpe(map_ent->node, map_ent->idx);
    
    188 242
             return true;
    
    189 243
         } else {
    
    ... ... @@ -191,6 +245,18 @@ bool lookupIPE(const StgInfoTable *info, InfoProvEnt *out) {
    191 245
         }
    
    192 246
     }
    
    193 247
     
    
    248
    +// Returns 0 when the info table is not present in the info table map.
    
    249
    +// See Note [Stable identifiers for IPE entries]
    
    250
    +uint64_t lookupIPEId(const StgInfoTable *info) {
    
    251
    +    updateIpeMap();
    
    252
    +    IpeMapEntry *map_ent = (IpeMapEntry *) lookupHashTable(ipeMap, (StgWord)(info));
    
    253
    +    if (map_ent){
    
    254
    +        return IPE_ENTRY_KEY(*map_ent);
    
    255
    +    } else {
    
    256
    +        return 0;
    
    257
    +    }
    
    258
    +}
    
    259
    +
    
    194 260
     void updateIpeMap(void) {
    
    195 261
         // Check if there's any work at all. If not so, we can circumvent locking,
    
    196 262
         // which decreases performance.
    

  • rts/ProfHeap.c
    ... ... @@ -23,6 +23,7 @@
    23 23
     #include "Printer.h"
    
    24 24
     #include "Trace.h"
    
    25 25
     #include "sm/GCThread.h"
    
    26
    +#include "IPE.h"
    
    26 27
     
    
    27 28
     #include <fs_rts.h>
    
    28 29
     #include <string.h>
    
    ... ... @@ -230,9 +231,10 @@ closureIdentity( const StgClosure *p )
    230 231
                 return closure_type_names[info->type];
    
    231 232
             }
    
    232 233
         }
    
    233
    -    case HEAP_BY_INFO_TABLE: {
    
    234
    -        return get_itbl(p);
    
    235
    -        }
    
    234
    +    case HEAP_BY_INFO_TABLE:
    
    235
    +    {
    
    236
    +        return (void *) (p->header.info);
    
    237
    +    }
    
    236 238
     
    
    237 239
         default:
    
    238 240
             barf("closureIdentity");
    
    ... ... @@ -853,6 +855,20 @@ aggregateCensusInfo( void )
    853 855
     }
    
    854 856
     #endif
    
    855 857
     
    
    858
    +static void
    
    859
    +recordIPEHeapSample(FILE *hp_file, uint64_t table_id, size_t count)
    
    860
    +{
    
    861
    +    // Print to heap profile file
    
    862
    +    fprintf(hp_file, "0x%" PRIx64, table_id);
    
    863
    +
    
    864
    +    // Create label string for tracing
    
    865
    +    char str[100];
    
    866
    +    sprintf(str, "0x%" PRIx64, table_id);
    
    867
    +
    
    868
    +    // Emit the profiling sample (convert count to bytes)
    
    869
    +    traceHeapProfSampleString(0, str, count * sizeof(W_));
    
    870
    +}
    
    871
    +
    
    856 872
     /* -----------------------------------------------------------------------------
    
    857 873
      * Print out the results of a heap census.
    
    858 874
      * -------------------------------------------------------------------------- */
    
    ... ... @@ -915,6 +931,11 @@ dumpCensus( Census *census )
    915 931
         }
    
    916 932
     #endif
    
    917 933
     
    
    934
    +    // Census entries which we need to group together.
    
    935
    +    // Used by IPE profiling to group together bands which don't have IPE information.
    
    936
    +    // Printing at the end in the 0 band
    
    937
    +    uint64_t uncategorised_count = 0;
    
    938
    +
    
    918 939
         for (ctr = census->ctrs; ctr != NULL; ctr = ctr->next) {
    
    919 940
     
    
    920 941
     #if defined(PROFILING)
    
    ... ... @@ -945,10 +966,13 @@ dumpCensus( Census *census )
    945 966
                                           count * sizeof(W_));
    
    946 967
                 break;
    
    947 968
             case HEAP_BY_INFO_TABLE:
    
    948
    -            fprintf(hp_file, "%p", ctr->identity);
    
    949
    -            char str[100];
    
    950
    -            sprintf(str, "%p", ctr->identity);
    
    951
    -            traceHeapProfSampleString(0, str, count * sizeof(W_));
    
    969
    +
    
    970
    +            uint64_t table_id = lookupIPEId(ctr->identity);
    
    971
    +            if (! table_id) {
    
    972
    +              uncategorised_count += count;
    
    973
    +              continue;
    
    974
    +            }
    
    975
    +            recordIPEHeapSample(hp_file, table_id, count);
    
    952 976
                 break;
    
    953 977
     #if defined(PROFILING)
    
    954 978
             case HEAP_BY_CCS:
    
    ... ... @@ -999,9 +1023,21 @@ dumpCensus( Census *census )
    999 1023
                 barf("dumpCensus; doHeapProfile");
    
    1000 1024
             }
    
    1001 1025
     
    
    1026
    +
    
    1027
    +
    
    1002 1028
             fprintf(hp_file, "\t%" FMT_Word "\n", (W_)count * sizeof(W_));
    
    1003 1029
         }
    
    1004 1030
     
    
    1031
    +    // Print the unallocated data into the 0 band for info table profiling.
    
    1032
    +    switch (RtsFlags.ProfFlags.doHeapProfile) {
    
    1033
    +        case HEAP_BY_INFO_TABLE:
    
    1034
    +            recordIPEHeapSample(hp_file, 0, uncategorised_count);
    
    1035
    +            break;
    
    1036
    +        default:
    
    1037
    +            ASSERT(uncategorised_count == 0);
    
    1038
    +            break;
    
    1039
    +    }
    
    1040
    +
    
    1005 1041
         traceHeapProfSampleEnd(era);
    
    1006 1042
         printSample(false, census->time);
    
    1007 1043
     
    

  • rts/eventlog/EventLog.c
    ... ... @@ -1472,7 +1472,7 @@ void postIPE(const InfoProvEnt *ipe)
    1472 1472
         CHECK(!ensureRoomForVariableEvent(&eventBuf, len));
    
    1473 1473
         postEventHeader(&eventBuf, EVENT_IPE);
    
    1474 1474
         postPayloadSize(&eventBuf, len);
    
    1475
    -    postWord64(&eventBuf, (StgWord) INFO_PTR_TO_STRUCT(ipe->info));
    
    1475
    +    postWord64(&eventBuf, (StgWord) (ipe->prov.info_prov_id));
    
    1476 1476
         postStringLen(&eventBuf, ipe->prov.table_name, table_name_len);
    
    1477 1477
         postStringLen(&eventBuf, closure_desc_buf, closure_desc_len);
    
    1478 1478
         postStringLen(&eventBuf, ipe->prov.ty_desc, ty_desc_len);
    

  • rts/include/rts/IPE.h
    ... ... @@ -14,6 +14,7 @@
    14 14
     #pragma once
    
    15 15
     
    
    16 16
     typedef struct InfoProv_ {
    
    17
    +    uint64_t   info_prov_id;
    
    17 18
         const char *table_name;
    
    18 19
         uint32_t closure_desc; // closure type
    
    19 20
         const char *ty_desc;
    
    ... ... @@ -63,9 +64,37 @@ typedef struct {
    63 64
     
    
    64 65
     GHC_STATIC_ASSERT(sizeof(IpeBufferEntry) % (WORD_SIZE_IN_BITS / 8) == 0, "sizeof(IpeBufferEntry) must be a multiple of the word size");
    
    65 66
     
    
    67
    +// The magic word is IPE\nIPE\n, which occupies the full 64 bit width of a word.
    
    68
    +// See Note [IPE Stripping and magic words]
    
    69
    +#define IPE_MAGIC_WORD 0x4950450049504500UL
    
    70
    +
    
    71
    +// Heap profiling currently requires a 32 bit pointer.. so for now just truncate
    
    72
    +// the key to fit. It should still be big enough.
    
    73
    +#if SIZEOF_VOID_P == 4
    
    74
    +// On 32-bit systems: keep lower 16 bits of module_id and idx
    
    75
    +#define IPE_PROF_KEY(key64) \
    
    76
    +    (uint32_t)((((key64) >> 16) & 0xFFFF0000) | ((key64) & 0x0000FFFF))
    
    77
    +#else
    
    78
    +// On 64-bit systems: use full key
    
    79
    +#define IPE_PROF_KEY(key64) (key64)
    
    80
    +#endif
    
    81
    +
    
    82
    +typedef struct {
    
    83
    +    StgWord64 magic;          // Must be IPE_MAGIC_WORD
    
    84
    +    IpeBufferEntry entries[]; // Flexible array member
    
    85
    +} IpeBufferEntryBlock;
    
    86
    +
    
    87
    +typedef struct {
    
    88
    +    StgWord64 magic;          // Must be IPE_MAGIC_WORD
    
    89
    +    char string_table[];    // Flexible array member for string table
    
    90
    +} IpeStringTableBlock;
    
    91
    +
    
    66 92
     typedef struct IpeBufferListNode_ {
    
    67 93
         struct IpeBufferListNode_ *next;
    
    68 94
     
    
    95
    +    // This field is filled in when the node is registered.
    
    96
    +    uint32_t node_id;
    
    97
    +
    
    69 98
         // Everything below is read-only and generated by the codegen
    
    70 99
     
    
    71 100
         // This flag should be treated as a boolean
    
    ... ... @@ -76,10 +105,10 @@ typedef struct IpeBufferListNode_ {
    76 105
         // When TNTC is enabled, these will point to the entry code
    
    77 106
         // not the info table itself.
    
    78 107
         const StgInfoTable **tables;
    
    79
    -    IpeBufferEntry *entries;
    
    108
    +    IpeBufferEntryBlock *entries_block;
    
    80 109
         StgWord entries_size; // decompressed size
    
    81 110
     
    
    82
    -    const char *string_table;
    
    111
    +    const IpeStringTableBlock *string_table_block;
    
    83 112
         StgWord string_table_size; // decompressed size
    
    84 113
     
    
    85 114
         // Shared by all entries
    
    ... ... @@ -98,6 +127,8 @@ void formatClosureDescIpe(const InfoProvEnt *ipe_buf, char *str_buf);
    98 127
     // Returns true on success, initializes `out`.
    
    99 128
     bool lookupIPE(const StgInfoTable *info, InfoProvEnt *out);
    
    100 129
     
    
    130
    +uint64_t lookupIPEId(const StgInfoTable *info);
    
    131
    +
    
    101 132
     #if defined(DEBUG)
    
    102 133
     void printIPE(const StgInfoTable *info);
    
    103 134
     #endif

  • testsuite/tests/hpc/recsel/Makefile
    1
    +TOP=../../..
    
    2
    +include $(TOP)/mk/boilerplate.mk
    
    3
    +include $(TOP)/mk/test.mk

  • testsuite/tests/hpc/recsel/recsel.hs
    1
    +{-# LANGUAGE RecordWildCards, NamedFieldPuns, Arrows #-}
    
    2
    +
    
    3
    +import Control.Monad.Identity
    
    4
    +import Control.Arrow (runKleisli, arr, returnA)
    
    5
    +import Data.Maybe
    
    6
    +import Data.List
    
    7
    +import Data.Bifunctor
    
    8
    +import Trace.Hpc.Mix
    
    9
    +import Trace.Hpc.Tix
    
    10
    +import Trace.Hpc.Reflect
    
    11
    +
    
    12
    +data Foo = Foo { fooA, fooB, fooC, fooD, fooE, fooF, fooG, fooH, fooI
    
    13
    +               , fooJ, fooK, fooL, fooM, fooN, fooO :: Int }
    
    14
    +data Bar = Bar { barFoo :: Foo }
    
    15
    +
    
    16
    +fAB Foo{..} = fooA + fooB
    
    17
    +fC Foo{fooC} = fooC
    
    18
    +fD x Foo{..} = fromMaybe 0 $ if x then Just fooD else Nothing
    
    19
    +fE Bar{barFoo = Foo{..}} = fooE
    
    20
    +fF Foo{fooF = f} = f
    
    21
    +fG f = let Foo{..} = f in fooG
    
    22
    +fH f = runIdentity $ do
    
    23
    +  Foo{..} <- pure f
    
    24
    +  return fooH
    
    25
    +fI f = runIdentity $ do
    
    26
    +  let Foo{..} = f
    
    27
    +  return fooI
    
    28
    +fJ f = [ fooJ | let Foo{..} = f ] !! 0
    
    29
    +fK = runIdentity . runKleisli (proc f -> do
    
    30
    +       Foo{..} <- arr id -< f
    
    31
    +       returnA -< fooK)
    
    32
    +fL = runIdentity . runKleisli (proc f -> do
    
    33
    +       let Foo{..} = f;
    
    34
    +       returnA -< fooL)
    
    35
    +fM f | Foo{..} <- f = fooM
    
    36
    +fN f = fooN f
    
    37
    +fO = runIdentity . runKleisli (proc Foo{..} -> returnA -< fooO)
    
    38
    +
    
    39
    +recSel (n, TopLevelBox [s]) | any (`isPrefixOf` s) ["foo", "bar"] = Just (n, s)
    
    40
    +recSel _ = Nothing
    
    41
    +
    
    42
    +main = do
    
    43
    +  let foo = Foo 42 23 0 1 2 3 4 5 6 7 0xaffe 9 10 11 12
    
    44
    +  mapM_ (print . ($ foo))
    
    45
    +        [fAB, fC, fD False, fE . Bar, fF, fG, fH, fI, fJ, fK, fL, fM, fN, fO]
    
    46
    +  (Mix _ _ _ _ mixs) <- readMix [".hpc"] (Left "Main")
    
    47
    +  let sels = mapMaybe recSel . zip [0..] $ map snd mixs
    
    48
    +  (Tix [TixModule "Main" _ _ tix]) <- examineTix
    
    49
    +  mapM_ print . sortOn snd $ map (first (tix !!)) sels

  • testsuite/tests/hpc/recsel/recsel.stdout
    1
    +65
    
    2
    +0
    
    3
    +0
    
    4
    +2
    
    5
    +3
    
    6
    +4
    
    7
    +5
    
    8
    +6
    
    9
    +7
    
    10
    +45054
    
    11
    +9
    
    12
    +10
    
    13
    +11
    
    14
    +12
    
    15
    +(0,"barFoo")
    
    16
    +(1,"fooA")
    
    17
    +(1,"fooB")
    
    18
    +(1,"fooC")
    
    19
    +(0,"fooD")
    
    20
    +(1,"fooE")
    
    21
    +(0,"fooF")
    
    22
    +(1,"fooG")
    
    23
    +(1,"fooH")
    
    24
    +(1,"fooI")
    
    25
    +(1,"fooJ")
    
    26
    +(1,"fooK")
    
    27
    +(1,"fooL")
    
    28
    +(1,"fooM")
    
    29
    +(1,"fooN")
    
    30
    +(1,"fooO")

  • testsuite/tests/hpc/recsel/test.T
    1
    +setTestOpts([omit_ghci, when(fast(), skip), js_skip])
    
    2
    +
    
    3
    +test('recsel',
    
    4
    +     [ignore_extension,
    
    5
    +      when(arch('wasm32'), fragile(23243))],
    
    6
    +     compile_and_run, ['-fhpc'])
    
    7
    +

  • testsuite/tests/profiling/should_run/caller-cc/all.T
    ... ... @@ -8,6 +8,7 @@ setTestOpts(only_ways(prof_ways))
    8 8
     setTestOpts(extra_files(['Main.hs']))
    
    9 9
     setTestOpts(extra_run_opts('7'))
    
    10 10
     setTestOpts(grep_prof("Main.hs"))
    
    11
    +setTestOpts(grep_prof("calling:"))
    
    11 12
     
    
    12 13
     # N.B. Main.hs is stolen from heapprof001.
    
    13 14
     
    

  • testsuite/tests/rts/ipe/ipeMap.c
    ... ... @@ -48,7 +48,8 @@ HaskellObj shouldFindOneIfItHasBeenRegistered(Capability *cap) {
    48 48
         // Allocate buffers for IPE buffer list node
    
    49 49
         IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
    
    50 50
         node->tables = malloc(sizeof(StgInfoTable *));
    
    51
    -    node->entries = malloc(sizeof(IpeBufferEntry));
    
    51
    +    node->entries_block = malloc(sizeof(StgWord64) + sizeof(IpeBufferEntry));
    
    52
    +    node->entries_block->magic = IPE_MAGIC_WORD;
    
    52 53
     
    
    53 54
         StringTable st;
    
    54 55
         init_string_table(&st);
    
    ... ... @@ -61,9 +62,13 @@ HaskellObj shouldFindOneIfItHasBeenRegistered(Capability *cap) {
    61 62
         node->compressed = 0;
    
    62 63
         node->count = 1;
    
    63 64
         node->tables[0] = get_itbl(fortyTwo);
    
    64
    -    node->entries[0] = makeAnyProvEntry(cap, &st, 42);
    
    65
    +    node->entries_block->entries[0] = makeAnyProvEntry(cap, &st, 42);
    
    65 66
         node->entries_size = sizeof(IpeBufferEntry);
    
    66
    -    node->string_table = st.buffer;
    
    67
    +
    
    68
    +    IpeStringTableBlock *string_table_block = malloc(sizeof(StgWord64) + st.size);
    
    69
    +    string_table_block->magic = IPE_MAGIC_WORD;
    
    70
    +    memcpy(string_table_block->string_table, st.buffer, st.size);
    
    71
    +    node->string_table_block = string_table_block;
    
    67 72
         node->string_table_size = st.size;
    
    68 73
     
    
    69 74
         registerInfoProvList(node);
    
    ... ... @@ -90,7 +95,8 @@ void shouldFindTwoIfTwoHaveBeenRegistered(Capability *cap,
    90 95
         // Allocate buffers for IPE buffer list node
    
    91 96
         IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
    
    92 97
         node->tables = malloc(sizeof(StgInfoTable *));
    
    93
    -    node->entries = malloc(sizeof(IpeBufferEntry));
    
    98
    +    node->entries_block = malloc(sizeof(StgWord64) + sizeof(IpeBufferEntry));
    
    99
    +    node->entries_block->magic = IPE_MAGIC_WORD;
    
    94 100
     
    
    95 101
         StringTable st;
    
    96 102
         init_string_table(&st);
    
    ... ... @@ -103,9 +109,12 @@ void shouldFindTwoIfTwoHaveBeenRegistered(Capability *cap,
    103 109
         node->compressed = 0;
    
    104 110
         node->count = 1;
    
    105 111
         node->tables[0] = get_itbl(twentyThree);
    
    106
    -    node->entries[0] = makeAnyProvEntry(cap, &st, 23);
    
    112
    +    node->entries_block->entries[0] = makeAnyProvEntry(cap, &st, 23);
    
    107 113
         node->entries_size = sizeof(IpeBufferEntry);
    
    108
    -    node->string_table = st.buffer;
    
    114
    +    IpeStringTableBlock *string_table_block = malloc(sizeof(StgWord64) + st.size);
    
    115
    +    string_table_block->magic = IPE_MAGIC_WORD;
    
    116
    +    memcpy(string_table_block->string_table, st.buffer, st.size);
    
    117
    +    node->string_table_block = string_table_block;
    
    109 118
         node->string_table_size = st.size;
    
    110 119
     
    
    111 120
         registerInfoProvList(node);
    
    ... ... @@ -121,7 +130,8 @@ void shouldFindTwoFromTheSameList(Capability *cap) {
    121 130
         // Allocate buffers for IPE buffer list node
    
    122 131
         IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
    
    123 132
         node->tables = malloc(sizeof(StgInfoTable *) * 2);
    
    124
    -    node->entries = malloc(sizeof(IpeBufferEntry) * 2);
    
    133
    +    node->entries_block = malloc(sizeof(StgWord64) + sizeof(IpeBufferEntry) * 2);
    
    134
    +    node->entries_block->magic = IPE_MAGIC_WORD;
    
    125 135
     
    
    126 136
         StringTable st;
    
    127 137
         init_string_table(&st);
    
    ... ... @@ -133,10 +143,13 @@ void shouldFindTwoFromTheSameList(Capability *cap) {
    133 143
         node->count = 2;
    
    134 144
         node->tables[0] = get_itbl(one);
    
    135 145
         node->tables[1] = get_itbl(two);
    
    136
    -    node->entries[0] = makeAnyProvEntry(cap, &st, 1);
    
    137
    -    node->entries[1] = makeAnyProvEntry(cap, &st, 2);
    
    146
    +    node->entries_block->entries[0] = makeAnyProvEntry(cap, &st, 1);
    
    147
    +    node->entries_block->entries[1] = makeAnyProvEntry(cap, &st, 2);
    
    138 148
         node->entries_size = sizeof(IpeBufferEntry) * 2;
    
    139
    -    node->string_table = st.buffer;
    
    149
    +    IpeStringTableBlock *string_table_block = malloc(sizeof(StgWord64) + st.size);
    
    150
    +    string_table_block->magic = IPE_MAGIC_WORD;
    
    151
    +    memcpy(string_table_block->string_table, st.buffer, st.size);
    
    152
    +    node->string_table_block = string_table_block;
    
    140 153
         node->string_table_size = st.size;
    
    141 154
     
    
    142 155
         registerInfoProvList(node);
    
    ... ... @@ -152,7 +165,11 @@ void shouldDealWithAnEmptyList(Capability *cap, HaskellObj fortyTwo) {
    152 165
         IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
    
    153 166
         node->count = 0;
    
    154 167
         node->next = NULL;
    
    155
    -    node->string_table = "";
    
    168
    +    IpeStringTableBlock *string_table_block = malloc(sizeof(StgWord64));
    
    169
    +    string_table_block->magic = IPE_MAGIC_WORD;
    
    170
    +
    
    171
    +    node->entries_block = malloc(sizeof(StgWord64));
    
    172
    +    node->entries_block->magic = IPE_MAGIC_WORD;
    
    156 173
     
    
    157 174
         registerInfoProvList(node);
    
    158 175
     
    

  • testsuite/tests/rts/ipe/ipe_lib.c
    ... ... @@ -64,7 +64,8 @@ IpeBufferListNode *makeAnyProvEntries(Capability *cap, int start, int end) {
    64 64
         // Allocate buffers for IpeBufferListNode
    
    65 65
         IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
    
    66 66
         node->tables = malloc(sizeof(StgInfoTable *) * n);
    
    67
    -    node->entries = malloc(sizeof(IpeBufferEntry) * n);
    
    67
    +    node->entries_block = malloc(sizeof(StgWord64) + sizeof(IpeBufferEntry) * n);
    
    68
    +    node->entries_block->magic = IPE_MAGIC_WORD;
    
    68 69
     
    
    69 70
         StringTable st;
    
    70 71
         init_string_table(&st);
    
    ... ... @@ -83,14 +84,19 @@ IpeBufferListNode *makeAnyProvEntries(Capability *cap, int start, int end) {
    83 84
         for (int i=start; i < end; i++) {
    
    84 85
             HaskellObj closure = rts_mkInt(cap, 42);
    
    85 86
             node->tables[i]  = get_itbl(closure);
    
    86
    -        node->entries[i] = makeAnyProvEntry(cap, &st, i);
    
    87
    +        node->entries_block->entries[i] = makeAnyProvEntry(cap, &st, i);
    
    87 88
         }
    
    88 89
     
    
    89 90
         // Set the rest of the fields
    
    90 91
         node->next = NULL;
    
    91 92
         node->compressed = 0;
    
    92 93
         node->count = n;
    
    93
    -    node->string_table = st.buffer;
    
    94
    +
    
    95
    +    IpeStringTableBlock *string_table_block =
    
    96
    +      malloc(sizeof(StgWord64) + st.size);
    
    97
    +    string_table_block->magic = IPE_MAGIC_WORD;
    
    98
    +    memcpy(string_table_block->string_table, st.buffer, st.size);
    
    99
    +    node->string_table_block = string_table_block;
    
    94 100
     
    
    95 101
         return node;
    
    96 102
     }