[Git][ghc/ghc][wip/romes/step-out-11] debugger: Re-use the last BreakpointId whole in step-out

Rodrigo Mesquita pushed to branch wip/romes/step-out-11 at Glasgow Haskell Compiler / GHC Commits: 6d265ed5 by Rodrigo Mesquita at 2025-08-01T16:34:14+01:00 debugger: Re-use the last BreakpointId whole in step-out Previously, to come up with a location to stop at for `:stepout`, we would store the location of the last BreakpointId surrounding the continuation, as described by Note [Debugger: Stepout internal break locs]. However, re-using just the location from the last source breakpoint isn't sufficient to provide the necessary information in the break location. Specifically, it wouldn't bind any variables at that location. Really, there is no reason not to re-use the last breakpoint wholesale, and re-use all the information we had there. Step-out should behave just as if we had stopped at the call, but s.t. continuing will not re-execute the call. This commit updates the CgBreakInfo to always store a BreakpointId, be it the original one or the one we're emulating (for step-out). More uniform and better information on break. - - - - - 2 changed files: - compiler/GHC/ByteCode/Breakpoints.hs - compiler/GHC/StgToByteCode.hs Changes: ===================================== compiler/GHC/ByteCode/Breakpoints.hs ===================================== @@ -167,7 +167,7 @@ data CgBreakInfo { cgb_tyvars :: ![IfaceTvBndr] -- ^ Type variables in scope at the breakpoint , cgb_vars :: ![Maybe (IfaceIdBndr, Word)] , cgb_resty :: !IfaceType - , cgb_tick_id :: !(Either InternalBreakLoc BreakpointId) + , cgb_tick_id :: !BreakpointId -- ^ This field records the original breakpoint tick identifier for this -- internal breakpoint info. It is used to convert a breakpoint -- *occurrence* index ('InternalBreakpointId') into a *definition* index @@ -177,8 +177,10 @@ data CgBreakInfo -- necessarily the same: See Note [Breakpoint identifiers]. -- -- If there is no original tick identifier (that is, the breakpoint was - -- created during code generation), instead refer directly to the SrcSpan - -- we want to use for it. + -- created during code generation), we re-use the BreakpointId of something else. + -- It would also be reasonable to have an @Either something BreakpointId@ + -- for @cgb_tick_id@, but currently we can always re-use a source-level BreakpointId. + -- In the case of step-out, see Note [Debugger: Stepout internal break locs] } -- See Note [Syncing breakpoint info] in GHC.Runtime.Eval @@ -207,7 +209,7 @@ assert_modules_match ibi_mod imbs_mod = -- | Get the source module and tick index for this breakpoint -- (as opposed to the module where this breakpoint occurs, which is in 'InternalBreakpointId') -getBreakSourceId :: InternalBreakpointId -> InternalModBreaks -> Either InternalBreakLoc BreakpointId +getBreakSourceId :: InternalBreakpointId -> InternalModBreaks -> BreakpointId getBreakSourceId (InternalBreakpointId ibi_mod ibi_ix) imbs = assert_modules_match ibi_mod (imodBreaks_module imbs) $ let cgb = imodBreaks_breakInfo imbs IM.! ibi_ix @@ -219,24 +221,23 @@ getBreakSourceMod (InternalBreakpointId ibi_mod ibi_ix) imbs = assert_modules_match ibi_mod (imodBreaks_module imbs) $ let cgb = imodBreaks_breakInfo imbs IM.! ibi_ix in case cgb_tick_id cgb of - Left InternalBreakLoc{} -> imodBreaks_module imbs - Right BreakpointId{bi_tick_mod} -> bi_tick_mod + BreakpointId{bi_tick_mod} -> bi_tick_mod -- | Get the source span for this breakpoint getBreakLoc :: (Module -> IO ModBreaks) -> InternalBreakpointId -> InternalModBreaks -> IO SrcSpan -getBreakLoc = getBreakXXX modBreaks_locs (\(InternalBreakLoc x) -> x) +getBreakLoc = getBreakXXX modBreaks_locs -- | Get the vars for this breakpoint getBreakVars :: (Module -> IO ModBreaks) -> InternalBreakpointId -> InternalModBreaks -> IO [OccName] -getBreakVars = getBreakXXX modBreaks_vars (const []) +getBreakVars = getBreakXXX modBreaks_vars -- | Get the decls for this breakpoint getBreakDecls :: (Module -> IO ModBreaks) -> InternalBreakpointId -> InternalModBreaks -> IO [String] -getBreakDecls = getBreakXXX modBreaks_decls (const []) +getBreakDecls = getBreakXXX modBreaks_decls -- | Get the decls for this breakpoint -getBreakCCS :: (Module -> IO ModBreaks) -> InternalBreakpointId -> InternalModBreaks -> IO (Maybe (String, String)) -getBreakCCS = getBreakXXX (fmap Just . modBreaks_ccs) (const Nothing) +getBreakCCS :: (Module -> IO ModBreaks) -> InternalBreakpointId -> InternalModBreaks -> IO ((String, String)) +getBreakCCS = getBreakXXX modBreaks_ccs -- | Internal utility to access a ModBreaks field at a particular breakpoint index -- @@ -253,12 +254,12 @@ getBreakCCS = getBreakXXX (fmap Just . modBreaks_ccs) (const Nothing) -- -- To avoid cyclic dependencies, we instead receive a function that looks up -- the 'ModBreaks' given a 'Module' -getBreakXXX :: (ModBreaks -> Array BreakTickIndex a) -> (InternalBreakLoc -> a) -> (Module -> IO ModBreaks) -> InternalBreakpointId -> InternalModBreaks -> IO a -getBreakXXX view viewInternal lookupModule (InternalBreakpointId ibi_mod ibi_ix) imbs = +getBreakXXX :: (ModBreaks -> Array BreakTickIndex a) -> (Module -> IO ModBreaks) -> InternalBreakpointId -> InternalModBreaks -> IO a +getBreakXXX view lookupModule (InternalBreakpointId ibi_mod ibi_ix) imbs = assert_modules_match ibi_mod (imodBreaks_module imbs) $ do let cgb = imodBreaks_breakInfo imbs IM.! ibi_ix case cgb_tick_id cgb of - Right BreakpointId{bi_tick_mod, bi_tick_index} + BreakpointId{bi_tick_mod, bi_tick_index} | bi_tick_mod == ibi_mod -> do let these_mbs = imodBreaks_modBreaks imbs @@ -267,8 +268,6 @@ getBreakXXX view viewInternal lookupModule (InternalBreakpointId ibi_mod ibi_ix) -> do other_mbs <- lookupModule bi_tick_mod return $ view other_mbs ! bi_tick_index - Left l -> - return $ viewInternal l -------------------------------------------------------------------------------- -- Instances ===================================== compiler/GHC/StgToByteCode.hs ===================================== @@ -63,7 +63,7 @@ import GHC.StgToCmm.Closure ( NonVoid(..), fromNonVoid, idPrimRepU, assertNonVoidIds, assertNonVoidStgArgs ) import GHC.StgToCmm.Layout import GHC.Runtime.Heap.Layout hiding (WordOff, ByteOff, wordsToBytes) -import GHC.Runtime.Interpreter ( interpreterProfiled, readIModModBreaks ) +import GHC.Runtime.Interpreter ( interpreterProfiled ) import GHC.Data.Bitmap import GHC.Data.FlatBag as FlatBag import GHC.Data.OrdList @@ -99,7 +99,6 @@ import GHC.CoreToIface import Control.Monad.IO.Class import Control.Monad.Trans.Reader (ReaderT(..)) import Control.Monad.Trans.State (StateT(..)) -import Data.Array ((!)) -- ----------------------------------------------------------------------------- -- Generating byte code for a complete module @@ -406,7 +405,7 @@ schemeER_wrk d p (StgTick bp@(Breakpoint tick_ty tick_id fvs) rhs) = do ty_vars = tyCoVarsOfTypesWellScoped (tick_ty:map idType fvs) toWord :: Maybe (Id, WordOff) -> Maybe (Id, Word) toWord = fmap (\(i, wo) -> (i, fromIntegral wo)) - breakInfo = dehydrateCgBreakInfo ty_vars (map toWord idOffSets) tick_ty (Right tick_id) + breakInfo = dehydrateCgBreakInfo ty_vars (map toWord idOffSets) tick_ty tick_id mibi <- newBreakInfo breakInfo @@ -1341,14 +1340,13 @@ doCase d s p scrut bndr alts -- continuation BCO, for step-out. -- See Note [Debugger: Stepout internal break locs] -> do - internal_tick_loc <- makeCaseInternalBreakLoc tick_id -- same fvs available in the case expression are available in the case continuation let idOffSets = getVarOffSets platform d p fvs ty_vars = tyCoVarsOfTypesWellScoped (tick_ty:map idType fvs) toWord :: Maybe (Id, WordOff) -> Maybe (Id, Word) toWord = fmap (\(i, wo) -> (i, fromIntegral wo)) - breakInfo = dehydrateCgBreakInfo ty_vars (map toWord idOffSets) tick_ty (Left internal_tick_loc) + breakInfo = dehydrateCgBreakInfo ty_vars (map toWord idOffSets) tick_ty tick_id mibi <- newBreakInfo breakInfo return $ case mibi of @@ -1374,25 +1372,6 @@ doCase d s p scrut bndr alts _ -> panic "schemeE(StgCase).push_alts" in return (PUSH_ALTS alt_bco scrut_rep `consOL` scrut_code) --- | Come up with an 'InternalBreakLoc' from the location of the given 'BreakpointId'. --- See also Note [Debugger: Stepout internal break locs] -makeCaseInternalBreakLoc :: BreakpointId -> BcM InternalBreakLoc -makeCaseInternalBreakLoc bid = do - hug <- hsc_HUG <$> getHscEnv - curr_mod <- getCurrentModule - mb_mod_brks <- getCurrentModBreaks - - InternalBreakLoc <$> case bid of - BreakpointId{bi_tick_mod, bi_tick_index} - | bi_tick_mod == curr_mod - , Just these_mbs <- mb_mod_brks - -> do - return $ modBreaks_locs these_mbs ! bi_tick_index - | otherwise - -> do - other_mbs <- liftIO $ readIModModBreaks hug bi_tick_mod - return $ modBreaks_locs other_mbs ! bi_tick_index - {- Note [Debugger: Stepout internal break locs] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1438,6 +1417,8 @@ always have a relevant breakpoint location: - So the source location will point to the thing you've just stepped out of + - The variables available are the same as the ones bound just before entering + - Doing :step-local from there will put you on the selected alternative (which at the source level may also be the e.g. next line in a do-block) @@ -2758,9 +2739,6 @@ newBreakInfo info = BcM $ \env st -> do getCurrentModule :: BcM Module getCurrentModule = BcM $ \env st -> return (bcm_module env, st) -getCurrentModBreaks :: BcM (Maybe ModBreaks) -getCurrentModBreaks = BcM $ \env st -> return (modBreaks env, st) - withBreakTick :: StgTickish -> BcM a -> BcM a withBreakTick bp (BcM act) = BcM $ \env st -> act env{last_bp_tick=Just bp} st @@ -2774,7 +2752,7 @@ tickFS = fsLit "ticked" -- Dehydrating CgBreakInfo -dehydrateCgBreakInfo :: [TyVar] -> [Maybe (Id, Word)] -> Type -> Either InternalBreakLoc BreakpointId -> CgBreakInfo +dehydrateCgBreakInfo :: [TyVar] -> [Maybe (Id, Word)] -> Type -> BreakpointId -> CgBreakInfo dehydrateCgBreakInfo ty_vars idOffSets tick_ty bid = CgBreakInfo { cgb_tyvars = map toIfaceTvBndr ty_vars View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/6d265ed5d591c24e5c0689825223a0a2... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/6d265ed5d591c24e5c0689825223a0a2... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Rodrigo Mesquita (@alt-romes)