GCC内联汇编副作用

时间:2017-10-25 22:20:14

标签: gcc assembly x86 inline-assembly

有人可以解释我(换句话说)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" */ );
}

提前致谢。我现在已经挣扎了两天了。

1 个答案:

答案 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内存是从内联汇编中读取或写入的。