fast-math
                                
                                 fast-math copied to clipboard
                                
                                    fast-math copied to clipboard
                            
                            
                            
                        Fragile rewrite rules
GHC panics on the following program:
module Test where
import Numeric.FastMath
import GHC.Exts
times4 :: Double -> Double
times4 (D# x) = D# ((x *## x) *## (x *## x))
ghc-7.10.1 Test.hs -cpp -XMagicHash -O -fforce-recomp
...
[5 of 5] Compiling Test             ( Test.hs, Test.o )
ghc: panic! (the 'impossible' happened)
  (GHC version 7.10.1 for x86_64-unknown-linux):
    Simplifier ticks exhausted
  When trying RuleFired double commute left *
  To increase the limit, use -fsimpl-tick-factor=N (default 100)
  If you need to do this, let GHC HQ know, and what factor you needed
  To see detailed counts use -ddump-simpl-stats
  Total ticks: 4564
Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug
It is not a bug in GHC though. It happens because of the following rewrite rule in Numeric.FastMath.Approximation
"double commute left *"   [~2] forall x1 x2 x3. (*##) x1 ((*##) x2 x3)
    = (*##) ((*##) x2 x3) x1
This rewrites (x *## x) *## (x *## x) to (x *## x) *## (x *## x).
From the GHC user's guide [1]:
 GHC makes no attempt to make sure that the rules are confluent or terminating. For example:
 "loop"        forall x y.  f x y = f y x
This rule will cause the compiler to go into an infinite loop. 
The are similar fragile rewrite rules in this package. I don't know what to about this, other than to remove those rules. Or at least put a big warning in the README.
[1] https://downloads.haskell.org/~ghc/7.10.1/docs/html/users_guide/rewrite-rules.html#rule-semantics
Good catch.  There's probably a way to force this to terminate by putting an id function call in there somewhere.
Do we have a resolution for this? @mikeizbicki has basically taken over the package these days…
This does not reproduce for me.
$ cat Test.hs
module Test (times4) where
import Numeric.FastMath
import GHC.Exts
times4 :: Double -> Double
times4 (D# x) = D# ((x *## x) *## (x *## x))
GHC 8.6.3:
[nix-shell:~]$ ghc Test.hs -cpp -XMagicHash -O -fforce-recomp -ddump-simpl -dsuppress-all
[1 of 1] Compiling Test             ( Test.hs, Test.o )
Result size of Tidy Core
  = {terms: 27, types: 11, coercions: 0, joins: 0/1}
-- RHS size: {terms: 12, types: 4, coercions: 0, joins: 0/1}
times4
times4
  = \ ds_d12P ->
      case ds_d12P of { D# x_a11H ->
      let {
        xx_a12Z
        xx_a12Z = *## x_a11H x_a11H } in
      D# (*## xx_a12Z xx_a12Z)
      }
... irrelevant bits ...
GHC 8.4.4:
[nix-shell:~]$ ghc Test.hs -cpp -XMagicHash -O -fforce-recomp -ddump-simpl -dsuppress-all
[1 of 1] Compiling Test             ( Test.hs, Test.o )
==================== Tidy Core ====================
Result size of Tidy Core
  = {terms: 27, types: 11, coercions: 0, joins: 0/1}
-- RHS size: {terms: 12, types: 4, coercions: 0, joins: 0/1}
times4
times4
  = \ ds_d12U ->
      case ds_d12U of { D# x_a11N ->
      let {
        xx_a134
        xx_a134 = *## x_a11N x_a11N } in
      D# (*## xx_a134 xx_a134)
      }
... irrelevant bits ...
GHC 8.2.2:
[nix-shell:~]$ ghc Test.hs -cpp -XMagicHash -O -fforce-recomp -ddump-simpl -dsuppress-all
[1 of 1] Compiling Test             ( Test.hs, Test.o )
==================== Tidy Core ====================
Result size of Tidy Core
  = {terms: 27, types: 11, coercions: 0, joins: 0/1}
-- RHS size: {terms: 12, types: 4, coercions: 0, joins: 0/1}
times4
times4
  = \ ds_dY7 ->
      case ds_dY7 of { D# x_aXd ->
      let {
        xx_aYh
        xx_aYh = *## x_aXd x_aXd } in
      D# (*## xx_aYh xx_aYh)
      }
... irrelevant bits ...
GHC HEAD:
[nix-shell:~]$ ghc Test.hs -cpp -XMagicHash -O -fforce-recomp -ddump-simpl -dsuppress-all
[1 of 1] Compiling Test             ( Test.hs, Test.o )
==================== Tidy Core ====================
Result size of Tidy Core
  = {terms: 27, types: 11, coercions: 0, joins: 0/1}
-- RHS size: {terms: 12, types: 4, coercions: 0, joins: 0/1}
times4
times4
  = \ ds_d12H ->
      case ds_d12H of { D# x_a11A ->
      let {
        xx_a12R
        xx_a12R = *## x_a11A x_a11A } in
      D# (*## xx_a12R xx_a12R)
      }
... irrelevant bits ...
-O1 and -O2 produce the same results, which is expected, since I don't think GHC can optimise that further.