這個問題在這里已經有了答案: 再次不穩定:有必要防止優化? (3 個回答) 19 小時前關閉。
我使用 gcc 為 ARM Cortex-M4 編譯了一個簡單的測驗代碼,它會優化讓我困惑的全域變數的 usgae。gcc優化全域變數使用的規則是什么?
GCC 編譯器:gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc
優化級別:-Os
我的測驗代碼:
以下代碼在“foo.c”中,函式 foo1() 和 foo2() 在任務 A 中被呼叫,函式 global_cnt_add() 在任務 B 中被呼叫。
int g_global_cnt = 0;
void dummy_func(void);
void global_cnt_add(void)
{
g_global_cnt ;
}
int foo1(void)
{
while (g_global_cnt == 0) {
// do nothing
}
return 0;
}
int foo2(void)
{
while (g_global_cnt == 0) {
dummy_func();
}
return 0;
}
The function dummy_func() is implemented in bar.c as following:
void dummy_func(void)
{
// do nothing
}
函式 foo1() 的匯編代碼如下所示:
int foo1(void)
{
while (g_global_cnt == 0) {
201218: 4b02 ldr r3, [pc, #8] ; (201224 <foo1 0xc>)
20121a: 681b ldr r3, [r3, #0]
20121c: b903 cbnz r3, 201220 <foo1 0x8>
20121e: e7fe b.n 20121e <foo1 0x6>
// do nothing
}
return 0;
}
201220: 2000 movs r0, #0
201222: 4770 bx lr
201224: 00204290 .word 0x00204290
函式 foo2() 的匯編代碼如下所示:
int foo2(void)
{
201228: b510 push {r4, lr}
while (g_global_cnt == 0) {
20122a: 4c04 ldr r4, [pc, #16] ; (20123c <foo2 0x14>)
20122c: 6823 ldr r3, [r4, #0]
20122e: b10b cbz r3, 201234 <foo2 0xc>
dummy_func();
}
return 0;
}
201230: 2000 movs r0, #0
201232: bd10 pop {r4, pc}
dummy_func();
201234: f1ff fcb8 bl 400ba8 <dummy_func>
201238: e7f8 b.n 20122c <foo2 0x4>
20123a: bf00 nop
20123c: 00204290 .word 0x00204290
在函式 foo1() 的匯編代碼中,全域變數“g_global_cnt”只加載一次,while 回圈永遠不會被打破。編譯器優化了“g_global_cnt”的使用,我知道我可以添加 volatile 來避免這種優化。
在函式foo2()的匯編代碼中,全域變數“g_global_cnt”在每個while回圈中被加載和檢查,while回圈可以被打破。
gcc 優化規則有何不同?
uj5u.com熱心網友回復:
為了理解這種行為,您必須考慮副作用和序列點ref。
對于編譯器而言,副作用是運算子、運算式、陳述句或函式的結果,即使在運算子、運算式、陳述句或函式已完成評估后仍然存在。
而 *A 序列點定義了計算機程式執行中的任何點,在該點處,可以保證先前評估的所有副作用都已執行,并且尚未執行后續評估的任何副作用。*
序列點的主要規則是,除了計算其值的變化之外,不會出于任何目的在點之間對變數進行多次訪問
參考C標準:
在抽象機中,所有運算式都按照語意的規定進行評估。如果一個實際的實作可以推斷出它的值沒有被使用并且沒有產生所需的副作用(包括呼叫函式或訪問易失性物件引起的任何副作用),則它不需要評估運算式的一部分。
在您的代碼中
int foo1(void)
{
while (g_global_cnt == 0) {
// do nothing
}
return 0;
}
閱讀后,g_global_cnt不再有可能影響變數值的副作用。編譯器無法知道它在函式范圍之外被修改,因此它認為您只能讀取一次,這是因為函式范圍內沒有更多的序列點。
告訴編譯器每次讀取都有副作用的方法是用識別符號標記變數volatile。
與int g_global_cnt = 0;:
adrp x0, g_global_cnt
add x0, x0, :lo12:g_global_cnt
ldr w0, [x0]
cmp w0, 0
beq .L3
mov w0, 0
ret
與volatile int g_global_cnt = 0;:
adrp x0, g_global_cnt
add x0, x0, :lo12:g_global_cnt
ldr w0, [x0]
cmp w0, 0
cset w0, eq
and w0, w0, 255
cmp w0, 0
bne .L3
mov w0, 0
ret
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/522240.html
標籤:C海合会手臂皮质-m4
上一篇:存盤的聯合和值
下一篇:如何在源檔案中包含所有內容?
