1

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

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

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

更新

当我将内联汇编更改为直接在输出寄存器上工作时,我看到我的内联汇编中断的问题,而不是使用 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

4

1 回答 1

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

如您所见,编译器确实删除了加载1res因此r24产生了未定义的结果。


更新

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

&表示(在特定的替代方案中)此操作数是 earlyclobber 操作数,在使用输入操作数完成指令之前对其进行修改。因此,此操作数可能不在用作输入操作数或用作任何内存地址的一部分的寄存器中。

没有人说 gcc 内联汇编很容易 :D

于 2013-06-03T20:15:32.937 回答