Skip to content

Segfault with -fenable-rewrite-rules

Summary

A very simple program compiled by GHC segfaults if -fenable-rewrite-rules command-line option is passed.

I think it is similar to #15570 (closed) so you might want to read it too.

Steps to reproduce

In the context of the reimplementation of the integer libraries into a single ghc-bignum library, I'm trying to compile this simple example (with a modified GHC):

main :: IO ()
main = print (-1 :: Integer)
> ghc-stage1 -Wall -fforce-recomp Test.hs -fno-ignore-interface-pragmas && ./Test 
[1 of 1] Compiling Main             ( Test.hs, Test.o )
Linking Test ...
-1

> ghc-stage1 -Wall -fforce-recomp Test.hs -fno-ignore-interface-pragmas -fenable-rewrite-rules && ./Test 
[1 of 1] Compiling Main             ( Test.hs, Test.o )
Linking Test ...
[1]    757796 segmentation fault (core dumped)  ./Test

GDB backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000409b84 in frame_dummy ()
(gdb) bt
#0  0x0000000000409b84 in frame_dummy ()
#1  0x0000000000000000 in ?? ()

Core

Core doesn't look suspicious:

  • -XNegativeLiterals isn't enabled hence Num's negate is applied to the literal 1.
  • negate for Integer (integerNegate) is inlined as it is not marked NOINLINE in ghc-bignum (yet).
  • the only difference is that the precedence (ww1) is inlined (0#) with -fenable-rewrite-rules
-- GOOD

main1 :: String
[GblId]
main1
  = case GHC.Show.$fShow(,)1 of { GHC.Types.I# ww1 ->
    case 1 of {
      GHC.Num.Integer.IS ds1 ->
        case ds1 of ds2 {
          __DEFAULT ->
            GHC.Show.$w$cshowsPrec4
              ww1
              (GHC.Num.Integer.IS (GHC.Prim.negateInt# ds2))
              (GHC.Types.[] @ Char);
          -9223372036854775808# ->
            GHC.Show.$w$cshowsPrec4
              ww1 GHC.Num.Integer.integerAbs1 (GHC.Types.[] @ Char)
        };
      GHC.Num.Integer.IP dt ->
        case GHC.Prim.uncheckedIShiftRL# (GHC.Prim.sizeofByteArray# dt) 3#
        of {
          __DEFAULT ->
            GHC.Show.$w$cshowsPrec4
              ww1 (GHC.Num.Integer.IN dt) (GHC.Types.[] @ Char);
          1# ->
            case GHC.Prim.indexWordArray# dt 0# of {
              __DEFAULT ->
                GHC.Show.$w$cshowsPrec4
                  ww1 (GHC.Num.Integer.IN dt) (GHC.Types.[] @ Char);
              9223372036854775808## ->
                GHC.Show.$w$cshowsPrec4
                  ww1 GHC.Num.Integer.integerNegate1 (GHC.Types.[] @ Char)
            }
        };
      GHC.Num.Integer.IN dt ->
        GHC.Show.$w$cshowsPrec4
          ww1 (GHC.Num.Integer.IP dt) (GHC.Types.[] @ Char)
    }
    }
-- BAD
main1
  = case 1 of {
      GHC.Num.Integer.IS ds1 ->
        case ds1 of ds2 {
          __DEFAULT ->
            GHC.Show.$w$cshowsPrec4
              0#
              (GHC.Num.Integer.IS (GHC.Prim.negateInt# ds2))
              (GHC.Types.[] @ Char);
          -9223372036854775808# ->
            GHC.Show.$w$cshowsPrec4
              0# GHC.Num.Integer.integerAbs1 (GHC.Types.[] @ Char)
        };
      GHC.Num.Integer.IP dt ->
        case GHC.Prim.uncheckedIShiftRL# (GHC.Prim.sizeofByteArray# dt) 3#
        of {
          __DEFAULT ->
            GHC.Show.$w$cshowsPrec4
              0# (GHC.Num.Integer.IN dt) (GHC.Types.[] @ Char);
          1# ->
            case GHC.Prim.indexWordArray# dt 0# of {
              __DEFAULT ->
                GHC.Show.$w$cshowsPrec4
                  0# (GHC.Num.Integer.IN dt) (GHC.Types.[] @ Char);
              9223372036854775808## ->
                GHC.Show.$w$cshowsPrec4
                  0# GHC.Num.Integer.integerNegate1 (GHC.Types.[] @ Char)
            }
        };
      GHC.Num.Integer.IN dt ->
        GHC.Show.$w$cshowsPrec4
          0# (GHC.Num.Integer.IP dt) (GHC.Types.[] @ Char)
    }

STG

STG looks normal too:

  • Integer literal 1 has been replaced with a constructor application IS 1# (equivalent to S# 1# in integer-gmp).
  • the only difference is still that the precedence (ww1) is inlined (0#) with -fenable-rewrite-rules
-- BAD
main1 :: GHC.Base.String
[GblId] =
    \u []
        case GHC.Num.Integer.IS [1#] of {
          GHC.Num.Integer.IS ds1 [Occ=Once!] ->
              case ds1 of ds2 [Occ=Once] {
                __DEFAULT ->
                    case negateInt# [ds2] of sat [Occ=Once] {
                    __DEFAULT ->
                    let {
                      sat [Occ=Once] :: GHC.Num.Integer.Integer
                      [LclId] =
                          CCCS GHC.Num.Integer.IS! [sat];
                    } in  GHC.Show.$w$cshowsPrec4 0# sat GHC.Types.[];
                    };
                -9223372036854775808# ->
                    GHC.Show.$w$cshowsPrec4
                        0# GHC.Num.Integer.integerAbs1 GHC.Types.[];
              };
          GHC.Num.Integer.IP dt ->
              case sizeofByteArray# [dt] of sat [Occ=Once] {
              __DEFAULT ->
              case uncheckedIShiftRL# [sat 3#] of {
                __DEFAULT ->
                    let {
                      sat [Occ=Once] :: GHC.Num.Integer.Integer
                      [LclId] =
                          CCCS GHC.Num.Integer.IN! [dt];
                    } in  GHC.Show.$w$cshowsPrec4 0# sat GHC.Types.[];
                1# ->
                    case indexWordArray# [dt 0#] of {
                      __DEFAULT ->
                          let {
                            sat [Occ=Once] :: GHC.Num.Integer.Integer
                            [LclId] =
                                CCCS GHC.Num.Integer.IN! [dt];
                          } in  GHC.Show.$w$cshowsPrec4 0# sat GHC.Types.[];
                      9223372036854775808## ->
                          GHC.Show.$w$cshowsPrec4
                              0# GHC.Num.Integer.integerNegate1 GHC.Types.[];
                    };
              };
              };
          GHC.Num.Integer.IN dt [Occ=Once] ->
              let {
                sat [Occ=Once] :: GHC.Num.Integer.Integer
                [LclId] =
                    CCCS GHC.Num.Integer.IP! [dt];
              } in  GHC.Show.$w$cshowsPrec4 0# sat GHC.Types.[];
        };

Rules

Both report 2 firing rules:

Rule fired: Class op show (BUILTIN)
Rule fired: Class op negate (BUILTIN)
Rule fired
    Rule: Class op show
    Module: (BUILTIN)
    Before: GHC.Show.show
              TyArg GHC.Num.Integer.Integer ValArg GHC.Show.$fShowInteger
    After:  GHC.Show.$fShowInteger_$cshow
    Cont:   ApplyToVal nodup x
            Stop[BoringCtxt] GHC.Base.String
Rule fired
    Rule: Class op negate
    Module: (BUILTIN)
    Before: GHC.Num.negate
              TyArg GHC.Num.Integer.Integer ValArg GHC.Num.$fNumInteger
    After:  GHC.Num.Integer.integerNegate
    Cont:   ApplyToVal nodup 1
            StrictArg GHC.Show.$w$cshowsPrec4
            ApplyToVal nodup w2
            Stop[BoringCtxt] GHC.Base.String

Inlinings

Both report the same inlinings:

Inlining done: System.IO.print
Inlining done: GHC.Show.$fShowInteger_$cshow
Inlining done: GHC.Show.$fShowInteger_$cshowsPrec
Inlining done: GHC.Num.Integer.integerNegate
Inlining done: GHC.TopHandler.runMainIO

Does anyone know what to do to debug this from here? Does -fenable-rewrite-rules have effects later in the pipeline (Cmm, etc.)?

The environment to reproduce this is a bit tricky as I would need to upload modified boot-libs too, but I could provide it if needed. In the meantime, I can provide any log and try any suggestion.

Environment

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information