zzz*_*hhh 11 x86 assembly gcc register-allocation inline-assembly
以下摘自GCC 手册的 Extended Asm docs,关于使用asm关键字在 C 中嵌入汇编指令:
如果一个输出参数 ( a ) 允许寄存器约束而另一个输出参数 ( b ) 允许内存约束,则会出现同样的问题。GCC 生成的用于访问b 中的内存地址的代码 可以包含可能由a共享的寄存器,并且 GCC 将这些寄存器视为 asm 的输入。如上所述,GCC 假设在写入任何输出之前消耗此类输入寄存器。如果 asm 语句在使用 b 之前写入 a,则此假设可能会导致不正确的行为。与对寄存器约束结合“&”改性剂一确保修改一个不影响通过引用的地址b. 否则,位置b,如果未定义 一个使用之前改性b。
斜体句子表示如果 asm 语句a在使用b.
我无法弄清楚这种“不正确的行为”是如何发生的,所以我希望有一个具体的 asm 代码示例来演示“不正确的行为”,以便我可以深入理解这一段。
当两个这样的 asm 代码并行运行时,我可以察觉到问题,但上面的段落没有提到多处理场景。
如果我们只有一个单核的CPU,能否请您出示一个asm代码,可能会产生这样的错误行为,即修改a影响引用的地址,b使得其位置b未定义。
我唯一熟悉的汇编语言是 Intel x86 汇编,因此请让示例针对该平台。
Jes*_*ter 10
考虑以下示例:
extern int* foo();
int bar()
{
int r;
__asm__(
"mov $0, %0 \n\t"
"add %1, %0"
: "=r" (r) : "m" (*foo()));
return r;
}
Run Code Online (Sandbox Code Playgroud)
通常的调用约定将返回值放入eax寄存器。因此,编译器很有可能决定使用eax自始至终,以避免不必要的复制。生成的程序集可能如下所示:
extern int* foo();
int bar()
{
int r;
__asm__(
"mov $0, %0 \n\t"
"add %1, %0"
: "=r" (r) : "m" (*foo()));
return r;
}
Run Code Online (Sandbox Code Playgroud)
请注意,下一条指令之前的mov $0, %eax零eax尝试使用它来引用输入参数,因此此代码将崩溃。使用早期的 clobber,您会强制编译器选择不同的寄存器。就我而言,结果代码是:
subl $12, %esp
call foo
mov $0, %eax
add (%eax), %eax
addl $12, %esp
ret
Run Code Online (Sandbox Code Playgroud)
编译器可以改为将结果移动foo()到edx(或任何其他空闲寄存器)中,如下所示:
subl $12, %esp
call foo
mov $0, %edx
add (%eax), %edx
addl $12, %esp
movl %edx, %eax
ret
Run Code Online (Sandbox Code Playgroud)
这个例子对输入参数使用了内存约束,但这个概念同样适用于输出。
鉴于下面的代码,Apple Clang 11-O3使用(%rax)了a和%eaxfor b。
void foo(int *a)
{
__asm__(
"nop # a is %[a].\n"
"nop # b is %[b].\n"
"nop # c is %[c].\n"
"nop # d is %[d].\n"
"nop # e is %[e].\n"
"nop # f is %[f].\n"
"nop # g is %[g].\n"
"nop # h is %[h].\n"
"nop # i is %[i].\n"
"nop # j is %[j].\n"
"nop # k is %[k].\n"
"nop # l is %[l].\n"
"nop # m is %[m].\n"
"nop # n is %[n].\n"
"nop # o is %[o].\n"
:
[a] "=m" (a[ 0]),
[b] "=r" (a[ 1]),
[c] "=r" (a[ 2]),
[d] "=r" (a[ 3]),
[e] "=r" (a[ 4]),
[f] "=r" (a[ 5]),
[g] "=r" (a[ 6]),
[h] "=r" (a[ 7]),
[i] "=r" (a[ 8]),
[j] "=r" (a[ 9]),
[k] "=r" (a[10]),
[l] "=r" (a[11]),
[m] "=r" (a[12]),
[n] "=r" (a[13]),
[o] "=r" (a[14])
);
}
Run Code Online (Sandbox Code Playgroud)
因此,如果将nop指令和注释替换为%[b]之前写入 的实际指令%[a],它们将破坏 所需的地址%[a]。
| 归档时间: |
|
| 查看次数: |
256 次 |
| 最近记录: |