天天看點

Makefile編譯選項CC與CXX/CPPFLAGS、CFLAGS與CXXFLAGS/LDFLAGS

讓我們先看看 Makefile 規則中的編譯指令通常是怎麼寫的。

大多數軟體包遵守如下約定俗成的規範:

盡管将源代碼編譯為二進制檔案的四個步驟由不同的程式(cpp,gcc/g++,as,ld)完成,但是事實上 cpp, as, ld 都是由 gcc/g++ 進行間接調用的。換句話說,控制了 gcc/g++ 就等于控制了所有四個步驟。從 Makefile 規則中的編譯指令可以看出,編譯工具的行為全靠 CC/CXX CPPFLAGS CFLAGS/CXXFLAGS LDFLAGS 這幾個變量在控制。當然理論上控制編譯工具行為的還應當有 AS ASFLAGS ARFLAGS 等變量,但是實踐中基本上沒有軟體包使用它們。

那麼我們如何控制這些變量呢?一種簡易的做法是首先設定與這些 Makefile 變量同名的環境變量并将它們 export 為全局,然後運作 configure 腳本,大多數 configure 腳本會使用這同名的環境變量代替 Makefile 中的值。但是少數 configure 腳本并不這樣做(比如GCC-3.4.6和Binutils-2.16.1的腳本就不傳遞LDFLAGS),你必須手動編輯生成的 Makefile 檔案,在其中尋找這些變量并修改它們的值,許多源碼包在每個子檔案夾中都有 Makefile 檔案,真是一件很累人的事!

這是 C 與 C++ 編譯器指令。預設值一般是 “gcc” 與 “g++”。這個變量本來與優化沒有關系,但是有些人因為擔心軟體包不遵守那些約定俗成的規範,害怕自己苦心設定的 CFLAGS/CXXFLAGS/LDFLAGS 之類的變量被忽略了,而索性将原本應當放置在其它變量中的選項一股老兒塞到 CC 或 CXX 中,比如:CC=”gcc -march=k8 -O2 -s”。這是一種怪異的用法,本文不提倡這種做法,而是提倡按照變量本來的含義使用變量。

這是用于預處理階段的選項。不過能夠用于此變量的選項,看不出有哪個與優化相關。如果你實在想設一個,那就使用下面這兩個吧:

-DNDEBUG

“NDEBUG”是一個标準的 ANSI 宏,表示不進行調試編譯。

-D_FILE_OFFSET_BITS=64

大多數包使用這個來提供大檔案(>2G)支援。

CFLAGS 表示用于 C 編譯器的選項,CXXFLAGS 表示用于 C++ 編譯器的選項。這兩個變量實際上涵蓋了編譯和彙編兩個步驟。大多數程式和庫在編譯時預設的優化級别是”2″(使用”-O2″選項)并且帶有調試符号來編 譯,也就是 CFLAGS=”-O2 -g”, CXXFLAGS=$CFLAGS 。事實上,”-O2″已經啟用絕大多數安全的優化選項了。另一方面,由于大部分選項可以同時用于這兩個變量,是以僅在最後講述隻能用于其中一個變量的選 項。[提醒]下面所列選項皆為非預設選項,你隻要按需添加即可。

先說說”-O3″在”-O2″基礎上增加的幾項:

-finline-functions

允許編譯器選擇某些簡單的函數在其被調用處展開,比較安全的選項,特别是在CPU二級緩存較大時建議使用。

-funswitch-loops

将循環體中不改變值的變量移動到循環體之外。

-fgcse-after-reload

為了清除多餘的溢出,在重載之後執行一個額外的載入消除步驟。

另外:

-fomit-frame-pointer

對于不需要棧指針的函數就不在寄存器中儲存指針,是以可以忽略存儲和檢索位址的代碼,同時對許多函數提供一個額外的寄存器。所有”-O”級别都打開 它,但僅在調試器可以不依靠棧指針運作時才有效。在AMD64平台上此選項預設打開,但是在x86平台上則預設關閉。建議顯式的設定它。

-falign-functions=N

-falign-jumps=N

-falign-loops=N

-falign-labels=N

這四個對齊選項在”-O2″中打開,其中的根據不同的平台N使用不同的預設值。如果你想指定不同于預設值的N,也可以單獨指定。比如,對于L2- cache>=1M的cpu而言,指定 -falign-functions=64 可能會獲得更好的性能。建議在指定了 -march 的時候不明确指定這裡的值。

調試選項:

-fprofile-arcs

在使用這一選項編譯程式并運作它以建立包含每個代碼塊的執行次數的檔案後,程式可以再次使用 -fbranch-probabilities 編譯,檔案中的資訊可以用來優化那些經常選取的分支。如果沒有這些資訊,gcc将猜測哪個分支将被經常運作以進行優化。這類優化資訊将會存放在一個以源文 件為名字的并以”.da”為字尾的檔案中。

全局選項:

-pipe

在編譯過程的不同階段之間使用管道而非臨時檔案進行通信,可以加快編譯速度。建議使用。

目錄選項:

–sysroot=dir

将dir作為邏輯根目錄。比如編譯器通常會在 /usr/include 和 /usr/lib 中搜尋頭檔案和庫,使用這個選項後将在 dir/usr/include 和 dir/usr/lib 目錄中搜尋。如果使用這個選項的同時又使用了 -isysroot 選項,則此選項僅作用于庫檔案的搜尋路徑,而 -isysroot 選項将作用于頭檔案的搜尋路徑。這個選項與優化無關,但是在 CLFS 中有着神奇的作用。

代碼生成選項:

-fno-bounds-check

關閉所有對數組通路的邊界檢查。該選項将提高數組索引的性能,但當超出數組邊界時,可能會造成不可接受的行為。

-freg-struct-return

如果struct和聯合體足夠小就通過寄存器傳回,這将提高較小結構的效率。如果不夠小,無法容納在一個寄存器中,将使用記憶體傳回。建議僅在完 全使用GCC編譯的系統上才使用。

-fpic

生成可用于共享庫的位置獨立代碼。所有的内部尋址均通過全局偏移表完成。要确定一個位址,需要将代碼自身的記憶體位置作為表中一項插入。該選項産生可 以在共享庫中存放并從中加載的目标子產品。

-fstack-check

為防止程式棧溢出而進行必要的檢測,僅在多線程環境中運作時才可能需要它。

-fvisibility=hidden

設定預設的ELF鏡像中符号的可見性為隐藏。使用這個特性可以非常充分的提高連接配接和加載共享庫的性能,生成更加優化的代碼,提供近乎完美的API輸 出和防止符号碰撞。我們強烈建議你在編譯任何共享庫的時候使用該選項。參見 -fvisibility-inlines-hidden 選項。

硬體體系結構相關選項[僅僅針對x86與x86_64]:

-march=cpu-type

為特定的cpu-type編譯二進制代碼(不能在更低級别的cpu上運作)。Intel可以用:pentium2, pentium3(=pentium3m), pentium4(=pentium4m), pentium-m, prescott, nocona, core2(GCC-4.3新增) 。AMD可以用:k6-2(=k6-3), athlon(=athlon-tbird), athlon-xp(=athlon-mp), k8(=opteron=athlon64=athlon-fx)

-mfpmath=sse

P3和athlon-xp級别及以上的cpu支援”sse”标量浮點指令。僅建議在P4和K8以上級别的處理器上使用該選項。

-malign-double

将double, long double, long long對齊于雙位元組邊界上;有助于生成更高速的代碼,但是程式的尺寸會變大,并且不能與未使用該選項編譯的程式一起工作。

-m128bit-long-double

指定long double為128位,pentium以上的cpu更喜歡這種标準,并且符合x86-64的ABI标準,但是卻不附合i386的ABI标準。

-mregparm=N

指定用于傳遞整數參數的寄存器數目(預設不使用寄存器)。0<=N<=3 ;注意:當N>0時你必須使用同一參數重新建構所有的子產品,包括所有的庫。

-msseregparm

使用SSE寄存器傳遞float和double參數和傳回值。注意:當你使用了這個選項以後,你必須使用同一參數重新建構所有的子產品,包括所有的 庫。

-mmmx

-msse

-msse2

-msse3

-m3dnow

-mssse3(沒寫錯!GCC-4.3新增)

-msse4.1(GCC-4.3新增)

-msse4.2(GCC-4.3新增)

-msse4(含4.1和4.2,GCC-4.3新增)

是否使用相應的擴充指令集以及内置函數,按照自己的cpu選擇吧!

-maccumulate-outgoing-args

指定在函數引導段中計算輸出參數所需最大空間,這在大部分現代cpu中是較快的方法;缺點是會明顯增加二進制檔案尺寸。

-mthreads

支援Mingw32的線程安全異常處理。對于依賴于線程安全異常處理的程式,必須啟用這個選項。使用這個選項時會定義”-D_MT”,它将包含使用 選項”-lmingwthrd”連接配接的一個特殊的線程輔助庫,用于為每個線程清理異常處理資料。

-minline-all-stringops

預設時GCC隻将确定目的地會被對齊在至少4位元組邊界的字元串操作内聯程序式代碼。該選項啟用更多的内聯并且增加二進制檔案的體積,但是可以提升依 賴于高速 memcpy, strlen, memset 操作的程式的性能。

-minline-stringops-dynamically

GCC-4.3新增。對未知尺寸字元串的小塊操作使用内聯代碼,而對大塊操作仍然調用庫函數,這是比”-minline-all- stringops”更聰明的政策。決定政策的算法可以通過”-mstringop-strategy”控制。

-momit-leaf-frame-pointer

不為葉子函數在寄存器中儲存棧指針,這樣可以節省寄存器,但是将會使調試變的困難。注意:不要與 -fomit-frame-pointer 同時使用,因為會造成代碼效率低下。

-m64

生成專門運作于64位環境的代碼,不能運作于32位環境,僅用于x86_64[含EMT64]環境。

-mcmodel=small

[預設值]程式和它的符号必須位于2GB以下的位址空間。指針仍然是64位。程式可以靜态連接配接也可以動态連接配接。僅用于x86_64[含EMT64] 環境。

-mcmodel=kernel

-mcmodel=medium

程式必須位于2GB以下的位址空間,但是它的符号可以位于任何位址空間。程式可以靜态連接配接也可以動态連接配接。注意:共享庫不能使用這個選項編譯!僅用 于x86_64[含EMT64]環境。

其它優化選項:

-fforce-addr

必須将位址複制到寄存器中才能對他們進行運算。由于所需位址通常在前面已經加載到寄存器中了,是以這個選項可以改進代碼。

-finline-limit=n

對僞指令數超過n的函數,編譯程式将不進行内聯展開,預設為600。增大此值将增加編譯時間和編譯記憶體用量并且生成的二進制檔案體積也會變大,此值 不宜太大。

-fmerge-all-constants

試圖将跨編譯單元的所有常量值和數組合并在一個副本中。但是标準C/C++要求每個變量都必須有不同的存儲位置,是以該選項可能會導緻某些不相容的 行為。

-fgcse-sm

在全局公共子表達式消除之後運作存儲移動,以試圖将存儲移出循環。gcc-3.4中曾屬于”-O2″級别的選項。

-fgcse-las

在全局公共子表達式消除之後消除多餘的在存儲到同一存儲區域之後的加載操作。gcc-3.4中曾屬于”-O2″級别的選項。

-floop-optimize

已廢除(GCC-4.1曾包含在”-O1″中)。

-floop-optimize2

使用改進版本的循環優化器代替原來”-floop-optimize”。該優化器将使用不同的選項(-funroll-loops, -fpeel-loops, -funswitch-loops, -ftree-loop-im)分别控制循環優化的不同方面。目前這個新版本的優化器尚在開發中,并且生成的代碼品質并不比以前的版本高。已廢除,僅存在 于GCC-4.1之前的版本中。

-funsafe-loop-optimizations

假定循環不會溢出,并且循環的退出條件不是無窮。這将可以在一個比較廣的範圍内進行循環優化,即使優化器自己也不能斷定這樣做是否正确。

-fsched-spec-load

允許一些裝載指令執行一些投機性的動作。

-ftree-loop-linear

在trees上進行線型循環轉換。它能夠改進緩沖性能并且允許進行更進一步的循環優化。

-fivopts

在trees上執行歸納變量優化。

-ftree-vectorize

在trees上執行循環向量化。

-ftracer

執行尾部複制以擴大超級塊的尺寸,它簡化了函數控制流,進而允許其它的優化措施做的更好。據說挺有效。

-funroll-loops

僅對循環次數能夠在編譯時或運作時确定的循環進行展開,生成的代碼尺寸将變大,執行速度可能變快也可能變慢。

-fprefetch-loop-arrays

生成數組預讀取指令,對于使用巨大數組的程式可以加快代碼執行速度,适合資料庫相關的大型軟體等。具體效果如何取決于代碼。

-fweb

建立經常使用的緩存器網絡,提供更佳的緩存器使用率。gcc-3.4中曾屬于”-O3″級别的選項。

-ffast-math

違反IEEE/ANSI标準以提高浮點數計算速度,是個危險的選項,僅在編譯不需要嚴格遵守IEEE規範且浮點計算密集的程式考慮采用。

-fsingle-precision-constant

将浮點常量作為單精度常量對待,而不是隐式地将其轉換為雙精度。

-fbranch-probabilities

在使用 -fprofile-arcs 選項編譯程式并執行它來建立包含每個代碼塊執行次數的檔案之後,程式可以利用這一選項再次編譯,檔案中所産生的資訊将被用來優化那些經常發生的分支代碼。 如果沒有這些資訊,gcc将猜測那一分支可能經常發生并進行優化。這類優化資訊将會存放在一個以源檔案為名字的并以”.da”為字尾的檔案中。

-frename-registers

試圖驅除代碼中的假依賴關系,這個選項對具有大量寄存器的機器很有效。gcc-3.4中曾屬于”-O3″級别的選項。

-fbranch-target-load-optimize

-fbranch-target-load-optimize2

在執行序啟動以及結尾之前執行分支目标緩存器加載最佳化。

-fstack-protector

在關鍵函數的堆棧中設定保護值。在傳回位址和傳回值之前,都将驗證這個保護值。如果出現了緩沖區溢出,保護值不再比對,程式就會退出。程式每次運 行,保護值都是随機的,是以不會被遠端猜出。

-fstack-protector-all

同上,但是在所有函數的堆棧中設定保護值。

–param max-gcse-memory=xxM

執行GCSE優化使用的最大記憶體量(xxM),太小将使該優化無法進行,預設為50M。

–param max-gcse-passes=n

執行GCSE優化的最大疊代次數,預設為 1。

傳遞給彙編器的選項:

-Wa,options

options是一個或多個由逗号分隔的可以傳遞給彙編器的選項清單。其中的每一個均可作為指令行選項傳遞給彙編器。

-Wa,–strip-local-absolute

從輸出符号表中移除局部絕對符号。

-Wa,-R

合并資料段和正文段,因為不必在資料段和代碼段之間轉移,是以它可能會産生更短的位址移動。

-Wa,–64

設定字長為64bit,僅用于x86_64,并且僅對ELF格式的目标檔案有效。此外,還需要使用”–enable-64-bit-bfd”選項 編譯的BFD支援。

-Wa,-march=CPU

按照特定的CPU進行優化:pentiumiii, pentium4, prescott, nocona, core, core2; athlon, sledgehammer, opteron, k8 。

僅可用于 CFLAGS 的選項:

-fhosted

按宿主環境編譯,其中需要有完整的标準庫,入口必須是main()函數且具有int型的傳回值。核心以外幾乎所有的程式都是如此。該選項隐含設定了 -fbuiltin,且與 -fno-freestanding 等價。

-ffreestanding

按獨立環境編譯,該環境可以沒有标準庫,且對main()函數沒有要求。最典型的例子就是作業系統核心。該選項隐含設定了 -fno-builtin,且與 -fno-hosted 等價。

僅可用于 CXXFLAGS 的選項:

-fno-enforce-eh-specs

C++标準要求強制檢查異常違例,但是該選項可以關閉違例檢查,進而減小生成代碼的體積。該選項類似于定義了”NDEBUG”宏。

-fno-rtti

如果沒有使用’dynamic_cast’和’typeid’,可以使用這個選項禁止為包含虛方法的類生成運作時表示代碼,進而節約空間。此選項對 于異常處理無效(仍然按需生成rtti代碼)。

-ftemplate-depth-n

将最大模版執行個體化深度設為’n',符合标準的程式不能超過17,預設值為500。

-fno-optional-diags

禁止輸出診斷消息,C++标準并不需要這些消息。

-fno-threadsafe-statics

GCC自動在通路C++局部靜态變量的代碼上加鎖,以保證線程安全。如果你不需要線程安全,可以使用這個選項。

-fvisibility-inlines-hidden

預設隐藏所有内聯函數,進而減小導出符号表的大小,既能縮減檔案的大小,還能提高運作性能,我們強烈建議你在編譯任何共享庫的時候使用該選項。參見 -fvisibility=hidden 選項。

LDFLAGS 是傳遞給連接配接器的選項。這是一個常被忽視的變量,事實上它對優化的影響也是很明顯的。

[提示]以下選項是在完整的閱讀了ld-2.18文檔之後挑選出來的選項。 http://blog.chinaunix.net/u1/41220/showart_354602.html 有2.14版本的中文手冊。

-s

删除可執行程式中的所有符号表和所有重定位資訊。其結果與運作指令 strip 所達到的效果相同,這個選項是比較安全的。

-Wl,options

options是由一個或多個逗号分隔的傳遞給連結器的選項清單。其中的每一個選項均會作為指令行選項提供給連結器。

-Wl,-On

當n>0時将會優化輸出,但是會明顯增加連接配接操作的時間,這個選項是比較安全的。

-Wl,–exclude-libs=ALL

不自動導出庫中的符号,也就是預設将庫中的符号隐藏。

-Wl,-m<emulation>

仿真<emulation>連接配接器,目前ld所有可用的仿真可以通過”ld -V”指令擷取。預設值取決于ld的編譯時配置。

-Wl,–sort-common

把全局公共符号按照大小排序後放到适當的輸出節,以防止符号間因為排布限制而出現間隙。

-Wl,-x

删除所有的本地符号。

-Wl,-X

删除所有的臨時本地符号。對于大多數目标平台,就是所有的名字以’L'開頭的本地符号。

-Wl,-zcomberloc

組合多個重定位節并重新排布它們,以便讓動态符号可以被緩存。

-Wl,–enable-new-dtags

在ELF中建立新式的”dynamic tags”,但在老式的ELF系統上無法識别。

-Wl,–as-needed

移除不必要的符号引用,僅在實際需要的時候才連接配接,可以生成更高效的代碼。

-Wl,–no-define-common

限制對普通符号的位址配置設定。該選項允許那些從共享庫中引用的普通符号隻在主程式中被配置設定位址。這會消除在共享庫中的無用的副本的空間,同時也防止了 在有多個指定了搜尋路徑的動态子產品在進行運作時符号解析時引起的混亂。

-Wl,–hash-style=gnu

使用gnu風格的符号散清單格式。它的動态連結性能比傳統的sysv風格(預設)有較大提升,但是它生成的可執行程式和庫與舊的Glibc以及動态 連結器不相容。

最後說兩個與優化無關的系統環境變量,因為會影響GCC編譯程式的方式,下面兩個是咱中國人比較關心的:

LANG

指定編譯程式使用的字元集,可用于建立寬字元檔案、串文字、注釋;預設為英文。[目前隻支援日文"C-JIS,C-SJIS,C-EUCJP",不 支援中文]

LC_ALL

指定多位元組字元的字元分類,主要用于确定字元串的字元邊界以及編譯程式使用何種語言發出診斷消息;預設設定與LANG相同。中文相關的幾 項:”zh_CN.GB2312 , zh_CN.GB18030 , zh_CN.GBK , zh_CN.UTF-8 , zh_TW.BIG5″。

繼續閱讀