在avr-gcc内联汇编中使用输出寄存器有哪些限制?

时间:2013-06-03 15:32:12

标签: gcc assembly inline-assembly avr

内联汇编中的输出寄存器必须使用“=”约束声明,这意味着“只写”[1]。这究竟是什么意思 - 它是否真的禁止在程序集中读取和修改它们?例如,请考虑以下代码:

uint8_t one ()
{
    uint8_t res;
    asm("ldi %[res],0\n"
        "inc %[res]\n"
        : [res] "=r" (res)
    );
    return res;
}

程序集将输出寄存器设置为0然后递增它。这打破了“只写”约束吗?

更新

当我将其更改为直接在输出寄存器上工作时,我发现我的内联asm中断的问题,而不是使用r16进行计算,最后将r16移动到输出寄存器中。代码在这里:http://ideone.com/JTpYma。它将结果打印到串行,您只需要定义F_CPU和BAUD。只有在使用gcc-4.8.0而不使用gcc-4.7.2时才会出现问题。

[1] http://www.nongnu.org/avr-libc/user-manual/inline_asm.html

1 个答案:

答案 0 :(得分:2)

编译器不关心你是否读过它,它只是不会将变量的初始值放入寄存器中。您的示例完全合法,但人们经常错误地希望从此代码中获得结果2

uint8_t one ()
{
    uint8_t res = 1;
    asm("inc %[res]\n"
        : [res] "=r" (res)
    );
    return res;
}

由于它只是一个输出约束,因此不能保证将res的初始值加载到寄存器中。实际上,初始化器甚至可以在假设asm块无论如何都会覆盖它的情况下进行优化。上面的代码是由我的avr-gcc版本编译的:

inc r24
ret

正如您所看到的,编译器确实已将1加载到res中,因此导入r24,从而产生未定义的结果。


更新

问题中更新程序的问题是它还有一个输入寄存器操作数。默认情况下,编译器假定在分配输出之前消耗所有输入,因此分配重叠寄存器是安全的。你的例子显然不是这样。您应该使用“ early clobber ”修饰符(&)作为输出。这就是手册对此的评价:

  

&意味着(在特定的替代方案中)此操作数是一个   earlyclobber操作数,在指令之前被修改   使用输入操作数完成。因此,这个操作数可能不会说谎   在寄存器中,用作输入操作数或任何操作数的一部分   记忆地址。

没有人说gcc inline asm很简单:D