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 )
40 import CostCentre ( CostCentreStack, currentCCS )
46 ------------------------------------------------------------------------
47 -- cgExpr: the main function
48 ------------------------------------------------------------------------
50 cgExpr :: StgExpr -> FCode ()
52 cgExpr (StgApp fun args) = cgIdApp fun args
53 cgExpr (StgOpApp op args ty) = cgOpApp op args ty
54 cgExpr (StgConApp con args) = cgConApp con args
55 cgExpr (StgSCC cc expr) = do { emitSetCCC cc; cgExpr expr }
56 cgExpr (StgTick m n expr) = do { emit (mkTickBox m n); cgExpr expr }
57 cgExpr (StgLit lit) = do cmm_lit <- cgLit lit
58 emitReturn [CmmLit cmm_lit]
60 cgExpr (StgLet binds expr) = do { cgBind binds; cgExpr expr }
61 cgExpr (StgLetNoEscape _ _ binds expr) = do { cgLneBinds binds; cgExpr expr }
63 cgExpr (StgCase expr _live_vars _save_vars bndr srt alt_type alts) =
64 cgCase expr bndr srt alt_type alts
66 cgExpr (StgLam {}) = panic "cgExpr: StgLam"
68 ------------------------------------------------------------------------
70 ------------------------------------------------------------------------
72 {- Generating code for a let-no-escape binding, aka join point is very
73 very similar to what we do for a case expression. The duality is
80 That is, the RHS of 'x' (ie 'b') will execute *later*, just like
81 the alternative of the case; it needs to be compiled in an environment
82 in which all volatile bindings are forgotten, and the free vars are
83 bound only to stable things like stack locations.. The 'e' part will
84 execute *next*, just like the scrutinee of a case. -}
86 -------------------------
87 cgLneBinds :: StgBinding -> FCode ()
88 cgLneBinds (StgNonRec bndr rhs)
89 = do { local_cc <- saveCurrentCostCentre
90 -- See Note [Saving the current cost centre]
91 ; info <- cgLetNoEscapeRhs local_cc bndr rhs
92 ; addBindC (cg_id info) info }
94 cgLneBinds (StgRec pairs)
95 = do { local_cc <- saveCurrentCostCentre
96 ; new_bindings <- fixC (\ new_bindings -> do
97 { addBindsC new_bindings
98 ; listFCs [ cgLetNoEscapeRhs local_cc b e
101 ; addBindsC new_bindings }
104 -------------------------
105 cgLetNoEscapeRhs, cgLetNoEscapeRhsBody
106 :: Maybe LocalReg -- Saved cost centre
111 cgLetNoEscapeRhs local_cc bndr rhs =
112 do { (info, rhs_body) <- getCodeR $ cgLetNoEscapeRhsBody local_cc bndr rhs
113 ; let (bid, _) = expectJust "cgLetNoEscapeRhs" $ maybeLetNoEscape info
114 ; emit (outOfLine $ mkLabel bid emptyStackInfo <*> rhs_body)
118 cgLetNoEscapeRhsBody local_cc bndr (StgRhsClosure cc _bi _ _upd srt args body)
119 = cgLetNoEscapeClosure bndr local_cc cc srt (nonVoidIds args) body
120 cgLetNoEscapeRhsBody local_cc bndr (StgRhsCon cc con args)
121 = cgLetNoEscapeClosure bndr local_cc cc NoSRT [] (StgConApp con args)
122 -- For a constructor RHS we want to generate a single chunk of
123 -- code which can be jumped to from many places, which will
124 -- return the constructor. It's easy; just behave as if it
125 -- was an StgRhsClosure with a ConApp inside!
127 -------------------------
130 -> Maybe LocalReg -- Slot for saved current cost centre
131 -> CostCentreStack -- XXX: *** NOT USED *** why not?
133 -> [NonVoid Id] -- Args (as in \ args -> body)
134 -> StgExpr -- Body (as in above)
137 cgLetNoEscapeClosure bndr cc_slot _unused_cc srt args body
138 = do { arg_regs <- forkProc $ do
139 { restoreCurrentCostCentre cc_slot
140 ; arg_regs <- bindArgsToRegs args
141 ; c_srt <- getSRTInfo srt
142 ; altHeapCheck arg_regs c_srt (cgExpr body)
143 -- Using altHeapCheck just reduces
144 -- instructions to save on stack
146 ; return $ lneIdInfo bndr arg_regs}
149 ------------------------------------------------------------------------
151 ------------------------------------------------------------------------
153 {- Note [Compiling case expressions]
154 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
155 It is quite interesting to decide whether to put a heap-check at the
156 start of each alternative. Of course we certainly have to do so if
157 the case forces an evaluation, or if there is a primitive op which can
160 A more interesting situation is this (a Plan-B situation)
166 default -> !R!; ...R...
168 where !x! indicates a possible heap-check point. The heap checks
169 in the alternatives *can* be omitted, in which case the topmost
170 heapcheck will take their worst case into account.
172 In favour of omitting !Q!, !R!:
174 - *May* save a heap overflow test,
175 if ...P... allocates anything.
177 - We can use relative addressing from a single Hp to
178 get at all the closures so allocated.
180 - No need to save volatile vars etc across heap checks
183 Against omitting !Q!, !R!
185 - May put a heap-check into the inner loop. Suppose
186 the main loop is P -> R -> P -> R...
187 Q is the loop exit, and only it does allocation.
188 This only hurts us if P does no allocation. If P allocates,
189 then there is a heap check in the inner loop anyway.
191 - May do more allocation than reqd. This sometimes bites us
192 badly. For example, nfib (ha!) allocates about 30\% more space if the
193 worst-casing is done, because many many calls to nfib are leaf calls
194 which don't need to allocate anything.
196 We can un-allocate, but that costs an instruction
198 Neither problem hurts us if there is only one alternative.
200 Suppose the inner loop is P->R->P->R etc. Then here is
201 how many heap checks we get in the *inner loop* under various
204 Alooc Heap check in branches (!Q!, !R!)?
205 P Q R yes no (absorb to !P!)
206 --------------------------------------
213 Best choices: absorb heap checks from Q and R into !P! iff
214 a) P itself does some allocation
216 b) P does allocation, or there is exactly one alternative
218 We adopt (b) because that is more likely to put the heap check at the
219 entry to a function, when not many things are live. After a bunch of
220 single-branch cases, we may have lots of things live
222 Hence: two basic plans for
226 ------ Plan A: the general case ---------
228 ...save current cost centre...
231 with sequel (SetLocals r)
233 ...restore current cost centre...
235 ...alts do their own heap checks
237 ------ Plan B: special case when ---------
238 (i) e does not allocate or call GC
239 (ii) either upstream code performs allocation
240 or there is just one alternative
242 Then heap allocation in the (single) case branch
243 is absorbed by the upstream check.
244 Very common example: primops on unboxed values
247 with sequel (SetLocals r)...
255 -------------------------------------
257 = GcInAlts -- Put a GC check at the start the case alternatives,
258 [LocalReg] -- which binds these registers
259 SRT -- using this SRT
260 | NoGcInAlts -- The scrutinee is a primitive value, or a call to a
261 -- primitive op which does no GC. Absorb the allocation
262 -- of the case alternative(s) into the upstream check
264 -------------------------------------
265 cgCase :: StgExpr -> Id -> SRT -> AltType -> [StgAlt] -> FCode ()
266 -- cgCase (OpApp ) bndr srt AlgAlt [(DataAlt flase, a2]
267 -- | isBoolTy (idType bndr)
271 cgCase scrut bndr srt alt_type alts
272 = do { up_hp_usg <- getVirtHp -- Upstream heap usage
273 ; let ret_bndrs = chooseReturnBndrs bndr alt_type alts
274 alt_regs = map idToReg ret_bndrs
275 simple_scrut = isSimpleScrut scrut alt_type
276 gc_plan | not simple_scrut = GcInAlts alt_regs srt
277 | isSingleton alts = NoGcInAlts
278 | up_hp_usg > 0 = NoGcInAlts
279 | otherwise = GcInAlts alt_regs srt
281 ; mb_cc <- maybeSaveCostCentre simple_scrut
282 ; c_srt <- getSRTInfo srt
283 ; withSequel (AssignTo alt_regs c_srt)
285 ; restoreCurrentCostCentre mb_cc
287 ; bindArgsToRegs ret_bndrs
288 ; cgAlts gc_plan (NonVoid bndr) alt_type alts }
291 maybeSaveCostCentre :: Bool -> FCode (Maybe LocalReg)
292 maybeSaveCostCentre simple_scrut
293 | simple_scrut = saveCurrentCostCentre
294 | otherwise = return Nothing
298 isSimpleScrut :: StgExpr -> AltType -> Bool
299 -- Simple scrutinee, does not block or allocate; hence safe to amalgamate
300 -- heap usage from alternatives into the stuff before the case
301 -- NB: if you get this wrong, and claim that the expression doesn't allocate
302 -- when it does, you'll deeply mess up allocation
303 isSimpleScrut (StgOpApp op _ _) _ = isSimpleOp op
304 isSimpleScrut (StgLit _) _ = True -- case 1# of { 0# -> ..; ... }
305 isSimpleScrut (StgApp _ []) (PrimAlt _) = True -- case x# of { 0# -> ..; ... }
306 isSimpleScrut _ _ = False
308 isSimpleOp :: StgOp -> Bool
309 -- True iff the op cannot block or allocate
310 isSimpleOp (StgFCallOp (CCall (CCallSpec _ _ safe)) _) = not (playSafe safe)
311 isSimpleOp (StgFCallOp (DNCall _) _) = False -- Safe!
312 isSimpleOp (StgPrimOp op) = not (primOpOutOfLine op)
315 chooseReturnBndrs :: Id -> AltType -> [StgAlt] -> [NonVoid Id]
316 -- These are the binders of a case that are assigned
317 -- by the evaluation of the scrutinee
318 -- Only non-void ones come back
319 chooseReturnBndrs bndr (PrimAlt _) _alts
322 chooseReturnBndrs _bndr (UbxTupAlt _) [(_, ids, _, _)]
323 = nonVoidIds ids -- 'bndr' is not assigned!
325 chooseReturnBndrs bndr (AlgAlt _) _alts
326 = nonVoidIds [bndr] -- Only 'bndr' is assigned
328 chooseReturnBndrs bndr PolyAlt _alts
329 = nonVoidIds [bndr] -- Only 'bndr' is assigned
331 chooseReturnBndrs _ _ _ = panic "chooseReturnBndrs"
332 -- UbxTupALt has only one alternative
334 -------------------------------------
335 cgAlts :: GcPlan -> NonVoid Id -> AltType -> [StgAlt] -> FCode ()
336 -- At this point the result of the case are in the binders
337 cgAlts gc_plan _bndr PolyAlt [(_, _, _, rhs)]
338 = maybeAltHeapCheck gc_plan (cgExpr rhs)
340 cgAlts gc_plan _bndr (UbxTupAlt _) [(_, _, _, rhs)]
341 = maybeAltHeapCheck gc_plan (cgExpr rhs)
342 -- Here bndrs are *already* in scope, so don't rebind them
344 cgAlts gc_plan bndr (PrimAlt _) alts
345 = do { tagged_cmms <- cgAltRhss gc_plan bndr alts
347 ; let bndr_reg = CmmLocal (idToReg bndr)
348 (DEFAULT,deflt) = head tagged_cmms
349 -- PrimAlts always have a DEFAULT case
350 -- and it always comes first
352 tagged_cmms' = [(lit,code)
353 | (LitAlt lit, code) <- tagged_cmms]
354 ; emit (mkCmmLitSwitch (CmmReg bndr_reg) tagged_cmms' deflt) }
356 cgAlts gc_plan bndr (AlgAlt tycon) alts
357 = do { tagged_cmms <- cgAltRhss gc_plan bndr alts
359 ; let fam_sz = tyConFamilySize tycon
360 bndr_reg = CmmLocal (idToReg bndr)
361 mb_deflt = case tagged_cmms of
362 ((DEFAULT,rhs) : _) -> Just rhs
364 -- DEFAULT is always first, if present
366 branches = [ (dataConTagZ con, cmm)
367 | (DataAlt con, cmm) <- tagged_cmms ]
369 -- Is the constructor tag in the node reg?
370 ; if isSmallFamily fam_sz
371 then let -- Yes, bndr_reg has constr. tag in ls bits
372 tag_expr = cmmConstrTag1 (CmmReg bndr_reg)
373 branches' = [(tag+1,branch) | (tag,branch) <- branches]
375 emitSwitch tag_expr branches' mb_deflt 1 fam_sz
377 else -- No, get tag from info table
378 let -- Note that ptr _always_ has tag 1
379 -- when the family size is big enough
380 untagged_ptr = cmmRegOffB bndr_reg (-1)
381 tag_expr = getConstrTag (untagged_ptr)
383 emitSwitch tag_expr branches mb_deflt 0 (fam_sz - 1) }
385 cgAlts _ _ _ _ = panic "cgAlts"
386 -- UbxTupAlt and PolyAlt have only one alternative
389 cgAltRhss :: GcPlan -> NonVoid Id -> [StgAlt] -> FCode [(AltCon, CmmAGraph)]
390 cgAltRhss gc_plan bndr alts
391 = forkAlts (map cg_alt alts)
393 base_reg = idToReg bndr
394 cg_alt :: StgAlt -> FCode (AltCon, CmmAGraph)
395 cg_alt (con, bndrs, _uses, rhs)
397 maybeAltHeapCheck gc_plan $
398 do { pprTrace "binding args for" (ppr bndr <+> ppr con) $ bindConArgs con base_reg bndrs
402 maybeAltHeapCheck :: GcPlan -> FCode a -> FCode a
403 maybeAltHeapCheck NoGcInAlts code
405 maybeAltHeapCheck (GcInAlts regs srt) code
406 = do { c_srt <- getSRTInfo srt
407 ; altHeapCheck regs c_srt code }
409 -----------------------------------------------------------------------------
411 -----------------------------------------------------------------------------
413 cgConApp :: DataCon -> [StgArg] -> FCode ()
414 cgConApp con stg_args
415 | isUnboxedTupleCon con -- Unboxed tuple: assign and return
416 = do { arg_exprs <- getNonVoidArgAmodes stg_args
417 ; tickyUnboxedTupleReturn (length arg_exprs)
418 ; emitReturn arg_exprs }
420 | otherwise -- Boxed constructors; allocate and return
421 = ASSERT( stg_args `lengthIs` dataConRepArity con )
422 do { (idinfo, init) <- buildDynCon (dataConWorkId con) currentCCS con stg_args
423 -- The first "con" says that the name bound to this closure is
424 -- is "con", which is a bit of a fudge, but it only affects profiling
427 ; emitReturn [idInfoToAmode idinfo] }
430 cgIdApp :: Id -> [StgArg] -> FCode ()
431 cgIdApp fun_id [] | isVoidId fun_id = emitReturn []
433 = do { fun_info <- getCgIdInfo fun_id
434 ; case maybeLetNoEscape fun_info of
435 Just (blk_id, lne_regs) -> cgLneJump blk_id lne_regs args
436 Nothing -> cgTailCall fun_id fun_info args }
438 cgLneJump :: BlockId -> [LocalReg] -> [StgArg] -> FCode ()
439 cgLneJump blk_id lne_regs args -- Join point; discard sequel
440 = do { cmm_args <- getNonVoidArgAmodes args
441 ; emit (mkMultiAssign lne_regs cmm_args
442 <*> mkBranch blk_id) }
444 cgTailCall :: Id -> CgIdInfo -> [StgArg] -> FCode ()
445 cgTailCall fun_id fun_info args
446 = case (getCallMethod fun_name (idCafInfo fun_id) lf_info (length args)) of
448 -- A value in WHNF, so we can just return it.
449 ReturnIt -> emitReturn [fun] -- ToDo: does ReturnIt guarantee tagged?
451 EnterIt -> ASSERT( null args ) -- Discarding arguments
452 do { let fun' = CmmLoad fun (cmmExprType fun)
453 ; [ret,call] <- forkAlts [
454 getCode $ emitReturn [fun], -- Is tagged; no need to untag
455 getCode $ do emit (mkAssign nodeReg fun)
456 emitCall Native (entryCode fun') []] -- Not tagged
457 ; emit (mkCmmIfThenElse (cmmIsTagged fun) ret call) }
459 SlowCall -> do -- A slow function call via the RTS apply routines
460 { tickySlowCall lf_info args
461 ; emit $ mkComment $ mkFastString "slowCall"
462 ; slowCall fun args }
464 -- A direct function call (possibly with some left-over arguments)
465 DirectEntry lbl arity -> do
466 { tickyDirectCall arity args
467 ; if node_points then
468 do emit $ mkComment $ mkFastString "directEntry"
469 emit (mkAssign nodeReg fun)
470 directCall lbl arity args
471 -- directCall lbl (arity+1) (StgVarArg fun_id : args))
472 -- >>= (emit . (mkComment (mkFastString "DirectEntry") <*>))
473 else do emit $ mkComment $ mkFastString "directEntry else"
474 directCall lbl arity args }
476 JumpToIt {} -> panic "cgTailCall" -- ???
479 fun_name = idName fun_id
480 fun = idInfoToAmode fun_info
481 lf_info = cgIdInfoLF fun_info
482 node_points = nodeMustPointToIt lf_info