【问题标题】:What are the limitations on the use of output registers in avr-gcc inline assembly?avr-gcc 内联汇编中使用输出寄存器有什么限制?
【发布时间】:2013-06-03 15:32:11
【问题描述】:

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

【问题讨论】:

    标签: gcc assembly inline-assembly avr


    【解决方案1】:

    编译器不在乎你是否读取它,它只是不会将变量的初始值放入寄存器中。您的示例完全合法,但人们经常错误地期望从此代码中得到结果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 内联汇编很容易 :D

    【讨论】:

    • 您可能在这里错了,请参阅我的编辑。或者它只是一个编译器错误。
    猜你喜欢
    • 1970-01-01
    • 2017-12-23
    • 2021-03-21
    • 1970-01-01
    • 2023-03-22
    • 2016-09-12
    • 1970-01-01
    • 1970-01-01
    • 2014-09-19
    相关资源
    最近更新 更多