0

是的,我知道这是一个有点疯狂的问题。请不要问我是否真的需要知道答案,或者这是否真的是“我的”问题所在。谢谢。

有时以下功能非常好:

isNeg# :: Int# -> Int#
isNeg# x
   | x <# 0#   = -1#
   | otherwise = 0#

例如,它可以这样使用:

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)

当使用带有 -fllvm 和 -O2 的 GHC 7.6.3 编译上述定义时isNeg,它会产生

# 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

问题是这js是一个条件跳转,这些似乎被认为是非常紧凑的循环的效率问题(所有东西都已经拆箱等)。另一种方法是写

isNeg# :: Int# -> Int#
isNeg# x = uncheckedIShiftRA# x (intSize# -# 1#)
  where
    intSize# = case bitSize (undefined::Int) of
                 I# size -> size

这产生

# BB#0:                                 # %cny
    sarq    $63, %r14
    movq    (%rbp), %rax
    movq    %r14, %rbx
    jmpq    *%rax  # TAILCALL

这非常简单,但我知道班次 ( sarq) 是相对较慢的操作。

另一个看起来很合理的选择是

isNeg2# :: Int# -> Int#
isNeg2# x = negateInt# ( dataToTag# (x <# 0#) )

可悲的是,这会产生非常糟糕的代码,甚至不值得在这里粘贴。

问题

  1. 有没有更好的办法?

  2. 将升级到 GHC 7.8.2 并将最后一个定义重写为

    isNeg2# x = negateInt# (x <# 0#) -- (注意:已编辑以纠正较早的错误)

与新类型相匹配的东西真的很棒吗?我无法测试它,因为我没有 GHC 7.8.2。

4

1 回答 1

5

自 GHC 7.6.3 以来,prims 的类型发生了变化。例如,比较不再产生布尔值。这意味着您的isNeg#函数应该改为:

isNeg# :: Int# -> Int#
isNeg# x
   = negateInt# (x <# 0#)

这产生(7.8.2,-O2):

_cMj:
        testq %r14,%r14
        setl %al
        movzbl %al,%ebx
        negq %rbx
        jmp *(%rbp)

编辑:并且 LLVM 代码包含一条sar指令(并不sarq奇怪),所以如果由于某种原因这让您感到困扰,那么也许您应该避免使用 LLVM。

于 2014-06-19T23:24:32.893 回答