Understanding the core2core simplifier <-> occurence-analysis interaction

Herbert Valerio Riedel hvr at gnu.org
Mon Oct 22 09:59:00 CEST 2012

Hello GHC HQ,

I've been trying to improve/fix a minor optimization sub-optimality
w.r.t to the following code (code like that results from the
generics-based NFData deriver[1]):

    data Foo = Foo1 | Foo2 | Foo3 !Int
    rnf1 :: Foo -> ()
    rnf1 x = case x of
               Foo1 -> ()
               Foo2 -> ()
               Foo3 {} -> ()
which the current GHC 7.6.1 translates to the following core

    NFDataTest2.rnf1 =
      \ x_aeG ->
        case x_aeG of _ {
          __DEFAULT -> GHC.Tuple.();
          NFDataTest2.Foo3 ds_deT -> GHC.Tuple.()

...whereas I'd have expected it to to compile it to a collapsed
'__DEFAULT'-only case, i.e.

    NFDataTest2.rnf1 =
      \ x_aeG -> case x_aeG of _ { __DEFAULT -> GHC.Tuple.() }

Now I've been hunting it down to the function SimplUtils.mkCase1 [2],
which according to the source-code comments, is supposed to merge
identical alternatives, i.e.:

| 3.  Merge identical alternatives.
|     If several alternatives are identical, merge them into
|     a single DEFAULT alternative.  I've occasionally seen this
|     making a big difference:
|         case e of               =====>     case e of
|           C _ -> f x                         D v -> ....v....
|           D v -> ....v....                   DEFAULT -> f x
|           DEFAULT -> f x

...and the 'mkCase1' function itself reads as follows:

    mkCase1 dflags scrut case_bndr alts_ty ((_con1,bndrs1,rhs1) : con_alts)
      | all isDeadBinder bndrs1                     -- Remember the default
      , length filtered_alts < length con_alts      -- alternative comes first
            -- Also Note [Dead binders]
      = do  { tick (AltMerge case_bndr)
            ; mkCase2 dflags scrut case_bndr alts_ty alts' }
        alts' = (DEFAULT, [], rhs1) : filtered_alts
        filtered_alts         = filter keep con_alts
        keep (_con,bndrs,rhs) = not (all isDeadBinder bndrs && rhs `cheapEqExpr` rhs1)

...now the problem seems to be, that 'isDeadBinder' returns 'False'; so
I hacked up 'mkCase1' and inserted a 'occurAnalyseExpr' on artificially
constructed single-alternative 'Case' values before applying
'isDeadBinder', and then it would return 'True' and simplify the case
expression as expected.

So now my question is, why isn't the occurrence information available
for the case-alternative's binders (the occInfo is set to 'NoOccInfo')
at 'mkCase1'-time?  When is occurence analysis performed relative to the

 [1]: http://hackage.haskell.org/package/deepseq-generics
 [2]: http://www.haskell.org/ghc/docs/7.6.1/html/libraries/ghc-7.6.1/src/SimplUtils.html#mkCase1


More information about the Glasgow-haskell-users mailing list