[GHC] #5129: "evaluate" optimized away

GHC ghc-devs at haskell.org
Mon Mar 19 08:57:03 UTC 2018


#5129: "evaluate" optimized away
-------------------------------------+-------------------------------------
        Reporter:  dons              |                Owner:  (none)
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  7.0.3
      Resolution:                    |             Keywords:  seq, evaluate
Operating System:  Unknown/Multiple  |         Architecture:
 Type of failure:  Incorrect result  |  Unknown/Multiple
  at runtime                         |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):  Phab:D615
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by osa1):

 Started looking into this,

 With -O1 evaluate (and `seq#`, and the `throwIfNegative` call) vanishes,
 and the whole program is transformed into an assertFailure:

 {{{
 Main.main1
   = \ (eta2_a3iv :: GHC.Prim.State# GHC.Prim.RealWorld) ->
       GHC.Prim.catch#
         @ () @ SomeException Main.main3 Main.main2 eta2_a3iv

 Main.main3
   = \ _ [Occ=Dead, OS=OneShot] -> case Main.main4 of wild_00 { }

 Main.main4 = assertFailure_rCn @ (IO ()) lvl1_r4kE

 Main.main2
   = \ (e1_a3iI [OS=OneShot] :: SomeException)
       (eta_B1 [OS=OneShot] :: GHC.Prim.State# GHC.Prim.RealWorld) ->
       case e1_a3iI of wild_a3jJ
 }}}

 With some debug prints I was able to find the simplifier call that
 eliminates seq#:

 {{{
 rebuildCase
   is_plain_seq
   expr ok for side effects: seq#
                               @ String @ RealWorld (throwIfNegative (I#
 -1#)) s_a3jI
   alts: [((#,#),
           [ipv_a3jL, ipv1_a3jM],
           case assertFailure
                  @ (IO ())
                  (build
                     @ Char
                     (\ (@ b_a3eo) ->
                        unpackFoldrCString#
                          @ b_a3eo "must throw when given a negative
 number"#))
           of {
           })]
   cont: Stop[BoringCtxt] (# State# RealWorld, () #)
   ret: (SimplFloats {lets:  FltLifted
                             []
                      joins: []
                      in_scope: InScope {...}},
         case assertFailure
                @ (IO ())
                (build
                   @ Char
                   (\ (@ b_a3eo) ->
                      unpackFoldrCString#
                        @ b_a3eo "must throw when given a negative
 number"#))
         of wild_00 {
         })
 }}}

 This basically says `rebuildCase` sees the `seq#` call in the scrutinee
 position, thinks that it's side-effect-free (because the primop is not
 marked as effectful), also thinks that it's a "plain seq", and eliminates
 the case expression.

 "Plain seq" in this context is defined like this:

 {{{
 is_plain_seq   = all_dead_bndrs && isDeadBinder case_bndr -- Evaluation
 *only* for effect
 }}}

 So the code looks correct to me; primops is not marked as effectful, and
 this case expression is only for the (non-existent) effects, so it can be
 eliminated.

 Looking at the discussion above, I think not marking `seq#` as effectful
 was deliberate (otherwise `seq#` becomes `spark#`). I guess the idea was
 to force sequencing via a dependency of `State# RealWorld` return value of
 `seq#` and the next IO action, but I'm not sure how is that supposed to
 work in pure code. Can anyone say a few words about how do we expect to
 keep `seq#` when it's not effectful, and its return value is not used?

-- 
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/5129#comment:24>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list