summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <bgamari.foss@gmail.com>2015-07-06 10:46:21 +0200
committerBen Gamari <ben@smart-cactus.org>2015-07-06 11:27:06 +0200
commitf7ede672aa70bfc90e10f5fdc0d0941e251fd934 (patch)
tree325e18be6bb1969a7878acf191319aa58b4fae64
parent93790bbc33c2568d442426d15e8f99ea4fe45217 (diff)
downloadhaskell-f7ede672aa70bfc90e10f5fdc0d0941e251fd934.tar.gz
Specialise: Avoid unnecessary recomputation of free variable information
When examining compile times for code with large ADTs (particularly those with many record constructors), I found that the specialiser contributed disproportionately to the compiler runtime. Some profiling suggested that the a great deal of time was being spent in `pair_fvs` being called from `consDictBind`. @simonpj pointed out that `flattenDictBinds` as called by `specBind` was unnecessarily discarding cached free variable information, which then needed to be recomputed by `pair_fvs`. Here I refactor the specializer to retain the free variable cache whenever possible. **Open Qustions** * I used `fst` in a couple of places to extract the bindings from a `DictBind`. Perhaps this is a sign that `DictBind` has outgrown its type synonym status? Test Plan: validate Reviewers: austin, simonpj Reviewed By: simonpj Subscribers: thomie, bgamari, simonpj Differential Revision: https://phabricator.haskell.org/D1012 GHC Trac Issues: #7450
-rw-r--r--compiler/specialise/Specialise.hs56
1 files changed, 34 insertions, 22 deletions
diff --git a/compiler/specialise/Specialise.hs b/compiler/specialise/Specialise.hs
index de1bf08a31..473f86ffbc 100644
--- a/compiler/specialise/Specialise.hs
+++ b/compiler/specialise/Specialise.hs
@@ -970,8 +970,10 @@ specBind rhs_env (NonRec fn rhs) body_uds
(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)]
+ final_binds :: [DictBind]
+ final_binds
+ | isEmptyBag dump_dbs = [mkDB $ NonRec b r | (b,r) <- pairs]
+ | otherwise = [flattenDictBinds dump_dbs pairs]
; if float_all then
-- Rather than discard the calls mentioning the bound variables
@@ -980,7 +982,7 @@ specBind rhs_env (NonRec fn rhs) body_uds
else
-- No call in final_uds mentions bound variables,
-- so we can just leave the binding here
- return (final_binds, free_uds) }
+ return (map fst final_binds, free_uds) }
specBind rhs_env (Rec pairs) body_uds
@@ -1001,13 +1003,13 @@ specBind rhs_env (Rec pairs) body_uds
; 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')
+ bind = flattenDictBinds dumped_dbs
+ (spec_defns3 ++ zip bndrs3 rhss')
; if float_all then
return ([], final_uds `snocDictBind` bind)
else
- return ([bind], final_uds) }
+ return ([fst bind], final_uds) }
---------------------------
@@ -1245,7 +1247,7 @@ bindAuxiliaryDicts
-> [DictId] -> [CoreExpr] -- Original dict bndrs, and the witnessing expressions
-> [DictId] -- A cloned dict-id for each dict arg
-> (SpecEnv, -- Substitute for all orig_dicts
- [CoreBind], -- Auxiliary dict bindings
+ [DictBind], -- Auxiliary dict bindings
[CoreExpr]) -- Witnessing expressions (all trivial)
-- Bind any dictionary arguments to fresh names, to preserve sharing
bindAuxiliaryDicts env@(SE { se_subst = subst, se_interesting = interesting })
@@ -1256,14 +1258,15 @@ bindAuxiliaryDicts env@(SE { se_subst = subst, se_interesting = interesting })
env' = env { se_subst = CoreSubst.extendIdSubstList subst (orig_dict_ids `zip` spec_dict_args)
, se_interesting = interesting `unionVarSet` interesting_dicts }
- interesting_dicts = mkVarSet [ dx_id | NonRec dx_id dx <- dx_binds
+ interesting_dicts = mkVarSet [ dx_id | (NonRec dx_id dx, _) <- dx_binds
, interestingDict env dx ]
-- See Note [Make the new dictionaries interesting]
+ go :: [CoreExpr] -> [CoreBndr] -> ([DictBind], [CoreExpr])
go [] _ = ([], [])
go (dx:dxs) (dx_id:dx_ids)
| exprIsTrivial dx = (dx_binds, dx:args)
- | otherwise = (NonRec dx_id dx : dx_binds, Var dx_id : args)
+ | otherwise = (mkDB (NonRec dx_id dx) : dx_binds, Var dx_id : args)
where
(dx_binds, args) = go dxs dx_ids
-- In the first case extend the substitution but not bindings;
@@ -1593,9 +1596,9 @@ instance Outputable UsageDetails where
[ptext (sLit "binds") <+> equals <+> ppr dbs,
ptext (sLit "calls") <+> equals <+> ppr calls]))
+-- | A 'DictBind' is a binding along with a cached set containing its free
+-- variables (both type variables and dictionaries)
type DictBind = (CoreBind, VarSet)
- -- The set is the free vars of the binding
- -- both tyvars and dicts
type DictExpr = CoreExpr
@@ -1808,9 +1811,11 @@ plusUDList = foldr plusUDs emptyUDs
_dictBindBndrs :: Bag DictBind -> [Id]
_dictBindBndrs dbs = foldrBag ((++) . bindersOf . fst) [] dbs
+-- | Construct a 'DictBind' from a 'CoreBind'
mkDB :: CoreBind -> DictBind
mkDB bind = (bind, bind_fvs bind)
+-- | Identify the free variables of a 'CoreBind'
bind_fvs :: CoreBind -> VarSet
bind_fvs (NonRec bndr rhs) = pair_fvs (bndr,rhs)
bind_fvs (Rec prs) = foldl delVarSet rhs_fvs bndrs
@@ -1826,27 +1831,34 @@ pair_fvs (bndr, rhs) = exprFreeVars rhs `unionVarSet` idFreeVars bndr
-- type T a = Int
-- x :: T a = 3
-flattenDictBinds :: Bag DictBind -> [(Id,CoreExpr)] -> [(Id,CoreExpr)]
+-- | Flatten a set of 'DictBind's and some other binding pairs into a single
+-- recursive binding, including some additional bindings.
+flattenDictBinds :: Bag DictBind -> [(Id,CoreExpr)] -> DictBind
flattenDictBinds dbs pairs
- = foldrBag add pairs dbs
+ = (Rec bindings, fvs)
where
- add (NonRec b r,_) pairs = (b,r) : pairs
- add (Rec prs1, _) pairs = prs1 ++ pairs
-
-snocDictBinds :: UsageDetails -> [CoreBind] -> UsageDetails
+ (bindings, fvs) = foldrBag add
+ ([], emptyVarSet)
+ (dbs `snocBag` mkDB (Rec pairs))
+ add (NonRec b r, fvs') (pairs, fvs) =
+ ((b,r) : pairs, fvs `unionVarSet` fvs')
+ add (Rec prs1, fvs') (pairs, fvs) =
+ (prs1 ++ pairs, fvs `unionVarSet` fvs')
+
+snocDictBinds :: UsageDetails -> [DictBind] -> 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 }
+ foldr consBag emptyBag dbs }
-consDictBind :: CoreBind -> UsageDetails -> UsageDetails
-consDictBind bind uds = uds { ud_binds = mkDB bind `consBag` ud_binds uds }
+consDictBind :: DictBind -> UsageDetails -> UsageDetails
+consDictBind bind uds = uds { ud_binds = 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 }
+snocDictBind :: UsageDetails -> DictBind -> UsageDetails
+snocDictBind uds bind = uds { ud_binds = ud_binds uds `snocBag` bind }
wrapDictBinds :: Bag DictBind -> [CoreBind] -> [CoreBind]
wrapDictBinds dbs binds