我正在编写一个 LLVM 传递模块来检测程序中的每一个内存操作,并且我的部分逻辑需要对指针执行一些非常热门的二进制逻辑。
如何在尽可能少的周期内实现“位?u64_value:零”,最好不使用显式分支?我在寄存器的最低有效位中有一个位,在另一个中有一个值(假设为 u64)。如果设置了该位,我希望保留该值。如果该位为零,我想将寄存器归零。
我可以使用 x86 BMI 指令。
在 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
select
是你的朋友。它主要编译为,cmov
但即使没有,后端也会处理它。从语义上讲,它是“如果 arg1 为真,则 arg2 否则为 arg3”,就像 C/C++/java 中的 ?: 一样。在 C++ API 中,您调用SelectInst::Create(yourBool, yourInputValue, ConstantInt::get(i64, 0), instructionName, currentBlock);
.
如果您可以编造有意义的名称作为说明,您会发现生活更轻松。一开始并不重要,但随着代码的增长,它会越来越简化调试。