GCC源代碼分析(1): GCC中的樹

    本文分析GCC4.3.1的源代碼。如某位牛人所說,我並不打算做“參考手冊”式的源碼分
析,而是打算做“航海日誌”式的。

* GCC中的樹
    對於GCC的前端和高級的分析和及優化而言,樹是其核心的數據結構。經過語法分析的
源程序都表示爲樹的形式。這裏要說一下,GCC中,實際上有三種樹:GENERIC,GIMPLE和SS
A。
    GENERIC:語言無關的一種通用表示,源程序經過前端的語法分析後,會被轉化成這種
形式。其實,這種形式中是可能有與語言相關的節點的,對於這樣的節點,前端要負責提供
一個將其轉化成GIMPLE的函數。幾乎沒有什麼分析和優化是在這個中間表示上進行的。
    GIMPLE:是GENERIC加了某些限制的簡化版本。GENERIC表示的程序,會被轉化成GIMPLE
表示的,這個是真正的語言無關的表示。有一些分析和優化是在這個表示上進行的。
    SSA:SSA是另外一種語言無關的中間表示,由GIMPLE轉化來,大量的分析和優化在其上
進行。

    首先,我們看一下,gcc目錄下的那些文件名中包含"tree":
%{
ChangeLog.tree-ssa  tree-nomudflap.c           tree-ssa-loop-niter.c
c-tree.h            tree-nrv.c                 tree-ssa-loop-prefetch.c
print-tree.c        tree-object-size.c         tree-ssa-loop-unswitch.c
tree-affine.c       tree-optimize.c            tree-ssa-math-opts.c
tree-affine.h       tree-outof-ssa.c           tree-ssanames.c
tree-browser.c      tree-parloops.c            tree-ssa-operands.c
tree-browser.def    tree-pass.h                tree-ssa-operands.h
tree.c              tree-phinodes.c            tree-ssa-phiopt.c
tree-cfg.c          tree-predcom.c             tree-ssa-pre.c
tree-cfgcleanup.c   tree-pretty-print.c        tree-ssa-propagate.c
tree-chrec.c        tree-profile.c             tree-ssa-propagate.h
tree-chrec.h        tree-scalar-evolution.c    tree-ssa-reassoc.c
tree-complex.c      tree-scalar-evolution.h    tree-ssa-sccvn.c
tree-data-ref.c     tree-sra.c                 tree-ssa-sccvn.h
tree-data-ref.h     tree-ssa-address.c         tree-ssa-sink.c
tree.def            tree-ssa-alias.c           tree-ssa-structalias.c
tree-dfa.c          tree-ssa-alias-warnings.c  tree-ssa-structalias.h
tree-dump.c         tree-ssa.c                 tree-ssa-ter.c
tree-dump.h         tree-ssa-ccp.c             tree-ssa-threadedge.c
tree-eh.c           tree-ssa-coalesce.c        tree-ssa-threadupdate.c
tree-flow.h         tree-ssa-copy.c            tree-ssa-uncprop.c
tree-flow-inline.h  tree-ssa-copyrename.c      tree-stdarg.c
tree-gimple.c       tree-ssa-dce.c             tree-stdarg.h
tree-gimple.h       tree-ssa-dom.c             treestruct.def
tree.h              tree-ssa-dse.c             tree-tailcall.c
tree-if-conv.c      tree-ssa-forwprop.c        tree-vect-analyze.c
tree-inline.c       tree-ssa-ifcombine.c       tree-vect-generic.c
tree-inline.h       tree-ssa-live.c            tree-vectorizer.c
tree-into-ssa.c     tree-ssa-live.h            tree-vectorizer.h
tree-iterator.c     tree-ssa-loop.c            tree-vect-patterns.c
tree-iterator.h     tree-ssa-loop-ch.c         tree-vect-transform.c
tree-loop-linear.c  tree-ssa-loop-im.c         tree-vn.c
tree-mudflap.c      tree-ssa-loop-ivcanon.c    tree-vrp.c
tree-mudflap.h      tree-ssa-loop-ivopts.c
tree-nested.c       tree-ssa-loop-manip.c
}%
    ...居然有這麼多。好吧,仔細看一下,其實有很多以tree開始的文件都是優化遍。我
們先來找到對樹的定義。其中有三個文件最爲可疑:tree.[h def c]。首先看看tree.h。
這個文件,從感覺上應該定義了樹的接口。該文件的第一行註釋是
%{
/* Front-end tree definitions for GNU compiler.
}%
看來是感覺對了。其他的註釋都是廢話。文件中的第一個比較重要的數據結構如下,它定義
了樹中可以出現的節點的編碼。
%{
/* Codes of tree nodes */

#define DEFTREECODE(SYM, STRING, TYPE, NARGS)   SYM,

enum tree_code {
#include "tree.def"

  LAST_AND_UNUSED_TREE_CODE /* A convenient way to get a value for
                   NUM_TREE_CODES.  */
};

#undef DEFTREECODE
}%
可以看到,這時用到了tree.def文件。這個文件原來是定義和樹的節點相關的信息的,使用
了通常的編程技巧,tree_code中僅僅使用了DEFTREECODE的第一個域,定義了表示這個節點
的常量編碼。我們看一下tree.def這個文件。第一行的註釋說的很清楚。
%{
/* This file contains the definitions and documentation for the
   tree codes used in GCC.
}%
根據名字,DEFTREECODE中四個域大概表示節點的枚舉常量名、字符串名、類型、參數(子節
點)的數目。在tree.def文件中,有更爲詳細的說明。對於類型爲 tcc_references,
tcc_expression, tcc_comparison, tcc_unary, tcc_binary, tcc_statement類型的節點,
(它們都用struct tree_exp來表示),第四個參數是要爲它們分配的參數的位置的個數。這
個同時也確定了樹節點的大小。其他類型的節點使用不同的結構體表示,其大小由一個
tree_union的成員結構體確定,第四個參數應該爲0。語言前端自己定義的節點類型如果是
tcc_exceptional 或 tcc_constant,此時,前端必須定義tree_size這個langhook來指出自
己定義的節點的大小。
在這裏,這些樹節點的定義順序是被仔細安排的,這樣,很多判斷可以變成範圍判斷。
tree.def裏面,詳細地給出了每一個樹節點的定義和文檔說明。這個文件完全可以當作樹的
參考手冊。這裏面的節點的定義,應該給出了GENERIC的定義,同時,由於GIMPLE是GENERIC
的一個受限制的子集,所以也給出了GIMPLE的定義。我們先繼續看tree.h,回頭再給出所有
節點的詳細說明(其實大部分也就是翻譯一下他們的註釋)。
%{
/* 這個是編譯器中最大可用的樹節點的種類的數目 */
#define MAX_TREE_CODES 512  
/* 這個定義在tree.c中,並在init_ttree()中進行了初始化。
 * 掩碼,用於表示一個樹節點(union tree_node)包含什麼。和下面的枚舉配合使用 */
extern unsigned char tree_contains_struct[MAX_TREE_CODES][64];
#define CODE_CONTAINS_STRUCT(CODE, STRUCT) (tree_contains_struct[(CODE)][(STRUCT)])

/* 下面文件中定義的每一個枚舉值都要對應union tree_node中的唯一一個成員。這些枚舉值用於區分
 * 聯合體的成員,用於垃圾收集以及用於在數組tree_contains_struct中指定每一類樹節點包含什麼
 * 結構 */
#define DEFTREESTRUCT(ENUM, NAME) ENUM,
enum tree_node_structure_enum {
#include "treestruct.def"
  LAST_TS_ENUM
};

/* 可見,在tree.def中定義的節點都是語言無關的*/
/* Number of language-independent tree codes.  */
#define NUM_TREE_CODES ((int) LAST_AND_UNUSED_TREE_CODE)
}%
剛纔說了,每一個樹節點都有一個類型,這裏就定義了可用的類型。
%{
/* Tree code classes.  */

/* Each tree_code has an associated code class represented by a
   TREE_CODE_CLASS.  */

enum tree_code_class {
  tcc_exceptional, /* 用於不能歸於其他類型的類型 */
  tcc_constant,    /* 常量類型 */
  /* 一下兩者的定義順序很重要 */
  tcc_type,        /* 用於表示“類型”的類型 */
  tcc_declaration, /* 聲明,同時也可以表示變量引用 */
  tcc_reference,   /* 對主存的引用,類似於指針 */
  tcc_comparison,  /* 比較表達式 */
  tcc_unary,       /* 一元算數表達式 */
  tcc_binary,      /* 二元算數表達式 */
  tcc_statement,   /* 語句表達式,它一般有副作用,並且我們不關心它自己的值 */
  tcc_vl_exp,      /* 有變長操作數向量的函數調用或是其它表達式 */
  tcc_expression,  /* 不能歸類於以上表達式的其他表達式 */
  tcc_gimple_stmt  /* 一個GIMPLE的語句,只有一個賦值節點是這個類型 */
};
}%
上面定義的每一種類型,都有一個對應的字符串表示,大概是用於打印輸出,便於閱讀。該
字符串表示在tree.h中聲明,在tree.c中定義。枚舉常量和字符串必須對應。
%{
const char *const tree_code_class_strings[] =
{
  "exceptional",
  "constant",
  "type",
  "declaration",
  "reference",
  "comparison",
  "unary",
  "binary",
  "statement",
  "vl_exp",
  "expression",
  "gimple_stmt"
};
/* 該宏用於返回參數對應的字符串 */
#define TREE_CODE_CLASS_STRING(CLASS)/
        tree_code_class_strings[(int) (CLASS)]
}%
既然每一個節點都有類型,就必然可以從節點的編碼查到它的類型,這個正是下面這個數組
完成的功能。
%{
extern const enum tree_code_class tree_code_type[];
#define TREE_CODE_CLASS(CODE)   tree_code_type[(int) (CODE)]
}%
這裏,只是聲明瞭這個數組,並給出了用於查詢的宏。對於不同的前端,這個數組有不同的
定義。這是由於,這個數組應該包含前端定義的所有的節點類型,不同的前端,可能有不同
的自定義節點。例如,在c的前端中,是這樣定義的(c-lang.c)。
%{
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE,

const enum tree_code_class tree_code_type[] = {
#include "tree.def"
  tcc_exceptional,
#include "c-common.def"
};
#undef DEFTREECODE
}%
同時,我們也發現,c前端自定義的樹節點在文件c-common.def中。在這之後,是用於節點
類型判斷的一系列的宏。
下面這個數組用於記錄查找每一種樹節點的“長度”,及參數個數,就是DEFTREECODE中最
後一個參數。基於同樣的原因,該數組在這裏也只有聲明,每個前端獨立定義自己的數組
%{
/* Number of argument-words in each kind of tree-node.  */

extern const unsigned char tree_code_length[];
#define TREE_CODE_LENGTH(CODE)  tree_code_length[(int) (CODE)]
}%
c的前端的定義如下(c-lang.c)
%{
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH,

const unsigned char tree_code_length[] = {
#include "tree.def"
  0,
#include "c-common.def"
};
#undef DEFTREECODE
}%
下面的數組定義了每一種樹節點的字符串名,定義方法同上。
%{
extern const char *const tree_code_name[];
}%
在c-lang.c中
%{
/* Names of tree components.
   Used for printing out the tree and error messages.  */
#define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME,

const char *const tree_code_name[] = {
#include "tree.def"
  "@@dummy",
#include "c-common.def"
};
#undef DEFTREECODE
}%
下面三行代碼定義了一個tree構成的vector。vec.h定義了一個vector模板,支持操作
vector。這裏就是用了這些接口。
%{
/* A vectors of trees.  */
DEF_VEC_P(tree); /* tree 是指向tree_node的指針類型,指出了vector的成員類型 */
DEF_VEC_ALLOC_P(tree,gc);
DEF_VEC_ALLOC_P(tree,heap);
}%
在接下來,tree.h中有一大段關於buildin函數的聲明。Note:暫時跳過。
分析從這裏繼續
%{
/* The definition of tree nodes fills the next several pages.  */
}%
一個樹節點可以代表一種數據類型,一個變量,一個表達式或一個語句。每一個節點都有一
個TREE_CODE表示它代表的對象。常見的包括INTEGER_TYPE表示整形,ARRAY_TYPE表示指針
類型(這個沒有錯嗎?),VAR_DECL表示一個聲明的變量,INTEGER_CST表示整型常量,
PLUS_EXPR表示一個加法表達式等。在不同的樹節點之間,有很多內容是一樣的,但是,每
一類樹節點也有自己獨有的一些內容。這些內容從不被直接訪問,都是通過某些宏進行的。
每一種樹節點都是以下面這個結構開始的
%{
/* In the following comments, expressions including decls*/
struct tree_base GTY(())
{
  /* 指出了該節點的節點編碼 */
  ENUM_BITFIELD(tree_code) code : 16;

  unsigned side_effects_flag : 1;
  unsigned constant_flag : 1;
/* 在VAR_CECL節點中,非0表示該節點需要地址,所以,不能只把它放在寄存器中。在
 * FUNCTION_DECL中,非0表示需要地址,所以該函數必須有獨立實體,儘管它可能是
 * inline函數。在FIELD_DECL中,非0表示允許程序員取該域的地址。在CONSTRUCTOR節點
 * 中,非0表示對象必須被構造在主存中。Note:在LABEL_DECL中的含義沒有看明白。在
 * xxx_TYPE節點中,非0表示該類型的對象必須是完整可尋址的。在INDENTFIER_NODE中
 * 非0表示該名字的某個外部聲明會取它的地址。在CALL_EXPR中,非0表示該調用是尾調用
 * 。在CASE_LABEL_EXPR中,非0表示CASE_LOW操作數已經被處理了。*/
  unsigned addressable_flag : 1;
  unsigned volatile_flag : 1;
  unsigned readonly_flag : 1;
  unsigned unsigned_flag : 1;
  unsigned asm_written_flag: 1;
  unsigned nowarning_flag : 1;

  unsigned used_flag : 1;
  unsigned nothrow_flag : 1;
/* 在VAR_DECL中,非0表示分配靜態存儲區。在FUNCTION_DECL中,非0表示函數已經被定義
 * 了。在CONSTRUCTOR中,非0表示分配靜態存儲區。在TARGET_EXPR和WITH_CLEANUP_EXPR
 * 中,非0表示相關的cleanup只在出現異常的時候執行,正常退出作用域時不執行。在
 * TRY_CATCH_EXPR中,非0表示應該將異常處理代碼看作honor_protect_cleanup_actions
 * 中獨立的cleanup函數。Note:
 * honor_protect_cleanup_actions是什麼?在CASE_LABEL_EXPR中表示CASE_HIGH操作數
 * 已經被處理了。在CALL_EXPR中表示該函數不適於inline....


 * Note:
 * 從這兩個標誌看出來,每一個標誌都被不同的節點用於不同的目的,對應於每一個目的
 * 都有一個宏來判斷(儘管它們檢查相同的標誌)。有些宏裏面還加入了check類的宏,來確
 * 保正確的訪問。鑑於太多了,就不一一寫出來了。註釋寫得挺好的。
 */
  unsigned static_flag : 1;
  unsigned public_flag : 1;
  unsigned private_flag : 1;
  unsigned protected_flag : 1;
  unsigned deprecated_flag : 1;
  unsigned invariant_flag : 1;
  unsigned saturating_flag : 1;

  unsigned lang_flag_0 : 1;
  unsigned lang_flag_1 : 1;
  unsigned lang_flag_2 : 1;
  unsigned lang_flag_3 : 1;
  unsigned lang_flag_4 : 1;
  unsigned lang_flag_5 : 1;
  unsigned lang_flag_6 : 1;
  unsigned visited : 1;

  unsigned spare : 23;

  /* FIXME tuples: Eventually, we need to move this somewhere external to
     the trees.  */
  union tree_ann_d *ann;
};
}%
一下的兩個樹定義是基於上面定義的特殊化的兩類樹節點。
%{
struct tree_common GTY(())
{
  struct tree_base base;
  tree chain;
  tree type;
};

/* GIMPLE_MODIFY_STMT */
struct gimple_stmt GTY(())
{
  struct tree_base base;
  source_locus locus;
  tree block;
  /* FIXME tuples: Eventually this should be of type ``struct gimple_expr''.  */
  tree GTY ((length ("TREE_CODE_LENGTH (TREE_CODE (&%h))"))) operands[1];
};
}%
接下來,有一大段在說明tree_base中的標誌對於哪些節點是有意義的。個人覺得,更後面
的訪問域的宏的定義看起來更清楚。tree_base中的註釋,是根據這些來的。
文件中定義了一大堆用於檢查樹節點屬性的宏,這些宏發現對樹節點的非法訪問的時候,就
會報告錯誤,並停止編譯。其中有一些註釋還是值得一看的。例如“將樹節點用鏈表穿起來
有很多用處。類型節點穿起來,用於記錄它們並輸出給調試器;同一個作用域中的聲明穿起來,用與記錄該作用域的內容;順序的語句節點會被穿起來;一般而言,鏈表使用
TREE_LIST節點來表示,而這種節點也是被穿起來的。”再之後,又是大量的宏,用於操作
樹節點。
下面是一些更加具體的樹節點的定義。
%{
/* 該節點表示整形常量,可以表示2字的常量 */
struct tree_int_cst GTY(())
{
  struct tree_common common;
  double_int int_cst; /* double_int 分爲high和low兩個部分,每部分都是機器字*/
};
/* 浮點常量,struct real_value定義在real.h中,表示一個浮點數*/
struct tree_real_cst GTY(())
{
  struct tree_common common;
  struct real_value * real_cst_ptr;
};
/* 定點常量,fixed_value中包含一個double_int和一個mode,在fixed-value中定義*/
struct tree_fixed_cst GTY(())
{
  struct tree_common common;
  struct fixed_value * fixed_cst_ptr;
};
/* 字符串常量 */
struct tree_string GTY(())
{
  struct tree_common common;
  int length;
  char str[1];
};
/* 複數常量*/
struct tree_complex GTY(())
{
  struct tree_common common;
  tree real;
  tree imag;
};
/* Vector常量*/
struct tree_vector GTY(())
{
  struct tree_common common;
  tree elements;
};
}%
另外還有一些特殊的樹節點也是類似定義的。另外一個比較重要的樹節點是表示表達式的樹
節點,定義如下。
%{
struct tree_exp GTY(())
{
  struct tree_common common;
  source_locus locus; /* 源文件中的位置 */
  tree block;         /* 所屬的塊 */
  tree GTY ((special ("tree_exp"),
         desc ("TREE_CODE ((tree) &%0)")))
    operands[1];      /* 操作數 */
};
}%
下面開始的一部分是用於操作SSA的宏。Note:關於SSA_NAME節點的定義沒有找到,但是這
種節點的確在tree.def中進行了聲明。在後面有一個結構體的聲明
%{
#ifndef _TREE_FLOW_H
struct ptr_info_def;
#endif
}%
這個結構定義在tree-flow.h中,用於記錄一些SSA結點的屬性,用於別名分析。遇到後再仔
細看它。
下面這個結構用於維護一個雙向鏈表,記錄了一個SSA_NAME的使用者。
%{
typedef struct ssa_use_operand_d GTY(())
{
  struct ssa_use_operand_d* GTY((skip(""))) prev;
  struct ssa_use_operand_d* GTY((skip(""))) next;
  /* Note:這兩個成員具體包含什麼信息?*/
  tree GTY((skip(""))) stmt;
  tree *GTY((skip(""))) use;
} ssa_use_operand_t;
}%
下面這個結構體很像是SSA_NAME的定義,但是卻沒有ssa_name這個域。根據上面的宏來看,
似乎應該有一個類型的樹節點,包含一個ssa_name的域,這個域的類型是struct
tree_ssa_name
%{
struct tree_ssa_name GTY(())
{
  struct tree_common common;

  /* 該SSA包裝(代表)的_DECL的對象(變量) */
  tree var;

  /* SSA 的版本號 */
  unsigned int version;

  /* 用於別名分析的屬性 */
  struct ptr_info_def *ptr_info;

  /* 多遍使用到的一個關於SSA的值,目前,只有不變量允許保存在這裏面跨越不同的遍*/
  tree value_handle;

  /* 該SSA名字的使用者構成的鏈表 */
  struct ssa_use_operand_d imm_uses;
};
}%
下面的一部分是phi節點的東西。phi節點是ssa中的一種特殊的節點。一個基本塊的所有的
phi節點被穿成一個單鏈表。
%{
struct phi_arg_d GTY(())
{
  /* imm_use必須是第一個域,我們會用它做一些指針運算 */
  /* Note:這兩個域是什麼意思?*/
  struct ssa_use_operand_d imm_use;
  tree def;
};

struct tree_phi_node GTY(())
{
  struct tree_base common;
  tree chain;
  tree result;
  int num_args;
  int capacity;

  /* 包含這個phi節點的基本塊 */
  struct basic_block_def *bb;

  /* phi節點的參數,它們的順序和對應的基本塊的前驅的順序一致 */
  struct phi_arg_d GTY ((length ("((tree)&%h)->phi.num_args"))) a[1];
};
}%
下面是表示BLOCK的樹節點的定義
%{
struct tree_block GTY(())
{
  struct tree_common common;

  /* 非0表示該塊已經準備好處理列在vars中的例外 */
  unsigned handler_block_flag : 1;
  unsigned abstract_flag : 1;
  /* Block的下標號,這些號並不保證在函數之間的唯一性,是否唯一依賴於輸出的調試格
   * 式*/
  unsigned block_num : 30;

  /* 對於一個內聯的函數,該成員記錄了它被定義的地方。這個只有在頂層塊中有效,頂
   * 層塊代表了被inline的函數的函數體。這個一般用於調試輸出*/
  location_t locus;

  tree vars;
  tree subblocks;
  tree supercontext;
  tree abstract_origin;
  /* 在塊重排的時候,一個詞法上的塊可能被分到若干不連續的地址範圍,此時,我們會做一份原
   * 塊的拷貝。這與abstract_origin是不同的,在abstract_origin中,我們有唯一一個
   * 原塊,它被複制若干份(inlining, unrolling),
   * 每一份是一個邏輯塊,並且,這些邏輯塊有不同的物理變量。
   * 而在這裏的情況中,我們只有一個邏輯塊,它被分割到不連續的地址範圍中。大部分
   * 調試格式都不支持表達這種情況,所以,我們創建多個邏輯塊,但它們共享相同的物
   * 理變量,來騙過它們。對於支持這種請況的,這將允許它們將邏輯塊重構出來。
   * 邏輯塊中的一個片段被選爲ORIGINAL,其他的通過fragment_origin指向它,它自己
   * 的這個域是空。所有的片段會從ORIGINAL開始,通過fragment_chain穿成鏈表*/
  tree fragment_origin;
  tree fragment_chain;
};
}%
下面的結構定義了類型節點。
%{
struct tree_type GTY(())
{
  struct tree_common common;
  tree values;
  tree size;
  tree size_unit;
  tree attributes;
  unsigned int uid;

  unsigned int precision : 9;
  ENUM_BITFIELD(machine_mode) mode : 7;

  unsigned string_flag : 1;
  unsigned no_force_blk_flag : 1;
  unsigned needs_constructing_flag : 1;
  unsigned transparent_union_flag : 1;
  unsigned packed_flag : 1;
  unsigned restrict_flag : 1;
  unsigned contains_placeholder_bits : 2;

  unsigned lang_flag_0 : 1;
  unsigned lang_flag_1 : 1;
  unsigned lang_flag_2 : 1;
  unsigned lang_flag_3 : 1;
  unsigned lang_flag_4 : 1;
  unsigned lang_flag_5 : 1;
  unsigned lang_flag_6 : 1;
  unsigned user_align : 1;

  unsigned int align;
  tree pointer_to;
  tree reference_to;
  union tree_type_symtab {
    int GTY ((tag ("0"))) address;
    const char * GTY ((tag ("1"))) pointer;
    struct die_struct * GTY ((tag ("2"))) die;
  } GTY ((desc ("debug_hooks == &sdb_debug_hooks ? 1 : debug_hooks == &dwarf2_debug_hooks ? 2 : 0"),
      descbits ("2"))) symtab;
  tree name;
  tree minval;
  tree maxval;
  tree next_variant;
  tree main_variant;
  tree binfo;
  tree context;
  /* 一個類型的規範形式,用於比較類型相等。如果其內容爲NULL_TREE,表示該類型不能
   * 與其它類型直接比較。此時,在比較相等的時候,需要逐項比較該類型的組成成分,
   * 而不是比較規範形式*/
  tree canonical;
  alias_set_type alias_set;
  /* Points to a structure whose details depend on the language in use.  */
  struct lang_type *lang_specific;
};
}%
注意,values,minval,maxval這些域常常是被重載的,對於不同的類型用不同的宏訪問。
瓷碗,前端也可能重載這些域。
看了這麼多節點的定義了,這個文件還沒有看到一半。大概翻了一下,後面的定義都是類似
的:定義一個結構體表示一種節點,這個結構體的第一項很可能是一個更一般的結構體,然
後定義一大堆宏來操作和判斷這個結構的屬性。
最後,給出最一般的一個結構,也是在其它文件中最常用的。它給出了一個樹節點能表示的
所有信息,它可以代表本文件(tree.h)中聲明的任何一種樹節點。
%{
union tree_node GTY ((ptr_alias (union lang_tree_node),
              desc ("tree_node_structure (&%h)")))
{
  struct tree_base GTY ((tag ("TS_BASE"))) base;
  struct tree_common GTY ((tag ("TS_COMMON"))) common;
  struct tree_int_cst GTY ((tag ("TS_INT_CST"))) int_cst;
  struct tree_real_cst GTY ((tag ("TS_REAL_CST"))) real_cst;
  struct tree_fixed_cst GTY ((tag ("TS_FIXED_CST"))) fixed_cst;
  struct tree_vector GTY ((tag ("TS_VECTOR"))) vector;
  struct tree_string GTY ((tag ("TS_STRING"))) string;
  struct tree_complex GTY ((tag ("TS_COMPLEX"))) complex;
  struct tree_identifier GTY ((tag ("TS_IDENTIFIER"))) identifier;
  struct tree_decl_minimal GTY((tag ("TS_DECL_MINIMAL"))) decl_minimal;
  struct tree_decl_common GTY ((tag ("TS_DECL_COMMON"))) decl_common;
  struct tree_decl_with_rtl GTY ((tag ("TS_DECL_WRTL"))) decl_with_rtl;
  struct tree_decl_non_common  GTY ((tag ("TS_DECL_NON_COMMON"))) decl_non_common;
  struct tree_parm_decl  GTY  ((tag ("TS_PARM_DECL"))) parm_decl;
  struct tree_decl_with_vis GTY ((tag ("TS_DECL_WITH_VIS"))) decl_with_vis;
  struct tree_var_decl GTY ((tag ("TS_VAR_DECL"))) var_decl;
  struct tree_field_decl GTY ((tag ("TS_FIELD_DECL"))) field_decl;
  struct tree_label_decl GTY ((tag ("TS_LABEL_DECL"))) label_decl;
  struct tree_result_decl GTY ((tag ("TS_RESULT_DECL"))) result_decl;
  struct tree_const_decl GTY ((tag ("TS_CONST_DECL"))) const_decl;
  struct tree_type_decl GTY ((tag ("TS_TYPE_DECL"))) type_decl;
  struct tree_function_decl GTY ((tag ("TS_FUNCTION_DECL"))) function_decl;
  struct tree_type GTY ((tag ("TS_TYPE"))) type;
  struct tree_list GTY ((tag ("TS_LIST"))) list;
  struct tree_vec GTY ((tag ("TS_VEC"))) vec;
  struct tree_exp GTY ((tag ("TS_EXP"))) exp;
  struct tree_ssa_name GTY ((tag ("TS_SSA_NAME"))) ssa_name;
  struct tree_phi_node GTY ((tag ("TS_PHI_NODE"))) phi;
  struct tree_block GTY ((tag ("TS_BLOCK"))) block;
  struct tree_binfo GTY ((tag ("TS_BINFO"))) binfo;
  struct tree_statement_list GTY ((tag ("TS_STATEMENT_LIST"))) stmt_list;
  struct gimple_stmt GTY ((tag ("TS_GIMPLE_STATEMENT"))) gstmt;
  struct tree_value_handle GTY ((tag ("TS_VALUE_HANDLE"))) value_handle;
  struct tree_constructor GTY ((tag ("TS_CONSTRUCTOR"))) constructor;
  struct tree_memory_tag GTY ((tag ("TS_MEMORY_TAG"))) mtag;
  struct tree_struct_field_tag GTY ((tag ("TS_STRUCT_FIELD_TAG"))) sft;
  struct tree_omp_clause GTY ((tag ("TS_OMP_CLAUSE"))) omp_clause;
  struct tree_memory_partition_tag GTY ((tag ("TS_MEMORY_PARTITION_TAG"))) mpt;
};
}%
可以看出,這裏使用了C語言中支持“多態性”的一個很常見的技巧。同時,我們也弄明白
了那個ssa_name的出處。在文件coretypes.h中,我們看到,"tree"被定義爲指向tree_node
的指針類型。
在tree.h中,還使用一個枚舉類型定義了C編譯器用到的所有數據類型的編碼,包括標準命
名的和無名的。並且,聲明瞭一個用於保存指向這些類型對應的樹節點的指針的數組。
%{
enum tree_index
{
  TI_ERROR_MARK,
  TI_INTQI_TYPE,
  TI_INTHI_TYPE,
  TI_INTSI_TYPE,
  TI_INTDI_TYPE,
  TI_INTTI_TYPE,

  TI_UINTQI_TYPE,
  TI_UINTHI_TYPE,
  TI_UINTSI_TYPE,
  TI_UINTDI_TYPE,
  TI_UINTTI_TYPE,

  TI_UINT32_TYPE,
  TI_UINT64_TYPE,

  TI_INTEGER_ZERO,
  TI_INTEGER_ONE,
  TI_INTEGER_MINUS_ONE,
  ...


  TI_SAT_UHA_TYPE,
  TI_SAT_USA_TYPE,
  TI_SAT_UDA_TYPE,
  TI_SAT_UTA_TYPE,

  TI_MAX
};

extern GTY(()) tree global_trees[TI_MAX];
}%
下面這個枚舉定義給出了C的標準的整形類型。這些類型的順序必須是,短的在前,有符號
的在前。在c-lex.c文件中的interpret_integer()會用到。
%{
enum integer_type_kind
{
  itk_char,
  itk_signed_char,
  itk_unsigned_char,
  itk_short,
  itk_unsigned_short,
  itk_int,
  itk_unsigned_int,
  itk_long,
  itk_unsigned_long,
  itk_long_long,
  itk_unsigned_long_long,
  itk_none
};

typedef enum integer_type_kind integer_type_kind;

/* 標準c整數類型,使用上面定義的枚舉類型索引該數組 */
extern GTY(()) tree integer_types[itk_none];
}%
好吧,關於樹的定義就先看到這裏了。其實,該文件中還有很多東西沒有提到。例如,對於
OpenMP的支持等(tree.c中,就是對這個文件中聲明的函數的實現,另外,還有樹結構的初
始化部分代碼)。這裏是純粹從代碼出發來分析的,如果想要了解關於樹的更加高級的概念
,參考“GENERIC and GIMPLE”一文。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章