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

4

如何在扩展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);

根据https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html,语法似乎是正确的。然而,我可能忽略了某些东西或犯了一些微不足道的错误,但出于某种原因我看不到。

GCC 5.3.0 p1.0(没有编译器参数)的输出为:

output0: 10
output1: 10

期望的输出是:

output0: 10
output1: 15

在GDB中查看它的结果是:

这段代码将eax加载为input0,edx加载为input1。然后用eax覆盖edx,用edx覆盖eax,使它们相等。最后将它们写回到output0和output1中。如果使用内存约束(=m)而不是寄存器约束(=r)来输出,则会得到预期的输出,并且汇编代码看起来更合理。

1
你可能需要查看早期占位符和修饰符。特别是我有一种感觉,你需要使用=&r来处理输出操作数,因为该寄存器在汇编模板的最后一条指令之前被修改。_GCC_会认为它可以将该寄存器作为输入再次使用。&将防止早期占位符分配的寄存器被用作输入寄存器。 - Michael Petch
您可能还想考虑在输入操作数上使用g约束,而不是r。由于输出仅定义为寄存器,并且模板中的mov指令可以接受至少一个内存或立即值操作数,因此使用g可以让编译器有机会执行其他优化。g约束的文档说明为_允许任何寄存器、内存或立即整数操作数,但不包括非通用寄存器_。 - Michael Petch
特别是如果您使用 g 作为输入操作数约束,编译器应该能够意识到某些输入实际上是常量(立即)值,这应该可以实现一些代码简化。如果您使用 GCC 编译并使用 -O3 优化级别,可以更好地看到这些优化。 - Michael Petch
2
@MichaelPetch 如果您想完全枚举允许的操作数并为编译器提供最大的灵活性,您将使用"=r,r,rm,rm", "=r,rm,r,rm" : "g,g,ri,ri", "g,ri,g,ri" - Ross Ridge
1个回答

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

您不需要将[output1]标记为早期clobber,因为它只在指令末尾被写入,所以如果它使用与[input0][input1]相同的寄存器,这并不重要。


3
除了Ross的回答之外,还有我的最爱派对技巧:asm ("" : "=r" (output0), "=r" (output1) : "0" (input0), "1" (input1));。没错,不需要汇编语言即可将输入“移动”到输出。好吧,我很少被邀请参加聚会... - David Wohlferd
@AttributedTensorField 和 Ross:这个变量不需要被声明为 volatile。它没有副作用,只是一个纯粹的“函数”,产生的输出值仅依赖于输入。 - Peter Cordes
@PeterCordes 它也不需要是一个汇编语句。 - Ross Ridge
@RossRidge:我认为OP打算在这个实验作为构建块/跳板成功后,用真正的东西替换mov指令。我喜欢@David Wohlferd的评论,因为SO问题中的代码经常犯将mov指令硬编码到内联汇编中的错误,而不是让编译器为他们完成。 - Peter Cordes
@PeterCordes 我也是这么想的。这就是为什么我认为没有必要建议改进,无论是删除volatile关键字,完全枚举约束条件,消除MOV指令,还是最好的改进方法,完全用普通的C语言重写它。 - Ross Ridge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接