2

我在这里阅读了一些答案和问题,并不断提出这个建议,但我注意到没有人真正解释过“确切”你需要做什么,在 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;
}
4

3 回答 3

3

如果要移植到带有红色区域的系统,您不能只从内联 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

于 2016-09-14T21:02:35.860 回答
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 寄存器。出于教育目的,没关系,但我发现将微优化留给编译器更合适。

于 2013-07-29T10:57:24.460 回答
1

您可以使用扩展的内联汇编。它是一个编译器功能,允许您在 C 代码中编写汇编指令。这里有一个很好的内联 gcc 汇编参考。

以下代码将 的值复制xy使用poppush指令中。
(在 x86_64 上使用 gcc 编译和测试)

这只有在使用 编译时才是安全的-mno-red-zone,或者在推送任何内容之前从 RSP 中减去 128。它会在某些功能中正常工作:使用一组周围代码进行测试不足以验证您使用 GNU C 内联汇编所做的事情的正确性。

  #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 编译器的内联汇编的信息。

于 2013-07-29T10:52:31.127 回答