【发布时间】:2019-12-08 03:43:26
【问题描述】:
基本事实的介绍/确认
众所周知,使用 GCC 风格的 C 和 C++ 编译器,您可以将内联汇编与“内存”破坏器一起使用:
asm("":::"memory");
为了防止(大多数)代码经过它重新排序,充当(线程本地)“内存屏障”(例如,为了与异步信号交互)。
注意:这些“编译器屏障”不会实现线程间同步。
它相当于调用非内联函数,可能会读取所有可以在当前范围之外读取的对象并更改所有可以更改的对象(非 const 对象):
int i;
void f() {
int j = i;
asm("":::"memory"); // can change i
j += i; // not j *= 2
// ... (assume j isn't unused)
}
本质上它与调用单独编译的 NOP 函数相同,只是非内联 NOP 函数调用稍后 (1) 内联,因此没有任何内容可以从中幸存。
(1) 说,经过编译器中间传递,经过分析
所以这里的j 不能更改,因为它是本地的,并且仍然是旧i 值的副本,但i 可能已经更改,所以编译几乎与以下内容相同:
volatile int vi;
int f2() {
int j = vi;
; // can "change" vi
j += vi; // not j *= 2
return j;
}
需要两次读取vi(出于不同的原因),因此编译器不会将其更改为2*vi。
到目前为止,我的理解是否正确? (我想是的。否则这个问题没有意义。)
真正的问题:外部还是静态
以上只是序言。我遇到的问题是静态变量,可能调用静态函数(或 C++ 等效的匿名命名空间):
内存破坏者能否访问无法通过非静态函数访问的静态数据,并调用无法通过其他方式调用的静态函数,因为这些函数在链接阶段都不可见,从其他模块,如果它们没有在 asm 指令的输入参数中明确命名?
static int si;
int f3() {
int j = si;
asm("":::"memory"); // can access si?
j += si; // optimized to j = si*2; ?
return j;
}
[注意:静态的使用有点模糊。建议是 TU 的边界很重要,静态变量是 TU 私有的,但我没有描述它是如何被操纵的。让我们假设它确实在那个 TU 中被操纵,或者编译器可能会假设它实际上是一个常量。]
换句话说,“clobber”是否相当于调用:
- 一个外部 NOP 函数,它不能直接命名
si,也不能以任何间接方式访问它,因为 TU 中的任何函数都不能传递si的地址,或者使si间接可修改 - 一个本地定义的NOP函数,可以访问
si
?
额外问题:全局优化
如果答案是在这种情况下静态变量不被视为外部变量,那么在一次编译程序时会产生什么影响?更具体地说:
在整个程序的全局编译过程中,通过对变量值的全局分析和推断,知道例如全局变量永远不会被修改(或永远不会分配负值......),除非可能在asm“clobber”,优化器的输入?
也就是说,如果非静态i仅在一个TU中命名,那么即使有asm语句,是否可以将其优化为静态int?在这种情况下,是否应该将全局变量明确列为 clobbers?
【问题讨论】:
-
你的理解有误。内存破坏的影响不仅限于非局部变量。
-
@prl 在访问自动对象方面受到限制。 memory effect on automatic GCC 9.2 表明 "memory" 不会破坏局部变量
j。 (与 clang 相同。) -
@prl:我对
asm("":::"memory")效果的体验是,它就像一个非内联函数调用;只有“转义”的局部变量被强制在内存中同步,否则 asm 语句将没有合理的方式来读取它们(例如,通过从假设的全局变量中读取它们的地址)。如果逃逸分析证明一个变量是真正的局部变量;它可以忽略"memory"clobber。要强制它进入内存,请在 asm 语句中使用"m"(var)输入操作数。 -
@curiousguy:未经测试,因此尚未发布答案:IIRC GCC 确实尊重
static变量的可能修改,至少在全球范围内。如果它们不是 asm 语句的输入,则可能不在函数范围内。但如果可能的话,它会优化它们,所以如果你想从内联汇编中访问static,请在它们上使用__attribute__((used))。 (或者从 asm 调用一个函数;未使用的或总是内联的函数也会被优化掉。) -
是的,我明白了。我认为如果不删除
static对象,它将尊重asm内存屏障。应该很简单地编写一些测试用例,看看它是否适用于没有volatile的简单用例。显然volatile根本不需要编译器屏障。
标签: c++ c gcc inline-assembly memory-barriers