use*_*875 5 c performance x86 assembly inline-assembly
我正在尝试做一些代码优化来消除分支,原始的c代码是
if( a < b )
k = (k<<1) + 1;
else
k = (k<<1)
Run Code Online (Sandbox Code Playgroud)
我打算用下面的汇编代码替换它
mov a, %rax
mov b, %rbx
mov k, %rcx
xor %rdx %rdx
shl 1, %rcx
cmp %rax, %rax
setb %rdx
add %rdx,%rcx
mov %rcx, k
Run Code Online (Sandbox Code Playgroud)
所以我写了内联汇编代码,如打击,
#define next(a, b, k)\
__asm__("shl $0x1, %0; \
xor %%rbx, %%rbx; \
cmp %1, %2; \
setb %%rbx; \
addl %%rbx,%0;":"+c"(k) :"g"(a),"g"(b))
Run Code Online (Sandbox Code Playgroud)
当我编译下面的代码时,我得到了错误:
operand type mismatch for `add'
operand type mismatch for `setb'
Run Code Online (Sandbox Code Playgroud)
我该如何解决?
sco*_*ttt 10
以下是代码中的错误:
setb %bl工作时setb %rbx不工作.T = (A < B)应转换为cmp B,A; setb TAT&T x86汇编语法.你有两个错误的CMP操作数.请记住,CMP就像SUB一样.一旦你意识到汇编程序产生了前两个错误消息,那么调试它们的技巧就是查看gcc生成的汇编程序代码.尝试使用x86操作码参考gcc $CFLAGS -S t.c比较有问题的行.专注于每条指令的允许操作数代码,您将很快看到问题.t.s
在下面发布的固定源代码中,我假设您的操作数是无符号的,因为您使用的是SETB而不是SETL.我使用RBX切换到RCX来保存临时值,因为RCX是ABI中的一个调用阻塞寄存器,并使用"=&c"约束将其标记为早期操作数,因为RCX在输入之前被清除 a并被b读取:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
static uint64_t next(uint64_t a, uint64_t b, uint64_t k)
{
uint64_t tmp;
__asm__("shl $0x1, %[k];"
"xor %%rcx, %%rcx;"
"cmp %[b], %[a];"
"setb %%cl;"
"addq %%rcx, %[k];"
: /* outputs */ [k] "+g" (k), [tmp] "=&c" (tmp)
: /* inputs */ [a] "r" (a), [b] "g" (b)
: /* clobbers */ "cc");
return k;
}
int main()
{
uint64_t t, t0, k;
k = next(1, 2, 0);
printf("%" PRId64 "\n", k);
scanf("%" SCNd64 "%" SCNd64, &t, &t0);
k = next(t, t0, k);
printf("%" PRId64 "\n", k);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
main()转换为:
<+0>: push %rbx
<+1>: xor %ebx,%ebx
<+3>: mov $0x4006c0,%edi
<+8>: mov $0x1,%bl
<+10>: xor %eax,%eax
<+12>: sub $0x10,%rsp
<+16>: shl %rax
<+19>: xor %rcx,%rcx
<+22>: cmp $0x2,%rbx
<+26>: setb %cl
<+29>: add %rcx,%rax
<+32>: mov %rax,%rbx
<+35>: mov %rax,%rsi
<+38>: xor %eax,%eax
<+40>: callq 0x400470 <printf@plt>
<+45>: lea 0x8(%rsp),%rdx
<+50>: mov %rsp,%rsi
<+53>: mov $0x4006c5,%edi
<+58>: xor %eax,%eax
<+60>: callq 0x4004a0 <__isoc99_scanf@plt>
<+65>: mov (%rsp),%rax
<+69>: mov %rbx,%rsi
<+72>: mov $0x4006c0,%edi
<+77>: shl %rsi
<+80>: xor %rcx,%rcx
<+83>: cmp 0x8(%rsp),%rax
<+88>: setb %cl
<+91>: add %rcx,%rsi
<+94>: xor %eax,%eax
<+96>: callq 0x400470 <printf@plt>
<+101>: add $0x10,%rsp
<+105>: xor %eax,%eax
<+107>: pop %rbx
<+108>: retq
Run Code Online (Sandbox Code Playgroud)
您可以在每次调用之前看到next()移入RSI的结果printf().
鉴于gcc(它看起来像gcc内联汇编程序)产生:
leal (%rdx,%rdx), %eax
xorl %edx, %edx
cmpl %esi, %edi
setl %dl
addl %edx, %eax
ret
Run Code Online (Sandbox Code Playgroud)
从
int f(int a, int b, int k)
{
if( a < b )
k = (k<<1) + 1;
else
k = (k<<1);
return k;
}
Run Code Online (Sandbox Code Playgroud)
它会认为编写自己的内联汇编程序完全是浪费时间和精力.
与往常一样,在开始编写内联汇编程序之前,请检查编译器实际执行的操作.如果您的编译器没有生成此代码,那么您可能需要将编译器的版本升级到更新的版本(我向Jan Hubicka报告了这种情况[当时为g86维护者x86-64] ca 2001,并且我确定它已经在gcc中使用了很长时间).
您可以这样做,编译器不会生成分支:
k = (k<<1) + (a < b) ;
Run Code Online (Sandbox Code Playgroud)
但如果你必须,我在你的代码中修改了一些东西,现在它应该按预期工作:
__asm__(
"shl $0x1, %0; \
xor %%eax, %%eax; \
cmpl %3, %2; \
setb %%al; \
addl %%eax, %0;"
:"=r"(k) /* output */
:"0"(k), "r"(a),"r"(b) /* input */
:"eax", "cc" /* clobbered register */
);
Run Code Online (Sandbox Code Playgroud)
需要注意的是setb需要一个reg8或mem8,你应该添加eax到修饰列表,因为你改变它,也cc只是为了安全起见,作为登记的限制,我不知道为什么你使用的那些,但=r与r工作就好了.您需要添加k输入和输出列表.GCC-Inline-Assembly-HOWTO还有更多内容
| 归档时间: |
|
| 查看次数: |
16436 次 |
| 最近记录: |