summaryrefslogtreecommitdiff
path: root/ghc/compiler/simplCore/BinderInfo.lhs
blob: 9b44d2ee41b411c61dce9a18308febb98a6350dd (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
%
% (c) The GRASP/AQUA Project, Glasgow University, 1992-1996
%
%************************************************************************
%*									*
\section[BinderInfo]{Information attached to binders by SubstAnal}
%*									*
%************************************************************************

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

module BinderInfo (
	BinderInfo(..),
	FunOrArg, DuplicationDanger, InsideSCC,  -- NB: all abstract (yay!)

	inlineUnconditionally, okToInline,

	addBinderInfo, orBinderInfo, andBinderInfo,

	argOccurrence, funOccurrence, dangerousArgOcc, noBinderInfo,
	markMany, markDangerousToDup, markInsideSCC,
	getBinderInfoArity,
	setBinderInfoArityToZero,

	isFun, isDupDanger -- for Simon Marlow deforestation
    ) where

IMP_Ubiq(){-uitous-}

import CoreUnfold	( FormSummary(..) )
import Pretty
import Util		( panic )
\end{code}

The @BinderInfo@ describes how a variable is used in a given scope.

NOTE: With SCCs we have to be careful what we unfold! We don't want to
change the attribution of execution costs. If we decide to unfold
within an SCC we can tag the definition as @DontKeepBinder@.
Definitions tagged as @KeepBinder@ are discarded when we enter the
scope of an SCC.

\begin{code}
data BinderInfo
  = DeadCode	-- Dead code; discard the binding.

  | ManyOcc	-- Everything else besides DeadCode and OneOccs

	Int	-- number of arguments on stack when called; this is a minimum guarantee


  | OneOcc	-- Just one occurrence (or one each in
		-- mutually-exclusive case alts).

      FunOrArg	-- How it occurs

      DuplicationDanger

      InsideSCC

      Int	-- Number of mutually-exclusive case alternatives
		-- in which it occurs

		-- Note that we only worry about the case-alt counts
		-- if the OneOcc is substitutable -- that's the only
		-- time we *use* the info; we could be more clever for
		-- other cases if we really had to. (WDP/PS)

      Int	-- number of arguments on stack when called; minimum guarantee

-- In general, we are feel free to substitute unless
-- (a) is in an argument position (ArgOcc)
-- (b) is inside a lambda [or type lambda?] (DupDanger)
-- (c) is inside an SCC expression (InsideSCC)
-- (d) is in the RHS of a binding for a variable with an INLINE pragma
--	(because the RHS will be inlined regardless of its size)
--	[again, DupDanger]

data FunOrArg
  = FunOcc 	-- An occurrence in a function position
  | ArgOcc	-- Other arg occurrence

    -- When combining branches of a case, only report FunOcc if
    -- both branches are FunOccs

data DuplicationDanger
  = DupDanger	-- Inside a non-linear lambda (that is, a lambda which
		-- is sure to be instantiated only once), or inside
		-- the rhs of an INLINE-pragma'd thing.  Either way,
		-- substituting a redex for this occurrence is
		-- dangerous because it might duplicate work.

  | NoDupDanger	-- It's ok; substitution won't duplicate work.

data InsideSCC
  = InsideSCC	    -- Inside an SCC; so be careful when substituting.
  | NotInsideSCC    -- It's ok.

noBinderInfo = ManyOcc 0	-- A non-committal value
\end{code}


Predicates
~~~~~~~~~~

\begin{code}
okToInline
	:: FormSummary	-- What the thing to be inlined is like
	-> BinderInfo 	-- How the thing to be inlined occurs
	-> Bool		-- True => it's small enough to inline
	-> Bool		-- True => yes, inline it

-- Always inline bottoms
okToInline BottomForm occ_info small_enough
  = True	-- Unless one of the type args is unboxed??
		-- This used to be checked for, but I can't
		-- see why so I've left it out.

-- A WHNF can be inlined if it occurs once, or is small
okToInline form occ_info small_enough
 | is_whnf_form form
 = small_enough || one_occ
 where
   one_occ = case occ_info of
		OneOcc _ _ _ n_alts _ -> n_alts <= 1
		other		      -> False
   	
   is_whnf_form VarForm   = True
   is_whnf_form ValueForm = True
   is_whnf_form other     = False
    
-- A non-WHNF can be inlined if it doesn't occur inside a lambda,
-- and occurs exactly once or 
--     occurs once in each branch of a case and is small
okToInline OtherForm (OneOcc _ NoDupDanger _ n_alts _) small_enough 
  = n_alts <= 1 || small_enough

okToInline form any_occ small_enough = False
\end{code}

@inlineUnconditionally@ decides whether a let-bound thing can
definitely be inlined.

\begin{code}
inlineUnconditionally :: Bool -> BinderInfo -> Bool

--inlineUnconditionally ok_to_dup DeadCode = True
inlineUnconditionally ok_to_dup (OneOcc FunOcc NoDupDanger NotInsideSCC n_alt_occs _)
  = n_alt_occs <= 1 || ok_to_dup
	    -- We [i.e., Patrick] don't mind the code explosion,
	    -- though.  We could have a flag to limit the
	    -- damage, e.g., limit to M alternatives.

inlineUnconditionally _ _ = False
\end{code}

\begin{code}
isFun :: FunOrArg -> Bool
isFun FunOcc = True
isFun _ = False

isDupDanger :: DuplicationDanger -> Bool
isDupDanger DupDanger = True
isDupDanger _ = False
\end{code}


Construction
~~~~~~~~~~~~~
\begin{code}
argOccurrence, funOccurrence :: Int -> BinderInfo

funOccurrence = OneOcc FunOcc NoDupDanger NotInsideSCC 1
argOccurrence = OneOcc ArgOcc NoDupDanger NotInsideSCC 1

markMany, markDangerousToDup, markInsideSCC :: BinderInfo -> BinderInfo

markMany (OneOcc _ _ _ _ ar) = ManyOcc ar
markMany (ManyOcc ar) 	     = ManyOcc ar
markMany DeadCode	     = panic "markMany"

markDangerousToDup (OneOcc posn _ in_scc n_alts ar)
  = OneOcc posn DupDanger in_scc n_alts ar
markDangerousToDup other = other

dangerousArgOcc = OneOcc ArgOcc DupDanger NotInsideSCC 1 0

markInsideSCC (OneOcc posn dup_danger _ n_alts ar)
  = OneOcc posn dup_danger InsideSCC n_alts ar
markInsideSCC other = other

addBinderInfo, orBinderInfo
	:: BinderInfo -> BinderInfo -> BinderInfo

addBinderInfo DeadCode info2 = info2
addBinderInfo info1 DeadCode = info1
addBinderInfo info1 info2
	= ManyOcc (min (getBinderInfoArity info1) (getBinderInfoArity info2))

-- (orBinderInfo orig new) is used when combining occurrence 
-- info from branches of a case

orBinderInfo DeadCode info2 = info2
orBinderInfo info1 DeadCode = info1
orBinderInfo (OneOcc posn1 dup1 scc1 n_alts1 ar_1)
	     (OneOcc posn2 dup2 scc2 n_alts2 ar_2)
  = OneOcc (combine_posns posn1 posn2)
	   (combine_dups  dup1  dup2)
	   (combine_sccs  scc1  scc2)
	   (n_alts1 + n_alts2)
	   (min ar_1 ar_2)
orBinderInfo info1 info2
	= ManyOcc (min (getBinderInfoArity info1) (getBinderInfoArity info2))

-- (andBinderInfo orig new) is used in two situations:
-- First, when a variable whose occurrence info
--   is currently "orig" is bound to a variable whose occurrence info is "new"
--	eg  (\new -> e) orig
--   What we want to do is to *worsen* orig's info to take account of new's
--
-- second, when completing a let-binding
--	let new = ...orig...
-- we compute the way orig occurs in (...orig...), and then use orBinderInfo
-- to worsen this info by the way new occurs in the let body; then we use
-- that to worsen orig's currently recorded occurrence info.

andBinderInfo DeadCode info2 = DeadCode
andBinderInfo info1 DeadCode = DeadCode
andBinderInfo (OneOcc posn1 dup1 scc1 n_alts1 ar_1)
	      (OneOcc posn2 dup2 scc2 n_alts2 ar_2)
  = OneOcc (combine_posns posn1 posn2)
	   (combine_dups  dup1  dup2)
	   (combine_sccs  scc1  scc2)
	   (n_alts1 + n_alts2)
	   ar_1					-- Min arity just from orig
andBinderInfo info1 info2 = ManyOcc (getBinderInfoArity info1)


combine_posns FunOcc FunOcc = FunOcc -- see comment at FunOrArg defn
combine_posns _  	 _  = ArgOcc

combine_dups DupDanger _ = DupDanger	-- Too paranoid?? ToDo
combine_dups _ DupDanger = DupDanger
combine_dups _ _	     = NoDupDanger

combine_sccs InsideSCC _ = InsideSCC	-- Too paranoid?? ToDo
combine_sccs _ InsideSCC = InsideSCC
combine_sccs _ _	     = NotInsideSCC

setBinderInfoArityToZero :: BinderInfo -> BinderInfo
setBinderInfoArityToZero DeadCode    = DeadCode
setBinderInfoArityToZero (ManyOcc _) = ManyOcc 0
setBinderInfoArityToZero (OneOcc fa dd sc i _) = OneOcc fa dd sc i 0
\end{code}

\begin{code}
getBinderInfoArity (DeadCode) = 0
getBinderInfoArity (ManyOcc i) = i
getBinderInfoArity (OneOcc _ _ _ _ i) = i
\end{code}

\begin{code}
instance Outputable BinderInfo where
  ppr sty DeadCode     = ppStr "Dead"
  ppr sty (ManyOcc ar) = ppBesides [ ppStr "Many-", ppInt ar ]
  ppr sty (OneOcc posn dup_danger in_scc n_alts ar)
    = ppBesides [ ppStr "One-", pp_posn posn, ppChar '-', pp_danger dup_danger,
		  ppChar '-', pp_scc in_scc,  ppChar '-', ppInt n_alts,
		  ppChar '-', ppInt ar ]
    where
      pp_posn FunOcc = ppStr "fun"
      pp_posn ArgOcc = ppStr "arg"

      pp_danger DupDanger   = ppStr "*dup*"
      pp_danger NoDupDanger = ppStr "nodup"

      pp_scc InsideSCC	  = ppStr "*SCC*"
      pp_scc NotInsideSCC = ppStr "noscc"
\end{code}