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串接起来。