【问题标题】:When can a volatile variable be optimized away completely?什么时候可以完全优化 volatile 变量?
【发布时间】:2015-03-05 04:12:16
【问题描述】:

考虑这个代码示例:

int main(void)
{
  volatile int a;
  static volatile int b;

  volatile int c;
  c = 20;
  static volatile int d;
  d = 30;

  volatile int e = 40;
  static volatile int f = 50;

  return 0;
}

没有volatile,编译器可以优化掉所有变量,因为它们永远不会被读取。

我认为ab 可以被优化掉,因为它们完全没有被使用,参见unused volatile variable

我认为cd 不能被删除,因为它们是被写入的,而且对 volatile 变量的写入必须实际发生。 e 应该等同于 c

GCC 不会优化掉f,但它也不会发出任何写入指令。 50被设置在数据部分中。 LLVM (clang) 完全删除 f

这些说法是真的吗?

  1. 如果永远不会访问 volatile 变量,则可以将其优化掉。
  2. 静态或全局变量的初始化不算作访问。

【问题讨论】:

  • 我认为这个问题更具体地说明了如何优化 volatile 变量。
  • c = 30; 的意思是阅读d = 30; 吗?
  • 是的,谢谢马特。我会编辑它。

标签: c language-lawyer volatile


【解决方案1】:

写入 volatile 变量(甚至是自动变量)算作可观察行为。

C11 (N1570) 5.1.2.3/6:

对一致性实现的最低要求是:

— 对 volatile 对象的访问严格按照抽象规则进行评估 机器。

——程序终止时,写入文件的所有数据都应与以下结果相同 根据抽象语义执行程序会产生。

——交互式设备的输入和输出动态应按照 7.21.3。这些要求的目的是无缓冲或行缓冲输出 尽快出现,以确保提示消息实际出现在之前 等待输入的程序。

这是程序的可观察行为

问题是:初始化 (e, f) 算作“访问”吗?正如 Sander de Dycker 所指出的,6.7.3 说:

什么构成对具有 volatile 限定类型的对象的访问是实现定义的。

这意味着是否可以优化 ef 取决于编译器 - 但这必须记录在案!

【讨论】:

  • 6.7.3 说“构成对具有 volatile 限定类型的对象的访问是实现定义的。”,它解决了您的最后一段。
【解决方案2】:

严格来说,任何被访问(读取或写入)的 volatile 变量都无法根据 C 标准进行优化。该标准规定,对 volatile 对象的访问可能会产生未知的副作用,并且对 volatile 对象的访问必须遵循 C 抽象机的规则(其中所有表达式都根据其语义进行评估)。

来自强大的标准(强调我的):

(C11, 6.7.3p7) “具有 volatile 限定类型的对象可能会以不知道的方式被修改 实施或有其他未知的副作用。因此任何引用的表达式 对这样的对象,要严格按照抽象机的规则进行评估, 如 5.1.2.3 所述。”

因此,即使是简单的变量初始化也应被视为访问。请记住,static 说明符还会导致对象被初始化(到0)并因此被访问。

现在已知编译器在使用 volatile 限定符时会表现出不同的行为,我猜其中很多编译器只会优化示例程序中的大部分 volatile 对象,但具有显式赋值 (=) 的对象除外。

【讨论】:

  • 注意声明不是表达式
  • 但它进一步说:“对具有 volatile 限定类型的对象的访问是由实现定义的。”这解释了上面提到的 gcc 和 llvm 之间的行为差​​异。
  • 如果一个 volatile 自动变量的地址没有被占用,这表明编译器可以优化所有可以在相同情况下访问它的指令但是它不能移动在访问它之前的点之后发生的副作用,反之亦然。
猜你喜欢
  • 1970-01-01
  • 2014-08-06
  • 2010-09-15
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 2016-11-21
  • 2019-07-31
  • 1970-01-01
相关资源
最近更新 更多