Skip to content

Extend constant folding operations to bit manipulations

In #9136 (closed) GHC has greatly improved how it folds expressions for Int/Word when the basic arithmetic operations +, -, * are involved.

I think that somewhat analogously this should be extended to binary manipulation operations. For example, consider the below snippet:

{-# OPTIONS_GHC -O2 -ddump-simpl -ddump-to-file -ddump-asm #-}
module G (bar, baz) where

import Data.Bits ((.|.))
import Data.Word (Word16)

bar :: Word16 -> Int
bar w = -9223372036854775808 .|. (281474976710656 .|. fromIntegral w)

baz :: Word16 -> Int
baz w = -9223372036854775808 + (281474976710656 + fromIntegral w)

Let's peek into the Core:

bar
  = \ (w_a1hX :: Word16) ->
      case w_a1hX of { GHC.Word.W16# x#_a1Lu ->
      GHC.Types.I#
        (GHC.Prim.orI#
           -9223372036854775808#
           (GHC.Prim.orI# 281474976710656# (GHC.Prim.word2Int# x#_a1Lu)))
      }

baz
  = \ (w_a1sr :: Word16) ->
      case w_a1sr of { GHC.Word.W16# x#_a1Lu ->
      GHC.Types.I#
        (GHC.Prim.+# -9223090561878065152# (GHC.Prim.word2Int# x#_a1Lu))
      }

Due to #9136 (closed), baz gets nicely folded away into a single addition. bar however is still in its "naive" form. The above example was extracted from an actual program where there may be long such chains of orI# and similar operations.

I think it's worth pointing out that if we peek in the ASM, bar seems to get folded away eventually but I don't trust it to always happen properly at that level and I don't particularly fancy trawling through the ASM output every time.

https://stackoverflow.com/a/45909278 has some laws we could use. I'm sure we could peek in LLVM source &c. too if we want to.

Edited by Mateusz Kowalczyk
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information