Do not inline or apply rules on LHS of rules
[ghc.git] / compiler / simplCore / FloatIn.hs
1 {-
2 (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
3
4 ************************************************************************
5 * *
6 \section[FloatIn]{Floating Inwards pass}
7 * *
8 ************************************************************************
9
10 The main purpose of @floatInwards@ is floating into branches of a
11 case, so that we don't allocate things, save them on the stack, and
12 then discover that they aren't needed in the chosen branch.
13 -}
14
15 {-# LANGUAGE CPP #-}
16
17 module FloatIn ( floatInwards ) where
18
19 #include "HsVersions.h"
20
21 import CoreSyn
22 import MkCore
23 import CoreUtils ( exprIsDupable, exprIsExpandable, exprType,
24 exprOkForSideEffects, mkTicks )
25 import CoreFVs ( CoreExprWithFVs, freeVars, freeVarsOf, idRuleAndUnfoldingVars )
26 import Id ( isOneShotBndr, idType )
27 import Var
28 import Type ( Type, isUnLiftedType, splitFunTy, applyTy )
29 import VarSet
30 import Util
31 import UniqFM
32 import DynFlags
33 import Outputable
34 import Data.List( mapAccumL )
35
36 {-
37 Top-level interface function, @floatInwards@. Note that we do not
38 actually float any bindings downwards from the top-level.
39 -}
40
41 floatInwards :: DynFlags -> CoreProgram -> CoreProgram
42 floatInwards dflags = map fi_top_bind
43 where
44 fi_top_bind (NonRec binder rhs)
45 = NonRec binder (fiExpr dflags [] (freeVars rhs))
46 fi_top_bind (Rec pairs)
47 = Rec [ (b, fiExpr dflags [] (freeVars rhs)) | (b, rhs) <- pairs ]
48
49 {-
50 ************************************************************************
51 * *
52 \subsection{Mail from Andr\'e [edited]}
53 * *
54 ************************************************************************
55
56 {\em Will wrote: What??? I thought the idea was to float as far
57 inwards as possible, no matter what. This is dropping all bindings
58 every time it sees a lambda of any kind. Help! }
59
60 You are assuming we DO DO full laziness AFTER floating inwards! We
61 have to [not float inside lambdas] if we don't.
62
63 If we indeed do full laziness after the floating inwards (we could
64 check the compilation flags for that) then I agree we could be more
65 aggressive and do float inwards past lambdas.
66
67 Actually we are not doing a proper full laziness (see below), which
68 was another reason for not floating inwards past a lambda.
69
70 This can easily be fixed. The problem is that we float lets outwards,
71 but there are a few expressions which are not let bound, like case
72 scrutinees and case alternatives. After floating inwards the
73 simplifier could decide to inline the let and the laziness would be
74 lost, e.g.
75
76 \begin{verbatim}
77 let a = expensive ==> \b -> case expensive of ...
78 in \ b -> case a of ...
79 \end{verbatim}
80 The fix is
81 \begin{enumerate}
82 \item
83 to let bind the algebraic case scrutinees (done, I think) and
84 the case alternatives (except the ones with an
85 unboxed type)(not done, I think). This is best done in the
86 SetLevels.hs module, which tags things with their level numbers.
87 \item
88 do the full laziness pass (floating lets outwards).
89 \item
90 simplify. The simplifier inlines the (trivial) lets that were
91 created but were not floated outwards.
92 \end{enumerate}
93
94 With the fix I think Will's suggestion that we can gain even more from
95 strictness by floating inwards past lambdas makes sense.
96
97 We still gain even without going past lambdas, as things may be
98 strict in the (new) context of a branch (where it was floated to) or
99 of a let rhs, e.g.
100 \begin{verbatim}
101 let a = something case x of
102 in case x of alt1 -> case something of a -> a + a
103 alt1 -> a + a ==> alt2 -> b
104 alt2 -> b
105
106 let a = something let b = case something of a -> a + a
107 in let b = a + a ==> in (b,b)
108 in (b,b)
109 \end{verbatim}
110 Also, even if a is not found to be strict in the new context and is
111 still left as a let, if the branch is not taken (or b is not entered)
112 the closure for a is not built.
113
114 ************************************************************************
115 * *
116 \subsection{Main floating-inwards code}
117 * *
118 ************************************************************************
119 -}
120
121 type FreeVarSet = IdSet
122 type BoundVarSet = IdSet
123
124 data FloatInBind = FB BoundVarSet FreeVarSet FloatBind
125 -- The FreeVarSet is the free variables of the binding. In the case
126 -- of recursive bindings, the set doesn't include the bound
127 -- variables.
128
129 type FloatInBinds = [FloatInBind]
130 -- In reverse dependency order (innermost binder first)
131
132 fiExpr :: DynFlags
133 -> FloatInBinds -- Binds we're trying to drop
134 -- as far "inwards" as possible
135 -> CoreExprWithFVs -- Input expr
136 -> CoreExpr -- Result
137
138 fiExpr _ to_drop (_, AnnLit lit) = ASSERT( null to_drop ) Lit lit
139 fiExpr _ to_drop (_, AnnType ty) = ASSERT( null to_drop ) Type ty
140 fiExpr _ to_drop (_, AnnVar v) = wrapFloats to_drop (Var v)
141 fiExpr _ to_drop (_, AnnCoercion co) = wrapFloats to_drop (Coercion co)
142 fiExpr dflags to_drop (_, AnnCast expr (fvs_co, co))
143 = wrapFloats (drop_here ++ co_drop) $
144 Cast (fiExpr dflags e_drop expr) co
145 where
146 [drop_here, e_drop, co_drop] = sepBindsByDropPoint dflags False [freeVarsOf expr, fvs_co] to_drop
147
148 {-
149 Applications: we do float inside applications, mainly because we
150 need to get at all the arguments. The next simplifier run will
151 pull out any silly ones.
152 -}
153
154 fiExpr dflags to_drop ann_expr@(_,AnnApp {})
155 = mkTicks ticks $ wrapFloats drop_here $ wrapFloats extra_drop $
156 mkApps (fiExpr dflags fun_drop ann_fun)
157 (zipWith (fiExpr dflags) arg_drops ann_args)
158 where
159 (ann_fun@(fun_fvs, _), ann_args, ticks)
160 = collectAnnArgsTicks tickishFloatable ann_expr
161 fun_ty = exprType (deAnnotate ann_fun)
162 ((_,extra_fvs), arg_fvs) = mapAccumL mk_arg_fvs (fun_ty, emptyVarSet) ann_args
163
164 -- All this faffing about is so that we can get hold of
165 -- the types of the arguments, to pass to noFloatIntoRhs
166 mk_arg_fvs :: (Type, FreeVarSet) -> CoreExprWithFVs -> ((Type, FreeVarSet), FreeVarSet)
167 mk_arg_fvs (fun_ty, extra_fvs) (_, AnnType ty)
168 = ((applyTy fun_ty ty, extra_fvs), emptyVarSet)
169
170 mk_arg_fvs (fun_ty, extra_fvs) (arg_fvs, ann_arg)
171 | noFloatIntoRhs ann_arg arg_ty
172 = ((res_ty, extra_fvs `unionVarSet` arg_fvs), emptyVarSet)
173 | otherwise
174 = ((res_ty, extra_fvs), arg_fvs)
175 where
176 (arg_ty, res_ty) = splitFunTy fun_ty
177
178 drop_here : extra_drop : fun_drop : arg_drops
179 = sepBindsByDropPoint dflags False (extra_fvs : fun_fvs : arg_fvs) to_drop
180
181 {-
182 Note [Do not destroy the let/app invariant]
183 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
184 Watch out for
185 f (x +# y)
186 We don't want to float bindings into here
187 f (case ... of { x -> x +# y })
188 because that might destroy the let/app invariant, which requires
189 unlifted function arguments to be ok-for-speculation.
190
191 Note [Floating in past a lambda group]
192 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
193 * We must be careful about floating inside a value lambda.
194 That risks losing laziness.
195 The float-out pass might rescue us, but then again it might not.
196
197 * We must be careful about type lambdas too. At one time we did, and
198 there is no risk of duplicating work thereby, but we do need to be
199 careful. In particular, here is a bad case (it happened in the
200 cichelli benchmark:
201 let v = ...
202 in let f = /\t -> \a -> ...
203 ==>
204 let f = /\t -> let v = ... in \a -> ...
205 This is bad as now f is an updatable closure (update PAP)
206 and has arity 0.
207
208 * Hack alert! We only float in through one-shot lambdas,
209 not (as you might guess) through lone big lambdas.
210 Reason: we float *out* past big lambdas (see the test in the Lam
211 case of FloatOut.floatExpr) and we don't want to float straight
212 back in again.
213
214 It *is* important to float into one-shot lambdas, however;
215 see the remarks with noFloatIntoRhs.
216
217 So we treat lambda in groups, using the following rule:
218
219 Float in if (a) there is at least one Id,
220 and (b) there are no non-one-shot Ids
221
222 Otherwise drop all the bindings outside the group.
223
224 This is what the 'go' function in the AnnLam case is doing.
225
226 Urk! if all are tyvars, and we don't float in, we may miss an
227 opportunity to float inside a nested case branch
228 -}
229
230 fiExpr dflags to_drop lam@(_, AnnLam _ _)
231 | okToFloatInside bndrs -- Float in
232 -- NB: Must line up with noFloatIntoRhs (AnnLam...); see Trac #7088
233 = mkLams bndrs (fiExpr dflags to_drop body)
234
235 | otherwise -- Dump it all here
236 = wrapFloats to_drop (mkLams bndrs (fiExpr dflags [] body))
237
238 where
239 (bndrs, body) = collectAnnBndrs lam
240
241 {-
242 We don't float lets inwards past an SCC.
243 ToDo: keep info on current cc, and when passing
244 one, if it is not the same, annotate all lets in binds with current
245 cc, change current cc to the new one and float binds into expr.
246 -}
247
248 fiExpr dflags to_drop (_, AnnTick tickish expr)
249 | tickish `tickishScopesLike` SoftScope
250 = Tick tickish (fiExpr dflags to_drop expr)
251
252 | otherwise -- Wimp out for now - we could push values in
253 = wrapFloats to_drop (Tick tickish (fiExpr dflags [] expr))
254
255 {-
256 For @Lets@, the possible ``drop points'' for the \tr{to_drop}
257 bindings are: (a)~in the body, (b1)~in the RHS of a NonRec binding,
258 or~(b2), in each of the RHSs of the pairs of a @Rec@.
259
260 Note that we do {\em weird things} with this let's binding. Consider:
261 \begin{verbatim}
262 let
263 w = ...
264 in {
265 let v = ... w ...
266 in ... v .. w ...
267 }
268 \end{verbatim}
269 Look at the inner \tr{let}. As \tr{w} is used in both the bind and
270 body of the inner let, we could panic and leave \tr{w}'s binding where
271 it is. But \tr{v} is floatable further into the body of the inner let, and
272 {\em then} \tr{w} will also be only in the body of that inner let.
273
274 So: rather than drop \tr{w}'s binding here, we add it onto the list of
275 things to drop in the outer let's body, and let nature take its
276 course.
277
278 Note [extra_fvs (1): avoid floating into RHS]
279 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
280 Consider let x=\y....t... in body. We do not necessarily want to float
281 a binding for t into the RHS, because it'll immediately be floated out
282 again. (It won't go inside the lambda else we risk losing work.)
283 In letrec, we need to be more careful still. We don't want to transform
284 let x# = y# +# 1#
285 in
286 letrec f = \z. ...x#...f...
287 in ...
288 into
289 letrec f = let x# = y# +# 1# in \z. ...x#...f... in ...
290 because now we can't float the let out again, because a letrec
291 can't have unboxed bindings.
292
293 So we make "extra_fvs" which is the rhs_fvs of such bindings, and
294 arrange to dump bindings that bind extra_fvs before the entire let.
295
296 Note [extra_fvs (2): free variables of rules]
297 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
298 Consider
299 let x{rule mentioning y} = rhs in body
300 Here y is not free in rhs or body; but we still want to dump bindings
301 that bind y outside the let. So we augment extra_fvs with the
302 idRuleAndUnfoldingVars of x. No need for type variables, hence not using
303 idFreeVars.
304 -}
305
306 fiExpr dflags to_drop (_,AnnLet (AnnNonRec id rhs@(rhs_fvs, ann_rhs)) body)
307 = fiExpr dflags new_to_drop body
308 where
309 body_fvs = freeVarsOf body `delVarSet` id
310 rhs_ty = idType id
311
312 rule_fvs = idRuleAndUnfoldingVars id -- See Note [extra_fvs (2): free variables of rules]
313 extra_fvs | noFloatIntoRhs ann_rhs rhs_ty = rule_fvs `unionVarSet` rhs_fvs
314 | otherwise = rule_fvs
315 -- See Note [extra_fvs (1): avoid floating into RHS]
316 -- No point in floating in only to float straight out again
317 -- Ditto ok-for-speculation unlifted RHSs
318
319 [shared_binds, extra_binds, rhs_binds, body_binds]
320 = sepBindsByDropPoint dflags False [extra_fvs, rhs_fvs, body_fvs] to_drop
321
322 new_to_drop = body_binds ++ -- the bindings used only in the body
323 [FB (unitVarSet id) rhs_fvs'
324 (FloatLet (NonRec id rhs'))] ++ -- the new binding itself
325 extra_binds ++ -- bindings from extra_fvs
326 shared_binds -- the bindings used both in rhs and body
327
328 -- Push rhs_binds into the right hand side of the binding
329 rhs' = fiExpr dflags rhs_binds rhs
330 rhs_fvs' = rhs_fvs `unionVarSet` floatedBindsFVs rhs_binds `unionVarSet` rule_fvs
331 -- Don't forget the rule_fvs; the binding mentions them!
332
333 fiExpr dflags to_drop (_,AnnLet (AnnRec bindings) body)
334 = fiExpr dflags new_to_drop body
335 where
336 (ids, rhss) = unzip bindings
337 rhss_fvs = map freeVarsOf rhss
338 body_fvs = freeVarsOf body
339
340 -- See Note [extra_fvs (1,2)]
341 rule_fvs = mapUnionVarSet idRuleAndUnfoldingVars ids
342 extra_fvs = rule_fvs `unionVarSet`
343 unionVarSets [ fvs | (fvs, rhs) <- rhss
344 , noFloatIntoExpr rhs ]
345
346 (shared_binds:extra_binds:body_binds:rhss_binds)
347 = sepBindsByDropPoint dflags False (extra_fvs:body_fvs:rhss_fvs) to_drop
348
349 new_to_drop = body_binds ++ -- the bindings used only in the body
350 [FB (mkVarSet ids) rhs_fvs'
351 (FloatLet (Rec (fi_bind rhss_binds bindings)))] ++
352 -- The new binding itself
353 extra_binds ++ -- Note [extra_fvs (1,2)]
354 shared_binds -- Used in more than one place
355
356 rhs_fvs' = unionVarSets rhss_fvs `unionVarSet`
357 unionVarSets (map floatedBindsFVs rhss_binds) `unionVarSet`
358 rule_fvs -- Don't forget the rule variables!
359
360 -- Push rhs_binds into the right hand side of the binding
361 fi_bind :: [FloatInBinds] -- one per "drop pt" conjured w/ fvs_of_rhss
362 -> [(Id, CoreExprWithFVs)]
363 -> [(Id, CoreExpr)]
364
365 fi_bind to_drops pairs
366 = [ (binder, fiExpr dflags to_drop rhs)
367 | ((binder, rhs), to_drop) <- zipEqual "fi_bind" pairs to_drops ]
368
369 {-
370 For @Case@, the possible ``drop points'' for the \tr{to_drop}
371 bindings are: (a)~inside the scrutinee, (b)~inside one of the
372 alternatives/default [default FVs always {\em first}!].
373
374 Floating case expressions inward was added to fix Trac #5658: strict bindings
375 not floated in. In particular, this change allows array indexing operations,
376 which have a single DEFAULT alternative without any binders, to be floated
377 inward. SIMD primops for unpacking SIMD vectors into an unboxed tuple of unboxed
378 scalars also need to be floated inward, but unpacks have a single non-DEFAULT
379 alternative that binds the elements of the tuple. We now therefore also support
380 floating in cases with a single alternative that may bind values.
381 -}
382
383 fiExpr dflags to_drop (_, AnnCase scrut case_bndr _ [(con,alt_bndrs,rhs)])
384 | isUnLiftedType (idType case_bndr)
385 , exprOkForSideEffects (deAnnotate scrut)
386 -- See PrimOp, Note [PrimOp can_fail and has_side_effects]
387 = wrapFloats shared_binds $
388 fiExpr dflags (case_float : rhs_binds) rhs
389 where
390 case_float = FB (mkVarSet (case_bndr : alt_bndrs)) scrut_fvs
391 (FloatCase scrut' case_bndr con alt_bndrs)
392 scrut' = fiExpr dflags scrut_binds scrut
393 [shared_binds, scrut_binds, rhs_binds]
394 = sepBindsByDropPoint dflags False [freeVarsOf scrut, rhs_fvs] to_drop
395 rhs_fvs = freeVarsOf rhs `delVarSetList` (case_bndr : alt_bndrs)
396 scrut_fvs = freeVarsOf scrut
397
398 fiExpr dflags to_drop (_, AnnCase scrut case_bndr ty alts)
399 = wrapFloats drop_here1 $
400 wrapFloats drop_here2 $
401 Case (fiExpr dflags scrut_drops scrut) case_bndr ty
402 (zipWith fi_alt alts_drops_s alts)
403 where
404 -- Float into the scrut and alts-considered-together just like App
405 [drop_here1, scrut_drops, alts_drops]
406 = sepBindsByDropPoint dflags False [scrut_fvs, all_alts_fvs] to_drop
407
408 -- Float into the alts with the is_case flag set
409 (drop_here2 : alts_drops_s) = sepBindsByDropPoint dflags True alts_fvs alts_drops
410
411 scrut_fvs = freeVarsOf scrut
412 alts_fvs = map alt_fvs alts
413 all_alts_fvs = unionVarSets alts_fvs
414 alt_fvs (_con, args, rhs) = foldl delVarSet (freeVarsOf rhs) (case_bndr:args)
415 -- Delete case_bndr and args from free vars of rhs
416 -- to get free vars of alt
417
418 fi_alt to_drop (con, args, rhs) = (con, args, fiExpr dflags to_drop rhs)
419
420 okToFloatInside :: [Var] -> Bool
421 okToFloatInside bndrs = all ok bndrs
422 where
423 ok b = not (isId b) || isOneShotBndr b
424 -- Push the floats inside there are no non-one-shot value binders
425
426 noFloatIntoRhs :: AnnExpr' Var (UniqFM Var) -> Type -> Bool
427 -- ^ True if it's a bad idea to float bindings into this RHS
428 -- Preconditio: rhs :: rhs_ty
429 noFloatIntoRhs rhs rhs_ty
430 = isUnLiftedType rhs_ty -- See Note [Do not destroy the let/app invariant]
431 || noFloatIntoExpr rhs
432
433 noFloatIntoExpr :: AnnExpr' Var (UniqFM Var) -> Bool
434 noFloatIntoExpr (AnnLam bndr e)
435 = not (okToFloatInside (bndr:bndrs))
436 -- NB: Must line up with fiExpr (AnnLam...); see Trac #7088
437 where
438 (bndrs, _) = collectAnnBndrs e
439 -- IMPORTANT: don't say 'True' for a RHS with a one-shot lambda at the top.
440 -- This makes a big difference for things like
441 -- f x# = let x = I# x#
442 -- in let j = \() -> ...x...
443 -- in if <condition> then normal-path else j ()
444 -- If x is used only in the error case join point, j, we must float the
445 -- boxing constructor into it, else we box it every time which is very bad
446 -- news indeed.
447
448 noFloatIntoExpr rhs = exprIsExpandable (deAnnotate' rhs)
449 -- We'd just float right back out again...
450 -- Should match the test in SimplEnv.doFloatFromRhs
451
452 {-
453 ************************************************************************
454 * *
455 \subsection{@sepBindsByDropPoint@}
456 * *
457 ************************************************************************
458
459 This is the crucial function. The idea is: We have a wad of bindings
460 that we'd like to distribute inside a collection of {\em drop points};
461 insides the alternatives of a \tr{case} would be one example of some
462 drop points; the RHS and body of a non-recursive \tr{let} binding
463 would be another (2-element) collection.
464
465 So: We're given a list of sets-of-free-variables, one per drop point,
466 and a list of floating-inwards bindings. If a binding can go into
467 only one drop point (without suddenly making something out-of-scope),
468 in it goes. If a binding is used inside {\em multiple} drop points,
469 then it has to go in a you-must-drop-it-above-all-these-drop-points
470 point.
471
472 We have to maintain the order on these drop-point-related lists.
473 -}
474
475 sepBindsByDropPoint
476 :: DynFlags
477 -> Bool -- True <=> is case expression
478 -> [FreeVarSet] -- One set of FVs per drop point
479 -> FloatInBinds -- Candidate floaters
480 -> [FloatInBinds] -- FIRST one is bindings which must not be floated
481 -- inside any drop point; the rest correspond
482 -- one-to-one with the input list of FV sets
483
484 -- Every input floater is returned somewhere in the result;
485 -- none are dropped, not even ones which don't seem to be
486 -- free in *any* of the drop-point fvs. Why? Because, for example,
487 -- a binding (let x = E in B) might have a specialised version of
488 -- x (say x') stored inside x, but x' isn't free in E or B.
489
490 type DropBox = (FreeVarSet, FloatInBinds)
491
492 sepBindsByDropPoint _ _is_case drop_pts []
493 = [] : [[] | _ <- drop_pts] -- cut to the chase scene; it happens
494
495 sepBindsByDropPoint dflags is_case drop_pts floaters
496 = go floaters (map (\fvs -> (fvs, [])) (emptyVarSet : drop_pts))
497 where
498 go :: FloatInBinds -> [DropBox] -> [FloatInBinds]
499 -- The *first* one in the argument list is the drop_here set
500 -- The FloatInBinds in the lists are in the reverse of
501 -- the normal FloatInBinds order; that is, they are the right way round!
502
503 go [] drop_boxes = map (reverse . snd) drop_boxes
504
505 go (bind_w_fvs@(FB bndrs bind_fvs bind) : binds) drop_boxes@(here_box : fork_boxes)
506 = go binds new_boxes
507 where
508 -- "here" means the group of bindings dropped at the top of the fork
509
510 (used_here : used_in_flags) = [ fvs `intersectsVarSet` bndrs
511 | (fvs, _) <- drop_boxes]
512
513 drop_here = used_here || not can_push
514
515 -- For case expressions we duplicate the binding if it is
516 -- reasonably small, and if it is not used in all the RHSs
517 -- This is good for situations like
518 -- let x = I# y in
519 -- case e of
520 -- C -> error x
521 -- D -> error x
522 -- E -> ...not mentioning x...
523
524 n_alts = length used_in_flags
525 n_used_alts = count id used_in_flags -- returns number of Trues in list.
526
527 can_push = n_used_alts == 1 -- Used in just one branch
528 || (is_case && -- We are looking at case alternatives
529 n_used_alts > 1 && -- It's used in more than one
530 n_used_alts < n_alts && -- ...but not all
531 floatIsDupable dflags bind) -- and we can duplicate the binding
532
533 new_boxes | drop_here = (insert here_box : fork_boxes)
534 | otherwise = (here_box : new_fork_boxes)
535
536 new_fork_boxes = zipWithEqual "FloatIn.sepBinds" insert_maybe fork_boxes used_in_flags
537
538 insert :: DropBox -> DropBox
539 insert (fvs,drops) = (fvs `unionVarSet` bind_fvs, bind_w_fvs:drops)
540
541 insert_maybe box True = insert box
542 insert_maybe box False = box
543
544 go _ _ = panic "sepBindsByDropPoint/go"
545
546
547 floatedBindsFVs :: FloatInBinds -> FreeVarSet
548 floatedBindsFVs binds = mapUnionVarSet fbFVs binds
549
550 fbFVs :: FloatInBind -> VarSet
551 fbFVs (FB _ fvs _) = fvs
552
553 wrapFloats :: FloatInBinds -> CoreExpr -> CoreExpr
554 -- Remember FloatInBinds is in *reverse* dependency order
555 wrapFloats [] e = e
556 wrapFloats (FB _ _ fl : bs) e = wrapFloats bs (wrapFloat fl e)
557
558 floatIsDupable :: DynFlags -> FloatBind -> Bool
559 floatIsDupable dflags (FloatCase scrut _ _ _) = exprIsDupable dflags scrut
560 floatIsDupable dflags (FloatLet (Rec prs)) = all (exprIsDupable dflags . snd) prs
561 floatIsDupable dflags (FloatLet (NonRec _ r)) = exprIsDupable dflags r