[GHC] #8905: Function arguments are always spilled/reloaded if scrutinee is already in WHNF

GHC ghc-devs at haskell.org
Sun Mar 16 18:27:18 UTC 2014


#8905: Function arguments are always spilled/reloaded if scrutinee is already in
WHNF
------------------------------+--------------------------------------------
       Reporter:  tibbe       |             Owner:
           Type:  bug         |            Status:  new
       Priority:  normal      |         Milestone:
      Component:  Compiler    |           Version:  7.9
       Keywords:              |  Operating System:  Unknown/Multiple
   Architecture:              |   Type of failure:  Runtime performance bug
  Unknown/Multiple            |         Test Case:
     Difficulty:  Unknown     |          Blocking:
     Blocked By:              |
Related Tickets:              |
------------------------------+--------------------------------------------
 The code generator unnecessarily spills and reloads function arguments if
 the scrutinee turns out to be already evaluated (i.e. has non-zero tag
 bits).

 Here's the beginning of a function body, taken from the `insert` function
 at https://github.com/tibbe/unordered-
 containers/blob/master/Data/HashMap/Base.hs#L303:
 {{{
 c2wQ:  // stack check
     if ((Sp + -72) < SpLim) goto c2wR; else goto c2wS;
 c2wR:  // stack check failure
     R1 = PicBaseReg + $wpoly_go_closure;
     I64[Sp - 40] = R2;
     I64[Sp - 32] = R3;
     P64[Sp - 24] = R4;
     I64[Sp - 16] = R5;
     P64[Sp - 8] = R6;
     Sp = Sp - 40;
     call (I64[BaseReg - 8])(R1) args: 48, res: 0, upd: 8;
 c2wS:  // stack check success
     I64[Sp - 40] = PicBaseReg + block_c2my_info;  // return addr for eval
     R1 = R6;  // t
     I64[Sp - 32] = R2;  // spill: s
     I64[Sp - 24] = R3;  // spill: x
     P64[Sp - 16] = R4;  // spill: k
     I64[Sp - 8] = R5;  // spill: h
     Sp = Sp - 40;
     if (R1 & 7 != 0) goto c2my; else goto c2mz;  // eval check of t
 c2mz:  // eval check failed
     call (I64[R1])(R1) returns to c2my, args: 8, res: 8, upd: 8;  // eval
 c2my:  // eval check succeeded
     _s2b1::I64 = I64[Sp + 8];  // reload: h
     _s2b2::I64 = I64[Sp + 16];  // reload: k
     _s2b3::P64 = P64[Sp + 24];  // reload: x
     _s2b4::I64 = I64[Sp + 32];  // reload: s
     switch [0 .. 4] (R1 & 7 - 1) {
         case 0 : goto c2wK;
         case 1 : goto c2wL;
         case 2 : goto c2wM;
         case 3 : goto c2wN;
         case 4 : goto c2wO;
     }
 }}}

 It seems to me that all the spills/reloads could be pushed into the `c2mz`
 block.

 The `c2my` block, in its current form, is reused for a heap check failure
 case, so the heap check most likely will have to do its own
 spilling/reloading. However, since the scrutinee not having tags bit or
 the eval checking failing is not the common case, they should be out of
 the common path.

 If it matters the data type is spine strict so GHC should have enough
 information to know that the common case (e.g. self-recursive calls)
 already have an evaluated argument (although there might be an indirection
 in some cases).

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


More information about the ghc-tickets mailing list