GCC的内联汇编程序识别声明器=r
和=&r
。这对我来说很有意义:=r
允许汇编程序重用一个输入寄存器进行输出。
然而,GCC的内联汇编程序也能识别声明器+r
和+&r
。这些对我来说没什么意义。毕竟,+r
和+&r
之间的区别不是没有区别的吗?单靠+r
不足以让编译器为单独使用一个变量保留一个寄存器吗?
例如,下面的GCC代码有什么问题?
#include <stdio.h>
int main()
{
int a = 0;
printf("Initially, a == %d.\n", a);
/* The architecture is amd64/x86-64. */
asm(
"inc %[a]\n"
: [a] "+r" (a)
: : "cc"
);
printf("However, after incrementation, a == %d.\n", a);
return 0;
}
顺便注意一下,我的内联程序集缺少一个输入声明,因为在我(可能是错误的)头脑中,+r
涵盖了输入、失败、输出等所有内容。请问我误解了什么?
背景
我在汇编中编写了8位和16位微控制器,但很少或根本没有在托管环境中编写汇编的经验。
发布于 2017-08-26 14:51:56
默认情况下,GCC假设内联程序集语句由一个简单的程序集指令组成,该程序集指令在写入任何输出操作数之前消耗其所有输入操作数。当编写使用多个程序集指令的内联程序集语句时,这个假设经常被打破,因此需要使用早期的clobber约束修饰符&
来指示在使用所有输入操作数之前将输出操作数写入哪个输出操作数。这对于使用=
修饰符的输出操作数和使用+
的读/写输出操作数都是必要的。例如,考虑以下两个功能:
int
foo() {
int a = 1;
asm("add %1, %0" : "+r" (a) : "r" (1));
return a;
}
int
bar() {
int a = 1;
asm("add %1, %0\n\t"
"add %1, %0"
: "+r" (a) : "r" (1));
return a;
}
两条内联程序集语句都使用相同的操作数和相同的约束,但是foo
中唯一的内联程序集语句是正确的,bar
中的内联程序集语句是坏的。启用优化后,GCC为这两个函数生成以下代码:
_foo:
movl $1, %eax
/APP
add %eax, %eax
/NO_APP
ret
_bar:
movl $1, %eax
/APP
add %eax, %eax
add %eax, %eax
/NO_APP
ret
GCC认为没有理由不对两个内联程序集语句中的两个操作数使用相同的寄存器EAX。虽然这在foo
中不是问题,但它会导致bar
计算错误的结果为4,而不是预期的3。
正确的bar
版本将使用早期的clobber修饰符:
int
baz() {
int a = 1;
asm("add %1, %0\n\t"
"add %1, %0"
: "+&r" (a) : "r" (1));
return a;
}
_baz:
movl $1, %eax
movl %eax, %edx
/APP
add %edx, %eax
add %edx, %eax
/NO_APP
ret
在编译baz
时,GCC知道对两个操作数使用不同的寄存器,因此在第二次读取输入操作数之前修改读/写输出操作数并不重要。
https://stackoverflow.com/questions/45895564
复制相似问题