[project @ 1996-01-18 16:33:17 by partain]
[ghc-hetmet.git] / ghc / compiler / simplCore / MagicUFs.lhs
1 %
2 % (c) The GRASP/AQUA Project, Glasgow University, 1993-1995
3 %
4 \section[MagicUFs]{Magic unfoldings that the simplifier knows about}
5
6 \begin{code}
7 #include "HsVersions.h"
8
9 module MagicUFs (
10         MagicUnfoldingFun,  -- absolutely abstract
11
12         mkMagicUnfoldingFun,
13         applyMagicUnfoldingFun,
14         
15         CoreArg, PlainCoreArg(..), CoreAtom, PlainCoreAtom(..),
16         CoreExpr, PlainCoreExpr(..), Id, Maybe, SimplEnv,
17         SplitUniqSupply, TickType, UniType,
18         SmplM(..), SimplCount
19     ) where
20
21 IMPORT_Trace            -- ToDo: not sure why this is being used
22
23 import AbsPrel          ( foldlId, foldrId, buildId,
24                           nilDataCon, consDataCon, mkListTy, mkFunTy,
25                           unpackCStringAppendId
26                         )
27 import AbsUniType       ( splitTypeWithDictsAsArgs, TyVarTemplate )
28 import BasicLit         ( BasicLit(..) )
29 import CmdLineOpts      ( SimplifierSwitch(..), switchIsOn, SwitchResult )
30 import Id
31 import IdInfo
32 import Maybes           ( Maybe(..), maybeToBool )
33 import Outputable
34 import PlainCore
35 import Pretty
36 import SimplEnv
37 import SimplMonad
38 import TaggedCore
39 import Util
40 \end{code}
41
42 %************************************************************************
43 %*                                                                      *
44 \subsection{Types, etc., for magic-unfolding functions}
45 %*                                                                      *
46 %************************************************************************
47
48 \begin{code}
49 data MagicUnfoldingFun
50   = MUF ( SimplEnv              -- state of play in simplifier...
51                                 -- (note: we can get simplifier switches
52                                 -- from the SimplEnv)
53         -> [PlainCoreArg]       -- arguments
54         -> SmplM (Maybe PlainCoreExpr))
55                                 -- Just result, or Nothing
56 \end{code}
57
58 Give us a string tag, we'll give you back the corresponding MUF.
59 \begin{code}
60 mkMagicUnfoldingFun :: FAST_STRING -> MagicUnfoldingFun
61
62 mkMagicUnfoldingFun tag
63   = assoc ("mkMagicUnfoldingFun:"  ++ _UNPK_ tag) magic_UFs_table tag
64 \end{code}
65
66 Give us an MUF and stuff to apply it to, and we'll give you back the
67 answer.
68 \begin{code}
69 applyMagicUnfoldingFun
70         :: MagicUnfoldingFun
71         -> SimplEnv
72         -> [PlainCoreArg]
73         -> SmplM (Maybe PlainCoreExpr)
74
75 applyMagicUnfoldingFun (MUF fun) env args = fun env args
76 \end{code}
77
78 %************************************************************************
79 %*                                                                      *
80 \subsection{The table of actual magic unfoldings}
81 %*                                                                      *
82 %************************************************************************
83
84 \begin{code}
85 magic_UFs_table :: [(FAST_STRING, MagicUnfoldingFun)]
86
87 magic_UFs_table
88   = [(SLIT("build"),  MUF build_fun),
89      (SLIT("foldl"),  MUF foldl_fun),
90      (SLIT("foldr"),  MUF foldr_fun) ]
91 \end{code}
92
93 %************************************************************************
94 %*                                                                      *
95 \subsubsection{Unfolding function for @append@}
96 %*                                                                      *
97 %************************************************************************
98
99 \begin{code}
100 -- First build, the way we express our lists.
101
102 build_fun :: SimplEnv
103           -> [PlainCoreArg]
104           -> SmplM (Maybe PlainCoreExpr)
105 build_fun env [TypeArg ty,ValArg (CoVarAtom e)] 
106         | switchIsSet env SimplDoInlineFoldrBuild =
107         let
108                 tyL     = mkListTy ty
109                 ourCons = mkCoTyApp (CoVar consDataCon) ty
110                 ourNil  = mkCoTyApp (CoVar nilDataCon) ty
111         in
112         newIds  [ ty `mkFunTy` (tyL `mkFunTy` tyL),
113                   tyL ]                 `thenSmpl` \ [c,n] ->
114         returnSmpl(Just (CoLet (CoNonRec c ourCons)
115                         (CoLet (CoNonRec n ourNil)
116                          (CoApp (CoApp (mkCoTyApp (CoVar e) tyL) (CoVarAtom c)) (CoVarAtom n)))))
117 -- ToDo: add `build' without an argument instance.
118 -- This is strange, because of g's type.
119 build_fun env _ = 
120         ASSERT (not (switchIsSet env SimplDoInlineFoldrBuild))
121         returnSmpl Nothing
122
123 -- Now foldr, the way we consume lists.
124
125 foldr_fun :: SimplEnv
126           -> [PlainCoreArg]
127           -> SmplM (Maybe PlainCoreExpr)
128 {-
129 foldr_fun env _ 
130   | trace "HEHJDHF!" False = error "NEVER" 
131 -}
132 foldr_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:ValArg arg_z:rest_args)
133   | isConsFun env arg_k && isNilForm env arg_z
134   =     -- foldr (:) [] ==> id
135         -- this transformation is *always* benificial
136         -- cf.  foldr (:) [] (build g) == g (:) []
137         -- with foldr (:) [] (build g) == build g
138         -- after unfolding build, they are the same thing.
139      tick FoldrConsNil                  `thenSmpl_` 
140      newId (mkListTy ty1)               `thenSmpl` \ x ->
141      returnSmpl({-trace "foldr (:) []"-} (Just (applyToArgs (CoLam [x] (CoVar x)) rest_args)))
142
143 foldr_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:ValArg arg_z:ValArg arg_list:rest_args)
144  | do_fb_red && isNilForm env arg_list
145   =     -- foldr f z [] = z
146         -- again another short cut, helps with unroling of constant lists
147     tick Foldr_Nil      `thenSmpl_`
148     returnSmpl (Just (atomToExpr arg_z))
149
150   | do_fb_red && arg_list_isBuildForm 
151   =     -- foldr k z (build g) ==> g k z
152         -- this next line *is* the foldr/build rule proper.
153     tick FoldrBuild     `thenSmpl_`
154     returnSmpl (Just (applyToArgs (CoVar g) (TypeArg ty2:ValArg arg_k:ValArg arg_z:rest_args)))
155
156   | do_fb_red && arg_list_isAppendForm 
157   =     -- foldr k z (foldr (:) ys xs) <args> ==> foldr k (foldr k z ys) xs <args>
158         -- this unfolds foldr one into foldr
159     tick FoldrFoldr     `thenSmpl_`
160     newId ty2   `thenSmpl` \ other_foldr ->
161         let
162             inner_foldr = applyToArgs (CoVar foldrId) 
163                         [TypeArg ty1,TypeArg ty2,
164                          ValArg arg_k,ValArg arg_z,ValArg ys]
165             outer_foldr = applyToArgs (CoVar foldrId) 
166                         ([TypeArg ty1,TypeArg ty2,
167                          ValArg arg_k,ValArg (CoVarAtom other_foldr),ValArg xs]
168                         ++ rest_args)
169         in returnSmpl (Just (CoLet (CoNonRec other_foldr inner_foldr) outer_foldr))
170
171  | do_fb_red && arg_list_isListForm
172  =      -- foldr k z (a:b:c:rest) = 
173         --      (\ f -> f a (f b (f c (foldr f z rest)))) k rest_args
174         -- NB: 'k' is used just one by foldr, but 'f' is used many
175         -- times inside the list structure. This means that
176         -- 'f' needs to be inside a lambda, to make sure the simplifier
177         -- realises this.
178         -- 
179         -- The structure of     
180         --       f a (f b (f c (foldr f z rest)))
181         -- in core becomes:
182         --      let ele_1 = foldr f z rest
183         --          ele_2 = f c ele_1
184         --          ele_3 = f b ele_2
185         --      in f a ele_3
186         --
187   tick Foldr_List       `thenSmpl_`
188   newIds ( 
189                 ty1 `mkFunTy` (ty2 `mkFunTy` ty2) :
190                 take (length the_list) (repeat ty2)
191         )                       `thenSmpl` \ (f_id:ele_id1:ele_ids) ->
192   let
193         fst_bind = CoNonRec 
194                         ele_id1 
195                         (applyToArgs (CoVar foldrId) 
196                                 [TypeArg ty1,TypeArg ty2,
197                                  ValArg (CoVarAtom f_id),
198                                  ValArg arg_z,
199                                  ValArg the_tl])
200         --ToDo: look for a zipWith that checks for the same length of a 3 lists
201         rest_binds = zipWith3 
202                          (\ e v e' -> CoNonRec e (mkRhs v e'))
203                          ele_ids
204                          (reverse (tail the_list))
205                          (init (ele_id1:ele_ids))
206         mkRhs v e = CoApp (CoApp (CoVar f_id) v) (CoVarAtom e)
207         core_list = foldr
208                         CoLet 
209                         (mkRhs (head the_list) (last (ele_id1:ele_ids)))
210                         (fst_bind:rest_binds)
211   in
212         returnSmpl (Just (applyToArgs (CoLam [f_id] core_list)
213                                       (ValArg arg_k:rest_args)))
214
215  where
216    do_fb_red            = switchIsSet env SimplDoFoldrBuild
217
218    arg_list_isBuildForm = maybeToBool buildForm
219    buildForm            = getBuildForm env arg_list
220    (Just g)             = buildForm
221
222    arg_list_isListForm      = maybeToBool listForm
223    listForm                 = getListForm env arg_list
224    (Just (the_list,the_tl)) = listForm
225
226    arg_list_isAppendForm = maybeToBool appendForm
227    appendForm            = getAppendForm env arg_list
228    (Just (xs,ys))        = appendForm
229
230 foldr_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:rest_args)
231 {- OLD:
232   | doing_inlining && isConsFun env arg_k 
233   =    -- foldr (:) z xs = xs ++ z              
234      tick FoldrCons     `thenSmpl_`
235      newIds [ty2,mkListTy ty1] `thenSmpl` \ [z,x] ->
236      returnSmpl (Just (applyToArgs 
237                         (CoLam [z,x] (applyToArgs 
238                                         (CoVar appendId) [
239                                                 TypeArg ty1,
240                                                 ValArg (CoVarAtom x),
241                                                 ValArg (CoVarAtom z)]))
242                         rest_args))
243 -}
244   | doing_inlining && (isInterestingArg env arg_k  
245                        || isConsFun env arg_k)
246   =   -- foldr k args =                         
247       --        (\ f z xs ->
248       --          letrec                                 
249       --             h x = case x of
250       --                    [] -> z
251       --                    (a:b) -> f a (h b)
252       --          in
253       --             h xs) k args
254       --
255      tick FoldrInline           `thenSmpl_`
256      newIds [
257                 ty1,                    -- a :: t1
258                 mkListTy ty1,           -- b :: [t1]
259                 ty2,                    -- v :: t2
260                 mkListTy ty1,           -- x :: t1
261                 mkListTy ty1 `mkFunTy` ty2,
262                                         -- h :: [t1] -> t2
263                 ty1 `mkFunTy` (ty2 `mkFunTy` ty2),
264                                         -- f
265                 ty2,                    -- z
266                 mkListTy ty1            -- xs
267                         ] `thenSmpl` \ [a,b,v,x,h,f,z,xs] ->
268            let
269              h_rhs = (CoLam [x] (CoCase (CoVar x)
270                       (CoAlgAlts
271                           [(nilDataCon,[],atomToExpr (CoVarAtom z)),
272                            (consDataCon,[a,b],body)]
273                        CoNoDefault)))
274              body = CoLet (CoNonRec v (CoApp (CoVar h) (CoVarAtom b)))
275                           (CoApp (CoApp (atomToExpr (CoVarAtom f))
276                                                   (CoVarAtom a))
277                                                     (CoVarAtom v))
278            in
279              returnSmpl (Just 
280                      (applyToArgs
281                          (CoLam [f,z,xs]
282                           (CoLet (CoRec [(h,h_rhs)]) 
283                                  (CoApp (CoVar h) (CoVarAtom xs))))
284                      (ValArg arg_k:rest_args)))
285    where
286         doing_inlining = switchIsSet env SimplDoInlineFoldrBuild 
287 foldr_fun _ _ = returnSmpl Nothing
288
289 isConsFun :: SimplEnv -> PlainCoreAtom -> Bool
290 isConsFun env (CoVarAtom v) = 
291     case lookupUnfolding env v of
292         GeneralForm _ _ (CoLam [(x,_),(y,_)] 
293                         (CoCon con tys [CoVarAtom x',CoVarAtom y'])) _
294                         | con == consDataCon && x==x' && y==y'
295           -> ASSERT ( length tys == 1 ) True
296         _ -> False
297 isConsFun env _ = False
298
299 isNilForm :: SimplEnv -> PlainCoreAtom -> Bool
300 isNilForm env (CoVarAtom v) = 
301     case lookupUnfolding env v of
302         GeneralForm _ _ (CoTyApp (CoVar id) _) _
303           | id == nilDataCon -> True
304         ConstructorForm id _ _
305           | id == nilDataCon   -> True
306         LiteralForm (NoRepStr s) | _NULL_ s -> True
307         _ -> False
308 isNilForm env _ = False
309
310 getBuildForm :: SimplEnv -> PlainCoreAtom -> Maybe Id
311 getBuildForm env (CoVarAtom v) = 
312     case lookupUnfolding env v of
313         GeneralForm False _ _ _ -> Nothing      -- not allowed to inline :-(
314         GeneralForm _ _ (CoApp (CoTyApp (CoVar bld) _) (CoVarAtom g)) _
315           | bld == buildId -> Just g
316         _ -> Nothing
317 getBuildForm env _ = Nothing
318
319 getAppendForm :: SimplEnv -> PlainCoreAtom -> Maybe (CoreAtom Id,CoreAtom Id)
320 getAppendForm env (CoVarAtom v) = 
321     case lookupUnfolding env v of
322         GeneralForm False _ _ _ -> Nothing      -- not allowed to inline :-(
323         GeneralForm _ _ (CoApp (CoApp (CoApp (CoTyApp (CoTyApp (CoVar fld) _) _) con) ys) xs) _
324           | fld == foldrId && isConsFun env con -> Just (xs,ys)
325         _ -> Nothing
326 getAppendForm env _ = Nothing
327
328 --
329 -- this gets a list of the form a : b : c : d and returns ([a,b,c],d)
330 -- it natuarally follows that [a,b,c] => ([a,b,c],e), where e = []
331 --
332
333 getListForm
334         :: SimplEnv 
335         -> PlainCoreAtom 
336         -> Maybe ([PlainCoreAtom],PlainCoreAtom)
337 getListForm env (CoVarAtom v) = 
338     case lookupUnfolding env v of
339        ConstructorForm id _ [head,tail]
340           | id == consDataCon -> 
341                 case getListForm env tail of
342                    Nothing -> Just ([head],tail)
343                    Just (lst,new_tail) -> Just (head:lst,new_tail)
344        _ -> Nothing
345 getListForm env _ = Nothing
346
347 isInterestingArg :: SimplEnv -> PlainCoreAtom -> Bool
348 isInterestingArg env (CoVarAtom v) = 
349     case lookupUnfolding env v of
350        GeneralForm False _ _ UnfoldNever -> False
351        GeneralForm _ _ exp guide -> True
352        _ -> False
353 isInterestingArg env _ = False
354
355 foldl_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:ValArg arg_z:ValArg arg_list:rest_args)
356   | do_fb_red && isNilForm env arg_list
357   =     -- foldl f z [] = z
358         -- again another short cut, helps with unroling of constant lists
359     tick Foldr_Nil      `thenSmpl_`
360     returnSmpl (Just (atomToExpr arg_z))
361
362   | do_fb_red && arg_list_isBuildForm 
363   =     -- foldl t1 t2 k z (build t3 g) ==> 
364         --                 let c {- INLINE -} = \ b g' a -> g' (f a b) 
365         --                     n {- INLINE -} = \ a -> a
366         --                 in g t1 c n z
367         -- this next line *is* the foldr/build rule proper.
368     tick FoldrBuild     `thenSmpl_`
369         -- c :: t2 -> (t1 -> t1) -> t1 -> t1
370         -- n :: t1 -> t1
371     newIds [
372         {- pre_c -}     ty2 `mkFunTy` ((ty1 `mkFunTy` ty1) `mkFunTy` (ty1 `mkFunTy` ty1)),
373         {- pre_n -}     ty1 `mkFunTy` ty1,
374         {- b -}         ty2,
375         {- g' -}        ty1 `mkFunTy` ty1, 
376         {- a -}         ty1,
377         {- a' -}        ty1,    
378         {- t -}         ty1
379         ]               `thenSmpl` \ [pre_c,
380                                       pre_n,
381                                       b,
382                                       g',
383                                       a,
384                                       a',
385                                       t] ->
386
387     let
388         c     = addIdUnfolding pre_c (iWantToBeINLINEd UnfoldAlways)
389         c_rhs = CoLam [b,g',a]
390                  (CoLet (CoNonRec t (CoApp (CoApp (atomToExpr arg_k) (CoVarAtom a)) (CoVarAtom b)))
391                          (CoApp (CoVar g') (CoVarAtom t)))
392         n     = addIdUnfolding pre_n (iWantToBeINLINEd UnfoldAlways) 
393         n_rhs = CoLam [a'] (CoVar a')
394     in
395     returnSmpl (Just (CoLet (CoNonRec c c_rhs) (CoLet (CoNonRec n n_rhs) 
396                   (applyToArgs (CoVar g) 
397                       (TypeArg (ty1 `mkFunTy` ty1):ValArg (CoVarAtom c):ValArg (CoVarAtom n)
398                                 :ValArg arg_z:rest_args)))))
399
400
401   | do_fb_red && arg_list_isAppendForm 
402   =     -- foldl k z (foldr (:) ys xs) <args> ==> foldl k (foldl k z xs) ys <args>
403         -- be caseful with for order of xs / ys
404     tick FoldrFoldr     `thenSmpl_`
405     newId ty1   `thenSmpl` \ other_foldl ->
406         let
407             inner_foldl = applyToArgs (CoVar foldlId) 
408                         [TypeArg ty1,TypeArg ty2,
409                          ValArg arg_k,ValArg arg_z,ValArg xs]
410             outer_foldl = applyToArgs (CoVar foldlId) 
411                         ([TypeArg ty1,TypeArg ty2,
412                          ValArg arg_k,ValArg (CoVarAtom other_foldl),ValArg ys]
413                         ++ rest_args)
414         in returnSmpl (Just (CoLet (CoNonRec other_foldl inner_foldl) outer_foldl))
415
416  | do_fb_red && arg_list_isListForm
417  =      -- foldl k z (a:b:c:rest) = 
418         --      (\ f -> foldl f (f (f (f z a) b) c) rest) k rest_args
419         -- NB: 'k' is used just one by foldr, but 'f' is used many
420         -- times inside the list structure. This means that
421         -- 'f' needs to be inside a lambda, to make sure the simplifier
422         -- realises this.
423         -- 
424         -- The structure of     
425         --       foldl f (f (f (f z a) b) c) rest
426         --       f a (f b (f c (foldr f z rest)))
427         -- in core becomes:
428         --      let ele_1 = f z a
429         --          ele_2 = f ele_1 b
430         --          ele_3 = f ele_2 c
431         --      in foldl f ele_3 rest
432         --
433   tick Foldr_List       `thenSmpl_`
434   newIds ( 
435                 ty1 `mkFunTy` (ty2 `mkFunTy` ty1) :
436                 take (length the_list) (repeat ty1)
437         )                       `thenSmpl` \ (f_id:ele_ids) ->
438   let
439         --ToDo: look for a zipWith that checks for the same length of a 3 lists
440         rest_binds = zipWith3 
441                          (\ e v e' -> CoNonRec e (mkRhs v e'))
442                          ele_ids                                -- :: [Id]
443                          the_list                               -- :: [PlainCoreAtom]
444                          (init (arg_z:map CoVarAtom ele_ids))   -- :: [PlainCoreAtom]
445         mkRhs v e = CoApp (CoApp (CoVar f_id) e) v
446
447         last_bind = applyToArgs (CoVar foldlId) 
448                                 [TypeArg ty1,TypeArg ty2,
449                                  ValArg (CoVarAtom f_id),
450                                  ValArg (CoVarAtom (last ele_ids)),
451                                  ValArg the_tl]
452         core_list = foldr
453                         CoLet 
454                         last_bind
455                         rest_binds
456   in
457         returnSmpl (Just (applyToArgs (CoLam [f_id] core_list)
458                                       (ValArg arg_k:rest_args)))
459
460  where
461    do_fb_red            = switchIsSet env SimplDoFoldrBuild
462
463    arg_list_isBuildForm = maybeToBool buildForm
464    buildForm            = getBuildForm env arg_list
465    (Just g)             = buildForm
466
467    arg_list_isListForm      = maybeToBool listForm
468    listForm                 = getListForm env arg_list
469    (Just (the_list,the_tl)) = listForm
470
471    arg_list_isAppendForm = maybeToBool appendForm
472    appendForm            = getAppendForm env arg_list
473    (Just (xs,ys))        = appendForm
474
475 foldl_fun env (TypeArg ty1:TypeArg ty2:ValArg arg_k:rest_args)
476   | doing_inlining && (isInterestingArg env arg_k  
477                        || isConsFun env arg_k)
478   =   -- foldl k args =                         
479       --        (\ f z xs ->
480       --          letrec                                 
481       --             h x r = case x of
482       --                      []    -> r
483       --                      (a:b) -> h b (f r a)
484       --          in
485       --             h xs z) k args
486       --
487      tick FoldrInline                           `thenSmpl_`
488      newIds [
489                 ty2,                    -- a :: t1
490                 mkListTy ty2,           -- b :: [t1]
491                 ty1,                    -- v :: t2
492                 mkListTy ty2,           -- x :: t1
493                 mkListTy ty2 `mkFunTy` (ty1 `mkFunTy` ty1),
494                                         -- h :: [t2] -> t1 -> t1
495                 ty1 `mkFunTy` (ty2 `mkFunTy` ty1),
496                                         -- f
497                 ty1,                    -- z
498                 mkListTy ty2,           -- xs
499                 ty1                     -- r
500                         ] `thenSmpl` \ [a,b,v,x,h,f,z,xs,r] ->
501            let
502              h_rhs = (CoLam [x,r] (CoCase (CoVar x)
503                       (CoAlgAlts
504                           [(nilDataCon,[],atomToExpr (CoVarAtom r)),
505                            (consDataCon,[a,b],body)]
506                        CoNoDefault)))
507              body = CoLet (CoNonRec v (CoApp (CoApp (CoVar f) (CoVarAtom r))
508                                                               (CoVarAtom a)))
509                           (CoApp (CoApp (atomToExpr (CoVarAtom h))
510                                                   (CoVarAtom b))
511                                                     (CoVarAtom v))
512            in
513              returnSmpl (Just 
514                      (applyToArgs
515                          (CoLam [f,z,xs]
516                           (CoLet (CoRec [(h,h_rhs)]) 
517                                  (CoApp (CoApp (CoVar h) (CoVarAtom xs)) 
518                                                          (CoVarAtom z))))
519                      (ValArg arg_k:rest_args)))
520    where
521         doing_inlining = switchIsSet env SimplDoInlineFoldrBuild 
522
523 foldl_fun env _ = returnSmpl Nothing
524 \end{code}
525