[英]VpnManagementAgent always returns zero profiles plus I can't seem to create new ones
[英]Create a function that always returns zero, but the optimizer doesn't know
我想創建一個總是返回零的函數,但這個事實對於優化器來說不應該是顯而易見的,因此使用該值的后續計算不會因“已知零”狀態而不斷地折疊掉。
在沒有鏈接時優化的情況下,這通常就像將它放在自己的編譯單元中一樣簡單:
int zero() {
return 0;
}
優化器無法跨單元查看,因此不會發現此函數的始終為零的特性。
但是,我需要一些適用於LTO的東西,以及盡可能多的未來聰明的優化。 我考慮從全球閱讀:
int x;
int zero() {
return x;
}
...但在我看來,一個足夠聰明的編譯器可以注意到x
永遠不會被寫入並仍然決定zero()
始終為零。
我考慮過使用volatile
,比如:
int zero() {
volatile int x = 0;
return x;
}
...但是,揮發性讀取所需的副作用的實際語義並不完全清楚,似乎不會排除該函數仍然返回零的可能性。
這種始終為零但不是編譯時的值在幾種情況下很有用,例如強制兩個值之間的無操作依賴性。 喜歡的東西: a += b & zero()
導致a
依賴於b
在最終二進制,但不改變的值a
。
不要通過告訴我“標准不能保證任何方式來做到這一點”來回答這個問題 - 我很清楚,我正在尋找一個實用的答案,而不是標准的語言。
如果編譯器可以解決這個問題我會很驚訝:
int not_a_zero_honest_guv()
{
// static makes sure the initialization code only gets called once
static int const i = std::ifstream("") ? 1:0;
return i;
}
int main()
{
std::cout << not_a_zero_honest_guv();
}
這使用函數local static的復雜(不可預測)運行時初始化。 如果頑皮的小編譯器發現空文件名總是會失敗,那么在那里放一些非法的文件名。
首先拋開:我相信OP的第三個建議:
int zero() {
volatile int x = 0;
return x;
}
實際上會工作(但這不是我的答案;見下文)。 兩個星期前這個完全相同的函數是主題是它是否允許編譯器優化掉本地volatile變量? 有很多討論和不同的意見,我在此不再贅述。 但是對於最近的測試,請參閱https://godbolt.org/g/SA7k5P 。
我的答案是在上面添加一個static
,即:
int zero() {
static volatile int x;
return x;
}
請參閱此處的一些測試: https : //godbolt.org/g/qzWYJt 。
現在隨着static
的增加,“可觀察行為”的抽象概念變得更加可信。 通過一些工作,我可以找出x
的地址,特別是如果我禁用地址空間布局隨機化 。 這可能是在.bss
段。 然后,通過更多的工作,我可以將調試器/黑客工具附加到正在運行的進程,然后更改 x
的值。 對於volatile
,我告訴編譯器我可能會這樣做,因此不允許通過優化x
來改變這種“可觀察行為”。 (它也許可以通過內聯來優化調用 zero
,但我不在乎。)
標題是否允許編譯器優化本地volatile變量? 有點誤導,因為討論集中在堆棧上的x
而不是局部變量 。 因此不適用於此。 但我們可以將x
從本地范圍更改為文件范圍甚至全局范圍,如:
volatile int x;
int zero() {
return x;
}
這不會改變我的論點。
進一步討論:
是的, volatile
有時會出現問題:例如,請參閱https://godbolt.org/g/s6JhpL和https://godbolt.org/g/s6JhpL中顯示的指向易失性問題,以及是否通過易失性參考/指針訪問聲明的非易失性對象所述訪問的易變規則? 。
是的,有時(總是?)編譯器有bug。
但我想說這個解決方案不是一個邊緣案例,並且編譯器編寫者之間存在共識,我將通過查看現有分析來做到這一點。
John Regehr的2010年博文“ Volatile Structs Are Broken”報告了一個在gcc和Clang中優化了易失性訪問的錯誤。 (它在三個小時內得到修復。)一位評論員引用了標准(強調增加):
“6.7.3 ......對具有volatile限定類型的對象的訪問構成是實現定義的。 ”
Regehr同意了,但補充說,對於如何處理非邊緣案件已達成共識:
是的,構成對volatile變量的訪問是實現定義的。 但是你錯過了這樣一個事實:所有合理的C實現都認為從volatile變量讀取是一個讀訪問,而寫一個volatile變量是一個寫訪問。
供進一步參考。 看到:
E. Eide,J。Regehr,“Volatiles被錯誤編譯,如何處理它”, 第八屆ACM會議論文和IEEE嵌入式軟件國際會議(EMSOFT) ,2008年 。
另一篇Regehr 2010博客文章, 九種使用volatile打破你的系統代碼的方法 。
Wintermute對Volatile及其有害影響的回答 。
這些是關於編譯器錯誤和程序員錯誤的報告。 但它們表明應該/確實有多么volatile
,並且這個答案符合這些規范。
你會發現每個編譯器都有一個擴展來實現這一點。
GCC:
__attribute__((noinline))
int zero()
{
return 0;
}
MSVC:
__declspec(noinline)
int zero()
{
return 0;
}
在clang和gcc上,破壞一個變量是有效的,但是會產生一些開銷
int zero()
{
int i = 0;
asm volatile(""::"g"(&i):"memory");
return i;
}
在gcc上的O3下編譯到
mov DWORD PTR [rsp-4], 0
lea rax, [rsp-4]
mov eax, DWORD PTR [rsp-4]
ret
和鏗鏘
mov dword ptr [rsp - 12], 0
lea rax, [rsp - 12]
mov qword ptr [rsp - 8], rax
mov eax, dword ptr [rsp - 12]
ret
住 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.