【发布时间】:2018-03-29 11:49:43
【问题描述】:
我目前正在学习 GCC 的扩展内联汇编。我写了一个 A + B 函数,想检测 ZF 标志,但事情表现得很奇怪。
我使用的编译器是 x86-64 Arch Linux 上的 gcc 7.3.1。
我从下面的代码开始,这段代码会正确打印a + b。
int a, b, sum;
scanf("%d%d", &a, &b);
asm volatile (
"movl %1, %0\n"
"addl %2, %0\n"
: "=r"(sum)
: "r"(a), "r"(b)
: "cc"
);
printf("%d\n", sum);
然后我只是添加了一个变量来检查标志,它给了我错误的输出。
int a, b, sum, zero;
scanf("%d%d", &a, &b);
asm volatile (
"movl %2, %0\n"
"addl %3, %0\n"
: "=r"(sum), "=@ccz"(zero)
: "r"(a), "r"(b)
: "cc"
);
printf("%d %d\n", sum, zero);
GAS 组件输出为
movl -24(%rbp), %eax # %eax = a
movl -20(%rbp), %edx # %edx = b
#APP
# 6 "main.c" 1
movl %eax, %edx
addl %edx, %edx
# 0 "" 2
#NO_APP
sete %al
movzbl %al, %eax
movl %edx, -16(%rbp) # sum = %edx
movl %eax, -12(%rbp) # zero = %eax
这一次,sum 将变为 a + a。但是当我刚刚交换%2和%3时,输出将是正确的a + b。
然后我在 wandbox.org 上检查了各种 gcc 版本(当输出是标志时,clang 似乎不支持它),从版本 4.5.4 到版本 4.7.4 给出了正确的结果a + b,并从版本开始4.8.1 输出全部为a + a。
我的问题是:我写错了代码还是 gcc 有什么问题?
【问题讨论】:
-
问题是你在所有输入被消耗之前破坏了 %0。允许优化器将相同的寄存器用于输入约束作为输出约束。为避免这种情况,您需要将输入约束 %0 设置为 early clobber。为此,请将
"=r"(sum)更改为"=&r"(sum) -
@MichaelPetch 感谢您的回复。这是否意味着我必须在使用 %0 之前使用其他寄存器来保存结果,或者只是限制使用哪个特定寄存器来保存结果,如果我不使用 early clobber?
-
如果您不想使用早期的 clobber 修饰符,那么您必须为输入约束指定一个特定的寄存器,为输出约束指定一个特定的寄存器(所有不同的寄存器)。您将无法使用
"=r"和"r"来允许编译器自动选择空闲寄存器,从而生成效率较低的代码。 -
@MichaelPetch 再次非常感谢您。我的问题解决了。但是你为什么不把它作为答案而不是评论发布呢?
标签: gcc assembly x86 inline-assembly att