免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2075 | 回复: 7
打印 上一主题 下一主题

linux的嵌入汇编的一个问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-06-09 10:41 |只看该作者 |倒序浏览
5可用积分
"=&a"(retval)
这是输出部,把寄存器eax的值放如retval中
我想请问的是 &a的前面的&符号是做什么用的呢?

最佳答案

查看完整内容

笔记:=========2.8.1 输出操作数是一个earlyclobber操作数 在inline asm中,特别需要注意earlyclobber的情况。 earlyclobber的意思是说,某个输出操作数,可能 在gcc尚未用完所有的输入操作数之前,就已经被写入(inline asm的输出操作)值了。 换言之,gcc尚未使用完 全部的输入操作数的时候,就已经对某些输出操作数产生了输出操作。 earlyclobber是整个gcc inline assembly语法中最晦涩难懂的地方,从 ...

论坛徽章:
0
2 [报告]
发表于 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的错误行为。

论坛徽章:
0
3 [报告]
发表于 2009-06-09 13:29 |只看该作者
Register should be used for output only

论坛徽章:
0
4 [报告]
发表于 2009-06-11 14:02 |只看该作者
原帖由 albcamus 于 2009-6-9 14:22 发表
笔记:
=========

2.8.1 输出操作数是一个earlyclobber操作数
      
      在inline asm中,特别需要注意earlyclobber的情况。 earlyclobber的意思是说,某个输出操作数,可能
      在gcc尚未用完所有 ...

好啊,版主把笔记共享出来吧

论坛徽章:
0
5 [报告]
发表于 2009-06-11 14:09 |只看该作者
原帖由 源方 于 2009-6-11 14:02 发表

好啊,版主把笔记共享出来吧


我本来想就inline assembly写一个文章投到ibm developerworks的, 都注册了。
结果技术上材料都写完了,却不会组织成一个可读的文章。一直拖到现在。

要是决定撤稿,就发到论坛上。

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
6 [报告]
发表于 2009-06-11 14:21 |只看该作者

回复 #5 albcamus 的帖子

直接发论坛,让大家拜读一下吧

论坛徽章:
0
7 [报告]
发表于 2009-06-11 14:44 |只看该作者
支持讓大家學習一下

论坛徽章:
0
8 [报告]
发表于 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

P.S 以上例子来自于外国人写的一个无标题文档,需要的朋友可以PM我。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP