dfe*_*uer 0 haskell integer-arithmetic
是的,我知道这是一个有点疯狂的问题.请不要问我是否真的需要知道答案,或者这是否真的是"我的"问题.谢谢.
有时以下功能非常好:
isNeg# :: Int# -> Int#
isNeg# x
| x <# 0# = -1#
| otherwise = 0#
Run Code Online (Sandbox Code Playgroud)
例如,它可以像这样使用:
cc# f x y = word2Int# (f (int2Word# x) (int2Word# y))
andi# x y = cc# and# x y
xori# x y = cc# xor# x y
ori# x y = cc# or# x y
ifNegFstElseSnd# :: Int# -> Int# -> Int#
ifNegFstElseSnd# x y = case isNeg# x of
n -> ori# (andi# n y) (xori# n y)
Run Code Online (Sandbox Code Playgroud)
当isNeg使用带有-fllvm和-O2的GHC 7.6.3编译上述定义时,它会产生
# BB#0: # %ci4
movq (%rbp), %rax
testq %r14, %r14
js .LBB0_2
# BB#1: # %nid
xorl %ebx, %ebx
jmpq *%rax # TAILCALL
.LBB0_2: # %cic
movq $-1, %rbx
jmpq *%rax # TAILCALL
Run Code Online (Sandbox Code Playgroud)
问题是这js是一个条件跳转,这些似乎被认为是非常紧凑的循环(其中所有东西都已经拆箱等)的效率问题.另一种方法是写
isNeg# :: Int# -> Int#
isNeg# x = uncheckedIShiftRA# x (intSize# -# 1#)
where
intSize# = case bitSize (undefined::Int) of
I# size -> size
Run Code Online (Sandbox Code Playgroud)
这产生了
# BB#0: # %cny
sarq $63, %r14
movq (%rbp), %rax
movq %r14, %rbx
jmpq *%rax # TAILCALL
Run Code Online (Sandbox Code Playgroud)
这非常简单,但我理解shift(sarq)操作相对较慢.
另一种看似非常合理的选择是
isNeg2# :: Int# -> Int#
isNeg2# x = negateInt# ( dataToTag# (x <# 0#) )
Run Code Online (Sandbox Code Playgroud)
遗憾的是,这会产生完全可怕的代码,甚至不值得在这里粘贴.
将升级到GHC 7.8.2并将最后一个定义重写为
isNeg2#x = negateInt#(x <#0#) - (注意:编辑以纠正先前的错误)
匹配新类型给一些非常好的东西?我无法测试它,因为我没有GHC 7.8.2.
自GHC 7.6.3以来,prims的类型已经改变.例如,比较不再导致布尔值.这意味着您的isNeg#功能应改为:
isNeg# :: Int# -> Int#
isNeg# x
= negateInt# (x <# 0#)
Run Code Online (Sandbox Code Playgroud)
这会产生(7.8.2,-O2):
_cMj:
testq %r14,%r14
setl %al
movzbl %al,%ebx
negq %rbx
jmp *(%rbp)
Run Code Online (Sandbox Code Playgroud)
编辑:LLVM代码包含一条sar指令(并不sarq奇怪)所以如果由于某种原因困扰你,那么也许你应该避免使用LLVM.