我只是想知道我是否要执行以下操作:
{
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,因此必须同时使用一种解决方案。当然,这是一个简化的示例,用于发布问题。
答案 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"
输入/输出操作数对于确保安全是必不可少的。