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 henceNum's negate
is applied to the literal1
. -
negate
forInteger
(integerNegate
) is inlined as it is not markedNOINLINE
inghc-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 applicationIS 1#
(equivalent toS# 1#
ininteger-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
- GHC version used: modified GHC head that can be found here: https://gitlab.haskell.org/hsyl20/ghc/tree/hsyl20-integer
- Operating System: Linux 5.3.8
- System Architecture: x86-64