Linux 下如何繞過編譯器優化

本文首次發表在 Linux 下如何繞過編譯器優化linux

有同窗在羣裏聊到編譯器優化的事情,不少時候指望編譯器默認作優化,可是有些場景是但願能繞過的,哪些呢?shell

這裏舉兩個實實在在的例子。優化

第一個,在調試的時候,若是默認開啓了優化,要關注的某個變量值,用 gdb 打印時可能會提示被優化掉了,會讓人丈二和尚摸不着頭腦。spa

第二個,就是某些場景,編譯器並不理解背後的實際狀況,好比說,連續往某個地址寫兩個值,編譯器覺得,這不是畫蛇添足了,幫把最後一個寫進去就行了,可是卻不知,這個地址多是個硬件寄存器地址呢,寫第一個,處理器調整一個狀態,再寫一個,再調整一個狀態,兩個都寫完,纔算完整,寫不一樣的位有不一樣的含義。debug

怎麼去掉顯式優化參數

對於第一個,一般不太須要去改整個內核,好比說,把整個 -O2/-Os 都拿掉,這時可能引發的莫名狀況比去 debug 某個問題可能還要棘手。因此,能夠有針對性的,只對某個文件作優化參數調整便可。調試

這個本質上是拿掉 CFLAGS 裏頭的優化參數,其實用替換就行了,可是可選的用法有:rest

文件級CFLAGS_REMOVE_xxx.o = -O2code

arch/mips/kernel/Makefile:cdn

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 掉特定參數:blog

$ 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 知識星球免費體驗卡
相關文章
相關標籤/搜索