学习永无止境!!
分类: C/C++
2009-11-11 14:40:35
|
编译后的汇编代码
movl eax_in,%eax
movl ebx_in,%ebx
movl ecx_in,%ecx
#APP GCC插入的注释,表示内嵌汇编开始
pushl %edi 内嵌的汇编语句
pushl %ebp
lcall %cs:
setc %al
addl eb,ec
popl %ebp
popl %edi
#NO_APP GCC 插入的注释,表示内嵌汇编结束
movl %eax,ea
movl %ebx,eb
movl %ecx,ec
movl %edx,ed
movl %esi,es
int main()
{
int bar=1;
bar=fun();
bar++;
return bar;
}
编译后的汇编代码
pushl %ebp
movl %esp,%ebp
call fun
incl %eax #显然,bar缺省使用eax寄存器
leave
ret
加了汇编后:
源程序
int main()
{
int bar=1;
bar=fun();
asm volatile("" : : : "eax");
bar++;
return bar;
}
编译后的汇编代码
pushl %ebp
movl %esp,%ebp
call fun
movl %eax,%edx #bar改为使用edx寄存器
incl %edx
movl %edx,%eax #放入main()的返回值
ret
"merory"是一个常用的限定,它表示汇编代码以不可预知的方式改变了内存,这样gcc在优化时就不会让cache到寄存器的内存变量使用该寄存器通过汇编代码,否则可能会发生同步出错.有了上面的例子,这个问题就很好理解了
三、对"&"限定符的解释
这是一个较常见用于输出的限定符,它告诉gcc输出操作数使用的寄存器不可再让输入操作数使用。
对于"g","r"等限定符,为了有效利用为数不多的几个通用寄存器,gcc一般会让输入操作数和输出操作数选用同一个寄存器。但如果代码没编好,会引起一些意想不到的错误,例如:
asm("call fun;mov ebx,%1":"=a"(foo):"r"(bar));
gcc编译的结果是foo和bar同时使用eax寄存器:
movl bar,eax
#APP
call fun
movl ebx,eax
#NO_APP
movl eax,foo
本来这段代码的意图是将fun()函数的返回值放入foo变量,但半路杀出个程咬金,用ebx的值冲掉了返回值,所以这是一段错误的代码,解决的方法是加上一个给输出操作数加上一个"&"限定符:
asm("call fun;mov ebx,%1":"=&a"(foo):"r"(bar));
这样gcc就会让输入操作数另寻高就,不再使用eax寄存器了
补充说明:
其实&对读代码根本没有意义,只有GCC或写汇编的才关心。
如果你非要知道&的作用,这里解释一下。
1. GCC处理嵌入汇编时,如果两个输入操作数值相同,可能会分配到同一个寄存器,以减少寄存器的使用。
2. GCC把嵌入汇编看成一个整体,它并不知道是一条指令还是多条指令,通常它认为汇编指令输出结果时,输入操作数还没有改变,单条指令时,这种假设大多成立,但多条指令时可能不成立。比如定义如下:
#define add1(a,b) asm("incl %0\n\taddl %2,%0":"=r"(res):"0"(a),"r"(b))
计算a+b+1。我们看看add1(a,a)生成的指令,假设%eax中包含a的值:
#%0=eax,%2=eax
#APP
incl %eax
addl %eax, %eax
#NO_APP
结果不对,读或写汇编的人很容易看出这点,但是GCC不知道,加个&,定义写成:
#define add1(a, b) asm("addl %2,%0":"=&r"(res):"0"(a),"r"(b))
告诉GCC参作数%0是earlyclobber,不要和%2分配到同一个寄存器,这样add1(a,a)生成指令:
movl %eax, %edx
#%0=edx, %2=eax
#APP
incl %edx
addl %eax, %edx # edx = output
#NO_APP
四、对%quot"限定符的解释
%:说明指令中可与下一操作数交换的那个操作数,这意味着编译可以交换这两个操作数以使得能以代价更小的方法来满足操作数约束,这常常用于真正只有两个操作数的加法指令的指令样板中,这种加法指令的结果必须存放在两个操作数之一中。