内联汇编-强制变量注册

时间:2018-08-27 20:25:27

标签: gcc assembly clang inline-assembly

我只是想知道我是否要执行以下操作:

{
    register uint32_t v1 asm("r6"), v2 asm("r7");
    register uint32_t v3 asm("r8"), v4 asm("r10");

    asm volatile (
        /* Move data */
        "   ldm     %[src], {%[v1],%[v2],%[v3],%[v4]};"
        "   stm     %[dst], {%[v1],%[v2],%[v3],%[v4]};"
        : /* output constraints */
          "=m"(*(uint64_t (*)[2])dst),
          [v1]"=&r"(v1), [v2]"=&r"(v2),
          [v3]"=&r"(v3), [v4]"=&r"(v4)
        : /* input constraints */
          "m"(*(const uint64_t (*)[2])src),
          [dst]"r"(read_dst),
          [src]"r"((const uint64_t* )src)
        : /* clobber constraints */
    );
}

是否保证v1位于r6,还是编译器可以自由地进行优化以使用另一个寄存器?有没有办法将名称绑定到特定寄存器? (是否没有在任何地方手动指定%r6?)

此外,对于临时变量,使用输出约束与Clobber约束之间是否有区别? (假设在内联汇编调用之后未引用它?)

我正在使用gcc和clang,因此必须同时使用一种解决方案。当然,这是一个简化的示例,用于发布问题。

1 个答案:

答案 0 :(得分:2)

是的,这很安全。 这正是register ... asm("regname");的目的,实际上是register asm local variables唯一受支持的使用。

实际上,即使您以后继续使用该变量,即使花费额外的指令,gcc也会强烈希望使用该寄存器。 (Using a specific zmm register in inline asm)。但是我希望,如果该变量失效,它将把寄存器分配给其他变量。不过,您可能仍想限制这些临时对象的范围,或者将其包装在inline函数中。


asm volatile中,输出操作数与clobber之间的AFAIK没有区别。输出操作数的好处是它允许编译器为您选择一个寄存器,但是如果您手动强制寄存器分配,那么这两种方法都没有好处。

如果禁用优化,则编译器实际上将为本地人保留堆栈空间并将其溢出。 (或者也许不是,因为它们是register变量?)


如果没有volatile,则没有输出约束,这会使您的asm语句隐含地 volatile。因此,如果未使用 all 的输出约束,但是您希望asm语句无论如何运行都具有"memory"的副作用,则需要显式使用volatile

在您的情况下,您只希望在使用内存输出的情况下进行复制。因此,您可能应该省略volatile,以便它可以证明没有东西可以关心结果,从而可以将副本优化为临时文件。复制到全局指针或未知指针的优化不能超过void foo(int*p) { *p=1; }。调用者可以观察到的是函数潜在的副作用。


此用例可复制16个字节

这看起来有点可疑。 gcc复制16字节的代码真的比这更糟糕吗?还是您正在尝试优化尺寸而不是速度?通常,您希望安排指令,以免立即使用加载结果,特别是对于有序CPU(在ARM世界中并不罕见)。

恭喜您正确解决了所有限制。关于GNU C内联汇编的SO问题中,有超过90%的问题具有不安全或次优的约束,甚至有99%。

您的早期消息输出约束和虚拟"m"输入/输出操作数对于确保安全是必不可少的。

相关问题