Note [Floating primops]

GHC/Core/Opt/FloatIn.hs:442 compiler 2 tickets

We try to float-in a case expression over an unlifted type.  The
motivating example was #5658: in particular, this change allows
array indexing operations, which have a single DEFAULT alternative
without any binders, to be floated inward.

In particular, we want to be able to transform

  case indexIntArray# arr i of vi {
    __DEFAULT -> case <# j n of _ {
      __DEFAULT -> False
      1# -> case indexIntArray# arr j of vj {
        __DEFAULT -> ... vi ... vj ...
      }
    }
  }

by floating in `indexIntArray# arr i` to produce

  case <# j n of _ {
    __DEFAULT -> False
    1# -> case indexIntArray# arr i of vi {
      __DEFAULT -> case indexIntArray# arr j of vj {
        __DEFAULT -> ... vi ... vj ...
      }
    }
  }

...which skips the `indexIntArray# arr i` call entirely in the out-of-bounds branch.

SIMD primops for unpacking SIMD vectors into an unboxed tuple of unboxed
scalars also need to be floated inward, but unpacks have a single non-DEFAULT
alternative that binds the elements of the tuple. We now therefore also support
floating in cases with a single alternative that may bind values.

But there are wrinkles

* Which unlifted cases do we float?
  See Note [Transformations affected by primop effects] in GHC.Builtin.PrimOps
  which explains:
   - We can float in or discard CanFail primops, but we can't float them out.
   - We don't want to discard a synchronous exception or side effect
     so we don't float those at all. Hence exprOkToDiscard.
   - Throwing precise exceptions is a special case of the previous point: We
     may /never/ float in a call to (something that ultimately calls)
     'raiseIO#'.
     See Note [Precise exceptions and strictness analysis] in GHC.Types.Demand.

* Because we can float can-fail primops (array indexing, division) inwards
  but not outwards, we must be careful not to transform
     case a /# b of r -> f (F# r)
  ===>
    f (case a /# b of r -> F# r)
  because that creates a new thunk that wasn't there before.  And
  because it can't be floated out (CanFail), the thunk will stay
  there.  Disaster!  (This happened in nofib 'simple' and 'scs'.)

  Solution: only float cases into the branches of other cases, and
  not into the arguments of an application, or the RHS of a let. This
  is somewhat conservative, but it's simple.  And it still hits the
  cases like #5658.   This is implemented in sepBindsByJoinPoint;
  if is_case is False we dump all floating cases right here.

* #14511 is another example of why we want to restrict float-in
  of case-expressions.  Consider
     case indexArray# a n of (# r #) -> writeArray# ma i (f r)
  Now, floating that indexing operation into the (f r) thunk will
  not create any new thunks, but it will keep the array 'a' alive
  for much longer than the programmer expected.

  So again, not floating a case into a let or argument seems like
  the Right Thing

For @Case@, the possible drop points for the 'to_drop'
bindings are:
  (a) inside the scrutinee
  (b) inside one of the alternatives/default (default FVs always /first/!).

References 2

Referenced by 4