[Haskell-cafe] GHC optimisations

Twan van Laarhoven twanvl at gmail.com
Tue Aug 21 22:13:46 EDT 2007


Isaac Dupree wrote:
> Simon Peyton-Jones wrote:
> 
>> ...
>> No, constant folding is part of the compiler, I'm afraid, in the 
>> module PrelRules.
>>
>> Simon
> 
> 
> _Constant_ folding is, but in GHC.Base there are rules like (unboxed) 
> multiplying by zero or one, or adding or subtracting zero, from an 
> unknown other (non-constant) value.  I think shifts might be doable via 
> RULES... if you were willing to make one rule for each denominator 2, 4, 
> 8 and so on, which rather depends on max. Int... (and that's not 
> Integers either, I guess)


Just to see what this would look like.


First of all, optimizing mod and div can not be done with PrelRules, 
because they are not primitives, quot and rem are. And most of the nice 
optimizations with shifts no longer work there. But using rules should 
work, assuming the inliner is not too fast.

Multiplication and division can become shifts:

 > {-# RULES
 >
 > -- x * 2^n  -->  x `shiftL` n
 > "x# *# 2#"  forall x#.  x# *# 2# = x# `iShiftL#` 1#
 > "2# *# x#"  forall x#.  2# *# x# = x# `iShiftL#` 1#
 >   -- etc.
 >
 > -- x `div` 2^n  -->  x `shiftR` n
 > "x# `divInt#` 2#"  forall x#.  divInt# x# 2# = x# `iShiftRA#` 1#
 > "x# `divInt#` 4#"  forall x#.  divInt# x# 4# = x# `iShiftRA#` 2#
 >   -- etc.

Mod can become and:

 > -- x `mod` 2^n  -->  x .&. (2^n - 1)
 > "x# `modInt#` 2#"  forall x#.  modInt# x# 2# = andInt# x# 1#
 > "x# `modInt#` 4#"  forall x#.  modInt# x# 4# = andInt# x# 3#
 >   -- etc.
 >
 >   #-}

Here I use a new function (see instance Bits Int),

 > andInt# :: Int# -> Int# -> Int#
 > andInt# x# y# = word2Int# (int2Word# x# `and#` int2Word# y#)

but you could write that inline as well.

A problem with these rules is that you need a whole lot of them. 32 per 
operation (on a 32 bit platform), * 4 operations, * 2 separate versions 
for words and ints = 256.



Other rules that could be interesting are:
 > forall a b. fromInteger a + fromInteger b = fromInteger (a + b)
 > forall a b. fromInteger a * fromInteger b = fromInteger (a * b)
 > -- etc.
To allow optimizations on generic Num code, although I am not sure what 
the Haskell spec has to say about this.



Now, if you want to get really creative you can use other semi-evil 
optimization tricks for quot and rem.
The following is based on code generated by Visual C++:
 > -- remPowInt x y == x `rem` (2^y)
 > remPowInt x y
 >     | r >= 0     =  r
 >     | otherwise  =  ((r - 1) .|. (complement yWithSign)) + 1
 >   where  r = x .&. yWithSign
 >          yWithSign = (1 `shiftL` (bitSize - 1)) .|.
 >                      ((1 `shiftL` y) - 1)
Or in assembly (for y == 2, so x `rem` 4)
 >  and         ecx,80000007h
 >  jns         main+60h (401060h)
 >  dec         ecx
 >  or          ecx,0FFFFFFF8h
 >  inc         ecx

The C++ compiler also performs other optimizations when multiplying with 
other constants, for example *3 becomes something like
 >  lea         eax, [eax+eax*2]
Divisions become horrendous constructs with magic numbers,
 >   -- eax := ecx / 5
 >  mov         eax,66666667h
 >  imul        ecx
 >  sar         edx,1
 >  mov         eax,edx
 >  shr         eax,1Fh
 >  add         eax,edx
But such things are probably best left to the code generator / a 
peephole optimizer, if they are done at all. I think the LEA trick 
should be feasible.


Twan


More information about the Haskell-Cafe mailing list