% % (c) The University of Glasgow 2006 % (c) The GRASP/AQUA Project, Glasgow University, 1993-1998 % %******************************************************** %* * \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
    import CgCase
    import CgCon
    import CgHeapery
    import CgInfoTbls
    import CgStackery
    import Cmm
    import CmmUtils
    import CLabel
    import ClosureInfo
    import CostCentre
    import Id
    import SMRep
    import BasicTypes

\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
    	-> 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 _ full_live_in_rhss
    	rhs_eob_info cc_slot _ 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
    		    ; _ <- emitReturnTarget (idName bndr) abs_c
    		    ; 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_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}