fail doesn't inline, leading to missed optimizations
Summary
The function fail
doesn't inline. GHC's passes over GHC core are able to detect that expressions following raiseIO#
are unreachable. In a more complicated setting (not elaborated on here), I've observed that this missed opportunity to eliminate unreachable code after raiseIO#
leads to missed opportunities for other optimizations like worker-wrapper and case-of-known-data-constructor.
Steps to reproduce
With GHC 8.6.4, compile this with ghc-8.6.4 -O2 -fforce-recomp -ddump-simpl -dsuppress-all fail_inline.hs
:
{-# language MagicHash #-}
module FailInline
( alpha
, beta
) where
import GHC.Exts
import GHC.IO
import Control.Exception
alpha :: IO Int
{-# NOINLINE alpha #-}
alpha = do
x <- fail "foobar"
pure (x + 1)
beta :: IO Int
{-# NOINLINE beta #-}
beta = do
x <- IO (raiseIO# (toException (userError "fizzbuzz")))
pure (x + 1)
The NOINLINE
pragmas are not necessary to trigger this problem, but they make the resulting GHC core more readable. Here are the relevant sections of GHC core that is dumped:
-- RHS size: {terms: 4, types: 4, coercions: 0, joins: 0/0}
beta1
beta1 = \ s_a2ld -> raiseIO# lvl3_r2KW s_a2ld
...
-- RHS size: {terms: 16, types: 29, coercions: 10, joins: 0/0}
alpha1
alpha1
= \ s_X2lp ->
case ((noinline (failIO1 `cast` <Co:8>) alpha2) `cast` <Co:2>)
s_X2lp
of
{ (# ipv_a2lg, ipv1_a2lh #) ->
(# ipv_a2lg,
case ipv1_a2lh of { I# x_a2JK -> I# (+# x_a2JK 1#) } #)
}
Expected behavior
I expected that alpha
and beta
would be optimized to the same thing (modulo the different error message).
Environment
- GHC version used: 8.6.4