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