本文分析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”一文。
|