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
205 | RecArg -- These are those functions' arguments; we are
206 -- interested to see if those arguments are scrutinised
207 | Other -- We track all others so we know what's in scope
209 extendBndrs env bndrs = extendVarEnvList env [(b,Other) | b <- bndrs]
210 extendBndr env bndr = extendVarEnv env bndr Other
214 calls :: !(IdEnv ([[CoreArg]])), -- Calls
215 -- The functions are a subset of the
216 -- RecFuns in the ScEnv
218 occs :: !(IdEnv ArgOcc) -- Information on argument occurrences
219 } -- The variables are a subset of the
220 -- RecArg in the ScEnv
222 nullUsage = SCU { calls = emptyVarEnv, occs = emptyVarEnv }
224 combineUsage u1 u2 = SCU { calls = plusVarEnv_C (++) (calls u1) (calls u2),
225 occs = plusVarEnv_C combineOcc (occs u1) (occs u2) }
227 combineUsages [] = nullUsage
228 combineUsages us = foldr1 combineUsage us
230 data ArgOcc = CaseScrut
234 instance Outputable ArgOcc where
235 ppr CaseScrut = ptext SLIT("case-scrut")
236 ppr OtherOcc = ptext SLIT("other-occ")
237 ppr Both = ptext SLIT("case-scrut and other")
239 combineOcc CaseScrut CaseScrut = CaseScrut
240 combineOcc OtherOcc OtherOcc = OtherOcc
241 combineOcc _ _ = Both
245 %************************************************************************
247 \subsection{The main recursive function}
249 %************************************************************************
252 scExpr :: ScEnv -> CoreExpr -> UniqSM (ScUsage, CoreExpr)
253 -- The unique supply is needed when we invent
254 -- a new name for the specialised function and its args
256 scExpr env e@(Type t) = returnUs (nullUsage, e)
257 scExpr env e@(Lit l) = returnUs (nullUsage, e)
258 scExpr env e@(Var v) = returnUs (varUsage env v OtherOcc, e)
259 scExpr env (Note n e) = scExpr env e `thenUs` \ (usg,e') ->
260 returnUs (usg, Note n e')
261 scExpr env (Lam b e) = scExpr (extendBndr env b) e `thenUs` \ (usg,e') ->
262 returnUs (usg, Lam b e')
264 scExpr env (Case scrut b alts)
265 = sc_scrut scrut `thenUs` \ (scrut_usg, scrut') ->
266 mapAndUnzipUs sc_alt alts `thenUs` \ (alts_usgs, alts') ->
267 returnUs (combineUsages alts_usgs `combineUsage` scrut_usg,
270 sc_scrut e@(Var v) = returnUs (varUsage env v CaseScrut, e)
271 sc_scrut e = scExpr env e
273 sc_alt (con,bs,rhs) = scExpr env rhs `thenUs` \ (usg,rhs') ->
274 returnUs (usg, (con,bs,rhs'))
276 env1 = extendBndrs env (b:bs)
278 scExpr env (Let bind body)
279 = scBind env bind `thenUs` \ (env', bind_usg, bind') ->
280 scExpr env' body `thenUs` \ (body_usg, body') ->
281 returnUs (bind_usg `combineUsage` body_usg, Let bind' body')
283 scExpr env e@(App _ _)
285 (fn, args) = collectArgs e
287 mapAndUnzipUs (scExpr env) args `thenUs` \ (usgs, args') ->
289 arg_usg = combineUsages usgs
290 fn_usg | Var f <- fn,
291 Just RecFun <- lookupVarEnv env f
292 = SCU { calls = unitVarEnv f [args], occs = emptyVarEnv }
296 returnUs (arg_usg `combineUsage` fn_usg, mkApps fn args')
297 -- Don't bother to look inside fn;
298 -- it's almost always a variable
300 ----------------------
301 scBind :: ScEnv -> CoreBind -> UniqSM (ScEnv, ScUsage, CoreBind)
302 scBind env (Rec [(fn,rhs)])
303 | not (null val_bndrs)
304 = scExpr env' body `thenUs` \ (usg@(SCU { calls = calls, occs = occs }), body') ->
305 specialise env fn bndrs body usg `thenUs` \ (rules, spec_prs) ->
306 returnUs (extendBndrs env bndrs,
307 SCU { calls = calls `delVarEnv` fn, occs = occs `delVarEnvList` val_bndrs},
308 Rec ((fn `addIdSpecialisations` rules, mkLams bndrs body') : spec_prs))
310 (bndrs,body) = collectBinders rhs
311 val_bndrs = filter isId bndrs
312 env' = env `extendVarEnvList` ((fn,RecFun): [(bndr,RecArg) | bndr <- bndrs])
315 = mapAndUnzipUs do_one prs `thenUs` \ (usgs, prs') ->
316 returnUs (extendBndrs env (map fst prs), combineUsages usgs, Rec prs')
318 do_one (bndr,rhs) = scExpr env rhs `thenUs` \ (usg, rhs') ->
319 returnUs (usg, (bndr,rhs'))
321 scBind env (NonRec bndr rhs)
322 = scExpr env rhs `thenUs` \ (usg, rhs') ->
323 returnUs (extendBndr env bndr, usg, NonRec bndr rhs')
325 ----------------------
327 | Just RecArg <- lookupVarEnv env v = SCU { calls = emptyVarEnv, occs = unitVarEnv v use }
328 | otherwise = nullUsage
332 %************************************************************************
334 \subsection{The specialiser}
336 %************************************************************************
341 -> [CoreBndr] -> CoreExpr -- Its RHS
342 -> ScUsage -- Info on usage
343 -> UniqSM ([CoreRule], -- Rules
344 [(Id,CoreExpr)]) -- Bindings
346 specialise env fn bndrs body (SCU {calls=calls, occs=occs})
347 = getUs `thenUs` \ us ->
349 all_calls = lookupVarEnv calls fn `orElse` []
351 good_calls :: [[CoreArg]]
353 | call_args <- all_calls,
354 length call_args >= n_bndrs, -- App is saturated
355 let call = (bndrs `zip` call_args),
356 any (good_arg occs) call,
357 let (_, pats) = argsToPats us call_args
360 pprTrace "specialise" (ppr all_calls $$ ppr good_calls) $
361 mapAndUnzipUs (spec_one env fn (mkLams bndrs body))
362 (nubBy same_call good_calls `zip` [1..])
364 n_bndrs = length bndrs
365 same_call as1 as2 = and (zipWith eqExpr as1 as2)
367 ---------------------
368 good_arg :: IdEnv ArgOcc -> (CoreBndr, CoreArg) -> Bool
369 good_arg arg_occs (bndr, arg)
370 = case exprIsConApp_maybe arg of -- exprIsConApp_maybe looks
371 Just (dc,_) -> not (isExistentialDataCon dc) -- through unfoldings
372 && bndr_usg_ok arg_occs bndr arg
375 bndr_usg_ok :: IdEnv ArgOcc -> Var -> CoreArg -> Bool
376 bndr_usg_ok arg_occs bndr arg
377 = pprTrace "bndr_ok" (ppr bndr <+> ppr (lookupVarEnv arg_occs bndr)) $
378 case lookupVarEnv arg_occs bndr of
379 Just CaseScrut -> True -- Used only by case scrutiny
380 Just Both -> case arg of -- Used by case and elsewhere
381 App _ _ -> True -- so the arg should be an explicit con app
383 other -> False -- Not used, or used wonkily
386 ---------------------
387 argsToPats :: UniqSupply -> [CoreArg] -> (UniqSupply, [CoreExpr])
388 argsToPats us args = mapAccumL argToPat us args
390 argToPat :: UniqSupply -> CoreArg -> (UniqSupply, CoreExpr)
391 -- C a (D (f x) (g y)) ==> C p1 (D p2 p3)
392 argToPat us (Type ty)
396 | Just (dc,args) <- exprIsConApp_maybe arg
398 (us',args') = argsToPats us args
400 (us', mkConApp dc args')
402 argToPat us (Var v) -- Don't uniqify existing vars,
403 = (us, Var v) -- so that we can spot when we pass them twice
406 = (us1, Var (mkSysLocal SLIT("sc") (uniqFromSupply us2) (exprType arg)))
408 (us1,us2) = splitUniqSupply us
410 ---------------------
413 -> CoreExpr -- Rhs of the original function
415 -> UniqSM (CoreRule, (Id,CoreExpr)) -- Rule and binding
421 f = /\b \y::[(a,b)] -> ....f (b,c) ((:) (a,(b,c)) v (h v))...
422 [c is presumably bound by the (...) part]
424 f_spec = /\ b c \ v::(a,(b,c)) ->
425 (...entire RHS of f...) (b,c) ((:) (a,(b,c)) v (h v))
430 h::(a,(b,c))->[(a,(b,c))] .
432 f (b,c) ((:) (a,(b,c)) v (h v)) = f_spec b c v
435 spec_one env fn rhs (pats, n)
436 = getUniqueUs `thenUs` \ spec_uniq ->
439 fn_loc = nameSrcLoc fn_name
440 spec_occ = mkSpecOcc (nameOccName fn_name)
441 pat_fvs = varSetElems (exprsFreeVars pats)
442 vars_to_bind = filter not_avail pat_fvs
443 not_avail v = not (v `elemVarEnv` env)
444 -- Put the type variables first just for tidiness
445 (tvs, ids) = partition isTyVar vars_to_bind
448 rule_name = _PK_ ("SC:" ++ showSDoc (ppr fn <> int n))
449 spec_rhs = mkLams bndrs (mkApps rhs pats)
450 spec_id = mkUserLocal spec_occ spec_uniq (exprType spec_rhs) fn_loc
451 rule = Rule rule_name pat_fvs pats (mkVarApps (Var spec_id) bndrs)
453 returnUs (rule, (spec_id, spec_rhs))