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 | } |