创建一个始终返回零的函数,但优化器不知道 [英] Create a function that always returns zero, but the optimizer doesn't know
问题描述
我想创建一个始终返回零的函数,但是对于优化器来说,这一事实应该并不明显,因此由于已知零"状态,使用该值进行的后续计算将不会不断折叠. /p>
在没有链接时优化的情况下,这通常就像将其放入自己的编译单元中一样简单:
int zero() {
return 0;
}
优化器看不到单位,因此不会发现该函数的始终为零的性质.
但是,我需要与LTO一起使用的东西以及将来可能进行的尽可能多的聪明优化.我考虑过从全球阅读:
int x;
int zero() {
return x;
}
...但是在我看来,足够聪明的编译器可能会注意到从未写入x
并仍然确定zero()
始终为零.
我考虑使用volatile
,例如:
int zero() {
volatile int x = 0;
return x;
}
...但是,volatile读取所需副作用的实际语义尚不清楚,并且似乎无法排除函数仍返回零的可能性.
这样的始终为零但不是在编译时的值在几种情况下很有用,例如在两个值之间强制使用no-op依赖性.像这样的内容:a += b & zero()
导致a
依赖最终二进制文件中的b
,但不会更改a
的值.
不要通过告诉我标准不能保证采取任何方式做到这一点"来回答这个问题-我很清楚,我正在寻找实用的答案,而不是标准的语言.
首先,我想:OP的第三条建议:
int zero() {
volatile int x = 0;
return x;
}
实际上可以工作(但这不是我的答案;请参见下文).两周前,此功能完全相同,是 https://godbolt.org/g/SA7k5P .
我的答案是在上面添加static
,即:
int zero() {
static volatile int x;
return x;
}
在此处查看一些测试: https://godbolt.org/g/qzWYJt .>
现在,添加static
后,可观察到的行为"的抽象概念变得更加可信.通过一点点工作,我可以弄清楚x
的地址,特别是如果我禁用了地址空间布局随机化.这可能在.bss
段中.然后,再做一些工作,我可以将调试器/黑客工具附加到正在运行的进程中,然后 change 值x
.并使用volatile
告诉编译器我可以执行此操作,因此不允许通过优化x
来更改此可观察到的行为". (也许可以通过内联将 call 优化为zero
,但我不在乎.)
x
位于 stack 而不是局部变量.因此,此处不适用.但是我们可以将x
从本地范围更改为文件范围,甚至更改为全局范围,如下所示:
volatile int x;
int zero() {
return x;
}
这不会改变我的观点.
进一步的讨论:
是的,volatile
有时会出现问题:例如,请参见此处显示的指向易失性的问题 https://godbolt.org/g/s6JhpL 和易碎的结构损坏报告了一个错误,该错误优化了易失性访问在gcc和Clang中都消失了. (它在三个小时内固定.)一位评论员引用了该标准(添加了重点):
"6.7.3 ...对具有volatile限定类型的对象的访问构成为实现定义."
Regehr同意,但补充说,在非边缘案件中如何使用它已达成共识:
是的,实现定义是对易失性变量的访问.但是您错过了以下事实:所有合理的C实现都将对volatile变量的读取视为读取访问,将对volatile变量的写入视为写入访问.
以获取更多参考.看到:
-
E. Eide,J. Regehr,错误编译了错误,并且该怎么办",第八届ACM和IEEE嵌入式软件国际会议(EMSOFT)的会议记录,.. p>
-
另一个Regehr 2010博客文章,使用易失性破坏系统代码的九种方法 .
-
Wintermute的答案到
In the absence of link-time optimization, this is generally as simple as putting this in its own compilation unit:
int zero() { return 0; }
The optimizer can't see across units, so the always-zero nature of this function won't be discovered.
However, I need something that works with LTO and with as many possible future clever optimizations as well. I considered reading from a global:
int x; int zero() { return x; }
... but it seems to me that a sufficiently smart compiler could notice that
x
is never written to and still decidezero()
is always zero.I considered using a
volatile
, like:int zero() { volatile int x = 0; return x; }
... but the actual semantics of the required side effects of volatile reads aren't exactly clear, and would not seem to exclude the possibility that the function still returns zero.
Such an always-zero-but-not-at-compile-time value is useful in several scenarios, such as forcing a no-op dependency between two values. Something like:
a += b & zero()
causesa
to depend onb
in the final binary, but doesn't change the value ofa
.Don't answer this by telling me the "standard doesn't guarantee any way to do this" - I'm well aware and I'm looking for a practical answer and not language from the standard.
解决方案First an aside: I believe that the OP's third suggestion:
int zero() { volatile int x = 0; return x; }
would in fact work (but this is not my answer; see below). This exact same function two weeks ago was the subject of Is it allowed for a compiler to optimize away a local volatile variable?, with much discussion and differing opinions, which I will not repeat here. But for a recent test of this, see https://godbolt.org/g/SA7k5P.
My answer is to add a
static
to the above, namely:int zero() { static volatile int x; return x; }
See some tests here: https://godbolt.org/g/qzWYJt.
Now with the addition of
static
, the abstract concept of "observable behavior" becomes more believable. With a little bit of work, I could figure out the address ofx
, especially if I disabled Address space layout randomization. This would probably be in the.bss
segment. Then with a bit more work I could attach a debugger/hacking tool to the running process and then change the value ofx
. And withvolatile
, I have told the compiler that I might do this, so it is not allowed to change this "observable behavior" by optimizingx
away. (It could perhaps optimize the call tozero
away by inlining, but I don't care.)The title of Is it allowed for a compiler to optimize away a local volatile variable? is a bit misleading, as the discussion centred on
x
being on the stack rather than it being a local variable. So is not applicable here. But we could changex
from local scope to file scope or even global scope, as in:volatile int x; int zero() { return x; }
This would not change my argument.
Further discussion:
Yes,
volatile
's are sometimes problematic: for example, see the pointer-to-volatile issues shown here https://godbolt.org/g/s6JhpL and in Does accessing a declared non-volatile object through a volatile reference/pointer confer volatile rules upon said accesses?.And yes, sometimes (always?) compilers have bugs.
But I would like to argue that this solution is not an edge case, and that there is a consensus among compiler writers, and I will do so by looking at existing analyses.
John Regehr's 2010 blogpost Volatile Structs Are Broken reports a bug where a volatile access was optimized away in both gcc and Clang. (It was fixed in three hours.) One commentator quoted the standard (emphasis added):
"6.7.3 ... What constitutes an access to an object that has volatile-qualified type is implementation-defined."
Regehr agreed, but added that there is consensus in how it should work on non-edge cases:
Yes, what constitutes an access to a volatile variable is implementation defined. But you have missed the fact that all reasonable C implementations consider a read from a volatile variable to be a read access and a write to a volatile variable to be a write access.
For further references. see:
Another Regehr 2010 blogpost, Nine ways to break your systems code using volatile.
Wintermute's answer to Volatile and its harmful implications.
These are reports about compiler bugs and programmers' errors. But they show how
volatile
should/does work, and that this answer meets those norms.这篇关于创建一个始终返回零的函数,但优化器不知道的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!