2 % (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
4 \section[SpecConstr]{Specialise over constructors}
11 #include "HsVersions.h"
14 import CoreLint ( showPass, endPass )
15 import CoreUtils ( exprType, exprIsConApp_maybe, eqExpr )
16 import CoreFVs ( exprsFreeVars )
17 import DataCon ( isExistentialDataCon )
18 import PprCore ( pprCoreRules )
19 import Id ( Id, idName, idSpecialisation, mkUserLocal, mkSysLocal )
23 import Name ( nameOccName, nameSrcLoc )
24 import Rules ( addIdSpecialisations )
25 import OccName ( mkSpecOcc )
26 import ErrUtils ( dumpIfSet_dyn )
27 import CmdLineOpts ( DynFlags, DynFlag(..) )
30 import Maybes ( orElse )
31 import Util ( mapAccumL )
32 import List ( nubBy, partition )
37 -----------------------------------------------------
39 -----------------------------------------------------
44 drop n (x:xs) = drop (n-1) xs
46 After the first time round, we could pass n unboxed. This happens in
47 numerical code too. Here's what it looks like in Core:
49 drop n xs = case xs of
54 _ -> drop (I# (n# -# 1#)) xs
56 Notice that the recursive call has an explicit constructor as argument.
57 Noticing this, we can make a specialised version of drop
59 RULE: drop (I# n#) xs ==> drop' n# xs
61 drop' n# xs = let n = I# n# in ...orig RHS...
63 Now the simplifier will apply the specialisation in the rhs of drop', giving
65 drop' n# xs = case xs of
69 _ -> drop (n# -# 1#) xs
73 We'd also like to catch cases where a parameter is carried along unchanged,
74 but evaluated each time round the loop:
76 f i n = if i>0 || i>n then i else f (i*2) n
78 Here f isn't strict in n, but we'd like to avoid evaluating it each iteration.
79 In Core, by the time we've w/wd (f is strict in i) we get
81 f i# n = case i# ># 0 of
83 True -> case n of n' { I# n# ->
86 True -> f (i# *# 2#) n'
88 At the call to f, we see that the argument, n is know to be (I# n#),
89 and n is evaluated elsewhere in the body of f, so we can play the same
90 trick as above. However we don't want to do that if the boxed version
91 of n is needed (else we'd avoid the eval but pay more for re-boxing n).
92 So in this case we want that the *only* uses of n are in case statements.
97 * A self-recursive function. Ignore mutual recursion for now,
98 because it's less common, and the code is simpler for self-recursion.
102 a) At a recursive call, one or more parameters is an explicit
103 constructor application
105 That same parameter is scrutinised by a case somewhere in
106 the RHS of the function
110 b) At a recursive call, one or more parameters has an unfolding
111 that is an explicit constructor application
113 That same parameter is scrutinised by a case somewhere in
114 the RHS of the function
116 Those are the only uses of the parameter
119 There's a bit of a complication with type arguments. If the call
122 f p = ...f ((:) [a] x xs)...
124 then our specialised function look like
126 f_spec x xs = let p = (:) [a] x xs in ....as before....
128 This only makes sense if either
129 a) the type variable 'a' is in scope at the top of f, or
130 b) the type variable 'a' is an argument to f (and hence fs)
132 Actually, (a) may hold for value arguments too, in which case
133 we may not want to pass them. Supose 'x' is in scope at f's
134 defn, but xs is not. Then we'd like
136 f_spec xs = let p = (:) [a] x xs in ....as before....
138 Similarly (b) may hold too. If x is already an argument at the
139 call, no need to pass it again.
141 Finally, if 'a' is not in scope at the call site, we could abstract
142 it as we do the term variables:
144 f_spec a x xs = let p = (:) [a] x xs in ...as before...
146 So the grand plan is:
148 * abstract the call site to a constructor-only pattern
149 e.g. C x (D (f p) (g q)) ==> C s1 (D s2 s3)
151 * Find the free variables of the abstracted pattern
153 * Pass these variables, less any that are in scope at
157 NOTICE that we only abstract over variables that are not in scope,
158 so we're in no danger of shadowing variables used in "higher up"
162 %************************************************************************
164 \subsection{Top level wrapper stuff}
166 %************************************************************************
169 specConstrProgram :: DynFlags -> UniqSupply -> [CoreBind] -> IO [CoreBind]
170 specConstrProgram dflags us binds
172 showPass dflags "SpecConstr"
174 let (binds', _) = initUs us (go emptyScEnv binds)
176 endPass dflags "SpecConstr" Opt_D_dump_spec binds'
178 dumpIfSet_dyn dflags Opt_D_dump_rules "Top-level specialisations"
179 (vcat (map dump_specs (concat (map bindersOf binds'))))
183 go env [] = returnUs []
184 go env (bind:binds) = scBind env bind `thenUs` \ (env', _, bind') ->
185 go env' binds `thenUs` \ binds' ->
186 returnUs (bind' : binds')
188 dump_specs var = pprCoreRules var (idSpecialisation var)
192 %************************************************************************
194 \subsection{Environments and such}
196 %************************************************************************
199 type ScEnv = VarEnv HowBound
201 emptyScEnv = emptyVarEnv
203 data HowBound = RecFun -- These are the recursive functions for which
204 -- we seek interesting call patterns
206 | RecArg -- These are those functions' arguments; we are
207 -- interested to see if those arguments are scrutinised
209 | Other -- We track all others so we know what's in scope
210 -- This is used in spec_one to check what needs to be
211 -- passed as a parameter and what is in scope at the
212 -- function definition site
214 extendBndrs env bndrs = extendVarEnvList env [(b,Other) | b <- bndrs]
215 extendBndr env bndr = extendVarEnv env bndr Other
219 calls :: !(IdEnv ([[CoreArg]])), -- Calls
220 -- The functions are a subset of the
221 -- RecFuns in the ScEnv
223 occs :: !(IdEnv ArgOcc) -- Information on argument occurrences
224 } -- The variables are a subset of the
225 -- RecArg in the ScEnv
227 nullUsage = SCU { calls = emptyVarEnv, occs = emptyVarEnv }
229 combineUsage u1 u2 = SCU { calls = plusVarEnv_C (++) (calls u1) (calls u2),
230 occs = plusVarEnv_C combineOcc (occs u1) (occs u2) }
232 combineUsages [] = nullUsage
233 combineUsages us = foldr1 combineUsage us
235 data ArgOcc = CaseScrut
239 instance Outputable ArgOcc where
240 ppr CaseScrut = ptext SLIT("case-scrut")
241 ppr OtherOcc = ptext SLIT("other-occ")
242 ppr Both = ptext SLIT("case-scrut and other")
244 combineOcc CaseScrut CaseScrut = CaseScrut
245 combineOcc OtherOcc OtherOcc = OtherOcc
246 combineOcc _ _ = Both
250 %************************************************************************
252 \subsection{The main recursive function}
254 %************************************************************************
257 scExpr :: ScEnv -> CoreExpr -> UniqSM (ScUsage, CoreExpr)
258 -- The unique supply is needed when we invent
259 -- a new name for the specialised function and its args
261 scExpr env e@(Type t) = returnUs (nullUsage, e)
262 scExpr env e@(Lit l) = returnUs (nullUsage, e)
263 scExpr env e@(Var v) = returnUs (varUsage env v OtherOcc, e)
264 scExpr env (Note n e) = scExpr env e `thenUs` \ (usg,e') ->
265 returnUs (usg, Note n e')
266 scExpr env (Lam b e) = scExpr (extendBndr env b) e `thenUs` \ (usg,e') ->
267 returnUs (usg, Lam b e')
269 scExpr env (Case scrut b alts)
270 = sc_scrut scrut `thenUs` \ (scrut_usg, scrut') ->
271 mapAndUnzipUs sc_alt alts `thenUs` \ (alts_usgs, alts') ->
272 returnUs (combineUsages alts_usgs `combineUsage` scrut_usg,
275 sc_scrut e@(Var v) = returnUs (varUsage env v CaseScrut, e)
276 sc_scrut e = scExpr env e
278 sc_alt (con,bs,rhs) = scExpr env rhs `thenUs` \ (usg,rhs') ->
279 returnUs (usg, (con,bs,rhs'))
281 env1 = extendBndrs env (b:bs)
283 scExpr env (Let bind body)
284 = scBind env bind `thenUs` \ (env', bind_usg, bind') ->
285 scExpr env' body `thenUs` \ (body_usg, body') ->
286 returnUs (bind_usg `combineUsage` body_usg, Let bind' body')
288 scExpr env e@(App _ _)
290 (fn, args) = collectArgs e
292 mapAndUnzipUs (scExpr env) args `thenUs` \ (usgs, args') ->
294 arg_usg = combineUsages usgs
295 fn_usg | Var f <- fn,
296 Just RecFun <- lookupVarEnv env f
297 = SCU { calls = unitVarEnv f [args], occs = emptyVarEnv }
301 returnUs (arg_usg `combineUsage` fn_usg, mkApps fn args')
302 -- Don't bother to look inside fn;
303 -- it's almost always a variable
305 ----------------------
306 scBind :: ScEnv -> CoreBind -> UniqSM (ScEnv, ScUsage, CoreBind)
307 scBind env (Rec [(fn,rhs)])
308 | not (null val_bndrs)
309 = scExpr env' body `thenUs` \ (usg@(SCU { calls = calls, occs = occs }), body') ->
310 specialise env fn bndrs body usg `thenUs` \ (rules, spec_prs) ->
311 returnUs (extendBndrs env bndrs,
312 SCU { calls = calls `delVarEnv` fn, occs = occs `delVarEnvList` val_bndrs},
313 Rec ((fn `addIdSpecialisations` rules, mkLams bndrs body') : spec_prs))
315 (bndrs,body) = collectBinders rhs
316 val_bndrs = filter isId bndrs
317 env' = env `extendVarEnvList` ((fn,RecFun): [(bndr,RecArg) | bndr <- bndrs])
320 = mapAndUnzipUs do_one prs `thenUs` \ (usgs, prs') ->
321 returnUs (extendBndrs env (map fst prs), combineUsages usgs, Rec prs')
323 do_one (bndr,rhs) = scExpr env rhs `thenUs` \ (usg, rhs') ->
324 returnUs (usg, (bndr,rhs'))
326 scBind env (NonRec bndr rhs)
327 = scExpr env rhs `thenUs` \ (usg, rhs') ->
328 returnUs (extendBndr env bndr, usg, NonRec bndr rhs')
330 ----------------------
332 | Just RecArg <- lookupVarEnv env v = SCU { calls = emptyVarEnv, occs = unitVarEnv v use }
333 | otherwise = nullUsage
337 %************************************************************************
339 \subsection{The specialiser}
341 %************************************************************************
346 -> [CoreBndr] -> CoreExpr -- Its RHS
347 -> ScUsage -- Info on usage
348 -> UniqSM ([CoreRule], -- Rules
349 [(Id,CoreExpr)]) -- Bindings
351 specialise env fn bndrs body (SCU {calls=calls, occs=occs})
352 = getUs `thenUs` \ us ->
354 all_calls = lookupVarEnv calls fn `orElse` []
356 good_calls :: [[CoreArg]]
358 | call_args <- all_calls,
359 length call_args >= n_bndrs, -- App is saturated
360 let call = (bndrs `zip` call_args),
361 any (good_arg occs) call,
362 let (_, pats) = argsToPats us call_args
365 pprTrace "specialise" (ppr all_calls $$ ppr good_calls) $
366 mapAndUnzipUs (spec_one env fn (mkLams bndrs body))
367 (nubBy same_call good_calls `zip` [1..])
369 n_bndrs = length bndrs
370 same_call as1 as2 = and (zipWith eqExpr as1 as2)
372 ---------------------
373 good_arg :: IdEnv ArgOcc -> (CoreBndr, CoreArg) -> Bool
374 good_arg arg_occs (bndr, arg)
375 = case exprIsConApp_maybe arg of -- exprIsConApp_maybe looks
376 Just (dc,_) -> not (isExistentialDataCon dc) -- through unfoldings
377 && bndr_usg_ok arg_occs bndr arg
380 bndr_usg_ok :: IdEnv ArgOcc -> Var -> CoreArg -> Bool
381 bndr_usg_ok arg_occs bndr arg
382 = pprTrace "bndr_ok" (ppr bndr <+> ppr (lookupVarEnv arg_occs bndr)) $
383 case lookupVarEnv arg_occs bndr of
384 Just CaseScrut -> True -- Used only by case scrutiny
385 Just Both -> case arg of -- Used by case and elsewhere
386 App _ _ -> True -- so the arg should be an explicit con app
388 other -> False -- Not used, or used wonkily
391 ---------------------
392 argsToPats :: UniqSupply -> [CoreArg] -> (UniqSupply, [CoreExpr])
393 argsToPats us args = mapAccumL argToPat us args
395 argToPat :: UniqSupply -> CoreArg -> (UniqSupply, CoreExpr)
396 -- C a (D (f x) (g y)) ==> C p1 (D p2 p3)
397 argToPat us (Type ty)
401 | Just (dc,args) <- exprIsConApp_maybe arg
403 (us',args') = argsToPats us args
405 (us', mkConApp dc args')
407 argToPat us (Var v) -- Don't uniqify existing vars,
408 = (us, Var v) -- so that we can spot when we pass them twice
411 = (us1, Var (mkSysLocal SLIT("sc") (uniqFromSupply us2) (exprType arg)))
413 (us1,us2) = splitUniqSupply us
415 ---------------------
418 -> CoreExpr -- Rhs of the original function
420 -> UniqSM (CoreRule, (Id,CoreExpr)) -- Rule and binding
426 f = /\b \y::[(a,b)] -> ....f (b,c) ((:) (a,(b,c)) v (h v))...
427 [c is presumably bound by the (...) part]
429 f_spec = /\ b c \ v::(a,(b,c)) ->
430 (...entire RHS of f...) (b,c) ((:) (a,(b,c)) v (h v))
435 h::(a,(b,c))->[(a,(b,c))] .
437 f (b,c) ((:) (a,(b,c)) v (h v)) = f_spec b c v
440 spec_one env fn rhs (pats, n)
441 = getUniqueUs `thenUs` \ spec_uniq ->
444 fn_loc = nameSrcLoc fn_name
445 spec_occ = mkSpecOcc (nameOccName fn_name)
446 pat_fvs = varSetElems (exprsFreeVars pats)
447 vars_to_bind = filter not_avail pat_fvs
448 not_avail v = not (v `elemVarEnv` env)
449 -- Put the type variables first just for tidiness
450 (tvs, ids) = partition isTyVar vars_to_bind
453 rule_name = _PK_ ("SC:" ++ showSDoc (ppr fn <> int n))
454 spec_rhs = mkLams bndrs (mkApps rhs pats)
455 spec_id = mkUserLocal spec_occ spec_uniq (exprType spec_rhs) fn_loc
456 rule = Rule rule_name pat_fvs pats (mkVarApps (Var spec_id) bndrs)
458 returnUs (rule, (spec_id, spec_rhs))