内联汇编中的输出寄存器必须使用“=”约束声明,这意味着“只写”[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
答案 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