我正在阅读这里的一些答案和问题,并不断提出这个建议,但我注意到没有人真正解释过你需要做什么来做这件事,在Windows上使用英特尔和GCC编译器。以下评论正是我想要做的。
#include <stdio.h>
int main()
{
int x = 1;
int y = 2;
//assembly code begin
/*
push x into stack; < Need Help
x=y; < With This
pop stack into y; < Please
*/
//assembly code end
printf("x=%d,y=%d",x,y);
getchar();
return 0;
}
答案 0 :(得分:6)
让编译器选择寄存器,使用k
前缀表示int
类型的32位寄存器(因此它在x86-64上按预期工作):
__asm__ ("pushl %k0\n\t"
"movl %k1, %k0\n\t"
"popl %k1"
: "+r" (x), "+r" (y));
在这种情况下,指定任何被破坏的操作数都没有必要(实际上是错误的)。
显而易见x
,y
在这里是可交换的,即交换操作数仍应使用:: "+%r" (x), "+r" (y)
产生相同的结果,其中%
表示此操作数并且下一个操作数可以通勤。
答案 1 :(得分:2)
如果它可以移植到具有红色区域的系统,则无法从内联asm 安全地推送/弹出。这包括每个非Windows x86-64平台。 (There's no way to tell gcc you want to clobber it)。好吧,你可以add rsp, -128
首先跳过红区,然后再推送/弹出任何东西,然后再恢复。但是,您不能使用"m"
约束,因为编译器可能使用RSP相对寻址和偏移量,假设RSP没有被修改。
但实际上这对于内联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;
}
从the Godbolt compiler explorer 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-&gt;寄存器映射,它不需要花费任何成本。
当手写asm时,使用注释来跟踪不同寄存器或向量寄存器的部分当前逻辑含义。
inline-asm也没有导致inline-asm块之外的任何额外指令,因此在这种情况下它非常有效。尽管如此,编译器仍然无法看透它,并且不知道值仍然是1和2,因此进一步的常量传播将被打败。 https://gcc.gnu.org/wiki/DontUseInlineAsm
答案 2 :(得分:1)
您可以使用扩展内联汇编。它是一个编译器功能,允许您在C代码中编写汇编指令。内联gcc程序集的良好参考可用here。
以下代码使用x
和y
说明将pop
的值复制到push
。
(在x86_64上使用gcc编译和测试)
只有在使用-mno-red-zone
进行编译时才会安全,或者在推送任何内容之前从RSP中减去128。它会碰巧在某些函数中没有问题:用一组周围代码进行测试不足以验证你用GNU C内联asm做的事情的正确性。
#include <stdio.h>
int main()
{
int x = 1;
int y = 2;
asm volatile (
"pushq %%rax\n" /* Push x into the stack */
"movq %%rbx, %%rax\n" /* Copy y into x */
"popq %%rbx\n" /* Pop x into y */
: "=b"(y), "=a"(x) /* OUTPUT values */
: "a"(x), "b"(y) /* INPUT values */
: /*No need for the clobber list, since the compiler knows
which registers have been modified */
);
printf("x=%d,y=%d",x,y);
getchar();
return 0;
}
结果x=2 y=1
,正如您所料。
intel编译器以类似的方式工作,我认为您只需要将关键字asm
更改为__asm__
。您可以找到有关INTEL编译器here的内联汇编的信息。
答案 3 :(得分:1)
#include <stdio.h>
int main()
{
int x=1;
int y=2;
printf("x::%d,y::%d\n",x,y);
__asm__( "movl %1, %%eax;"
"movl %%eax, %0;"
:"=r"(y)
:"r"(x)
:"%eax"
);
printf("x::%d,y::%d\n",x,y);
return 0;
}
/* Load x to eax
Load eax to y */
如果要交换值,也可以使用这种方式完成。请注意,这指示GCC处理被破坏的EAX寄存器。出于教育目的,它没关系,但我发现将微优化留给编译器更合适。