[GHC] #12603: INLINE and manually inlining produce different code
GHC
ghc-devs at haskell.org
Sun Dec 4 23:02:09 UTC 2016
#12603: INLINE and manually inlining produce different code
-------------------------------------+-------------------------------------
Reporter: bgamari | Owner: bgamari
Type: bug | Status: new
Priority: high | Milestone: 8.2.1
Component: Compiler | Version: 8.0.1
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: Runtime | Unknown/Multiple
performance bug | Test Case:
Blocked By: | Blocking:
Related Tickets: #12747 #12781 | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by simonpj):
OK I know what is happening here.
Without an INLINE on `fgFromInt` we get:
{{{
-- Initially
fgFromInt w = w + (2^8)
attrFromIntINLINE w = Attr (fgFromInt w)
-- After float-out
lvl = 2^8
fgFromInt w = w + lvl
attrFromIntINLINE w = Attr (fgFromInt w)
-- After inlining
attrFromIntINLINE w = case w of I# w' ->
case lvl of I# lvl' ->
Attr (w' +# lvl')
}}}
The `Attr` constructor has one strict field, which is reprsented unboxed.
We only compute `(2^8)` once.
But with an INLINE on `fgFromInt` we get this:
{{{
-- Initially
fgFromInt w = w + (2^8)
attrFromIntINLINE w = Attr (fgFromInt w)
-- After float-out
lvl = 2^8
fgFromInt w = w + lvl
{- INLINE rhs = w + (2^8) -}
attrFromIntINLINE w = Attr (fgFromInt w)
-- After inlining
attrFromIntINLINE w = case w of I# w' ->
case 2# ^# 8# of lvl' ->
Attr (w' +# lvl')
}}}
The INLINE pragma promises to inline what you wrote, not some optimised
version thereof. So we inline `w + (2^8)`. In pcinciple we should
optimise that just as well after it has been inlined. We've missed the
float-out pass, but there's another one later.
Alas, however, by the time the second float-out pass runs, the `(2^8)` has
been transformed to its unboxed form, and currently we don't float those.
Result we compute `(2^8)` on each iteration, rather than just once.
There's even a `Note` about it in `SetLevels`:
{{{
Note [Unlifted MFEs]
~~~~~~~~~~~~~~~~~~~~
We don't float unlifted MFEs, which potentially loses big opportunites.
For example:
\x -> f (h y)
where h :: Int -> Int# is expensive. We'd like to float the (h y) outside
the \x, but we don't because it's unboxed. Possible solution: box it.
}}}
---------------
What do do? The bad thing is really the inability to float expressions of
unboxed type, because that inability makes GHC vulnerable to these "phase
effects" when optimisation depends declicately on the exact order things
happen in.
Perhaps we could fix that by boxing. So, just before doing the floating
stuff `SetLevels` could do
{{{
e :: Int#
--->
(case e of y -> \void. y) void
--->
let v = /\a -> case e of y -> \void. y
in v a void
}}}
at least when `e` is not cheap. Now it can float the `(case e of y -> I#
y)`.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/12603#comment:26>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list