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

时间:2016-03-15 04:16:10

标签: gcc assembly

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

    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);

基于https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html,语法看似正确。但是,我必须忽略某些事情,或者由于某种原因无法承认一些微不足道的错误。

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

  

输出0:10
  output1: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
  0x0000000000400589< + 51>:mov eax,edx
  0x000000000040058b< + 53>:mov DWORD PTR [rbp-0x8],edx
  0x000000000040058e< + 56>:mov DWORD PTR [rbp-0x4],eax

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

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

1 个答案:

答案 0 :(得分:3)

问题是GCC假定所有输出操作数都只在指令结束时写入,所有输入操作数都被消耗掉了。这意味着它可以使用相同的操作数(例如寄存器)作为输入操作数和输出操作数,这是这里发生的事情。解决方案是使用early clobber constraint标记[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)
              :);

您不需要将[output1]标记为早期删除,因为它仅在指令末尾写入,因此如果它使用与[input0]或{{[input1]相同的寄存器则无关紧要1}}。