1 \section[CprAnalyse]{Identify functions that always return a
2 constructed product result}
5 module CprAnalyse ( cprAnalyse ) where
7 #include "HsVersions.h"
9 import CmdLineOpts ( opt_D_verbose_core2core, opt_D_dump_cpranal )
10 import CoreLint ( beginPass, endPass )
12 import CoreUtils ( exprIsValue )
13 import Id ( Id, setIdCprInfo, idCprInfo, idArity,
14 isBottomingId, idDemandInfo )
15 import IdInfo ( CprInfo(..) )
16 import Demand ( isStrict )
18 import Util ( nTimes, mapAccumL )
24 This module performs an analysis of a set of Core Bindings for the
25 Constructed Product Result (CPR) transformation.
27 It detects functions that always explicitly (manifestly?) construct a
28 result value with a product type. A product type is a type which has
29 only one constructor. For example, tuples and boxed primitive values
32 We must also ensure that the function's body starts with sufficient
33 manifest lambdas otherwise loss of sharing can occur. See the comment
36 The transformation of bindings to worker/wrapper pairs is done by the
37 worker-wrapper pass. The worker-wrapper pass splits bindings on the
38 basis of both strictness and CPR info. If an id has both then it can
39 combine the transformations so that only one pair is produced.
41 The analysis here detects nested CPR information. For example, if a
42 function returns a constructed pair, the first element of which is a
43 constructed int, then the analysis will detect nested CPR information
44 for the int as well. Unfortunately, the current transformations can't
45 take advantage of the nested CPR information. They have (broken now,
46 I think) code which will flatten out nested CPR components and rebuild
47 them in the wrapper, but enabling this would lose laziness. It is
48 possible to make use of the nested info: if we knew that a caller was
49 strict in that position then we could create a specialized version of
50 the function which flattened/reconstructed that position.
52 It is not known whether this optimisation would be worthwhile.
54 So we generate and carry round nested CPR information, but before
55 using this info to guide the creation of workers and wrappers we map
56 all components of a CPRInfo to NoCprInfo.
62 Within this module Id's CPR information is represented by
63 ``AbsVal''. When adding this information to the Id's pragma info field
64 we convert the ``Absval'' to a ``CprInfo'' value.
66 Abstract domains consist of a `no information' value (Top), a function
67 value (Fun) which when applied to an argument returns a new AbsVal
68 (note the argument is not used in any way), , for product types, a
69 corresponding length tuple (Tuple) of abstract values. And finally,
70 Bot. Bot is not a proper abstract value but a generic bottom is
71 useful for calculating fixpoints and representing divergent
72 computations. Note that we equate Bot and Fun^n Bot (n > 0), and
73 likewise for Top. This saves a lot of delving in types to keep
74 everything exactly correct.
76 Since functions abstract to constant functions we could just
77 represent them by the abstract value of their result. However, it
78 turns out (I know - I tried!) that this requires a lot of type
79 manipulation and the code is more straightforward if we represent
80 functions by an abstract constant function.
83 data AbsVal = Top -- Not a constructed product
85 | Fun AbsVal -- A function that takes an argument
86 -- and gives AbsVal as result.
88 | Tuple -- A constructed product of values
90 | Bot -- Bot'tom included for convenience
91 -- we could use appropriate Tuple Vals
94 isFun :: AbsVal -> Bool
98 -- For pretty debugging
99 instance Outputable AbsVal where
100 ppr Top = ptext SLIT("Top")
101 ppr (Fun r) = ptext SLIT("Fun->") <> (parens.ppr) r
102 ppr Tuple = ptext SLIT("Tuple ")
103 ppr Bot = ptext SLIT("Bot")
106 -- lub takes the lowest upper bound of two abstract values, standard.
107 lub :: AbsVal -> AbsVal -> AbsVal
112 lub Tuple Tuple = Tuple
113 lub (Fun l) (Fun r) = Fun (lub l r)
114 lub l r = panic "CPR Analysis tried to take the lub of a function and a tuple"
119 The environment maps Ids to their abstract CPR value.
123 type CPREnv = VarEnv AbsVal
125 initCPREnv = emptyVarEnv
132 Take a list of core bindings and return a new list with CPR function
133 ids decorated with their CprInfo pragmas.
137 cprAnalyse :: [CoreBind]
141 beginPass "Constructed Product analysis" ;
142 let { binds_plus_cpr = do_prog binds } ;
143 endPass "Constructed Product analysis"
144 (opt_D_dump_cpranal || opt_D_verbose_core2core)
148 do_prog :: [CoreBind] -> [CoreBind]
149 do_prog binds = snd $ mapAccumL cprAnalBind initCPREnv binds
152 The cprAnal functions take binds/expressions and an environment which
153 gives CPR info for visible ids and returns a new bind/expression
154 with ids decorated with their CPR info.
157 -- Return environment extended with info from this binding
158 cprAnalBind :: CPREnv -> CoreBind -> (CPREnv, CoreBind)
159 cprAnalBind rho (NonRec b e)
160 = (extendVarEnv rho b absval, NonRec b' e')
162 (e', absval) = cprAnalExpr rho e
163 b' = addIdCprInfo b e' absval
165 cprAnalBind rho (Rec prs)
166 = (final_rho, Rec (map do_pr prs))
168 do_pr (b,e) = (b', e')
170 b' = addIdCprInfo b e' absval
171 (e', absval) = cprAnalExpr final_rho e
173 -- When analyzing mutually recursive bindings the iterations to find
174 -- a fixpoint is bounded by the number of bindings in the group.
175 -- for simplicity we just iterate that number of times.
176 final_rho = nTimes (length prs) do_one_pass init_rho
177 init_rho = rho `extendVarEnvList` [(b,Bot) | (b,e) <- prs]
179 do_one_pass :: CPREnv -> CPREnv
180 do_one_pass rho = foldl (\ rho (b,e) -> extendVarEnv rho b (snd (cprAnalExpr rho e)))
184 cprAnalExpr :: CPREnv -> CoreExpr -> (CoreExpr, AbsVal)
186 -- If Id will always diverge when given sufficient arguments then
187 -- we can just set its abs val to Bot. Any other CPR info
188 -- from other paths will then dominate, which is what we want.
189 -- Check in rho, if not there it must be imported, so check
191 cprAnalExpr rho e@(Var v)
192 | isBottomingId v = (e, Bot)
193 | otherwise = (e, case lookupVarEnv rho v of
195 Nothing -> getCprAbsVal v)
197 -- Literals are unboxed
198 cprAnalExpr rho (Lit l) = (Lit l, Top)
200 -- For apps we don't care about the argument's abs val. This
201 -- app will return a constructed product if the function does. We strip
202 -- a Fun from the functions abs val, unless the argument is a type argument
203 -- or it is already Top or Bot.
204 cprAnalExpr rho (App fun arg@(Type _))
205 = (App fun_cpr arg, fun_res)
207 (fun_cpr, fun_res) = cprAnalExpr rho fun
209 cprAnalExpr rho (App fun arg)
210 = (App fun_cpr arg_cpr, res_res)
212 (fun_cpr, fun_res) = cprAnalExpr rho fun
213 (arg_cpr, _) = cprAnalExpr rho arg
214 res_res = case fun_res of
215 Fun res_res -> res_res
218 Tuple -> WARN( True, ppr (App fun arg) ) Top
219 -- This really should not happen!
222 -- Map arguments to Top (we aren't constructing them)
223 -- Return the abstract value of the body, since functions
224 -- are represented by the CPR value of their result, and
225 -- add a Fun for this lambda..
226 cprAnalExpr rho (Lam b body) | isTyVar b = (Lam b body_cpr, body_aval)
227 | otherwise = (Lam b body_cpr, Fun body_aval)
229 (body_cpr, body_aval) = cprAnalExpr (extendVarEnv rho b Top) body
231 cprAnalExpr rho (Let bind body)
232 = (Let bind' body', body_aval)
234 (rho', bind') = cprAnalBind rho bind
235 (body', body_aval) = cprAnalExpr rho' body
237 cprAnalExpr rho (Case scrut bndr alts)
238 = (Case scrut_cpr bndr alts_cpr, alts_aval)
240 (scrut_cpr, scrut_aval) = cprAnalExpr rho scrut
241 (alts_cpr, alts_aval) = cprAnalCaseAlts (extendVarEnv rho bndr scrut_aval) alts
243 cprAnalExpr rho (Note n exp)
244 = (Note n exp_cpr, expr_aval)
246 (exp_cpr, expr_aval) = cprAnalExpr rho exp
248 cprAnalExpr rho (Type t)
251 cprAnalCaseAlts :: CPREnv -> [CoreAlt] -> ([CoreAlt], AbsVal)
252 cprAnalCaseAlts rho alts
253 = foldl anal_alt ([], Bot) alts
255 anal_alt :: ([CoreAlt], AbsVal) -> CoreAlt -> ([CoreAlt], AbsVal)
256 anal_alt (done, aval) (con, binds, exp)
257 = (done ++ [(con,binds,exp_cpr)], aval `lub` exp_aval)
258 where (exp_cpr, exp_aval) = cprAnalExpr rho' exp
259 rho' = rho `extendVarEnvList` (zip binds (repeat Top))
262 addIdCprInfo :: Id -> CoreExpr -> AbsVal -> Id
263 addIdCprInfo bndr rhs absval
264 | useful_info && ok_to_add = setIdCprInfo bndr cpr_info
267 cpr_info = absToCprInfo absval
268 useful_info = case cpr_info of { ReturnsCPR -> True; NoCPRInfo -> False }
270 ok_to_add = case absval of
271 Fun _ -> idArity bndr >= n_fun_tys absval
272 -- Enough visible lambdas
274 Tuple -> exprIsValue rhs || isStrict (idDemandInfo bndr)
275 -- If the rhs is a value, and returns a constructed product,
276 -- it will be inlined at usage sites, so we give it a Tuple absval
277 -- If it isn't a value, we won't inline it (code/work dup worries), so
278 -- we discard its absval.
280 -- Also, if the strictness analyser has figured out that it's strict,
281 -- the let-to-case transformation will happen, so again it's good.
282 -- (CPR analysis runs before the simplifier has had a chance to do
283 -- the let-to-case transform.)
284 -- This made a big difference to PrelBase.modInt, which had something like
285 -- modInt = \ x -> let r = ... -> I# v in
286 -- ...body strict in r...
287 -- r's RHS isn't a value yet; but modInt returns r in various branches, so
288 -- if r doesn't have the CPR property then neither does modInt
292 n_fun_tys :: AbsVal -> Int
293 n_fun_tys (Fun av) = 1 + n_fun_tys av
297 absToCprInfo :: AbsVal -> CprInfo
298 absToCprInfo Tuple = ReturnsCPR
299 absToCprInfo (Fun r) = absToCprInfo r
300 absToCprInfo _ = NoCPRInfo
303 -- Cpr Info doesn't store the number of arguments a function has, so the caller
304 -- must take care to add the appropriate number of Funs.
305 getCprAbsVal v = case idCprInfo v of
307 ReturnsCPR -> nTimes arity Fun Tuple
310 -- Imported (non-nullary) constructors will have the CPR property
311 -- in their IdInfo, so no need to look at their unfolding