1 -----------------------------------------------------------------------------
3 -- Stg to C-- code generation: expressions
5 -- (c) The University of Glasgow 2004-2006
7 -----------------------------------------------------------------------------
9 module StgCmmExpr ( cgExpr ) where
11 #define FAST_STRING_NOT_NEEDED
12 #include "HsVersions.h"
14 import {-# SOURCE #-} StgCmmBind ( cgBind )
38 import CostCentre ( CostCentreStack, currentCCS )
44 ------------------------------------------------------------------------
45 -- cgExpr: the main function
46 ------------------------------------------------------------------------
48 cgExpr :: StgExpr -> FCode ()
50 cgExpr (StgApp fun args) = cgIdApp fun args
51 cgExpr (StgOpApp op args ty) = cgOpApp op args ty
52 cgExpr (StgConApp con args) = cgConApp con args
54 cgExpr (StgSCC cc expr) = do { emitSetCCC cc; cgExpr expr }
55 cgExpr (StgTick m n expr) = do { emit (mkTickBox m n); cgExpr expr }
56 cgExpr (StgLit lit) = emitReturn [CmmLit (mkSimpleLit lit)]
58 cgExpr (StgLet binds expr) = do { emit (mkComment $ mkFastString "calling cgBind"); cgBind binds; emit (mkComment $ mkFastString "calling cgExpr"); cgExpr expr }
59 cgExpr (StgLetNoEscape _ _ binds expr) = do { cgLneBinds binds; cgExpr expr }
61 cgExpr (StgCase expr _live_vars _save_vars bndr srt alt_type alts)
62 = cgCase expr bndr srt alt_type alts
64 cgExpr (StgLam {}) = panic "cgExpr: StgLam"
66 ------------------------------------------------------------------------
68 ------------------------------------------------------------------------
70 {- Generating code for a let-no-escape binding, aka join point is very
71 very similar to whatwe do for a case expression. The duality is
78 That is, the RHS of 'x' (ie 'b') will execute *later*, just like
79 the alternative of the case; it needs to be compiled in an environment
80 in which all volatile bindings are forgotten, and the free vars are
81 bound only to stable things like stack locations.. The 'e' part will
82 execute *next*, just like the scrutinee of a case. -}
84 -------------------------
85 cgLneBinds :: StgBinding -> FCode ()
86 cgLneBinds (StgNonRec bndr rhs)
87 = do { local_cc <- saveCurrentCostCentre
88 -- See Note [Saving the current cost centre]
89 ; (bndr,info) <- cgLetNoEscapeRhs local_cc bndr rhs
90 ; addBindC bndr info }
92 cgLneBinds (StgRec pairs)
93 = do { local_cc <- saveCurrentCostCentre
94 ; new_bindings <- fixC (\ new_bindings -> do
95 { addBindsC new_bindings
96 ; listFCs [ cgLetNoEscapeRhs local_cc b e
99 ; addBindsC new_bindings }
101 -------------------------
103 :: Maybe LocalReg -- Saved cost centre
106 -> FCode (Id, CgIdInfo)
108 cgLetNoEscapeRhs local_cc bndr (StgRhsClosure cc _bi _ _upd srt args body)
109 = cgLetNoEscapeClosure bndr local_cc cc srt args body
110 cgLetNoEscapeRhs local_cc bndr (StgRhsCon cc con args)
111 = cgLetNoEscapeClosure bndr local_cc cc NoSRT [] (StgConApp con args)
112 -- For a constructor RHS we want to generate a single chunk of
113 -- code which can be jumped to from many places, which will
114 -- return the constructor. It's easy; just behave as if it
115 -- was an StgRhsClosure with a ConApp inside!
117 -------------------------
120 -> Maybe LocalReg -- Slot for saved current cost centre
121 -> CostCentreStack -- XXX: *** NOT USED *** why not?
123 -> [Id] -- Args (as in \ args -> body)
124 -> StgExpr -- Body (as in above)
125 -> FCode (Id, CgIdInfo)
127 cgLetNoEscapeClosure bndr cc_slot _unused_cc srt args body
128 = do { arg_regs <- forkProc $ do
129 { restoreCurrentCostCentre cc_slot
130 ; arg_regs <- bindArgsToRegs args
131 ; c_srt <- getSRTInfo srt
132 ; altHeapCheck arg_regs c_srt (cgExpr body)
133 -- Using altHeapCheck just reduces
134 -- instructions to save on stack
136 ; return (bndr, lneIdInfo bndr arg_regs) }
139 ------------------------------------------------------------------------
141 ------------------------------------------------------------------------
143 {- Note [Compiling case expressions]
144 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
145 It is quite interesting to decide whether to put a heap-check at the
146 start of each alternative. Of course we certainly have to do so if
147 the case forces an evaluation, or if there is a primitive op which can
150 A more interesting situation is this (a Plan-B situation)
156 default -> !R!; ...R...
158 where !x! indicates a possible heap-check point. The heap checks
159 in the alternatives *can* be omitted, in which case the topmost
160 heapcheck will take their worst case into account.
162 In favour of omitting !Q!, !R!:
164 - *May* save a heap overflow test,
165 if ...P... allocates anything.
167 - We can use relative addressing from a single Hp to
168 get at all the closures so allocated.
170 - No need to save volatile vars etc across heap checks
173 Against omitting !Q!, !R!
175 - May put a heap-check into the inner loop. Suppose
176 the main loop is P -> R -> P -> R...
177 Q is the loop exit, and only it does allocation.
178 This only hurts us if P does no allocation. If P allocates,
179 then there is a heap check in the inner loop anyway.
181 - May do more allocation than reqd. This sometimes bites us
182 badly. For example, nfib (ha!) allocates about 30\% more space if the
183 worst-casing is done, because many many calls to nfib are leaf calls
184 which don't need to allocate anything.
186 We can un-allocate, but that costs an instruction
188 Neither problem hurts us if there is only one alternative.
190 Suppose the inner loop is P->R->P->R etc. Then here is
191 how many heap checks we get in the *inner loop* under various
194 Alooc Heap check in branches (!Q!, !R!)?
195 P Q R yes no (absorb to !P!)
196 --------------------------------------
203 Best choices: absorb heap checks from Q and R into !P! iff
204 a) P itself does some allocation
206 b) P does allocation, or there is exactly one alternative
208 We adopt (b) because that is more likely to put the heap check at the
209 entry to a function, when not many things are live. After a bunch of
210 single-branch cases, we may have lots of things live
212 Hence: two basic plans for
216 ------ Plan A: the general case ---------
218 ...save current cost centre...
221 with sequel (SetLocals r)
223 ...restore current cost centre...
225 ...alts do their own heap checks
227 ------ Plan B: special case when ---------
228 (i) e does not allocate or call GC
229 (ii) either upstream code performs allocation
230 or there is just one alternative
232 Then heap allocation in the (single) case branch
233 is absorbed by the upstream check.
234 Very common example: primops on unboxed values
237 with sequel (SetLocals r)...
245 -------------------------------------
247 = GcInAlts -- Put a GC check at the start the case alternatives,
248 [LocalReg] -- which binds these registers
249 SRT -- using this SRT
250 | NoGcInAlts -- The scrutinee is a primitive value, or a call to a
251 -- primitive op which does no GC. Absorb the allocation
252 -- of the case alternative(s) into the upstream check
254 -------------------------------------
255 cgCase :: StgExpr -> Id -> SRT -> AltType -> [StgAlt] -> FCode ()
256 cgCase scrut bndr srt alt_type alts
257 = do { up_hp_usg <- getVirtHp -- Upstream heap usage
258 ; let ret_bndrs = chooseReturnBndrs bndr alt_type alts
259 alt_regs = map idToReg ret_bndrs
260 simple_scrut = isSimpleScrut scrut alt_type
261 gc_plan | not simple_scrut = GcInAlts alt_regs srt
262 | isSingleton alts = NoGcInAlts
263 | up_hp_usg > 0 = NoGcInAlts
264 | otherwise = GcInAlts alt_regs srt
266 ; mb_cc <- maybeSaveCostCentre simple_scrut
267 ; c_srt <- getSRTInfo srt
268 ; withSequel (AssignTo alt_regs c_srt)
270 ; restoreCurrentCostCentre mb_cc
272 ; bindArgsToRegs ret_bndrs
273 ; cgAlts gc_plan bndr alt_type alts }
276 maybeSaveCostCentre :: Bool -> FCode (Maybe LocalReg)
277 maybeSaveCostCentre simple_scrut
278 | simple_scrut = saveCurrentCostCentre
279 | otherwise = return Nothing
284 isSimpleScrut :: StgExpr -> AltType -> Bool
285 -- Simple scrutinee, does not allocate
286 isSimpleScrut (StgOpApp _ _ _) _ = True
287 isSimpleScrut (StgLit _) _ = True
288 isSimpleScrut (StgApp _ []) (PrimAlt _) = True
289 isSimpleScrut _ _ = False
292 chooseReturnBndrs :: Id -> AltType -> [StgAlt] -> [Id]
293 -- These are the binders of a case that are assigned
294 -- by the evaluation of the scrutinee
295 -- Only non-void ones come back
296 chooseReturnBndrs bndr (PrimAlt _) _alts
299 chooseReturnBndrs _bndr (UbxTupAlt _) [(_, ids, _, _)]
300 = nonVoidIds ids -- 'bndr' is not assigned!
302 chooseReturnBndrs bndr (AlgAlt _) _alts
303 = [bndr] -- Only 'bndr' is assigned
305 chooseReturnBndrs bndr PolyAlt _alts
306 = [bndr] -- Only 'bndr' is assigned
308 chooseReturnBndrs _ _ _ = panic "chooseReturnBndrs"
309 -- UbxTupALt has only one alternative
311 nonVoidIds :: [Id] -> [Id]
312 nonVoidIds ids = [id | id <- ids, not (isVoidRep (idPrimRep id))]
314 -------------------------------------
315 cgAlts :: GcPlan -> Id -> AltType -> [StgAlt] -> FCode ()
316 -- At this point the result of the case are in the binders
317 cgAlts gc_plan _bndr PolyAlt [(_, _, _, rhs)]
318 = maybeAltHeapCheck gc_plan (cgExpr rhs)
320 cgAlts gc_plan _bndr (UbxTupAlt _) [(_, _, _, rhs)]
321 = maybeAltHeapCheck gc_plan (cgExpr rhs)
322 -- Here bndrs are *already* in scope, so don't rebind them
324 cgAlts gc_plan bndr (PrimAlt _) alts
325 = do { tagged_cmms <- cgAltRhss gc_plan bndr alts
327 ; let bndr_reg = CmmLocal (idToReg bndr)
328 (DEFAULT,deflt) = head tagged_cmms
329 -- PrimAlts always have a DEFAULT case
330 -- and it always comes first
332 tagged_cmms' = [(lit,code)
333 | (LitAlt lit, code) <- tagged_cmms]
334 ; emit (mkCmmLitSwitch (CmmReg bndr_reg) tagged_cmms' deflt) }
336 cgAlts gc_plan bndr (AlgAlt tycon) alts
337 = do { tagged_cmms <- cgAltRhss gc_plan bndr alts
339 ; let fam_sz = tyConFamilySize tycon
340 bndr_reg = CmmLocal (idToReg bndr)
341 mb_deflt = case tagged_cmms of
342 ((DEFAULT,rhs) : _) -> Just rhs
344 -- DEFAULT is always first, if present
346 branches = [ (dataConTagZ con, cmm)
347 | (DataAlt con, cmm) <- tagged_cmms ]
349 -- Is the constructor tag in the node reg?
350 ; if isSmallFamily fam_sz
351 then let -- Yes, bndr_reg has constr. tag in ls bits
352 tag_expr = cmmConstrTag1 (CmmReg bndr_reg)
353 branches' = [(tag+1,branch) | (tag,branch) <- branches]
355 emitSwitch tag_expr branches' mb_deflt 1 fam_sz
357 else -- No, get tag from info table
358 let -- Note that ptr _always_ has tag 1
359 -- when the family size is big enough
360 untagged_ptr = cmmRegOffB bndr_reg (-1)
361 tag_expr = getConstrTag (untagged_ptr)
363 emitSwitch tag_expr branches mb_deflt 0 (fam_sz - 1) }
365 cgAlts _ _ _ _ = panic "cgAlts"
366 -- UbxTupAlt and PolyAlt have only one alternative
369 cgAltRhss :: GcPlan -> Id -> [StgAlt] -> FCode [(AltCon, CmmAGraph)]
370 cgAltRhss gc_plan bndr alts
371 = forkAlts (map cg_alt alts)
373 base_reg = idToReg bndr
374 cg_alt :: StgAlt -> FCode (AltCon, CmmAGraph)
375 cg_alt (con, bndrs, _uses, rhs)
377 maybeAltHeapCheck gc_plan $
378 do { bindConArgs con base_reg bndrs
382 maybeAltHeapCheck :: GcPlan -> FCode a -> FCode a
383 maybeAltHeapCheck NoGcInAlts code
385 maybeAltHeapCheck (GcInAlts regs srt) code
386 = do { c_srt <- getSRTInfo srt
387 ; altHeapCheck regs c_srt code }
389 -----------------------------------------------------------------------------
391 -----------------------------------------------------------------------------
393 cgConApp :: DataCon -> [StgArg] -> FCode ()
394 cgConApp con stg_args
395 = ASSERT( stg_args `lengthIs` dataConRepArity con )
396 do { idinfo <- buildDynCon (dataConWorkId con) currentCCS con stg_args
397 -- The first "con" says that the name bound to this closure is
398 -- is "con", which is a bit of a fudge, but it only affects profiling
400 ; emitReturn [idInfoToAmode idinfo] }
402 cgIdApp :: Id -> [StgArg] -> FCode ()
404 = do { fun_info <- getCgIdInfo fun_id
405 ; case maybeLetNoEscape fun_info of
406 Just (blk_id, lne_regs) -> cgLneJump blk_id lne_regs args
407 Nothing -> cgTailCall fun_id fun_info args }
409 cgLneJump :: BlockId -> [LocalReg] -> [StgArg] -> FCode ()
410 cgLneJump blk_id lne_regs args -- Join point; discard sequel
411 = do { cmm_args <- getNonVoidArgAmodes args
412 ; emit (mkMultiAssign lne_regs cmm_args
413 <*> mkBranch blk_id) }
415 cgTailCall :: Id -> CgIdInfo -> [StgArg] -> FCode ()
416 cgTailCall fun_id fun_info args
417 = case (getCallMethod fun_name (idCafInfo fun_id) lf_info (length args)) of
419 -- A value in WHNF, so we can just return it.
420 ReturnIt -> emitReturn [fun] -- ToDo: does ReturnIt guarantee tagged?
422 EnterIt -> ASSERT( null args ) -- Discarding arguments
423 do { [ret,call] <- forkAlts [
424 getCode $ emitReturn [fun], -- Is tagged; no need to untag
425 getCode $ emitCall (entryCode fun) [fun]] -- Not tagged
426 ; emit (mkCmmIfThenElse (cmmIsTagged fun) ret call) }
428 SlowCall -> do -- A slow function call via the RTS apply routines
429 { tickySlowCall lf_info args
430 ; slowCall fun args }
432 -- A direct function call (possibly with some left-over arguments)
433 DirectEntry lbl arity -> do
434 { tickyDirectCall arity args
435 ; if node_points then
436 do call <- getCode $ directCall lbl arity args
437 emit (mkAssign nodeReg fun <*> call)
438 -- directCall lbl (arity+1) (StgVarArg fun_id : args))
439 -- >>= (emit . (mkComment (mkFastString "DirectEntry") <*>))
440 else directCall lbl arity args }
442 JumpToIt {} -> panic "cgTailCall" -- ???
445 fun_name = idName fun_id
446 fun = idInfoToAmode fun_info
447 lf_info = cgIdInfoLF fun_info
448 node_points = nodeMustPointToIt lf_info