diff options
author | Ömer Sinan Ağacan <omeragacan@gmail.com> | 2016-09-20 00:19:15 -0400 |
---|---|---|
committer | Ömer Sinan Ağacan <omeragacan@gmail.com> | 2016-09-20 00:19:27 -0400 |
commit | 14c2e8e0c11bb2b95f81303284d1460bb80a9a98 (patch) | |
tree | b9c67117f0e2f7f79037e9a07c20a0256800f5cc /compiler/codeGen | |
parent | ea310f9956179f91ca973bc747b0bc7b061bc174 (diff) | |
download | haskell-14c2e8e0c11bb2b95f81303284d1460bb80a9a98.tar.gz |
Codegen for case: Remove redundant void id checks
New unarise (714bebf) eliminates void binders in patterns already, so no
need to eliminate them here. I leave assertions to make sure this is the
case.
Assertion failure -> bug in unarise
Reviewers: bgamari, simonpj, austin, simonmar, hvr
Reviewed By: simonpj
Subscribers: thomie
Differential Revision: https://phabricator.haskell.org/D2416
Diffstat (limited to 'compiler/codeGen')
-rw-r--r-- | compiler/codeGen/StgCmm.hs | 19 | ||||
-rw-r--r-- | compiler/codeGen/StgCmmBind.hs | 20 | ||||
-rw-r--r-- | compiler/codeGen/StgCmmClosure.hs | 51 | ||||
-rw-r--r-- | compiler/codeGen/StgCmmCon.hs | 15 | ||||
-rw-r--r-- | compiler/codeGen/StgCmmEnv.hs | 22 | ||||
-rw-r--r-- | compiler/codeGen/StgCmmExpr.hs | 26 | ||||
-rw-r--r-- | compiler/codeGen/StgCmmLayout.hs | 30 | ||||
-rw-r--r-- | compiler/codeGen/StgCmmTicky.hs | 3 |
8 files changed, 114 insertions, 72 deletions
diff --git a/compiler/codeGen/StgCmm.hs b/compiler/codeGen/StgCmm.hs index 85f8845c8a..28ca97d9a2 100644 --- a/compiler/codeGen/StgCmm.hs +++ b/compiler/codeGen/StgCmm.hs @@ -138,7 +138,9 @@ cgTopRhs :: DynFlags -> RecFlag -> Id -> StgRhs -> (CgIdInfo, FCode ()) -- It's already been externalised if necessary cgTopRhs dflags _rec bndr (StgRhsCon _cc con args) - = cgTopRhsCon dflags bndr con args + = cgTopRhsCon dflags bndr con (assertNonVoidStgArgs args) + -- con args are always non-void, + -- see Note [Post-unarisation invariants] in UnariseStg cgTopRhs dflags rec bndr (StgRhsClosure cc bi fvs upd_flag args body) = ASSERT(null fvs) -- There should be no free variables @@ -219,8 +221,8 @@ cgDataCon data_con = do { dflags <- getDynFlags ; let (tot_wds, -- #ptr_wds + #nonptr_wds - ptr_wds, -- #ptr_wds - arg_things) = mkVirtConstrOffsets dflags arg_reps + ptr_wds) -- #ptr_wds + = mkVirtConstrSizes dflags arg_reps nonptr_wds = tot_wds - ptr_wds @@ -240,14 +242,17 @@ cgDataCon data_con -- NB 2: We don't set CC when entering data (WDP 94/06) do { _ <- ticky_code ; ldvEnter (CmmReg nodeReg) - ; tickyReturnOldCon (length arg_things) + ; tickyReturnOldCon (length arg_reps) ; void $ emitReturn [cmmOffsetB dflags (CmmReg nodeReg) (tagForCon dflags data_con)] } -- The case continuation code expects a tagged pointer - arg_reps :: [(PrimRep, UnaryType)] - arg_reps = [(typePrimRep rep_ty, rep_ty) | ty <- dataConRepArgTys data_con - , rep_ty <- repTypeArgs ty] + -- We're generating info tables, so we don't know and care about + -- what the actual arguments are. Using () here as the place holder. + arg_reps :: [NonVoid PrimRep] + arg_reps = [NonVoid (typePrimRep rep_ty) | ty <- dataConRepArgTys data_con + , rep_ty <- repTypeArgs ty + , not (isVoidTy rep_ty)] -- Dynamic closure code for non-nullary constructors only ; when (not (isNullaryRepDataCon data_con)) diff --git a/compiler/codeGen/StgCmmBind.hs b/compiler/codeGen/StgCmmBind.hs index 93756ec406..e173f354b7 100644 --- a/compiler/codeGen/StgCmmBind.hs +++ b/compiler/codeGen/StgCmmBind.hs @@ -206,7 +206,9 @@ cgRhs :: Id cgRhs id (StgRhsCon cc con args) = withNewTickyCounterCon (idName id) $ - buildDynCon id True cc con args + buildDynCon id True cc con (assertNonVoidStgArgs args) + -- con args are always non-void, + -- see Note [Post-unarisation invariants] in UnariseStg {- See Note [GC recovery] in compiler/codeGen/StgCmmClosure.hs -} cgRhs id (StgRhsClosure cc bi fvs upd_flag args body) @@ -273,8 +275,9 @@ mkRhsClosure dflags bndr _cc _bi , StgApp selectee [{-no args-}] <- strip sel_expr , the_fv == scrutinee -- Scrutinee is the only free variable - , let (_, _, params_w_offsets) = mkVirtConstrOffsets dflags (addIdReps params) - -- Just want the layout + , let (_, _, params_w_offsets) = mkVirtConstrOffsets dflags (addIdReps (assertNonVoidIds params)) + -- pattern binders are always non-void, + -- see Note [Post-unarisation invariants] in UnariseStg , Just the_offset <- assocMaybe params_w_offsets (NonVoid selectee) , let offset_into_int = bytesToWordsRoundUp dflags the_offset @@ -305,7 +308,7 @@ mkRhsClosure dflags bndr _cc _bi -- args are all distinct local variables -- The "-1" is for fun_id -- Missed opportunity: (f x x) is not detected - , all (isGcPtrRep . idPrimRep . unsafe_stripNV) fvs + , all (isGcPtrRep . idPrimRep . fromNonVoid) fvs , isUpdatable upd_flag , n_fvs <= mAX_SPEC_AP_SIZE dflags , not (gopt Opt_SccProfilingOn dflags) @@ -348,7 +351,7 @@ mkRhsClosure dflags bndr cc _ fvs upd_flag args body fv_details :: [(NonVoid Id, ByteOff)] (tot_wds, ptr_wds, fv_details) = mkVirtHeapOffsets dflags (isLFThunk lf_info) - (addIdReps (map unsafe_stripNV reduced_fvs)) + (addIdReps reduced_fvs) closure_info = mkClosureInfo dflags False -- Not static bndr lf_info tot_wds ptr_wds descr @@ -392,7 +395,8 @@ cgRhsStdThunk bndr lf_info payload mod_name <- getModuleName ; dflags <- getDynFlags ; let (tot_wds, ptr_wds, payload_w_offsets) - = mkVirtHeapOffsets dflags (isLFThunk lf_info) (addArgReps payload) + = mkVirtHeapOffsets dflags (isLFThunk lf_info) + (addArgReps (nonVoidStgArgs payload)) descr = closureDescription dflags mod_name (idName bndr) closure_info = mkClosureInfo dflags False -- Not static @@ -421,9 +425,9 @@ mkClosureLFInfo :: DynFlags -> LambdaFormInfo mkClosureLFInfo dflags bndr top fvs upd_flag args | null args = - mkLFThunk (idType bndr) top (map unsafe_stripNV fvs) upd_flag + mkLFThunk (idType bndr) top (map fromNonVoid fvs) upd_flag | otherwise = - mkLFReEntrant top (map unsafe_stripNV fvs) args (mkArgDescr dflags args) + mkLFReEntrant top (map fromNonVoid fvs) args (mkArgDescr dflags args) ------------------------------------------------------------------------ diff --git a/compiler/codeGen/StgCmmClosure.hs b/compiler/codeGen/StgCmmClosure.hs index f831789454..23b803cc56 100644 --- a/compiler/codeGen/StgCmmClosure.hs +++ b/compiler/codeGen/StgCmmClosure.hs @@ -18,6 +18,9 @@ module StgCmmClosure ( idPrimRep, isVoidRep, isGcPtrRep, addIdReps, addArgReps, argPrimRep, + NonVoid(..), fromNonVoid, nonVoidIds, nonVoidStgArgs, + assertNonVoidIds, assertNonVoidStgArgs, + -- * LambdaFormInfo LambdaFormInfo, -- Abstract StandardFormInfo, -- ...ditto... @@ -84,6 +87,8 @@ import Outputable import DynFlags import Util +import Data.Coerce (coerce) + ----------------------------------------------------------------------------- -- Data types and synonyms ----------------------------------------------------------------------------- @@ -115,6 +120,42 @@ isKnownFun LFLetNoEscape = True isKnownFun _ = False +------------------------------------- +-- Non-void types +------------------------------------- +-- We frequently need the invariant that an Id or a an argument +-- is of a non-void type. This type is a witness to the invariant. + +newtype NonVoid a = NonVoid a + deriving (Eq, Show) + +fromNonVoid :: NonVoid a -> a +fromNonVoid (NonVoid a) = a + +instance (Outputable a) => Outputable (NonVoid a) where + ppr (NonVoid a) = ppr a + +nonVoidIds :: [Id] -> [NonVoid Id] +nonVoidIds ids = [NonVoid id | id <- ids, not (isVoidTy (idType id))] + +-- | Used in places where some invariant ensures that all these Ids are +-- non-void; e.g. constructor field binders in case expressions. +-- See Note [Post-unarisation invariants] in UnariseStg. +assertNonVoidIds :: [Id] -> [NonVoid Id] +assertNonVoidIds ids = ASSERT(not (any (isVoidTy . idType) ids)) + coerce ids + +nonVoidStgArgs :: [StgArg] -> [NonVoid StgArg] +nonVoidStgArgs args = [NonVoid arg | arg <- args, not (isVoidTy (stgArgType arg))] + +-- | Used in places where some invariant ensures that all these arguments are +-- non-void; e.g. constructor arguments. +-- See Note [Post-unarisation invariants] in UnariseStg. +assertNonVoidStgArgs :: [StgArg] -> [NonVoid StgArg] +assertNonVoidStgArgs args = ASSERT(not (any (isVoidTy . stgArgType) args)) + coerce args + + ----------------------------------------------------------------------------- -- Representations ----------------------------------------------------------------------------- @@ -126,11 +167,13 @@ idPrimRep id = typePrimRep (idType id) -- NB: typePrimRep fails on unboxed tuples, -- but by StgCmm no Ids have unboxed tuple type -addIdReps :: [Id] -> [(PrimRep, Id)] -addIdReps ids = [(idPrimRep id, id) | id <- ids] +addIdReps :: [NonVoid Id] -> [NonVoid (PrimRep, Id)] +addIdReps = map (\id -> let id' = fromNonVoid id + in NonVoid (idPrimRep id', id')) -addArgReps :: [StgArg] -> [(PrimRep, StgArg)] -addArgReps args = [(argPrimRep arg, arg) | arg <- args] +addArgReps :: [NonVoid StgArg] -> [NonVoid (PrimRep, StgArg)] +addArgReps = map (\arg -> let arg' = fromNonVoid arg + in NonVoid (argPrimRep arg', arg')) argPrimRep :: StgArg -> PrimRep argPrimRep arg = typePrimRep (stgArgType arg) diff --git a/compiler/codeGen/StgCmmCon.hs b/compiler/codeGen/StgCmmCon.hs index 4255f10201..3a615f750f 100644 --- a/compiler/codeGen/StgCmmCon.hs +++ b/compiler/codeGen/StgCmmCon.hs @@ -58,7 +58,7 @@ import Data.Char cgTopRhsCon :: DynFlags -> Id -- Name of thing bound to this RHS -> DataCon -- Id - -> [StgArg] -- Args + -> [NonVoid StgArg] -- Args -> (CgIdInfo, FCode ()) cgTopRhsCon dflags id con args = let id_info = litIdInfo dflags id (mkConLFInfo con) (CmmLabel closure_label) @@ -72,7 +72,7 @@ cgTopRhsCon dflags id con args = do { this_mod <- getModuleName ; when (platformOS (targetPlatform dflags) == OSMinGW32) $ -- Windows DLLs have a problem with static cross-DLL refs. - ASSERT( not (isDllConApp dflags this_mod con args) ) return () + MASSERT( not (isDllConApp dflags this_mod con (map fromNonVoid args)) ) ; ASSERT( args `lengthIs` countConRepArgs con ) return () -- LAY IT OUT @@ -120,7 +120,7 @@ buildDynCon :: Id -- Name of the thing to which this constr will -> CostCentreStack -- Where to grab cost centre from; -- current CCS if currentOrSubsumedCCS -> DataCon -- The data constructor - -> [StgArg] -- Its args + -> [NonVoid StgArg] -- Its args -> FCode (CgIdInfo, FCode CmmAGraph) -- Return details about how to find it and initialization code buildDynCon binder actually_bound cc con args @@ -133,7 +133,7 @@ buildDynCon' :: DynFlags -> Id -> Bool -> CostCentreStack -> DataCon - -> [StgArg] + -> [NonVoid StgArg] -> FCode (CgIdInfo, FCode CmmAGraph) {- We used to pass a boolean indicating whether all the @@ -192,7 +192,7 @@ because they don't support cross package data references well. buildDynCon' dflags platform binder _ _cc con [arg] | maybeIntLikeCon con , platformOS platform /= OSMinGW32 || not (gopt Opt_PIC dflags) - , StgLitArg (MachInt val) <- arg + , NonVoid (StgLitArg (MachInt val)) <- arg , val <= fromIntegral (mAX_INTLIKE dflags) -- Comparisons at type Integer! , val >= fromIntegral (mIN_INTLIKE dflags) -- ...ditto... = do { let intlike_lbl = mkCmmClosureLabel rtsUnitId (fsLit "stg_INTLIKE") @@ -206,7 +206,7 @@ buildDynCon' dflags platform binder _ _cc con [arg] buildDynCon' dflags platform binder _ _cc con [arg] | maybeCharLikeCon con , platformOS platform /= OSMinGW32 || not (gopt Opt_PIC dflags) - , StgLitArg (MachChar val) <- arg + , NonVoid (StgLitArg (MachChar val)) <- arg , let val_int = ord val :: Int , val_int <= mAX_CHARLIKE dflags , val_int >= mIN_CHARLIKE dflags @@ -228,7 +228,6 @@ buildDynCon' dflags _ binder actually_bound ccs con args gen_code reg = do { let (tot_wds, ptr_wds, args_w_offsets) = mkVirtConstrOffsets dflags (addArgReps args) - -- No void args in args_w_offsets nonptr_wds = tot_wds - ptr_wds info_tbl = mkDataConInfoTable dflags con False ptr_wds nonptr_wds @@ -250,7 +249,7 @@ buildDynCon' dflags _ binder actually_bound ccs con args -- Binding constructor arguments --------------------------------------------------------------- -bindConArgs :: AltCon -> LocalReg -> [Id] -> FCode [LocalReg] +bindConArgs :: AltCon -> LocalReg -> [NonVoid Id] -> FCode [LocalReg] -- bindConArgs is called from cgAlt of a case -- (bindConArgs con args) augments the environment with bindings for the -- binders args, assuming that we have just returned from a 'case' which diff --git a/compiler/codeGen/StgCmmEnv.hs b/compiler/codeGen/StgCmmEnv.hs index 44d3df84ee..30307a2a3a 100644 --- a/compiler/codeGen/StgCmmEnv.hs +++ b/compiler/codeGen/StgCmmEnv.hs @@ -13,8 +13,6 @@ module StgCmmEnv ( litIdInfo, lneIdInfo, rhsIdInfo, mkRhsInit, idInfoToAmode, - NonVoid(..), unsafe_stripNV, nonVoidIds, - addBindC, addBindsC, bindArgsToRegs, bindToReg, rebindToReg, @@ -30,6 +28,7 @@ import TyCon import StgCmmMonad import StgCmmUtils import StgCmmClosure +import StgSyn (StgArg) import CLabel @@ -46,25 +45,6 @@ import UniqFM import VarEnv ------------------------------------- --- Non-void types -------------------------------------- --- We frequently need the invariant that an Id or a an argument --- is of a non-void type. This type is a witness to the invariant. - -newtype NonVoid a = NonVoid a - deriving (Eq, Show) - --- Use with care; if used inappropriately, it could break invariants. -unsafe_stripNV :: NonVoid a -> a -unsafe_stripNV (NonVoid a) = a - -instance (Outputable a) => Outputable (NonVoid a) where - ppr (NonVoid a) = ppr a - -nonVoidIds :: [Id] -> [NonVoid Id] -nonVoidIds ids = [NonVoid id | id <- ids, not (isVoidRep (idPrimRep id))] - -------------------------------------- -- Manipulating CgIdInfo ------------------------------------- diff --git a/compiler/codeGen/StgCmmExpr.hs b/compiler/codeGen/StgCmmExpr.hs index 91cfba6bd0..fdd902d8c7 100644 --- a/compiler/codeGen/StgCmmExpr.hs +++ b/compiler/codeGen/StgCmmExpr.hs @@ -525,24 +525,24 @@ isSimpleOp (StgPrimCallOp _) _ = return False ----------------- chooseReturnBndrs :: Id -> AltType -> [StgAlt] -> [NonVoid Id] --- These are the binders of a case that are assigned --- by the evaluation of the scrutinee --- Only non-void ones come back +-- These are the binders of a case that are assigned by the evaluation of the +-- scrutinee. +-- They're non-void, see Note [Post-unarisation invariants] in UnariseStg. chooseReturnBndrs bndr (PrimAlt _) _alts - = nonVoidIds [bndr] + = assertNonVoidIds [bndr] chooseReturnBndrs _bndr (MultiValAlt n) [(_, ids, _)] - = ASSERT2(n == length (nonVoidIds ids), ppr n $$ ppr ids $$ ppr _bndr) - nonVoidIds ids -- 'bndr' is not assigned! + = ASSERT2(n == length ids, ppr n $$ ppr ids $$ ppr _bndr) + assertNonVoidIds ids -- 'bndr' is not assigned! chooseReturnBndrs bndr (AlgAlt _) _alts - = nonVoidIds [bndr] -- Only 'bndr' is assigned + = assertNonVoidIds [bndr] -- Only 'bndr' is assigned chooseReturnBndrs bndr PolyAlt _alts - = nonVoidIds [bndr] -- Only 'bndr' is assigned + = assertNonVoidIds [bndr] -- Only 'bndr' is assigned chooseReturnBndrs _ _ _ = panic "chooseReturnBndrs" - -- UbxTupALt has only one alternative + -- MultiValAlt has only one alternative ------------------------------------- cgAlts :: (GcPlan,ReturnKind) -> NonVoid Id -> AltType -> [StgAlt] @@ -651,7 +651,9 @@ cgAltRhss gc_plan bndr alts = do cg_alt (con, bndrs, rhs) = getCodeScoped $ maybeAltHeapCheck gc_plan $ - do { _ <- bindConArgs con base_reg bndrs + do { _ <- bindConArgs con base_reg (assertNonVoidIds bndrs) + -- alt binders are always non-void, + -- see Note [Post-unarisation invariants] in UnariseStg ; _ <- cgExpr rhs ; return con } forkAlts (map cg_alt alts) @@ -677,7 +679,9 @@ cgConApp con stg_args | otherwise -- Boxed constructors; allocate and return = ASSERT2( stg_args `lengthIs` countConRepArgs con, ppr con <> parens (ppr (countConRepArgs con)) <+> ppr stg_args ) do { (idinfo, fcode_init) <- buildDynCon (dataConWorkId con) False - currentCCS con stg_args + currentCCS con (assertNonVoidStgArgs stg_args) + -- con args are always non-void, + -- see Note [Post-unarisation invariants] in UnariseStg -- The first "con" says that the name bound to this -- closure is is "con", which is a bit of a fudge, but -- it only affects profiling (hence the False) diff --git a/compiler/codeGen/StgCmmLayout.hs b/compiler/codeGen/StgCmmLayout.hs index 59bbc8d5ea..21698c7bbf 100644 --- a/compiler/codeGen/StgCmmLayout.hs +++ b/compiler/codeGen/StgCmmLayout.hs @@ -17,7 +17,7 @@ module StgCmmLayout ( slowCall, directCall, - mkVirtHeapOffsets, mkVirtConstrOffsets, getHpRelOffset, + mkVirtHeapOffsets, mkVirtConstrOffsets, mkVirtConstrSizes, getHpRelOffset, ArgRep(..), toArgRep, argRepSizeW -- re-exported from StgCmmArgRep ) where @@ -388,8 +388,8 @@ getHpRelOffset virtual_offset mkVirtHeapOffsets :: DynFlags - -> Bool -- True <=> is a thunk - -> [(PrimRep,a)] -- Things to make offsets for + -> Bool -- True <=> is a thunk + -> [NonVoid (PrimRep,a)] -- Things to make offsets for -> (WordOff, -- _Total_ number of words allocated WordOff, -- Number of words allocated for *pointers* [(NonVoid a, ByteOff)]) @@ -398,14 +398,12 @@ mkVirtHeapOffsets -- increasing offset; BUT THIS MAY BE DIFFERENT TO INPUT ORDER -- First in list gets lowest offset, which is initial offset + 1. -- --- Void arguments are removed, so output list may be shorter than --- input list --- -- mkVirtHeapOffsets always returns boxed things with smaller offsets -- than the unboxed things mkVirtHeapOffsets dflags is_thunk things - = ( bytesToWordsRoundUp dflags tot_bytes + = ASSERT(not (any (isVoidRep . fst . fromNonVoid) things)) + ( bytesToWordsRoundUp dflags tot_bytes , bytesToWordsRoundUp dflags bytes_of_ptrs , ptrs_w_offsets ++ non_ptrs_w_offsets ) @@ -414,24 +412,34 @@ mkVirtHeapOffsets dflags is_thunk things | otherwise = fixedHdrSizeW dflags hdr_bytes = wordsToBytes dflags hdr_words - non_void_things = filterOut (isVoidRep . fst) things - (ptrs, non_ptrs) = partition (isGcPtrRep . fst) non_void_things + (ptrs, non_ptrs) = partition (isGcPtrRep . fst . fromNonVoid) things (bytes_of_ptrs, ptrs_w_offsets) = mapAccumL computeOffset 0 ptrs (tot_bytes, non_ptrs_w_offsets) = mapAccumL computeOffset bytes_of_ptrs non_ptrs - computeOffset bytes_so_far (rep, thing) + computeOffset bytes_so_far nv_thing = (bytes_so_far + wordsToBytes dflags (argRepSizeW dflags (toArgRep rep)), (NonVoid thing, hdr_bytes + bytes_so_far)) + where (rep,thing) = fromNonVoid nv_thing -- | Just like mkVirtHeapOffsets, but for constructors mkVirtConstrOffsets - :: DynFlags -> [(PrimRep,a)] + :: DynFlags -> [NonVoid (PrimRep, a)] -> (WordOff, WordOff, [(NonVoid a, ByteOff)]) mkVirtConstrOffsets dflags = mkVirtHeapOffsets dflags False +-- | Just like mkVirtConstrOffsets, but used when we don't have the actual +-- arguments. Useful when e.g. generating info tables; we just need to know +-- sizes of pointer and non-pointer fields. +mkVirtConstrSizes :: DynFlags -> [NonVoid PrimRep] -> (WordOff, WordOff) +mkVirtConstrSizes dflags field_reps + = (tot_wds, ptr_wds) + where + (tot_wds, ptr_wds, _) = + mkVirtConstrOffsets dflags + (map (\nv_rep -> NonVoid (fromNonVoid nv_rep, ())) field_reps) ------------------------------------------------------------------------- -- diff --git a/compiler/codeGen/StgCmmTicky.hs b/compiler/codeGen/StgCmmTicky.hs index 8df2dcac28..8e4e5ece5a 100644 --- a/compiler/codeGen/StgCmmTicky.hs +++ b/compiler/codeGen/StgCmmTicky.hs @@ -107,7 +107,6 @@ module StgCmmTicky ( #include "HsVersions.h" import StgCmmArgRep ( slowCallPattern , toArgRep , argRepString ) -import StgCmmEnv ( NonVoid, unsafe_stripNV ) import StgCmmClosure import StgCmmUtils import StgCmmMonad @@ -234,7 +233,7 @@ emitTickyCounter cloType name args else n <+> ext <+> p ; fun_descr_lit <- newStringCLit $ showSDocDebug dflags ppr_for_ticky_name - ; arg_descr_lit <- newStringCLit $ map (showTypeCategory . idType . unsafe_stripNV) args + ; arg_descr_lit <- newStringCLit $ map (showTypeCategory . idType . fromNonVoid) args ; emitDataLits ctr_lbl -- Must match layout of includes/rts/Ticky.h's StgEntCounter -- |