| ... |
... |
@@ -36,6 +36,8 @@ import GHC.Cmm.BlockId |
|
36
|
36
|
import GHC.Cmm hiding ( succ )
|
|
37
|
37
|
import GHC.Cmm.Info
|
|
38
|
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
|
| ... |
... |
@@ -1183,6 +1185,47 @@ cgIdApp fun_id args = do |
|
1183
|
1185
|
-- DynFlags, then passed to StgToCmmConfig for this phase.
|
|
1184
|
1186
|
|
|
1185
|
1187
|
|
|
|
1188
|
+{- Note [Forcing lifted data]
|
|
|
1189
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
1190
|
+Consider
|
|
|
1191
|
+
|
|
|
1192
|
+ f :: Maybe Bool -> Bool
|
|
|
1193
|
+ f (Just x) = x
|
|
|
1194
|
+
|
|
|
1195
|
+After `f` pattern-matches on the `Just` it must then evaluate `x` and
|
|
|
1196
|
+return it to the caller. It could do so by unconditionally entering the
|
|
|
1197
|
+code in x's closure, but we don't want to do so for two reasons:
|
|
|
1198
|
+
|
|
|
1199
|
+* If the RHS had been `case x of (a,b) -> blah`, GHC generates an inline test
|
|
|
1200
|
+ for EPT; for non-tagged pointers, it then pushes a return address and
|
|
|
1201
|
+ enters it (see the AssignTo case of emitEnter, below). It would be
|
|
|
1202
|
+ consistent to do the same for tail calls too.
|
|
|
1203
|
+
|
|
|
1204
|
+* Finally, if we adopt the design described in #23173, if x is untagged, its
|
|
|
1205
|
+ entry code might warn or panic. In that case we definitely should not to enter
|
|
|
1206
|
+ it.
|
|
|
1207
|
+
|
|
|
1208
|
+Where are these decisions taken? getCallMethod (GHC.StgToCmm.Closure)
|
|
|
1209
|
+classifies the application and cgIdApp dispatches on the result.
|
|
|
1210
|
+Since `Bool` is not a function type and `x` is not statically EPT
|
|
|
1211
|
+(See Note [EPT enforcement]), we land in the EnterIt case under a Return
|
|
|
1212
|
+sequel. In that case, emitEnter emits a jump to the RTS stub stg_enter_data
|
|
|
1213
|
+(rts/Apply.cmm), which tests the tag, returns the pointer if it is set and
|
|
|
1214
|
+enters the closure otherwise.
|
|
|
1215
|
+
|
|
|
1216
|
+Why a stub here, when the AssignTo case inlines it? There the continuation is
|
|
|
1217
|
+a known address that the code can directly jump to. If we were creating a stub
|
|
|
1218
|
+as well, it would become a register-indirect jump that would be difficult to
|
|
|
1219
|
+predict by the CPU.
|
|
|
1220
|
+Under Return, the continuation's address is unknown, so inlining the stub has
|
|
|
1221
|
+no specialisation effect, yet incur more code to generate and place in
|
|
|
1222
|
+potentially tight loops.
|
|
|
1223
|
+That costs 2.6% more compiler allocation on eval-heavy T13960 and about 19 bytes
|
|
|
1224
|
+per site, while nofib stays within +-0.2% runtime either way.
|
|
|
1225
|
+
|
|
|
1226
|
+See #27594.
|
|
|
1227
|
+-}
|
|
|
1228
|
+
|
|
1186
|
1229
|
emitEnter :: CmmExpr -> FCode ReturnKind
|
|
1187
|
1230
|
emitEnter fun = do
|
|
1188
|
1231
|
{ platform <- getPlatform
|
| ... |
... |
@@ -1190,22 +1233,14 @@ emitEnter fun = do |
|
1190
|
1233
|
; adjustHpBackwards
|
|
1191
|
1234
|
; sequel <- getSequel
|
|
1192
|
1235
|
; updfr_off <- getUpdFrameOff
|
|
1193
|
|
- ; align_check <- stgToCmmAlignCheck <$> getStgToCmmConfig
|
|
1194
|
1236
|
; case sequel of
|
|
1195
|
|
- -- For a return, we have the option of generating a tag-test or
|
|
1196
|
|
- -- not. If the value is tagged, we can return directly, which
|
|
1197
|
|
- -- is quicker than entering the value. This is a code
|
|
1198
|
|
- -- size/speed trade-off: when optimising for speed rather than
|
|
1199
|
|
- -- size we could generate the tag test.
|
|
1200
|
|
- --
|
|
1201
|
|
- -- Right now, we do what the old codegen did, and omit the tag
|
|
1202
|
|
- -- test, just generating an enter.
|
|
|
1237
|
+ -- For a return we jump to stg_enter_data, which returns an already
|
|
|
1238
|
+ -- tagged pointer to our caller and enters an untagged one.
|
|
|
1239
|
+ -- See Note [Forcing lifted data]
|
|
1203
|
1240
|
Return -> do
|
|
1204
|
|
- { let entry = entryCode platform
|
|
1205
|
|
- $ closureInfoPtr platform align_check
|
|
1206
|
|
- $ CmmReg (nodeReg platform)
|
|
1207
|
|
- ; emit $ mkJump profile NativeNodeCall entry
|
|
1208
|
|
- [cmmUntag platform fun] updfr_off
|
|
|
1241
|
+ { let enter_data = CmmLit (CmmLabel
|
|
|
1242
|
+ (mkCmmCodeLabel rtsUnitId (fsLit "stg_enter_data")))
|
|
|
1243
|
+ ; emit $ mkJump profile NativeNodeCall enter_data [fun] updfr_off
|
|
1209
|
1244
|
; return AssignedDirectly
|
|
1210
|
1245
|
}
|
|
1211
|
1246
|
|