fast-math icon indicating copy to clipboard operation
fast-math copied to clipboard

Fragile rewrite rules

Open thomie opened this issue 9 years ago • 4 comments

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

thomie avatar Jul 28 '15 17:07 thomie

Good catch. There's probably a way to force this to terminate by putting an id function call in there somewhere.

mikeizbicki avatar Aug 01 '15 22:08 mikeizbicki

Do we have a resolution for this? @mikeizbicki has basically taken over the package these days…

liyang avatar Oct 13 '15 07:10 liyang

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 ...

chessai avatar Jan 30 '19 23:01 chessai

-O1 and -O2 produce the same results, which is expected, since I don't think GHC can optimise that further.

chessai avatar Jan 30 '19 23:01 chessai