但如果使用后一种写法,"Instruction List"中的别名也要相应的改动。关于别名,我们后面会讨论。
像 等号(=)和加号(+)修饰符一样,符号(&)也只能用于对Output操作表达式的修饰。当使用它进行修饰时,等于向GCC声明:"GCC不得 为任何Input操作表达式分配与此Output操作表达式相同的寄存器"。其原因是&修饰符意味着被其修饰的Output操作表达式要在所有的 Input操作表达式被输入前输出。我们看下面这个例子:
int main(int __argc, char* __argv[])
{
int __in1 = 8, __in2 = 4, __out = 3;
__asm__ ("popl %0 nt"
"movl %1, %%esi nt"
"movl %2, %%edi nt"
: "=a"(__out)
: "r" (__in1), "r" (__in2));
return 0;
}
此 例中,%0对应的就是Output操作表达式,它被指定的寄存器是%eax,整个Instruction List的第一条指令popl %0,编译后就成为popl %eax,这时%eax的内容已经被修改,随后在Instruction List后,GCC会通过movl %eax, address_of_out这条指令将%eax的内容放置到Output变量__out中。对于本例中的两个Input操作表达式而言,它们的寄存器约 束为"r",即要求GCC为其指定合适的寄存器,然后在Instruction List之前将__in1和__in2的内容放入被选出的寄存器中,如果它们中的一个选择了已经被__out指定的寄存器%eax,假如是__in1,那 么GCC在Instruction List之前会插入指令movl address_of_in1, %eax,那么随后popl %eax指令就修改了%eax的值,此时%eax中存放的已经不是Input变量__in1的值了,那么随后的movl %1, %%esi指令,将不会按照我们的本意——即将__in1的值放入%esi中——而是将__out的值放入%esi中了。
下面就是本例的编译结果,很明显,GCC为__in2选择了和__out相同的寄存器%eax,这与我们的初衷不符。
main:
pushl %ebp
movl %esp, %ebp
subl $12, %esp
movl $8, -4(%ebp)
movl $4, -8(%ebp)
movl $3, -12(%ebp)
movl -4(%ebp), %edx # __in1使用寄存器%edx
movl -8(%ebp), %eax # __in2使用寄存器%eax
#APP
popl %eax
movl %edx, %esi
movl %eax, %edi
#NO_APP
movl %eax, %eax
movl %eax, -12(%ebp) # __out使用寄存器%eax
movl $0, %eax
leave
ret
为 了避免这种情况,我们必须向GCC声明这一点,要求GCC为所有的Input操作表达式指定别的寄存器,方法就是在Output操作表达式"=a" (__out)的操作约束中加入&约束,由于GCC规定等号(=)约束必须放在第一个,所以我们写作"=&a"(__out)。
下面是我们将&约束加入之后编译的结果:
main:
pushl %ebp
movl %esp, %ebp
subl $12, %esp
movl $8, -4(%ebp)
movl $4, -8(%ebp)
movl $3, -12(%ebp)
movl -4(%ebp), %edx #__in1使用寄存器%edx
movl -8(%ebp), %eax
movl %eax, %ecx # __in2使用寄存器%ecx
#APP
popl %eax
movl %edx, %esi
movl %ecx, %edi
#NO_APP
movl %eax, %eax
movl %eax, -12(%ebp) #__out使用寄存器%eax
movl $0, %eax
leave
ret










