X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=compiler%2Fspecialise%2FRules.lhs;h=18dfdce32ba37dcbe7486bc28879ed8d33681b43;hp=4f62115f9c3d6b63ba071104a4f951d7ef5a8775;hb=ad94d40948668032189ad22a0ad741ac1f645f50;hpb=818c42cb4310f7543b117ae93426c17acbe1b2c9 diff --git a/compiler/specialise/Rules.lhs b/compiler/specialise/Rules.lhs index 4f62115..18dfdce 100644 --- a/compiler/specialise/Rules.lhs +++ b/compiler/specialise/Rules.lhs @@ -4,6 +4,13 @@ \section[CoreRules]{Transformation rules} \begin{code} +{-# OPTIONS -w #-} +-- The above warning supression flag is a temporary kludge. +-- While working on this module you are encouraged to remove it and fix +-- any warnings in the module. See +-- http://hackage.haskell.org/trac/ghc/wiki/CodingStyle#Warnings +-- for details + module Rules ( RuleBase, emptyRuleBase, mkRuleBase, extendRuleBaseList, unionRuleBase, pprRuleBase, ruleCheckProgram, @@ -20,36 +27,32 @@ module Rules ( import CoreSyn -- All of it import OccurAnal ( occurAnalyseExpr ) -import CoreFVs ( exprFreeVars, exprsFreeVars, bindFreeVars, rulesRhsFreeVars ) +import CoreFVs ( exprFreeVars, exprsFreeVars, bindFreeVars, rulesFreeVars ) import CoreUnfold ( isCheapUnfolding, unfoldingTemplate ) -import CoreUtils ( tcEqExprX ) +import CoreUtils ( tcEqExprX, exprType ) import PprCore ( pprRules ) -import Type ( TvSubstEnv ) +import Type ( Type, TvSubstEnv ) import Coercion ( coercionKind ) import TcType ( tcSplitTyConApp_maybe ) import CoreTidy ( tidyRules ) -import Id ( Id, idUnfolding, isLocalId, isGlobalId, idName, +import Id ( Id, idUnfolding, isLocalId, isGlobalId, idName, idType, idSpecialisation, idCoreRules, setIdSpecialisation ) import IdInfo ( SpecInfo( SpecInfo ) ) import Var ( Var ) -import VarEnv ( IdEnv, InScopeSet, emptyTidyEnv, - emptyInScopeSet, mkInScopeSet, extendInScopeSetList, - emptyVarEnv, lookupVarEnv, extendVarEnv, - nukeRnEnvL, mkRnEnv2, rnOccR, rnOccL, inRnEnvR, - rnBndrR, rnBndr2, rnBndrL, rnBndrs2, - rnInScope, extendRnInScopeList, lookupRnInScope ) +import VarEnv import VarSet -import Name ( Name, NamedThing(..), nameOccName ) +import Name ( Name, NamedThing(..) ) import NameEnv import Unify ( ruleMatchTyX, MatchEnv(..) ) import BasicTypes ( Activation, CompilerPhase, isActive ) +import StaticFlags ( opt_PprStyle_Debug ) import Outputable import FastString -import Maybes ( isJust, orElse ) +import Maybes import OrdList import Bag -import Util ( singleton ) -import List ( isPrefixOf ) +import Util +import Data.List \end{code} @@ -95,7 +98,7 @@ mkLocalRule name act fn bndrs args rhs = Rule { ru_name = name, ru_fn = fn, ru_act = act, ru_bndrs = bndrs, ru_args = args, ru_rhs = rhs, ru_rough = roughTopNames args, - ru_orph = Just (nameOccName fn), ru_local = True } + ru_local = True } -------------- roughTopNames :: [CoreExpr] -> [Maybe Name] @@ -140,11 +143,11 @@ ruleCantMatch ts as = False \begin{code} mkSpecInfo :: [CoreRule] -> SpecInfo -mkSpecInfo rules = SpecInfo rules (rulesRhsFreeVars rules) +mkSpecInfo rules = SpecInfo rules (rulesFreeVars rules) extendSpecInfo :: SpecInfo -> [CoreRule] -> SpecInfo extendSpecInfo (SpecInfo rs1 fvs1) rs2 - = SpecInfo (rs2 ++ rs1) (rulesRhsFreeVars rs2 `unionVarSet` fvs1) + = SpecInfo (rs2 ++ rs1) (rulesFreeVars rs2 `unionVarSet` fvs1) addSpecInfo :: SpecInfo -> SpecInfo -> SpecInfo addSpecInfo (SpecInfo rs1 fvs1) (SpecInfo rs2 fvs2) @@ -200,10 +203,27 @@ pprRuleBase rules = vcat [ pprRules (tidyRules emptyTidyEnv rs) %* * %************************************************************************ +Note [Extra args in rule matching] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If we find a matching rule, we return (Just (rule, rhs)), +but the rule firing has only consumed as many of the input args +as the ruleArity says. It's up to the caller to keep track +of any left-over args. E.g. if you call + lookupRule ... f [e1, e2, e3] +and it returns Just (r, rhs), where r has ruleArity 2 +then the real rewrite is + f e1 e2 e3 ==> rhs e3 + +You might think it'd be cleaner for lookupRule to deal with the +leftover arguments, by applying 'rhs' to them, but the main call +in the Simplifier works better as it is. Reason: the 'args' passed +to lookupRule are the result of a lazy substitution + \begin{code} lookupRule :: (Activation -> Bool) -> InScopeSet -> RuleBase -- Imported rules - -> Id -> [CoreExpr] -> Maybe (RuleName, CoreExpr) + -> Id -> [CoreExpr] -> Maybe (CoreRule, CoreExpr) +-- See Note [Extra argsin rule matching] lookupRule is_active in_scope rule_base fn args = matchRules is_active in_scope fn args rules where @@ -217,13 +237,13 @@ lookupRule is_active in_scope rule_base fn args matchRules :: (Activation -> Bool) -> InScopeSet -> Id -> [CoreExpr] - -> [CoreRule] -> Maybe (RuleName, CoreExpr) + -> [CoreRule] -> Maybe (CoreRule, CoreExpr) -- See comments on matchRule matchRules is_active in_scope fn args rules - = case go [] rules of + = -- pprTrace "matchRules" (ppr fn <+> ppr rules) $ + case go [] rules of [] -> Nothing - (m:ms) -> Just (case findBest (fn,args) m ms of - (rule, ans) -> (ru_name rule, ans)) + (m:ms) -> Just (findBest (fn,args) m ms) where rough_args = map roughTopName args @@ -231,7 +251,9 @@ matchRules is_active in_scope fn args rules go ms [] = ms go ms (r:rs) = case (matchRule is_active in_scope args rough_args r) of Just e -> go ((r,e):ms) rs - Nothing -> go ms rs + Nothing -> -- pprTrace "match failed" (ppr r $$ ppr args $$ + -- ppr [(arg_id, unfoldingTemplate unf) | Var arg_id <- args, let unf = idUnfolding arg_id, isCheapUnfolding unf] ) + go ms rs findBest :: (Id, [CoreExpr]) -> (CoreRule,CoreExpr) -> [(CoreRule,CoreExpr)] -> (CoreRule,CoreExpr) @@ -244,10 +266,15 @@ findBest target (rule1,ans1) ((rule2,ans2):prs) | rule1 `isMoreSpecific` rule2 = findBest target (rule1,ans1) prs | rule2 `isMoreSpecific` rule1 = findBest target (rule2,ans2) prs #ifdef DEBUG - | otherwise = pprTrace "Rules.findBest: rule overlap (Rule 1 wins)" - (vcat [ptext SLIT("Expression to match:") <+> ppr fn <+> sep (map ppr args), - ptext SLIT("Rule 1:") <+> ppr rule1, - ptext SLIT("Rule 2:") <+> ppr rule2]) $ + | otherwise = let pp_rule rule + | opt_PprStyle_Debug = ppr rule + | otherwise = doubleQuotes (ftext (ru_name rule)) + in pprTrace "Rules.findBest: rule overlap (Rule 1 wins)" + (vcat [if opt_PprStyle_Debug then + ptext SLIT("Expression to match:") <+> ppr fn <+> sep (map ppr args) + else empty, + ptext SLIT("Rule 1:") <+> pp_rule rule1, + ptext SLIT("Rule 2:") <+> pp_rule rule2]) $ findBest target (rule1,ans1) prs #else | otherwise = findBest target (rule1,ans1) prs @@ -309,11 +336,9 @@ matchRule is_active in_scope args rough_args | ruleCantMatch tpl_tops rough_args = Nothing | otherwise = case matchN in_scope tpl_vars tpl_args args of - Nothing -> Nothing - Just (binds, tpl_vals, leftovers) -> Just (mkLets binds $ - rule_fn - `mkApps` tpl_vals - `mkApps` leftovers) + Nothing -> Nothing + Just (binds, tpl_vals) -> Just (mkLets binds $ + rule_fn `mkApps` tpl_vals) where rule_fn = occurAnalyseExpr (mkLams tpl_vars rhs) -- We could do this when putting things into the rulebase, I guess @@ -325,36 +350,53 @@ matchN :: InScopeSet -> [CoreExpr] -- Template -> [CoreExpr] -- Target; can have more elts than template -> Maybe ([CoreBind], -- Bindings to wrap around the entire result - [CoreExpr], -- What is substituted for each template var - [CoreExpr]) -- Leftover target exprs + [CoreExpr]) -- What is substituted for each template var matchN in_scope tmpl_vars tmpl_es target_es - = do { ((tv_subst, id_subst, binds), leftover_es) + = do { (tv_subst, id_subst, binds) <- go init_menv emptySubstEnv tmpl_es target_es ; return (fromOL binds, - map (lookup_tmpl tv_subst id_subst) tmpl_vars, - leftover_es) } + map (lookup_tmpl tv_subst id_subst) tmpl_vars') } where - init_menv = ME { me_tmpls = mkVarSet tmpl_vars, me_env = init_rn_env } - init_rn_env = mkRnEnv2 (extendInScopeSetList in_scope tmpl_vars) + (init_rn_env, tmpl_vars') = mapAccumL rnBndrL (mkRnEnv2 in_scope) tmpl_vars + -- See Note [Template binders] + + init_menv = ME { me_tmpls = mkVarSet tmpl_vars', me_env = init_rn_env } - go menv subst [] es = Just (subst, es) + go menv subst [] es = Just subst go menv subst ts [] = Nothing -- Fail if too few actual args go menv subst (t:ts) (e:es) = do { subst1 <- match menv subst t e ; go menv subst1 ts es } lookup_tmpl :: TvSubstEnv -> IdSubstEnv -> Var -> CoreExpr - lookup_tmpl tv_subst id_subst tmpl_var - | isTyVar tmpl_var = case lookupVarEnv tv_subst tmpl_var of + lookup_tmpl tv_subst id_subst tmpl_var' + | isTyVar tmpl_var' = case lookupVarEnv tv_subst tmpl_var' of Just ty -> Type ty - Nothing -> unbound tmpl_var - | otherwise = case lookupVarEnv id_subst tmpl_var of + Nothing -> unbound tmpl_var' + | otherwise = case lookupVarEnv id_subst tmpl_var' of Just e -> e - other -> unbound tmpl_var + other -> unbound tmpl_var' - unbound var = pprPanic "Template variable unbound in rewrite rule" (ppr var) + unbound var = pprPanic "Template variable unbound in rewrite rule" + (ppr var $$ ppr tmpl_vars $$ ppr tmpl_vars' $$ ppr tmpl_es $$ ppr target_es) \end{code} +Note [Template binders] +~~~~~~~~~~~~~~~~~~~~~~~ +Consider the following match: + Template: forall x. f x + Target: f (x+1) +This should succeed, because the template variable 'x' has +nothing to do with the 'x' in the target. + +On reflection, this case probably does just work, but this might not + Template: forall x. f (\x.x) + Target: f (\y.y) +Here we want to clone when we find the \x, but to know that x must be in scope + +To achive this, we use rnBndrL to rename the template variables if +necessary; the renamed ones are the tmpl_vars' + --------------------------------------------- The inner workings of matching @@ -412,24 +454,32 @@ match menv subst (Var v1) e2 | Just subst <- match_var menv subst v1 e2 = Just subst +match menv subst e1 (Note n e2) + = match menv subst e1 e2 + -- Note [Notes in RULE matching] + -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + -- Look through Notes. In particular, we don't want to + -- be confused by InlineMe notes. Maybe we should be more + -- careful about profiling notes, but for now I'm just + -- riding roughshod over them. + --- See Note [Notes in call patterns] in SpecConstr + -- Here is another important rule: if the term being matched is a -- variable, we expand it so long as its unfolding is a WHNF -- (Its occurrence information is not necessarily up to date, -- so we don't use it.) match menv subst e1 (Var v2) - | not (inRnEnvR rn_env v2), - -- If v2 is in the RnEnvR, then it's locally bound and can't - -- have an unfolding. We must make this check because if it - -- is locally bound we must not look it up in the in-scope set - -- E.g. (\x->x) where x is already in scope - isCheapUnfolding unfolding + | isCheapUnfolding unfolding = match menv subst e1 (unfoldingTemplate unfolding) where rn_env = me_env menv - unfolding = idUnfolding (lookupRnInScope rn_env v2) + unfolding = idUnfolding (lookupRnInScope rn_env (rnOccR rn_env v2)) -- Notice that we look up v2 in the in-scope set -- See Note [Lookup in-scope] + -- Remember to apply any renaming first (hence rnOccR) +-- Note [Matching lets] +-- ~~~~~~~~~~~~~~~~~~~~ -- Matching a let-expression. Consider -- RULE forall x. f (g x) = -- and target expression @@ -443,24 +493,64 @@ match menv subst e1 (Var v2) -- We can only do this if -- (a) Widening the scope of w does not capture any variables -- We use a conservative test: w is not already in scope +-- If not, we clone the binders, and substitute -- (b) The free variables of R are not bound by the part of the -- target expression outside the let binding; e.g. -- f (\v. let w = v+1 in g E) -- Here we obviously cannot float the let-binding for w. +-- +-- You may think rule (a) would never apply, because rule matching is +-- mostly invoked from the simplifier, when we have just run substExpr +-- over the argument, so there will be no shadowing anyway. +-- The fly in the ointment is that the forall'd variables of the +-- RULE itself are considered in scope. +-- +-- I though of various cheapo ways to solve this tiresome problem, +-- but ended up doing the straightforward thing, which is to +-- clone the binders if they are in scope. It's tiresome, and +-- potentially inefficient, because of the calls to substExpr, +-- but I don't think it'll happen much in pracice. + +{- Cases to think about + (let x=y+1 in \x. (x,x)) + --> let x=y+1 in (\x1. (x1,x1)) + (\x. let x = y+1 in (x,x)) + --> let x1 = y+1 in (\x. (x1,x1) + (let x=y+1 in (x,x), let x=y-1 in (x,x)) + --> let x=y+1 in let x1=y-1 in ((x,x),(x1,x1)) + +Watch out! + (let x=y+1 in let z=x+1 in (z,z) + --> matches (p,p) but watch out that the use of + x on z's rhs is OK! +I'm removing the cloning because that makes the above case +fail, because the inner let looks as if it has locally-bound vars -} match menv subst@(tv_subst, id_subst, binds) e1 (Let bind e2) | all freshly_bound bndrs, not (any locally_bound bind_fvs) = match (menv { me_env = rn_env' }) - (tv_subst, id_subst, binds `snocOL` bind) - e1 e2 + (tv_subst, id_subst, binds `snocOL` bind') + e1 e2' where rn_env = me_env menv - bndrs = bindersOf bind + bndrs = bindersOf bind bind_fvs = varSetElems (bindFreeVars bind) + locally_bound x = inRnEnvR rn_env x freshly_bound x = not (x `rnInScope` rn_env) - locally_bound x = inRnEnvR rn_env x + bind' = bind + e2' = e2 rn_env' = extendRnInScopeList rn_env bndrs +{- + (rn_env', bndrs') = mapAccumL rnBndrR rn_env bndrs + s_prs = [(bndr, Var bndr') | (bndr,bndr') <- zip bndrs bndrs', bndr /= bndr'] + subst = mkSubst (rnInScopeSet rn_env) emptyVarEnv (mkVarEnv s_prs) + (bind', e2') | null s_prs = (bind, e2) + | otherwise = (s_bind, substExpr subst e2) + s_bind = case bind of + NonRec {} -> NonRec (head bndrs') (head rhss) + Rec {} -> Rec (bndrs' `zip` map (substExpr subst) rhss) +-} match menv subst (Lit lit1) (Lit lit2) | lit1 == lit2 @@ -538,7 +628,8 @@ match menv subst e1 (Let bind e2) -} -- Everything else fails -match menv subst e1 e2 = Nothing +match menv subst e1 e2 = -- pprTrace "Failing at" ((text "e1:" <+> ppr e1) $$ (text "e2:" <+> ppr e2)) $ + Nothing ------------------------------------------ match_var :: MatchEnv @@ -553,10 +644,21 @@ match_var menv subst@(tv_subst, id_subst, binds) v1 e2 -> Nothing -- Occurs check failure -- e.g. match forall a. (\x-> a x) against (\y. y y) - | otherwise -- No renaming to do on e2 - -> Just (tv_subst, extendVarEnv id_subst v1 e2, binds) - - Just e2' | tcEqExprX (nukeRnEnvL rn_env) e2' e2 + | otherwise -- No renaming to do on e2, because no free var + -- of e2 is in the rnEnvR of the envt + -- However, we must match the *types*; e.g. + -- forall (c::Char->Int) (x::Char). + -- f (c x) = "RULE FIRED" + -- We must only match on args that have the right type + -- It's actually quite difficult to come up with an example that shows + -- you need type matching, esp since matching is left-to-right, so type + -- args get matched first. But it's possible (e.g. simplrun008) and + -- this is the Right Thing to do + -> do { tv_subst' <- Unify.ruleMatchTyX menv tv_subst (idType v1') (exprType e2) + -- c.f. match_ty below + ; return (tv_subst', extendVarEnv id_subst v1' e2, binds) } + + Just e1' | tcEqExprX (nukeRnEnvL rn_env) e1' e2 -> Just subst | otherwise @@ -604,6 +706,11 @@ We only want to replace (f T) with f', not (f Int). \begin{code} ------------------------------------------ +match_ty :: MatchEnv + -> SubstEnv + -> Type -- Template + -> Type -- Target + -> Maybe SubstEnv match_ty menv (tv_subst, id_subst, binds) ty1 ty2 = do { tv_subst' <- Unify.ruleMatchTyX menv tv_subst ty1 ty2 ; return (tv_subst', id_subst, binds) }