%
% (c) The University of Glasgow 2006
% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
%
\section[CgClosure]{Code generation for closures}
This module provides the support code for @StgToAbstractC@ to deal
with {\em closures} on the RHSs of let(rec)s. See also
@CgCon@, which deals with constructors.
\begin{code}
module CgClosure ( cgTopRhsClosure,
cgStdRhsClosure,
cgRhsClosure,
emitBlackHoleCode,
) where
#include "HsVersions.h"
import CgExpr ( cgExpr )
import CgMonad
import CgBindery
import CgHeapery
import CgStackery
import CgProf
import CgTicky
import CgParallel
import CgInfoTbls
import CgCallConv
import CgUtils
import ClosureInfo
import SMRep
import OldCmm
import OldCmmUtils
import CLabel
import StgSyn
import CostCentre
import Id
import Name
import Module
import ListSetOps
import Util
import BasicTypes
import StaticFlags
import DynFlags
import Outputable
import FastString
import Data.List
\end{code}
%********************************************************
%* *
\subsection[closures-no-free-vars]{Top-level closures}
%* *
%********************************************************
For closures bound at top level, allocate in static space.
They should have no free variables.
\begin{code}
cgTopRhsClosure :: Id
-> CostCentreStack
-> StgBinderInfo
-> UpdateFlag
-> [Id]
-> StgExpr
-> FCode (Id, CgIdInfo)
cgTopRhsClosure id ccs binder_info upd_flag args body = do
{
let name = idName id
; lf_info <- mkClosureLFInfo id TopLevel [] upd_flag args
; srt_info <- getSRTInfo
; mod_name <- getModuleName
; dflags <- getDynFlags
; let descr = closureDescription dflags mod_name name
closure_info = mkClosureInfo True id lf_info 0 0 srt_info descr
closure_label = mkLocalClosureLabel name $ idCafInfo id
cg_id_info = stableIdInfo id (mkLblExpr closure_label) lf_info
closure_rep = mkStaticClosureFields closure_info ccs True []
; emitDataLits closure_label closure_rep
; forkClosureBody (closureCodeBody binder_info closure_info
ccs args body)
; returnFC (id, cg_id_info) }
\end{code}
%********************************************************
%* *
\subsection[non-top-level-closures]{Non top-level closures}
%* *
%********************************************************
For closures with free vars, allocate in heap.
\begin{code}
cgStdRhsClosure
:: Id
-> CostCentreStack
-> StgBinderInfo
-> [Id]
-> [Id]
-> StgExpr
-> LambdaFormInfo
-> [StgArg]
-> FCode (Id, CgIdInfo)
cgStdRhsClosure bndr _cc _bndr_info _fvs _args _body lf_info payload
= do
{
amodes <- getArgAmodes payload
; mod_name <- getModuleName
; dflags <- getDynFlags
; let (tot_wds, ptr_wds, amodes_w_offsets)
= mkVirtHeapOffsets (isLFThunk lf_info) amodes
descr = closureDescription dflags mod_name (idName bndr)
closure_info = mkClosureInfo False
bndr lf_info tot_wds ptr_wds
NoC_SRT
descr
; heap_offset <- allocDynClosure closure_info curCCS curCCS amodes_w_offsets
; returnFC (bndr, heapIdInfo bndr heap_offset lf_info) }
\end{code}
Here's the general case.
\begin{code}
cgRhsClosure :: Id
-> CostCentreStack
-> StgBinderInfo
-> [Id]
-> UpdateFlag
-> [Id]
-> StgExpr
-> FCode (Id, CgIdInfo)
cgRhsClosure bndr cc bndr_info fvs upd_flag args body = do
{
let
name = idName bndr
bndr_is_a_fv = bndr `elem` fvs
reduced_fvs | bndr_is_a_fv = fvs `minusList` [bndr]
| otherwise = fvs
; lf_info <- mkClosureLFInfo bndr NotTopLevel fvs upd_flag args
; fv_infos <- mapFCs getCgIdInfo reduced_fvs
; srt_info <- getSRTInfo
; mod_name <- getModuleName
; dflags <- getDynFlags
; let bind_details :: [(CgIdInfo, VirtualHpOffset)]
(tot_wds, ptr_wds, bind_details)
= mkVirtHeapOffsets (isLFThunk lf_info) (map add_rep fv_infos)
add_rep info = (cgIdInfoArgRep info, info)
descr = closureDescription dflags mod_name name
closure_info = mkClosureInfo False
bndr lf_info tot_wds ptr_wds
srt_info descr
; forkClosureBody (do
{
let
mbtag = tagForArity (length args)
bind_fv (info, offset)
| Just tag <- mbtag
= bindNewToUntagNode (cgIdInfoId info) offset (cgIdInfoLF info) tag
| otherwise
= bindNewToNode (cgIdInfoId info) offset (cgIdInfoLF info)
; mapCs bind_fv bind_details
; whenC bndr_is_a_fv (bindNewToReg bndr nodeReg lf_info)
; closureCodeBody bndr_info closure_info cc args body })
; let
to_amode (info, offset) = do { amode <- idInfoToAmode info
; return (amode, offset) }
; amodes_w_offsets <- mapFCs to_amode bind_details
; heap_offset <- allocDynClosure closure_info curCCS curCCS amodes_w_offsets
; returnFC (bndr, heapIdInfo bndr heap_offset lf_info) }
mkClosureLFInfo :: Id
-> TopLevelFlag
-> [Id]
-> UpdateFlag
-> [Id]
-> FCode LambdaFormInfo
mkClosureLFInfo bndr top fvs upd_flag args
| null args = return (mkLFThunk (idType bndr) top fvs upd_flag)
| otherwise = do { arg_descr <- mkArgDescr (idName bndr) args
; return (mkLFReEntrant top fvs args arg_descr) }
\end{code}
%************************************************************************
%* *
\subsection[code-for-closures]{The code for closures}
%* *
%************************************************************************
\begin{code}
closureCodeBody :: StgBinderInfo
-> ClosureInfo
-> CostCentreStack
-> [Id]
-> StgExpr
-> Code
\end{code}
There are two main cases for the code for closures. If there are {\em
no arguments}, then the closure is a thunk, and not in normal form.
So it should set up an update frame (if it is shared).
NB: Thunks cannot have a primitive type!
\begin{code}
closureCodeBody _binder_info cl_info _cc [] body = do
{ body_absC <- getCgStmts $ do
{ tickyEnterThunk cl_info
; ldvEnterClosure cl_info
; thunkWrapper cl_info $ do
{ enterCostCentreThunk (CmmReg nodeReg)
; cgExpr body }
}
; emitClosureCodeAndInfoTable cl_info [] body_absC }
\end{code}
If there is /at least one argument/, then this closure is in
normal form, so there is no need to set up an update frame.
The Macros for GrAnSim are produced at the beginning of the
argSatisfactionCheck (by calling fetchAndReschedule). There info if
Node points to closure is available. -- HWL
\begin{code}
closureCodeBody _binder_info cl_info cc args body
= ASSERT( length args > 0 )
do {
vSp <- getVirtSp
; let (reg_args, other_args) = assignCallRegs (addIdReps args)
(sp_top, stk_args) = mkVirtStkOffsets vSp other_args
; let ticky_ctr_lbl = mkRednCountsLabel (closureName cl_info) (clHasCafRefs cl_info)
; emitTickyCounter cl_info args sp_top
; setTickyCtrLabel ticky_ctr_lbl $ do
{ reg_save_code <- mkSlowEntryCode cl_info reg_args
; blks <- forkProc $
mkFunEntryCode cl_info cc reg_args stk_args
sp_top reg_save_code body
; emitClosureCodeAndInfoTable cl_info [] blks
}}
mkFunEntryCode :: ClosureInfo
-> CostCentreStack
-> [(Id,GlobalReg)]
-> [(Id,VirtualSpOffset)]
-> VirtualSpOffset
-> CmmStmts
-> StgExpr
-> Code
mkFunEntryCode cl_info cc reg_args stk_args sp_top reg_save_code body = do
{
; bindArgsToRegs reg_args
; bindArgsToStack stk_args
; setRealAndVirtualSp sp_top
; funWrapper cl_info reg_args reg_save_code $ do
{ tickyEnterFun cl_info
; enterCostCentreFun cc
(CmmMachOp mo_wordSub [ CmmReg nodeReg
, CmmLit (mkIntCLit (funTag cl_info)) ])
(node : map snd reg_args)
; cgExpr body }
}
\end{code}
The "slow entry" code for a function. This entry point takes its
arguments on the stack. It loads the arguments into registers
according to the calling convention, and jumps to the function's
normal entry point. The function's closure is assumed to be in
R1/node.
The slow entry point is used in two places:
(a) unknown calls: eg. stg_PAP_entry
(b) returning from a heap-check failure
\begin{code}
mkSlowEntryCode :: ClosureInfo -> [(Id,GlobalReg)] -> FCode CmmStmts
mkSlowEntryCode cl_info reg_args
| Just (_, ArgGen _) <- closureFunInfo cl_info
= do { emitSimpleProc slow_lbl (emitStmts load_stmts)
; return save_stmts }
| otherwise = return noStmts
where
name = closureName cl_info
has_caf_refs = clHasCafRefs cl_info
slow_lbl = mkSlowEntryLabel name has_caf_refs
load_stmts = mkStmts load_assts `plusStmts` mkStmts [stk_adj_pop, jump_to_entry]
save_stmts = oneStmt stk_adj_push `plusStmts` mkStmts save_assts
reps_w_regs :: [(CgRep,GlobalReg)]
reps_w_regs = [(idCgRep id, reg) | (id,reg) <- reverse reg_args]
(final_stk_offset, stk_offsets)
= mapAccumL (\off (rep,_) -> (off + cgRepSizeW rep, off))
0 reps_w_regs
load_assts = zipWithEqual "mk_load" mk_load reps_w_regs stk_offsets
mk_load (rep,reg) offset = CmmAssign (CmmGlobal reg)
(CmmLoad (cmmRegOffW spReg offset)
(argMachRep rep))
save_assts = zipWithEqual "mk_save" mk_save reps_w_regs stk_offsets
mk_save (rep,reg) offset = ASSERT( argMachRep rep `cmmEqType` globalRegType reg )
CmmStore (cmmRegOffW spReg offset)
(CmmReg (CmmGlobal reg))
stk_adj_pop = CmmAssign spReg (cmmRegOffW spReg final_stk_offset)
stk_adj_push = CmmAssign spReg (cmmRegOffW spReg ( final_stk_offset))
live_regs = Just $ map snd reps_w_regs
jump_to_entry = CmmJump (mkLblExpr (entryLabelFromCI cl_info)) live_regs
\end{code}
%************************************************************************
%* *
\subsubsection[closure-code-wrappers]{Wrappers around closure code}
%* *
%************************************************************************
\begin{code}
thunkWrapper:: ClosureInfo -> Code -> Code
thunkWrapper closure_info thunk_code = do
{ let node_points = nodeMustPointToIt (closureLFInfo closure_info)
; if node_points
then granFetchAndReschedule [] node_points
else granYield [] node_points
; thunkEntryChecks closure_info $ do
{
; whenC (blackHoleOnEntry closure_info && node_points)
(blackHoleIt closure_info)
; setupUpdate closure_info thunk_code }
}
funWrapper :: ClosureInfo
-> [(Id,GlobalReg)]
-> CmmStmts
-> Code
-> Code
funWrapper closure_info arg_regs reg_save_code fun_body = do
{ let node_points = nodeMustPointToIt (closureLFInfo closure_info)
live = Just $ map snd arg_regs
; whenC node_points (ldvEnterClosure closure_info)
; granYield arg_regs node_points
; funEntryChecks closure_info reg_save_code live fun_body
}
\end{code}
%************************************************************************
%* *
\subsubsubsection[update-and-BHs]{Update and black-hole wrappers}
%* *
%************************************************************************
\begin{code}
blackHoleIt :: ClosureInfo -> Code
blackHoleIt closure_info = emitBlackHoleCode (closureSingleEntry closure_info)
emitBlackHoleCode :: Bool -> Code
emitBlackHoleCode is_single_entry = do
dflags <- getDynFlags
let eager_blackholing = not opt_SccProfilingOn
&& dopt Opt_EagerBlackHoling dflags
whenC eager_blackholing $ do
tickyBlackHole (not is_single_entry)
stmtsC [
CmmStore (cmmOffsetW (CmmReg nodeReg) fixedHdrSize)
(CmmReg (CmmGlobal CurrentTSO)),
CmmCall (CmmPrim MO_WriteBarrier Nothing) [] [] CmmMayReturn,
CmmStore (CmmReg nodeReg) (CmmReg (CmmGlobal EagerBlackholeInfo))
]
\end{code}
\begin{code}
setupUpdate :: ClosureInfo -> Code -> Code
setupUpdate closure_info code
| closureReEntrant closure_info
= code
| not (isStaticClosure closure_info)
= do
if not (closureUpdReqd closure_info)
then do tickyUpdateFrameOmitted; code
else do
tickyPushUpdateFrame
dflags <- getDynFlags
if blackHoleOnEntry closure_info &&
not opt_SccProfilingOn && dopt Opt_EagerBlackHoling dflags
then pushBHUpdateFrame (CmmReg nodeReg) code
else pushUpdateFrame (CmmReg nodeReg) code
| otherwise
= do { tickyUpdateBhCaf closure_info
; if closureUpdReqd closure_info
then do
{ upd_closure <- link_caf closure_info True
; pushBHUpdateFrame upd_closure code }
else do
{
; tickyUpdateFrameOmitted
; code }
}
link_caf :: ClosureInfo
-> Bool
-> FCode CmmExpr
link_caf cl_info _is_upd = do
{
; let use_cc = costCentreFrom (CmmReg nodeReg)
blame_cc = use_cc
tso = CmmReg (CmmGlobal CurrentTSO)
; hp_offset <- allocDynClosure bh_cl_info use_cc blame_cc [(tso,fixedHdrSize)]
; hp_rel <- getHpRelOffset hp_offset
; ret <- newTemp bWord
; emitRtsCallGen [CmmHinted ret NoHint] rtsPackageId (fsLit "newCAF")
[ CmmHinted (CmmReg (CmmGlobal BaseReg)) AddrHint,
CmmHinted (CmmReg nodeReg) AddrHint,
CmmHinted hp_rel AddrHint ]
(Just [node])
; emitIf (CmmMachOp mo_wordEq [ CmmReg (CmmLocal ret), CmmLit zeroCLit]) $
let target = entryCode (closureInfoPtr (CmmReg nodeReg)) in
stmtC (CmmJump target $ Just [node])
; returnFC hp_rel }
where
bh_cl_info :: ClosureInfo
bh_cl_info = cafBlackHoleClosureInfo cl_info
\end{code}
%************************************************************************
%* *
\subsection[CgClosure-Description]{Profiling Closure Description.}
%* *
%************************************************************************
For "global" data constructors the description is simply occurrence
name of the data constructor itself. Otherwise it is determined by
@closureDescription@ from the let binding information.
\begin{code}
closureDescription :: DynFlags
-> Module
-> Name
-> String
closureDescription dflags mod_name name
= showSDocDumpOneLine dflags (char '<' <>
(if isExternalName name
then ppr name
else pprModule mod_name <> char '.' <> ppr name) <>
char '>')
\end{code}