2 % (c) The GRASP/AQUA Project, Glasgow University, 1993-1996
4 \section[MagicUFs]{Magic unfoldings that the simplifier knows about}
7 #include "HsVersions.h"
10 MagicUnfoldingFun, -- absolutely abstract
13 applyMagicUnfoldingFun
19 import PrelInfo ( mkListTy )
20 import SimplEnv ( SimplEnv )
21 import SimplMonad ( SmplM(..), SimplCount )
22 import Type ( mkFunTys )
23 import Unique ( Unique{-instances-} )
24 import Util ( assoc, zipWith3Equal, panic )
27 %************************************************************************
29 \subsection{Types, etc., for magic-unfolding functions}
31 %************************************************************************
34 data MagicUnfoldingFun
35 = MUF ( SimplEnv -- state of play in simplifier...
36 -- (note: we can get simplifier switches
38 -> [CoreArg] -- arguments
39 -> SmplM (Maybe CoreExpr))
40 -- Just result, or Nothing
43 Give us a value's @Unique@, we'll give you back the corresponding MUF.
45 mkMagicUnfoldingFun :: Unique -> MagicUnfoldingFun
47 mkMagicUnfoldingFun tag
48 = assoc "mkMagicUnfoldingFun" magic_UFs_table tag
50 magic_UFs_table = panic "MagicUFs.magic_UFs_table:ToDo"
53 Give us an MUF and stuff to apply it to, and we'll give you back the
56 applyMagicUnfoldingFun
60 -> SmplM (Maybe CoreExpr)
62 applyMagicUnfoldingFun (MUF fun) env args = fun env args
65 %************************************************************************
67 \subsection{The table of actual magic unfoldings}
69 %************************************************************************
74 magic_UFs_table :: [(FAST_STRING, MagicUnfoldingFun)]
77 = [(SLIT("augment"), MUF augment_fun),
78 (SLIT("build"), MUF build_fun),
79 (SLIT("foldl"), MUF foldl_fun),
80 (SLIT("foldr"), MUF foldr_fun),
81 (SLIT("unpackFoldrPS#"), MUF unpack_foldr_fun),
82 (SLIT("unpackAppendPS#"), MUF unpack_append_fun)]
85 %************************************************************************
87 \subsubsection{Unfolding function for @append@}
89 %************************************************************************
92 -- First build, the way we express our lists.
96 -> SmplM (Maybe CoreExpr)
97 build_fun env [TypeArg ty,ValArg (VarArg e)]
98 | switchIsSet env SimplDoInlineFoldrBuild =
101 ourCons = CoTyApp (Var consDataCon) ty
102 ourNil = CoTyApp (Var nilDataCon) ty
104 newIds [ mkFunTys [ty, tyL] tyL, tyL ] `thenSmpl` \ [c,n] ->
105 returnSmpl(Just (Let (NonRec c ourCons)
106 (Let (NonRec n ourNil)
107 (App (App (CoTyApp (Var e) tyL) (VarArg c)) (VarArg n)))))
108 -- ToDo: add `build' without an argument instance.
109 -- This is strange, because of g's type.
111 ASSERT (not (switchIsSet env SimplDoInlineFoldrBuild))
116 augment_fun :: SimplEnv
118 -> SmplM (Maybe CoreExpr)
120 augment_fun env [TypeArg ty,ValArg (VarArg e),ValArg nil]
121 | switchIsSet env SimplDoInlineFoldrBuild =
124 ourCons = CoTyApp (Var consDataCon) ty
126 newId (mkFunTys [ty, tyL] tyL) `thenSmpl` \ c ->
127 returnSmpl (Just (Let (NonRec c ourCons)
128 (App (App (CoTyApp (Var e) tyL) (VarArg c)) nil)))
129 -- ToDo: add `build' without an argument instance.
130 -- This is strange, because of g's type.
132 ASSERT (not (switchIsSet env SimplDoInlineFoldrBuild))
136 Now foldr, the way we consume lists.
139 foldr_fun :: SimplEnv
141 -> SmplM (Maybe CoreExpr)
143 foldr_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:ValArg arg_z:rest_args)
144 | do_fb_red && isConsFun env arg_k && isNilForm env arg_z
145 = -- foldr (:) [] ==> id
146 -- this transformation is *always* benificial
147 -- cf. foldr (:) [] (build g) == g (:) []
148 -- with foldr (:) [] (build g) == build g
149 -- after unfolding build, they are the same thing.
150 tick Foldr_Cons_Nil `thenSmpl_`
151 newId (mkListTy ty1) `thenSmpl` \ x ->
152 returnSmpl({-trace "foldr (:) []"-} (Just (mkGenApp (Lam x (Var x)) rest_args)))
154 do_fb_red = switchIsSet env SimplDoFoldrBuild
156 foldr_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:ValArg arg_z:ValArg arg_list:rest_args)
157 | do_fb_red && isNilForm env arg_list
158 = -- foldr f z [] = z
159 -- again another short cut, helps with unroling of constant lists
160 tick Foldr_Nil `thenSmpl_`
161 returnSmpl (Just (argToExpr arg_z))
163 | do_fb_red && arg_list_isBuildForm
164 = -- foldr k z (build g) ==> g k z
165 -- this next line *is* the foldr/build rule proper.
166 tick FoldrBuild `thenSmpl_`
167 returnSmpl (Just (mkGenApp (Var g) (TypeArg ty2:ValArg arg_k:ValArg arg_z:rest_args)))
169 | do_fb_red && arg_list_isAugmentForm
170 = -- foldr k z (augment g h) ==> let v = foldr k z h in g k v
171 -- this next line *is* the foldr/augment rule proper.
172 tick FoldrAugment `thenSmpl_`
173 newId ty2 `thenSmpl` \ v ->
175 (Let (NonRec v (mkGenApp (Var foldrId)
176 [TypeArg ty1,TypeArg ty2,
180 (mkGenApp (Var g') (TypeArg ty2:ValArg arg_k:ValArg (VarArg v):rest_args))))
182 | do_fb_red && arg_list_isListForm
183 = -- foldr k z (a:b:c:rest) =
184 -- (\ f -> f a (f b (f c (foldr f z rest)))) k rest_args
185 -- NB: 'k' is used just one by foldr, but 'f' is used many
186 -- times inside the list structure. This means that
187 -- 'f' needs to be inside a lambda, to make sure the simplifier
191 -- f a (f b (f c (foldr f z rest)))
193 -- let ele_1 = foldr f z rest
198 tick Foldr_List `thenSmpl_`
200 mkFunTys [ty1, ty2] ty2 :
201 take (length the_list) (repeat ty2)
202 ) `thenSmpl` \ (f_id:ele_id1:ele_ids) ->
206 (mkGenApp (Var foldrId)
207 [TypeArg ty1,TypeArg ty2,
208 ValArg (VarArg f_id),
211 rest_binds = zipWith3Equal
212 (\ e v e' -> NonRec e (mkRhs v e'))
214 (reverse (tail the_list))
215 (init (ele_id1:ele_ids))
216 mkRhs v e = App (App (Var f_id) v) (VarArg e)
219 (mkRhs (head the_list) (last (ele_id1:ele_ids)))
220 (fst_bind:rest_binds)
222 returnSmpl (Just (mkGenApp (Lam f_id core_list)
223 (ValArg arg_k:rest_args)))
228 | do_fb_red && arg_list_isStringForm -- ok, its a string!
229 -- foldr f z "foo" => unpackFoldrPS# f z "foo"#
230 = tick Str_FoldrStr `thenSmpl_`
231 returnSmpl (Just (mkGenApp (Var unpackCStringFoldrId)
233 ValArg (LitArg (MachStr str_val)):
238 do_fb_red = switchIsSet env SimplDoFoldrBuild
240 arg_list_isStringForm = maybeToBool stringForm
241 stringForm = getStringForm env arg_list
242 (Just str_val) = stringForm
244 arg_list_isBuildForm = maybeToBool buildForm
245 buildForm = getBuildForm env arg_list
248 arg_list_isAugmentForm = maybeToBool augmentForm
249 augmentForm = getAugmentForm env arg_list
250 (Just (g',h)) = augmentForm
252 arg_list_isListForm = maybeToBool listForm
253 listForm = getListForm env arg_list
254 (Just (the_list,the_tl)) = listForm
256 arg_list_isAppendForm = maybeToBool appendForm
257 appendForm = getAppendForm env arg_list
258 (Just (xs,ys)) = appendForm
261 foldr_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:rest_args)
262 | doing_inlining && isConsFun env arg_k && not dont_fold_back_append
263 = -- foldr (:) z xs = xs ++ z
264 tick Foldr_Cons `thenSmpl_`
265 newIds [ty2,mkListTy ty1] `thenSmpl` \ [z,x] ->
266 returnSmpl (Just (mkGenApp
267 (Lam z (Lam x (mkGenApp
271 ValArg (VarArg z)])))
273 | doing_inlining && (isInterestingArg env arg_k
274 || isConsFun env arg_k)
280 -- (a:b) -> f a (h b)
284 -- tick FoldrInline `thenSmpl_`
287 mkListTy ty1, -- b :: [t1]
289 mkListTy ty1, -- x :: t1
290 mkFunTys [mkListTy ty1] ty2,
292 mkFunTys [ty1, ty2] ty2,
296 ] `thenSmpl` \ [a,b,v,x,h,f,z,xs] ->
298 h_rhs = (Lam x (Case (Var x)
300 [(nilDataCon,[],argToExpr (VarArg z)),
301 (consDataCon,[a,b],body)]
303 body = Let (NonRec v (App (Var h) (VarArg b)))
304 (App (App (argToExpr (VarArg f))
310 (Lam f (Lam z (Lam xs
311 (Let (Rec [(h,h_rhs)])
312 (App (Var h) (VarArg xs))))))
313 (ValArg arg_k:rest_args)))
315 doing_inlining = switchIsSet env SimplDoInlineFoldrBuild
316 dont_fold_back_append = switchIsSet env SimplDontFoldBackAppend
317 foldr_fun _ _ = returnSmpl Nothing
319 isConsFun :: SimplEnv -> CoreArg -> Bool
320 isConsFun env (VarArg v) =
321 case lookupUnfolding env v of
322 GenForm _ _ (Lam (x,_) (Lam (y,_)
323 (Con con tys [VarArg x',VarArg y']))) _
324 | con == consDataCon && x==x' && y==y'
325 -> ASSERT ( length tys == 1 ) True
327 isConsFun env _ = False
329 isNilForm :: SimplEnv -> CoreArg -> Bool
330 isNilForm env (VarArg v) =
331 case lookupUnfolding env v of
332 GenForm _ _ (CoTyApp (Var id) _) _
333 | id == nilDataCon -> True
335 | id == nilDataCon -> True
336 LitForm (NoRepStr s) | _NULL_ s -> True
338 isNilForm env _ = False
340 getBuildForm :: SimplEnv -> CoreArg -> Maybe Id
341 getBuildForm env (VarArg v) =
342 case lookupUnfolding env v of
343 GenForm False _ _ _ -> Nothing
344 -- not allowed to inline :-(
345 GenForm _ _ (App (CoTyApp (Var bld) _) (VarArg g)) _
346 | bld == buildId -> Just g
347 GenForm _ _ (App (App (CoTyApp (Var bld) _)
349 | bld == augmentId && isNilForm env h -> Just g
351 getBuildForm env _ = Nothing
355 getAugmentForm :: SimplEnv -> CoreArg -> Maybe (Id,CoreArg)
356 getAugmentForm env (VarArg v) =
357 case lookupUnfolding env v of
358 GenForm False _ _ _ -> Nothing
359 -- not allowed to inline :-(
360 GenForm _ _ (App (App (CoTyApp (Var bld) _)
362 | bld == augmentId -> Just (g,h)
364 getAugmentForm env _ = Nothing
366 getStringForm :: SimplEnv -> CoreArg -> Maybe FAST_STRING
367 getStringForm env (LitArg (NoRepStr str)) = Just str
368 getStringForm env _ = Nothing
371 getAppendForm :: SimplEnv -> CoreArg -> Maybe (GenCoreAtom Id,GenCoreAtom Id)
372 getAppendForm env (VarArg v) =
373 case lookupUnfolding env v of
374 GenForm False _ _ _ -> Nothing -- not allowed to inline :-(
375 GenForm _ _ (App (App (App (CoTyApp (CoTyApp (Var fld) _) _) con) ys) xs) _
376 | fld == foldrId && isConsFun env con -> Just (xs,ys)
378 getAppendForm env _ = Nothing
382 -- this gets a list of the form a : b : c : d and returns ([a,b,c],d)
383 -- it natuarally follows that [a,b,c] => ([a,b,c],e), where e = []
389 -> Maybe ([CoreArg],CoreArg)
390 getListForm env (VarArg v) =
391 case lookupUnfolding env v of
392 ConForm id _ [head,tail]
393 | id == consDataCon ->
394 case getListForm env tail of
395 Nothing -> Just ([head],tail)
396 Just (lst,new_tail) -> Just (head:lst,new_tail)
398 getListForm env _ = Nothing
400 isInterestingArg :: SimplEnv -> CoreArg -> Bool
401 isInterestingArg env (VarArg v) =
402 case lookupUnfolding env v of
403 GenForm False _ _ UnfoldNever -> False
404 GenForm _ _ exp guide -> True
406 isInterestingArg env _ = False
408 foldl_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:ValArg arg_z:ValArg arg_list:rest_args)
409 | do_fb_red && isNilForm env arg_list
410 = -- foldl f z [] = z
411 -- again another short cut, helps with unroling of constant lists
412 tick Foldl_Nil `thenSmpl_`
413 returnSmpl (Just (argToExpr arg_z))
415 | do_fb_red && arg_list_isBuildForm
416 = -- foldl t1 t2 k z (build t3 g) ==>
417 -- let c {- INLINE -} = \ b g' a -> g' (f a b)
418 -- n {- INLINE -} = \ a -> a
420 -- this next line *is* the foldr/build rule proper.
421 tick FoldlBuild `thenSmpl_`
422 -- c :: t2 -> (t1 -> t1) -> t1 -> t1
425 {- pre_c -} mkFunTys [ty2, mkFunTys [ty1] ty1, ty1] ty1,
426 {- pre_n -} mkFunTys [ty1] ty1,
428 {- g' -} mkFunTys [ty1] ty1,
432 ] `thenSmpl` \ [pre_c,
441 c = addIdUnfolding pre_c (iWantToBeINLINEd UnfoldAlways)
442 c_rhs = Lam b (Lam g' (Lam a
443 (Let (NonRec t (App (App (argToExpr arg_k) (VarArg a)) (VarArg b)))
444 (App (Var g') (VarArg t)))))
445 n = addIdUnfolding pre_n (iWantToBeINLINEd UnfoldAlways)
446 n_rhs = Lam a' (Var a')
448 returnSmpl (Just (Let (NonRec c c_rhs) (Let (NonRec n n_rhs)
450 (TypeArg (mkFunTys [ty1] ty1):ValArg (VarArg c):ValArg (VarArg n)
451 :ValArg arg_z:rest_args)))))
453 | do_fb_red && arg_list_isAugmentForm
454 = -- foldl t1 t2 k z (augment t3 g h) ==>
455 -- let c {- INLINE -} = \ b g' a -> g' (f a b)
456 -- n {- INLINE -} = \ a -> a
457 -- r {- INLINE -} = foldr t2 (t1 -> t1) c n h
459 -- this next line *is* the foldr/build rule proper.
460 tick FoldlAugment `thenSmpl_`
461 -- c :: t2 -> (t1 -> t1) -> t1 -> t1
464 {- pre_c -} mkFunTys [ty2, mkFunTys [ty1] ty1, ty1] ty1,
465 {- pre_n -} mkFunTys [ty1] ty1,
466 {- pre_r -} mkFunTys [ty1] ty1,
468 {- g_ -} mkFunTys [ty1] ty1,
472 ] `thenSmpl` \ [pre_c,
482 c = addIdUnfolding pre_c (iWantToBeINLINEd UnfoldAlways)
483 c_rhs = Lam b (Lam g_ (Lam a
484 (Let (NonRec t (App (App (argToExpr arg_k) (VarArg a)) (VarArg b)))
485 (App (Var g_) (VarArg t)))))
486 n = addIdUnfolding pre_n (iWantToBeINLINEd UnfoldAlways)
487 n_rhs = Lam a' (Var a')
488 r = addIdUnfolding pre_r (iWantToBeINLINEd UnfoldAlways)
489 r_rhs = mkGenApp (Var foldrId)
490 [TypeArg ty2,TypeArg (mkFunTys [ty1] ty1),
495 returnSmpl (Just (Let (NonRec c c_rhs)
496 (Let (NonRec n n_rhs)
497 (Let (NonRec r r_rhs)
499 (TypeArg (mkFunTys [ty1] ty1):ValArg (VarArg c):ValArg (VarArg r)
500 :ValArg arg_z:rest_args))))))
502 | do_fb_red && arg_list_isListForm
503 = -- foldl k z (a:b:c:rest) =
504 -- (\ f -> foldl f (f (f (f z a) b) c) rest) k rest_args
505 -- NB: 'k' is used just one by foldr, but 'f' is used many
506 -- times inside the list structure. This means that
507 -- 'f' needs to be inside a lambda, to make sure the simplifier
511 -- foldl f (f (f (f z a) b) c) rest
512 -- f a (f b (f c (foldr f z rest)))
517 -- in foldl f ele_3 rest
519 tick Foldl_List `thenSmpl_`
521 mkFunTys [ty1, ty2] ty1 :
522 take (length the_list) (repeat ty1)
523 ) `thenSmpl` \ (f_id:ele_ids) ->
525 rest_binds = zipWith3Equal
526 (\ e v e' -> NonRec e (mkRhs v e'))
528 the_list -- :: [CoreArg]
529 (init (arg_z:map VarArg ele_ids)) -- :: [CoreArg]
530 mkRhs v e = App (App (Var f_id) e) v
532 last_bind = mkGenApp (Var foldlId)
533 [TypeArg ty1,TypeArg ty2,
534 ValArg (VarArg f_id),
535 ValArg (VarArg (last ele_ids)),
542 returnSmpl (Just (mkGenApp (Lam f_id core_list)
543 (ValArg arg_k:rest_args)))
546 do_fb_red = switchIsSet env SimplDoFoldrBuild
548 arg_list_isAugmentForm = maybeToBool augmentForm
549 augmentForm = getAugmentForm env arg_list
550 (Just (g',h)) = augmentForm
552 arg_list_isBuildForm = maybeToBool buildForm
553 buildForm = getBuildForm env arg_list
556 arg_list_isListForm = maybeToBool listForm
557 listForm = getListForm env arg_list
558 (Just (the_list,the_tl)) = listForm
561 arg_list_isAppendForm = maybeToBool appendForm
562 appendForm = getAppendForm env arg_list
563 (Just (xs,ys)) = appendForm
566 foldl_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:rest_args)
567 | doing_inlining && (isInterestingArg env arg_k
568 || isConsFun env arg_k)
574 -- (a:b) -> h b (f r a)
578 -- tick FoldrInline `thenSmpl_`
581 mkListTy ty2, -- b :: [t1]
583 mkListTy ty2, -- x :: t1
584 mkFunTys [mkListTy ty2, ty1] ty1,
585 -- h :: [t2] -> t1 -> t1
586 mkFunTys [ty1, ty2] ty1,
591 ] `thenSmpl` \ [a,b,v,x,h,f,z,xs,r] ->
593 h_rhs = (Lam x (Lam r (Case (Var x))
595 [(nilDataCon,[],argToExpr (VarArg r)),
596 (consDataCon,[a,b],body)]
598 body = Let (NonRec v (App (App (Var f) (VarArg r))
600 (App (App (argToExpr (VarArg h))
606 (Lam f (Lam z (Lam xs
607 (Let (Rec [(h,h_rhs)])
608 (App (App (Var h) (VarArg xs))
610 (ValArg arg_k:rest_args)))
612 doing_inlining = switchIsSet env SimplDoInlineFoldrBuild
614 foldl_fun env _ = returnSmpl Nothing
620 -- Foldr unpackFoldr "str"# (:) stuff ==> unpackAppend "str"#
622 unpack_foldr_fun env [TypeArg ty,ValArg str,ValArg arg_k,ValArg arg_z]
623 | switchIsSet env SimplDoFoldrBuild && isConsFun env arg_k
624 = tick Str_UnpackCons `thenSmpl_`
625 returnSmpl (Just (mkGenApp (Var unpackCStringAppendId)
628 unpack_foldr_fun env _ = returnSmpl Nothing
630 unpack_append_fun env
631 [ValArg (LitArg (MachStr str_val)),ValArg arg_z]
632 | switchIsSet env SimplDoFoldrBuild && isNilForm env arg_z
633 = tick Str_UnpackNil `thenSmpl_`
634 returnSmpl (Just (Lit (NoRepStr str_val)))
635 unpack_append_fun env _ = returnSmpl Nothing