Matthew Pickering pushed to branch wip/stable-ipe-info at Glasgow Haskell Compiler / GHC
Commits:
-
43b606bb
by Florian Ragwitz at 2025-06-27T16:31:26-04:00
-
d4952549
by Ben Gamari at 2025-06-27T16:32:08-04:00
-
9b299678
by Matthew Pickering at 2025-07-03T11:36:32+01:00
-
fec3fd0a
by Matthew Pickering at 2025-07-03T11:36:32+01:00
-
eefb8f46
by Matthew Pickering at 2025-07-03T14:55:35+01:00
18 changed files:
- compiler/GHC/Cmm.hs
- compiler/GHC/CmmToAsm/PPC/Ppr.hs
- compiler/GHC/CmmToAsm/Ppr.hs
- compiler/GHC/CmmToLlvm/Data.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/StgToCmm/InfoTableProv.hs
- docs/users_guide/9.14.1-notes.rst
- rts/IPE.c
- rts/ProfHeap.c
- rts/eventlog/EventLog.c
- rts/include/rts/IPE.h
- + testsuite/tests/hpc/recsel/Makefile
- + testsuite/tests/hpc/recsel/recsel.hs
- + testsuite/tests/hpc/recsel/recsel.stdout
- + testsuite/tests/hpc/recsel/test.T
- testsuite/tests/profiling/should_run/caller-cc/all.T
- testsuite/tests/rts/ipe/ipeMap.c
- testsuite/tests/rts/ipe/ipe_lib.c
Changes:
| ... | ... | @@ -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' |
| ... | ... | @@ -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"
|
| ... | ... | @@ -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 |
| ... | ... | @@ -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"
|
| 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 |
| ... | ... | @@ -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
|
| ... | ... | @@ -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 |
| ... | ... | @@ -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.
|
| ... | ... | @@ -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 |
| ... | ... | @@ -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);
|
| ... | ... | @@ -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 |
| 1 | +TOP=../../..
|
|
| 2 | +include $(TOP)/mk/boilerplate.mk
|
|
| 3 | +include $(TOP)/mk/test.mk |
| 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 |
| 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") |
| 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 | + |
| ... | ... | @@ -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 |
| ... | ... | @@ -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 |
| ... | ... | @@ -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 | } |