Linux 下如何繞過編譯器優化

吳章金發表於2019-08-29

本文首次發表在 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 知識星球』:

Linux 知識星球免費體驗卡

相關文章