Note [Case-to-let for strictly-used binders]

GHC/Core/Opt/Simplify/Iteration.hs:2894 compiler 6 tickets

If we have this:
   case <scrut> of r { _ -> ..r.. }
where 'r' is used strictly in (..r..), we /could/ safely transform to
   let r = <scrut> in ...r...
As a special case,  we have a plain `seq` like
   case r of r1 { _ -> ...r1... }
where `r` is used strictly, we /could/ simply drop the `case` to get
   ...r....

HOWEVER, there are some serious downsides to this transformation, so
GHC doesn't do it any longer (#24251):

* Suppose the Simplifier sees
     case x of y* { __DEFAULT ->
     let z = case y of { __DEFAULT -> expr } in
     z+1 }
  The "y*" means "y is used strictly in its scope.  Now we may:
     - Eliminate the inner case because `y` is evaluated.
  Now the demand-info on `y` is not right, because `y` is no longer used
  strictly in its scope.  But it is hard to spot that without doing a new
  demand analysis.  So there is a danger that we will subsequently:
     - Eliminate the outer case because `y` is used strictly
  Yikes!  We can't eliminate both!

* It introduces space leaks (#24251).  Consider
      go 0 where go x = x `seq` go (x + 1)
  It is an infinite loop, true, but it should not leak space. Yet if we drop
  the `seq`, it will.  Another great example is #21741.

* Dropping the outer `case` can change the error behaviour.  For example,
  we might transform
       case x of { _ -> error "bad" }    -->     error "bad"
  which is might be puzzling if 'x' currently lambda-bound, but later gets
  let-bound to (error "good").  Tht is OK accoring to the paper "A semantics for
  imprecise exceptions", but see #8900 for an example where the loss of this
  transformation bit us in practice.

* If we have (case e of x -> f x), where `f` is strict, then it looks as if `x`
  is strictly used, and we could soundly transform to
     let x = e in f x
  But if f's strictness info got worse (which can happen in in obscure cases;
  see #21392) then we might have turned a non-thunk into a thunk!  Bad.

Lacking this "drop-strictly-used-seq" transformation means we can end up with
some redundant-looking evals.  For example, consider
    f x y = case x of DEFAULT ->    -- A redundant-looking eval
            case y of
              True  -> case x of { Nothing -> False; Just z  -> z }
              False -> case x of { Nothing -> True;  Just z  -> z }
That outer eval will be retained right through to code generation.  But,
perhaps surprisingly, that is probably a /good/ thing:

   Key point: those inner (case x) expressions will be compiled a simple 'if',
   because the code generator can see that `x` is, at those points, evaluated
   and properly tagged.

If we dropped the outer eval, both the inner (case x) expressions would need to
do a proper eval, pushing a return address, with an info table. See the example
in #15631 where, in the Description, the (case ys) will be a simple multi-way
jump.

In fact (#24251), when I stopped GHC implementing the drop-strictly-used-seqs
transformation, binary sizes fell by 1%, and a few programs actually allocated
less and ran faster.  A case in point is nofib/imaginary/digits-of-e2. (I'm not
sure exactly why it improves so much, though.)

Slightly related: Note [Empty case alternatives] in GHC.Core.

Historical notes:

There have been various earlier versions of this patch:

* By Sept 18 the code looked like this:
     || scrut_is_demanded_var scrut

    scrut_is_demanded_var :: CoreExpr -> Bool
    scrut_is_demanded_var (Cast s _) = scrut_is_demanded_var s
    scrut_is_demanded_var (Var _)    = isStrUsedDmd (idDemandInfo case_bndr)
    scrut_is_demanded_var _          = False

  This only fired if the scrutinee was a /variable/, which seems
  an unnecessary restriction. So in #15631 I relaxed it to allow
  arbitrary scrutinees.  Less code, less to explain -- but the change
  had 0.00% effect on nofib.

* Previously, in Jan 13 the code looked like this:
     || case_bndr_evald_next rhs

    case_bndr_evald_next :: CoreExpr -> Bool
      See Note [Case binder next]
    case_bndr_evald_next (Var v)         = v == case_bndr
    case_bndr_evald_next (Cast e _)      = case_bndr_evald_next e
    case_bndr_evald_next (App e _)       = case_bndr_evald_next e
    case_bndr_evald_next (Case e _ _ _)  = case_bndr_evald_next e
    case_bndr_evald_next _               = False

  This patch was part of fixing #7542. See also
  Note [Eta reduction soundness], criterion (E) in GHC.Core.Utils.)


Further notes about case elimination
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider:       test :: Integer -> IO ()
                test = print

Turns out that this compiles to:
    Print.test
      = \ eta :: Integer
          eta1 :: Void# ->
          case PrelNum.< eta PrelNum.zeroInteger of wild { __DEFAULT ->
          case hPutStr stdout
                 (PrelNum.jtos eta ($w[] @ Char))
                 eta1
          of wild1 { (# new_s, a4 #) -> PrelIO.lvl23 new_s  }}

Notice the strange '<' which has no effect at all. This is a funny one.
It started like this:

f x y = if x < 0 then jtos x
          else if y==0 then "" else jtos x

At a particular call site we have (f v 1).  So we inline to get

        if v < 0 then jtos x
        else if 1==0 then "" else jtos x

Now simplify the 1==0 conditional:

        if v<0 then jtos v else jtos v

Now common-up the two branches of the case:

        case (v<0) of DEFAULT -> jtos v

Why don't we drop the case?  Because it's strict in v.  It's technically
wrong to drop even unnecessary evaluations, and in practice they
may be a result of 'seq' so we *definitely* don't want to drop those.
I don't really know how to improve this situation.

References 2

Referenced by 2