在 asm volatile inline PTX 指令中,为什么还要指定 "memory" 副作用?

In asm volatile inline PTX instructions, why also specify "memory" side effecs?

考虑以下 CUDA Inline PTX Assebly 指南 (v10.2) 的摘录:

The compiler assumes that an asm() statement has no side effects except to change the output operands. To ensure that the asm is not deleted or moved during generation of PTX, you should use the volatile keyword, e.g.:

asm volatile ("mov.u32 %0, %%clock;" : "=r"(x));

Normally any memory that is written to will be specified as an out operand, but if there is a hidden side effect on user memory (for example, indirect access of a memory location via an operand), or if you want to stop any memory optimizations around the asm() statement performed during generation of PTX, you can add a "memory" clobbers specification after a 3rd colon...

听起来volatile:: "memory"都是为了表示记忆中的副作用。现在,当然,可能会有非内存副作用(比如 trap;)。但是 - 当我使用 volatile 时,不是 useless/meaningless 也指定 :: "memory") 吗?

略有关联:When using inline PTX asm() instructions, what does 'volatile' do?

volatile 内联 asm 语句被视为其输入的 纯函数 :每次 运行 时给出相同的输出相同的显式输入。

另外,没有 "memory" 破坏:不会读取或写入任何未作为输入或输出操作数提及的内容。

It sounds like both volatile and :: "memory" are intended to indicate side effects in memory.

不,volatile 只是意味着输出操作数不是输入操作数的纯函数。 "memory" 破坏大部分是正交的,不是 volatile

您引用的示例似乎正在读取 %%clock 循环计数器或每次都需要重新执行的东西,否则编译器可能 CSE 并将其提升到循环之外。您不希望它强制编译器 spill/reload 它在寄存器中的任何全局变量。 volatile 并不意味着内存副作用,所以它只是这个用例的门票。

asm 模板在编译器背后读取或写入任何其他变量(不是通过显式 "m""=m""+m" 操作数)仍然是一个错误因为 volatile 并不意味着 "memory" 破坏。

在 GNU C 内联 asm 中,即使 "r"(pointer_variable) 而不是 暗示指向的数据已被读取或写入。例如如果您对变量所做的所有操作只是将指向它的指针作为输入传递给 asm 语句而没有 "memory" 破坏,则可以将赋值优化为死存储。

A "memory" clobber 让编译器假定任何全局可访问的内存(或通过指针输入可访问)可能已被读取或写入,因此spill/reload vars from registers 围绕这样一个 asm 语句。 (除非 escape analysis 可以证明没有其他东西可以指向它们,即指向 var 的指针没有 "escaped" 本地范围。就像编译器如何决定他们可以将 var 保存在通过非内联函数调用注册。)


那么 "memory" 没有 volatile 一个人安全吗?否

如果使用 none 的显式输出操作数,

A "memory" 破坏不会阻止 asm 语句优化。 (没有“=...”操作数,asm 语句是隐式易变的)。

必须假定具有内存破坏的非易失性 asm 语句修改抽象机中那个点的任何可访问内存 if/when asm 模板字符串执行,但编译器仍然可以自由地进行导致根本不发生这种情况的转换,或者发生的频率低于源。 (例如,如果循环中发生变化的其他变量都是地址未逃脱函数的本地变量,则将其从循环中提升出来。)

volatile asm 语句仍被假定为纯函数 wrt。它的显式输入和输出,因此如果循环每次迭代都使用相同的 "in",则可以将 asm("..." : "=r"(out) : "r"(in) : "memory"); 提升到循环之外。 (这只有在循环变量都是 asm 语句无法指向的局部变量时才会发生(像非内联函数调用一样进行转义分析)。否则 "memory" 破坏器会阻止重新排序。)

或者如果 "out" 的所有使用都可以优化掉,则完全优化掉,而不管语句周围的任何内存访问。如果您省略 volatile.

,则该决定仅 基于显式操作数

没有 volatile"memory" 破坏器没有太多用例;你可以想象用它来描述一个内部使用缓存来记忆结果的函数。编译器可以 运行 根据需要频繁或不频繁地使用它,我们实际上并不关心内部缓存是否发生了变化。这是副作用,但不是有价值的副作用。


(我假设 CUDA 内联 asm 与 GNU C 内联 asm 具有相同的语义,如 supported/implemented by Clang/LLVM 和 GCC。从引用肯定是这样的。我对 CUDA 真的一无所知,所以我上面所说的一切都是基于 GNU C 内联 asm,因为 CUDA asm 看起来是相同的。如果我错了请纠正我,例如如果 asm 没有输出操作数的语句不是隐式的 volatile 或者如果 CUDA 没有指针。

由于 GNU C 内联汇编语法是为 C 设计的,后来又改用于 CUDA,因此从 C 的角度思考(包括指针和转义分析)可能有助于您理解设计。)