[GHC] #9661: Branchless ==# is compiled to branchy code
GHC
ghc-devs at haskell.org
Fri Oct 3 09:51:40 UTC 2014
#9661: Branchless ==# is compiled to branchy code
-------------------------------------+-------------------------------------
Reporter: dfeuer | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 7.9
Resolution: | Keywords:
Operating System: | Architecture: Unknown/Multiple
Unknown/Multiple | Difficulty: Unknown
Type of failure: Runtime | Blocked By:
performance bug | Related Tickets:
Test Case: |
Blocking: |
Differential Revisions: |
-------------------------------------+-------------------------------------
Comment (by dfeuer):
Replying to [comment:1 jstolarek]:
> After some thinking I don't think it's constant folding but more
generally the special rules for `==#`. As promised here are some hints how
to get started on this one:
>
> 1. Try to reproduce the problem with a simpler example, eg. using only
two comparison primops and `orI#`. Try out different combinations of
comparison primops. See also whether this happens for `andI#`. We want to
be sure that this only depends on presence of `==#` and not other
operators (don't forget `/=#`).
This happens with both `==#` and `/=#`. It does not appear to happen with
the others. Furthermore, it appears to happen only when one of the
arguments is known at compile time.
> 2. Once you have a smaller test case follow the transformation that GHC
is performing to arrive at the result. Is it turning the simpler test case
into the final result in one step or are there any intermediate steps?
There are intermediate steps, but it starts very early. For example,
{{{#!hs
--Desugar
equals0AndEquals2
equals0AndEquals2 =
\ x_a1wL y_a1wM -> andI# (==# x_a1wL 0) (==# y_a1wM 2)
== Gentle simplification ==>
equals0AndEquals2
equals0AndEquals2 =
\ x_a1wL y_a1wM ->
andI#
(case x_a1wL of _ {
__DEFAULT -> 0;
0 -> 1
})
(case y_a1wM of _ {
__DEFAULT -> 0;
2 -> 1
})
-- Nothing changes for a bit ....
== Simplifier phase 2 ==>
equals0AndEquals2
equals0AndEquals2 =
\ x_a1wL y_a1wM ->
case x_a1wL of _ {
__DEFAULT -> 0;
0 ->
case y_a1wM of _ {
__DEFAULT -> 0;
2 -> 1
}
}
> 3. My guess would be that the culprit is `litEq` function in
`PrelRules.lhs` module. The comment above it claims to do exactly what
you've reported here. However, I'm not sure what the fix should be.
Disabling this is probably not a good idea, since in some cases we really
want that transformation to happen. We'd need to think more but first
let's confirm whether `litEq` is the cause or not. You can verify that by
disabling that rule and seeing whether the code compiles without branches.
I'm confident you're right, but I won't have time to do that experiment
until after Yom Kippur. The comment says
{{{
-- This is a Good Thing, because it allows case-of case things
-- to happen, and case-default absorption to happen. For
-- example:
--
-- if (n ==# 3#) || (n ==# 4#) then e1 else e2
-- will transform to
-- case n of
-- 3# -> e1
-- 4# -> e1
-- m -> e2
}}}
There are a couple `tagToEnum#` invocations missing there, but let's look
at the whole process. We're presumably starting with
{{{#!hs
if (n == 3) || (n == 4) then e1 else e2
}}}
Expanding `if`,
{{{#!hs
case (n == 3) || (n == 4) of
True -> e1
False -> e2
}}}
Expanding `(||)`,
{{{#!hs
case (case n == 3 of {True -> True; False -> n == 4}) of
True -> e1
False -> e2
}}}
Case of case gets us
{{{#!hs
case n == 3 of
True -> case True of {True -> e1; False -> e2}
False -> case n == 4 of {True -> e1; False -> e2}
}}}
Case of known constructor or whatever it's called produces
{{{#!hs
case n == 3 of
True -> e1
False -> case n == 4 of
True -> e1
False -> e2
}}}
So we can get this far without knowing anything about what `(==)` means.
Now let's add part of that knowledge:
{{{#!hs
case n# ==# 3# of
1# -> e1
0# -> case n# ==# 4# of
1# -> e1
0# -> e2
}}}
In this case, this is probably just fine, but I imagine the example is
intended to suggest that there are (perhaps many) more tests, so that a
different pattern matching strategy may be in order. I would venture to
guess that what we want to do is attack it at this stage—recognizing that
we're looking at nested case of `==#` with a shared operand.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/9661#comment:2>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list