[GHC] #16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round`

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 .
round`
-------------------------------------+-------------------------------------
Reporter: Fuuzetsu | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 8.6.3
Keywords: | Operating System: Unknown/Multiple
Architecture: | Type of failure: None/Unknown
Unknown/Multiple |
Test Case: | Blocked By:
Blocking: | Related Tickets:
Differential Rev(s): | Wiki Page:
-------------------------------------+-------------------------------------
Consider this simple code:
{{{#!hs
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -ddump-simpl -ddump-to-file -ddump-rule-firings #-}
module C (f, g) where
import Data.Int (Int64)
f :: Double -> Int64
f = round
g :: Double -> Int64
g = fromIntegral @Int @Int64 . round
}}}
There is a rule in {{{GHC.Int}}} that should translate {{{f}}} into
{{{g}}}
{{{#!hs
"round/Double->Int64"
round = (fromIntegral :: Int -> Int64) . (round :: Double -> Int)
}}}
If I compile the above module, I see
{{{
Rule fired: round/Double->Int64 (GHC.Int)
Rule fired: fromIntegral/a->Int64 (GHC.Int)
Rule fired: fromIntegral/Int->Int (GHC.Real)
Rule fired: fromIntegral/a->Int64 (GHC.Int)
Rule fired: fromIntegral/Int->Int (GHC.Real)
Rule fired: round/Double->Int (GHC.Float)
}}}
however in Core the functions end up different: {{{g}}} has an extra
dictionary passed around:
{{{#!hs
-- RHS size: {terms: 11, types: 6, coercions: 0, joins: 0/0}
f :: Double -> Int64
[GblId,
Arity=1,
Str=m,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (x_a1sG [Occ=Once!] :: Double) ->
case x_a1sG of { GHC.Types.D# ww1_a28u [Occ=Once] ->
case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt
ww1_a28u of
{ GHC.Types.I# x#_a1Rr [Occ=Once] ->
GHC.Int.I64# x#_a1Rr
}
}}]
f = \ (x_a1sG :: Double) ->
case x_a1sG of { GHC.Types.D# ww1_a28u ->
case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt ww1_a28u of
{ GHC.Types.I# x#_a1Rr ->
GHC.Int.I64# x#_a1Rr
}
}
-- RHS size: {terms: 12, types: 14, coercions: 0, joins: 0/0}
g :: Double -> Int64
[GblId,
Arity=1,
Caf=NoCafRefs,
Str=m,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (x_X1sX [Occ=Once!] :: Double) ->
case x_X1sX of { GHC.Types.D# ds1_a28C [Occ=Once] ->
case {__pkg_ccall base-4.12.0.0 Double#
-> State# RealWorld -> (# State# RealWorld,
Double# #)}_a28B
ds1_a28C GHC.Prim.realWorld#
of
{ (# _ [Occ=Dead], ds3_a28H [Occ=Once] #) ->
GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H)
}
}}]
g = \ (x_X1sX :: Double) ->
case x_X1sX of { GHC.Types.D# ds1_a28C ->
case {__pkg_ccall base-4.12.0.0 Double#
-> State# RealWorld -> (# State# RealWorld,
Double# #)}_a28B
ds1_a28C GHC.Prim.realWorld#
of
{ (# ds2_a28G, ds3_a28H #) ->
GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H)
}
}
}}}
This makes it over an order of magnitude slower than what it should be!
{{{
benchmarked f
time 111.1 ns (109.1 ns .. 113.9 ns)
0.997 R² (0.996 R² .. 0.998 R²)
mean 111.1 ns (110.4 ns .. 112.0 ns)
std dev 2.724 ns (2.195 ns .. 3.271 ns)
benchmarked g
time 7.160 ns (7.104 ns .. 7.224 ns)
1.000 R² (0.999 R² .. 1.000 R²)
mean 7.196 ns (7.171 ns .. 7.231 ns)
std dev 100.5 ps (71.97 ps .. 153.8 ps)
}}}
I don't know why the dictionary is present in {{{f}}} but not in {{{g}}}.
--
Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122
GHC http://www.haskell.org/ghc/
The Glasgow Haskell Compiler

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by Fuuzetsu): It seems the problem is that we only fire the {{{round/Double->Int64}}} rule and then for some reason we don't subsequently fire {{{round/Double->Int}}} rule. I don't know why that is. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Description changed by Fuuzetsu: Old description:
Consider this simple code:
{{{#!hs {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -ddump-simpl -ddump-to-file -ddump-rule-firings #-} module C (f, g) where
import Data.Int (Int64)
f :: Double -> Int64 f = round
g :: Double -> Int64 g = fromIntegral @Int @Int64 . round }}}
There is a rule in {{{GHC.Int}}} that should translate {{{f}}} into {{{g}}}
{{{#!hs "round/Double->Int64" round = (fromIntegral :: Int -> Int64) . (round :: Double -> Int) }}}
If I compile the above module, I see
{{{ Rule fired: round/Double->Int64 (GHC.Int) Rule fired: fromIntegral/a->Int64 (GHC.Int) Rule fired: fromIntegral/Int->Int (GHC.Real) Rule fired: fromIntegral/a->Int64 (GHC.Int) Rule fired: fromIntegral/Int->Int (GHC.Real) Rule fired: round/Double->Int (GHC.Float) }}}
however in Core the functions end up different: {{{g}}} has an extra dictionary passed around:
{{{#!hs -- RHS size: {terms: 11, types: 6, coercions: 0, joins: 0/0} f :: Double -> Int64 [GblId, Arity=1, Str=
m, Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False) Tmpl= \ (x_a1sG [Occ=Once!] :: Double) -> case x_a1sG of { GHC.Types.D# ww1_a28u [Occ=Once] -> case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt ww1_a28u of { GHC.Types.I# x#_a1Rr [Occ=Once] -> GHC.Int.I64# x#_a1Rr } }}] f = \ (x_a1sG :: Double) -> case x_a1sG of { GHC.Types.D# ww1_a28u -> case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt ww1_a28u of { GHC.Types.I# x#_a1Rr -> GHC.Int.I64# x#_a1Rr } }-- RHS size: {terms: 12, types: 14, coercions: 0, joins: 0/0} g :: Double -> Int64 [GblId, Arity=1, Caf=NoCafRefs, Str=
m, Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False) Tmpl= \ (x_X1sX [Occ=Once!] :: Double) -> case x_X1sX of { GHC.Types.D# ds1_a28C [Occ=Once] -> case {__pkg_ccall base-4.12.0.0 Double# -> State# RealWorld -> (# State# RealWorld, Double# #)}_a28B ds1_a28C GHC.Prim.realWorld# of { (# _ [Occ=Dead], ds3_a28H [Occ=Once] #) -> GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H) } }}] g = \ (x_X1sX :: Double) -> case x_X1sX of { GHC.Types.D# ds1_a28C -> case {__pkg_ccall base-4.12.0.0 Double# -> State# RealWorld -> (# State# RealWorld, Double# #)}_a28B ds1_a28C GHC.Prim.realWorld# of { (# ds2_a28G, ds3_a28H #) -> GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H) } } }}}This makes it over an order of magnitude slower than what it should be!
{{{ benchmarked f time 111.1 ns (109.1 ns .. 113.9 ns) 0.997 R² (0.996 R² .. 0.998 R²) mean 111.1 ns (110.4 ns .. 112.0 ns) std dev 2.724 ns (2.195 ns .. 3.271 ns)
benchmarked g time 7.160 ns (7.104 ns .. 7.224 ns) 1.000 R² (0.999 R² .. 1.000 R²) mean 7.196 ns (7.171 ns .. 7.231 ns) std dev 100.5 ps (71.97 ps .. 153.8 ps) }}}
I don't know why the dictionary is present in {{{f}}} but not in {{{g}}}.
New description:
Consider this simple code:
{{{#!hs
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -ddump-simpl -ddump-to-file -ddump-rule-firings #-}
module C (f, g) where
import Data.Int (Int64)
f :: Double -> Int64
f = round
g :: Double -> Int64
g = fromIntegral @Int @Int64 . round
}}}
There is a rule in {{{GHC.Int}}} that should translate {{{f}}} into
{{{g}}}
{{{#!hs
"round/Double->Int64"
round = (fromIntegral :: Int -> Int64) . (round :: Double -> Int)
}}}
If I compile the above module, I see
{{{
Rule fired: round/Double->Int64 (GHC.Int)
Rule fired: fromIntegral/a->Int64 (GHC.Int)
Rule fired: fromIntegral/Int->Int (GHC.Real)
Rule fired: fromIntegral/a->Int64 (GHC.Int)
Rule fired: fromIntegral/Int->Int (GHC.Real)
Rule fired: round/Double->Int (GHC.Float)
}}}
however in Core the functions end up different: {{{f}}} has an extra
dictionary passed around:
{{{#!hs
-- RHS size: {terms: 11, types: 6, coercions: 0, joins: 0/0}
f :: Double -> Int64
[GblId,
Arity=1,
Str=m,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (x_a1sG [Occ=Once!] :: Double) ->
case x_a1sG of { GHC.Types.D# ww1_a28u [Occ=Once] ->
case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt
ww1_a28u of
{ GHC.Types.I# x#_a1Rr [Occ=Once] ->
GHC.Int.I64# x#_a1Rr
}
}}]
f = \ (x_a1sG :: Double) ->
case x_a1sG of { GHC.Types.D# ww1_a28u ->
case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt ww1_a28u of
{ GHC.Types.I# x#_a1Rr ->
GHC.Int.I64# x#_a1Rr
}
}
-- RHS size: {terms: 12, types: 14, coercions: 0, joins: 0/0}
g :: Double -> Int64
[GblId,
Arity=1,
Caf=NoCafRefs,
Str=m,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (x_X1sX [Occ=Once!] :: Double) ->
case x_X1sX of { GHC.Types.D# ds1_a28C [Occ=Once] ->
case {__pkg_ccall base-4.12.0.0 Double#
-> State# RealWorld -> (# State# RealWorld,
Double# #)}_a28B
ds1_a28C GHC.Prim.realWorld#
of
{ (# _ [Occ=Dead], ds3_a28H [Occ=Once] #) ->
GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H)
}
}}]
g = \ (x_X1sX :: Double) ->
case x_X1sX of { GHC.Types.D# ds1_a28C ->
case {__pkg_ccall base-4.12.0.0 Double#
-> State# RealWorld -> (# State# RealWorld,
Double# #)}_a28B
ds1_a28C GHC.Prim.realWorld#
of
{ (# ds2_a28G, ds3_a28H #) ->
GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H)
}
}
}}}
This makes it over an order of magnitude slower than what it should be!
{{{
benchmarked f
time 111.1 ns (109.1 ns .. 113.9 ns)
0.997 R² (0.996 R² .. 0.998 R²)
mean 111.1 ns (110.4 ns .. 112.0 ns)
std dev 2.724 ns (2.195 ns .. 3.271 ns)
benchmarked g
time 7.160 ns (7.104 ns .. 7.224 ns)
1.000 R² (0.999 R² .. 1.000 R²)
mean 7.196 ns (7.171 ns .. 7.231 ns)
std dev 100.5 ps (71.97 ps .. 153.8 ps)
}}}
I don't know why the dictionary is present in {{{f}}} but not in {{{g}}}.
--
--
Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:2
GHC http://www.haskell.org/ghc/
The Glasgow Haskell Compiler

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by akio): * cc: akio (added) Comment: In the interface file for GHC.Int I see: {{{ "round/Double->Int64" forall ($dRealFrac :: GHC.Real.RealFrac GHC.Types.Double) ($dIntegral :: GHC.Real.Integral Int64) GHC.Real.round @ GHC.Types.Double $dRealFrac @ Int64 $dIntegral = GHC.Base.. @ GHC.Types.Int @ Int64 @ GHC.Types.Double (GHC.Real.fromIntegral @ GHC.Types.Int @ Int64 GHC.Real.$fIntegralInt $fNumInt64) (GHC.Float.$fRealFracDouble_$cround @ GHC.Types.Int GHC.Real.$fIntegralInt) }}} So the RHS of the RULE already contains the bad code. This appears to be caused by the fact that the RHS of a rule is rewritten with GHC's built-in rules, even when there is a conflicting user-defined one. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Here is what is happening * There is a built-in rule for `round` that rewrites `round (RFDict _ _ r _ _) = r`; that is, `round` applied to a suitable dictionary just selects the method. * Currently, built-in rules are always active; they can't be delayed to a later phase. This could be changed relatively easily, but that's the way it is right now. * Rules are applied to the RHS of other rules, if the phases allow; this seems reasonable. After all, that's what will happen whenever the rule is applied. * In particular, the built-in rule is applied to the RHS of the rule in `GHC.Int`, as comment:3 says. * I think the reason things go wrong is that in the RHS of hte rule in `GHC.Int` you really want to apply the rule `"round/Double->Int"` in `GHC.Float`, but perhaps it is not imported by `GHC.Int`? Anyway, probably the best solution here is to rewrite the rule in `GHC.Int` to {{{ "round/Double->Int64" round = (fromIntegral :: Int -> Int64) . GHC.Float.roundDoubleInt }}} (And maybe the same for `fromIntegral`?) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by Fuuzetsu): Replying to [comment:4 simonpj]:
The reason things go wrong is that in the RHS of hte rule in `GHC.Int` you really want to apply the rule `"round/Double->Int"` in `GHC.Float`, but perhaps it is not imported by `GHC.Int`?
Anyway, probably the best solution here is to rewrite the rule in `GHC.Int` to {{{ "round/Double->Int64" round = (fromIntegral :: Int -> Int64) . GHC.Float.roundDoubleInt }}} (And maybe the same for `fromIntegral`?)
It seems wrong to me to manually expand rules that already exist. Seems very unprincipled and easy to miss other existing cases: I'm confident {{{Int64}}} isn't the only offender, there are similar rules all around. I'm happy to submit a patch as proposed but I wonder if there isn't a better way to ensure the relevant rules are in scope together... -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by akio): Replying to [comment:4 simonpj]:
* Rules are applied to the RHS of other rules, if the phases allow; this seems reasonable. After all, that's what will happen whenever the rule is applied.
Hmm, this doesn't appear to be true in general. It looks like user-defined rules are never applied to the RHS of other rules. Only built-in rules are. I checked this by compiling the following module: {{{#!hs module Foo where import qualified GHC.List as List foo, bar:: Int foo = 0 bar = 0 baz :: [Int] baz = [] {-# RULES "foo" foo = 1 #-} {-# RULES "bar/foo" bar = foo #-} -- the "foo" rule doesn't fire {-# RULES "baz" baz = List.concat [] #-} -- the "concat" rule from GHC.List doesn't fire }}} Here is what I think is the relevant code https://gitlab.haskell.org/ghc/ghc/blob/9ea8dcea3e5ba96808ef91028e0efde9d31f.... The rule RHS is simplified under `rule_env`, which has the `sm_rules` field set to `False`. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj):
It looks like user-defined rules are never applied to the RHS of other rules
Hmm. You are right. See `Note [Simplifying rules]` in `SimplUtils`. So rules are switched off on both RHS and LHS of rules. But as noted above, built-in rules are not switched off. In `Rules.hs` I see {{{ matchRule dflags rule_env _is_active fn args _rough_args (BuiltinRule { ru_try = match_fn }) -- Built-in rules can't be switched off, it seems = case match_fn dflags rule_env fn args of Nothing -> Nothing Just expr -> Just expr }}} This seems wrong. If we are going to switch off rules we should switch them all off. We could just add {{{ matchRule dflags rule_env _is_active fn args _rough_args (BuiltinRule { ru_try = match_fn }) | not (is_active AlwaysActive) -- Built-in rules are always active, but = Nothing -- sometimes we switch off rules altogether -- E.g. see SimplUtils.updModeForRules | otherwise = case match_fn dflags rule_env fn args of Nothing -> Nothing Just expr -> Just expr }}} That still leaves the issue that two rules, a built-in one and a user- specified one, can be simultaneously active. Which will win? Well, it turns out that that user specified one does. See this code in `Rules.hs` {{{ isMoreSpecific :: CoreRule -> CoreRule -> Bool -- This tests if one rule is more specific than another -- We take the view that a BuiltinRule is less specific than -- anything else, because we want user-define rules to "win" -- In particular, class ops have a built-in rule, but we -- any user-specific rules to win -- eg (Trac #4397) -- truncate :: (RealFrac a, Integral b) => a -> b -- {-# RULES "truncate/Double->Int" truncate = double2Int #-} -- double2Int :: Double -> Int -- We want the specific RULE to beat the built-in class-op rule }}} I don't think this behaviour is documented in the user manual -- it should be. That would fix this ticket. But in a rather delicate way: we rely on /not/ applying a built-in rule R1 to the RHS of another rule R2, in case when R2 fires there is a user-defined rule R3 that will beat R1. However, ignore built-in rules for the moment. The `isMoreSpecfic` code above makes rule S1 "beat" rule S2 if S1 is more specific -- that is, if S1's LHS is a substitution instance of S2's. (We should document this behaviour too.) So consider {{{ {-# RULES "S1" forall x. f [x] = blah1 "S2" forall y. f [Just y] = blah2 "S3" forall z. h z = f [z] #-} If we applied rules to the RHS of S3, only S1 would apply. But if didn't (as happens now), and we had a term `h (Just 3)`, we'd rewrite it with S3 to `f [Just 3]`, and now both S1 and S2 apply, and S2 would win. So this is a distinct reason, which we should add to `Note [Simplifying rules]`, for not applying rules to the RHS of rules. Would anyone like to write a patch for these fixes? * Make the above change to `matchRule` * Document the overlap behaviour of RULES in the user manual * Add the above reasoning to `Note [Simplifying rules]` Thanks! -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Finally, I still think it'd be clearer (i.e. easier to reason about what will ultimately happen) to call `roundDoubleInt` directly in the rule. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC