diff options
Diffstat (limited to 'compiler/simplCore/CSE.hs')
-rw-r--r-- | compiler/simplCore/CSE.hs | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/compiler/simplCore/CSE.hs b/compiler/simplCore/CSE.hs new file mode 100644 index 0000000000..7dbf892f9e --- /dev/null +++ b/compiler/simplCore/CSE.hs @@ -0,0 +1,313 @@ +{- +(c) The AQUA Project, Glasgow University, 1993-1998 + +\section{Common subexpression} +-} + +{-# LANGUAGE CPP #-} + +module CSE (cseProgram) where + +#include "HsVersions.h" + +import CoreSubst +import Var ( Var ) +import Id ( Id, idType, idInlineActivation, zapIdOccInfo ) +import CoreUtils ( mkAltExpr + , exprIsTrivial) +import Type ( tyConAppArgs ) +import CoreSyn +import Outputable +import BasicTypes ( isAlwaysActive ) +import TrieMap + +import Data.List + +{- + Simple common sub-expression + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When we see + x1 = C a b + x2 = C x1 b +we build up a reverse mapping: C a b -> x1 + C x1 b -> x2 +and apply that to the rest of the program. + +When we then see + y1 = C a b + y2 = C y1 b +we replace the C a b with x1. But then we *dont* want to +add x1 -> y1 to the mapping. Rather, we want the reverse, y1 -> x1 +so that a subsequent binding + y2 = C y1 b +will get transformed to C x1 b, and then to x2. + +So we carry an extra var->var substitution which we apply *before* looking up in the +reverse mapping. + + +Note [Shadowing] +~~~~~~~~~~~~~~~~ +We have to be careful about shadowing. +For example, consider + f = \x -> let y = x+x in + h = \x -> x+x + in ... + +Here we must *not* do CSE on the inner x+x! The simplifier used to guarantee no +shadowing, but it doesn't any more (it proved too hard), so we clone as we go. +We can simply add clones to the substitution already described. + +Note [Case binders 1] +~~~~~~~~~~~~~~~~~~~~~~ +Consider + + f = \x -> case x of wild { + (a:as) -> case a of wild1 { + (p,q) -> ...(wild1:as)... + +Here, (wild1:as) is morally the same as (a:as) and hence equal to wild. +But that's not quite obvious. In general we want to keep it as (wild1:as), +but for CSE purpose that's a bad idea. + +So we add the binding (wild1 -> a) to the extra var->var mapping. +Notice this is exactly backwards to what the simplifier does, which is +to try to replaces uses of 'a' with uses of 'wild1' + +Note [Case binders 2] +~~~~~~~~~~~~~~~~~~~~~~ +Consider + case (h x) of y -> ...(h x)... + +We'd like to replace (h x) in the alternative, by y. But because of +the preceding [Note: case binders 1], we only want to add the mapping + scrutinee -> case binder +to the reverse CSE mapping if the scrutinee is a non-trivial expression. +(If the scrutinee is a simple variable we want to add the mapping + case binder -> scrutinee +to the substitution + +Note [CSE for INLINE and NOINLINE] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +There are some subtle interactions of CSE with functions that the user +has marked as INLINE or NOINLINE. (Examples from Roman Leshchinskiy.) +Consider + + yes :: Int {-# NOINLINE yes #-} + yes = undefined + + no :: Int {-# NOINLINE no #-} + no = undefined + + foo :: Int -> Int -> Int {-# NOINLINE foo #-} + foo m n = n + + {-# RULES "foo/no" foo no = id #-} + + bar :: Int -> Int + bar = foo yes + +We do not expect the rule to fire. But if we do CSE, then we risk +getting yes=no, and the rule does fire. Actually, it won't because +NOINLINE means that 'yes' will never be inlined, not even if we have +yes=no. So that's fine (now; perhaps in the olden days, yes=no would +have substituted even if 'yes' was NOINLINE. + +But we do need to take care. Consider + + {-# NOINLINE bar #-} + bar = <rhs> -- Same rhs as foo + + foo = <rhs> + +If CSE produces + foo = bar +then foo will never be inlined to <rhs> (when it should be, if <rhs> +is small). The conclusion here is this: + + We should not add + <rhs> :-> bar + to the CSEnv if 'bar' has any constraints on when it can inline; + that is, if its 'activation' not always active. Otherwise we + might replace <rhs> by 'bar', and then later be unable to see that it + really was <rhs>. + +Note that we do not (currently) do CSE on the unfolding stored inside +an Id, even if is a 'stable' unfolding. That means that when an +unfolding happens, it is always faithful to what the stable unfolding +originally was. + + +Note [CSE for case expressions] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Consider + case f x of y { pat -> ...let y = f x in ... } +Then we can CSE the inner (f x) to y. In fact 'case' is like a strict +let-binding, and we can use cseRhs for dealing with the scrutinee. + +************************************************************************ +* * +\section{Common subexpression} +* * +************************************************************************ +-} + +cseProgram :: CoreProgram -> CoreProgram +cseProgram binds = snd (mapAccumL cseBind emptyCSEnv binds) + +cseBind :: CSEnv -> CoreBind -> (CSEnv, CoreBind) +cseBind env (NonRec b e) + = (env2, NonRec b' e') + where + (env1, b') = addBinder env b + (env2, e') = cseRhs env1 (b',e) + +cseBind env (Rec pairs) + = (env2, Rec (bs' `zip` es')) + where + (bs,es) = unzip pairs + (env1, bs') = addRecBinders env bs + (env2, es') = mapAccumL cseRhs env1 (bs' `zip` es) + +cseRhs :: CSEnv -> (OutBndr, InExpr) -> (CSEnv, OutExpr) +cseRhs env (id',rhs) + = case lookupCSEnv env rhs' of + Nothing + | always_active -> (extendCSEnv env rhs' id', rhs') + | otherwise -> (env, rhs') + Just id + | always_active -> (extendCSSubst env id' id, Var id) + | otherwise -> (env, Var id) + -- In the Just case, we have + -- x = rhs + -- ... + -- x' = rhs + -- We are replacing the second binding with x'=x + -- and so must record that in the substitution so + -- that subsequent uses of x' are replaced with x, + -- See Trac #5996 + where + rhs' = cseExpr env rhs + + always_active = isAlwaysActive (idInlineActivation id') + -- See Note [CSE for INLINE and NOINLINE] + +tryForCSE :: CSEnv -> InExpr -> OutExpr +tryForCSE env expr + | exprIsTrivial expr' = expr' -- No point + | Just smaller <- lookupCSEnv env expr' = Var smaller + | otherwise = expr' + where + expr' = cseExpr env expr + +cseExpr :: CSEnv -> InExpr -> OutExpr +cseExpr env (Type t) = Type (substTy (csEnvSubst env) t) +cseExpr env (Coercion c) = Coercion (substCo (csEnvSubst env) c) +cseExpr _ (Lit lit) = Lit lit +cseExpr env (Var v) = lookupSubst env v +cseExpr env (App f a) = App (cseExpr env f) (tryForCSE env a) +cseExpr env (Tick t e) = Tick t (cseExpr env e) +cseExpr env (Cast e co) = Cast (cseExpr env e) (substCo (csEnvSubst env) co) +cseExpr env (Lam b e) = let (env', b') = addBinder env b + in Lam b' (cseExpr env' e) +cseExpr env (Let bind e) = let (env', bind') = cseBind env bind + in Let bind' (cseExpr env' e) +cseExpr env (Case scrut bndr ty alts) = Case scrut' bndr'' ty alts' + where + alts' = cseAlts env2 scrut' bndr bndr'' alts + (env1, bndr') = addBinder env bndr + bndr'' = zapIdOccInfo bndr' + -- The swizzling from Note [Case binders 2] may + -- cause a dead case binder to be alive, so we + -- play safe here and bring them all to life + (env2, scrut') = cseRhs env1 (bndr'', scrut) + -- Note [CSE for case expressions] + +cseAlts :: CSEnv -> OutExpr -> InBndr -> InBndr -> [InAlt] -> [OutAlt] + +cseAlts env scrut' bndr bndr' alts + = map cse_alt alts + where + (con_target, alt_env) + = case scrut' of + Var v' -> (v', extendCSSubst env bndr v') -- See Note [Case binders 1] + -- map: bndr -> v' + + _ -> (bndr', extendCSEnv env scrut' bndr') -- See Note [Case binders 2] + -- map: scrut' -> bndr' + + arg_tys = tyConAppArgs (idType bndr) + + cse_alt (DataAlt con, args, rhs) + | not (null args) + -- Don't try CSE if there are no args; it just increases the number + -- of live vars. E.g. + -- case x of { True -> ....True.... } + -- Don't replace True by x! + -- Hence the 'null args', which also deal with literals and DEFAULT + = (DataAlt con, args', tryForCSE new_env rhs) + where + (env', args') = addBinders alt_env args + new_env = extendCSEnv env' con_expr con_target + con_expr = mkAltExpr (DataAlt con) args' arg_tys + + cse_alt (con, args, rhs) + = (con, args', tryForCSE env' rhs) + where + (env', args') = addBinders alt_env args + +{- +************************************************************************ +* * +\section{The CSE envt} +* * +************************************************************************ +-} + +type InExpr = CoreExpr -- Pre-cloning +type InBndr = CoreBndr +type InAlt = CoreAlt + +type OutExpr = CoreExpr -- Post-cloning +type OutBndr = CoreBndr +type OutAlt = CoreAlt + +data CSEnv = CS { cs_map :: CoreMap (OutExpr, Id) -- Key, value + , cs_subst :: Subst } + +emptyCSEnv :: CSEnv +emptyCSEnv = CS { cs_map = emptyCoreMap, cs_subst = emptySubst } + +lookupCSEnv :: CSEnv -> OutExpr -> Maybe Id +lookupCSEnv (CS { cs_map = csmap }) expr + = case lookupCoreMap csmap expr of + Just (_,e) -> Just e + Nothing -> Nothing + +extendCSEnv :: CSEnv -> OutExpr -> Id -> CSEnv +extendCSEnv cse expr id + = cse { cs_map = extendCoreMap (cs_map cse) expr (expr,id) } + +csEnvSubst :: CSEnv -> Subst +csEnvSubst = cs_subst + +lookupSubst :: CSEnv -> Id -> OutExpr +lookupSubst (CS { cs_subst = sub}) x = lookupIdSubst (text "CSE.lookupSubst") sub x + +extendCSSubst :: CSEnv -> Id -> Id -> CSEnv +extendCSSubst cse x y = cse { cs_subst = extendIdSubst (cs_subst cse) x (Var y) } + +addBinder :: CSEnv -> Var -> (CSEnv, Var) +addBinder cse v = (cse { cs_subst = sub' }, v') + where + (sub', v') = substBndr (cs_subst cse) v + +addBinders :: CSEnv -> [Var] -> (CSEnv, [Var]) +addBinders cse vs = (cse { cs_subst = sub' }, vs') + where + (sub', vs') = substBndrs (cs_subst cse) vs + +addRecBinders :: CSEnv -> [Id] -> (CSEnv, [Id]) +addRecBinders cse vs = (cse { cs_subst = sub' }, vs') + where + (sub', vs') = substRecBndrs (cs_subst cse) vs |