
#8834: 64-bit windows cabal.exe segfaults in GC ----------------------------------+---------------------------------- Reporter: awson | Owner: jstolarek Type: bug | Status: patch Priority: highest | Milestone: 7.8.1 Component: Compiler | Version: 7.8.1-rc2 Resolution: | Keywords: Operating System: Windows | Architecture: x86_64 (amd64) Type of failure: Runtime crash | Difficulty: Unknown Test Case: | Blocked By: Blocking: | Related Tickets: ----------------------------------+---------------------------------- Comment (by jstolarek): Here's a quick summary of what I've learned: * As we noticed earlier the bug does not happen when only `-O1` is used. Enabling `-O1 -fspec-constr` triggers the bug, but with `-O1 -fspec-constr -fno-cmm-sink` everything works perfectly fine. * I believe that Simon Marlow correctly identified offending piece of Cmm code, although my knowledge of calling conventions doesn't allow me to say why that piece of code is incorrect. Here's how that piece of code looks only with `-O1`: {{{ c2h9: _s2cz::I64 = _s2ct::I64 + _s2cv::I64; (_s2cE::I64) = call "ccall" arg hints: [PtrHint, `signed',] result hints: [PtrHint] memchr(_s2cz::I64, 10, _s2cw::I64); if (_s2cE::I64 == 0) goto c2h4; else goto c2h5; c2h4: Hp = Hp - 32; _s2cH::P64 = Data.Maybe.Nothing_closure+1; goto s2cF; c2h5: I64[Hp - 24] = GHC.Types.I#_con_info; I64[Hp - 16] = _s2cE::I64 - _s2cz::I64; I64[Hp - 8] = Data.Maybe.Just_con_info; P64[Hp] = Hp - 23; _s2cH::P64 = Hp - 6; goto s2cF; s2cF: call MO_Touch(_s2cu::P64); I64[Sp - 40] = c2f7; R1 = _s2cH::P64; I64[Sp - 32] = _s2ct::I64; P64[Sp - 24] = _s2cu::P64; I64[Sp - 16] = _s2cv::I64; I64[Sp - 8] = _s2cw::I64; Sp = Sp - 40; if (R1 & 7 != 0) goto c2f7; else goto c2f8; }}} It looks that sinking is not performed here for some reason that I was unable to figure out. Enabling `-O1 -fspec-constr` changes the above code to: {{{ c2hi: _s2dr::I64 = R5; _s2du::I64 = R2 + R4; _c2fB::I64 = R5; (_s2dz::I64) = call "ccall" arg hints: [PtrHint, `signed',] result hints: [PtrHint] memchr(_s2du::I64, 10, _c2fB::I64); if (_s2dz::I64 == 0) goto c2hd; else goto c2he; c2hd: call MO_Touch(R3); I64[Hp - 128] = Data.ByteString.Internal.PS_con_info; P64[Hp - 120] = R3; I64[Hp - 112] = R2; I64[Hp - 104] = R4; I64[Hp - 96] = _s2dr::I64; I64[Hp - 88] = :_con_info; P64[Hp - 80] = Hp - 127; P64[Hp - 72] = GHC.Types.[]_closure+1; _c2h8::P64 = Hp - 86; Hp = Hp - 72; R1 = _c2h8::P64; call (P64[Sp])(R1) args: 8, res: 0, upd: 8; }}} Enabling `-fspec-constr` sinks R2, R3 and R4 past the call to `memchr` (which BTW. is consistent with awson's earlier observation that segfault is happening somewhere around the call to `memchr`). * Changing definition of `isTrivial` from: {{{ isTrivial :: CmmExpr -> Bool isTrivial (CmmReg _) = True isTrivial (CmmLit _) = True isTrivial _ = False }}} to {{{ isTrivial :: CmmExpr -> Bool isTrivial (CmmReg (CmmLocal _)) = True isTrivial (CmmLit _) = True isTrivial _ = False }}} makes the bug disappear. I believe this is not a proper fix, because it only hides the bug - global registers should be safe to inline! The above Cmm code changes to (`-O1 -fspec-constr` enabled): {{{ 2hi: _s2du::I64 = _s2do::I64 + _s2dq::I64; (_s2dz::I64) = call "ccall" arg hints: [PtrHint, `signed',] result hints: [PtrHint] memchr(_s2du::I64, 10, _s2dr::I64); if (_s2dz::I64 == 0) goto c2hd; else goto c2he; 2hd: call MO_Touch(_s2dp::P64); I64[Hp - 128] = Data.ByteString.Internal.PS_con_info; P64[Hp - 120] = _s2dp::P64; I64[Hp - 112] = _s2do::I64; I64[Hp - 104] = _s2dq::I64; I64[Hp - 96] = _s2dr::I64; I64[Hp - 88] = :_con_info; P64[Hp - 80] = Hp - 127; P64[Hp - 72] = GHC.Types.[]_closure+1; _c2h8::P64 = Hp - 86; Hp = Hp - 72; R1 = _c2h8::P64; call (P64[Sp])(R1) args: 8, res: 0, upd: 8; }}} * Since it looks like the problem is caused by sinking registers past the unsafe foreign call to `memchr` I changed the definition of `foreignTargetRegs` in the instance definition of `DefinerOfRegs` in `CmmNode` (lines 345-346) from: {{{ foreignTargetRegs (ForeignTarget _ (ForeignConvention _ _ _ CmmNeverReturns)) = [] foreignTargetRegs _ = activeCallerSavesRegs }}} to: {{{ foreignTargetRegs (ForeignTarget _ (ForeignConvention _ _ _ CmmNeverReturns)) = [] foreignTargetRegs _ = activeRegs }}} This says that any global register can be clobbered by unsafe foreign call, in practice preventing any global register sinking past such calls. After this change the Cmm dump looks like this (again, `-O1 -fspec-constr` enabled): {{{ c2hi: _s2dr::I64 = R5; _s2dq::I64 = R4; _s2dp::P64 = R3; _s2do::I64 = R2; _s2du::I64 = R2 + R4; _c2fB::I64 = R5; (_s2dz::I64) = call "ccall" arg hints: [PtrHint, `signed',] result hints: [PtrHint] memchr(_s2du::I64, 10, _c2fB::I64); if (_s2dz::I64 == 0) goto c2hd; else goto c2he; c2hd: call MO_Touch(_s2dp::P64); I64[Hp - 128] = Data.ByteString.Internal.PS_con_info; P64[Hp - 120] = _s2dp::P64; I64[Hp - 112] = _s2do::I64; I64[Hp - 104] = _s2dq::I64; I64[Hp - 96] = _s2dr::I64; I64[Hp - 88] = :_con_info; P64[Hp - 80] = Hp - 127; P64[Hp - 72] = GHC.Types.[]_closure+1; _c2h8::P64 = Hp - 86; Hp = Hp - 72; R1 = _c2h8::P64; call (P64[Sp])(R1) args: 8, res: 0, upd: 8; }}} Based on above facts I believe that the problem lies in incorrect definition of caller saves registers. On the other hand I believe we are defining R2, R3 and R4 as callee-saves according to the specification. So there is still possibility that the bug lies somewhere else. At this point I am stuck. I will attach Cmm dumps in a moment so others can verify my findings. All dumps are for the `BugIso` module - they don't include boilerplate from `Main`. Replying to [comment:54 awson]:
Hmm, on my experiments changing `isTrivial` to {{{ isTrivial :: CmmExpr -> Bool isTrivial (CmmReg (CmmLocal _)) = True -- isTrivial (CmmLit _) = True isTrivial _ = False }}} alone was not enough to fix things - see my comment 19. So doing `CmmLit` branch `True` makes this work?
On my experiments the change of `isTrivial` that you just posted is enough to prevent the segmentation fault. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/8834#comment:55 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler