[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