%
% (c) The University of Glasgow 2006
% (c) The GRASP Project, Glasgow University, 1992-1998
%
\section[CgCon]{Code generation for constructors}
This module provides the support code for @StgToAbstractC@ to deal
with {\em constructors} on the RHSs of let(rec)s. See also
@CgClosure@, which deals with closures.
\begin{code}
module CgCon (
cgTopRhsCon, buildDynCon,
bindConArgs, bindUnboxedTupleComponents,
cgReturnDataCon,
cgTyCon
) where
#include "HsVersions.h"
import CgMonad
import StgSyn
import CgBindery
import CgStackery
import CgUtils
import CgCallConv
import CgHeapery
import CgTailCall
import CgProf
import CgTicky
import CgInfoTbls
import CLabel
import ClosureInfo
import CmmUtils
import Cmm
import SMRep
import CostCentre
import Constants
import TyCon
import DataCon
import Id
import IdInfo
import Type
import PrelInfo
import Outputable
import ListSetOps
import Util
import Module
import FastString
import StaticFlags
\end{code}
%************************************************************************
%* *
\subsection[toplevel-constructors]{Top-level constructors}
%* *
%************************************************************************
\begin{code}
cgTopRhsCon :: Id
-> DataCon
-> [StgArg]
-> FCode (Id, CgIdInfo)
cgTopRhsCon id con args
= do {
#if mingw32_TARGET_OS
; this_pkg <- getThisPackage
; ASSERT( not (isDllConApp this_pkg con args) ) return ()
#endif
; ASSERT( args `lengthIs` dataConRepArity con ) return ()
; amodes <- getArgAmodes args
; let
name = idName id
lf_info = mkConLFInfo con
closure_label = mkClosureLabel name $ idCafInfo id
caffy = any stgArgHasCafRefs args
(closure_info, amodes_w_offsets) = layOutStaticConstr con amodes
closure_rep = mkStaticClosureFields
closure_info
dontCareCCS
caffy
payload
payload = map get_lit amodes_w_offsets
get_lit (CmmLit lit, _offset) = lit
get_lit other = pprPanic "CgCon.get_lit" (ppr other)
; emitDataLits closure_label closure_rep
; returnFC (id, taggedStableIdInfo id (mkLblExpr closure_label) lf_info con) }
\end{code}
%************************************************************************
%* *
%* non-top-level constructors *
%* *
%************************************************************************
\subsection[code-for-constructors]{The code for constructors}
\begin{code}
buildDynCon :: Id
-> CostCentreStack
-> DataCon
-> [(CgRep,CmmExpr)]
-> FCode CgIdInfo
\end{code}
First we deal with the case of zero-arity constructors. Now, they
will probably be unfolded, so we don't expect to see this case much,
if at all, but it does no harm, and sets the scene for characters.
In the case of zero-arity constructors, or, more accurately, those
which have exclusively size-zero (VoidRep) args, we generate no code
at all.
\begin{code}
buildDynCon binder _ con []
= returnFC (taggedStableIdInfo binder
(mkLblExpr (mkClosureLabel (dataConName con)
(idCafInfo binder)))
(mkConLFInfo con)
con)
\end{code}
The following three paragraphs about @Char@-like and @Int@-like
closures are obsolete, but I don't understand the details well enough
to properly word them, sorry. I've changed the treatment of @Char@s to
be analogous to @Int@s: only a subset is preallocated, because @Char@
has now 31 bits. Only literals are handled here. -- Qrczak
Now for @Char@-like closures. We generate an assignment of the
address of the closure to a temporary. It would be possible simply to
generate no code, and record the addressing mode in the environment,
but we'd have to be careful if the argument wasn't a constant --- so
for simplicity we just always asssign to a temporary.
Last special case: @Int@-like closures. We only special-case the
situation in which the argument is a literal in the range
@mIN_INTLIKE@..@mAX_INTLILKE@. NB: for @Char@-like closures we can
work with any old argument, but for @Int@-like ones the argument has
to be a literal. Reason: @Char@ like closures have an argument type
which is guaranteed in range.
Because of this, we use can safely return an addressing mode.
We don't support this optimisation when compiling into Windows DLLs yet
because they don't support cross package data references well.
\begin{code}
buildDynCon binder _ con [arg_amode]
| maybeIntLikeCon con
#if defined(mingw32_TARGET_OS)
, not opt_PIC
#endif
, (_, CmmLit (CmmInt val _)) <- arg_amode
, let val_int = (fromIntegral val) :: Int
, val_int <= mAX_INTLIKE && val_int >= mIN_INTLIKE
= do { let intlike_lbl = mkCmmGcPtrLabel rtsPackageId (fsLit "stg_INTLIKE_closure")
offsetW = (val_int mIN_INTLIKE) * (fixedHdrSize + 1)
intlike_amode = CmmLit (cmmLabelOffW intlike_lbl offsetW)
; returnFC (taggedStableIdInfo binder intlike_amode (mkConLFInfo con) con) }
buildDynCon binder _ con [arg_amode]
| maybeCharLikeCon con
#if defined(mingw32_TARGET_OS)
, not opt_PIC
#endif
, (_, CmmLit (CmmInt val _)) <- arg_amode
, let val_int = (fromIntegral val) :: Int
, val_int <= mAX_CHARLIKE && val_int >= mIN_CHARLIKE
= do { let charlike_lbl = mkCmmGcPtrLabel rtsPackageId (fsLit "stg_CHARLIKE_closure")
offsetW = (val_int mIN_CHARLIKE) * (fixedHdrSize + 1)
charlike_amode = CmmLit (cmmLabelOffW charlike_lbl offsetW)
; returnFC (taggedStableIdInfo binder charlike_amode (mkConLFInfo con) con) }
\end{code}
Now the general case.
\begin{code}
buildDynCon binder ccs con args
= do {
; let
(closure_info, amodes_w_offsets) = layOutDynConstr con args
; hp_off <- allocDynClosure closure_info use_cc blame_cc amodes_w_offsets
; returnFC (taggedHeapIdInfo binder hp_off lf_info con) }
where
lf_info = mkConLFInfo con
use_cc
| currentOrSubsumedCCS ccs = curCCS
| otherwise = CmmLit (mkCCostCentreStack ccs)
blame_cc = use_cc
\end{code}
%************************************************************************
%* *
%* constructor-related utility function: *
%* bindConArgs is called from cgAlt of a case *
%* *
%************************************************************************
\subsection[constructor-utilities]{@bindConArgs@: constructor-related utility}
@bindConArgs@ $con args$ augments the environment with bindings for the
binders $args$, assuming that we have just returned from a @case@ which
found a $con$.
\begin{code}
bindConArgs :: DataCon -> [Id] -> Code
bindConArgs con args
= do
let
bind_arg (arg, offset) = bindNewToUntagNode arg offset (mkLFArgument arg) (tagForCon con)
(_, args_w_offsets) = layOutDynConstr con (addIdReps args)
ASSERT(not (isUnboxedTupleCon con)) return ()
mapCs bind_arg args_w_offsets
\end{code}
Unboxed tuples are handled slightly differently - the object is
returned in registers and on the stack instead of the heap.
\begin{code}
bindUnboxedTupleComponents
:: [Id]
-> FCode ([(Id,GlobalReg)],
WordOff,
WordOff,
VirtualSpOffset)
bindUnboxedTupleComponents args
= do {
vsp <- getVirtSp
; rsp <- getRealSp
; let (reg_args, stk_args) = assignReturnRegs (addIdReps args)
(ptr_args, nptr_args) = separateByPtrFollowness stk_args
(ptr_sp, ptr_offsets) = mkVirtStkOffsets rsp ptr_args
(nptr_sp, nptr_offsets) = mkVirtStkOffsets ptr_sp nptr_args
ptrs = ptr_sp rsp
nptrs = nptr_sp ptr_sp
; setRealAndVirtualSp nptr_sp
; freeStackSlots [vsp+1,vsp+2 .. rsp]
; bindArgsToRegs reg_args
; bindArgsToStack ptr_offsets
; bindArgsToStack nptr_offsets
; returnFC (reg_args, ptrs, nptrs, rsp) }
\end{code}
%************************************************************************
%* *
Actually generate code for a constructor return
%* *
%************************************************************************
Note: it's the responsibility of the @cgReturnDataCon@ caller to be
sure the @amodes@ passed don't conflict with each other.
\begin{code}
cgReturnDataCon :: DataCon -> [(CgRep, CmmExpr)] -> Code
cgReturnDataCon con amodes
| isUnboxedTupleCon con = returnUnboxedTuple amodes
| opt_SccProfilingOn = build_it_then enter_it
| otherwise
= ASSERT( amodes `lengthIs` dataConRepArity con )
do { EndOfBlockInfo _ sequel <- getEndOfBlockInfo
; case sequel of
CaseAlts _ (Just (alts, deflt_lbl)) bndr
->
case assocMaybe alts (dataConTagZ con) of {
Just join_lbl -> build_it_then (jump_to join_lbl);
Nothing
| isDeadBinder bndr -> performReturn (jump_to deflt_lbl)
| otherwise -> build_it_then (jump_to deflt_lbl) }
_otherwise
-> build_it_then emitReturnInstr
}
where
enter_it = stmtsC [ CmmAssign nodeReg (cmmUntag (CmmReg nodeReg)),
CmmJump (entryCode (closureInfoPtr (CmmReg nodeReg))) [] ]
jump_to lbl = stmtC (CmmJump (CmmLit lbl) [])
build_it_then return_code
= do {
tickyReturnNewCon (length amodes)
; idinfo <- buildDynCon (dataConWorkId con) currentCCS con amodes
; amode <- idInfoToAmode idinfo
; checkedAbsC (CmmAssign nodeReg amode)
; performReturn return_code }
\end{code}
%************************************************************************
%* *
Generating static stuff for algebraic data types
%* *
%************************************************************************
[These comments are rather out of date]
\begin{tabular}{lll}
Info tbls & Macro & Kind of constructor \\
\hline
info & @CONST_INFO_TABLE@& Zero arity (no info -- compiler uses static closure)\\
info & @CHARLIKE_INFO_TABLE@& Charlike (no info -- compiler indexes fixed array)\\
info & @INTLIKE_INFO_TABLE@& Intlike; the one macro generates both info tbls\\
info & @SPEC_INFO_TABLE@& SPECish, and bigger than or equal to @MIN_UPD_SIZE@\\
info & @GEN_INFO_TABLE@& GENish (hence bigger than or equal to @MIN_UPD_SIZE@)\\
\end{tabular}
Possible info tables for constructor con:
\begin{description}
\item[@_con_info@:]
Used for dynamically let(rec)-bound occurrences of
the constructor, and for updates. For constructors
which are int-like, char-like or nullary, when GC occurs,
the closure tries to get rid of itself.
\item[@_static_info@:]
Static occurrences of the constructor
macro: @STATIC_INFO_TABLE@.
\end{description}
For zero-arity constructors, \tr{con}, we NO LONGER generate a static closure;
it's place is taken by the top level defn of the constructor.
For charlike and intlike closures there is a fixed array of static
closures predeclared.
\begin{code}
cgTyCon :: TyCon -> FCode [Cmm]
cgTyCon tycon
= do { constrs <- mapM (getCmm . cgDataCon) (tyConDataCons tycon)
; extra <-
if isEnumerationTyCon tycon then do
tbl <- getCmm (emitRODataLits "cgTyCon" (mkLocalClosureTableLabel (tyConName tycon) NoCafRefs)
[ CmmLabelOff (mkLocalClosureLabel (dataConName con) NoCafRefs) (tagForCon con)
| con <- tyConDataCons tycon])
return [tbl]
else
return []
; return (extra ++ constrs)
}
\end{code}
Generate the entry code, info tables, and (for niladic constructor) the
static closure, for a constructor.
\begin{code}
cgDataCon :: DataCon -> Code
cgDataCon data_con
= do {
; let
(static_cl_info, _) =
layOutStaticConstr data_con arg_reps
(dyn_cl_info, arg_things) =
layOutDynConstr data_con arg_reps
emit_info cl_info ticky_code
= do { code_blks <- getCgStmts the_code
; emitClosureCodeAndInfoTable cl_info [] code_blks }
where
the_code = do { _ <- ticky_code
; ldvEnter (CmmReg nodeReg)
; body_code }
arg_reps :: [(CgRep, Type)]
arg_reps = [(typeCgRep ty, ty) | ty <- dataConRepArgTys data_con]
body_code = do {
tickyReturnOldCon (length arg_things)
; stmtC (CmmAssign nodeReg
(tagCons data_con (CmmReg nodeReg)))
; performReturn emitReturnInstr }
; whenC (not (isNullaryRepDataCon data_con))
(emit_info dyn_cl_info tickyEnterDynCon)
; emit_info static_cl_info tickyEnterStaticCon }
\end{code}