如何使用GCC在C中编写以下内联汇编代码

时间:2013-07-29 10:25:11

标签: c gcc assembly x86 inline-assembly

我正在阅读这里的一些答案和问题,并不断提出这个建议,但我注意到没有人真正解释过你需要做什么来做这件事,在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 个答案:

答案 0 :(得分:6)

让编译器选择寄存器,使用k前缀表示int类型的32位寄存器(因此它在x86-64上按预期工作):

__asm__ ("pushl %k0\n\t"
         "movl %k1, %k0\n\t"
         "popl %k1"
         : "+r" (x), "+r" (y));

在这种情况下,指定任何被破坏的操作数都没有必要(实际上是错误的)。


显而易见xy在这里是可交换的,即交换操作数仍应使用:: "+%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

以下代码使用xy说明将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寄存器。出于教育目的,它没关系,但我发现将微优化留给编译器更合适。

相关问题