使用单个位作为单词的掩码

Use a single bit as mask for a word(s)

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

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

我可以使用 x86 BMI 指令。

在 AMD、Intel Broadwell 及更高版本上,CMOV 仅为 1 uop,具有 1 个延迟周期。或者在 Haswell 及更早版本上为 2 微指令/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 微指令,但是根据所选位的值设置 CF 的普通 bt 在 AMD 和英特尔上很便宜。)

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

有了这两个,您测试的寄存器可以不同于 CMOV 目标。

https://agner.org/optimize/ for more performance info, and also https://whosebug.com/tags/x86/info

select 是你的朋友。它主要编译为 cmov 但后端会处理它,即使没有。从语义上讲,它是 "if arg1 is true then arg2 else arg3",更像是 C/C++/java 中的 ?:。在 C++ API 中你调用 SelectInst::Create(yourBool, yourInputValue, ConstantInt::get(i64, 0), instructionName, currentBlock);.

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