0

我正在编写一个 LLVM 传递模块来检测程序中的每一个内存操作,并且我的部分逻辑需​​要对指针执行一些非常热门的二进制逻辑。

如何在尽可能少的周期内实现“位?u64_value:零”,最好不使用显式分支?我在寄存器的最低有效位中有一个位,在另一个中有一个值(假设为 u64)。如果设置了该位,我希望保留该值。如果该位为零,我想将寄存器归零。

我可以使用 x86 BMI 指令。

4

2 回答 2

2

在 AMD 和 Intel Broadwell 及更高版本上,CMOV 仅为 1 uop,具有 1 个延迟周期。或者在 Haswell 和更早的版本上 2 uops / 2 个周期。这是有条件地将寄存器归零的最佳选择。

xor  r10d, r10d   # r10=0.  hoist out of loops if possible

test    al, 1           # test the low bit of RAX, setting ZF
cmovz   rax, r10        # zero RAX if the low bit was zero, otherwise unmodified

test r64, imm8编码不存在,因此如果您正在测试低 8 位之外全为零的掩码,则需要使用低 8 寄存器。)

如果位位置在寄存器中,则bt reg, reg在 Intel 和 AMD 上只有 1 uop。(bts reg,reg通过 Ryzen 在 AMD K8 上是 2 uops,但是bt根据所选位的值设置 CF 在 AMD 和 Intel 上很便宜。)

bt     rax, rdx      # CF = RAX & (1<<rdx)
cmovnc rax, r10

使用这两种方法,您测试的寄存器可能与 CMOV 目标不同。

有关更多性能信息,请参阅https://agner.org/optimize/,以及https://stackoverflow.com/tags/x86/info

于 2018-12-21T20:11:40.273 回答
1

select是你的朋友。它主要编译为,cmov但即使没有,后端也会处理它。从语义上讲,它是“如果 arg1 为真,则 arg2 否则为 arg3”,就像 C/C++/java 中的 ?: 一样。在 C++ API 中,您调用SelectInst::Create(yourBool, yourInputValue, ConstantInt::get(i64, 0), instructionName, currentBlock);.

如果您可以编造有意义的名称作为说明,您会发现生活更轻松。一开始并不重要,但随着代码的增长,它会越来越简化调试。

于 2018-12-21T22:29:25.410 回答