这是正确的;要求一个指针作为内联汇编的输入not暗示所指向的内存也是输入或输出或两者。对于寄存器输入和寄存器输出,对于所有 gcc 知道的 asm,只需通过屏蔽低位来对齐指针,或者向其添加一个常量。 (在这种情况下你会want它可以优化死店。)
The simple option is asm volatile
and a "memory"
clobber1.
您要求的更窄更具体的方式是使用“虚拟”内存操作数也寄存器中的指针。您的 asm 模板没有引用此操作数(除非在 asm 注释中查看编译器选择的内容)。它告诉编译器你使用的是哪个内存actually读、写或读+写。
虚拟内存输入:"m" (*(const int (*)[]) iptr)
或输出:"=m" (*(int (*)[]) iptr)
。或者当然"+m"
使用相同的语法。
该语法正在转换为数组指针并取消引用,因此实际输入是 Carray。 (如果您实际上有一个数组,而不是指针,则不需要任何转换,只需将其作为内存操作数即可。)
如果您未指定尺寸[]
,它告诉 GCC 相对于该指针访问的任何内存都是输入、输出或输入/输出操作数。如果你使用[10]
or [some_variable]
,告诉编译器具体的大小。对于运行时变量大小,gcc 实际上会错过以下优化:iptr[size+1]
is not输入的一部分。
GCC 记录了这一点 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clobbers-and-Scratch-Registers-1因此支持它。我认为如果数组元素类型与指针相同,或者如果它是,则这不是严格别名违规char
.
(来自海湾合作委员会手册)
一个 x86 示例,其中字符串内存参数的长度未知。
asm("repne scasb"
: "=c" (count), "+D" (p)
: "m" (*(const char (*)[]) p), "0" (-1), "a" (0));
如果可以避免在指针输入操作数上使用早期破坏,则虚拟内存输入操作数通常会使用同一寄存器选择简单的寻址模式。
但是,如果您确实使用 Early-clobber 来保证 asm 循环的严格正确性,有时虚拟操作数会使 gcc 在内存操作数的基地址上浪费指令(以及额外的寄存器)。检查汇编output编译器的。
背景:
这是内联 asm 示例中的一个普遍错误,通常未被检测到,因为 asm 包装在一个函数中,该函数不会内联到任何调用者中,从而诱使编译器重新排序存储以进行合并,从而消除死存储。
GNU C 内联 asm 语法是围绕描述single给编译器的指令。目的是告诉编译器有关内存输入或内存输出的信息"m"
or "=m"
操作数约束,它选择寻址模式。
在内联汇编中编写整个循环需要小心确保编译器真正知道发生了什么(或者asm volatile
plus a "memory"
clobber),否则在更改周围代码或启用允许跨文件内联的链接时优化时,您将面临损坏的风险。
也可以看看使用内联汇编循环数组 https://stackoverflow.com/questions/34244185/looping-over-arrays-with-inline-assembly用于使用asm
语句作为循环body,仍然在 C 中执行循环逻辑。使用实际(非虚拟)"m"
and "=m"
操作数,编译器可以通过使用它选择的寻址模式中的位移来展开循环。
脚注 1:A"memory"
clobber 让编译器将 asm 视为非内联函数调用(可以读取或写入除本地内存之外的任何内存)逃逸分析 https://en.wikipedia.org/wiki/Escape_analysis已证明没有逃脱)。转义分析包括 asm 语句本身的输入操作数,还包括任何早期调用可能存储指针的任何全局或静态变量。因此,通常本地循环计数器不必在循环周围溢出/重新加载asm
声明与"memory"
破坏。
asm volatile
有必要确保 asm 不会被优化,即使其输出操作数未使用(因为您需要发生未声明的写入内存的副作用)。
或者对于仅由asm读取的内存,如果相同的输入缓冲区包含不同的输入数据,则需要asm再次运行。没有volatile
,asm语句可以是CSEd https://en.wikipedia.org/wiki/Common_subexpression_elimination脱离循环。 (A"memory"
破坏者确实not使优化器在考虑是否将所有内存视为输入asm
语句甚至需要运行。)
asm
没有输出操作数是隐式的volatile
,但最好将其明确化。 (GCC 手册有一节介绍asm 易失性 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Volatile).
e.g. asm("... sum an array ..." : "=r"(sum) : "r"(pointer), "r"(end_pointer) : "memory")
有一个输出操作数,因此不是隐式易失性的。如果你像这样使用它
arr[5] = 1;
total += asm_sum(arr, len);
memcpy(arr, foo, len);
total += asm_sum(arr, len);
Without volatile
第二个asm_sum
可以进行优化,假设具有相同输入操作数(指针和长度)的相同 asm 将产生相同的输出。你需要volatile
对于任何不是其显式输入操作数的纯函数的汇编语言。如果不优化的话then the "memory"
clobber 将达到要求内存同步的预期效果。