有人可以解释我(换句话说)GCC doc中的以下部分:
这是一个虚构的平方和指令,它在存储器中有两个指向浮点值的指针,并产生一个浮点寄存器输出。请注意,x和y都在asm参数中出现两次,一次指定访问的内存,一次指定asm使用的基本寄存器。这样做通常不会浪费寄存器,因为GCC可以将相同的寄存器用于这两个目的。但是,在这个asm中同时使用%1和%3作为x并且期望它们是相同的是愚蠢的。实际上,%3可能不是注册。它可能是x指向的对象的符号内存引用。
asm ("sumsq %0, %1, %2"
: "+f" (result)
: "r" (x), "r" (y), "m" (*x), "m" (*y));
这是一个虚构的* z ++ = * x ++ * * y ++指令。请注意,必须将x,y和z指针寄存器指定为输入/输出,因为asm会修改它们。
asm ("vecmul %0, %1, %2"
: "+r" (z), "+r" (x), "+r" (y), "=m" (*z)
: "m" (*x), "m" (*y));
在第一个示例中,在输入操作数中列出*x
和*y
的要点是什么?同样的文件说明:
特别是,没有办法指定输入操作数被修改而不指定它们作为输出操作数。
在第二个例子中为什么要使用输入操作数部分?无论如何,它的操作数都没有在汇编语句中使用。
作为奖励,如何从this SO帖子更改以下示例,以便不需要volatile
关键字?
void swap_2 (int *a, int *b)
{
int tmp0, tmp1;
__asm__ volatile (
"movl (%0), %k2\n\t" /* %2 (tmp0) = (*a) */
"movl (%1), %k3\n\t" /* %3 (tmp1) = (*b) */
"cmpl %k3, %k2\n\t"
"jle %=f\n\t" /* if (%2 <= %3) (at&t!) */
"movl %k3, (%0)\n\t"
"movl %k2, (%1)\n\t"
"%=:\n\t"
: "+r" (a), "+r" (b), "=r" (tmp0), "=r" (tmp1) :
: "memory" /* "cc" */ );
}
提前致谢。我现在已经挣扎了两天了。
答案 0 :(得分:3)
在第一个示例中,*x
和*y
必须列为输入操作数,以便GCC知道指令的结果取决于它们。否则,GCC可以通过内联汇编片段将商店移动到*x
和*y
,然后可以访问未初始化的内存。通过编译此示例可以看出这一点:
double
f (void)
{
double result;
double a = 5;
double b = 7;
double *x = &a;
double *y = &b;
asm ("sumsq %0, %1, %2"
: "+X" (result)
: "r" (x), "r" (y) /*, "m" (*x), "m" (*y)*/);
return result;
}
结果是:
f:
leaq -16(%rsp), %rax
leaq -8(%rsp), %rdx
pxor %xmm0, %xmm0
#APP
# 8 "t.c" 1
sumsq %xmm0, %rax, %rdx
# 0 "" 2
#NO_APP
ret
两条leaq
指令只是将寄存器设置为指向堆栈中未初始化的红色区域。作业不见了。
第二个例子也是如此。
我认为您可以使用相同的技巧来消除volatile
。但我认为这里实际上没有必要,因为已经有一个"memory"
clobber,它告诉GCC内存是从内联汇编中读取或写入的。