\section[Specialise]{Stamping out overloading, and (optionally) polymorphism}
\begin{code}
--- 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/Commentary/CodingStyle#Warnings
--- for details
-
module Specialise ( specProgram ) where
#include "HsVersions.h"
-import Id ( Id, idName, idType, mkUserLocal, idCoreRules,
- idInlinePragma, setInlinePragma, setIdUnfolding,
- isLocalId, idUnfolding )
-import TcType ( Type, mkTyVarTy, tcSplitSigmaTy,
- tyVarsOfTypes, tyVarsOfTheta, isClassPred,
- tcCmpType, isUnLiftedType
- )
-import CoreSubst ( Subst, mkEmptySubst, extendTvSubstList, lookupIdSubst,
- substBndr, substBndrs, substTy, substInScope,
- cloneIdBndr, cloneIdBndrs, cloneRecIdBndrs,
- extendIdSubst
- )
+import Id
+import TcType
+import CoreSubst
import CoreUnfold ( mkUnfolding, mkInlineRule )
-import SimplUtils ( interestingArg )
-import Var ( DictId )
import VarSet
import VarEnv
import CoreSyn
import Rules
import CoreUtils ( exprIsTrivial, applyTypeToArgs, mkPiTypes )
import CoreFVs ( exprFreeVars, exprsFreeVars, idFreeVars )
-import UniqSupply ( UniqSupply,
- UniqSM, initUs_,
- MonadUnique(..)
- )
+import UniqSupply ( UniqSupply, UniqSM, initUs_, MonadUnique(..) )
import Name
import MkId ( voidArgId, realWorldPrimId )
import FiniteMap
import Maybes ( catMaybes, isJust )
-import BasicTypes ( Arity )
+import BasicTypes ( isNeverActive, inlinePragmaActivation )
import Bag
import Util
import Outputable
\begin{code}
specProgram :: UniqSupply -> [CoreBind] -> [CoreBind]
-specProgram us binds = initSM us (do (binds', uds') <- go binds
- return (dumpAllDictBinds uds' binds'))
+specProgram us binds = initSM us $
+ do { (binds', uds') <- go binds
+ ; return (wrapDictBinds (ud_binds uds') binds') }
where
-- We need to start with a Subst that knows all the things
-- that are in scope, so that the substitution engine doesn't
\begin{code}
specVar :: Subst -> Id -> CoreExpr
-specVar subst v = lookupIdSubst subst v
+specVar subst v = lookupIdSubst (text "specVar") subst v
specExpr :: Subst -> CoreExpr -> SpecM (CoreExpr, UsageDetails)
-- We carry a substitution down:
-- the RHS of specialised bindings (no type-let!)
---------------- First the easy cases --------------------
-specExpr subst (Type ty) = return (Type (substTy subst ty), emptyUDs)
+specExpr subst (Type ty) = return (Type (CoreSubst.substTy subst ty), emptyUDs)
specExpr subst (Var v) = return (specVar subst v, emptyUDs)
specExpr _ (Lit lit) = return (Lit lit, emptyUDs)
specExpr subst (Cast e co) = do
(e', uds) <- specExpr subst e
- return ((Cast e' (substTy subst co)), uds)
+ return ((Cast e' (CoreSubst.substTy subst co)), uds)
specExpr subst (Note note body) = do
(body', uds) <- specExpr subst body
return (Note (specNote subst note) body', uds)
---------------- Lambda/case require dumping of usage details --------------------
specExpr subst e@(Lam _ _) = do
(body', uds) <- specExpr subst' body
- let (filtered_uds, body'') = dumpUDs bndrs' uds body'
- return (mkLams bndrs' body'', filtered_uds)
+ let (free_uds, dumped_dbs) = dumpUDs bndrs' uds
+ return (mkLams bndrs' (wrapDictBindsE dumped_dbs body'), free_uds)
where
(bndrs, body) = collectBinders e
(subst', bndrs') = substBndrs subst bndrs
-- More efficient to collect a group of binders together all at once
-- and we don't want to split a lambda group with dumped bindings
-specExpr subst (Case scrut case_bndr ty alts) = do
- (scrut', uds_scrut) <- specExpr subst scrut
- (alts', uds_alts) <- mapAndCombineSM spec_alt alts
- return (Case scrut' case_bndr' (substTy subst ty) alts', uds_scrut `plusUDs` uds_alts)
- where
- (subst_alt, case_bndr') = substBndr subst case_bndr
- -- No need to clone case binder; it can't float like a let(rec)
-
- spec_alt (con, args, rhs) = do
- (rhs', uds) <- specExpr subst_rhs rhs
- let (uds', rhs'') = dumpUDs args uds rhs'
- return ((con, args', rhs''), uds')
- where
- (subst_rhs, args') = substBndrs subst_alt args
+specExpr subst (Case scrut case_bndr ty alts)
+ = do { (scrut', scrut_uds) <- specExpr subst scrut
+ ; (scrut'', case_bndr', alts', alts_uds)
+ <- specCase subst scrut' case_bndr alts
+ ; return (Case scrut'' case_bndr' (CoreSubst.substTy subst ty) alts'
+ , scrut_uds `plusUDs` alts_uds) }
---------------- Finally, let is the interesting case --------------------
specExpr subst (Let bind body) = do
-- Must apply the type substitution to coerceions
specNote :: Subst -> Note -> Note
specNote _ note = note
+
+
+specCase :: Subst
+ -> CoreExpr -- Scrutinee, already done
+ -> Id -> [CoreAlt]
+ -> SpecM ( CoreExpr -- New scrutinee
+ , Id
+ , [CoreAlt]
+ , UsageDetails)
+specCase subst scrut' case_bndr [(con, args, rhs)]
+ | isDictId case_bndr -- See Note [Floating dictionaries out of cases]
+ , interestingDict scrut'
+ , not (isDeadBinder case_bndr && null sc_args')
+ = do { (case_bndr_flt : sc_args_flt) <- mapM clone_me (case_bndr' : sc_args')
+
+ ; let sc_rhss = [ Case (Var case_bndr_flt) case_bndr' (idType sc_arg')
+ [(con, args', Var sc_arg')]
+ | sc_arg' <- sc_args' ]
+
+ -- Extend the substitution for RHS to map the *original* binders
+ -- to their floated verions. Attach an unfolding to these floated
+ -- binders so they look interesting to interestingDict
+ mb_sc_flts :: [Maybe DictId]
+ mb_sc_flts = map (lookupVarEnv clone_env) args'
+ clone_env = zipVarEnv sc_args' (zipWith add_unf sc_args_flt sc_rhss)
+ subst_prs = (case_bndr, Var (add_unf case_bndr_flt scrut'))
+ : [ (arg, Var sc_flt)
+ | (arg, Just sc_flt) <- args `zip` mb_sc_flts ]
+ subst_rhs' = extendIdSubstList subst_rhs subst_prs
+
+ ; (rhs', rhs_uds) <- specExpr subst_rhs' rhs
+ ; let scrut_bind = mkDB (NonRec case_bndr_flt scrut')
+ case_bndr_set = unitVarSet case_bndr_flt
+ sc_binds = [(NonRec sc_arg_flt sc_rhs, case_bndr_set)
+ | (sc_arg_flt, sc_rhs) <- sc_args_flt `zip` sc_rhss ]
+ flt_binds = scrut_bind : sc_binds
+ (free_uds, dumped_dbs) = dumpUDs (case_bndr':args') rhs_uds
+ all_uds = flt_binds `addDictBinds` free_uds
+ alt' = (con, args', wrapDictBindsE dumped_dbs rhs')
+ ; return (Var case_bndr_flt, case_bndr', [alt'], all_uds) }
+ where
+ (subst_rhs, (case_bndr':args')) = substBndrs subst (case_bndr:args)
+ sc_args' = filter is_flt_sc_arg args'
+
+ clone_me bndr = do { uniq <- getUniqueM
+ ; return (mkUserLocal occ uniq ty loc) }
+ where
+ name = idName bndr
+ ty = idType bndr
+ occ = nameOccName name
+ loc = getSrcSpan name
+
+ add_unf sc_flt sc_rhs -- Sole purpose: make sc_flt respond True to interestingDictId
+ = setIdUnfolding sc_flt (mkUnfolding False False sc_rhs)
+
+ arg_set = mkVarSet args'
+ is_flt_sc_arg var = isId var
+ && not (isDeadBinder var)
+ && isDictTy var_ty
+ && not (tyVarsOfType var_ty `intersectsVarSet` arg_set)
+ where
+ var_ty = idType var
+
+
+specCase subst scrut case_bndr alts
+ = do { (alts', uds_alts) <- mapAndCombineSM spec_alt alts
+ ; return (scrut, case_bndr', alts', uds_alts) }
+ where
+ (subst_alt, case_bndr') = substBndr subst case_bndr
+ spec_alt (con, args, rhs) = do
+ (rhs', uds) <- specExpr subst_rhs rhs
+ let (free_uds, dumped_dbs) = dumpUDs (case_bndr' : args') uds
+ return ((con, args', wrapDictBindsE dumped_dbs rhs'), free_uds)
+ where
+ (subst_rhs, args') = substBndrs subst_alt args
\end{code}
+Note [Floating dictionaries out of cases]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider
+ g = \d. case d of { MkD sc ... -> ...(f sc)... }
+Naively we can't float d2's binding out of the case expression,
+because 'sc' is bound by the case, and that in turn means we can't
+specialise f, which seems a pity.
+
+So we invert the case, by floating out a binding
+for 'sc_flt' thus:
+ sc_flt = case d of { MkD sc ... -> sc }
+Now we can float the call instance for 'f'. Indeed this is just
+what'll happen if 'sc' was originally bound with a let binding,
+but case is more efficient, and necessary with equalities. So it's
+good to work with both.
+
+You might think that this won't make any difference, because the
+call instance will only get nuked by the \d. BUT if 'g' itself is
+specialised, then transitively we should be able to specialise f.
+
+In general, given
+ case e of cb { MkD sc ... -> ...(f sc)... }
+we transform to
+ let cb_flt = e
+ sc_flt = case cb_flt of { MkD sc ... -> sc }
+ in
+ case cb_flt of bg { MkD sc ... -> ....(f sc_flt)... }
+
+The "_flt" things are the floated binds; we use the current substitution
+to substitute sc -> sc_flt in the RHS
+
%************************************************************************
%* *
\subsubsection{Dealing with a binding}
-> SpecM ([CoreBind], -- New bindings
UsageDetails) -- And info to pass upstream
-specBind rhs_subst bind body_uds
- = do { (bind', bind_uds) <- specBindItself rhs_subst bind (calls body_uds)
- ; return (finishSpecBind bind' bind_uds body_uds) }
-
-finishSpecBind :: CoreBind -> UsageDetails -> UsageDetails -> ([CoreBind], UsageDetails)
-finishSpecBind bind
- (MkUD { dict_binds = rhs_dbs, calls = rhs_calls, ud_fvs = rhs_fvs })
- (MkUD { dict_binds = body_dbs, calls = body_calls, ud_fvs = body_fvs })
- | not (mkVarSet bndrs `intersectsVarSet` all_fvs)
- -- Common case 1: the bound variables are not
- -- mentioned in the dictionary bindings
- = ([bind], MkUD { dict_binds = body_dbs `unionBags` rhs_dbs
- -- It's important that the `unionBags` is this way round,
- -- because body_uds may bind dictionaries that are
- -- used in the calls passed to specDefn. So the
- -- dictionary bindings in rhs_uds may mention
- -- dictionaries bound in body_uds.
- , calls = all_calls
- , ud_fvs = all_fvs })
-
- | case bind of { NonRec {} -> True; Rec {} -> False }
- -- Common case 2: no specialisation happened, and binding
- -- is non-recursive. But the binding may be
- -- mentioned in body_dbs, so we should put it first
- = ([], MkUD { dict_binds = rhs_dbs `unionBags` ((bind, b_fvs) `consBag` body_dbs)
- , calls = all_calls
- , ud_fvs = all_fvs `unionVarSet` b_fvs })
-
- | otherwise -- General case: make a huge Rec (sigh)
- = ([], MkUD { dict_binds = unitBag (Rec all_db_prs, all_db_fvs)
- , calls = all_calls
- , ud_fvs = all_fvs `unionVarSet` b_fvs })
- where
- all_fvs = rhs_fvs `unionVarSet` body_fvs
- all_calls = zapCalls bndrs (rhs_calls `unionCalls` body_calls)
+-- Returned UsageDetails:
+-- No calls for binders of this bind
+specBind rhs_subst (NonRec fn rhs) body_uds
+ = do { (rhs', rhs_uds) <- specExpr rhs_subst rhs
+ ; (fn', spec_defns, body_uds1) <- specDefn rhs_subst body_uds fn rhs
- bndrs = bindersOf bind
- b_fvs = bind_fvs bind
+ ; let pairs = spec_defns ++ [(fn', rhs')]
+ -- fn' mentions the spec_defns in its rules,
+ -- so put the latter first
- (all_db_prs, all_db_fvs) = add (bind, b_fvs) $
- foldrBag add ([], emptyVarSet) $
- rhs_dbs `unionBags` body_dbs
- add (NonRec b r, b_fvs) (prs, fvs) = ((b,r) : prs, b_fvs `unionVarSet` fvs)
- add (Rec b_prs, b_fvs) (prs, fvs) = (b_prs ++ prs, b_fvs `unionVarSet` fvs)
+ combined_uds = body_uds1 `plusUDs` rhs_uds
+ -- This way round a call in rhs_uds of a function f
+ -- at type T will override a call of f at T in body_uds1; and
+ -- that is good because it'll tend to keep "earlier" calls
+ -- See Note [Specialisation of dictionary functions]
----------------------------
-specBindItself :: Subst -> CoreBind -> CallDetails -> SpecM (CoreBind, UsageDetails)
-
--- specBindItself deals with the RHS, specialising it according
--- to the calls found in the body (if any)
-specBindItself rhs_subst (NonRec fn rhs) call_info
- = do { (rhs', rhs_uds) <- specExpr rhs_subst rhs -- Do RHS of original fn
- ; (fn', spec_defns, spec_uds) <- specDefn rhs_subst call_info fn rhs
- ; if null spec_defns then
- return (NonRec fn rhs', rhs_uds)
- else
- return (Rec ((fn',rhs') : spec_defns), rhs_uds `plusUDs` spec_uds) }
- -- bndr' mentions the spec_defns in its SpecEnv
- -- Not sure why we couln't just put the spec_defns first
-
-specBindItself rhs_subst (Rec pairs) call_info
+ (free_uds, dump_dbs, float_all) = dumpBindUDs [fn] combined_uds
+ -- See Note [From non-recursive to recursive]
+
+ final_binds | isEmptyBag dump_dbs = [NonRec b r | (b,r) <- pairs]
+ | otherwise = [Rec (flattenDictBinds dump_dbs pairs)]
+
+ ; if float_all then
+ -- Rather than discard the calls mentioning the bound variables
+ -- we float this binding along with the others
+ return ([], free_uds `snocDictBinds` final_binds)
+ else
+ -- No call in final_uds mentions bound variables,
+ -- so we can just leave the binding here
+ return (final_binds, free_uds) }
+
+
+specBind rhs_subst (Rec pairs) body_uds
-- Note [Specialising a recursive group]
= do { let (bndrs,rhss) = unzip pairs
; (rhss', rhs_uds) <- mapAndCombineSM (specExpr rhs_subst) rhss
- ; let all_calls = call_info `unionCalls` calls rhs_uds
- ; (bndrs1, spec_defns1, spec_uds1) <- specDefns rhs_subst all_calls pairs
-
- ; if null spec_defns1 then -- Common case: no specialisation
- return (Rec (bndrs `zip` rhss'), rhs_uds)
- else do -- Specialisation occurred; do it again
- { (bndrs2, spec_defns2, spec_uds2) <-
- -- pprTrace "specB" (ppr bndrs $$ ppr rhs_uds) $
- specDefns rhs_subst (calls spec_uds1) (bndrs1 `zip` rhss)
-
- ; let all_defns = spec_defns1 ++ spec_defns2 ++ zip bndrs2 rhss'
+ ; let scope_uds = body_uds `plusUDs` rhs_uds
+ -- Includes binds and calls arising from rhss
+
+ ; (bndrs1, spec_defns1, uds1) <- specDefns rhs_subst scope_uds pairs
+
+ ; (bndrs3, spec_defns3, uds3)
+ <- if null spec_defns1 -- Common case: no specialisation
+ then return (bndrs1, [], uds1)
+ else do { -- Specialisation occurred; do it again
+ (bndrs2, spec_defns2, uds2)
+ <- specDefns rhs_subst uds1 (bndrs1 `zip` rhss)
+ ; return (bndrs2, spec_defns2 ++ spec_defns1, uds2) }
+
+ ; let (final_uds, dumped_dbs, float_all) = dumpBindUDs bndrs uds3
+ bind = Rec (flattenDictBinds dumped_dbs $
+ spec_defns3 ++ zip bndrs3 rhss')
- ; return (Rec all_defns, rhs_uds `plusUDs` spec_uds1 `plusUDs` spec_uds2) } }
+ ; if float_all then
+ return ([], final_uds `snocDictBind` bind)
+ else
+ return ([bind], final_uds) }
---------------------------
specDefns :: Subst
- -> CallDetails -- Info on how it is used in its scope
- -> [(Id,CoreExpr)] -- The things being bound and their un-processed RHS
- -> SpecM ([Id], -- Original Ids with RULES added
- [(Id,CoreExpr)], -- Extra, specialised bindings
- UsageDetails) -- Stuff to fling upwards from the specialised versions
+ -> UsageDetails -- Info on how it is used in its scope
+ -> [(Id,CoreExpr)] -- The things being bound and their un-processed RHS
+ -> SpecM ([Id], -- Original Ids with RULES added
+ [(Id,CoreExpr)], -- Extra, specialised bindings
+ UsageDetails) -- Stuff to fling upwards from the specialised versions
-- Specialise a list of bindings (the contents of a Rec), but flowing usages
-- upwards binding by binding. Example: { f = ...g ...; g = ...f .... }
-- in turn generates a specialised call for 'f', we catch that in this one sweep.
-- But not vice versa (it's a fixpoint problem).
-specDefns _subst _call_info []
- = return ([], [], emptyUDs)
-specDefns subst call_info ((bndr,rhs):pairs)
- = do { (bndrs', spec_defns, spec_uds) <- specDefns subst call_info pairs
- ; let all_calls = call_info `unionCalls` calls spec_uds
- ; (bndr', spec_defns1, spec_uds1) <- specDefn subst all_calls bndr rhs
- ; return (bndr' : bndrs',
- spec_defns1 ++ spec_defns,
- spec_uds1 `plusUDs` spec_uds) }
+specDefns _subst uds []
+ = return ([], [], uds)
+specDefns subst uds ((bndr,rhs):pairs)
+ = do { (bndrs1, spec_defns1, uds1) <- specDefns subst uds pairs
+ ; (bndr1, spec_defns2, uds2) <- specDefn subst uds1 bndr rhs
+ ; return (bndr1 : bndrs1, spec_defns1 ++ spec_defns2, uds2) }
---------------------------
specDefn :: Subst
- -> CallDetails -- Info on how it is used in its scope
+ -> UsageDetails -- Info on how it is used in its scope
-> Id -> CoreExpr -- The thing being bound and its un-processed RHS
-> SpecM (Id, -- Original Id with added RULES
[(Id,CoreExpr)], -- Extra, specialised bindings
UsageDetails) -- Stuff to fling upwards from the specialised versions
-specDefn subst calls fn rhs
+specDefn subst body_uds fn rhs
-- The first case is the interesting one
| rhs_tyvars `lengthIs` n_tyvars -- Rhs of fn's defn has right number of big lambdas
&& rhs_ids `lengthAtLeast` n_dicts -- and enough dict args
&& notNull calls_for_me -- And there are some calls to specialise
+ && not (isNeverActive (idInlineActivation fn))
+ -- Don't specialise NOINLINE things
+ -- See Note [Auto-specialisation and RULES]
-- && not (certainlyWillInline (idUnfolding fn)) -- And it's not small
-- See Note [Inline specialisation] for why we do not
-- switch off specialisation for inline functions
- = do { -- Make a specialised version for each call in calls_for_me
+ = -- pprTrace "specDefn: some" (ppr fn $$ ppr calls_for_me) $
+ do { -- Make a specialised version for each call in calls_for_me
stuff <- mapM spec_call calls_for_me
; let (spec_defns, spec_uds, spec_rules) = unzip3 (catMaybes stuff)
fn' = addIdSpecialisations fn spec_rules
- ; return (fn', spec_defns, plusUDList spec_uds) }
+ final_uds = body_uds_without_me `plusUDs` plusUDList spec_uds
+ -- It's important that the `plusUDs` is this way
+ -- round, because body_uds_without_me may bind
+ -- dictionaries that are used in calls_for_me passed
+ -- to specDefn. So the dictionary bindings in
+ -- spec_uds may mention dictionaries bound in
+ -- body_uds_without_me
+
+ ; return (fn', spec_defns, final_uds) }
| otherwise -- No calls or RHS doesn't fit our preconceptions
= WARN( notNull calls_for_me, ptext (sLit "Missed specialisation opportunity for") <+> ppr fn )
-- Note [Specialisation shape]
- return (fn, [], emptyUDs)
+ -- pprTrace "specDefn: none" (ppr fn $$ ppr calls_for_me) $
+ return (fn, [], body_uds_without_me)
where
fn_type = idType fn
+ fn_arity = idArity fn
+ fn_unf = realIdUnfolding fn -- Ignore loop-breaker-ness here
(tyvars, theta, _) = tcSplitSigmaTy fn_type
n_tyvars = length tyvars
n_dicts = length theta
- inline_prag = idInlinePragma fn
+ inl_act = inlinePragmaActivation (idInlinePragma fn)
-- Figure out whether the function has an INLINE pragma
-- See Note [Inline specialisations]
- fn_has_inline_rule :: Maybe Arity -- Gives arity of the *specialised* inline rule
- fn_has_inline_rule = case idUnfolding fn of
- InlineRule { uf_arity = arity } -> Just (arity - n_dicts)
- _other -> Nothing
+ fn_has_inline_rule :: Maybe Bool -- Derive sat-flag from existing thing
+ fn_has_inline_rule = case isInlineRule_maybe fn_unf of
+ Just (_,sat) -> Just sat
+ Nothing -> Nothing
+
+ spec_arity = unfoldingArity fn_unf - n_dicts -- Arity of the *specialised* inline rule
(rhs_tyvars, rhs_ids, rhs_body) = collectTyAndValBinders rhs
+ (body_uds_without_me, calls_for_me) = callsForMe fn body_uds
+
rhs_dict_ids = take n_dicts rhs_ids
body = mkLams (drop n_dicts rhs_ids) rhs_body
-- Glue back on the non-dict lambdas
- calls_for_me = case lookupFM calls fn of
- Nothing -> []
- Just cs -> fmToList cs
-
already_covered :: [CoreExpr] -> Bool
already_covered args -- Note [Specialisations already covered]
- = isJust (lookupRule (const True) (substInScope subst)
+ = isJust (lookupRule (const True) realIdUnfolding
+ (substInScope subst)
fn args (idCoreRules fn))
mk_ty_args :: [Maybe Type] -> [CoreExpr]
----------------------------------------------------------
-- Specialise to one particular call pattern
- spec_call :: (CallKey, ([DictExpr], VarSet)) -- Call instance
+ spec_call :: CallInfo -- Call instance
-> SpecM (Maybe ((Id,CoreExpr), -- Specialised definition
UsageDetails, -- Usage details from specialised body
CoreRule)) -- Info for the Id's SpecEnv
spec_tv_binds = [(tv,ty) | (tv, Just ty) <- rhs_tyvars `zip` call_ts]
spec_ty_args = map snd spec_tv_binds
ty_args = mk_ty_args call_ts
- rhs_subst = extendTvSubstList subst spec_tv_binds
+ rhs_subst = CoreSubst.extendTvSubstList subst spec_tv_binds
- ; (rhs_subst1, inst_dict_ids) <- cloneDictBndrs rhs_subst rhs_dict_ids
+ ; (rhs_subst1, inst_dict_ids) <- newDictBndrs rhs_subst rhs_dict_ids
-- Clone rhs_dicts, including instantiating their types
; let (rhs_subst2, dx_binds) = bindAuxiliaryDicts rhs_subst1 $
rule_name = mkFastString ("SPEC " ++ showSDoc (ppr fn <+> ppr spec_ty_args))
spec_env_rule = mkLocalRule
rule_name
- inline_prag -- Note [Auto-specialisation and RULES]
+ inl_act -- Note [Auto-specialisation and RULES]
(idName fn)
(poly_tyvars ++ inst_dict_ids)
inst_args
(mkVarApps (Var spec_f) app_args)
-- Add the { d1' = dx1; d2' = dx2 } usage stuff
- final_uds = foldr addDictBind rhs_uds dx_binds
+ final_uds = foldr consDictBind rhs_uds dx_binds
+
+ -- Adding arity information just propagates it a bit faster
+ -- See Note [Arity decrease] in Simplify
+ -- Copy InlinePragma information from the parent Id.
+ -- So if f has INLINE[1] so does spec_f
+ spec_f_w_arity = spec_f `setIdArity` max 0 (fn_arity - n_dicts)
+ `setInlineActivation` inl_act
+ -- Add an InlineRule if the parent has one
-- See Note [Inline specialisations]
- final_spec_f | Just spec_arity <- fn_has_inline_rule
- = spec_f `setInlinePragma` inline_prag
- `setIdUnfolding` mkInlineRule spec_rhs spec_arity
- | otherwise
- = spec_f
+ final_spec_f
+ | Just sat <- fn_has_inline_rule
+ = let
+ mb_spec_arity = if sat then Just spec_arity else Nothing
+ in
+ spec_f_w_arity `setIdUnfolding` mkInlineRule spec_rhs mb_spec_arity
+ | otherwise
+ = spec_f_w_arity
+
; return (Just ((final_spec_f, spec_rhs), final_uds, spec_env_rule)) } }
where
my_zipEqual xs ys zs
:: Subst
-> [(DictId,DictId,CoreExpr)] -- (orig_dict, inst_dict, dx)
-> (Subst, -- Substitute for all orig_dicts
- [(DictId, CoreExpr)]) -- Auxiliary bindings
+ [CoreBind]) -- Auxiliary bindings
-- Bind any dictionary arguments to fresh names, to preserve sharing
-- Substitution already substitutes orig_dict -> inst_dict
bindAuxiliaryDicts subst triples = go subst [] triples
go subst binds ((d, dx_id, dx) : pairs)
| exprIsTrivial dx = go (extendIdSubst subst d dx) binds pairs
-- No auxiliary binding necessary
- | otherwise = go subst_w_unf ((dx_id,dx) : binds) pairs
+ -- Note that we bind the *original* dict in the substitution,
+ -- overriding any d->dx_id binding put there by substBndrs
+
+ | otherwise = go subst_w_unf (NonRec dx_id dx : binds) pairs
where
- dx_id1 = dx_id `setIdUnfolding` mkUnfolding False dx
+ dx_id1 = dx_id `setIdUnfolding` mkUnfolding False False dx
subst_w_unf = extendIdSubst subst d (Var dx_id1)
-- Important! We're going to substitute dx_id1 for d
-- and we want it to look "interesting", else we won't gather *any*
-- a consequent call (g d') with an auxiliary definition
-- d' = df dNumInt
-- We want that consequent call to look interesting
+ --
+ -- Again, note that we bind the *original* dict in the substitution,
+ -- overriding any d->dx_id binding put there by substBndrs
\end{code}
+Note [From non-recursive to recursive]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Even in the non-recursive case, if any dict-binds depend on 'fn' we might
+have built a recursive knot
+
+ f a d x = <blah>
+ MkUD { ud_binds = d7 = MkD ..f..
+ , ud_calls = ...(f T d7)... }
+
+The we generate
+
+ Rec { fs x = <blah>[T/a, d7/d]
+ f a d x = <blah>
+ RULE f T _ = fs
+ d7 = ...f... }
+
+Here the recursion is only through the RULE.
+
+
+Note [Specialisation of dictionary functions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Here is a nasty example that bit us badly: see Trac #3591
+
+ dfun a d = MkD a d (meth d)
+ d4 = <blah>
+ d2 = dfun T d4
+ d1 = $p1 d2
+ d3 = dfun T d1
+
+None of these definitions is recursive. What happened was that we
+generated a specialisation:
+
+ RULE forall d. dfun T d = dT
+ dT = (MkD a d (meth d)) [T/a, d1/d]
+ = MkD T d1 (meth d1)
+
+But now we use the RULE on the RHS of d2, to get
+
+ d2 = dT = MkD d1 (meth d1)
+ d1 = $p1 d2
+
+and now d1 is bottom! The problem is that when specialising 'dfun' we
+should first dump "below" the binding all floated dictionary bindings
+that mention 'dfun' itself. So d2 and d3 (and hence d1) must be
+placed below 'dfun', and thus unavailable to it when specialising
+'dfun'. That in turn means that the call (dfun T d1) must be
+discarded. On the other hand, the call (dfun T d4) is fine, assuming
+d4 doesn't mention dfun.
+
+But look at this:
+
+ class C a where { foo,bar :: [a] -> [a] }
+
+ instance C Int where
+ foo x = r_bar x
+ bar xs = reverse xs
+
+ r_bar :: C a => [a] -> [a]
+ r_bar xs = bar (xs ++ xs)
+
+That translates to:
+
+ r_bar a (c::C a) (xs::[a]) = bar a d (xs ++ xs)
+
+ Rec { $fCInt :: C Int = MkC foo_help reverse
+ foo_help (xs::[Int]) = r_bar Int $fCInt xs }
+
+The call (r_bar $fCInt) mentions $fCInt,
+ which mentions foo_help,
+ which mentions r_bar
+But we DO want to specialise r_bar at Int:
+
+ Rec { $fCInt :: C Int = MkC foo_help reverse
+ foo_help (xs::[Int]) = r_bar Int $fCInt xs
+
+ r_bar a (c::C a) (xs::[a]) = bar a d (xs ++ xs)
+ RULE r_bar Int _ = r_bar_Int
+
+ r_bar_Int xs = bar Int $fCInt (xs ++ xs)
+ }
+
+Note that, because of its RULE, r_bar joins the recursive
+group. (In this case it'll unravel a short moment later.)
+
+
+Conclusion: we catch the nasty case using filter_dfuns in
+callsForMe To be honest I'm not 100% certain that this is 100%
+right, but it works. Sigh.
+
+
Note [Specialising a recursive group]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider
RULE f g_spec = 0
But that's a bit complicated. For now we ask the programmer's help,
-by *copying the INLINE activation pragma* to the auto-specialised rule.
-So if g says {-# NOINLINE[2] g #-}, then the auto-spec rule will also
-not be active until phase 2.
+by *copying the INLINE activation pragma* to the auto-specialised
+rule. So if g says {-# NOINLINE[2] g #-}, then the auto-spec rule
+will also not be active until phase 2. And that's what programmers
+should jolly well do anyway, even aside from specialisation, to ensure
+that g doesn't inline too early.
+This in turn means that the RULE would never fire for a NOINLINE
+thing so not much point in generating a specialisation at all.
Note [Specialisation shape]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
where choose doesn't have any dict arguments. Thus far I have not
tried to fix this (wait till there's a real example).
-
Note [Inline specialisations]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We transfer to the specialised function any INLINE stuff from the
-original. This means (a) the Activation in the IdInfo, and (b) any
-InlineMe on the RHS.
+original. This means
+ (a) the Activation for its inlining (from its InlinePragma)
+ (b) any InlineRule
This is a change (Jun06). Previously the idea is that the point of
inlining was precisely to specialise the function at its call site,
boring to trigger inlining), and it's certainly better to call the
specialised version.
-A case in point is dictionary functions, which are current marked
-INLINE, but which are worth specialising.
-
%************************************************************************
%* *
\begin{code}
data UsageDetails
= MkUD {
- dict_binds :: !(Bag DictBind),
+ ud_binds :: !(Bag DictBind),
-- Floated dictionary bindings
-- The order is important;
-- in ds1 `union` ds2, bindings in ds2 can depend on those in ds1
-- (Remember, Bags preserve order in GHC.)
- calls :: !CallDetails,
+ ud_calls :: !CallDetails
- ud_fvs :: !VarSet -- A superset of the variables mentioned in
- -- either dict_binds or calls
+ -- INVARIANT: suppose bs = bindersOf ud_binds
+ -- Then 'calls' may *mention* 'bs',
+ -- but there should be no calls *for* bs
}
instance Outputable UsageDetails where
- ppr (MkUD { dict_binds = dbs, calls = calls, ud_fvs = fvs })
+ ppr (MkUD { ud_binds = dbs, ud_calls = calls })
= ptext (sLit "MkUD") <+> braces (sep (punctuate comma
[ptext (sLit "binds") <+> equals <+> ppr dbs,
- ptext (sLit "calls") <+> equals <+> ppr calls,
- ptext (sLit "fvs") <+> equals <+> ppr fvs]))
+ ptext (sLit "calls") <+> equals <+> ppr calls]))
type DictBind = (CoreBind, VarSet)
-- The set is the free vars of the binding
type DictExpr = CoreExpr
emptyUDs :: UsageDetails
-emptyUDs = MkUD { dict_binds = emptyBag, calls = emptyFM, ud_fvs = emptyVarSet }
+emptyUDs = MkUD { ud_binds = emptyBag, ud_calls = emptyVarEnv }
------------------------------------------------------------
-type CallDetails = FiniteMap Id CallInfo
+type CallDetails = IdEnv CallInfoSet
newtype CallKey = CallKey [Maybe Type] -- Nothing => unconstrained type argument
-- CallInfo uses a FiniteMap, thereby ensuring that
--
-- The list of types and dictionaries is guaranteed to
-- match the type of f
-type CallInfo = FiniteMap CallKey ([DictExpr], VarSet)
+type CallInfoSet = FiniteMap CallKey ([DictExpr], VarSet)
-- Range is dict args and the vars of the whole
-- call (including tyvars)
-- [*not* include the main id itself, of course]
+type CallInfo = (CallKey, ([DictExpr], VarSet))
+
instance Outputable CallKey where
ppr (CallKey ts) = ppr ts
cmp (Just t1) (Just t2) = tcCmpType t1 t2
unionCalls :: CallDetails -> CallDetails -> CallDetails
-unionCalls c1 c2 = plusFM_C plusFM c1 c2
+unionCalls c1 c2 = plusVarEnv_C plusFM c1 c2
+
+-- plusCalls :: UsageDetails -> CallDetails -> UsageDetails
+-- plusCalls uds call_ds = uds { ud_calls = ud_calls uds `unionCalls` call_ds }
+
+callDetailsFVs :: CallDetails -> VarSet
+callDetailsFVs calls = foldVarEnv (unionVarSet . callInfoFVs) emptyVarSet calls
+callInfoFVs :: CallInfoSet -> VarSet
+callInfoFVs call_info = foldFM (\_ (_,fv) vs -> unionVarSet fv vs) emptyVarSet call_info
+
+------------------------------------------------------------
singleCall :: Id -> [Maybe Type] -> [DictExpr] -> UsageDetails
singleCall id tys dicts
- = MkUD {dict_binds = emptyBag,
- calls = unitFM id (unitFM (CallKey tys) (dicts, call_fvs)),
- ud_fvs = call_fvs }
+ = MkUD {ud_binds = emptyBag,
+ ud_calls = unitVarEnv id (unitFM (CallKey tys) (dicts, call_fvs)) }
where
call_fvs = exprsFreeVars dicts `unionVarSet` tys_fvs
tys_fvs = tyVarsOfTypes (catMaybes tys)
-- *don't* say what the value of the implicit param is!
|| not (spec_tys `lengthIs` n_tyvars)
|| not ( dicts `lengthIs` n_dicts)
- || not (any interestingArg dicts) -- Note [Interesting dictionary arguments]
+ || not (any interestingDict dicts) -- Note [Interesting dictionary arguments]
-- See also Note [Specialisations already covered]
- = -- pprTrace "mkCallUDs: discarding" (vcat [ppr f, ppr args, ppr n_tyvars, ppr n_dicts, ppr (map interestingArg dicts)])
+ = -- pprTrace "mkCallUDs: discarding" (vcat [ppr f, ppr args, ppr n_tyvars, ppr n_dicts, ppr (map interestingDict dicts)])
emptyUDs -- Not overloaded, or no specialisation wanted
| otherwise
- = -- pprTrace "mkCallUDs: keeping" (vcat [ppr f, ppr args, ppr n_tyvars, ppr n_dicts, ppr (map interestingArg dicts)])
+ = -- pprTrace "mkCallUDs: keeping" (vcat [ppr f, ppr args, ppr n_tyvars, ppr n_dicts, ppr (map interestingDict dicts)])
singleCall f spec_tys dicts
where
(tyvars, theta, _) = tcSplitSigmaTy (idType f)
because the code for the specialised f is not improved at all, because
d is lambda-bound. We simply get junk specialisations.
-We re-use the function SimplUtils.interestingArg function to determine
-what sort of dictionary arguments have *some* information in them.
+What is "interesting"? Just that it has *some* structure.
+\begin{code}
+interestingDict :: CoreExpr -> Bool
+-- A dictionary argument is interesting if it has *some* structure
+interestingDict (Var v) = hasSomeUnfolding (idUnfolding v)
+ || isDataConWorkId v
+interestingDict (Type _) = False
+interestingDict (App fn (Type _)) = interestingDict fn
+interestingDict (Note _ a) = interestingDict a
+interestingDict (Cast e _) = interestingDict e
+interestingDict _ = True
+\end{code}
\begin{code}
plusUDs :: UsageDetails -> UsageDetails -> UsageDetails
-plusUDs (MkUD {dict_binds = db1, calls = calls1, ud_fvs = fvs1})
- (MkUD {dict_binds = db2, calls = calls2, ud_fvs = fvs2})
- = MkUD {dict_binds = d, calls = c, ud_fvs = fvs1 `unionVarSet` fvs2}
- where
- d = db1 `unionBags` db2
- c = calls1 `unionCalls` calls2
+plusUDs (MkUD {ud_binds = db1, ud_calls = calls1})
+ (MkUD {ud_binds = db2, ud_calls = calls2})
+ = MkUD { ud_binds = db1 `unionBags` db2
+ , ud_calls = calls1 `unionCalls` calls2 }
plusUDList :: [UsageDetails] -> UsageDetails
plusUDList = foldr plusUDs emptyUDs
--- zapCalls deletes calls to ids from uds
-zapCalls :: [Id] -> CallDetails -> CallDetails
-zapCalls ids calls = delListFromFM calls ids
+-----------------------------
+_dictBindBndrs :: Bag DictBind -> [Id]
+_dictBindBndrs dbs = foldrBag ((++) . bindersOf . fst) [] dbs
mkDB :: CoreBind -> DictBind
mkDB bind = (bind, bind_fvs bind)
-- type T a = Int
-- x :: T a = 3
-addDictBind :: (Id,CoreExpr) -> UsageDetails -> UsageDetails
-addDictBind (dict,rhs) uds
- = uds { dict_binds = db `consBag` dict_binds uds
- , ud_fvs = ud_fvs uds `unionVarSet` fvs }
+flattenDictBinds :: Bag DictBind -> [(Id,CoreExpr)] -> [(Id,CoreExpr)]
+flattenDictBinds dbs pairs
+ = foldrBag add pairs dbs
where
- db@(_, fvs) = mkDB (NonRec dict rhs)
+ add (NonRec b r,_) pairs = (b,r) : pairs
+ add (Rec prs1, _) pairs = prs1 ++ pairs
+
+snocDictBinds :: UsageDetails -> [CoreBind] -> UsageDetails
+-- Add ud_binds to the tail end of the bindings in uds
+snocDictBinds uds dbs
+ = uds { ud_binds = ud_binds uds `unionBags`
+ foldr (consBag . mkDB) emptyBag dbs }
-dumpAllDictBinds :: UsageDetails -> [CoreBind] -> [CoreBind]
-dumpAllDictBinds (MkUD {dict_binds = dbs}) binds
+consDictBind :: CoreBind -> UsageDetails -> UsageDetails
+consDictBind bind uds = uds { ud_binds = mkDB bind `consBag` ud_binds uds }
+
+addDictBinds :: [DictBind] -> UsageDetails -> UsageDetails
+addDictBinds binds uds = uds { ud_binds = listToBag binds `unionBags` ud_binds uds }
+
+snocDictBind :: UsageDetails -> CoreBind -> UsageDetails
+snocDictBind uds bind = uds { ud_binds = ud_binds uds `snocBag` mkDB bind }
+
+wrapDictBinds :: Bag DictBind -> [CoreBind] -> [CoreBind]
+wrapDictBinds dbs binds
= foldrBag add binds dbs
where
add (bind,_) binds = bind : binds
-dumpUDs :: [CoreBndr]
- -> UsageDetails -> CoreExpr
- -> (UsageDetails, CoreExpr)
-dumpUDs bndrs (MkUD { dict_binds = orig_dbs
- , calls = orig_calls
- , ud_fvs = fvs}) body
- = (new_uds, foldrBag add_let body dump_dbs)
- -- This may delete fewer variables
- -- than in priciple possible
+wrapDictBindsE :: Bag DictBind -> CoreExpr -> CoreExpr
+wrapDictBindsE dbs expr
+ = foldrBag add expr dbs
where
- new_uds =
- MkUD { dict_binds = free_dbs
- , calls = free_calls
- , ud_fvs = fvs `minusVarSet` bndr_set}
-
+ add (bind,_) expr = Let bind expr
+
+----------------------
+dumpUDs :: [CoreBndr] -> UsageDetails -> (UsageDetails, Bag DictBind)
+-- Used at a lambda or case binder; just dump anything mentioning the binder
+dumpUDs bndrs uds@(MkUD { ud_binds = orig_dbs, ud_calls = orig_calls })
+ | null bndrs = (uds, emptyBag) -- Common in case alternatives
+ | otherwise = -- pprTrace "dumpUDs" (ppr bndrs $$ ppr free_uds $$ ppr dump_dbs) $
+ (free_uds, dump_dbs)
+ where
+ free_uds = MkUD { ud_binds = free_dbs, ud_calls = free_calls }
bndr_set = mkVarSet bndrs
- add_let (bind,_) body = Let bind body
+ (free_dbs, dump_dbs, dump_set) = splitDictBinds orig_dbs bndr_set
+ free_calls = deleteCallsMentioning dump_set $ -- Drop calls mentioning bndr_set on the floor
+ deleteCallsFor bndrs orig_calls -- Discard calls for bndr_set; there should be
+ -- no calls for any of the dicts in dump_dbs
+
+dumpBindUDs :: [CoreBndr] -> UsageDetails -> (UsageDetails, Bag DictBind, Bool)
+-- Used at a lambda or case binder; just dump anything mentioning the binder
+dumpBindUDs bndrs (MkUD { ud_binds = orig_dbs, ud_calls = orig_calls })
+ = -- pprTrace "dumpBindUDs" (ppr bndrs $$ ppr free_uds $$ ppr dump_dbs) $
+ (free_uds, dump_dbs, float_all)
+ where
+ free_uds = MkUD { ud_binds = free_dbs, ud_calls = free_calls }
+ bndr_set = mkVarSet bndrs
+ (free_dbs, dump_dbs, dump_set) = splitDictBinds orig_dbs bndr_set
+ free_calls = deleteCallsFor bndrs orig_calls
+ float_all = dump_set `intersectsVarSet` callDetailsFVs free_calls
+
+callsForMe :: Id -> UsageDetails -> (UsageDetails, [CallInfo])
+callsForMe fn (MkUD { ud_binds = orig_dbs, ud_calls = orig_calls })
+ = -- pprTrace ("callsForMe")
+ -- (vcat [ppr fn,
+ -- text "Orig dbs =" <+> ppr (_dictBindBndrs orig_dbs),
+ -- text "Orig calls =" <+> ppr orig_calls,
+ -- text "Dep set =" <+> ppr dep_set,
+ -- text "Calls for me =" <+> ppr calls_for_me]) $
+ (uds_without_me, calls_for_me)
+ where
+ uds_without_me = MkUD { ud_binds = orig_dbs, ud_calls = delVarEnv orig_calls fn }
+ calls_for_me = case lookupVarEnv orig_calls fn of
+ Nothing -> []
+ Just cs -> filter_dfuns (fmToList cs)
- (free_dbs, dump_dbs, dump_set)
- = foldlBag dump_db (emptyBag, emptyBag, bndr_set) orig_dbs
- -- Important that it's foldl not foldr;
- -- we're accumulating the set of dumped ids in dump_set
+ dep_set = foldlBag go (unitVarSet fn) orig_dbs
+ go dep_set (db,fvs) | fvs `intersectsVarSet` dep_set
+ = extendVarSetList dep_set (bindersOf db)
+ | otherwise = dep_set
+
+ -- Note [Specialisation of dictionary functions]
+ filter_dfuns | isDFunId fn = filter ok_call
+ | otherwise = \cs -> cs
- free_calls = filterCalls dump_set orig_calls
+ ok_call (_, (_,fvs)) = not (fvs `intersectsVarSet` dep_set)
- dump_db (free_dbs, dump_dbs, dump_idset) db@(bind, fvs)
+----------------------
+splitDictBinds :: Bag DictBind -> IdSet -> (Bag DictBind, Bag DictBind, IdSet)
+-- Returns (free_dbs, dump_dbs, dump_set)
+splitDictBinds dbs bndr_set
+ = foldlBag split_db (emptyBag, emptyBag, bndr_set) dbs
+ -- Important that it's foldl not foldr;
+ -- we're accumulating the set of dumped ids in dump_set
+ where
+ split_db (free_dbs, dump_dbs, dump_idset) db@(bind, fvs)
| dump_idset `intersectsVarSet` fvs -- Dump it
= (free_dbs, dump_dbs `snocBag` db,
extendVarSetList dump_idset (bindersOf bind))
| otherwise -- Don't dump it
= (free_dbs `snocBag` db, dump_dbs, dump_idset)
-filterCalls :: VarSet -> CallDetails -> CallDetails
--- Remove any calls that mention the variables
-filterCalls bs calls
- = mapFM (\_ cs -> filter_calls cs) $
- filterFM (\k _ -> not (k `elemVarSet` bs)) calls
+
+----------------------
+deleteCallsMentioning :: VarSet -> CallDetails -> CallDetails
+-- Remove calls *mentioning* bs
+deleteCallsMentioning bs calls
+ = mapVarEnv filter_calls calls
where
- filter_calls :: CallInfo -> CallInfo
+ filter_calls :: CallInfoSet -> CallInfoSet
filter_calls = filterFM (\_ (_, fvs) -> not (fvs `intersectsVarSet` bs))
+
+deleteCallsFor :: [Id] -> CallDetails -> CallDetails
+-- Remove calls *for* bs
+deleteCallsFor bs calls = delVarEnvList calls bs
\end{code}
let (subst', bndrs') = cloneRecIdBndrs subst us (map fst pairs)
return (subst', subst', Rec (bndrs' `zip` map snd pairs))
-cloneDictBndrs :: Subst -> [CoreBndr] -> SpecM (Subst, [CoreBndr])
-cloneDictBndrs subst bndrs
- = do { us <- getUniqueSupplyM
- ; return (cloneIdBndrs subst us bndrs) }
+newDictBndrs :: Subst -> [CoreBndr] -> SpecM (Subst, [CoreBndr])
+-- Make up completely fresh binders for the dictionaries
+-- Their bindings are going to float outwards
+newDictBndrs subst bndrs
+ = do { bndrs' <- mapM new bndrs
+ ; let subst' = extendIdSubstList subst
+ [(d, Var d') | (d,d') <- bndrs `zip` bndrs']
+ ; return (subst', bndrs' ) }
+ where
+ new b = do { uniq <- getUniqueM
+ ; let n = idName b
+ ty' = CoreSubst.substTy subst (idType b)
+ ; return (mkUserLocal (nameOccName n) uniq ty' (getSrcSpan n)) }
newSpecIdSM :: Id -> Type -> SpecM Id
-- Give the new Id a similar occurrence name to the old one
newSpecIdSM old_id new_ty
= do { uniq <- getUniqueM
- ; let
- name = idName old_id
- new_occ = mkSpecOcc (nameOccName name)
- new_id = mkUserLocal new_occ uniq new_ty (getSrcSpan name)
+ ; let name = idName old_id
+ new_occ = mkSpecOcc (nameOccName name)
+ new_id = mkUserLocal new_occ uniq new_ty (getSrcSpan name)
; return new_id }
\end{code}