GCC 簡介請見 GCC Toulibre 20091216。
請見 Installing GCC。安裝 GCC 之前可能需要安裝以下函式庫 (詳細請見 Prerequisites for GCC):
$ wget ftp://ftp.gnu.org/gnu/gcc/gcc-4.6.0/gcc-4.6.0.tar.gz; tar xvf gcc-4.6.0.tar.gz; $ cd gcc-4.6.0 $ wget ftp://ftp.gmplib.org/pub/gmp-5.0.2/gmp-5.0.2.tar.bz2; tar xvf gmp-5.0.2.tar.bz2 $ mv gmp-5.0.2 gmp $ wget http://www.mpfr.org/mpfr-current/mpfr-3.0.1.tar.gz; tar xvf mpfr-3.0.1.tar.gz $ mv mpfr-3.0.1 mpfr $ wget http://www.multiprecision.org/mpc/download/mpc-0.9.tar.gz; tar xvf mpc-0.9.tar.gz $ mv mpc-0.9 mpc # 使用 gcc -v 觀察系統 gcc 編譯時的參數 $ mkdir install build; cd build $ ../gcc-4.6.0/configure --prefix=$INSTALL \ --enable-languages=c,c++
請見 An Introduction to GCC、The Definitive Guide to GCC和 Using the GNU Compiler Collection。 gcc 本身是一個驅動器 (driver),它會依序呼叫其它元件例如: cpp (C 前處理器)、cc1 (C 編譯器) 進行編譯。collect2 蒐集資訊交給 ld。
$ /usr/libexec/cc1 --help
指定共享函式庫的路徑。假設共享函式庫為 /path/to/lib/libFoo.so。
# -Wl 把後面參數傳遞給鏈結器 $ gcc -Wl,-rpath /path/to/lib -L /path/to/lib -lFoo example.c # 查詢優化選項。 $ gcc -Q --help=optimizers -O1 # 查詢平台特定的選項。 $ gcc -Q --target-help # 查詢 GCC 是否有支援特定 -Wno 選項。 $ echo xx | gcc -Wno-initializer-overrides -x c - -c 2>&1 | grep 'unrecognized command line option'
交叉編譯的介紹請見 GCC Configuration and Building 和 GCC for Cross Compilation。crosstool-NG和 crossdev 可用來協助建立交叉工具鍊。後者需要 root 權限。有問題可寄信至 crossgcc 郵件列表。
$ wget http://ftp.gnu.org/pub/gnu/gperf/gperf-3.0.4.tar.gz; tar xvf gperf-3.0.4.tar.gz $ cd gperf-3.0.4 $ ./configure --prefix=$INSTALL $ make; make install $ export PATH=$INSTALL:$PATH
$ $ wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.16.0.tar.bz2; tar xvf crosstool-ng-1.16.0.tar.bz2 $ cd crosstool-ng-1.11.4 $ ./configure --prefix=$INSTALL $ make install
$ mkdir toolchain-build; cd toolchain-build $ cp $INSTALL/lib/ct-ng-1.11.4/samples/arm-unknown-linux-gnueabi/* . $ mv crosstool.config .config # 於 Paths and misc options 中調整最大可工作數量,加快工具鏈的生成。 $ ct-ng menuconfig $ ct-ng build $ cd $HOME/x-tools/
交叉編譯有底下術語:
請見使用gcov完成代码覆盖率的测试 和 gcov - a Test Coverage Program。
# 產生 instrumented 代碼 $ gcc -fprofile-generate hello.c # 產生 profiling data $./a.out # 使用 profiling data 重新編譯 $ gcc -fprofile-use hello.c
.gcda
後綴的檔案其格式定義在 gcc/gcov-io.h
裡面 (10.4 Brief description of gcov data files)。
Link Time Optimization (LTO) 的簡介請見 Link Time Optimization,細節請見 24 Link Time Optimization。Using the GNU Compiler Collection 也有詳細解釋。
$ ../gcc-4.6.0/configure --prefix=$INSTALL --enable-languages=c,c++ --enable-lto; make; make install $ gcc -o myprog -flto -O2 foo.c bar.c # -fuse-linker-plugin 需要 GNU ld 2.21 以後或是 gold $ gcc -o myprog -O2 -flto -fuse-linker-plugin a.o b.o -lfoo
$ wget http://ftp.gnu.org/gnu/binutils/binutils-2.21.tar.gz; tar xvf binutils-2.21.tar.gz $ mkdir build install; cd build $ ../binutils-2.21/configure --prefix=$INSTALL --enable-gold=yes
# svn checkout svn://gcc.gnu.org/svn/gcc/trunk gcc $ git clone git://gcc.gnu.org/git/gcc.git $ make STAGE1_CFLAGS="-g3 -O0" all-stage1
An overview of compilation and GCC 中會見到 m/c Ind,這代表與機器無關 (machine code independent)。
gcc
: 主要目錄。gcc/config
: 各平台相關檔案。gcc/LANG
和 gcc/libLANG
: 前端相關檔案。GENERIC → GIMPLE → Tree SSA → RTL
First level gray box probing of GCC。所謂 gray box probing 是指對系統有基本的認識,透過觀察結合我們對該系統的基本認識進而推論其運作流程 3)。
# 列出所有優化。 $ gcc -c --help=optimizers # 列出 -O2 開啟/關閉的優化。 $ gcc -c -O2 --help=optimizers -Q # ir 可以是 tree、ipa 或是 rtl。pass 若為 all,則會輸出該 ir 的所有傾印檔。 $ gcc -fdump-<ir>-<passname> $ gcc -fdump-tree-all test.c # 觀察 test.c.004t.gimple。
int a; int main() { int x = 10; int y = 5; x = a + x * y; y = y - a * x; } main () { int D.1587; int a.0; int a.1; int D.1590; int x; int y; x = 10; y = 5; D.1587 = x * y; a.0 = a; // 先將內存搬至暫存。 x = D.1587 + a.0; a.1 = a; D.1590 = a.1 * x; y = y - D.1590; }
int main() { int a[3], x; a[1] = a[2] = 10; x = a[1] + a[2]; a[0] = a[1] + a[1] * x; } main () { int D.1586; int D.1587; int D.1588; int D.1589; int D.1590; int D.1591; int a[3]; int x; a[2] = 10; D.1586 = a[2]; a[1] = D.1586; D.1587 = a[1]; D.1588 = a[2]; x = D.1587 + D.1588; D.1589 = x + 1; D.1590 = a[1]; D.1591 = D.1589 * D.1590; // a[1] + a[1] * x == a[1] * (1 + x) a[0] = D.1591; }
int main() { int **a,*b,c; b = &c; a = &b; **a = 10; /* c = 10 */ } main () { int * D.1587; int * * a; int * b; int c; b = &c; a = &b; D.1587 = *a; *D.1587 = 10; }
struct
typedef struct address { char *name; } ad; typedef struct student { int roll; ad *ct; } st; int main() { st *s; s = malloc(sizeof(st)); s->roll = 1; s->ct = malloc(sizeof(ad)); s->ct->name = "Mumbai"; } main () { void * D.1593; struct ad * D.1594; struct st * s; extern void * malloc (long unsigned int); s = malloc (16); s->roll = 1; D.1593 = malloc (8); // s->ct = malloc(sizeof(ad)); s->ct = D.1593; D.1594 = s->ct; // s->ct->name = "Mumbai"; D.1594->name = "Mumbai"; }
比較 test.c.004t.gimple
和 test.c.013t.cfg
。
while
和 if
述句。int main() { int a = 2, b = 3, c = 4; while (a <= 7) { a = a+1; } if (a <= 12) a = a + b + c; } main () { int D.1592; int a; int b; int c; a = 2; b = 3; c = 4; goto <D.1587>; <D.1586>: a = a + 1; <D.1587>: if (a <= 7) goto <D.1586>; else goto <D.1588>; <D.1588>: if (a <= 12) goto <D.1590>; else goto <D.1591>; <D.1590>: D.1592 = a + b; a = D.1592 + c; <D.1591>: } main () { int c; int b; int a; int D.1592; <bb 2>: a = 2; b = 3; c = 4; goto <bb 4>; <bb 3>: a = a + 1; <bb 4>: if (a <= 7) goto <bb 3>; else goto <bb 5>; <bb 5>: if (a <= 12) goto <bb 6>; else goto <bb 7>; <bb 6>: D.1592 = a + b; a = D.1592 + c; <bb 7>: return; }
$ gcc -fdump-ipa-cgraph -S test.c $ less test.c.000i.cgraph
extern int divide(int, int); int multiply(int a, int b) { return a*b; } int main() { int x,y; x = divide(20, 5); y = multiply(x, 2); printf("%d\n", y); } printf/3(-1) @0x801e0c9a0 // 3 代表此為進入點之底幾個函式。-1 代表此為外部函式,尚待解析。 called by: main/1 (1.00 per call) calls: References: Refering this function: divide/2(-1) @0x801e0c840 called by: main/1 (1.00 per call) calls: References: Refering this function: main/1(1) @0x801e0c6e0 (asm: main) analyzed needed reachable body finalized called by: calls: printf/3 (1.00 per call) multiply/0 (1.00 per call) divide/2 (1.00 per call) References: Refering this function: multiply/0(0) @0x801e0c580 (asm: multiply) analyzed needed reachable body finalized called by: main/1 (1.00 per call) calls: References: Refering this function: variable pool:
$ gcc -fdump-rtl-all test.c $ less test.c.144r.expand
int main() { int a = 0; a = a + 1; } // int a = 0; (insn 5 4 6 3 (set (mem/c/i:SI (plus:DI (reg/f:DI 54 virtual-stack-vars) (const_int -4 [0xfffffffffffffffc])) [0 a+0 S4 A32]) (const_int 0 [0])) test.c:2 -1 (nil)) // a = a + 1; // // 6: 當前指令。 // 5: 前一條指令。 // 12: 後一條指令。 // 3: 所處的 basic block。 // // parallel 表示之後的運算是同時進行。 // set 即是賦值運算。 (insn 6 5 12 3 (parallel [ (set (mem/c/i:SI (plus:DI (reg/f:DI 54 virtual-stack-vars) (const_int -4 [0xfffffffffffffffc])) [0 a+0 S4 A32]) (plus:SI (mem/c/i:SI (plus:DI (reg/f:DI 54 virtual-stack-vars) (const_int -4 [0xfffffffffffffffc])) [0 a+0 S4 A32]) (const_int 1 [0x1]))) (clobber (reg:CC 17 flags)) ]) test.c:3 -1 // 檔案名稱:行數 (nil))
(clobber (reg:CC 17 flags))
代表 a = a + 1
有可能會改到 (clobber) condition code。int a = 0; int main() { a = a + 1; } // load a into reg59 (insn 5 4 6 3 (set (reg:SI 59 [ a.0 ]) (mem/c/i:SI (symbol_ref:DI ("a") [flags 0x2] <var_decl 0x801f23000 a>) [0 a+0 S4 A32])) test.c:4 -1 (nil)) // set reg60 = reg59 + 1 (insn 6 5 7 3 (parallel [ (set (reg:SI 60 [ a.1 ]) (plus:SI (reg:SI 59 [ a.0 ]) (const_int 1 [0x1]))) (clobber (reg:CC 17 flags)) ]) test.c:4 -1 (nil)) // store reg60 into a (insn 7 6 13 3 (set (mem/c/i:SI (symbol_ref:DI ("a") [flags 0x2] <var_decl 0x801f23000 a>) [0 a+0 S4 A32]) (reg:SI 60 [ a.1 ])) test.c:4 -1 (nil))
void foo(int a) { a = a + 1; } (insn 6 5 7 3 (parallel [ (set (mem/c/i:SI (plus:DI (reg/f:DI 54 virtual-stack-vars) (const_int -4 [0xfffffffffffffffc])) [0 a+0 S4 A32]) (plus:SI (mem/c/i:SI (plus:DI (reg/f:DI 54 virtual-stack-vars) (const_int -4 [0xfffffffffffffffc])) [0 a+0 S4 A32]) (const_int 1 [0x1]))) (clobber (reg:CC 17 flags)) ]) test.c:2 -1 (nil))
GCC 內部運作流程請見 GCC Control Flow and Plugins。GCC 解析命令行參數請見 gcc.c
和 toplev.c
。
main
(main.c
) 調用 toplev_main
。int main (int argc, char **argv) { return toplev_main (argc, argv); }
toplev_main
(gcc/toplev.c
)。int toplev_main (int argc, char **argv) { ... 略 ... // 進行編譯。 if (!exit_after_options) do_compile (); ... 略 ... }
do_compile
(gcc/toplev.c
) 。static void do_compile (void) { ... 略 ... /* Language-dependent initialization. Returns true on success. */ if (lang_dependent_init (main_input_filename)) { ... 略 ... // 編譯檔案。 compile_file (); } ... 略 ... }
compile_file
(gcc/toplev.c
) 編譯檔案 (translation unit)。static void compile_file (void) { ... 略 ... // 不同語言會定義不同的 lang_hooks,以便在此調用對應的 parser。 lang_hooks.parse_file (); ... 略 ... }
c-objc-common.h
定義其 parser 為 c_common_parse_file
(c-family/c-opts.c
)。#undef LANG_HOOKS_PARSE_FILE #define LANG_HOOKS_PARSE_FILE c_common_parse_file void c_common_parse_file (void) { for (;;) { c_finish_options (); pch_init (); push_file_scope (); // 調用此函式開始解析。 c_parse_file (); pop_file_scope (); ... 略 ... } }
c_parse_file
(c-parser.c
)。void c_parse_file (void) { c_parser_translation_unit (the_parser); }
c_write_global_declarations
(c-decl.c
) void c_write_global_declarations (void) { // AST tree t; FOR_EACH_VEC_ELT (tree, all_translation_units, i, t) c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t))); c_write_global_declarations_1 (BLOCK_VARS (ext_block)); finalize_compilation_unit (); ... 略 ... }
GCC 內部所有的轉換和優化過程皆以 Pass 稱之,請見 Manipulating GIMPLE and RTL IRs 和 9 Passes and Files of the Compiler。主要檔案為 gcc/passes.c
。請見 gcc/tree-pass.h
中的 struct opt_pass
。gcc/basic-block.h
定義了處理基本塊的巨集。Pass 基本上分為 GIMPLE 和 RTL pass。
init_optimization_passes
(gcc/passes.c
) 排定優化序列。void init_optimization_passes (void) { struct opt_pass **p; #define NEXT_PASS(PASS) (p = next_pass_1 (p, &((PASS).pass))) /* All passes needed to lower the function into shape optimizers can operate on. These passes are always run first on the function, but backend might produce already lowered functions that are not processed by these passes. */ /* Interprocedural optimization passes. */ ... 略 ... }
檢視 RTL 語句請見 First Level Gray Box Probing。
-da
可以傾印 RTL 级别每个 pass 的 dump 文件。
# -Wa 代表之後的參數餵給匯編器。 $ gcc -da -Wa,--keep-locals hello.c $ objdump -t a.out 0000000000000000 l df *ABS* 0000000000000000 hello.c 00000000004004e4 l .text 0000000000000000 .LFB0 00000000004004ea l .text 0000000000000000 .LFE0 00000000004004ea l .text 0000000000000000 .LFB1 00000000004004fa l .text 0000000000000000 .LFE1
–keep-locals
大致可代表基本塊的位置4)。關於 CFG 的討論請見 Generate annotations for a binary translator5)。
cfgloop.*
是整個過程中會用到。tree-ssa-loop.*
處理 GIMPLE。loop-.*
處理 RTL。
predict.*
GCC 有關 OpenMP 的文件請見 GNU libgomp 和 OpenMP。源碼請見 gcc/omp-low.c
和 libgomp/
。文件不見得符合現狀,例如: OpenMP loop implementation。
請見 23 Plugins 和 plugins。 編譯開啟插件功能的 GCC。
$ wget http://mirrors.kernel.org/gnu/gcc/gcc-4.6.0/gcc-4.6.0.tar.gz; tar xvf gcc-4.6.0.tar.gz $ mkdir install build; cd build $ ../gcc-4.6.0/configure --prefix=$INSTALL --enable-languages=c,c++ --enable-plugin --enable-lto $ make && make install
gcc-vcg-plugin 是一個 gcc 插件,用來在運行 gdb 執行 gcc 時,傾印 gcc 內部資料結構。其它介紹請見 gcc-vcg-plugin : 視覺化 GCC 內部結構。
$ wget http://gcc-vcg-plugin.googlecode.com/files/gcc-vcg-plugin-1.1.tar.gz; tar xvf gcc-vcg-plugin-1.1.tar.gz $ ../gcc-vcg-plugin-1.1/configure --prefix=$INSTALL $ GCC=/path/to/just_installed_gcc make $ wget http://vcgviewer.googlecode.com/files/vcgview-2.0.tar.gz; tar xvf vcgview-2.0.tar.gz $ cd vcgview-2.0; ./autogen.sh $ cd ../; mkdir install build; cd build $ ../vcgview-2.0/configure --prefix=/tmp/chenwj/install
$ git clone http://git.fedorahosted.org/git/gcc-python-plugin.git
GTY 是 gcc 內部的內存管理器。
$ ./cloc --force-lang="C",c --by-file-by-lang --csv folder/
-Ohs
和 -O3
比較。-Ohz
和 -Os
比較。-fgcse
-funroll-loops
-finline-functions
-fmove-loop-invariants
-fstrict-aliasing
-fschedule-insns
–fshort-enums
–fno-jump-tables