【发布时间】:2021-05-13 20:58:05
【问题描述】:
我正在尝试在 x86_64 汇编中找到一种有效的方法来执行以下操作:
if(N < word_size) {
dst[N] = 1; // as in Nth bit of dst = 1
}
else {
dst[word_size - 1:0] = 0
}
如果“else”情况没有取消设置其他位,或者“if”情况确实取消了其他位,我可以获得所需的结果。重要的是 如果 N > word_size 它不会设置任何位
我找不到任何可能执行此操作的指令,因为 bt[s/c], shlx, sal, rol, shld 似乎都按宽度占用了 src 的模块。
用例基本上是我将迭代一个已知长度的位向量,并希望 A)找到第一个设置位并返回其位置,或 B)测试所有位,如果没有设置位找到向量的返回长度。
// rsi has length
L(keep_searching):
movq %(rdi), %rax
testq %rax, %rax
jnz L(found)
subq $64, rsi
jbe L(done) // this done will return origional value of rsi
addq $8, %rdi
jmp L(keep_searching)
我认为如果我可以在rax 中快速设置一点,如果rsi
有谁知道可以做到这一点的指令吗?我能想到的要检查的每条指令都使用 src 上的模块。任何帮助将不胜感激。
谢谢!
一些对我来说适用于 32 位的版本。如果我使用 MMX,@PeterCordes 指出 pllsq 正是我想要的。
uint64_t __attribute__((noinline, noclone)) shift(uint64_t cnt) {
uint64_t ret = 0;
asm volatile(
"cmpq $32, %[cnt]\n\t"
"setbe %b[ret]\n\t"
"shlxq %[cnt], %[ret], %[ret]\n\t"
: [ ret ] "+r"(ret)
: [ cnt ] "r"(cnt)
: "cc");
return ret;
}
uint64_t __attribute__((noinline, noclone)) shift2(uint64_t cnt) {
uint64_t ret = 0, tmp = 0;
asm volatile(
"leaq -33(%[cnt]), %[tmp]\n\t"
"movl $1, %k[ret]\n\t"
"shlxq %[cnt], %[ret], %[ret]\n\t"
"sarq $63, %[tmp]\n\t"
"andq %[tmp], %[ret]\n\t"
: [ ret ] "+r"(ret), [ tmp ] "+r"(tmp), [ cnt ] "+r"(cnt)
:
: "cc");
return ret;
}
uint64_t __attribute__((noinline, noclone)) shift3(uint64_t cnt) {
uint64_t ret, tmp;
asm volatile(
"leaq -33(%[cnt]), %[tmp]\n\t"
"btsq %[cnt], %[ret]\n\t"
"sarq $63, %[tmp]\n\t"
"andq %[tmp], %[ret]\n\t"
: [ ret ] "+r"(ret), [ tmp ] "+r"(tmp), [ cnt ] "+r"(cnt)
:
: "cc");
return ret;
}
【问题讨论】:
-
您应该使用
setcc指令,其中cc是条件。setcc如果条件为真,则将指定寄存器设置为 1,如果条件为假,则设置为 0。似乎是适合您情况的完美说明。 -
所以你认为
cmpq $64, %rsi; setbe %rcx; shlx %rsi, %rcx, %rcx?我会试一试,看看它是如何运行的。好主意! -
x86 SIMD 移位(如
psllq xmm1, xmm2)使计数饱和而不是换行。但这似乎不太可能对 this 案例有用。可能只是单独进行最后一次迭代,以便您可以矢量化搜索(一次检查 16 个字节(2 个 qwords 或 4 个 dwords)以确保全非零)。并且绝对将条件分支之一放在底部并删除jmp,例如sub $64, %rsi/ja L(keep_searching)。 Why are loops always compiled into "do...while" style (tail jump)? -
setcc r/m8很不方便,因为它只适用于 8 位寄存器。但是,如果你有一个像 RDX 这样的归零寄存器,你可以setbe dl/shlx %rsi, %rdx, %rcx。即使您不想使用 SIMD,在test/jz keep_searching之前将其 ORing 到 RAX 似乎也不值得。正确预测的未进行的测试/jnz 是单个 uop,比您为创建 RAX 值所做的所有工作要便宜,完成后您必须再次解码 RAX。 -
@PeterCordes 我的想法是,如果我这样做,我将能够使所有回报成为一个未采取的分支。但是,另一种方法是
cmovcc
标签: assembly x86-64 micro-optimization