我之前question的回答表明Haskell将plusWord2#
表示为llvm.uadd.with.overflow
。我希望使用进位进行长时间的添加,就像x86 ADC指令的工作方式一样。该指令不仅添加了它的两个参数,还添加了进位的内容。
然后可以添加如下的长数字:
ADD x1 y1
ADC x2 y2
ADC x3 y3
...
每个单词产生一条指令(无视任何周围的移动等)。
我查看了GMP库,以及它在通用C代码中如何进行长时间的添加。这是mpn/generic/add_n.c
sl = ul + vl;
cy1 = sl < ul;
rl = sl + cy;
cy2 = rl < sl;
cy = cy1 | cy2;
注意它保存了原始加法和进位位加法的进位。这些操作中只有一个可以携带,因此或之后的携带就足够了。
显然,GMP具有针对特定平台的特定汇编代码,但我认为通用代码是一个很好的基础,因为它可能被编写为编译成合理的代码。 plusWord2#
原语操作意味着我不需要进行愚蠢的比较来获取进位,所以我在Haskell中实现了如下的GMP通用代码:
addWithCarry :: Word# -> Word# -> Word# -> (# Word#, Word# #)
addWithCarry x y c =
let
(# c1, r1 #) = plusWord2# x y
(# c2, r2 #) = plusWord2# r1 c
in
(# or# c1 c2, r2 #)
不幸的是,这导致x86代码将进位位保存到寄存器中,然后在其上添加进位,并添加这两个数字,从而导致每个字三个或四个指令而不是一个。
所以我想知道的是我如何组合llvm.uadd.with.overflow
在x86上创建一系列ADC指令来实现多精度算术。如果我得到了产生有效长加法的LLVM代码,我希望我可以将它转换回Haskell原语操作,直接从Haskell代码生成有效的加法。
备注:
我当然可以使用Haskell的FFI来调用内联汇编或GMP,但这会停止内联,并且我怀疑与小型(即&lt; = 256位)操作数的内联代码相比相对较慢。
我发现&#39; clang&#39;有__builtin_addc
,这是一个三个参数加法的形式,它不仅需要两个数字而且还包含一个进位,但是GHC不能通过clang进行编译,所以我不知道它是如何有用的。
答案 0 :(得分:1)
这里要做的正确的事情是确保你的Haskell前端使用Clang在使用__builtin_addc时形成的相同模式来表示你在LLVM的IR中携带算术。
然而,今天不要期望用LLVM生成好的代码。请参阅http://llvm.org/PR20748,其中记录了我们目前在x86上为这样的琐碎模式生成的绝对可怕的代码。但是一旦修复了这个bug,就应该得到你想要的生成代码。