
#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