% % (c) The GRASP/AQUA Project, Glasgow University, 1993-1998 % % $Id: CgLetNoEscape.lhs,v 1.26 2004/09/30 10:35:47 simonpj Exp $ % %******************************************************** %* * \section[CgLetNoEscape]{Handling ``let-no-escapes''} %* * %******************************************************** \begin{code} module CgLetNoEscape ( cgLetNoEscapeClosure ) where #include "HsVersions.h" import {-# SOURCE #-} CgExpr ( cgExpr ) import StgSyn import CgMonad import CgBindery ( CgIdInfo, letNoEscapeIdInfo, nukeDeadBindings ) import CgCase ( restoreCurrentCostCentre ) import CgCon ( bindUnboxedTupleComponents ) import CgHeapery ( unbxTupleHeapCheck ) import CgInfoTbls ( emitDirectReturnTarget ) import CgStackery ( allocStackTop, deAllocStackTop, getSpRelOffset ) import Cmm ( CmmStmt(..) ) import CmmUtils ( mkLblExpr, oneStmt ) import CLabel ( mkReturnInfoLabel ) import ClosureInfo ( mkLFLetNoEscape ) import CostCentre ( CostCentreStack ) import Id ( Id, idName ) import Var ( idUnique ) import SMRep ( retAddrSizeW ) import BasicTypes ( RecFlag(..) ) \end{code} %************************************************************************ %* * \subsection[what-is-non-escaping]{What {\em is} a ``non-escaping let''?} %* * %************************************************************************ [The {\em code} that detects these things is elsewhere.] Consider: \begin{verbatim} let x = fvs \ args -> e in if ... then x else if ... then x else ... \end{verbatim} @x@ is used twice (so we probably can't unfold it), but when it is entered, the stack is deeper than it was when the definition of @x@ happened. Specifically, if instead of allocating a closure for @x@, we saved all @x@'s fvs on the stack, and remembered the stack depth at that moment, then whenever we enter @x@ we can simply set the stack pointer(s) to these remembered (compile-time-fixed) values, and jump to the code for @x@. All of this is provided x is: \begin{enumerate} \item non-updatable; \item guaranteed to be entered before the stack retreats -- ie x is not buried in a heap-allocated closure, or passed as an argument to something; \item all the enters have exactly the right number of arguments, no more no less; \item all the enters are tail calls; that is, they return to the caller enclosing the definition of @x@. \end{enumerate} Under these circumstances we say that @x@ is {\em non-escaping}. An example of when (4) does {\em not} hold: \begin{verbatim} let x = ... in case x of ...alts... \end{verbatim} Here, @x@ is certainly entered only when the stack is deeper than when @x@ is defined, but here it must return to \tr{...alts...} So we can't just adjust the stack down to @x@'s recalled points, because that would lost @alts@' context. Things can get a little more complicated. Consider: \begin{verbatim} let y = ... in let x = fvs \ args -> ...y... in ...x... \end{verbatim} Now, if @x@ is used in a non-escaping way in \tr{...x...}, {\em and} @y@ is used in a non-escaping way in \tr{...y...}, {\em then} @y@ is non-escaping. @x@ can even be recursive! Eg: \begin{verbatim} letrec x = [y] \ [v] -> if v then x True else ... in ...(x b)... \end{verbatim} %************************************************************************ %* * \subsection[codeGen-for-non-escaping]{Generating code for a ``non-escaping let''} %* * %************************************************************************ Generating code for this is fun. It is all very very similar to what we do for a case expression. The duality is between \begin{verbatim} let-no-escape x = b in e \end{verbatim} and \begin{verbatim} case e of ... -> b \end{verbatim} That is, the RHS of @x@ (ie @b@) will execute {\em later}, just like the alternative of the case; it needs to be compiled in an environment in which all volatile bindings are forgotten, and the free vars are bound only to stable things like stack locations.. The @e@ part will execute {\em next}, just like the scrutinee of a case. First, we need to save all @x@'s free vars on the stack, if they aren't there already. \begin{code} cgLetNoEscapeClosure :: Id -- binder -> CostCentreStack -- NB: *** NOT USED *** ToDo (WDP 94/06) -> StgBinderInfo -- NB: ditto -> SRT -> StgLiveVars -- variables live in RHS, including the binders -- themselves in the case of a recursive group -> EndOfBlockInfo -- where are we going to? -> Maybe VirtualSpOffset -- Slot for current cost centre -> RecFlag -- is the binding recursive? -> [Id] -- args (as in \ args -> body) -> StgExpr -- body (as in above) -> FCode (Id, CgIdInfo) -- ToDo: deal with the cost-centre issues cgLetNoEscapeClosure bndr cc binder_info srt full_live_in_rhss rhs_eob_info cc_slot rec args body = let arity = length args lf_info = mkLFLetNoEscape arity in -- saveVolatileVarsAndRegs done earlier in cgExpr. do { (vSp, _) <- forkEvalHelp rhs_eob_info (do { allocStackTop retAddrSizeW ; nukeDeadBindings full_live_in_rhss }) (do { deAllocStackTop retAddrSizeW ; abs_c <- forkProc $ cgLetNoEscapeBody bndr cc cc_slot args body -- Ignore the label that comes back from -- mkRetDirectTarget. It must be conjured up elswhere ; emitDirectReturnTarget (idName bndr) abs_c srt ; return () }) ; returnFC (bndr, letNoEscapeIdInfo bndr vSp lf_info) } \end{code} \begin{code} cgLetNoEscapeBody :: Id -- Name of the joint point -> CostCentreStack -> Maybe VirtualSpOffset -> [Id] -- Args -> StgExpr -- Body -> Code cgLetNoEscapeBody bndr cc cc_slot all_args body = do { (arg_regs, ptrs, nptrs, ret_slot) <- bindUnboxedTupleComponents all_args -- restore the saved cost centre. BUT: we must not free the stack slot -- containing the cost centre, because it might be needed for a -- recursive call to this let-no-escape. ; restoreCurrentCostCentre cc_slot False{-don't free-} -- Enter the closures cc, if required ; -- enterCostCentreCode closure_info cc IsFunction -- The "return address" slot doesn't have a return address in it; -- but the heap-check needs it filled in if the heap-check fails. -- So we pass code to fill it in to the heap-check macro ; sp_rel <- getSpRelOffset ret_slot ; let lbl = mkReturnInfoLabel (idUnique bndr) frame_hdr_asst = oneStmt (CmmStore sp_rel (mkLblExpr lbl)) -- Do heap check [ToDo: omit for non-recursive case by recording in -- in envt and absorbing at call site] ; unbxTupleHeapCheck arg_regs ptrs nptrs frame_hdr_asst (cgExpr body) } \end{code}