注意! 優化選項 -O 等同於 -O1。

GCC 簡介請見 GCC Toulibre 20091216

安裝 GCC

請見 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++

使用 GCC

請見 An Introduction to GCCThe Definitive Guide to GCCUsing 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 BuildingGCC for Cross Compilationcrosstool-NGcrossdev 可用來協助建立交叉工具鍊。後者需要 root 權限。有問題可寄信至 crossgcc 郵件列表。

  1. 安裝 gperf。
    $ 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
  2. 下載並編譯 crosstool-ng。
    $ $ 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 
  3. 建置交叉工具鍊。
    $ 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/

交叉編譯有底下術語:

  • host: 運行交叉工具鏈的平台。
  • build: 編譯交叉工具鏈的平台。
  • target: 運行交叉工具鏈得到的執行檔,其運行的平台。

反馈制导优化

請見使用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 OptimizationUsing 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
  1. 編譯 gold。
    $ 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

Extension

Q & A

Internal

# 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/LANGgcc/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.gimpletest.c.013t.cfg

  • whileif 述句。
    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:

RTL

$ gcc -fdump-rtl-all test.c
$ less test.c.144r.expand
  1. 存取本地變數。
    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。
  2. 存取全域變數。
    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))
  3. 存取實參。
    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 內部運作流程請見 GCC Control Flow and Plugins。GCC 解析命令行參數請見 gcc.ctoplev.c

  1. main (main.c) 調用 toplev_main
    int
    main (int argc, char **argv)
    {
      return toplev_main (argc, argv);
    }
  2. 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 ();
       
       
        ... 略 ...
      }
  3. 以 C 語言為例,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);
      }
  4. 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 IRs9 Passes and Files of the Compiler。主要檔案為 gcc/passes.c。請見 gcc/tree-pass.h 中的 struct opt_passgcc/basic-block.h 定義了處理基本塊的巨集。Pass 基本上分為 GIMPLE 和 RTL pass。

  1. 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

傾印內部資訊

GCC 自帶選項

-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)

Loop

cfgloop.* 是整個過程中會用到。tree-ssa-loop.* 處理 GIMPLE。loop-.* 處理 RTL。

分支預測

predict.*

OpenMP

GCC 有關 OpenMP 的文件請見 GNU libgompOpenMP。源碼請見 gcc/omp-low.clibgomp/。文件不見得符合現狀,例如: OpenMP loop implementation

插件

請見 23 Pluginsplugins。 編譯開啟插件功能的 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

Submitted Patch

其它

外部連結

資源中心

文章

其它

登录