%
-% (c) The GRASP/AQUA Project, Glasgow University, 1992-1996
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
%
%************************************************************************
%* *
import CoreSyn -- input
import StgSyn -- output
-import Bag ( emptyBag, unitBag, unionBags, unionManyBags, bagToList )
import CoreUtils ( coreExprType )
-import CostCentre ( noCostCentre )
-import MkId ( mkSysLocal )
-import Id ( idType, isBottomingId,
- externallyVisibleId, mkIdWithNewUniq,
- nullIdEnv, addOneToIdEnv, lookupIdEnv, growIdEnvList,
- IdEnv, Id
+import SimplUtils ( findDefault )
+import CostCentre ( noCCS )
+import Id ( Id, mkSysLocal, idType, getIdStrictness, idUnique, isExportedId,
+ externallyVisibleId, setIdUnique, idName, getIdDemandInfo
)
-import Literal ( mkMachInt, Literal(..) )
-import PrelVals ( unpackCStringId, unpackCString2Id,
- integerZeroId, integerPlusOneId,
- integerPlusTwoId, integerMinusOneId
- )
-import PrimOp ( PrimOp(..) )
-import SrcLoc ( noSrcLoc )
-import TyCon ( TyCon{-instance Uniquable-} )
-import Type ( splitAlgTyConApp, Type )
-import TysWiredIn ( stringTy )
-import Unique ( integerTyConKey, ratioTyConKey, Unique{-instance Eq-} )
+import Var ( Var, varType, modifyIdInfo )
+import IdInfo ( setDemandInfo, StrictnessInfo(..) )
+import UsageSPUtils ( primOpUsgTys )
+import DataCon ( DataCon, dataConName, dataConId )
+import Demand ( Demand, isStrict, wwStrict, wwLazy )
+import Name ( Name, nameModule, isLocallyDefinedName )
+import Module ( isDynamicModule )
+import Const ( Con(..), Literal(..), isLitLitLit, conStrictness, isWHNFCon )
+import VarEnv
+import PrimOp ( PrimOp(..), primOpUsg, primOpSig )
+import Type ( isUnLiftedType, isUnboxedTupleType, Type, splitFunTy_maybe,
+ UsageAnn(..), tyUsg, applyTy, mkUsgTy )
+import TysPrim ( intPrimTy )
import UniqSupply -- all of it, really
-import Util ( zipLazy )
+import Util ( lengthExceeds )
+import BasicTypes ( TopLevelFlag(..) )
+import Maybes
import Outputable
-import Ratio ( numerator, denominator )
-
-isLeakFreeType x y = False -- safe option; ToDo
\end{code}
+ *************************************************
*************** OVERVIEW *********************
+ *************************************************
-The business of this pass is to convert Core to Stg. On the way:
+The business of this pass is to convert Core to Stg. On the way it
+does some important transformations:
-* We discard type lambdas and applications. In so doing we discard
- "trivial" bindings such as
+1. We discard type lambdas and applications. In so doing we discard
+ "trivial" bindings such as
x = y t1 t2
- where t1, t2 are types
+ where t1, t2 are types
+
+2. We get the program into "A-normal form". In particular:
+
+ f E ==> let x = E in f x
+ OR ==> case E of x -> f x
+
+ where E is a non-trivial expression.
+ Which transformation is used depends on whether f is strict or not.
+ [Previously the transformation to case used to be done by the
+ simplifier, but it's better done here. It does mean that f needs
+ to have its strictness info correct!.]
+
+ Similarly, convert any unboxed let's into cases.
+ [I'm experimenting with leaving 'ok-for-speculation' rhss in let-form
+ right up to this point.]
+
+3. We clone all local binders. The code generator uses the uniques to
+ name chunks of code for thunks, so it's important that the names used
+ are globally unique, not simply not-in-scope, which is all that
+ the simplifier ensures.
+
+
+NOTE THAT:
* We don't pin on correct arities any more, because they can be mucked up
by the lambda lifter. In particular, the lambda lifter can take a local
%* *
%************************************************************************
-Because we're going to come across ``boring'' bindings like
-\tr{let x = /\ tyvars -> y in ...}, we want to keep a small
-environment, so we can just replace all occurrences of \tr{x}
-with \tr{y}.
-
-March 98: We also use this environment to give all locally bound
+March 98: We keep a small environment to give all locally bound
Names new unique ids, since the code generator assumes that binders
are unique across a module. (Simplifier doesn't maintain this
invariant any longer.)
+A binder to be floated out becomes an @StgFloatBind@.
+
+\begin{code}
+type StgEnv = IdEnv Id
+
+data StgFloatBind = NoBindF
+ | RecF [(Id, StgRhs)]
+ | NonRecF
+ Id
+ StgExpr -- *Can* be a StgLam
+ RhsDemand
+ [StgFloatBind]
+
+-- The interesting one is the NonRecF
+-- NonRecF x rhs demand binds
+-- means
+-- x = let binds in rhs
+-- (or possibly case etc if x demand is strict)
+-- The binds are kept separate so they can be floated futher
+-- if appropriate
+\end{code}
+
+A @RhsDemand@ gives the demand on an RHS: strict (@isStrictDem@) and
+thus case-bound, or if let-bound, at most once (@isOnceDem@) or
+otherwise.
+
\begin{code}
-type StgEnv = IdEnv StgArg
+data RhsDemand = RhsDemand { isStrictDem :: Bool, -- True => used at least once
+ isOnceDem :: Bool -- True => used at most once
+ }
+
+mkDem :: Demand -> Bool -> RhsDemand
+mkDem strict once = RhsDemand (isStrict strict) once
+
+mkDemTy :: Demand -> Type -> RhsDemand
+mkDemTy strict ty = RhsDemand (isStrict strict) (isOnceTy ty)
+
+isOnceTy :: Type -> Bool
+isOnceTy ty = case tyUsg ty of
+ UsOnce -> True
+ UsMany -> False
+
+bdrDem :: Id -> RhsDemand
+bdrDem id = mkDem (getIdDemandInfo id) (isOnceTy (idType id))
+
+safeDem, onceDem :: RhsDemand
+safeDem = RhsDemand False False -- always safe to use this
+onceDem = RhsDemand False True -- used at most once
\end{code}
No free/live variable information is pinned on in this pass; it's added
\begin{code}
topCoreBindsToStg :: UniqSupply -- name supply
- -> [CoreBinding] -- input
+ -> [CoreBind] -- input
-> [StgBinding] -- output
topCoreBindsToStg us core_binds
- = initUs us (coreBindsToStg nullIdEnv core_binds)
+ = initUs_ us (coreBindsToStg emptyVarEnv core_binds)
where
- coreBindsToStg :: StgEnv -> [CoreBinding] -> UniqSM [StgBinding]
+ coreBindsToStg :: StgEnv -> [CoreBind] -> UniqSM [StgBinding]
coreBindsToStg env [] = returnUs []
coreBindsToStg env (b:bs)
- = coreBindToStg env b `thenUs` \ (new_b, new_env) ->
+ = coreBindToStg TopLevel env b `thenUs` \ (bind_spec, new_env) ->
coreBindsToStg new_env bs `thenUs` \ new_bs ->
- returnUs (new_b ++ new_bs)
+ case bind_spec of
+ NonRecF bndr rhs dem floats
+ -> ASSERT2( not (isStrictDem dem) &&
+ not (isUnLiftedType (idType bndr)),
+ ppr b ) -- No top-level cases!
+
+ mkStgBinds floats rhs `thenUs` \ new_rhs ->
+ returnUs (StgNonRec bndr (exprToRhs dem new_rhs) : new_bs)
+ -- Keep all the floats inside...
+ -- Some might be cases etc
+ -- We might want to revisit this decision
+
+ RecF prs -> returnUs (StgRec prs : new_bs)
+ NoBindF -> pprTrace "topCoreBindsToStg" (ppr b) $
+ returnUs new_bs
\end{code}
+
%************************************************************************
%* *
\subsection[coreToStg-binds]{Converting bindings}
%************************************************************************
\begin{code}
-coreBindToStg :: StgEnv
- -> CoreBinding
- -> UniqSM ([StgBinding], -- Empty or singleton
- StgEnv) -- Floats
+coreBindToStg :: TopLevelFlag -> StgEnv -> CoreBind -> UniqSM (StgFloatBind, StgEnv)
+
+coreBindToStg top_lev env (NonRec binder rhs)
+ = coreExprToStgFloat env rhs dem `thenUs` \ (floats, stg_rhs) ->
+ case (floats, stg_rhs) of
+ ([], StgApp var []) | not (isExportedId binder)
+ -> returnUs (NoBindF, extendVarEnv env binder var)
+ -- A trivial binding let x = y in ...
+ -- can arise if postSimplExpr floats a NoRep literal out
+ -- so it seems sensible to deal with it well.
+ -- But we don't want to discard exported things. They can
+ -- occur; e.g. an exported user binding f = g
+
+ other -> newLocalId top_lev env binder `thenUs` \ (new_env, new_binder) ->
+ returnUs (NonRecF new_binder stg_rhs dem floats, new_env)
+ where
+ dem = bdrDem binder
-coreBindToStg env (NonRec binder rhs)
- = coreRhsToStg env rhs `thenUs` \ stg_rhs ->
- let
- -- Binds to return if RHS is trivial
- triv_binds | externallyVisibleId binder = [StgNonRec binder stg_rhs] -- Retain it
- | otherwise = [] -- Discard it
- in
- case stg_rhs of
- StgRhsClosure cc bi fvs upd [] (StgApp atom [] lvs) ->
- -- Trivial RHS, so augment envt, and ditch the binding
- returnUs (triv_binds, new_env)
- where
- new_env = addOneToIdEnv env binder atom
-
- StgRhsCon cc con_id [] ->
- -- Trivial RHS, so augment envt, and ditch the binding
- returnUs (triv_binds, new_env)
- where
- new_env = addOneToIdEnv env binder (StgConArg con_id)
-
- other -> -- Non-trivial RHS
- mkUniqueBinder env binder `thenUs` \ (new_env, new_binder) ->
- returnUs ([StgNonRec new_binder stg_rhs], new_env)
- where
- mkUniqueBinder env binder
- | externallyVisibleId binder = returnUs (env, binder)
- | otherwise =
- -- local binder, give it a new unique Id.
- newUniqueLocalId binder `thenUs` \ binder' ->
- let
- new_env = addOneToIdEnv env binder (StgVarArg binder')
- in
- returnUs (new_env, binder')
-
-
-coreBindToStg env (Rec pairs)
- = -- NB: *** WE DO NOT CHECK FOR TRIV_BINDS in REC BIND ****
- -- (possibly ToDo)
- let
- (binders, rhss) = unzip pairs
- in
- newLocalIds env True{-maybe externally visible-} binders `thenUs` \ (binders', env') ->
- mapUs (coreRhsToStg env') rhss `thenUs` \ stg_rhss ->
- returnUs ([StgRec (binders' `zip` stg_rhss)], env')
+coreBindToStg top_lev env (Rec pairs)
+ = newLocalIds top_lev env binders `thenUs` \ (env', binders') ->
+ mapUs (do_rhs env') pairs `thenUs` \ stg_rhss ->
+ returnUs (RecF (binders' `zip` stg_rhss), env')
+ where
+ binders = map fst pairs
+ do_rhs env (bndr,rhs) = coreExprToStgFloat env rhs dem `thenUs` \ (floats, stg_expr) ->
+ mkStgBinds floats stg_expr `thenUs` \ stg_expr' ->
+ -- NB: stg_expr' might still be a StgLam (and we want that)
+ returnUs (exprToRhs dem stg_expr')
+ where
+ dem = bdrDem bndr
\end{code}
%************************************************************************
\begin{code}
-coreRhsToStg :: StgEnv -> CoreExpr -> UniqSM StgRhs
-
-coreRhsToStg env core_rhs
- = coreExprToStg env core_rhs `thenUs` \ stg_expr ->
-
- let stg_rhs = case stg_expr of
- StgLet (StgNonRec var1 rhs) (StgApp (StgVarArg var2) [] _)
- | var1 == var2 -> rhs
- -- This curious stuff is to unravel what a lambda turns into
- -- We have to do it this way, rather than spot a lambda in the
- -- incoming rhs. Why? Because trivial bindings might conceal
- -- what the rhs is actually like.
-
- StgCon con args _ -> StgRhsCon noCostCentre con args
-
- other -> StgRhsClosure noCostCentre -- No cost centre (ToDo?)
- stgArgOcc -- safe
- bOGUS_FVs
- Updatable -- Be pessimistic
- []
- stg_expr
- in
- returnUs stg_rhs
+exprToRhs :: RhsDemand -> StgExpr -> StgRhs
+exprToRhs dem (StgLam _ bndrs body)
+ = ASSERT( not (null bndrs) )
+ StgRhsClosure noCCS
+ stgArgOcc
+ noSRT
+ bOGUS_FVs
+ ReEntrant -- binders is non-empty
+ bndrs
+ body
+
+{-
+ We reject the following candidates for 'static constructor'dom:
+
+ - any dcon that takes a lit-lit as an arg.
+ - [Win32 DLLs only]: any dcon that is (or takes as arg)
+ that's living in a DLL.
+
+ These constraints are necessary to ensure that the code
+ generated in the end for the static constructors, which
+ live in the data segment, remain valid - i.e., it has to
+ be constant. For obvious reasons, that's hard to guarantee
+ with lit-lits. The second case of a constructor referring
+ to static closures hiding out in some DLL is an artifact
+ of the way Win32 DLLs handle global DLL variables. A (data)
+ symbol exported from a DLL has to be accessed through a
+ level of indirection at the site of use, so whereas
+
+ extern StgClosure y_closure;
+ extern StgClosure z_closure;
+ x = { ..., &y_closure, &z_closure };
+
+ is legal when the symbols are in scope at link-time, it is
+ not when y_closure is in a DLL. So, any potential static
+ closures that refers to stuff that's residing in a DLL
+ will be put in an (updateable) thunk instead.
+
+ An alternative strategy is to support the generation of
+ constructors (ala C++ static class constructors) which will
+ then be run at load time to fix up static closures.
+-}
+exprToRhs dem (StgCon (DataCon con) args _)
+ | not is_dynamic &&
+ all (not.is_lit_lit) args = StgRhsCon noCCS con args
+ where
+ is_dynamic = isDynCon con || any (isDynArg) args
+
+ is_lit_lit (StgVarArg _) = False
+ is_lit_lit (StgConArg x) =
+ case x of
+ Literal l -> isLitLitLit l
+ _ -> False
+
+exprToRhs dem expr
+ = StgRhsClosure noCCS -- No cost centre (ToDo?)
+ stgArgOcc -- safe
+ noSRT -- figure out later
+ bOGUS_FVs
+ (if isOnceDem dem then SingleEntry else Updatable)
+ -- HA! Paydirt for "dem"
+ []
+ expr
+
+isDynCon :: DataCon -> Bool
+isDynCon con = isDynName (dataConName con)
+
+isDynArg :: StgArg -> Bool
+isDynArg (StgVarArg v) = isDynName (idName v)
+isDynArg (StgConArg con) =
+ case con of
+ DataCon dc -> isDynCon dc
+ Literal l -> isLitLitLit l
+ _ -> False
+
+isDynName :: Name -> Bool
+isDynName nm =
+ not (isLocallyDefinedName nm) &&
+ isDynamicModule (nameModule nm)
\end{code}
%************************************************************************
\begin{code}
-coreArgsToStg :: StgEnv -> [CoreArg] -> ([Type], [StgArg])
-
-coreArgsToStg env [] = ([], [])
-coreArgsToStg env (a:as)
- = case a of
- TyArg t -> (t:trest, vrest)
- VarArg v -> (trest, stgLookup env v : vrest)
- LitArg l -> (trest, StgLitArg l : vrest)
+coreArgsToStg :: StgEnv -> [(CoreArg,RhsDemand)] -> UniqSM ([StgFloatBind], [StgArg])
+-- Arguments are all value arguments (tyargs already removed), paired with their demand
+
+coreArgsToStg env []
+ = returnUs ([], [])
+
+coreArgsToStg env (ad:ads)
+ = coreArgToStg env ad `thenUs` \ (bs1, a') ->
+ coreArgsToStg env ads `thenUs` \ (bs2, as') ->
+ returnUs (bs1 ++ bs2, a' : as')
+
+
+coreArgToStg :: StgEnv -> (CoreArg,RhsDemand) -> UniqSM ([StgFloatBind], StgArg)
+-- This is where we arrange that a non-trivial argument is let-bound
+
+coreArgToStg env (arg,dem)
+ = coreExprToStgFloat env arg dem `thenUs` \ (floats, arg') ->
+ case arg' of
+ StgCon con [] _ -> returnUs (floats, StgConArg con)
+ StgApp v [] -> returnUs (floats, StgVarArg v)
+ other -> newStgVar arg_ty `thenUs` \ v ->
+ returnUs ([NonRecF v arg' dem floats], StgVarArg v)
where
- (trest,vrest) = coreArgsToStg env as
+ arg_ty = coreExprType arg
\end{code}
%************************************************************************
\begin{code}
-coreExprToStg :: StgEnv -> CoreExpr -> UniqSM StgExpr
+coreExprToStg :: StgEnv -> CoreExpr -> RhsDemand -> UniqSM StgExpr
+coreExprToStg env expr dem
+ = coreExprToStgFloat env expr dem `thenUs` \ (binds,stg_expr) ->
+ mkStgBinds binds stg_expr `thenUs` \ stg_expr' ->
+ deStgLam stg_expr'
+\end{code}
-coreExprToStg env (Lit lit)
- = returnUs (StgApp (StgLitArg lit) [] bOGUS_LVs)
+%************************************************************************
+%* *
+\subsubsection[coreToStg-let(rec)]{Let and letrec expressions}
+%* *
+%************************************************************************
-coreExprToStg env (Var var)
- = returnUs (mk_app (stgLookup env var) [])
+\begin{code}
+coreExprToStgFloat :: StgEnv -> CoreExpr
+ -> RhsDemand
+ -> UniqSM ([StgFloatBind], StgExpr)
+-- Transform an expression to STG. The demand on the expression is
+-- given by RhsDemand, and is solely used ot figure out the usage
+-- of constructor args: if the constructor is used once, then so are
+-- its arguments. The strictness info in RhsDemand isn't used.
+
+-- The StgExpr returned *can* be an StgLam
+\end{code}
-coreExprToStg env (Con con args)
- = let
- (types, stg_atoms) = coreArgsToStg env args
- in
- returnUs (StgCon con stg_atoms bOGUS_LVs)
+Simple cases first
-coreExprToStg env (Prim op args)
- = let
- (types, stg_atoms) = coreArgsToStg env args
- in
- returnUs (StgPrim op stg_atoms bOGUS_LVs)
+\begin{code}
+coreExprToStgFloat env (Var var) dem
+ = returnUs ([], StgApp (stgLookup env var) [])
+
+coreExprToStgFloat env (Let bind body) dem
+ = coreBindToStg NotTopLevel env bind `thenUs` \ (new_bind, new_env) ->
+ coreExprToStgFloat new_env body dem `thenUs` \ (floats, stg_body) ->
+ returnUs (new_bind:floats, stg_body)
+\end{code}
+
+Covert core @scc@ expression directly to STG @scc@ expression.
+
+\begin{code}
+coreExprToStgFloat env (Note (SCC cc) expr) dem
+ = coreExprToStg env expr dem `thenUs` \ stg_expr ->
+ returnUs ([], StgSCC cc stg_expr)
+
+coreExprToStgFloat env (Note other_note expr) dem
+ = coreExprToStgFloat env expr dem
+\end{code}
+
+\begin{code}
+coreExprToStgFloat env expr@(Type _) dem
+ = pprPanic "coreExprToStgFloat: tyarg unexpected:" $ ppr expr
\end{code}
+
%************************************************************************
%* *
\subsubsection[coreToStg-lambdas]{Lambda abstractions}
%************************************************************************
\begin{code}
-coreExprToStg env expr@(Lam _ _)
+coreExprToStgFloat env expr@(Lam _ _) dem
= let
- (_, binders, body) = collectBinders expr
+ expr_ty = coreExprType expr
+ (binders, body) = collectBinders expr
+ id_binders = filter isId binders
+ body_dem = trace "coreExprToStg: approximating body_dem in Lam"
+ safeDem
in
- newLocalIds env False{-all local-} binders `thenUs` \ (binders', env') ->
- coreExprToStg env' body `thenUs` \ stg_body ->
-
- if null binders then -- it was all type/usage binders; tossed
- returnUs stg_body
+ if null id_binders then -- It was all type/usage binders; tossed
+ coreExprToStgFloat env body dem
else
- newStgVar (coreExprType expr) `thenUs` \ var ->
- returnUs
- (StgLet (StgNonRec var
- (StgRhsClosure noCostCentre
- stgArgOcc
- bOGUS_FVs
- ReEntrant -- binders is non-empty
- binders'
- stg_body))
- (StgApp (StgVarArg var) [] bOGUS_LVs))
+ -- At least some value binders
+ newLocalIds NotTopLevel env id_binders `thenUs` \ (env', binders') ->
+ coreExprToStgFloat env' body body_dem `thenUs` \ (floats, stg_body) ->
+ mkStgBinds floats stg_body `thenUs` \ stg_body' ->
+
+ case stg_body' of
+ StgLam ty lam_bndrs lam_body ->
+ -- If the body reduced to a lambda too, join them up
+ returnUs ([], StgLam expr_ty (binders' ++ lam_bndrs) lam_body)
+
+ other ->
+ -- Body didn't reduce to a lambda, so return one
+ returnUs ([], StgLam expr_ty binders' stg_body')
\end{code}
+
%************************************************************************
%* *
\subsubsection[coreToStg-applications]{Applications}
%************************************************************************
\begin{code}
-coreExprToStg env expr@(App _ _)
+coreExprToStgFloat env expr@(App _ _) dem
= let
- (fun,args) = collect_args expr []
- (_, stg_args) = coreArgsToStg env args
+ (fun,rads,_,_) = collect_args expr
+ ads = reverse rads
in
+ coreArgsToStg env ads `thenUs` \ (arg_floats, stg_args) ->
+
-- Now deal with the function
- case (fun, args) of
+ case (fun, stg_args) of
(Var fun_id, _) -> -- A function Id, so do an StgApp; it's ok if
-- there are no arguments.
- returnUs (mk_app (stgLookup env fun_id) stg_args)
+ returnUs (arg_floats,
+ StgApp (stgLookup env fun_id) stg_args)
(non_var_fun, []) -> -- No value args, so recurse into the function
- coreExprToStg env non_var_fun
+ ASSERT( null arg_floats )
+ coreExprToStgFloat env non_var_fun dem
other -> -- A non-variable applied to things; better let-bind it.
- newStgVar (coreExprType fun) `thenUs` \ fun_id ->
- coreExprToStg env fun `thenUs` \ (stg_fun) ->
- let
- fun_rhs = StgRhsClosure noCostCentre -- No cost centre (ToDo?)
- stgArgOcc
- bOGUS_FVs
- SingleEntry -- Only entered once
- []
- stg_fun
- in
- returnUs (StgLet (StgNonRec fun_id fun_rhs)
- (StgApp (StgVarArg fun_id) stg_args bOGUS_LVs))
+ newStgVar (coreExprType fun) `thenUs` \ fun_id ->
+ coreExprToStgFloat env fun onceDem `thenUs` \ (fun_floats, stg_fun) ->
+ returnUs (NonRecF fun_id stg_fun onceDem fun_floats : arg_floats,
+ StgApp fun_id stg_args)
+
where
- -- Collect arguments, discarding type/usage applications
- collect_args (App e (TyArg _)) args = collect_args e args
- collect_args (App fun arg) args = collect_args fun (arg:args)
- collect_args (Note (Coerce _ _) expr) args = collect_args expr args
- collect_args (Note InlineCall expr) args = collect_args expr args
- collect_args fun args = (fun, args)
+ -- Collect arguments and demands (*in reverse order*)
+ -- collect_args e = (f, args_w_demands, ty, stricts)
+ -- => e = f tys args, (i.e. args are just the value args)
+ -- e :: ty
+ -- stricts is the leftover demands of e on its further args
+ -- If stricts runs out, we zap all the demands in args_w_demands
+ -- because partial applications are lazy
+
+ collect_args :: CoreExpr -> (CoreExpr, [(CoreExpr,RhsDemand)], Type, [Demand])
+
+ collect_args (Note (Coerce ty _) e) = let (the_fun,ads,_,ss) = collect_args e
+ in (the_fun,ads,ty,ss)
+ collect_args (Note InlineCall e) = collect_args e
+ collect_args (Note (TermUsg _) e) = collect_args e
+
+ collect_args (App fun (Type tyarg)) = let (the_fun,ads,fun_ty,ss) = collect_args fun
+ in (the_fun,ads,applyTy fun_ty tyarg,ss)
+ collect_args (App fun arg)
+ = case ss of
+ [] -> -- Strictness info has run out
+ (the_fun, (arg, mkDemTy wwLazy arg_ty) : zap ads, res_ty, repeat wwLazy)
+ (ss1:ss_rest) -> -- Enough strictness info
+ (the_fun, (arg, mkDemTy ss1 arg_ty) : ads, res_ty, ss_rest)
+ where
+ (the_fun, ads, fun_ty, ss) = collect_args fun
+ (arg_ty, res_ty) = expectJust "coreExprToStgFloat:collect_args" $
+ splitFunTy_maybe fun_ty
+
+ collect_args (Var v)
+ = (Var v, [], idType v, stricts)
+ where
+ stricts = case getIdStrictness v of
+ StrictnessInfo demands _ -> demands
+ other -> repeat wwLazy
+
+ collect_args fun = (fun, [], coreExprType fun, repeat wwLazy)
+
+ -- "zap" nukes the strictness info for a partial application
+ zap ads = [(arg, RhsDemand False once) | (arg, RhsDemand _ once) <- ads]
\end{code}
%************************************************************************
%* *
-\subsubsection[coreToStg-cases]{Case expressions}
+\subsubsection[coreToStg-con]{Constructors and primops}
%* *
%************************************************************************
+For data constructors, the demand on an argument is the demand on the
+constructor as a whole (see module UsageSPInf). For primops, the
+demand is derived from the type of the primop.
-******* TO DO TO DO: fix what follows
-
-Special case for
-
- case (op x1 ... xn) of
- y -> e
-
-where the type of the case scrutinee is a multi-constuctor algebraic type.
-Then we simply compile code for
-
- let y = op x1 ... xn
- in
- e
-
-In this case:
-
- case (op x1 ... xn) of
- C a b -> ...
- y -> e
-
-where the type of the case scrutinee is a multi-constuctor algebraic type.
-we just bomb out at the moment. It never happens in practice.
-
-**** END OF TO DO TO DO
+If usage inference is off, we simply make all bindings updatable for
+speed.
\begin{code}
-coreExprToStg env (Case scrut@(Prim op args) (AlgAlts alts (BindDefault binder rhs)))
- = if not (null alts) then
- panic "cgCase: case on PrimOp with default *and* alts\n"
- -- For now, die if alts are non-empty
- else
- coreExprToStg env (Let (NonRec binder scrut) rhs)
-
-coreExprToStg env (Case discrim alts)
- = coreExprToStg env discrim `thenUs` \ stg_discrim ->
- alts_to_stg discrim alts `thenUs` \ stg_alts ->
- getUnique `thenUs` \ uniq ->
- returnUs (
- StgCase stg_discrim
- bOGUS_LVs
- bOGUS_LVs
- uniq
- stg_alts
- )
- where
- discrim_ty = coreExprType discrim
- (_, discrim_ty_args, _) = splitAlgTyConApp discrim_ty
-
- alts_to_stg discrim (AlgAlts alts deflt)
- = default_to_stg discrim deflt `thenUs` \ stg_deflt ->
- mapUs boxed_alt_to_stg alts `thenUs` \ stg_alts ->
- returnUs (StgAlgAlts discrim_ty stg_alts stg_deflt)
- where
- boxed_alt_to_stg (con, bs, rhs)
- = coreExprToStg env rhs `thenUs` \ stg_rhs ->
- returnUs (con, bs, [ True | b <- bs ]{-bogus use mask-}, stg_rhs)
-
- alts_to_stg discrim (PrimAlts alts deflt)
- = default_to_stg discrim deflt `thenUs` \ stg_deflt ->
- mapUs unboxed_alt_to_stg alts `thenUs` \ stg_alts ->
- returnUs (StgPrimAlts discrim_ty stg_alts stg_deflt)
- where
- unboxed_alt_to_stg (lit, rhs)
- = coreExprToStg env rhs `thenUs` \ stg_rhs ->
- returnUs (lit, stg_rhs)
+coreExprToStgFloat env expr@(Con con args) dem
+ = let
+ (stricts,_) = conStrictness con
+ onces = case con of
+ DEFAULT -> panic "coreExprToStgFloat: DEFAULT"
+
+ Literal _ -> ASSERT( null args' {-'cpp-} ) []
+
+ DataCon c -> repeat (isOnceDem dem)
+ -- HA! This is the sole reason we propagate
+ -- dem all the way down
+
+ PrimOp p -> let tyargs = map (\ (Type ty) -> ty) $
+ takeWhile isTypeArg args
+ (arg_tys,_) = primOpUsgTys p tyargs
+ in ASSERT( length arg_tys == length args' {-'cpp-} )
+ -- primops always fully applied, so == not >=
+ map isOnceTy arg_tys
+
+ dems' = zipWith mkDem stricts onces
+ args' = filter isValArg args
+ in
+ coreArgsToStg env (zip args' dems') `thenUs` \ (arg_floats, stg_atoms) ->
- default_to_stg discrim NoDefault
- = returnUs StgNoDefault
+ -- YUK YUK: must unique if present
+ (case con of
+ PrimOp (CCallOp (Right _) a b c) -> getUniqueUs `thenUs` \ u ->
+ returnUs (PrimOp (CCallOp (Right u) a b c))
+ _ -> returnUs con
+ ) `thenUs` \ con' ->
- default_to_stg discrim (BindDefault binder rhs)
- = coreExprToStg env rhs `thenUs` \ stg_rhs ->
- returnUs (StgBindDefault binder True{-used? no it is lying-} stg_rhs)
-\end{code}
-
-%************************************************************************
-%* *
-\subsubsection[coreToStg-let(rec)]{Let and letrec expressions}
-%* *
-%************************************************************************
-
-\begin{code}
-coreExprToStg env (Let bind body)
- = coreBindToStg env bind `thenUs` \ (stg_binds, new_env) ->
- coreExprToStg new_env body `thenUs` \ stg_body ->
- returnUs (mkStgLets stg_binds stg_body)
+ returnUs (arg_floats, StgCon con' stg_atoms (coreExprType expr))
\end{code}
%************************************************************************
%* *
-\subsubsection[coreToStg-scc]{SCC expressions}
+\subsubsection[coreToStg-cases]{Case expressions}
%* *
%************************************************************************
-Covert core @scc@ expression directly to STG @scc@ expression.
\begin{code}
-coreExprToStg env (Note (SCC cc) expr)
- = coreExprToStg env expr `thenUs` \ stg_expr ->
- returnUs (StgSCC (coreExprType expr) cc stg_expr)
-\end{code}
+coreExprToStgFloat env (Case scrut bndr alts) dem
+ = coreExprToStgFloat env scrut (bdrDem bndr) `thenUs` \ (binds, scrut') ->
+ newEvaldLocalId env bndr `thenUs` \ (env', bndr') ->
+ alts_to_stg env' (findDefault alts) `thenUs` \ alts' ->
+ returnUs (binds, mkStgCase scrut' bndr' alts')
+ where
+ scrut_ty = idType bndr
+ prim_case = isUnLiftedType scrut_ty && not (isUnboxedTupleType scrut_ty)
+
+ alts_to_stg env (alts, deflt)
+ | prim_case
+ = default_to_stg env deflt `thenUs` \ deflt' ->
+ mapUs (prim_alt_to_stg env) alts `thenUs` \ alts' ->
+ returnUs (StgPrimAlts scrut_ty alts' deflt')
+
+ | otherwise
+ = default_to_stg env deflt `thenUs` \ deflt' ->
+ mapUs (alg_alt_to_stg env) alts `thenUs` \ alts' ->
+ returnUs (StgAlgAlts scrut_ty alts' deflt')
+
+ alg_alt_to_stg env (DataCon con, bs, rhs)
+ = coreExprToStg env rhs dem `thenUs` \ stg_rhs ->
+ returnUs (con, filter isId bs, [ True | b <- bs ]{-bogus use mask-}, stg_rhs)
+ -- NB the filter isId. Some of the binders may be
+ -- existential type variables, which STG doesn't care about
+
+ prim_alt_to_stg env (Literal lit, args, rhs)
+ = ASSERT( null args )
+ coreExprToStg env rhs dem `thenUs` \ stg_rhs ->
+ returnUs (lit, stg_rhs)
-\begin{code}
-coreExprToStg env (Note other_note expr) = coreExprToStg env expr
+ default_to_stg env Nothing
+ = returnUs StgNoDefault
+
+ default_to_stg env (Just rhs)
+ = coreExprToStg env rhs dem `thenUs` \ stg_rhs ->
+ returnUs (StgBindDefault stg_rhs)
+ -- The binder is used for prim cases and not otherwise
+ -- (hack for old code gen)
\end{code}
isn't in the StgEnv. (WDP 94/06)
\begin{code}
-stgLookup :: StgEnv -> Id -> StgArg
-stgLookup env var = case (lookupIdEnv env var) of
- Nothing -> StgVarArg var
- Just atom -> atom
+stgLookup :: StgEnv -> Id -> Id
+stgLookup env var = case (lookupVarEnv env var) of
+ Nothing -> var
+ Just var -> var
\end{code}
Invent a fresh @Id@:
\begin{code}
newStgVar :: Type -> UniqSM Id
newStgVar ty
- = getUnique `thenUs` \ uniq ->
- returnUs (mkSysLocal SLIT("stg") uniq ty noSrcLoc)
+ = getUniqueUs `thenUs` \ uniq ->
+ returnUs (mkSysLocal SLIT("stg") uniq ty)
\end{code}
\begin{code}
-newUniqueLocalId :: Id -> UniqSM Id
-newUniqueLocalId i =
- getUnique `thenUs` \ uniq ->
- returnUs (mkIdWithNewUniq i uniq)
-
-newLocalIds :: StgEnv -> Bool -> [Id] -> UniqSM ([Id], StgEnv)
-newLocalIds env maybe_visible [] = returnUs ([], env)
-newLocalIds env maybe_visible (i:is)
- | maybe_visible && externallyVisibleId i =
- newLocalIds env maybe_visible is `thenUs` \ (is', env') ->
- returnUs (i:is', env')
- | otherwise =
- newUniqueLocalId i `thenUs` \ i' ->
- let
- new_env = addOneToIdEnv env i (StgVarArg i')
- in
- newLocalIds new_env maybe_visible is `thenUs` \ (is', env') ->
- returnUs (i':is', env')
+-- we overload the demandInfo field of an Id to indicate whether the Id is definitely
+-- evaluated or not (i.e. whether it is a case binder). This can be used to eliminate
+-- some redundant cases (c.f. dataToTag# above).
+
+newEvaldLocalId env id
+ = getUniqueUs `thenUs` \ uniq ->
+ let
+ id' = modifyIdInfo (`setDemandInfo` wwStrict) (setIdUnique id uniq)
+ new_env = extendVarEnv env id id'
+ in
+ returnUs (new_env, id')
+
+
+newLocalId TopLevel env id
+ = returnUs (env, id)
+ -- Don't clone top-level binders. MkIface relies on their
+ -- uniques staying the same, so it can snaffle IdInfo off the
+ -- STG ids to put in interface files.
+
+newLocalId NotTopLevel env id
+ = -- Local binder, give it a new unique Id.
+ getUniqueUs `thenUs` \ uniq ->
+ let
+ id' = setIdUnique id uniq
+ new_env = extendVarEnv env id id'
+ in
+ returnUs (new_env, id')
+
+newLocalIds :: TopLevelFlag -> StgEnv -> [Id] -> UniqSM (StgEnv, [Id])
+newLocalIds top_lev env []
+ = returnUs (env, [])
+newLocalIds top_lev env (b:bs)
+ = newLocalId top_lev env b `thenUs` \ (env', b') ->
+ newLocalIds top_lev env' bs `thenUs` \ (env'', bs') ->
+ returnUs (env'', b':bs')
\end{code}
\begin{code}
-mkStgLets :: [StgBinding]
- -> StgExpr -- body of let
- -> StgExpr
+-- Stg doesn't have a lambda *expression*,
+deStgLam (StgLam ty bndrs body) = mkStgLamExpr ty bndrs body
+deStgLam expr = returnUs expr
+
+mkStgLamExpr ty bndrs body
+ = ASSERT( not (null bndrs) )
+ newStgVar ty `thenUs` \ fn ->
+ returnUs (StgLet (StgNonRec fn lam_closure) (StgApp fn []))
+ where
+ lam_closure = StgRhsClosure noCCS
+ stgArgOcc
+ noSRT
+ bOGUS_FVs
+ ReEntrant -- binders is non-empty
+ bndrs
+ body
+
+mkStgBinds :: [StgFloatBind]
+ -> StgExpr -- *Can* be a StgLam
+ -> UniqSM StgExpr -- *Can* be a StgLam
+
+mkStgBinds [] body = returnUs body
+mkStgBinds (b:bs) body
+ = deStgLam body `thenUs` \ body' ->
+ go (b:bs) body'
+ where
+ go [] body = returnUs body
+ go (b:bs) body = go bs body `thenUs` \ body' ->
+ mkStgBind b body'
+
+-- The 'body' arg of mkStgBind can't be a StgLam
+mkStgBind NoBindF body = returnUs body
+mkStgBind (RecF prs) body = returnUs (StgLet (StgRec prs) body)
+
+mkStgBind (NonRecF bndr rhs dem floats) body
+#ifdef DEBUG
+ -- We shouldn't get let or case of the form v=w
+ = case rhs of
+ StgApp v [] -> pprTrace "mkStgLet" (ppr bndr <+> ppr v)
+ (mk_stg_let bndr rhs dem floats body)
+ other -> mk_stg_let bndr rhs dem floats body
+
+mk_stg_let bndr rhs dem floats body
+#endif
+ | isUnLiftedType bndr_ty -- Use a case/PrimAlts
+ = ASSERT( not (isUnboxedTupleType bndr_ty) )
+ mkStgBinds floats $
+ mkStgCase rhs bndr (StgPrimAlts bndr_ty [] (StgBindDefault body))
+
+ | is_whnf
+ = if is_strict then
+ -- Strict let with WHNF rhs
+ mkStgBinds floats $
+ StgLet (StgNonRec bndr (exprToRhs dem rhs)) body
+ else
+ -- Lazy let with WHNF rhs; float until we find a strict binding
+ let
+ (floats_out, floats_in) = splitFloats floats
+ in
+ mkStgBinds floats_in rhs `thenUs` \ new_rhs ->
+ mkStgBinds floats_out $
+ StgLet (StgNonRec bndr (exprToRhs dem new_rhs)) body
+
+ | otherwise -- Not WHNF
+ = if is_strict then
+ -- Strict let with non-WHNF rhs
+ mkStgBinds floats $
+ mkStgCase rhs bndr (StgAlgAlts bndr_ty [] (StgBindDefault body))
+ else
+ -- Lazy let with non-WHNF rhs, so keep the floats in the RHS
+ mkStgBinds floats rhs `thenUs` \ new_rhs ->
+ returnUs (StgLet (StgNonRec bndr (exprToRhs dem new_rhs)) body)
+
+ where
+ bndr_ty = idType bndr
+ is_strict = isStrictDem dem
+ is_whnf = case rhs of
+ StgCon _ _ _ -> True
+ StgLam _ _ _ -> True
+ other -> False
+
+-- Split at the first strict binding
+splitFloats fs@(NonRecF _ _ dem _ : _)
+ | isStrictDem dem = ([], fs)
+
+splitFloats (f : fs) = case splitFloats fs of
+ (fs_out, fs_in) -> (f : fs_out, fs_in)
+
+splitFloats [] = ([], [])
-mkStgLets binds body = foldr StgLet body binds
--- mk_app spots an StgCon in a function position,
--- and turns it into an StgCon. See notes with
--- getArgAmode in CgBindery.
-mk_app (StgConArg con) args = StgCon con args bOGUS_LVs
-mk_app other_fun args = StgApp other_fun args bOGUS_LVs
+mkStgCase scrut bndr alts
+ = ASSERT( case scrut of { StgLam _ _ _ -> False; other -> True } )
+ -- We should never find
+ -- case (\x->e) of { ... }
+ -- The simplifier eliminates such things
+ StgCase scrut bOGUS_LVs bOGUS_LVs bndr noSRT alts
\end{code}