summaryrefslogtreecommitdiff
path: root/ghc/compiler/simplCore/LiberateCase.lhs
blob: 908f28a52350e52a9a694a08b8e682fa333f2ffa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
%
% (c) The AQUA Project, Glasgow University, 1994
%
\section[LiberateCase]{Unroll recursion to allow evals to be lifted from a loop}


\begin{code}
#include "HsVersions.h"

module LiberateCase ( liberateCase ) where

IMPORT_Trace

import Id		( localiseId, toplevelishId{-debugging-} )
import IdEnv
import Maybes
import Outputable
import PlainCore
import Pretty
import SimplEnv		( UnfoldingGuidance(..) )
import Util
\end{code}

This module walks over @Core@, and looks for @case@ on free variables.
The criterion is:
	if there is case on a free on the route to the recursive call,
	then the recursive call is replaced with an unfolding.

Example

\begin{verbatim}
f = \ t -> case v of
	       V a b -> a : f t 
\end{verbatim}

=> the inner f is replaced.

\begin{verbatim}
f = \ t -> case v of
	       V a b -> a : (letrec
				f =  \ t -> case v of
					       V a b -> a : f t 
			     in f) t 
\end{verbatim}
(note the NEED for shadowing)

=> Run Andr\'e's wonder pass ...
\begin{verbatim}
f = \ t -> case v of
	       V a b -> a : (letrec
				f = \ t -> a : f t 
			     in f t)
\begin{verbatim}
Better code, because 'a' is  free inside the inner letrec, rather
than needing projection from v.


To think about (Apr 94)
~~~~~~~~~~~~~~

Main worry: duplicating code excessively.  At the moment we duplicate
the entire binding group once at each recursive call.  But there may
be a group of recursive calls which share a common set of evaluated
free variables, in which case the duplication is a plain waste.

Another thing we could consider adding is some unfold-threshold thing,
so that we'll only duplicate if the size of the group rhss isn't too
big.

Data types
~~~~~~~~~~

The ``level'' of a binder tells how many
recursive defns lexically enclose the binding
A recursive defn "encloses" its RHS, not its
scope.  For example:
\begin{verbatim}
	letrec f = let g = ... in ...
	in
	let h = ...
	in ...
\end{verbatim}
Here, the level of @f@ is zero, the level of @g@ is one, 
and the level of @h@ is zero (NB not one).

\begin{code}
type LibCaseLevel = Int		

topLevel :: LibCaseLevel
topLevel = 0
\end{code}

\begin{code}
data LibCaseEnv 
  = LibCaseEnv
	Int			-- Bomb-out size for deciding if
				-- potential liberatees are too big.
				-- (passed in from cmd-line args)

	LibCaseLevel		-- Current level

	(IdEnv LibCaseLevel)	-- Binds all non-top-level in-scope Ids
				-- (top-level and imported things have
				-- a level of zero)

	(IdEnv PlainCoreBinding)-- Binds *only* recursively defined
				-- Ids, to their own binding group,
				-- and *only* in their own RHSs

	[(Id,LibCaseLevel)]     -- Each of these Ids was scrutinised by an
				-- enclosing case expression, with the
				-- specified number of enclosing
				-- recursive bindings; furthermore,
				-- the Id is bound at a lower level
				-- than the case expression.  The
				-- order is insignificant; it's a bag
				-- really

initEnv :: Int -> LibCaseEnv
initEnv bomb_size = LibCaseEnv bomb_size 0 nullIdEnv nullIdEnv []

bombOutSize (LibCaseEnv bomb_size _ _ _ _) = bomb_size
\end{code}


Programs
~~~~~~~~
\begin{code}
liberateCase :: Int -> [PlainCoreBinding] -> [PlainCoreBinding]
liberateCase bomb_size prog
  = do_prog (initEnv bomb_size) prog
  where
    do_prog env [] = []
    do_prog env (bind:binds) = bind' : do_prog env' binds
			     where
			       (env', bind') = libCaseBind env bind
\end{code}

Bindings
~~~~~~~~

\begin{code}
libCaseBind :: LibCaseEnv -> PlainCoreBinding -> (LibCaseEnv, PlainCoreBinding)

libCaseBind env (CoNonRec binder rhs)
  = (addBinders env [binder], CoNonRec binder (libCase env rhs))

libCaseBind env (CoRec pairs)
  = (env_body, CoRec pairs') 
  where
    (binders, rhss) = unzip pairs

    env_body = addBinders env binders

    pairs' = [(binder, libCase env_rhs rhs) | (binder,rhs) <- pairs]

    env_rhs = if all rhs_small_enough rhss then extended_env else env

	-- We extend the rec-env by binding each Id to its rhs, first
	-- processing the rhs with an *un-extended* environment, so
	-- that the same process doesn't occur for ever!

    extended_env
      = addRecBinds env [ (localiseId binder, libCase env_body rhs)
			| (binder, rhs) <- pairs ]

	-- Why "localiseId" above?  Because we're creating a new local
	-- copy of the original binding.  In particular, the original
	-- binding might have been for a TopLevId, and this copy clearly
	-- will not be top-level!
	
	-- It is enough to change just the binder, because subsequent
	-- simplification will propagate the right info from the binder.

	-- Why does it matter?  Because the codeGen keeps a separate
	-- environment for top-level Ids, and it is disastrous for it
	-- to think that something is top-level when it isn't.

    rhs_small_enough rhs
      = case (calcUnfoldingGuidance True{-sccs OK-} lIBERATE_BOMB_SIZE rhs) of
	  UnfoldNever -> False
	  _ 	      -> True	-- we didn't BOMB, so it must be OK

    lIBERATE_BOMB_SIZE = bombOutSize env
\end{code}


Expressions
~~~~~~~~~~~

\begin{code}
libCase :: LibCaseEnv
	-> PlainCoreExpr
	-> PlainCoreExpr

libCase env (CoLit lit)		 = CoLit lit
libCase env (CoVar v)		 = mkCoLetsNoUnboxed (libCaseId env v) (CoVar v)
libCase env (CoApp fun arg)      = mkCoLetsNoUnboxed (libCaseAtom env arg) (CoApp (libCase env fun) arg)
libCase env (CoTyApp fun ty)     = CoTyApp (libCase env fun) ty
libCase env (CoCon con tys args) = mkCoLetsNoUnboxed (libCaseAtoms env args) (CoCon con tys args)
libCase env (CoPrim op tys args) = mkCoLetsNoUnboxed (libCaseAtoms env args) (CoPrim op tys args)
libCase env (CoTyLam tyvar body) = CoTyLam tyvar (libCase env body)
libCase env (CoSCC cc body)      = CoSCC cc (libCase env body)

libCase env (CoLam binders body)
  = CoLam binders (libCase env' body)
  where
    env' = addBinders env binders

libCase env (CoLet bind body) 
  = CoLet bind' (libCase env_body body)
  where
    (env_body, bind') = libCaseBind env bind

libCase env (CoCase scrut alts)
  = CoCase (libCase env scrut) (libCaseAlts env_alts alts)
  where
    env_alts = case scrut of
		  CoVar scrut_var -> addScrutedVar env scrut_var
		  other		  -> env
\end{code}


Case alternatives
~~~~~~~~~~~~~~~~~

\begin{code}
libCaseAlts env (CoAlgAlts alts deflt)
  = CoAlgAlts (map do_alt alts) (libCaseDeflt env deflt)
  where
    do_alt (con,args,rhs) = (con, args, libCase (addBinders env args) rhs)

libCaseAlts env (CoPrimAlts alts deflt)
  = CoPrimAlts (map do_alt alts) (libCaseDeflt env deflt) 
  where
    do_alt (lit,rhs) = (lit, libCase env rhs)

libCaseDeflt env CoNoDefault 
   = CoNoDefault
libCaseDeflt env (CoBindDefault binder rhs) 
   = CoBindDefault binder (libCase (addBinders env [binder]) rhs)
\end{code}

Atoms and Ids
~~~~~~~~~~~~~
\begin{code}
libCaseAtoms :: LibCaseEnv -> [PlainCoreAtom] -> [PlainCoreBinding]
libCaseAtoms env atoms = concat [libCaseAtom env atom | atom <- atoms]

libCaseAtom :: LibCaseEnv -> PlainCoreAtom -> [PlainCoreBinding]
libCaseAtom env (CoVarAtom arg_id) = libCaseId env arg_id
libCaseAtom env (CoLitAtom lit)    = []

libCaseId :: LibCaseEnv -> Id -> [PlainCoreBinding]
libCaseId env v
  | maybeToBool maybe_rec_bind &&	-- It's a use of a recursive thing
    there_are_free_scruts		-- with free vars scrutinised in RHS
  = [the_bind]

  | otherwise
  = []

  where
    maybe_rec_bind :: Maybe PlainCoreBinding	-- The binding of the recursive thingy
    maybe_rec_bind = lookupRecId env v
    Just the_bind = maybe_rec_bind

    rec_id_level = lookupLevel env v

    there_are_free_scruts = freeScruts env rec_id_level
\end{code}			



Utility functions
~~~~~~~~~~~~~~~~~
\begin{code}
addBinders :: LibCaseEnv -> [Id] -> LibCaseEnv
addBinders (LibCaseEnv bomb lvl lvl_env rec_env scruts) binders
  = LibCaseEnv bomb lvl lvl_env' rec_env scruts
  where
    lvl_env' = growIdEnvList lvl_env (binders `zip` repeat lvl)

addRecBinds :: LibCaseEnv -> [(Id,PlainCoreExpr)] -> LibCaseEnv
addRecBinds (LibCaseEnv bomb lvl lvl_env rec_env scruts) pairs
  = LibCaseEnv bomb lvl' lvl_env' rec_env' scruts
  where
    lvl'     = lvl + 1
    lvl_env' = growIdEnvList lvl_env [(binder,lvl) | (binder,_) <- pairs]
    rec_env' = growIdEnvList rec_env [(binder, CoRec pairs) | (binder,_) <- pairs]

addScrutedVar :: LibCaseEnv 
	      -> Id		-- This Id is being scrutinised by a case expression
	      -> LibCaseEnv	

addScrutedVar env@(LibCaseEnv bomb lvl lvl_env rec_env scruts) scrut_var
  | bind_lvl < lvl
  = LibCaseEnv bomb lvl lvl_env rec_env scruts'
	-- Add to scruts iff the scrut_var is being scrutinised at
	-- a deeper level than its defn 

  | otherwise = env
  where
    scruts'  = (scrut_var, lvl) : scruts
    bind_lvl = case lookupIdEnv lvl_env scrut_var of
		 Just lvl -> lvl
		 Nothing  -> --false: ASSERT(toplevelishId scrut_var)
			     topLevel

lookupRecId :: LibCaseEnv -> Id -> Maybe PlainCoreBinding
lookupRecId (LibCaseEnv bomb lvl lvl_env rec_env scruts) id
#ifndef DEBUG
  = lookupIdEnv rec_env id
#else
  = case (lookupIdEnv rec_env id) of
      xxx@(Just _) -> xxx
      xxx	   -> --false: ASSERT(toplevelishId id)
		      xxx
#endif

lookupLevel :: LibCaseEnv -> Id -> LibCaseLevel
lookupLevel (LibCaseEnv bomb lvl lvl_env rec_env scruts) id
  = case lookupIdEnv lvl_env id of
      Just lvl -> lvl
      Nothing  -> ASSERT(toplevelishId id)
		  topLevel

freeScruts :: LibCaseEnv 
	   -> LibCaseLevel 	-- Level of the recursive Id
	   -> Bool		-- True <=> there is an enclosing case of a variable
				-- bound outside (ie level <=) the recursive Id.
freeScruts (LibCaseEnv bomb lvl lvl_env rec_env scruts) rec_bind_lvl
  = not (null free_scruts)
  where
    free_scruts = [v | (v,lvl) <- scruts, lvl > rec_bind_lvl]
\end{code}