Note [wantFloatLocal]

GHC/CoreToStg/Prep.hs:2300 compiler

Consider
  let x = let y = e1 in e2
  in e
Similarly for `(\x. e) (let y = e1 in e2)`.
Do we want to float `y` out of `x`?
(This is discussed in detail in the paper
"Let-floating: moving bindings to give faster programs".)

`wantFloatLocal` is concerned with answering this question.
It considers the Demand on `x`, whether or not `e2` is unlifted and the
`FloatInfo` of the `y` binding (e.g., it might itself be unlifted, a value,
strict, or ok-for-spec).

We float out if ...
  1. ... the binding context is strict anyway, so either `x` is used strictly
     or has unlifted type.
     Doing so is trivially sound and won`t increase allocations, so we
     return `FloatAll`.
     This might happen while ANF-ising `f (g (h 13))` where `f`,`g` are strict:
       f (g (h 13))
       ==> { ANF }
       case (case h 13 of r -> g r) of r2 -> f r2
       ==> { Float }
       case h 13 of r -> case g r of r2 -> f r2
     The latter is easier to read and grows less stack.
  2. ... `e2` becomes a value in doing so, in which case we won't need to
     allocate a thunk for `x`/the arg that closes over the FVs of `e1`.
     In general, this is only sound if `y=e1` is `LazyContextFloatable`.
     (See Note [BindInfo and FloatInfo].)
     Nothing is won if `x` doesn't become a value
     (i.e., `let x = let sat = f 14 in g sat in e`),
     so we return `FloatNone` if there is any float that is
     `StrictContextFloatable`, and return `FloatAll` otherwise.

To elaborate on (2), consider the case when the floated binding is
`e1 = divInt# a b`, e.g., not `LazyContextFloatable`:
  let x = I# (a `divInt#` b)
  in e
this ANFises to
  let x = case a `divInt#` b of r { __DEFAULT -> I# r }
  in e
If `x` is used lazily, we may not float `r` further out.
A float binding `x +# y` is OK, though, and so every ok-for-spec-eval
binding is `LazyContextFloatable`.

Wrinkles:

 (W1) When the outer binding is a letrec, i.e.,
        letrec x = case a +# b of r { __DEFAULT -> f y r }
               y = [x]
        in e
      we don't want to float `LazyContextFloatable` bindings such as `r` either
      and require `TopLvlFloatable` instead.
      The reason is that we don't track FV of FloatBindings, so we would need
      to park them in the letrec,
        letrec r = a +# b -- NB: r`s RHS might scope over x and y
               x = f y r
               y = [x]
        in e
      and now we have violated Note [Core letrec invariant].
      So we preempt this case in `wantFloatLocal`, responding `FloatNone` unless
      all floats are `TopLvlFloatable`.

References 2

Referenced by 8