在寫關于編譯器必須如何處理的答案時volatile,我相信我可能偶然發現了一個 gcc 錯誤,并希望有人在我報告之前進行驗證。
我寫了一個簡單的函式,例如:
int foo (int a, int b, int c)
{
b = a 1;
c = b 1;
a = c 1;
return a;
}
如果沒有優化,這會導致大量無意義的資料來回移動。通過優化,編譯器只獲取a存盤的暫存器,然后添加 3 并回傳該結果。說 x86lea eax, [rdi 3]和ret. 這是意料之中的,到目前為止一切順利。
為了演示排序和易失性訪問,我將示例更改為:
int foo (int a, int b, int c)
{
b = a 1;
c = *(volatile int*)&b 1;
a = c 1;
return a;
}
這里有一個對bvolatile 限定的內容的左值訪問,據我所知,絕對不允許編譯器優化掉該訪問1)。從 gcc 4.1.2(可能更早)到 gcc 10.3,我得到了一致的行為(在 clang 中相同)。即使使用-O3以下命令,x86 機器代碼看起來也像這樣:
foo:
add edi, 1
mov DWORD PTR [rsp-4], edi
mov eax, DWORD PTR [rsp-4]
add eax, 2
ret
然后我在 gcc 11.1 及更高版本上嘗試相同的方法,現在我得到:
foo:
lea eax, [rdi 3]
ret
https://godbolt.org/z/e5x74z3Kb
ARM gcc 11.1 做了類似的事情。
這是編譯器錯誤嗎?
1)參考資料:ISO/IEC 9899:2018 5.1.2.3,特別是 §2、§4 和 §6。
uj5u.com熱心網友回復:
將地址傳遞給非行內函式使 GCC 尊重volatile轉換為以后訪問(可能更早,沒有檢查)到函式 arg 或本地。 https://godbolt.org/z/cssveev7n
我復制了這c = 條線,b由于使用 GCC 主干,由于 volatile 轉換,asm 包含兩個負載。
void bar(void*);
int foo (int a, int b, int c)
{
bar(&b); // b's address has now "escaped" - potentially globally visible
b = a 1;
c = *(volatile int*)&b 1;
c = *(volatile int*)&b 1; // both accesses present.
a = c 1;
return a;
}
# GCC trunk -O3 -fverbose-asm
call bar #
mov DWORD PTR [rsp 12], ebx # b, tmp89
mov eax, DWORD PTR [rsp 12] # _2, MEM[(volatile int *)&b]
mov eax, DWORD PTR [rsp 12] # _3, MEM[(volatile int *)&b]
...
add eax, 2
ret
所以這似乎是無辜的,除非在一些微基準用例中;它不會使用像這樣的強制轉換來破壞手動執行的原子,例如Linux 內核的READ_ONCE/WRITE_ONCE宏。
仍然可以說違反了 ISO C 規則,如果int使用volatile int. 如果沒有,它只是 GCC 定義行為,所以它取決于 GCC。我將其作為資料點發布,而不是在問題的該方面的任一方向的論點。
uj5u.com熱心網友回復:
根據 C18 5.1.2.3/6,對 volatile 物件的訪問(嚴格按照抽象機的規則)是程式可觀察行為的一部分,所有符合要求的實作都必須重現這些行為。此背景關系中的術語“訪問”包括讀取和寫入。
C18 5.1.2.3/2 和 /4 強調 volatile 訪問是需要副作用的,排除在允許實作避免產生不需要的副作用的規則之外。
我在 GCC 中看到的唯一結果是一個論點,盡管它(volatile int*)&b是一個具有volatile-qualified 型別的左值,但它可以證明它指定的物件 ( b) 實際上不是“易失性物件”,如果你按照它宣言。這與此版本函式的 GCC 11.2 觀察到的行為一致:
int foo (int a, int b, int c)
{
volatile int bv = a 1;
c = bv 1;
a = c 1;
return a;
}
,它產生的程式集與舊版本的 GCC 對原始代碼 ( Godbolt )所做的相同。
這是否構成不符合語言標準的錯誤尚不清楚,但 GCC 肯定會阻礙程式員的明顯意圖。
uj5u.com熱心網友回復:
我聽到一個編譯器團隊令人信服地爭論(好吧,我差點睡著了,所以我得到了一個粗略的輪廓),在外部范圍的字大小的物件之外, volatile 是一種毫無意義的裝飾。此外,編譯器提供了一些圍繞無意義屬性物件的傳統行為,以方便使用遺留代碼的人們。這種解釋是基于對 C 標準的荒謬簡化,這比正確的要好,它在技術上是正確的,是 alpha 極客的黃金標準。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/383275.html
上一篇:txt檔案中的隨機值不一樣
下一篇:分配結構的變數實際上有什么作用?
