【发布时间】:2017-10-25 22:20:13
【问题描述】:
有人可以向我解释(换句话说)来自 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" */ );
}
提前致谢。我现在为此苦苦挣扎了两天。
【问题讨论】:
-
我的猜测是
"m" (*x), "m" (*y)被添加为输入操作数,以确保在调用扩展程序集模板之前将 x 和 y 的值实现到内存中。如果您不使用它们,并且通过寄存器传递地址,则无法保证代码生成器实际上将数据写入 x 和 y 的内存(由 2 个寄存器指向两个)。"m" (*x), "m" (*y)输入约束确保在执行内联汇编之前 x 和 y 的值在内存中。这种情况可能会在某些代码结构和优化处于启用状态时发生。 -
上面的 Mu 注释也适用于第二个示例 (vecmul)。
-
如果允许虚构指令将内存操作数作为参数,这将不是问题。
-
交换代码效率非常低,但从技术上讲,它甚至不需要 volatile 修饰符。事实上,它可能会产生效率较低的代码,并且在优化时存在 volatile 并且将 swap_2 内联到其他函数中。不需要 volatile,因为程序集模板的所有副作用都通过输入、输出和 clobber 操作数来考虑。
-
在旁注中,实际上可以交换两个输入的数据而无需在程序集模板内使用任何代码,并严格使用约束来完成工作。这在这个答案中得到了证明: stackoverflow.com/a/39499595/3857942 。那是内联汇编的更高级使用。
标签: gcc assembly x86 inline-assembly