【问题标题】:(+r) vs. (=r) constraints in GCC x86 Inline AssemblyGCC x86 内联汇编中的 (+r) 与 (=r) 约束
【发布时间】:2014-11-03 19:53:44
【问题描述】:

我在 c 中使用内联汇编块对数组进行排序的一段代码存在问题。

我的完整代码是这样的:

#include <stdio.h>
#define n 20

int main()
{
    int array[n];
    int i;
    int swapped;

    printf("Enter the elements one by one \n");
    for (i = 0; i < n; i++)
    {
        scanf("%d", &array[i]);
    }

    printf("Input array elements \n");
    for (i = 0; i < n ; i++)
    {
        printf("%d\n", array[i]);
    }

    /*  Bubble sorting begins */   
    do
    {
          swapped = 0;
          for (i = 1; i < n; i++)
          {
/* 
           if (array[i] < array [i-1])
           {
               swapped =1;
               int temp = array [i-1];
               array [i-1] = array [i];
               array[i] = temp;
           }
*/

               //body of the for loop
               //converted to assembly
               __asm__ __volatile__("cmp %0, %1;"
                                    "jge DONE;"
                                    "mov eax, %0;"
                                    "mov %0, %1;"
                                    "mov %1, eax;"
                                    "mov %2, 1;"
                                    "DONE: "
                                    : "+r" (array[i]), "+r" (array[i-1]), "=r" (swapped)
                                    : //no input
                                    : "eax", "cc"
                                    );


          }

    } while (swapped > 0);

    printf("Sorted array is...\n");
    for (i = 0; i < n; i++)
    {
        printf("%d\n", array[i]);
    }

    return 0;
}

由于某种原因,do while 变成了一个无限循环,但是当我将 swapped 变量的修饰符更改为 "+r" (swapped) 时,它可以工作。我查看了两种情况下生成的汇编代码(-save-temp),除了在使用“+r”的情况下将swapped 变量移动到寄存器之外没有注意到任何其他内容,这是预期的。

为什么我需要使用"+r"

【问题讨论】:

  • 我会害怕写这样的跳跃。想象一下编译器展开循环,它会跳转到哪里?另外,为什么要为此编写内联汇编?编译器应该从 C 代码中生成更好的代码。
  • 你用-masm=intel编译吗?我看不到你的命令行。
  • 好吧,我在这里的唯一目的是学习 GCC 内联汇编。此外,我使用了 volatile 并使用 -O0 编译了代码,只是为了防止这些优化。
  • 如果您担心扩展,可以使用 %=,例如:jge DONE%=;DONE%=:。这将扩展为一个唯一编号(请参阅gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#AssemblerTemplate 下的“其他格式字符串”)。此外,如果您使用符号名称,您可能会发现代码更易于阅读(即将"=r" (swapped) 更改为[swapped] "=r" (swapped),然后使用%[swapped] 而不是%2)。最后,如果您使用 gcc -S 输出 asm,您将看到所有命令一起运行。考虑使用\n\t 而不是;。 FWIW。

标签: c gcc assembly x86 inline-assembly


【解决方案1】:

如果你使用=,这意味着它是一个输出并且它必须被写入。但是,您只在有交换的情况下才编写它。编译器将优化掉您使用的swapped = 0,因为它假定汇编程序块将产生一个将覆盖它的新值。这意味着,如果没有交换,编译器将愉快地使用它为%2 选择的寄存器中的任何垃圾作为swapped 的新值,并且偶然会产生一个无限循环。

这里有一些代码来说明:

swapped = 0;
/* this is the asm block */
{
    /* this is not initialized, because it's declared as output */
    int register_for_operand2;
    if (array[i] < array[i - 1])
    {
        /* body here */
        register_for_operand2 = 1;
    }
    /* the compiler generates this code for the output operand */
    /* this will copy uninitialized value if the above condition was false */
    swapped = register_for_operand2;
}

【讨论】:

  • 正确。我刚刚想通了,正要发布答案。谢谢!
  • 在尝试调试时,我发现了一个奇怪的行为。如果我放一个 printf("\n"); ,代码仍然可以工作 "=r" (交换);在评估 while 条件之前。只是想知道为什么?
  • 正如小丑所说,它将使用寄存器中的“垃圾”。显然 printf 是(只是偶然)留下你想要的价值。显然不是你想要依赖的行为......
猜你喜欢
  • 2011-04-23
  • 2015-12-23
  • 2012-11-12
  • 1970-01-01
  • 2015-05-19
  • 1970-01-01
  • 2012-10-20
  • 2011-04-05
  • 1970-01-01
相关资源
最近更新 更多