Sebastian Graf pushed to branch wip/sg/enter-taggable-invariant at Glasgow Haskell Compiler / GHC
Commits:
-
7e37b737
by Sebastian Graf at 2026-08-02T19:03:01+02:00
4 changed files:
Changes:
| ... | ... | @@ -35,7 +35,9 @@ import GHC.Cmm.Graph |
| 35 | 35 | import GHC.Cmm.BlockId
|
| 36 | 36 | import GHC.Cmm hiding ( succ )
|
| 37 | 37 | import GHC.Cmm.Info
|
| 38 | -import GHC.Cmm.Utils ( cmmTagMask, mkWordCLit, cmmLoadGCWord )
|
|
| 38 | +import GHC.Cmm.Utils ( cmmTagMask, mkWordCLit )
|
|
| 39 | +import GHC.Cmm.CLabel ( mkCmmCodeLabel )
|
|
| 40 | +import GHC.Unit ( rtsUnitId )
|
|
| 39 | 41 | import GHC.Platform.Tag ( mAX_PTR_TAG )
|
| 40 | 42 | import GHC.Core
|
| 41 | 43 | import GHC.Core.DataCon
|
| ... | ... | @@ -1190,32 +1192,17 @@ emitEnter fun = do |
| 1190 | 1192 | ; adjustHpBackwards
|
| 1191 | 1193 | ; sequel <- getSequel
|
| 1192 | 1194 | ; updfr_off <- getUpdFrameOff
|
| 1193 | - ; align_check <- stgToCmmAlignCheck <$> getStgToCmmConfig
|
|
| 1194 | 1195 | ; case sequel of
|
| 1195 | - -- For a return we test the tag and, if the value is already tagged
|
|
| 1196 | - -- (hence evaluated), return it directly rather than entering it. This
|
|
| 1197 | - -- upholds the invariant that the entry code of a taggable normal form is
|
|
| 1198 | - -- never reached: only an untagged closure is entered. The tag test costs
|
|
| 1199 | - -- a branch on every tail enter, but a tagged value reaching here is the
|
|
| 1200 | - -- common case.
|
|
| 1196 | + -- For a return we evaluate through stg_enter_data, the specialisation
|
|
| 1197 | + -- of stg_ap_0_fast to values whose type rules out functions
|
|
| 1198 | + -- (getCallMethod selects EnterIt only for those): it returns a tagged
|
|
| 1199 | + -- (hence evaluated) value directly and enters an untagged closure, so
|
|
| 1200 | + -- the entry code of a taggable normal form is never reached and the
|
|
| 1201 | + -- tail enter costs a single jump in the generated code.
|
|
| 1201 | 1202 | Return -> do
|
| 1202 | - { fun_tmp <- assignTemp fun
|
|
| 1203 | - ; let funR = CmmReg (CmmLocal fun_tmp)
|
|
| 1204 | - entry = entryCode platform
|
|
| 1205 | - $ closureInfoPtr platform align_check
|
|
| 1206 | - $ CmmReg (nodeReg platform)
|
|
| 1207 | - ret_addr = entryCode platform
|
|
| 1208 | - $ cmmLoadGCWord platform (CmmStackSlot Old updfr_off)
|
|
| 1209 | - ; lenter <- newBlockId
|
|
| 1210 | - ; lret <- newBlockId
|
|
| 1211 | - ; tscope <- getTickScope
|
|
| 1212 | - ; emit $
|
|
| 1213 | - mkCbranch (cmmIsTagged platform funR) lret lenter Nothing <*>
|
|
| 1214 | - outOfLine lenter
|
|
| 1215 | - ( mkJump profile NativeNodeCall entry [cmmUntag platform funR] updfr_off
|
|
| 1216 | - , tscope ) <*>
|
|
| 1217 | - mkLabel lret tscope <*>
|
|
| 1218 | - mkReturn profile ret_addr [funR] updfr_off
|
|
| 1203 | + { let enter_data = CmmLit (CmmLabel
|
|
| 1204 | + (mkCmmCodeLabel rtsUnitId (fsLit "stg_enter_data")))
|
|
| 1205 | + ; emit $ mkJump profile NativeNodeCall enter_data [fun] updfr_off
|
|
| 1219 | 1206 | ; return AssignedDirectly
|
| 1220 | 1207 | }
|
| 1221 | 1208 |
| ... | ... | @@ -54,6 +54,25 @@ import CLOSURE stg_restore_cccs_eval_info; |
| 54 | 54 | * everything being returned is guaranteed evaluated, so it would be a no-op.
|
| 55 | 55 | */
|
| 56 | 56 | |
| 57 | +/* -----------------------------------------------------------------------------
|
|
| 58 | + Evaluate a value whose type rules out functions.
|
|
| 59 | + |
|
| 60 | + A specialisation of stg_ap_0_fast (below) to closures that are a
|
|
| 61 | + thunk, a constructor, or an indirection to one: the FUN/PAP/BCO arms
|
|
| 62 | + of the generic evaluation are unreachable, and an indirection's own
|
|
| 63 | + entry code performs the following, so evaluation reduces to the tag
|
|
| 64 | + test and an entry jump. The code generator jumps here to evaluate in
|
|
| 65 | + tail position; see emitEnter in GHC.StgToCmm.Expr.
|
|
| 66 | + -------------------------------------------------------------------------- */
|
|
| 67 | + |
|
| 68 | +stg_enter_data ( P_ x )
|
|
| 69 | +{
|
|
| 70 | + if (GETTAG(x) != 0) {
|
|
| 71 | + return (x);
|
|
| 72 | + }
|
|
| 73 | + jump %GET_ENTRY(x) (x);
|
|
| 74 | +}
|
|
| 75 | + |
|
| 57 | 76 | STRING(stg_ap_0_ret_str,"stg_ap_0_ret... ")
|
| 58 | 77 | |
| 59 | 78 | stg_ap_0_fast ( P_ fun )
|
| ... | ... | @@ -848,6 +848,7 @@ extern char **environ; |
| 848 | 848 | SymI_HasDataProto(stg_ap_ppppp_info) \
|
| 849 | 849 | SymI_HasDataProto(stg_ap_pppppp_info) \
|
| 850 | 850 | SymI_HasDataProto(stg_ap_0_fast) \
|
| 851 | + SymI_HasDataProto(stg_enter_data) \
|
|
| 851 | 852 | SymI_HasDataProto(stg_ap_v_fast) \
|
| 852 | 853 | SymI_HasDataProto(stg_ap_f_fast) \
|
| 853 | 854 | SymI_HasDataProto(stg_ap_d_fast) \
|
| ... | ... | @@ -302,6 +302,7 @@ RTS_RET(stg_ap_ppppp); |
| 302 | 302 | RTS_RET(stg_ap_pppppp);
|
| 303 | 303 | |
| 304 | 304 | RTS_FUN_DECL(stg_ap_0_fast);
|
| 305 | +RTS_FUN_DECL(stg_enter_data);
|
|
| 305 | 306 | RTS_FUN_DECL(stg_ap_v_fast);
|
| 306 | 307 | RTS_FUN_DECL(stg_ap_f_fast);
|
| 307 | 308 | RTS_FUN_DECL(stg_ap_d_fast);
|