[GHC] #14999: unwinding info for stg_catch_frame is wrong

GHC ghc-devs at haskell.org
Wed Apr 4 09:50:32 UTC 2018


#14999: unwinding info for stg_catch_frame is wrong
-------------------------------------+-------------------------------------
        Reporter:  niteria           |                Owner:  niteria
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:  8.4.3
       Component:  Compiler          |              Version:
      Resolution:                    |             Keywords:
Operating System:  Linux             |         Architecture:  x86_64
 Type of failure:  Debugging         |  (amd64)
  information is incorrect           |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by niteria):

 First thing is to understand how to read unwind information in Cmm.

 If you have:
 {{{
 A // starts at addr X and ends at addr Y
 unwind Sp = Just Sp + 16;
 B // starts at addr Y and ends at addr Z
 }}}

 Does the unwind information reflect the state after `A` or `B`?
 If it reflects the state after `A` then you'd expect the LOC to be `Y`.
 If it reflects the state after `B` then you'd expect the LOC to be `Z`.

 I couldn't find a relevant note, so I resorted to experimentation.
 It appears that the unwind annotation reflects the state after `A`, but
 before `B`.

 With that in mind we can look at relevant Cmm dumps (`-ddump-cmm -ddump-
 opt-cmm -ddump-cmm-proc -ddump-cmm-verbose`):

 {{{

 ==================== Parsed Cmm ====================
 [stg_catch_frame_ret() //  [R1]
          { info_tbl: [(c5,
                        label: stg_catch_frame_info
                        rep:tag:34 StackRep [True, False])]
            stack_info: arg_space: 32 updfr_space: Just 8
          }
      {offset
        c5: // c1
            _c1::I64 = I64[(old + 32)];   // CmmAssign
            _c2::I64 = I64[(old + 24)];   // CmmAssign
            _c3::P64 = P64[(old + 16)];   // CmmAssign
            _c4::P64 = R1;   // CmmAssign
            //tick src<Small.cmm:(8,1)-(11,1)>
            unwind Sp = Just Sp + (8 + 16);   // CmmUnwind
            R1 = _c4::P64;   // CmmAssign
            call (P64[(old + 8)])(R1) args: 8, res: 0, upd: 8;   // CmmCall
      }
  }]



 ==================== Post control-flow optimisations ====================
 {offset
   c5: // c1
       _c1::I64 = I64[(old + 32)];   // CmmAssign
       _c2::I64 = I64[(old + 24)];   // CmmAssign
       _c3::P64 = P64[(old + 16)];   // CmmAssign
       _c4::P64 = R1;   // CmmAssign
       //tick src<Small.cmm:(8,1)-(11,1)>
       unwind Sp = Just Sp + (8 + 16);   // CmmUnwind
       R1 = _c4::P64;   // CmmAssign
       call (P64[(old + 8)])(R1) args: 8, res: 0, upd: 8;   // CmmCall
 }



 ==================== Post common block elimination ====================
 {offset
   c5: // c1
       _c1::I64 = I64[(old + 32)];   // CmmAssign
       _c2::I64 = I64[(old + 24)];   // CmmAssign
       _c3::P64 = P64[(old + 16)];   // CmmAssign
       _c4::P64 = R1;   // CmmAssign
       //tick src<Small.cmm:(8,1)-(11,1)>
       unwind Sp = Just Sp + (8 + 16);   // CmmUnwind
       R1 = _c4::P64;   // CmmAssign
       call (P64[(old + 8)])(R1) args: 8, res: 0, upd: 8;   // CmmCall
 }



 ==================== Post switch plan ====================
 {offset
   c5: // c1
       _c1::I64 = I64[(old + 32)];   // CmmAssign
       _c2::I64 = I64[(old + 24)];   // CmmAssign
       _c3::P64 = P64[(old + 16)];   // CmmAssign
       _c4::P64 = R1;   // CmmAssign
       //tick src<Small.cmm:(8,1)-(11,1)>
       unwind Sp = Just Sp + (8 + 16);   // CmmUnwind
       R1 = _c4::P64;   // CmmAssign
       call (P64[(old + 8)])(R1) args: 8, res: 0, upd: 8;   // CmmCall
 }



 ==================== Layout Stack ====================
 {offset
   c5: // c1
       unwind Sp = Just Sp + 24;   // CmmUnwind
       _c1::I64 = I64[Sp];   // CmmAssign
       _c2::I64 = I64[Sp + 8];   // CmmAssign
       _c3::P64 = P64[Sp + 16];   // CmmAssign
       _c4::P64 = R1;   // CmmAssign
       //tick src<Small.cmm:(8,1)-(11,1)>
       unwind Sp = Just Sp + (8 + 16);   // CmmUnwind
       R1 = _c4::P64;   // CmmAssign
       unwind Sp = Just Sp + 0;   // CmmUnwind
       Sp = Sp + 24;   // CmmAssign
       call (P64[Sp])(R1) args: 8, res: 0, upd: 8;   // CmmCall
 }



 ==================== Sink assignments ====================
 {offset
   c5: // c1
       unwind Sp = Just Sp + 24;   // CmmUnwind
       //tick src<Small.cmm:(8,1)-(11,1)>
       unwind Sp = Just Sp + 24;   // CmmUnwind
       R1 = R1;   // CmmAssign
       unwind Sp = Just Sp;   // CmmUnwind
       Sp = Sp + 24;   // CmmAssign
       call (P64[Sp])(R1) args: 8, res: 0, upd: 8;   // CmmCall
 }



 ==================== Post common block elimination 2 ====================
 {offset
   c5: // c1
       unwind Sp = Just Sp + 24;   // CmmUnwind
       //tick src<Small.cmm:(8,1)-(11,1)>
       unwind Sp = Just Sp + 24;   // CmmUnwind
       R1 = R1;   // CmmAssign
       unwind Sp = Just Sp;   // CmmUnwind
       Sp = Sp + 24;   // CmmAssign
       call (P64[Sp])(R1) args: 8, res: 0, upd: 8;   // CmmCall
 }



 ==================== CAFEnv ====================
 [(c5, {})]



 ==================== after setInfoTableStackMap ====================
 stg_catch_frame_ret() //  [R1]
         { info_tbl: [(c5,
                       label: stg_catch_frame_info
                       rep:tag:34 StackRep [True, False])]
           stack_info: arg_space: 32 updfr_space: Just 8
         }
     {offset
       c5: // c1
           unwind Sp = Just Sp + 24;   // CmmUnwind
           //tick src<Small.cmm:(8,1)-(11,1)>
           unwind Sp = Just Sp + 24;   // CmmUnwind
           R1 = R1;   // CmmAssign
           unwind Sp = Just Sp;   // CmmUnwind
           Sp = Sp + 24;   // CmmAssign
           call (P64[Sp])(R1) args: 8, res: 0, upd: 8;   // CmmCall
     }
 }



 ==================== Post control-flow optimisations ====================
 stg_catch_frame_ret() //  [R1]
         { info_tbl: [(c5,
                       label: stg_catch_frame_info
                       rep:tag:34 StackRep [True, False])]
           stack_info: arg_space: 32 updfr_space: Just 8
         }
     {offset
       c5: // c1
           unwind Sp = Just Sp + 24;   // CmmUnwind
           //tick src<Small.cmm:(8,1)-(11,1)>
           unwind Sp = Just Sp + 24;   // CmmUnwind
           R1 = R1;   // CmmAssign
           unwind Sp = Just Sp;   // CmmUnwind
           Sp = Sp + 24;   // CmmAssign
           call (P64[Sp])(R1) args: 8, res: 0, upd: 8;   // CmmCall
     }
 }



 ==================== Post CPS Cmm ====================
 [stg_catch_frame_ret() //  [R1]
          { info_tbl: [(c5,
                        label: stg_catch_frame_info
                        rep:tag:34 StackRep [True, False])]
            stack_info: arg_space: 32 updfr_space: Just 8
          }
      {offset
        c5: // c1
            unwind Sp = Just Sp + 24;   // CmmUnwind
            //tick src<Small.cmm:(8,1)-(11,1)>
            unwind Sp = Just Sp + 24;   // CmmUnwind
            R1 = R1;   // CmmAssign
            unwind Sp = Just Sp;   // CmmUnwind
            Sp = Sp + 24;   // CmmAssign
            call (P64[Sp])(R1) args: 8, res: 0, upd: 8;   // CmmCall
      }
  }]



 ==================== Optimised Cmm ====================
 stg_catch_frame_ret() //  [R1]
         { [(c5,
             stg_catch_frame_info:
                 const 66;
                 const 34;)]
         }
     {offset
       c5: // c1
           unwind Sp = Just Sp + 24;   // CmmUnwind
           //tick src<Small.cmm:(8,1)-(11,1)>
           unwind Sp = Just Sp + 24;   // CmmUnwind
           // nop
           unwind Sp = Just Sp;   // CmmUnwind
           Sp = Sp + 24;   // CmmAssign
           call (P64[Sp])(R1) args: 8, res: 0, upd: 8;   // CmmCall
     }
 }
 }}}

 The first place where things go obviously wrong is `Layout Stack`.

 Let's focus on this fragment:

 {{{
       unwind Sp = Just Sp + (8 + 16);   // CmmUnwind
       R1 = _c4::P64;   // CmmAssign
       unwind Sp = Just Sp + 0;   // CmmUnwind
       Sp = Sp + 24;   // CmmAssign
 }}}

 It looks like we swapped the unwind with the `Sp` update.
 Here's what we'd want:

 {{{
       unwind Sp = Just Sp + (8 + 16);   // CmmUnwind
       R1 = _c4::P64;   // CmmAssign
       Sp = Sp + 24;   // CmmAssign
       unwind Sp = Just Sp + 0;   // CmmUnwind
 }}}

 It turns out that the place that's responsible for this is `manifestSp` in
 `CmmLayoutStack`.

 After a small change:

 {{{
 diff --git a/compiler/cmm/CmmLayoutStack.hs
 b/compiler/cmm/CmmLayoutStack.hs
 index d2525d1..1e8c7c4 100644
 --- a/compiler/cmm/CmmLayoutStack.hs
 +++ b/compiler/cmm/CmmLayoutStack.hs
 @@ -860,7 +860,7 @@ manifestSp dflags stackmaps stack0 sp0 sp_high
        = block
        where sp_unwind = CmmRegOff spReg (sp0 - wORD_SIZE dflags)

 -    -- Add unwind pseudo-instruction right before the Sp adjustment
 +    -- Add unwind pseudo-instruction right after the Sp adjustment
      -- if there is one.
      add_adj_unwind block
        | debugLevel dflags > 0
 @@ -870,8 +870,9 @@ manifestSp dflags stackmaps stack0 sp0 sp_high
        = block
        where sp_unwind = CmmRegOff spReg (sp0 - wORD_SIZE dflags - sp_off)

 -    final_middle = maybeAddSpAdj dflags sp_off
 -                 . add_adj_unwind
 +    final_middle =
 +                 add_adj_unwind
 +                 . maybeAddSpAdj dflags sp_off
                   . add_initial_unwind
                   . blockFromList
                   . map adj_pre_sp
 }}}

 We get:

 {{{
 0000000000000010 <stg_catch_frame_info>:
   10:   48 83 c5 18             add    $0x18,%rbp
   14:   ff 65 00                jmpq   *0x0(%rbp)
 }}}

 {{{
 Contents of the .debug_frame section:

 00000000 0000000000000014 ffffffff CIE "" cf=1 df=-8 ra=16
    LOC           CFA      rbp   rsp   ra
 0000000000000000 rbp+0    v+0   s     c+0

 00000018 000000000000002c 00000000 FDE cie=00000000
 pc=000000000000000f..0000000000000017
    LOC           CFA      rbp   rsp   ra
 000000000000000f rbp+0    v+0   s     c+0
 000000000000000f rbp+24   v+0   s     c+0
 0000000000000014 rbp+0    v+0   s     c+0
 }}}



 As a side note, the manually inserted unwind info in `stg_catch_frame`
 appears to be redundant. Layout Stack appears to be doing the same thing.

 5279b08ba3f01e4b7e28d12b2751413d789d9fbe is what introduced this behavior,
 so I'd like Ben to check if I didn't go off the rails somewhere.

-- 
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14999#comment:2>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list