gcc 原始碼分析-前端篇2
阿新 • • 發佈:2019-02-08
2. 對ID及保留字的處理
在c語言中,系統預留了很多關鍵字,也被稱為保留字,比如表示資料型別的int,short,char,控制分支執行的if,then等。
任何關鍵字,本質上也是一個ID,比如它也有長度,對int就是3,對short就是5,也有內容,比如int,short。但比起一般id,
它還有其他一些屬性,比如表示資料型別的int,它有一個數值範圍,它的值範圍是-xxx到xxx之間。
由於這樣的關係,gcc把ID和關鍵字都儲存在一張表中,這張表定義如下:
#define MAX_HASH_TABLE 1009
tree hash_table[MAX_HASH_TABLE]; /* id hash buckets */
同時定義了一個函式get_identifier (text) 來操縱這一張表:
函式具體實現就是通過傳入的字串查詢該id,如果沒有則建立這個ID,這裡new了一個tree_identifier, 同時把它儲存在hash_table表中,為了加快查詢,這裡用到了hash演算法。在這裡我們見到了struct tree_common結構體chain成員的用法,對於hash演算法來說,很難避免衝突,對於傳入的不同字串,有可能它算出的hash值是一樣的,這時候,我們就把相沖突的ID放在chain成員變數中;
函式的最後給tree_identifier的成員length,pointer附值。
gcc 在執行之初,便開始建立這些表示資料型別的關鍵字,比如int,short,char,void,這個在函式init_lex依次建立,並把建立的相對應的
tree 結點附給:
ridpointers[(int) RID_INT]
ridpointers[(int) RID_CHAR]
ridpointers[(int) RID_VOID]
ridpointers[(int) RID_SHORT]
關鍵字的ID生成了,還有附一些其他引數,這個過程是在init_decl_processing完成;在GCC中要表示一個數據型別,要用到下面的結構:
struct tree_type
{
char common[sizeof (struct tree_common)];
union tree_node *values;
union tree_node *sep;
union tree_node *size;
enum machine_mode mode : 8;
unsigned char size_unit;
unsigned char align;
unsigned char sep_unit;
union tree_node *pointer_to;
union tree_node *reference_to;
int parse_info;
int symtab_address;
union tree_node *name;
union tree_node *max;
union tree_node *next_variant;
union tree_node *main_variant;
union tree_node *basetypes;
union tree_node *noncopied_parts;
/* Points to a structure whose details depend on the language in use. */
struct lang_type *lang_specific;
};
在表示int型的資料型別中,它的兩個成員變數sep,max比較重要; sep表示它的最小值,而max表示它的最大值;
在函式make_signed_type中建立了一個 INTEGER_TYPE 型別的tree 節點;它實際上是一個 struct tree_type 型別的節點,
在該函式中,建立的節點的sep,max成員被賦予INTEGER_CST 節點,它實際上是一個struct tree_int_cst型別節點:
struct tree_int_cst
{
char common[sizeof (struct tree_common)];
long int_cst_low;
long int_cst_high;
};
可以看出,它有兩個成員特殊成員:int_cst_low,int_cst_high
對於最小值,它給出的值是: int_cst_low=0x80000000,int_cst_high=0xffffffff
對於最大值,它給出的值是:int_cst_low=0x7fffffff,int_cst_high=0x0
它們均是在build_int_2 函式中建立;
在函式make_signed_type的最後layout_type中,設定int 型資料節點的size成員,它也是一個struct tree_int_cst型別節點,只是它的
int_cst_low值是4,而int_cst_high為0;
這樣int型節點生成了,這個節點資料和它的ID,最後被封裝成一個宣告型別節點,它用struct tree_decl結構體表示:
可以看出,這是一個巨複雜的結構,int 型別節點轉變成一個int 宣告節點過程中,它將生成一個struct tree_decl節點,它的
成員變數name將是int 的ID節點值,它的type 就是剛剛生成的int 節點,這個int 宣告節點最後會被放入到記錄全域性節點的
global_binding_level中,
global_binding_level->name 指向剛剛建立的int 宣告節點;
在init_decl_processing函式接下來將建立char 型別節點,unsigned int 型別,short 型別,這些值都回放入global_binding_level->name
然後通過chain連線起來;
總結一下,gcc 用hash表來儲存所有的ID,包括保留字;gcc 對於內建的資料型別(int,short,char)在初始化時會生成tree_decl 結構的結點,
並把它記錄在global_binding_level的name變數中,這個name始終指向最後一個宣告的結點,並通過節點的chain串接起來。
在c語言中,系統預留了很多關鍵字,也被稱為保留字,比如表示資料型別的int,short,char,控制分支執行的if,then等。
任何關鍵字,本質上也是一個ID,比如它也有長度,對int就是3,對short就是5,也有內容,比如int,short。但比起一般id,
它還有其他一些屬性,比如表示資料型別的int,它有一個數值範圍,它的值範圍是-xxx到xxx之間。
由於這樣的關係,gcc把ID和關鍵字都儲存在一張表中,這張表定義如下:
#define MAX_HASH_TABLE 1009
tree hash_table[MAX_HASH_TABLE]; /* id hash buckets */
同時定義了一個函式get_identifier (text) 來操縱這一張表:
函式具體實現就是通過傳入的字串查詢該id,如果沒有則建立這個ID,這裡new了一個tree_identifier, 同時把它儲存在hash_table表中,為了加快查詢,這裡用到了hash演算法。在這裡我們見到了struct tree_common結構體chain成員的用法,對於hash演算法來說,很難避免衝突,對於傳入的不同字串,有可能它算出的hash值是一樣的,這時候,我們就把相沖突的ID放在chain成員變數中;
函式的最後給tree_identifier的成員length,pointer附值。
gcc 在執行之初,便開始建立這些表示資料型別的關鍵字,比如int,short,char,void,這個在函式init_lex依次建立,並把建立的相對應的
tree 結點附給:
ridpointers[(int) RID_INT]
ridpointers[(int) RID_CHAR]
ridpointers[(int) RID_VOID]
ridpointers[(int) RID_SHORT]
關鍵字的ID生成了,還有附一些其他引數,這個過程是在init_decl_processing完成;在GCC中要表示一個數據型別,要用到下面的結構:
struct tree_type
{
char common[sizeof (struct tree_common)];
union tree_node *values;
union tree_node *sep;
union tree_node *size;
enum machine_mode mode : 8;
unsigned char size_unit;
unsigned char align;
unsigned char sep_unit;
union tree_node *pointer_to;
union tree_node *reference_to;
int parse_info;
int symtab_address;
union tree_node *name;
union tree_node *max;
union tree_node *next_variant;
union tree_node *main_variant;
union tree_node *basetypes;
union tree_node *noncopied_parts;
/* Points to a structure whose details depend on the language in use. */
struct lang_type *lang_specific;
};
在表示int型的資料型別中,它的兩個成員變數sep,max比較重要; sep表示它的最小值,而max表示它的最大值;
在函式make_signed_type中建立了一個 INTEGER_TYPE 型別的tree 節點;它實際上是一個 struct tree_type 型別的節點,
在該函式中,建立的節點的sep,max成員被賦予INTEGER_CST 節點,它實際上是一個struct tree_int_cst型別節點:
struct tree_int_cst
{
char common[sizeof (struct tree_common)];
long int_cst_low;
long int_cst_high;
};
可以看出,它有兩個成員特殊成員:int_cst_low,int_cst_high
對於最小值,它給出的值是: int_cst_low=0x80000000,int_cst_high=0xffffffff
對於最大值,它給出的值是:int_cst_low=0x7fffffff,int_cst_high=0x0
它們均是在build_int_2 函式中建立;
在函式make_signed_type的最後layout_type中,設定int 型資料節點的size成員,它也是一個struct tree_int_cst型別節點,只是它的
int_cst_low值是4,而int_cst_high為0;
這樣int型節點生成了,這個節點資料和它的ID,最後被封裝成一個宣告型別節點,它用struct tree_decl結構體表示:
struct tree_decl { char common[sizeof (struct tree_common)]; char *filename; int linenum; union tree_node *size; enum machine_mode mode : 8; unsigned char size_unit; unsigned char align; unsigned char voffset_unit; union tree_node *name; union tree_node *context; int offset; union tree_node *voffset; union tree_node *arguments; union tree_node *result; union tree_node *initial; char *print_name; char *assembler_name; struct rtx_def *rtl; /* acts as link to register transfer language (rtl) info */ int frame_size; /* For FUNCTION_DECLs: size of stack frame */ struct rtx_def *saved_insns; /* For FUNCTION_DECLs: points to insn that constitutes its definition on the permanent obstack. */ int block_symtab_address; /* Points to a structure whose details depend on the language in use. */ struct lang_decl *lang_specific; };
可以看出,這是一個巨複雜的結構,int 型別節點轉變成一個int 宣告節點過程中,它將生成一個struct tree_decl節點,它的
成員變數name將是int 的ID節點值,它的type 就是剛剛生成的int 節點,這個int 宣告節點最後會被放入到記錄全域性節點的
global_binding_level中,
global_binding_level->name 指向剛剛建立的int 宣告節點;
在init_decl_processing函式接下來將建立char 型別節點,unsigned int 型別,short 型別,這些值都回放入global_binding_level->name
然後通過chain連線起來;
總結一下,gcc 用hash表來儲存所有的ID,包括保留字;gcc 對於內建的資料型別(int,short,char)在初始化時會生成tree_decl 結構的結點,
並把它記錄在global_binding_level的name變數中,這個name始終指向最後一個宣告的結點,並通過節點的chain串接起來。