linux的嵌入汇编的一个问题
"=&a"(retval)
这是输出部,把寄存器eax的值放如retval中
我想请问的是 &a的前面的&符号是做 什么用的呢?
albcamus 发表于 2009-06-09 10:41
笔记:
=========
2.8.1 输出操作数是一个 earlyclobber操作数
在inline asm中,特别需要注意 earlyclobber的情况。 earlyclobber的 意思是说,某个输出操作数,可能
在gcc尚未用完所有的输入操作数之前,就已经被写入(inline asm的输出操作)值了。 换言之,gcc尚未使用完
全部的输入操作数的时候,就已经对某些输出操作数产生了输出操作。
earlyclobber是整个gcc inline assembly语法中最晦涩难懂的地方,从而也是程序最容易有BUG的地方。
让我们看一个示例程序:
/* WARNING: WRONG! */
$ cat -n wrong.c
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int var1 = 5, var2 = 5;
6
7 asm volatile
8 (
9 "movl $10, %0\n\t"
10 "movl $20, %1"
11 : "=r" (var1)
12 : "r" (var2)
13 );
14
15 printf("var1 is %d\n", var1);
16 return 0;
17 }
在这个程序中,我们希望给var1变量结合一个寄存器,给var2结合另一个寄存器。 但是程序犯了两个错误:
a) 与var2结合的寄存器是输入操作数,但是第10行给它赋值了;
b) 与var1结合的寄存器是一个 earlyclobber,它在gcc尚未访问完毕输入操作数之前就被赋值,但是程序中没有
指出这一点。
错误a)是我们故意犯的,只是为了说明 earlyclobber的含义。 错误b)的发生就在于程序没有理解 earlyclobber。
我们先看看这个程序的结果是 什么:
/* FIXME: 错误程序不一定产生错误结果,有时候需要打开-O2等优化开关才会出错 */
$ gcc -m32 -O2 wrong.c
$ ./a.out
var1 is 20
可以看到,经过inline asm之后,var1的值变成了20,而不是我们期望的10。 为什么会发生这样的结果呢? 我们看看使用
-S和-fverbose-asm编译之后的*.s文件中的相关代码:
movl $5, -12(%ebp) /, var1
movl $15, -8(%ebp) /, var2
movl -8(%ebp), %eax / var2, tmp62
/APP
/ 7 "wrong.c" 1
movl $10, %eax / tmp61
movl $20, %eax / tmp62
/NO_APP
movl %eax, -12(%ebp) / tmp61, var1
从这段汇编代码中可以看出,gcc让var1和var2都结合到了eax寄存器中。 这造成第9行var1被写之后,在第10行中由于var2的
被访问而再次被写——于是得到了错误结果。
正确的程序是:
$ cat correct.c
#include <stdio.h>
int main(void)
{
int var1 = 5, var2 = 15;
asm volatile
(
"movl $10, %0\n\t"
"movl $20, %1"
: "=&r" (var1)
: "r" (var2)
);
printf("var1 is %d\n", var1);
return 0;
}
编译、运行:
$ gcc -m32 correct.c
$ ./a.out
var1 is 10
正是我们期待的结果。 这是因为,输出部的约束部分加上了 earlyclobber修饰符:&。 这样,gcc就会保证:
a) 不会把该操作数和任何一个输入操作数结合到一起;
b) 严格检查,该操作数必须和寄存器结合,而 *不是* 任何内存位置
我们看看上面这个正确程序用-S -fverbose-asm编译之后的*.s文件中的相关代码:
movl $5, -12(%ebp) /, var1
movl $15, -8(%ebp) /, var2
movl -8(%ebp), %eax / var2, tmp62
/APP
/ 7 "correct.c" 1
movl $10, %edx /
movl $20, %eax / tmp62
/NO_APP
movl %edx, %eax /, tmp61
movl %eax, -12(%ebp) / tmp61, var1
可以看出,gcc为var1结合了edx,为var2结合了eax。 这正是因为我们声明了var1是一个 earlyclobber,从而避免了
gcc的错误行为。
bradgei 发表于 2009-06-09 13:29
Register should be used for output only
源方 发表于 2009-06-11 14:02
[quote]原帖由 [i]albcamus[/i] 于 2009-6-9 14:22 发表 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7036784&ptid=1116656][img]http://linux.chinaunix.net/bbs/images/common/back.gif[/img][/url]
笔记:
=========
2.8.1 输出操作数是一个 earlyclobber操作数
在inline asm中,特别需要注意 earlyclobber的情况。 earlyclobber的 意思是说,某个输出操作数,可能
在gcc尚未用完所有 ... [/quote]
好啊,版主把笔记共享出来吧
albcamus 发表于 2009-06-11 14:09
[quote]原帖由 [i]源方[/i] 于 2009-6-11 14:02 发表 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7039695&ptid=1116656][img]http://linux.chinaunix.net/bbs/images/common/back.gif[/img][/url]
好啊,版主把笔记共享出来吧 [/quote]
我本来想就inline assembly写一个文章投到ibm developerworks的, 都注册了。
结果技术上材料都写完了,却不会组织成一个可读的文章。一直拖到现在。
要是决定撤稿,就发到论坛上。
dreamice 发表于 2009-06-11 14:21
回复 #5 albcamus 的帖子
直接发论坛,让大家拜读一下吧
luo118 发表于 2009-06-11 14:44
支持讓大家學習一下
bradgei 发表于 2009-06-12 23:28
我来弱弱的补充下版主。。。。以下例子也说明"&"的用处。。。。
asm volatile("ldr %0, [%1]" "\n\t"
"str %2, [%1, #4]" "\n\t"
: "=&r" (rdv)
: "r" (&table), "r" (wdv)
: "memory"
);
上面例子:从一个表读一个值,将另一个值写到另外一个地方,如果编译器选择同一个register做为输入和输出入,那么输出的值在 "ldr %0, [%1]" "\n\t"这条汇编语句后就被输入值冲掉了。。。。。所以在输出地方加一个"&",
简而言之,“&” 是说明 Register should be used for output only
"=&a"(retval)
这是输出部,把寄存器eax的值放如retval中
我想请问的是 &a的前面的&符号是做 什么用的呢?
albcamus 发表于 2009-06-09 10:41
笔记:
=========
2.8.1 输出操作数是一个 earlyclobber操作数
在inline asm中,特别需要注意 earlyclobber的情况。 earlyclobber的 意思是说,某个输出操作数,可能
在gcc尚未用完所有的输入操作数之前,就已经被写入(inline asm的输出操作)值了。 换言之,gcc尚未使用完
全部的输入操作数的时候,就已经对某些输出操作数产生了输出操作。
earlyclobber是整个gcc inline assembly语法中最晦涩难懂的地方,从而也是程序最容易有BUG的地方。
让我们看一个示例程序:
/* WARNING: WRONG! */
$ cat -n wrong.c
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int var1 = 5, var2 = 5;
6
7 asm volatile
8 (
9 "movl $10, %0\n\t"
10 "movl $20, %1"
11 : "=r" (var1)
12 : "r" (var2)
13 );
14
15 printf("var1 is %d\n", var1);
16 return 0;
17 }
在这个程序中,我们希望给var1变量结合一个寄存器,给var2结合另一个寄存器。 但是程序犯了两个错误:
a) 与var2结合的寄存器是输入操作数,但是第10行给它赋值了;
b) 与var1结合的寄存器是一个 earlyclobber,它在gcc尚未访问完毕输入操作数之前就被赋值,但是程序中没有
指出这一点。
错误a)是我们故意犯的,只是为了说明 earlyclobber的含义。 错误b)的发生就在于程序没有理解 earlyclobber。
我们先看看这个程序的结果是 什么:
/* FIXME: 错误程序不一定产生错误结果,有时候需要打开-O2等优化开关才会出错 */
$ gcc -m32 -O2 wrong.c
$ ./a.out
var1 is 20
可以看到,经过inline asm之后,var1的值变成了20,而不是我们期望的10。 为什么会发生这样的结果呢? 我们看看使用
-S和-fverbose-asm编译之后的*.s文件中的相关代码:
movl $5, -12(%ebp) /, var1
movl $15, -8(%ebp) /, var2
movl -8(%ebp), %eax / var2, tmp62
/APP
/ 7 "wrong.c" 1
movl $10, %eax / tmp61
movl $20, %eax / tmp62
/NO_APP
movl %eax, -12(%ebp) / tmp61, var1
从这段汇编代码中可以看出,gcc让var1和var2都结合到了eax寄存器中。 这造成第9行var1被写之后,在第10行中由于var2的
被访问而再次被写——于是得到了错误结果。
正确的程序是:
$ cat correct.c
#include <stdio.h>
int main(void)
{
int var1 = 5, var2 = 15;
asm volatile
(
"movl $10, %0\n\t"
"movl $20, %1"
: "=&r" (var1)
: "r" (var2)
);
printf("var1 is %d\n", var1);
return 0;
}
编译、运行:
$ gcc -m32 correct.c
$ ./a.out
var1 is 10
正是我们期待的结果。 这是因为,输出部的约束部分加上了 earlyclobber修饰符:&。 这样,gcc就会保证:
a) 不会把该操作数和任何一个输入操作数结合到一起;
b) 严格检查,该操作数必须和寄存器结合,而 *不是* 任何内存位置
我们看看上面这个正确程序用-S -fverbose-asm编译之后的*.s文件中的相关代码:
movl $5, -12(%ebp) /, var1
movl $15, -8(%ebp) /, var2
movl -8(%ebp), %eax / var2, tmp62
/APP
/ 7 "correct.c" 1
movl $10, %edx /
movl $20, %eax / tmp62
/NO_APP
movl %edx, %eax /, tmp61
movl %eax, -12(%ebp) / tmp61, var1
可以看出,gcc为var1结合了edx,为var2结合了eax。 这正是因为我们声明了var1是一个 earlyclobber,从而避免了
gcc的错误行为。
bradgei 发表于 2009-06-09 13:29
Register should be used for output only
源方 发表于 2009-06-11 14:02
[quote]原帖由 [i]albcamus[/i] 于 2009-6-9 14:22 发表 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7036784&ptid=1116656][img]http://linux.chinaunix.net/bbs/images/common/back.gif[/img][/url]
笔记:
=========
2.8.1 输出操作数是一个 earlyclobber操作数
在inline asm中,特别需要注意 earlyclobber的情况。 earlyclobber的 意思是说,某个输出操作数,可能
在gcc尚未用完所有 ... [/quote]
好啊,版主把笔记共享出来吧
albcamus 发表于 2009-06-11 14:09
[quote]原帖由 [i]源方[/i] 于 2009-6-11 14:02 发表 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7039695&ptid=1116656][img]http://linux.chinaunix.net/bbs/images/common/back.gif[/img][/url]
好啊,版主把笔记共享出来吧 [/quote]
我本来想就inline assembly写一个文章投到ibm developerworks的, 都注册了。
结果技术上材料都写完了,却不会组织成一个可读的文章。一直拖到现在。
要是决定撤稿,就发到论坛上。
dreamice 发表于 2009-06-11 14:21
回复 #5 albcamus 的帖子
直接发论坛,让大家拜读一下吧
luo118 发表于 2009-06-11 14:44
支持讓大家學習一下
bradgei 发表于 2009-06-12 23:28
我来弱弱的补充下版主。。。。以下例子也说明"&"的用处。。。。
asm volatile("ldr %0, [%1]" "\n\t"
"str %2, [%1, #4]" "\n\t"
: "=&r" (rdv)
: "r" (&table), "r" (wdv)
: "memory"
);
上面例子:从一个表读一个值,将另一个值写到另外一个地方,如果编译器选择同一个register做为输入和输出入,那么输出的值在 "ldr %0, [%1]" "\n\t"这条汇编语句后就被输入值冲掉了。。。。。所以在输出地方加一个"&",
简而言之,“&” 是说明 Register should be used for output only