扩展 GCC asm 中多个输入和输出操作数的正确用法是什么?

Att*_*eld 4 assembly gcc

在寄存器约束下,扩展 GCC asm 中的多个输入和输出操作数的正确使用是什么?考虑一下我的问题的最小版本。下面简要介绍 GCC、AT&T 语法的扩展 asm 代码:

    int input0 = 10;
    int input1 = 15;
    int output0 = 0;
    int output1 = 1;

    asm volatile("mov %[input0], %[output0]\t\n"
                 "mov %[input1], %[output1]\t\n"
                 : [output0] "=r" (output0), [output1] "=r" (output1)
                 : [input0] "r" (input0), [input1] "r" (input1)
                 :);

    printf("output0: %d\n", output0);
    printf("output1: %d\n", output1);
Run Code Online (Sandbox Code Playgroud)

基于https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html,语法看起来是正确的,但是,我一定忽略了某些东西,或者犯了一些由于某种原因我看不到的小错误。

GCC 5.3.0 p1.0(无编译器参数)的输出是:

输出0:10
输出1:10

预期输出是:

输出 0: 10
输出 1: 15

在 GDB 中查看显示:

0x0000000000400581 <+43>:mov eax,DWORD PTR [rbp-0x10]
0x0000000000400584 <+46>:mov edx,DWORD PTR [rbp-0xc] 0x0000000000400587 <
+49>:mov edx,eax
0x00 00000000400589 <+51>: mov eax ,edx
0x000000000040058b <+53>: mov DWORD PTR [rbp-0x8],edx
0x000000000040058e <+56>: mov DWORD PTR [rbp-0x4],eax

据我所见,它使用 input0 加载 eax,使用 input1 加载 edx。然后它用 eax 覆盖 edx,用 edx 覆盖 eax,使它们相等。然后将它们写回output0 和output1。

如果我对输出使用内存约束 (=m) 而不是寄存器约束 (=r),它会给出预期的输出,并且程序集看起来更合理。

Ros*_*dge 8

问题是 GCC 假定所有输出操作数仅在指令末尾、所有输入操作数都已消耗之后写入。这意味着它可以使用相同的操作数(例如寄存器)作为输入操作数和输出操作数,这就是这里发生的情况。解决方案是[output0]使用早期的破坏约束进行标记,以便 GCC 知道它是在 asm 语句结束之前写入的。

例如:

 asm volatile("mov %[input0], %[output0]\t\n"
              "mov %[input1], %[output1]\t\n"
              : [output0] "=&r" (output0), [output1] "=r" (output1)
              : [input0] "r" (input0), [input1] "r" (input1)
              :);
Run Code Online (Sandbox Code Playgroud)

您不需要将其标记[output1]为早期破坏,因为它仅在指令末尾写入,因此它是否使用与[input0]or相同的寄存器并不重要[input1]

  • 除了罗斯的回答之外,还有我最喜欢的派对技巧:`asm ("" : "=r" (output0), "=r" (output1) : "0" (input0), "1" (input1)); `。没错,不需要汇编即可将输入“移动”到输出。好吧,我没有被邀请参加很多聚会...... (3认同)