%
% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
%
-% $Id: CgCase.lhs,v 1.26 1999/04/23 13:53:28 simonm Exp $
+% $Id: CgCase.lhs,v 1.38 2000/03/23 17:45:19 simonpj Exp $
%
%********************************************************
%* *
%********************************************************
\begin{code}
-module CgCase ( cgCase, saveVolatileVarsAndRegs,
- restoreCurrentCostCentre, freeCostCentreSlot,
- splitTyConAppThroughNewTypes ) where
+module CgCase ( cgCase, saveVolatileVarsAndRegs, restoreCurrentCostCentre
+ ) where
#include "HsVersions.h"
import AbsCUtils ( mkAbstractCs, mkAbsCStmts, mkAlgAltsCSwitch,
getAmodeRep, nonemptyAbsC
)
-import CoreSyn ( isDeadBinder )
import CgUpdate ( reserveSeqFrame )
import CgBindery ( getVolatileRegs, getArgAmodes, getArgAmode,
bindNewToReg, bindNewToTemp,
CtrlReturnConvention(..)
)
import CgStackery ( allocPrimStack, allocStackTop,
- deAllocStackTop, freeStackSlots
+ deAllocStackTop, freeStackSlots, dataStackSlots
)
import CgTailCall ( tailCallFun )
import CgUsages ( getSpRelOffset, getRealSp )
import ClosureInfo ( mkLFArgument )
import CmdLineOpts ( opt_SccProfilingOn, opt_GranMacros )
import CostCentre ( CostCentre )
-import Id ( Id, idPrimRep )
+import Id ( Id, idPrimRep, isDeadBinder )
import DataCon ( DataCon, dataConTag, fIRST_TAG, ConTag,
- isUnboxedTupleCon, dataConType )
+ isUnboxedTupleCon )
import VarSet ( varSetElems )
-import Const ( Con(..), Literal )
+import Literal ( Literal )
import PrimOp ( primOpOutOfLine, PrimOp(..) )
import PrimRep ( getPrimRepSize, retPrimRepSize, PrimRep(..)
)
isNewTyCon, isAlgTyCon, isFunTyCon, isPrimTyCon,
tyConDataCons, tyConFamilySize )
import Type ( Type, typePrimRep, splitAlgTyConApp,
- splitTyConApp_maybe,
- splitFunTys, applyTys )
-import Unique ( Unique, Uniquable(..) )
+ splitTyConApp_maybe, repType )
+import Unique ( Unique, Uniquable(..), mkPseudoUnique1 )
import Maybes ( maybeToBool )
+import Util
import Outputable
\end{code}
-> Code
\end{code}
-Several special cases for inline primitive operations.
+Special case #1: PrimOps returning enumeration types.
+
+For enumeration types, we invent a temporary (builtin-unique 1) to
+hold the tag, and cross our fingers that this doesn't clash with
+anything else. Builtin-unique 0 is used for a similar reason when
+compiling enumerated-type primops in CgExpr.lhs. We can't use the
+unique from the case binder, because this is used to hold the actual
+closure (when the case binder is live, that is).
+
+There is an extra special case for
+
+ case tagToEnum# x of
+ ...
+
+which generates no code for the primop, unless x is used in the
+alternatives (in which case we lookup the tag in the relevant closure
+table to get the closure).
+
+Being a bit short of uniques for temporary variables here, we use
+mkPseudoUnique1 to generate a temporary for the tag. We can't use
+mkBuiltinUnique, because that occasionally clashes with some
+temporaries generated for _ccall_GC, amongst others (see CgExpr.lhs).
\begin{code}
-cgCase (StgCon (PrimOp TagToEnumOp) [arg] res_ty)
- live_in_whole_case live_in_alts bndr srt alts
+cgCase (StgPrimApp op args res_ty)
+ live_in_whole_case live_in_alts bndr srt (StgAlgAlts ty alts deflt)
| isEnumerationTyCon tycon
- = getArgAmode arg `thenFC` \amode ->
- let
- [res] = getPrimAppResultAmodes (getUnique bndr) alts
+ = getArgAmodes args `thenFC` \ arg_amodes ->
+
+ let tag_amode = case op of
+ TagToEnumOp -> only arg_amodes
+ _ -> CTemp (mkPseudoUnique1{-see above-} 1) IntRep
+
+ closure = CVal (CIndex (CLbl (mkClosureTblLabel tycon) PtrRep) tag_amode PtrRep) PtrRep
in
- absC (CAssign res (CTableEntry
- (CLbl (mkClosureTblLabel tycon) PtrRep)
- amode PtrRep)) `thenC`
- -- Scrutinise the result
- cgInlineAlts bndr alts
+ case op of {
+ TagToEnumOp -> nopC; -- no code!
+
+ _ -> -- Perform the operation
+ getVolatileRegs live_in_alts `thenFC` \ vol_regs ->
+
+ absC (COpStmt [tag_amode] op
+ arg_amodes -- note: no liveness arg
+ vol_regs)
+ } `thenC`
+
+ -- bind the default binder if necessary
+ -- The deadness info is set by StgVarInfo
+ (if (isDeadBinder bndr)
+ then nopC
+ else bindNewToTemp bndr `thenFC` \ bndr_amode ->
+ absC (CAssign bndr_amode closure))
+ `thenC`
+
+ -- compile the alts
+ cgAlgAlts NoGC uniq Nothing{-cc_slot-} False{-no semi-tagging-}
+ False{-not poly case-} alts deflt
+ False{-don't emit yield-} `thenFC` \ (tagged_alts, deflt_c) ->
+
+ -- Do the switch
+ absC (mkAlgAltsCSwitch tag_amode tagged_alts deflt_c)
- | otherwise = panic "cgCase: tagToEnum# of non-enumerated type"
where
(Just (tycon,_)) = splitTyConApp_maybe res_ty
+ uniq = getUnique bndr
+\end{code}
-cgCase (StgCon (PrimOp op) args res_ty)
+Special case #2: inline PrimOps.
+
+\begin{code}
+cgCase (StgPrimApp op args res_ty)
live_in_whole_case live_in_alts bndr srt alts
| not (primOpOutOfLine op)
=
two bindings pointing at the same stack locn doesn't work (it
confuses nukeDeadBindings). Hence, use a new temp.
-}
- (if (isDeadBinder bndr)
- then nopC
- else bindNewToTemp bndr `thenFC` \deflt_amode ->
- absC (CAssign deflt_amode amode)) `thenC`
+ bindNewToTemp bndr `thenFC` \deflt_amode ->
+ absC (CAssign deflt_amode amode) `thenC`
cgPrimAlts NoGC amode alts deflt []
\end{code}
:: Unique
-> StgCaseAlts
-> [CAddrMode]
-\end{code}
-
-If there's an StgBindDefault which does use the bound
-variable, then we can only handle it if the type involved is
-an enumeration type. That's important in the case
-of comparisions:
-
- case x ># y of
- r -> f r
-The only reason for the restriction to *enumeration* types is our
-inability to invent suitable temporaries to hold the results;
-Elaborating the CTemp addr mode to have a second uniq field
-(which would simply count from 1) would solve the problem.
-Anyway, cgInlineAlts is now capable of handling all cases;
-it's only this function which is being wimpish.
-
-\begin{code}
-getPrimAppResultAmodes uniq (StgAlgAlts ty alts
- (StgBindDefault rhs))
- | isEnumerationTyCon spec_tycon = [tag_amode]
- | otherwise = pprPanic "getPrimAppResultAmodes: non-enumeration algebraic alternatives with default" (ppr uniq <+> ppr rhs)
- where
- -- A temporary variable to hold the tag; this is unaffected by GC because
- -- the heap-checks in the branches occur after the switch
- tag_amode = CTemp uniq IntRep
- (spec_tycon, _, _) = splitAlgTyConApp ty
-\end{code}
-
-If we don't have a default case, we could be scrutinising an unboxed
-tuple, or an enumeration type...
-
-\begin{code}
-getPrimAppResultAmodes uniq (StgAlgAlts ty alts other_default)
- -- Default is either StgNoDefault or StgBindDefault with unused binder
-
- | isEnumerationTyCon tycon = [CTemp uniq IntRep]
+getPrimAppResultAmodes uniq (StgAlgAlts ty alts some_default)
| isUnboxedTupleTyCon tycon =
case alts of
| otherwise = panic ("getPrimAppResultAmodes: case of primop has strange type: " ++ showSDoc (ppr ty))
where (tycon, _, _) = splitAlgTyConApp ty
-\end{code}
-The situation is simpler for primitive results, because there is only
-one!
+-- The situation is simpler for primitive results, because there is only
+-- one!
-\begin{code}
getPrimAppResultAmodes uniq (StgPrimAlts ty _ _)
= [CTemp uniq (typePrimRep ty)]
\end{code}
=
let uniq = getUnique bndr in
- -- get the stack liveness for the info table (after the CC slot has
- -- been freed - this is important).
- freeCostCentreSlot cc_slot `thenC`
buildContLivenessMask uniq `thenFC` \ liveness_mask ->
case alts of
(StgAlgAlts ty alts deflt) ->
-- bind the default binder (it covers all the alternatives)
- (if (isDeadBinder bndr)
- then nopC
- else bindNewToReg bndr node mkLFArgument) `thenC`
+ bindNewToReg bndr node mkLFArgument `thenC`
-- Generate sequel info for use downstream
-- At the moment, we only do it if the type is vector-returnable.
if is_alg && isUnboxedTupleTyCon spec_tycon then
case alts of
[alt] -> let lbl = mkReturnInfoLabel uniq in
- cgUnboxedTupleAlt lbl cc_slot True alt
+ cgUnboxedTupleAlt uniq cc_slot True alt
`thenFC` \ abs_c ->
getSRTLabel `thenFC` \srt_label ->
absC (CRetDirect uniq abs_c (srt_label, srt)
-- primitive alts...
(StgPrimAlts ty alts deflt) ->
+ -- Restore the cost centre
+ restoreCurrentCostCentre cc_slot `thenFC` \ cc_restore ->
+
-- Generate the switch
getAbsC (cgPrimEvalAlts bndr ty alts deflt) `thenFC` \ abs_c ->
-- Generate the labelled block, starting with restore-cost-centre
getSRTLabel `thenFC` \srt_label ->
- restoreCurrentCostCentre cc_slot `thenFC` \ cc_restore ->
absC (CRetDirect uniq (cc_restore `mkAbsCStmts` abs_c)
(srt_label,srt) liveness_mask) `thenC`
-- Return an amode for the block
- returnFC (CaseAlts (CLbl (mkReturnPtLabel uniq) RetRep) Nothing)
+ returnFC (CaseAlts (CLbl (mkReturnInfoLabel uniq) RetRep) Nothing)
\end{code}
= panic "cgInlineAlts: single alternative, not an unboxed tuple"
\end{code}
-Hack: to deal with
-
- case <# x y of z {
- DEFAULT -> ...
- }
-
-\begin{code}
-cgInlineAlts bndr (StgAlgAlts ty [] (StgBindDefault rhs))
- = bindNewToTemp bndr `thenFC` \amode ->
- let
- (tycon, _, _) = splitAlgTyConApp ty
- closure_lbl = CTableEntry (CLbl (mkClosureTblLabel tycon) PtrRep) amode PtrRep
- in
- absC (CAssign amode closure_lbl) `thenC`
- cgExpr rhs
-\end{code}
-
-Second case: algebraic case, several alternatives.
-Tag is held in a temporary.
-
-\begin{code}
-cgInlineAlts bndr (StgAlgAlts ty alts deflt)
- = -- bind the default binder (it covers all the alternatives)
-
- -- ToDo: BUG! bndr isn't bound in the alternatives
- -- Shows up when compiling Word.lhs
- -- case cmp# a b of r {
- -- True -> f1 r
- -- False -> f2 r
-
- cgAlgAlts NoGC uniq Nothing{-cc_slot-} False{-no semi-tagging-}
- False{-not poly case-} alts deflt
- False{-don't emit yield-} `thenFC` \ (tagged_alts, deflt_c) ->
-
- -- Do the switch
- absC (mkAlgAltsCSwitch tag_amode tagged_alts deflt_c)
- where
- -- A temporary variable to hold the tag; this is unaffected by GC because
- -- the heap-checks in the branches occur after the switch
- tag_amode = CTemp uniq IntRep
- uniq = getUnique bndr
-\end{code}
-
Third (real) case: primitive result type.
\begin{code}
= cgPrimInlineAlts bndr ty alts deflt
\end{code}
-
%************************************************************************
%* *
\subsection[CgCase-alg-alts]{Algebraic alternatives}
= -- We have arranged that Node points to the thing
restoreCurrentCostCentre cc_slot `thenFC` \restore_cc ->
getAbsC (absC restore_cc `thenC`
- (if opt_GranMacros && emit_yield
- then yield [node] False
- else absC AbsCNop) `thenC`
+ -- HWL: maybe need yield here
+ --(if emit_yield
+ -- then yield [node] True
+ -- else absC AbsCNop) `thenC`
possibleHeapCheck gc_flag is_fun [node] [] Nothing (cgExpr rhs)
-- Node is live, but doesn't need to point at the thing itself;
-- it's ok for Node to point to an indirection or FETCH_ME
=
restoreCurrentCostCentre cc_slot `thenFC` \restore_cc ->
getAbsC (absC restore_cc `thenC`
- (if opt_GranMacros && emit_yield
- then yield [node] True -- XXX live regs wrong
- else absC AbsCNop) `thenC`
+ -- HWL: maybe need yield here
+ -- (if emit_yield
+ -- then yield [node] True -- XXX live regs wrong
+ -- else absC AbsCNop) `thenC`
(case gc_flag of
NoGC -> mapFCs bindNewToTemp args `thenFC` \_ -> nopC
GCMayHappen -> bindConArgs con args
lbl = mkAltLabel uniq tag
cgUnboxedTupleAlt
- :: CLabel -- label of the alternative
+ :: Unique -- unique for label of the alternative
-> Maybe VirtualSpOffset -- Restore cost centre
-> Bool -- ctxt switch
-> (DataCon, [Id], [Bool], StgExpr) -- alternative
restoreCurrentCostCentre cc_slot `thenFC` \restore_cc ->
absC restore_cc `thenC`
- (if opt_GranMacros && emit_yield
- then yield live_regs True -- XXX live regs wrong?
- else absC AbsCNop) `thenC`
+ -- HWL: maybe need yield here
+ -- (if emit_yield
+ -- then yield live_regs True -- XXX live regs wrong?
+ -- else absC AbsCNop) `thenC`
let
-- ToDo: could maybe use Nothing here if stack_res is False
-- since the heap-check can just return to the top of the
cgPrimEvalAlts bndr ty alts deflt
= cgPrimAltsWithDefault bndr GCMayHappen (CReg reg) alts deflt [reg]
where
- reg = dataReturnConvPrim kind
+ reg = WARN( case kind of { PtrRep -> True; other -> False }, text "cgPrimEE" <+> ppr bndr <+> ppr ty )
+ dataReturnConvPrim kind
kind = typePrimRep ty
cgPrimAltsWithDefault bndr gc_flag scrutinee alts deflt regs
= -- first bind the default if necessary
- (if isDeadBinder bndr
- then nopC
- else bindNewPrimToAmode bndr scrutinee) `thenC`
+ bindNewPrimToAmode bndr scrutinee `thenC`
cgPrimAlts gc_flag scrutinee alts deflt regs
cgPrimAlts gc_flag scrutinee alts deflt regs
= if not opt_SccProfilingOn then
returnFC (Nothing, AbsCNop)
else
- allocPrimStack (getPrimRepSize CostCentreRep) `thenFC` \ slot ->
+ allocPrimStack (getPrimRepSize CostCentreRep) `thenFC` \ slot ->
+ dataStackSlots [slot] `thenC`
getSpRelOffset slot `thenFC` \ sp_rel ->
returnFC (Just slot,
CAssign (CVal sp_rel CostCentreRep) (CReg CurCostCentre))
-freeCostCentreSlot :: Maybe VirtualSpOffset -> Code
-freeCostCentreSlot Nothing = nopC
-freeCostCentreSlot (Just slot) = freeStackSlots [slot]
-
restoreCurrentCostCentre :: Maybe VirtualSpOffset -> FCode AbstractC
restoreCurrentCostCentre Nothing = returnFC AbsCNop
restoreCurrentCostCentre (Just slot)
= getSpRelOffset slot `thenFC` \ sp_rel ->
+ freeStackSlots [slot] `thenC`
returnFC (CCallProfCCMacro SLIT("RESTORE_CCCS") [CVal sp_rel CostCentreRep])
-- we use the RESTORE_CCCS macro, rather than just
-- assigning into CurCostCentre, in case RESTORE_CCC
-> Bool -- True <=> algebraic case
-> [MagicId] -- live registers
-> [(VirtualSpOffset,Int)] -- stack slots to tag
- -> Maybe CLabel -- return address
+ -> Maybe Unique -- return address unique
-> Code -- continuation
-> Code
= code
\end{code}
-splitTyConAppThroughNewTypes is like splitTyConApp_maybe except
-that it looks through newtypes in addition to synonyms. It's
-useful in the back end where we're not interested in newtypes
-anymore.
-
-Sometimes, we've thrown away the constructors during pruning in the
-renamer. In these cases, we emit a warning and fall back to using a
-SEQ_FRAME to evaluate the case scrutinee.
-
\begin{code}
getScrutineeTyCon :: Type -> Maybe TyCon
getScrutineeTyCon ty =
- case (splitTyConAppThroughNewTypes ty) of
+ case splitTyConApp_maybe (repType ty) of
Nothing -> Nothing
Just (tc,_) ->
if isFunTyCon tc then Nothing else -- not interested in funs
if isPrimTyCon tc then Just tc else -- return primitive tycons
-- otherwise (algebraic tycons) check the no. of constructors
- case (tyConFamilySize tc) of
- 0 -> pprTrace "Warning" (hcat [
- text "constructors for ",
- ppr tc,
- text " not available.\n\tUse -fno-prune-tydecls to fix."
- ]) Nothing
- _ -> Just tc
-
-splitTyConAppThroughNewTypes :: Type -> Maybe (TyCon, [Type])
-splitTyConAppThroughNewTypes ty
- = case splitTyConApp_maybe ty of
- Just (tc, tys)
- | isNewTyCon tc -> splitTyConAppThroughNewTypes ty
- | otherwise -> Just (tc, tys)
- where
- ([ty], _) = splitFunTys (applyTys (dataConType (head (tyConDataCons tc))) tys)
-
- other -> Nothing
-
+ Just tc
\end{code}