我什么时候可以安全地使用" = r"注册操作数约束?

时间:2012-05-29 20:46:10

标签: gcc inline-assembly

请考虑以下代码:

#include <stdio.h>

void main() {
    uint32_t num = 2;

    __asm__ __volatile__ ("CPUID");
    __asm__ __volatile__ ("movl $1, %%ecx":);
    __asm__ __volatile__ ("andl $0, %%ecx": "=r"(num));

    printf("%i\n", num);
}

我最初的期望是这段代码会打印0,而且如果我注释掉CPUID行,它就会这样做,但是因为它给了我垃圾。经过一些试验,错误和研究,我意识到我得到了随机寄存器的价值。显然GCC并不认为我想要执行语句的结果。

问题在于,我看到(其他人的)代码依赖于该语句正确获取AND的结果,而不管其他寄存器发生了什么。显然,根据我的观察,此类代码已被破坏,"=r"应替换为"=c"

我的问题是,我们能否依赖"=r"约束表现一致或根据明显的期望?或者GCC的实现太不透明/怪异/其他并且最好只是在每种情况下都避免它?

1 个答案:

答案 0 :(得分:2)

为了使用=r输出说明符,你需要让gcc自由选择它想要使用的寄存器。您可以通过为输出指定输入和输出一般为%0,为第一个输入指定以%1开头的输入。

在您的情况下,您说num可以在注册表中。但asm指令中没有任何内容使用输出寄存器。所以gcc基本上会忽略这一点。

如果您对CPUID说明发表评论或未对CPUID说明发表评论,那么您获得不同价值的原因是eax可以写入ebxecx,{ {1}}和edx。我在我的系统上尝试了你的例子,并在两种情况下得到了0。但我注意到生成的程序集正在打印eax的值。所以我想我在运行此计划时CPUID正在将0写入eax

如果您确实想使用=r约束,则需要执行以下操作:

  asm("CPUID \n\t"
      "movl $1, %0 \n\t"
      "andl $0, %0 \n\t"
      :"=r"(num) );

否则,如果您的asm代码专门提到了一个寄存器,那么您需要在约束列表中指定它。在您的示例中,这意味着使用=c