本文首次發表在 Linux 下如何繞過編譯器優化
有同學在群裡聊到編譯器優化的事情,很多時候期望編譯器預設做優化,但是有些場景是希望能繞過的,哪些呢?
這裡舉兩個實實在在的例子。
第一個,在除錯的時候,如果預設開啟了優化,要關注的某個變數值,用 gdb 列印時可能會提示被優化掉了,會讓人丈二和尚摸不著頭腦。
第二個,就是某些場景,編譯器並不理解背後的實際情況,比如說,連續往某個地址寫兩個值,編譯器以為,這不是多此一舉了,幫把最後一個寫進去就好了,但是殊不知,這個地址可能是個硬體暫存器地址呢,寫第一個,處理器調整一個狀態,再寫一個,再調整一個狀態,兩個都寫完,才算完整,寫不同的位有不同的含義。
怎麼去掉顯式優化引數
對於第一個,通常不太需要去改整個核心,比如說,把整個 -O2/-Os
都拿掉,這時可能引起的莫名情況比去 debug 某個問題可能還要棘手。所以,可以有針對性的,只對某個檔案做優化引數調整即可。
這個本質上是拿掉 CFLAGS
裡頭的優化引數,其實用替換就好了,但是可選的用法有:
檔案級:CFLAGS_REMOVE_xxx.o = -O2
arch/mips/kernel/Makefile
:
ifdef CONFIG_FUNCTION_TRACER
CFLAGS_REMOVE_ftrace.o = -pg
CFLAGS_REMOVE_early_printk.o = -pg
CFLAGS_REMOVE_perf_event.o = -pg
CFLAGS_REMOVE_perf_event_mipsxx.o = -pg
endif
複製程式碼
原理如下,就是從原始編譯引數中 filter-out
掉特定引數:
$ grep CFLAGS_REMOVE -ur linux-stable/scripts/Makefile.lib
_c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))
複製程式碼
目錄級:KBUILD_CFLAGS := $(filter-out -O2, $(KBUILD_CFLAGS))
arch/mips/boot/compressed/Makefile
:
KBUILD_CFLAGS := $(filter-out -pg, $(KBUILD_CFLAGS))
複製程式碼
自己主動 filter-out
掉。也可以直接呼叫指令碼替換:
KBUILD_CFLAGS := $(shell echo $(KBUILD_CFLAGS) | sed -e "s/-pg//")
複製程式碼
當然,用 Makefile 內建的 filter-out
效率會高,只是方便大家理解邏輯。
怎麼確認這個編譯引數是否真地生效呢,有兩種方法:
一種是直接在相應 Makefile 列印 KBUILD_CFLAGS
,例如:$(error $(KBUILD_CFLAGS))
,另外一種是 make /path/to/xxx.o V=1
檢視。在 Linux Lab 裡頭可以用 make k-x /path/to/xxx.o V=1
。
除了直接拿掉,也可以考慮替換成 -Og
,這個更適合除錯需要。
怎麼去掉隱式優化
第二個,也來看看例項:
drivers/cpufreq/loongson2_cpufreq.c
:
static void loongson2_cpu_wait(void)
{
unsigned long flags;
u32 cpu_freq;
spin_lock_irqsave(&loongson2_wait_lock, flags);
cpu_freq = LOONGSON_CHIPCFG(0);
LOONGSON_CHIPCFG(0) &= ~0x7; /* Put CPU into wait mode */
LOONGSON_CHIPCFG(0) = cpu_freq; /* Restore CPU state */
spin_unlock_irqrestore(&loongson2_wait_lock, flags);
local_irq_enable();
}
複製程式碼
上面中間三句,從 gcc 的角度來看,這不是傻嘛,啥也沒做啊,又讀又寫是什麼鬼,目標變數的值根本“沒變”呢。原因是什麼,這個背後的 LOONGSON_CHIPCFG(0)
是硬體暫存器地址,有它的時序意義,不同的位有不同的意義,寫不同的值會有不同的動作。這個時候就得明確告訴 gcc:
arch/mips/include/asm/mach-loongson64/loongson.h
:
#define LOONGSON_CHIPCFG(id) (*(volatile u32 *)(loongson_chipcfg[id]))
複製程式碼
這種情況怎麼確認呢?make /path/to/xxx.s
,看看程式碼還在不在。在 Linux Lab 裡頭可以用 make k-x /path/to/xxx.s
。
送您一枚免費體驗卡
更多 Linux 精彩歡迎透過下方免費體驗卡訪問『Linux 知識星球』: