如果要移植到带有红色区域的系统,您不能只从内联 asm 安全地推送/弹出。这包括所有非 Windows x86-64 平台。(没有办法告诉 gcc 你想破坏它)。好吧,您可以 add rsp, -128
先跳过红色区域,然后再推/弹出任何东西,然后再恢复它。但是您不能使用"m"
约束,因为编译器可能会使用带有偏移量的 RSP 相对寻址,假设 RSP 没有被修改。
但实际上这是在 inline asm 中做的一件荒谬的事情。
以下是使用 inline-asm 交换两个 C 变量的方法:
#include <stdio.h>
int main()
{
int x = 1;
int y = 2;
asm("" // no actual instructions.
: "=r"(y), "=r"(x) // request both outputs in the compiler's choice of register
: "0"(x), "1"(y) // matching constraints: request each input in the same register as the other output
);
// apparently "=m" doesn't compile: you can't use a matching constraint on a memory operand
printf("x=%d,y=%d\n",x,y);
// getchar(); // Set up your terminal not to close after the program exits if you want similar behaviour: don't embed it into your programs
return 0;
}
来自Godbolt 编译器资源管理器的 gcc -O3 输出(针对 x86-64 System V ABI,而不是 Windows):
.section .rodata
.LC0:
.string "x=%d,y=%d"
.section .text
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
xor eax, eax
mov edx, 1
mov esi, 2
#APP
# 8 "/tmp/gcc-explorer-compiler116814-16347-5i3lz1/example.cpp" 1
# I used "\n" instead of just "" so we could see exactly where our inline-asm code ended up.
# 0 "" 2
#NO_APP
call printf
xor eax, eax
add rsp, 8
ret
C变量是一个高级概念;决定相同的寄存器现在在逻辑上保存不同的命名变量不需要任何成本,而不是在不更改 varname->register 映射的情况下交换寄存器内容。
手写 asm 时,使用注释来跟踪不同寄存器或向量寄存器部分的当前逻辑含义。
inline-asm 也不会导致 inline-asm 块之外的任何额外指令,因此在这种情况下它非常有效。尽管如此,编译器还是看不透它,也不知道值仍然是 1 和 2,因此进一步的常量传播将被击败。 https://gcc.gnu.org/wiki/DontUseInlineAsm