Note [wantFloatLocal]
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
- Core letrec invariant GHC.Core
- BindInfo and FloatInfo GHC.CoreToStg.Prep
Referenced by 8
- GHC.CoreToStg.Prep call site ×4
- Floating in CorePrep GHC.CoreToStg.Prep ×2
- Pin evaluatedness on floats GHC.CoreToStg.Prep
- Floats and FloatDecision GHC.CoreToStg.Prep