linux的嵌入汇编的一个问题(关于earlyclobber)

  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

你可能感兴趣的:(linux,汇编,gcc,assembly,编译器,output)