diff options
Diffstat (limited to 'ghc/compiler/absCSyn/AbsCFuns.lhs')
-rw-r--r-- | ghc/compiler/absCSyn/AbsCFuns.lhs | 864 |
1 files changed, 864 insertions, 0 deletions
diff --git a/ghc/compiler/absCSyn/AbsCFuns.lhs b/ghc/compiler/absCSyn/AbsCFuns.lhs new file mode 100644 index 0000000000..448ac5b54b --- /dev/null +++ b/ghc/compiler/absCSyn/AbsCFuns.lhs @@ -0,0 +1,864 @@ +% +% (c) The GRASP/AQUA Project, Glasgow University, 1993-1995 +% +\section[AbsCFuns]{Help functions for Abstract~C datatype} + +\begin{code} +#include "HsVersions.h" + +module AbsCFuns ( + nonemptyAbsC, + mkAbstractCs, mkAbsCStmts, + mkAlgAltsCSwitch, + kindFromMagicId, + getAmodeKind, amodeCanSurviveGC, + mixedTypeLocn, mixedPtrLocn, + flattenAbsC, +--UNUSED: getDestinationRegs, + mkAbsCStmtList, + + -- printing/forcing stuff comes from PprAbsC + + -- and for interface self-sufficiency... + AbstractC, CAddrMode, PrimKind, SplitUniqSupply + ) where + +import AbsCSyn + +import AbsPrel ( PrimOp(..) + IF_ATTACK_PRAGMAS(COMMA tagOf_PrimOp) + IF_ATTACK_PRAGMAS(COMMA pprPrimOp) + ) +import AbsUniType ( kindFromType, splitTyArgs, TauType(..), + TyVar, TyCon, Arity(..), Class, UniType + IF_ATTACK_PRAGMAS(COMMA cmpTyCon COMMA cmpClass) + IF_ATTACK_PRAGMAS(COMMA cmpTyVar) + IF_ATTACK_PRAGMAS(COMMA cmpUniType) + ) + +#ifndef DPH +import CLabelInfo ( CLabel, mkReturnPtLabel, mkVecTblLabel ) +#else +import CLabelInfo ( CLabel, mkReturnPtLabel, + isNestableBlockLabel, isSlowFastLabelPair ) +#endif {- Data Parallel Haskell -} + +import BasicLit ( kindOfBasicLit ) +import Digraph ( stronglyConnComp ) +import Id ( fIRST_TAG, ConTag(..), DataCon(..), Id ) +import Maybes ( Maybe(..) ) +import PrimKind ( getKindSize, retKindSize, PrimKind(..) ) +import SplitUniq +import StgSyn ( StgAtom ) +import Unique -- UniqueSupply primitives used in flattening monad +import Util + +infixr 9 `thenFlt` +\end{code} + +Check if there is any real code in some Abstract~C. If so, return it +(@Just ...@); otherwise, return @Nothing@. Don't be too strict! + +It returns the "reduced" code in the Just part so that the work of +discarding AbsCNops isn't lost, and so that if the caller uses +the reduced version there's less danger of a big tree of AbsCNops getting +materialised and causing a space leak. + +\begin{code} +nonemptyAbsC :: AbstractC -> Maybe AbstractC +nonemptyAbsC AbsCNop = Nothing +--UNUSED:nonemptyAbsC (CComment _) = Nothing +nonemptyAbsC (AbsCStmts s1 s2) = case (nonemptyAbsC s1) of + Nothing -> nonemptyAbsC s2 + Just x -> Just (AbsCStmts x s2) +nonemptyAbsC s@(CSimultaneous c) = case (nonemptyAbsC c) of + Nothing -> Nothing + Just x -> Just s +nonemptyAbsC other = Just other +\end{code} + +\begin{code} +mkAbstractCs :: [AbstractC] -> AbstractC +mkAbstractCs [] = AbsCNop +mkAbstractCs cs = foldr1 mkAbsCStmts cs + +-- for fiddling around w/ killing off AbsCNops ... (ToDo) +mkAbsCStmts :: AbstractC -> AbstractC -> AbstractC +mkAbsCStmts = AbsCStmts + +{- Discarded SLPJ June 95; it calls nonemptyAbsC too much! + = BIND (case (nonemptyAbsC abc2) of + Nothing -> AbsCNop + Just d2 -> d2) _TO_ abc2b -> + + case (nonemptyAbsC abc1) of { + Nothing -> abc2b; + Just d1 -> AbsCStmts d1 abc2b + } BEND +-} +{- + = case (nonemptyAbsC abc1) of + Nothing -> abc2 + Just d1 -> AbsCStmts d1 abc2 +-} +{- old2: + = case (nonemptyAbsC abc1) of + Nothing -> case (nonemptyAbsC abc2) of + Nothing -> AbsCNop + Just d2 -> d2 + Just d1 -> AbsCStmts d1 abc2 +-} +{- old: + if abc1_empty then + if abc2_empty + then AbsCNop + else abc2 + else if {- abc1 not empty but -} abc2_empty then + abc1 + else {- neither empty -} + AbsCStmts abc1 abc2 + where + abc1_empty = noAbsCcode abc1 + abc2_empty = noAbsCcode abc2 +-} +\end{code} + +Get the sho' 'nuff statements out of an @AbstractC@. +\begin{code} +{- +mkAbsCStmtList :: AbstractC -> [AbstractC] + +mkAbsCStmtList AbsCNop = [] +--UNUSED:mkAbsCStmtList (CComment _) = [] +mkAbsCStmtList (AbsCStmts s1 s2) = mkAbsCStmtList s1 ++ mkAbsCStmtList s2 +mkAbsCStmtList s@(CSimultaneous c) = if null (mkAbsCStmtList c) + then [] + else [s] +mkAbsCStmtList other = [other] +-} + +mkAbsCStmtList :: AbstractC -> [AbstractC] +mkAbsCStmtList absC = mkAbsCStmtList' absC [] + +-- Optimised a la foldr/build! + +mkAbsCStmtList' AbsCNop r = r +--UNUSED:mkAbsCStmtList' (CComment _) r = r +mkAbsCStmtList' (AbsCStmts s1 s2) r = + mkAbsCStmtList' s1 (mkAbsCStmtList' s2 r) +mkAbsCStmtList' s@(CSimultaneous c) r = + if null (mkAbsCStmtList c) then r else s : r +mkAbsCStmtList' other r = other : r + +\end{code} + +\begin{code} +mkAlgAltsCSwitch :: CAddrMode -> [(ConTag, AbstractC)] -> AbstractC -> AbstractC + +mkAlgAltsCSwitch scrutinee tagged_alts deflt_absc + = CSwitch scrutinee (adjust tagged_alts) deflt_absc + where + -- Adjust the tags in the switch to start at zero. + -- This is the convention used by primitive ops which return algebraic + -- data types. Why? Because for two-constructor types, zero is faster + -- to create and distinguish from 1 than are 1 and 2. + + -- We also need to convert to BasicLits to keep the CSwitch happy + adjust tagged_alts + = [ (MachInt (toInteger (tag - fIRST_TAG)) False{-unsigned-}, abs_c) + | (tag, abs_c) <- tagged_alts ] +\end{code} + +%************************************************************************ +%* * +\subsubsection[AbsCFuns-kinds-from-MagicIds]{Kinds from MagicIds} +%* * +%************************************************************************ + +\begin{code} +kindFromMagicId BaseReg = PtrKind +kindFromMagicId StkOReg = PtrKind +kindFromMagicId (VanillaReg kind _) = kind +kindFromMagicId (FloatReg _) = FloatKind +kindFromMagicId (DoubleReg _) = DoubleKind +kindFromMagicId TagReg = IntKind +kindFromMagicId RetReg = RetKind +kindFromMagicId SpA = PtrKind +kindFromMagicId SuA = PtrKind +kindFromMagicId SpB = PtrKind +kindFromMagicId SuB = PtrKind +kindFromMagicId Hp = PtrKind +kindFromMagicId HpLim = PtrKind +kindFromMagicId LivenessReg = IntKind +kindFromMagicId ActivityReg = IntKind +kindFromMagicId StdUpdRetVecReg = PtrKind +kindFromMagicId StkStubReg = PtrKind +kindFromMagicId CurCostCentre = CostCentreKind +kindFromMagicId VoidReg = VoidKind +#ifdef DPH +kindFromMagicId (DataReg _ n) = kind +#endif {- Data Parallel Haskell -} +\end{code} + +%************************************************************************ +%* * +\subsection[AbsCFuns-amode-kinds]{Finding @PrimitiveKinds@ of amodes} +%* * +%************************************************************************ + +See also the return conventions for unboxed things; currently living +in @CgCon@ (next to the constructor return conventions). + +ToDo: tiny tweaking may be in order +\begin{code} +getAmodeKind :: CAddrMode -> PrimKind + +getAmodeKind (CVal _ kind) = kind +getAmodeKind (CAddr _) = PtrKind +getAmodeKind (CReg magic_id) = kindFromMagicId magic_id +getAmodeKind (CTemp uniq kind) = kind +getAmodeKind (CLbl label kind) = kind +getAmodeKind (CUnVecLbl _ _) = PtrKind +getAmodeKind (CCharLike _) = PtrKind +getAmodeKind (CIntLike _) = PtrKind +getAmodeKind (CString _) = PtrKind +getAmodeKind (CLit lit) = kindOfBasicLit lit +getAmodeKind (CLitLit _ kind) = kind +getAmodeKind (COffset _) = IntKind +getAmodeKind (CCode abs_C) = CodePtrKind +getAmodeKind (CLabelledCode label abs_C) = CodePtrKind +getAmodeKind (CJoinPoint _ _) = panic "getAmodeKind:CJoinPoint" +getAmodeKind (CTableEntry _ _ kind) = kind +getAmodeKind (CMacroExpr kind _ _) = kind +getAmodeKind (CCostCentre _ _) = panic "getAmodeKind:CCostCentre" +\end{code} + +@amodeCanSurviveGC@ tells, well, whether or not the amode is invariant +across a garbage collection. Used only for PrimOp arguments (not that +it matters). + +\begin{code} +amodeCanSurviveGC :: CAddrMode -> Bool + +amodeCanSurviveGC (CTableEntry base offset _) + = amodeCanSurviveGC base && amodeCanSurviveGC offset + -- "Fixed table, so it's OK" (JSM); code is slightly paranoid + +amodeCanSurviveGC (CLbl _ _) = True +amodeCanSurviveGC (CUnVecLbl _ _) = True +amodeCanSurviveGC (CCharLike arg) = amodeCanSurviveGC arg +amodeCanSurviveGC (CIntLike arg) = amodeCanSurviveGC arg +amodeCanSurviveGC (CString _) = True +amodeCanSurviveGC (CLit _) = True +amodeCanSurviveGC (CLitLit _ _) = True +amodeCanSurviveGC (COffset _) = True +amodeCanSurviveGC (CMacroExpr _ _ args) = all amodeCanSurviveGC args + +amodeCanSurviveGC _ = False + -- there are some amodes that "cannot occur" as args + -- to a PrimOp, but it is safe to return False (rather than panic) +\end{code} + +@mixedTypeLocn@ tells whether an amode identifies an ``StgWord'' +location; that is, one which can contain values of various types. + +\begin{code} +mixedTypeLocn :: CAddrMode -> Bool + +mixedTypeLocn (CVal (NodeRel _) _) = True +mixedTypeLocn (CVal (SpBRel _ _) _) = True +mixedTypeLocn (CVal (HpRel _ _) _) = True +mixedTypeLocn other = False -- All the rest +\end{code} + +@mixedPtrLocn@ tells whether an amode identifies a +location which can contain values of various pointer types. + +\begin{code} +mixedPtrLocn :: CAddrMode -> Bool + +mixedPtrLocn (CVal (SpARel _ _) _) = True +mixedPtrLocn other = False -- All the rest +\end{code} + +%************************************************************************ +%* * +\subsection[AbsCFuns-flattening]{Flatten Abstract~C} +%* * +%************************************************************************ + +The following bits take ``raw'' Abstract~C, which may have all sorts of +nesting, and flattens it into one long @AbsCStmtList@. Mainly, +@CClosureInfos@ and code for switches are pulled out to the top level. + +The various functions herein tend to produce +\begin{enumerate} +\item +A {\em flattened} \tr{<something>} of interest for ``here'', and +\item +Some {\em unflattened} Abstract~C statements to be carried up to the +top-level. The only real reason (now) that it is unflattened is +because it means the recursive flattening can be done in just one +place rather than having to remember lots of places. +\end{enumerate} + +Care is taken to reduce the occurrence of forward references, while still +keeping laziness a much as possible. Essentially, this means that: +\begin{itemize} +\item +{\em All} the top-level C statements resulting from flattening a +particular AbsC statement (whether the latter is nested or not) appear +before {\em any} of the code for a subsequent AbsC statement; +\item +but stuff nested within any AbsC statement comes +out before the code for the statement itself. +\end{itemize} + +The ``stuff to be carried up'' always includes a label: a +@CStaticClosure@, @CClosureUpdInfo@, @CRetUnVector@, @CFlatRetVector@, or +@CCodeBlock@. The latter turns into a C function, and is never +actually produced by the code generator. Rather it always starts life +as a @CLabelledCode@ addressing mode; when such an addr mode is +flattened, the ``tops'' stuff is a @CCodeBlock@. + +\begin{code} +flattenAbsC :: SplitUniqSupply -> AbstractC -> AbstractC + +flattenAbsC us abs_C + = case (initFlt us (flatAbsC abs_C)) of { (here, tops) -> + here `mkAbsCStmts` tops } +\end{code} + +%************************************************************************ +%* * +\subsubsection{Flattening monadery} +%* * +%************************************************************************ + +The flattener is monadised. It's just a @UniqueSupply@, along with a +``come-back-to-here'' label to pin on heap and stack checks. + +\begin{code} +type FlatM result + = CLabel + -> SplitUniqSupply + -> result + +initFlt :: SplitUniqSupply -> FlatM a -> a + +initFlt init_us m = m (panic "initFlt:CLabel") init_us + +#ifdef __GLASGOW_HASKELL__ +{-# INLINE thenFlt #-} +{-# INLINE returnFlt #-} +#endif + +thenFlt :: FlatM a -> (a -> FlatM b) -> FlatM b + +thenFlt expr cont label us + = case (splitUniqSupply us) of { (s1, s2) -> + case (expr label s1) of { result -> + cont result label s2 }} + +returnFlt :: a -> FlatM a +returnFlt result label us = result + +mapFlt :: (a -> FlatM b) -> [a] -> FlatM [b] + +mapFlt f [] = returnFlt [] +mapFlt f (x:xs) + = f x `thenFlt` \ r -> + mapFlt f xs `thenFlt` \ rs -> + returnFlt (r:rs) + +mapAndUnzipFlt :: (a -> FlatM (b,c)) -> [a] -> FlatM ([b],[c]) + +mapAndUnzipFlt f [] = returnFlt ([],[]) +mapAndUnzipFlt f (x:xs) + = f x `thenFlt` \ (r1, r2) -> + mapAndUnzipFlt f xs `thenFlt` \ (rs1, rs2) -> + returnFlt (r1:rs1, r2:rs2) + +getUniqFlt :: FlatM Unique +getUniqFlt label us = getSUnique us + +getUniqsFlt :: Int -> FlatM [Unique] +getUniqsFlt i label us = getSUniques i us + +setLabelFlt :: CLabel -> FlatM a -> FlatM a +setLabelFlt new_label cont label us = cont new_label us + +getLabelFlt :: FlatM CLabel +getLabelFlt label us = label +\end{code} + +%************************************************************************ +%* * +\subsubsection{Flattening the top level} +%* * +%************************************************************************ + +\begin{code} +flatAbsC :: AbstractC + -> FlatM (AbstractC, -- Stuff to put inline [Both are fully + AbstractC) -- Stuff to put at top level flattened] + +flatAbsC AbsCNop = returnFlt (AbsCNop, AbsCNop) + +flatAbsC (AbsCStmts s1 s2) + = flatAbsC s1 `thenFlt` \ (inline_s1, top_s1) -> + flatAbsC s2 `thenFlt` \ (inline_s2, top_s2) -> + returnFlt (mkAbsCStmts inline_s1 inline_s2, + mkAbsCStmts top_s1 top_s2) + +flatAbsC (CClosureInfoAndCode cl_info slow maybe_fast upd descr) + = flatAbsC slow `thenFlt` \ (slow_heres, slow_tops) -> + flat_maybe maybe_fast `thenFlt` \ (fast_heres, fast_tops) -> + flatAmode upd `thenFlt` \ (upd_lbl, upd_tops) -> + returnFlt (AbsCNop, mkAbstractCs [slow_tops, fast_tops, upd_tops, + CClosureInfoAndCode cl_info slow_heres fast_heres upd_lbl descr] + ) + where + flat_maybe :: Maybe AbstractC -> FlatM (Maybe AbstractC, AbstractC) + flat_maybe Nothing = returnFlt (Nothing, AbsCNop) + flat_maybe (Just abs_c) = flatAbsC abs_c `thenFlt` \ (heres, tops) -> + returnFlt (Just heres, tops) + +flatAbsC (CCodeBlock label abs_C) + = flatAbsC abs_C `thenFlt` \ (absC_heres, absC_tops) -> + returnFlt (AbsCNop, absC_tops `mkAbsCStmts` CCodeBlock label absC_heres) + +flatAbsC (CClosureUpdInfo info) = flatAbsC info + +flatAbsC (CStaticClosure closure_lbl closure_info cost_centre amodes) + = flatAmodes (cost_centre:amodes) `thenFlt` \ (new_cc:new_amodes, tops) -> + returnFlt (AbsCNop, tops `mkAbsCStmts` + CStaticClosure closure_lbl closure_info new_cc new_amodes) + +flatAbsC (CRetVector tbl_label stuff deflt) + = do_deflt deflt `thenFlt` \ (deflt_amode, deflt_tops) -> + mapAndUnzipFlt (do_alt deflt_amode) stuff `thenFlt` \ (alt_amodes, alt_tops) -> + returnFlt (AbsCNop, mkAbstractCs [deflt_tops, + mkAbstractCs alt_tops, + CFlatRetVector tbl_label alt_amodes]) + + where + do_deflt deflt = case nonemptyAbsC deflt of + Nothing -> returnFlt (bogus_default_label, AbsCNop) + Just deflt' -> flatAmode (CCode deflt) -- Deals correctly with the + -- CJump (CLabelledCode ...) case + + do_alt deflt_amode Nothing = returnFlt (deflt_amode, AbsCNop) + do_alt deflt_amode (Just alt) = flatAmode alt + + bogus_default_label = panic "flatAbsC: CRetVector: default needed and not available" + + +flatAbsC (CRetUnVector label amode) + = flatAmode amode `thenFlt` \ (new_amode, tops) -> + returnFlt (AbsCNop, tops `mkAbsCStmts` CRetUnVector label new_amode) + +flatAbsC (CFlatRetVector label amodes) + = flatAmodes amodes `thenFlt` \ (new_amodes, tops) -> + returnFlt (AbsCNop, tops `mkAbsCStmts` CFlatRetVector label new_amodes) + +flatAbsC cc@(CCostCentreDecl _ _) -- at top, already flat + = returnFlt (AbsCNop, cc) + +-- now the real stmts: + +flatAbsC (CAssign dest source) + = flatAmode dest `thenFlt` \ (dest_amode, dest_tops) -> + flatAmode source `thenFlt` \ (src_amode, src_tops) -> + returnFlt ( CAssign dest_amode src_amode, mkAbsCStmts dest_tops src_tops ) + +-- special case: jump to some anonymous code +flatAbsC (CJump (CCode abs_C)) = flatAbsC abs_C + +flatAbsC (CJump target) + = flatAmode target `thenFlt` \ (targ_amode, targ_tops) -> + returnFlt ( CJump targ_amode, targ_tops ) + +flatAbsC (CFallThrough target) + = flatAmode target `thenFlt` \ (targ_amode, targ_tops) -> + returnFlt ( CFallThrough targ_amode, targ_tops ) + +flatAbsC (CReturn target return_info) + = flatAmode target `thenFlt` \ (targ_amode, targ_tops) -> + returnFlt ( CReturn targ_amode return_info, targ_tops ) + +flatAbsC (CSwitch discrim alts deflt) + = flatAmode discrim `thenFlt` \ (discrim_amode, discrim_tops) -> + mapAndUnzipFlt flat_alt alts `thenFlt` \ (flat_alts, flat_alts_tops) -> + flatAbsC deflt `thenFlt` \ (flat_def_alt, def_tops) -> + returnFlt ( + CSwitch discrim_amode flat_alts flat_def_alt, + mkAbstractCs (discrim_tops : def_tops : flat_alts_tops) + ) + where + flat_alt (tag, absC) + = flatAbsC absC `thenFlt` \ (alt_heres, alt_tops) -> + returnFlt ( (tag, alt_heres), alt_tops ) + +flatAbsC stmt@(CInitHdr a b cc u) + = flatAmode cc `thenFlt` \ (new_cc, tops) -> + returnFlt (CInitHdr a b new_cc u, tops) + +flatAbsC stmt@(COpStmt results op args liveness_mask vol_regs) + = flatAmodes results `thenFlt` \ (results_here, tops1) -> + flatAmodes args `thenFlt` \ (args_here, tops2) -> + returnFlt (COpStmt results_here op args_here liveness_mask vol_regs, + mkAbsCStmts tops1 tops2) + +flatAbsC stmt@(CSimultaneous abs_c) + = flatAbsC abs_c `thenFlt` \ (stmts_here, tops) -> + doSimultaneously stmts_here `thenFlt` \ new_stmts_here -> + returnFlt (new_stmts_here, tops) + +flatAbsC stmt@(CMacroStmt macro amodes) + = flatAmodes amodes `thenFlt` \ (amodes_here, tops) -> + returnFlt (CMacroStmt macro amodes_here, tops) + +flatAbsC stmt@(CCallProfCtrMacro str amodes) + = flatAmodes amodes `thenFlt` \ (amodes_here, tops) -> + returnFlt (CCallProfCtrMacro str amodes_here, tops) + +flatAbsC stmt@(CCallProfCCMacro str amodes) + = flatAmodes amodes `thenFlt` \ (amodes_here, tops) -> + returnFlt (CCallProfCCMacro str amodes_here, tops) + +--UNUSED:flatAbsC comment_stmt@(CComment comment) = returnFlt (AbsCNop, AbsCNop) + +flatAbsC stmt@(CSplitMarker) = returnFlt (AbsCNop, stmt) + +#ifdef DPH + -- Hack since 0.16 because Direct entry code blocks can be nested + -- within other Direct entry blocks... + flatAbsC (CNativeInfoTableAndCode cinfo descr + (CCodeBlock slow_label + (AbsCStmts slow_abs_c + (CCodeBlock fast_label fast_abs_c)))) + | isSlowFastLabelPair slow_label fast_label + = flatAbsC slow_abs_c `thenFlt` \ (slow_here, slow_top) -> + flatAbsC fast_abs_c `thenFlt` \ (fast_here, fast_top) -> + returnFlt (CNativeInfoTableAndCode cinfo descr + (CCodeBlock slow_label + (AbsCStmts slow_here + (CCodeBlock fast_label fast_here))), + mkAbsCStmts slow_top fast_top) + + flatAbsC (CNativeInfoTableAndCode cinfo descr abs_C) + = flatAbsC abs_C `thenFlt` \ (heres, tops) -> + returnFlt (CNativeInfoTableAndCode cinfo descr heres, tops) +#endif {- Data Parallel Haskell -} + +--flatAbsC stmt = panic ("flatAbsC: funny statement " ++ printRealC (\x->False) stmt) +\end{code} + +%************************************************************************ +%* * +\subsection[flat-amodes]{Flattening addressing modes} +%* * +%************************************************************************ + +\begin{code} +flatAmode :: CAddrMode -> FlatM (CAddrMode, AbstractC) + +-- easy ones first +flatAmode amode@(CVal _ _) = returnFlt (amode, AbsCNop) + +flatAmode amode@(CAddr _) = returnFlt (amode, AbsCNop) +flatAmode amode@(CReg _) = returnFlt (amode, AbsCNop) +flatAmode amode@(CTemp _ _) = returnFlt (amode, AbsCNop) +flatAmode amode@(CLbl _ _) = returnFlt (amode, AbsCNop) +flatAmode amode@(CUnVecLbl _ _) = returnFlt (amode, AbsCNop) +flatAmode amode@(CString _) = returnFlt (amode, AbsCNop) +flatAmode amode@(CLit _) = returnFlt (amode, AbsCNop) +flatAmode amode@(CLitLit _ _) = returnFlt (amode, AbsCNop) +flatAmode amode@(COffset _) = returnFlt (amode, AbsCNop) + +-- CIntLike must be a literal -- no flattening +flatAmode amode@(CIntLike int) = returnFlt(amode, AbsCNop) + +-- CCharLike may be arbitrary value -- have to flatten +flatAmode amode@(CCharLike char) + = flatAmode char `thenFlt` \ (flat_char, tops) -> + returnFlt(CCharLike flat_char, tops) + +flatAmode (CJoinPoint _ _) = panic "flatAmode:CJoinPoint" + +flatAmode (CLabelledCode label abs_C) + -- Push the code (with this label) to the top level + = flatAbsC abs_C `thenFlt` \ (body_code, tops) -> + returnFlt (CLbl label CodePtrKind, + tops `mkAbsCStmts` CCodeBlock label body_code) + +flatAmode (CCode abs_C) + = case mkAbsCStmtList abs_C of + [CJump amode] -> flatAmode amode -- Elide redundant labels + _ -> + -- de-anonymous-ise the code and push it (labelled) to the top level + getUniqFlt `thenFlt` \ new_uniq -> + BIND (mkReturnPtLabel new_uniq) _TO_ return_pt_label -> + flatAbsC abs_C `thenFlt` \ (body_code, tops) -> + returnFlt ( + CLbl return_pt_label CodePtrKind, + tops `mkAbsCStmts` CCodeBlock return_pt_label body_code + -- DO NOT TOUCH the stuff sent to the top... + ) + BEND + +flatAmode (CTableEntry base index kind) + = flatAmode base `thenFlt` \ (base_amode, base_tops) -> + flatAmode index `thenFlt` \ (ix_amode, ix_tops) -> + returnFlt ( CTableEntry base_amode ix_amode kind, mkAbsCStmts base_tops ix_tops ) + +flatAmode (CMacroExpr pk macro amodes) + = flatAmodes amodes `thenFlt` \ (amodes_here, tops) -> + returnFlt ( CMacroExpr pk macro amodes_here, tops ) + +flatAmode amode@(CCostCentre _ _) = returnFlt (amode, AbsCNop) +\end{code} + +And a convenient way to do a whole bunch of 'em. +\begin{code} +flatAmodes :: [CAddrMode] -> FlatM ([CAddrMode], AbstractC) + +flatAmodes [] = returnFlt ([], AbsCNop) + +flatAmodes amodes + = mapAndUnzipFlt flatAmode amodes `thenFlt` \ (amodes_here, tops) -> + returnFlt (amodes_here, mkAbstractCs tops) +\end{code} + +%************************************************************************ +%* * +\subsection[flat-simultaneous]{Doing things simultaneously} +%* * +%************************************************************************ + +\begin{code} +doSimultaneously :: AbstractC -> FlatM AbstractC +\end{code} + +Generate code to perform the @CAssign@s and @COpStmt@s in the +input simultaneously, using temporary variables when necessary. + +We use the strongly-connected component algorithm, in which + * the vertices are the statements + * an edge goes from s1 to s2 iff + s1 assigns to something s2 uses + that is, if s1 should *follow* s2 in the final order + +ADR Comment + +Wow - fancy stuff. But are we ever going to do anything other than +assignments in parallel? If not, wouldn't it be simpler to generate +the following: + + x1, x2, x3 = e1, e2, e3 + + | + | + V + { int t1 = e1; + int t2 = e2; + int t3 = e3; + x1 = t1; + x2 = t2; + x3 = t3; + } + +and leave it to the C compiler to figure out whether it needs al +those variables. + +(Likewise, why not let the C compiler delete silly code like + + x = x + +for us?) + +tnemmoC RDA + +\begin{code} +type CVertex = (Int, AbstractC) -- Give each vertex a unique number, + -- for fast comparison + +type CEdge = (CVertex, CVertex) + +doSimultaneously abs_c + = let + enlisted = en_list abs_c + in + case enlisted of -- it's often just one stmt + [] -> returnFlt AbsCNop + [x] -> returnFlt x + _ -> doSimultaneously1 (zip [(1::Int)..] enlisted) + +-- en_list puts all the assignments in a list, filtering out Nops and +-- assignments which do nothing +en_list AbsCNop = [] +en_list (AbsCStmts a1 a2) = en_list a1 ++ en_list a2 +en_list (CAssign am1 am2) | sameAmode am1 am2 = [] +en_list other = [other] + +sameAmode :: CAddrMode -> CAddrMode -> Bool +-- ToDo: Move this function, or make CAddrMode an instance of Eq +-- At the moment we put in just enough to catch the cases we want: +-- the second (destination) argument is always a CVal. +sameAmode (CReg r1) (CReg r2) = r1 == r2 +sameAmode (CVal (SpARel r1 v1) _) (CVal (SpARel r2 v2) _) = r1 == r2 && v1 == v2 +sameAmode (CVal (SpBRel r1 v1) _) (CVal (SpBRel r2 v2) _) = r1 == r2 && v1 == v2 +sameAmode other1 other2 = False + +doSimultaneously1 :: [CVertex] -> FlatM AbstractC +doSimultaneously1 vertices + = let + edges :: [CEdge] + edges = concat (map edges_from vertices) + + edges_from :: CVertex -> [CEdge] + edges_from v1 = [(v1,v2) | v2 <- vertices, v1 `should_follow` v2] + + should_follow :: CVertex -> CVertex -> Bool + (n1, CAssign dest1 _) `should_follow` (n2, CAssign _ src2) + = dest1 `conflictsWith` src2 + (n1, COpStmt dests1 _ _ _ _) `should_follow` (n2, CAssign _ src2) + = or [dest1 `conflictsWith` src2 | dest1 <- dests1] + (n1, CAssign dest1 _)`should_follow` (n2, COpStmt _ _ srcs2 _ _) + = or [dest1 `conflictsWith` src2 | src2 <- srcs2] + (n1, COpStmt dests1 _ _ _ _) `should_follow` (n2, COpStmt _ _ srcs2 _ _) + = or [dest1 `conflictsWith` src2 | dest1 <- dests1, src2 <- srcs2] + +-- (_, COpStmt _ _ _ _ _) `should_follow` (_, CCallProfCtrMacro _ _) = False +-- (_, CCallProfCtrMacro _ _) `should_follow` (_, COpStmt _ _ _ _ _) = False + + eq_vertex :: CVertex -> CVertex -> Bool + (n1, _) `eq_vertex` (n2, _) = n1 == n2 + + components = stronglyConnComp eq_vertex edges vertices + + -- do_components deal with one strongly-connected component + do_component :: [CVertex] -> FlatM AbstractC + + -- A singleton? Then just do it. + do_component [(n,abs_c)] = returnFlt abs_c + + -- Two or more? Then go via temporaries. + do_component ((n,first_stmt):rest) + = doSimultaneously1 rest `thenFlt` \ abs_cs -> + go_via_temps first_stmt `thenFlt` \ (to_temps, from_temps) -> + returnFlt (mkAbstractCs [to_temps, abs_cs, from_temps]) + + go_via_temps (CAssign dest src) + = getUniqFlt `thenFlt` \ uniq -> + let the_temp = CTemp uniq (getAmodeKind dest) in + returnFlt (CAssign the_temp src, CAssign dest the_temp) + + go_via_temps (COpStmt dests op srcs liveness_mask vol_regs) + = getUniqsFlt (length dests) `thenFlt` \ uniqs -> + let the_temps = zipWith (\ u d -> CTemp u (getAmodeKind d)) uniqs dests + in + returnFlt (COpStmt the_temps op srcs liveness_mask vol_regs, + mkAbstractCs (zipWith CAssign dests the_temps)) + in + mapFlt do_component components `thenFlt` \ abs_cs -> + returnFlt (mkAbstractCs abs_cs) +\end{code} + + +@conflictsWith@ tells whether an assignment to its first argument will +screw up an access to its second. + +\begin{code} +conflictsWith :: CAddrMode -> CAddrMode -> Bool +(CReg reg1) `conflictsWith` (CReg reg2) = reg1 == reg2 +(CReg reg) `conflictsWith` (CVal reg_rel _) = reg `regConflictsWithRR` reg_rel +(CReg reg) `conflictsWith` (CAddr reg_rel) = reg `regConflictsWithRR` reg_rel +(CTemp u1 _) `conflictsWith` (CTemp u2 _) = u1 == u2 +(CVal reg_rel1 k1) `conflictsWith` (CVal reg_rel2 k2) + = rrConflictsWithRR (getKindSize k1) (getKindSize k2) reg_rel1 reg_rel2 + +other1 `conflictsWith` other2 = False +-- CAddr and literals are impossible on the LHS of an assignment + +regConflictsWithRR :: MagicId -> RegRelative -> Bool + +regConflictsWithRR (VanillaReg k ILIT(1)) (NodeRel _) = True + +regConflictsWithRR SpA (SpARel _ _) = True +regConflictsWithRR SpB (SpBRel _ _) = True +regConflictsWithRR Hp (HpRel _ _) = True +regConflictsWithRR _ _ = False + +rrConflictsWithRR :: Int -> Int -- Sizes of two things + -> RegRelative -> RegRelative -- The two amodes + -> Bool + +rrConflictsWithRR s1 s2 rr1 rr2 = rr rr1 rr2 + where + rr (SpARel p1 o1) (SpARel p2 o2) + | s1 == 0 || s2 == 0 = False -- No conflict if either is size zero + | s1 == 1 && s2 == 1 = b1 == b2 + | otherwise = (b1+s1) >= b2 && + (b2+s2) >= b1 + where + b1 = p1-o1 + b2 = p2-o2 + + rr (SpBRel p1 o1) (SpBRel p2 o2) + | s1 == 0 || s2 == 0 = False -- No conflict if either is size zero + | s1 == 1 && s2 == 1 = b1 == b2 + | otherwise = (b1+s1) >= b2 && + (b2+s2) >= b1 + where + b1 = p1-o1 + b2 = p2-o2 + + rr (NodeRel o1) (NodeRel o2) + | s1 == 0 || s2 == 0 = False -- No conflict if either is size zero + | s1 == 1 && s2 == 1 = o1 `possiblyEqualHeapOffset` o2 + | otherwise = True -- Give up + + rr (HpRel _ _) (HpRel _ _) = True -- Give up + + rr other1 other2 = False +\end{code} + +%************************************************************************ +%* * +\subsection[gaze-into-simultaneous]{Registers live in a @CSimultaneous@?} +%* * +%************************************************************************ + +Hidden in a blob of ``simultaneous assignments'' is the info of how +many pointer (``followable'') registers are live (i.e., assigned +into). What we do here is merely fish out the destination registers. + +\begin{code} +{- UNUSED: +getDestinationRegs :: AbstractC -> [MagicId] + +getDestinationRegs abs_c + = foldr gather [{-acc-}] (en_list abs_c) + where + gather :: AbstractC -> [MagicId] -> [MagicId] + + -- only CAssigns and COpStmts now possible... + + gather (CAssign (CReg magic_id) _) acc | magic_id `not_elem` acc + = magic_id : acc + where + not_elem = isn'tIn "getDestinationRegs" + + gather (COpStmt dests _ _ _ _) acc + = foldr gather2 acc dests + where + gather2 (CReg magic_id) acc | magic_id `not_elem` acc = magic_id : acc + gather2 _ acc = acc + + not_elem = isn'tIn "getDestinationRegs2" + + gather _ acc = acc +-} +\end{code} |