Hannes Siebenhandl pushed to branch wip/fendor/hpc-bc-support at Glasgow Haskell Compiler / GHC
Commits:
-
79bda793
by Matthew Pickering at 2026-03-17T10:51:41+01:00
-
60d51a25
by Matthew Pickering at 2026-03-17T10:51:41+01:00
-
a238bd42
by Matthew Pickering at 2026-03-17T10:51:41+01:00
-
56dce546
by Matthew Pickering at 2026-03-17T10:51:41+01:00
16 changed files:
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Driver/CodeOutput.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/StgToByteCode.hs
- rts/Disassembler.c
- rts/Hpc.c
- rts/Interpreter.c
- rts/include/rts/Bytecodes.h
- + testsuite/tests/hpc/ghc_ghci/BytecodeMain.hs
- testsuite/tests/hpc/ghc_ghci/Makefile
- + testsuite/tests/hpc/ghc_ghci/hpc_ghc_ghci_bytecode.stdout
- testsuite/tests/hpc/ghc_ghci/test.T
Changes:
| ... | ... | @@ -855,6 +855,12 @@ assembleI platform i = case i of |
| 855 | 855 | emit_ bci_BRK_FUN [ Op p1, Op info_addr, Op info_unitid_addr
|
| 856 | 856 | , SmallOp ix_hi, SmallOp ix_lo, Op np ]
|
| 857 | 857 | |
| 858 | + HPC_TICK lbl ix -> do
|
|
| 859 | + p <- lit1 (BCONPtrLbl lbl)
|
|
| 860 | + let ix_hi = fromIntegral (ix `shiftR` 16)
|
|
| 861 | + ix_lo = fromIntegral (ix .&. 0xffff)
|
|
| 862 | + emit_ bci_HPC_TICK [Op p, SmallOp ix_hi, SmallOp ix_lo]
|
|
| 863 | + |
|
| 858 | 864 | #if MIN_VERSION_rts(1,0,3)
|
| 859 | 865 | BCO_NAME name -> do np <- lit1 (BCONPtrStr name)
|
| 860 | 866 | emit_ bci_BCO_NAME [Op np]
|
| ... | ... | @@ -15,6 +15,7 @@ import GHC.ByteCode.Types |
| 15 | 15 | import GHC.Cmm.Type (Width)
|
| 16 | 16 | import GHC.StgToCmm.Layout ( ArgRep(..) )
|
| 17 | 17 | import GHC.Utils.Outputable
|
| 18 | +import GHC.Data.FastString ( FastString )
|
|
| 18 | 19 | import GHC.Types.Name
|
| 19 | 20 | import GHC.Types.Literal
|
| 20 | 21 | import GHC.Types.Unique
|
| ... | ... | @@ -257,6 +258,7 @@ data BCInstr |
| 257 | 258 | |
| 258 | 259 | -- Breakpoints
|
| 259 | 260 | | BRK_FUN !InternalBreakpointId
|
| 261 | + | HPC_TICK !FastString !Word32
|
|
| 260 | 262 | |
| 261 | 263 | #if MIN_VERSION_rts(1,0,3)
|
| 262 | 264 | -- | A "meta"-instruction for recording the name of a BCO for debugging purposes.
|
| ... | ... | @@ -452,6 +454,7 @@ instance Outputable BCInstr where |
| 452 | 454 | = text "BRK_FUN" <+> text "<breakarray>"
|
| 453 | 455 | <+> ppr info_mod <+> ppr infox
|
| 454 | 456 | <+> text "<cc>"
|
| 457 | + ppr (HPC_TICK lbl ix) = text "HPC_TICK" <+> ppr lbl <+> ppr ix
|
|
| 455 | 458 | #if MIN_VERSION_rts(1,0,3)
|
| 456 | 459 | ppr (BCO_NAME nm) = text "BCO_NAME" <+> text (show nm)
|
| 457 | 460 | #endif
|
| ... | ... | @@ -577,6 +580,7 @@ bciStackUse OP_INDEX_ADDR{} = 0 |
| 577 | 580 | |
| 578 | 581 | bciStackUse SWIZZLE{} = 0
|
| 579 | 582 | bciStackUse BRK_FUN{} = 0
|
| 583 | +bciStackUse HPC_TICK{} = 0
|
|
| 580 | 584 | |
| 581 | 585 | -- These insns actually reduce stack use, but we need the high-tide level,
|
| 582 | 586 | -- so can't use this info. Not that it matters much.
|
| ... | ... | @@ -712,8 +712,7 @@ backendSupportsHpc (Named NCG) = True |
| 712 | 712 | backendSupportsHpc (Named LLVM) = True
|
| 713 | 713 | backendSupportsHpc (Named ViaC) = True
|
| 714 | 714 | backendSupportsHpc (Named JavaScript) = False
|
| 715 | --- TODO: @terrorjack thinks that the bytecode backend should support HPC now since (!13493)
|
|
| 716 | -backendSupportsHpc (Named Bytecode) = False
|
|
| 715 | +backendSupportsHpc (Named Bytecode) = True
|
|
| 717 | 716 | backendSupportsHpc (Named NoBackend) = True
|
| 718 | 717 | |
| 719 | 718 | -- | This flag says whether the back end supports foreign
|
| ... | ... | @@ -276,14 +276,15 @@ outputForeignStubs logger tmpfs dflags unit_state mod location stubs |
| 276 | 276 | NoStubs ->
|
| 277 | 277 | return (False, Nothing)
|
| 278 | 278 | |
| 279 | - ForeignStubs (CHeader h_code) (CStub c_code _ _) -> do
|
|
| 279 | + ForeignStubs (CHeader h_code) cstub -> do
|
|
| 280 | 280 | let
|
| 281 | - stub_c_output_d = pprCode c_code
|
|
| 281 | + stub_c_output_d = pprCode (getCStub cstub $$ pprCStubInitFiniDecls platform cstub)
|
|
| 282 | 282 | stub_c_output_w = showSDoc dflags stub_c_output_d
|
| 283 | 283 | |
| 284 | 284 | -- Header file protos for "foreign export"ed functions.
|
| 285 | 285 | stub_h_output_d = pprCode h_code
|
| 286 | 286 | stub_h_output_w = showSDoc dflags stub_h_output_d
|
| 287 | + platform = targetPlatform dflags
|
|
| 287 | 288 | |
| 288 | 289 | putDumpFileMaybe logger Opt_D_dump_foreign
|
| 289 | 290 | "Foreign export header file"
|
| ... | ... | @@ -343,6 +344,28 @@ outputForeignStubs logger tmpfs dflags unit_state mod location stubs |
| 343 | 344 | cplusplus_hdr = "#if defined(__cplusplus)\nextern \"C\" {\n#endif\n"
|
| 344 | 345 | cplusplus_ftr = "#if defined(__cplusplus)\n}\n#endif\n"
|
| 345 | 346 | |
| 347 | +pprCStubInitFiniDecls :: Platform -> CStub -> SDoc
|
|
| 348 | +pprCStubInitFiniDecls platform cstub =
|
|
| 349 | + vcat (zipWith (pprInitOrFiniDecl ".init_array") [0 :: Int ..] (getInitializers cstub))
|
|
| 350 | + $$ vcat (zipWith (pprInitOrFiniDecl ".fini_array") [0 :: Int ..] (getFinalizers cstub))
|
|
| 351 | + where
|
|
| 352 | + pprInitOrFiniDecl :: String -> Int -> CLabel -> SDoc
|
|
| 353 | + pprInitOrFiniDecl section_name n lbl =
|
|
| 354 | + vcat
|
|
| 355 | + [ hsep [text "extern void", pprCLabel platform lbl, text "(void);"]
|
|
| 356 | + , hsep [ text "static void (*"
|
|
| 357 | + <> text "__ghc_init_fini_"
|
|
| 358 | + <> int n
|
|
| 359 | + <> text ")(void)"
|
|
| 360 | + , text "__attribute__((used, section("
|
|
| 361 | + <> doubleQuotes (text section_name)
|
|
| 362 | + <> text ")))"
|
|
| 363 | + , equals
|
|
| 364 | + , pprCLabel platform lbl
|
|
| 365 | + <> semi
|
|
| 366 | + ]
|
|
| 367 | + ]
|
|
| 368 | + |
|
| 346 | 369 | |
| 347 | 370 | -- It is more than likely that the stubs file will
|
| 348 | 371 | -- turn out to be empty, in which case no file should be created.
|
| ... | ... | @@ -3790,12 +3790,6 @@ makeDynFlagsConsistent dflags |
| 3790 | 3790 | pgmError (backendDescription (backend dflags) ++
|
| 3791 | 3791 | " supports only unregisterised ABI but target platform doesn't use it.")
|
| 3792 | 3792 | |
| 3793 | - | gopt Opt_Hpc dflags && not (backendSupportsHpc (backend dflags))
|
|
| 3794 | - = let dflags' = gopt_unset dflags Opt_Hpc
|
|
| 3795 | - warn = "Hpc can't be used with " ++ backendDescription (backend dflags) ++
|
|
| 3796 | - ". Ignoring -fhpc."
|
|
| 3797 | - in loop dflags' warn
|
|
| 3798 | - |
|
| 3799 | 3793 | | backendSwappableWithViaC (backend dflags) &&
|
| 3800 | 3794 | platformUnregisterised (targetPlatform dflags)
|
| 3801 | 3795 | = loop (dflags { backend = viaCBackend })
|
| ... | ... | @@ -163,20 +163,20 @@ deSugar hsc_env |
| 163 | 163 | export_set (typeEnvTyCons type_env) binds
|
| 164 | 164 | else return (binds, Nothing)
|
| 165 | 165 | ; let modBreaks
|
| 166 | - | Just (_, specs) <- m_tickInfo
|
|
| 166 | + | Just (_, _, breakpointSpecs) <- m_tickInfo
|
|
| 167 | 167 | , breakpointsAllowed dflags
|
| 168 | - = Just $ mkModBreaks (interpreterProfiled $ hscInterp hsc_env) mod specs
|
|
| 168 | + = Just $ mkModBreaks (interpreterProfiled $ hscInterp hsc_env) mod breakpointSpecs
|
|
| 169 | 169 | | otherwise
|
| 170 | 170 | = Nothing
|
| 171 | 171 | |
| 172 | 172 | ; ds_hpc_info <- case m_tickInfo of
|
| 173 | - Just (orig_file2, ticks)
|
|
| 173 | + Just (orig_file2, hpcTicks, _)
|
|
| 174 | 174 | | gopt Opt_Hpc $ hsc_dflags hsc_env
|
| 175 | 175 | -> do
|
| 176 | 176 | hashNo <- if gopt Opt_Hpc $ hsc_dflags hsc_env
|
| 177 | - then writeMixEntries (hpcDir dflags) mod ticks orig_file2
|
|
| 177 | + then writeMixEntries (hpcDir dflags) mod hpcTicks orig_file2
|
|
| 178 | 178 | else return 0 -- dummy hash when none are written
|
| 179 | - pure $ HpcInfo (fromIntegral $ sizeSS ticks) hashNo
|
|
| 179 | + pure $ HpcInfo (fromIntegral $ sizeSS hpcTicks) hashNo
|
|
| 180 | 180 | _ -> pure $ emptyHpcInfo
|
| 181 | 181 | |
| 182 | 182 | ; (msgs, mb_res) <- initDs hsc_env tcg_env $
|
| ... | ... | @@ -100,7 +100,7 @@ addTicksToBinds |
| 100 | 100 | -- hasn't set it), so we have to work from this set.
|
| 101 | 101 | -> [TyCon] -- ^ Type constructors in this module
|
| 102 | 102 | -> LHsBinds GhcTc
|
| 103 | - -> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick))
|
|
| 103 | + -> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick, SizedSeq Tick))
|
|
| 104 | 104 | |
| 105 | 105 | addTicksToBinds logger cfg
|
| 106 | 106 | mod mod_loc exports tyCons binds
|
| ... | ... | @@ -133,12 +133,13 @@ addTicksToBinds logger cfg |
| 133 | 133 | |
| 134 | 134 | (binds1,st) = foldr tickPass (binds, initTTState) passes
|
| 135 | 135 | |
| 136 | - extendedMixEntries = ticks st
|
|
| 136 | + hpcEntries = hpcTicks st
|
|
| 137 | + breakpointEntries = breakpointTicks st
|
|
| 137 | 138 | |
| 138 | 139 | putDumpFileMaybe logger Opt_D_dump_ticked "HPC" FormatHaskell
|
| 139 | 140 | (pprLHsBinds binds1)
|
| 140 | 141 | |
| 141 | - return (binds1, Just (orig_file2, extendedMixEntries))
|
|
| 142 | + return (binds1, Just (orig_file2, hpcEntries, breakpointEntries))
|
|
| 142 | 143 | |
| 143 | 144 | | otherwise = return (binds, Nothing)
|
| 144 | 145 | |
| ... | ... | @@ -1049,23 +1050,31 @@ addTickArithSeqInfo (FromThenTo e1 e2 e3) = |
| 1049 | 1050 | (addTickLHsExpr e2)
|
| 1050 | 1051 | (addTickLHsExpr e3)
|
| 1051 | 1052 | |
| 1052 | -data TickTransState = TT { ticks :: !(SizedSeq Tick)
|
|
| 1053 | - , ccIndices :: !CostCentreState
|
|
| 1054 | - , recSelTicks :: !(IdEnv CoreTickish)
|
|
| 1053 | +data TickTransState = TT { hpcTicks :: !(SizedSeq Tick)
|
|
| 1054 | + , breakpointTicks :: !(SizedSeq Tick)
|
|
| 1055 | + , ccIndices :: !CostCentreState
|
|
| 1056 | + , recSelTicks :: !(IdEnv CoreTickish)
|
|
| 1055 | 1057 | }
|
| 1056 | 1058 | |
| 1057 | 1059 | initTTState :: TickTransState
|
| 1058 | -initTTState = TT { ticks = emptySS
|
|
| 1059 | - , ccIndices = newCostCentreState
|
|
| 1060 | - , recSelTicks = emptyVarEnv
|
|
| 1060 | +initTTState = TT { hpcTicks = emptySS
|
|
| 1061 | + , breakpointTicks = emptySS
|
|
| 1062 | + , ccIndices = newCostCentreState
|
|
| 1063 | + , recSelTicks = emptyVarEnv
|
|
| 1061 | 1064 | }
|
| 1062 | 1065 | |
| 1063 | -addMixEntry :: Tick -> TM Int
|
|
| 1064 | -addMixEntry ent = do
|
|
| 1065 | - c <- fromIntegral . sizeSS . ticks <$> getState
|
|
| 1066 | +addHpcEntry :: Tick -> TM Int
|
|
| 1067 | +addHpcEntry ent = do
|
|
| 1068 | + c <- fromIntegral . sizeSS . hpcTicks <$> getState
|
|
| 1066 | 1069 | setState $ \st ->
|
| 1067 | - st { ticks = addToSS (ticks st) ent
|
|
| 1068 | - }
|
|
| 1070 | + st { hpcTicks = addToSS (hpcTicks st) ent }
|
|
| 1071 | + return c
|
|
| 1072 | + |
|
| 1073 | +addBreakpointEntry :: Tick -> TM Int
|
|
| 1074 | +addBreakpointEntry ent = do
|
|
| 1075 | + c <- fromIntegral . sizeSS . breakpointTicks <$> getState
|
|
| 1076 | + setState $ \st ->
|
|
| 1077 | + st { breakpointTicks = addToSS (breakpointTicks st) ent }
|
|
| 1069 | 1078 | return c
|
| 1070 | 1079 | |
| 1071 | 1080 | addRecSelTick :: Id -> CoreTickish -> TM ()
|
| ... | ... | @@ -1290,7 +1299,7 @@ mkTickish boxLabel countEntries topOnly pos fvs decl_path = do |
| 1290 | 1299 | |
| 1291 | 1300 | env <- getEnv
|
| 1292 | 1301 | case tickishType env of
|
| 1293 | - HpcTicks -> HpcTick (this_mod env) <$> addMixEntry me
|
|
| 1302 | + HpcTicks -> HpcTick (this_mod env) <$> addHpcEntry me
|
|
| 1294 | 1303 | |
| 1295 | 1304 | ProfNotes -> do
|
| 1296 | 1305 | flavour <- mkHpcCCFlavour <$> getCCIndexM cc_name
|
| ... | ... | @@ -1299,7 +1308,7 @@ mkTickish boxLabel countEntries topOnly pos fvs decl_path = do |
| 1299 | 1308 | return $ ProfNote cc count True{-scopes-}
|
| 1300 | 1309 | |
| 1301 | 1310 | Breakpoints -> do
|
| 1302 | - i <- addMixEntry me
|
|
| 1311 | + i <- addBreakpointEntry me
|
|
| 1303 | 1312 | pure (Breakpoint noExtField (BreakpointId (this_mod env) i) ids)
|
| 1304 | 1313 | |
| 1305 | 1314 | SourceNotes | RealSrcSpan pos' _ <- pos ->
|
| ... | ... | @@ -1324,19 +1333,19 @@ mkBinTickBoxHpc :: (Bool -> BoxLabel) -> SrcSpan -> LHsExpr GhcTc |
| 1324 | 1333 | mkBinTickBoxHpc boxLabel pos e = do
|
| 1325 | 1334 | env <- getEnv
|
| 1326 | 1335 | binTick <- HsBinTick
|
| 1327 | - <$> addMixEntry (Tick { tick_loc = pos
|
|
| 1336 | + <$> addHpcEntry (Tick { tick_loc = pos
|
|
| 1328 | 1337 | , tick_path = declPath env
|
| 1329 | 1338 | , tick_ids = []
|
| 1330 | 1339 | , tick_label = boxLabel True
|
| 1331 | 1340 | })
|
| 1332 | - <*> addMixEntry (Tick { tick_loc = pos
|
|
| 1341 | + <*> addHpcEntry (Tick { tick_loc = pos
|
|
| 1333 | 1342 | , tick_path = declPath env
|
| 1334 | 1343 | , tick_ids = []
|
| 1335 | 1344 | , tick_label = boxLabel False
|
| 1336 | 1345 | })
|
| 1337 | 1346 | <*> pure e
|
| 1338 | 1347 | tick <- HpcTick (this_mod env)
|
| 1339 | - <$> addMixEntry (Tick { tick_loc = pos
|
|
| 1348 | + <$> addHpcEntry (Tick { tick_loc = pos
|
|
| 1340 | 1349 | , tick_path = declPath env
|
| 1341 | 1350 | , tick_ids = []
|
| 1342 | 1351 | , tick_label = ExpBox False
|
| ... | ... | @@ -20,6 +20,7 @@ import GHC.ByteCode.Types |
| 20 | 20 | |
| 21 | 21 | import GHC.Cmm.CallConv
|
| 22 | 22 | import GHC.Cmm.Expr
|
| 23 | +import GHC.Cmm.CLabel (mkHpcTicksLabel, pprCLabel)
|
|
| 23 | 24 | import GHC.Cmm.Reg ( GlobalArgRegs(..) )
|
| 24 | 25 | import GHC.Cmm.Node
|
| 25 | 26 | import GHC.Cmm.Utils
|
| ... | ... | @@ -73,6 +74,7 @@ import Data.List ( genericReplicate, intersperse |
| 73 | 74 | import Foreign hiding (shiftL, shiftR)
|
| 74 | 75 | import Control.Monad
|
| 75 | 76 | import Data.Char
|
| 77 | +import Data.Word
|
|
| 76 | 78 | |
| 77 | 79 | import GHC.Unit.Module
|
| 78 | 80 | |
| ... | ... | @@ -603,6 +605,11 @@ schemeE _d _s _p (StgTick (Breakpoint _ bp_id _) _rhs) |
| 603 | 605 | = pprPanic "schemeE: Breakpoint without let binding:"
|
| 604 | 606 | (ppr bp_id <+> text "forgot to run bcPrep?")
|
| 605 | 607 | |
| 608 | +schemeE d s p (StgTick (HpcTick mod ix) rhs) = do
|
|
| 609 | + platform <- profilePlatform <$> getProfile
|
|
| 610 | + rhs_code <- schemeE d s p rhs
|
|
| 611 | + pure (unitOL (HPC_TICK (mkHpcTickLabel platform mod) (fromIntegral ix)) `appOL` rhs_code)
|
|
| 612 | + |
|
| 606 | 613 | -- ignore other kinds of tick
|
| 607 | 614 | schemeE d s p (StgTick _ rhs) = schemeE d s p rhs
|
| 608 | 615 | |
| ... | ... | @@ -2767,6 +2774,10 @@ getLastBreakTick = BcM $ \env st -> |
| 2767 | 2774 | tickFS :: FastString
|
| 2768 | 2775 | tickFS = fsLit "ticked"
|
| 2769 | 2776 | |
| 2777 | +mkHpcTickLabel :: Platform -> Module -> FastString
|
|
| 2778 | +mkHpcTickLabel platform mod =
|
|
| 2779 | + fsLit (showSDocOneLine defaultSDocContext (pprCode (pprCLabel platform (mkHpcTicksLabel mod))))
|
|
| 2780 | + |
|
| 2770 | 2781 | -- Dehydrating CgBreakInfo
|
| 2771 | 2782 | |
| 2772 | 2783 | dehydrateCgBreakInfo :: [TyVar] -> [Maybe (Id, Word)] -> Type -> Either InternalBreakLoc BreakpointId -> CgBreakInfo
|
| ... | ... | @@ -101,6 +101,13 @@ disInstr ( StgBCO *bco, int pc ) |
| 101 | 101 | }
|
| 102 | 102 | debugBelch("\n");
|
| 103 | 103 | break; }
|
| 104 | + case bci_HPC_TICK: {
|
|
| 105 | + W_ p1, info_wix;
|
|
| 106 | + p1 = BCO_GET_LARGE_ARG;
|
|
| 107 | + info_wix = BCO_READ_NEXT_32;
|
|
| 108 | + debugBelch("HPC_TICK "); printPtr((StgPtr)literals[p1]);
|
|
| 109 | + debugBelch(" %" FMT_Word "\n", info_wix);
|
|
| 110 | + break; }
|
|
| 104 | 111 | case bci_SWIZZLE: {
|
| 105 | 112 | W_ stkoff = BCO_GET_LARGE_ARG;
|
| 106 | 113 | StgInt by = BCO_GET_LARGE_ARG;
|
| ... | ... | @@ -270,6 +270,9 @@ hs_hpc_module(char *modName, |
| 270 | 270 | HpcModuleInfo *tmpModule;
|
| 271 | 271 | uint32_t i;
|
| 272 | 272 | |
| 273 | + debugTrace(DEBUG_hpc, "hs_hpc_module(%s, count=%u, hash=%u)\n",
|
|
| 274 | + modName, modCount, modHashNo);
|
|
| 275 | + |
|
| 273 | 276 | if (moduleHash == NULL) {
|
| 274 | 277 | moduleHash = allocStrHashTable();
|
| 275 | 278 | }
|
| ... | ... | @@ -320,6 +323,8 @@ hs_hpc_module(char *modName, |
| 320 | 323 | }
|
| 321 | 324 | tmpModule->from_file = false;
|
| 322 | 325 | }
|
| 326 | + |
|
| 327 | + startupHpc();
|
|
| 323 | 328 | }
|
| 324 | 329 | |
| 325 | 330 | static void
|
| ... | ... | @@ -1739,6 +1739,7 @@ run_BCO: |
| 1739 | 1739 | &&lbl_bci_TESTEQ_W8 - &&lbl_bci_DEFAULT,
|
| 1740 | 1740 | &&lbl_bci_PRIMCALL - &&lbl_bci_DEFAULT,
|
| 1741 | 1741 | &&lbl_bci_BCO_NAME - &&lbl_bci_DEFAULT,
|
| 1742 | + &&lbl_bci_HPC_TICK - &&lbl_bci_DEFAULT,
|
|
| 1742 | 1743 | &&lbl_bci_DEFAULT - &&lbl_bci_DEFAULT,
|
| 1743 | 1744 | &&lbl_bci_OP_ADD_64 - &&lbl_bci_DEFAULT,
|
| 1744 | 1745 | &&lbl_bci_OP_SUB_64 - &&lbl_bci_DEFAULT,
|
| ... | ... | @@ -2106,6 +2107,15 @@ run_BCO: |
| 2106 | 2107 | NEXT_INSTRUCTION;
|
| 2107 | 2108 | }
|
| 2108 | 2109 | |
| 2110 | + INSTRUCTION(bci_HPC_TICK): {
|
|
| 2111 | + W_ arg1_ticks_array, arg2_tick_index;
|
|
| 2112 | + arg1_ticks_array = BCO_GET_LARGE_ARG;
|
|
| 2113 | + arg2_tick_index = BCO_READ_NEXT_32;
|
|
| 2114 | + |
|
| 2115 | + ((StgWord64*)BCO_LIT(arg1_ticks_array))[arg2_tick_index]++;
|
|
| 2116 | + NEXT_INSTRUCTION;
|
|
| 2117 | + }
|
|
| 2118 | + |
|
| 2109 | 2119 | INSTRUCTION(bci_STKCHECK): {
|
| 2110 | 2120 | // Explicit stack check at the beginning of a function
|
| 2111 | 2121 | // *only* (stack checks in case alternatives are
|
| ... | ... | @@ -118,6 +118,7 @@ |
| 118 | 118 | #define bci_PRIMCALL 87
|
| 119 | 119 | |
| 120 | 120 | #define bci_BCO_NAME 88
|
| 121 | +#define bci_HPC_TICK 89
|
|
| 121 | 122 | |
| 122 | 123 | #define bci_OP_ADD_64 90
|
| 123 | 124 | #define bci_OP_SUB_64 91
|
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +inc :: Int -> Int
|
|
| 4 | +inc x = x + 1
|
|
| 5 | + |
|
| 6 | +double :: Int -> Int
|
|
| 7 | +double x = x * 2
|
|
| 8 | + |
|
| 9 | +main :: IO ()
|
|
| 10 | +main = print (double (inc 1011)) |
| ... | ... | @@ -7,3 +7,9 @@ hpc_ghc_ghci: |
| 7 | 7 | '$(TEST_HC)' $(TEST_HC_OPTS) -fhpc -c A.hs
|
| 8 | 8 | echo b | '$(TEST_HC)' $(TEST_HC_OPTS_INTERACTIVE) B.hs
|
| 9 | 9 | |
| 10 | +hpc_ghc_ghci_bytecode:
|
|
| 11 | + rm -f ./*.tix
|
|
| 12 | + printf "main\n:quit\n" | '$(TEST_HC)' $(TEST_HC_OPTS_INTERACTIVE) -fhpc -fbyte-code-and-object-code -fprefer-byte-code BytecodeMain.hs
|
|
| 13 | + @[ -f .hpc/Main.mix ] || (echo "ERROR: Expected .hpc/Main.mix file not found"; exit 1)
|
|
| 14 | + @set -- ./*.tix; [ -f "$$1" ] || (echo "ERROR: Expected .tix file not found"; exit 1); '$(HPC)' report "$$1" Main > hpc-report.txt
|
|
| 15 | + @grep -F "100% expressions used" hpc-report.txt >/dev/null || (echo "ERROR: Expected full expression coverage in hpc report"; cat hpc-report.txt; exit 1) |
| 1 | +2024 |
| ... | ... | @@ -3,3 +3,8 @@ test('hpc_ghc_ghci', |
| 3 | 3 | [extra_files(['A.hs', 'B.hs']),
|
| 4 | 4 | only_ways(['normal']), when(compiler_profiled(), skip), req_interp],
|
| 5 | 5 | run_command, ['$MAKE -s --no-print-directory hpc_ghc_ghci'])
|
| 6 | + |
|
| 7 | +test('hpc_ghc_ghci_bytecode',
|
|
| 8 | + [extra_files(['BytecodeMain.hs']),
|
|
| 9 | + only_ways(['normal']), when(compiler_profiled(), skip), req_interp],
|
|
| 10 | + run_command, ['$MAKE -s --no-print-directory hpc_ghc_ghci_bytecode']) |