C++ 编译器是否允许常量折叠本地volatile?
考虑以下简单代码:C++ 编译器是否允许常量折叠本地volatile?,c++,language-lawyer,volatile,C++,Language Lawyer,Volatile,考虑以下简单代码: void g(); void foo() { volatile bool x = false; if (x) g(); } 您可以看到,gcc和clang都没有优化对g的潜在调用。这在我的理解中是正确的:抽象机器假设volatile变量可能随时发生变化(由于硬件映射),因此不断将false初始化折叠到if检查中是错误的 但是MSVC完全消除了对g的调用(尽管保持了对的读写操作的易变性)。这是符合标准的行为吗 背景:我偶尔使用这种构造来动
void g();
void foo()
{
volatile bool x = false;
if (x)
g();
}
您可以看到,gcc
和clang
都没有优化对g
的潜在调用。这在我的理解中是正确的:抽象机器假设volatile
变量可能随时发生变化(由于硬件映射),因此不断将false
初始化折叠到if
检查中是错误的
但是MSVC完全消除了对g
的调用(尽管保持了对的读写操作的易变性)。这是符合标准的行为吗
背景:我偶尔使用这种构造来动态地打开/关闭调试输出:编译器必须始终从内存中读取值,因此在调试期间更改变量/内存应该相应地修改控制流。MSVC输出确实重新读取了该值,但忽略了它(可能是由于不断折叠和/或死代码消除),这当然违背了我在这里的意图
编辑:
- 这里讨论了如何消除对
volatile
的读写:(谢谢Nathan!)。我认为标准非常明确,这些读写必须发生。但这一讨论并没有涉及编译器将这些读取的结果视为理所当然并在此基础上进行优化是否合法。我想这是标准,但如果有人证明我错了,我会很高兴
- 当然,我可以将
x
设为非局部变量来回避这个问题。这个问题更多的是出于好奇
我认为[intro.execution](段落编号不同)可以用来解释MSVC的行为:
具有自动存储持续时间的每个对象的实例都与其块中的每个条目相关联。这样的对象存在,并且在块执行期间和块挂起期间,保留其最后存储的值
本标准不允许消除易变GLA值的读取,但上述段落可解释为允许预测值false
顺便说一句,C标准(N1570 6.2.4/2)规定
对象存在,具有恒定地址,并在其整个生命周期中保留其最后存储的值。34
34)对于易失性对象,最后一个存储不需要在程序中显式显示
目前还不清楚在C内存/对象模型中是否有一个非显式存储到具有自动存储持续时间的对象中。TL;DR编译器可以对每个易失性访问执行任何操作。但是文档必须告诉您。-“通过易失性glvalue进行访问的语义是由实现定义的。”
该标准定义了一个程序允许的“易失性访问”和其他“可观察行为”(通过“副作用”实现)序列,实现必须按照“仿佛”规则遵守这些序列
但标准上说(我用黑体字强调):
工作草案,编程语言C++标准
文件编号:N4659
日期:2017-03-21
§10.1.7.1 cv限定符
5通过volatile glvalue进行访问的语义由实现定义。[…]
对于交互式设备(我用黑体字强调):
§4.6程序执行
5执行格式良好的程序的一致性实现应产生与使用相同程序和相同输入的抽象机器的相应实例的可能执行之一相同的可观察行为。[……]
7一致性实施的最低要求是:
(7.1)-通过易失性glvalues的访问严格按照抽象机器的规则进行评估。
(7.2)-在程序终止时,写入文件的所有数据应与根据抽象语义执行程序可能产生的结果之一相同。
(7.3)-交互设备的输入和输出动态应在程序等待输入之前实际交付提示输出交互设备的构成由实现定义。
这些统称为程序的可观察行为。[……]
(无论如何,标准没有规定为程序生成的特定代码。)
因此,尽管该标准说,不稳定访问不能从抽象机器副作用的抽象序列中排除&一些代码(可能)定义的可观察行为,除非编译器文档告诉您什么是易失性访问,否则您不能期望任何东西反映在目标代码或真实行为中。交互式设备也是如此
如果您对抽象机器副作用的抽象序列和/或某些代码(可能)定义的后续可观察行为的volatile vis感兴趣,请这样说。但是,如果您对生成的相应目标代码感兴趣,那么必须在编译器和编译的上下文中对其进行解释
长期以来人们错误地认为,对于volatile访问,抽象机器求值/读取导致实现的读取&抽象机器赋值/写入导致实现的写入如果没有实现文档的说明,这种想法是没有根据的。当/iff实现说它实际上在“易失性访问”上做了一些事情时,人们有理由期待某些事情——可能是生成特定的目标代码。我认为跳过检查是合法的
每个人都喜欢引用的段落
34)我