GCC-3.4.6源代码学习笔记(7)

1.2.4. 与地址相关节点的构造

C++中,指针、引用和地址在一定程度上可以混用。语言允许通过指针或者引用直接改变地址所在的内容。而函数调用,事实上是通过跳转到相应的地址来实现。另外在语言中,数组名也代表数组的首地址。因此,编译器可能需要首先创建与地址相关的节点,再由这个节点出发构建其它节点。比如,构建函数或数组节点。

1.2.4.1.      指针类型节点的构造

1.2.4.1.1.            用于表示类型的树节点

用于表示语言中的类型的树节点是tree_type,它的定义如下。

 

1089 struct tree_type GTY(())                                                                                   in tree.h

1090 {

1091   struct tree_common common;

1092   tree values;

1093   tree size;

1094   tree size_unit;

1095   tree attributes;

1096   unsigned int uid;

1097

1098   unsigned int precision : 9;

1099   ENUM_BITFIELD(machine_mode) mode : 7;

1100

1101   unsigned string_flag : 1;

1102   unsigned no_force_blk_flag : 1;

1103   unsigned needs_constructing_flag : 1;

1104   unsigned transparent_union_flag : 1;

1105   unsigned packed_flag : 1;

1106   unsigned restrict_flag : 1;

1107   unsigned spare : 2;

1108

1109   unsigned lang_flag_0 : 1;

1110   unsigned lang_flag_1 : 1;

1111   unsigned lang_flag_2 : 1;

1112   unsigned lang_flag_3 : 1;

1113   unsigned lang_flag_4 : 1;

1114   unsigned lang_flag_5 : 1;

1115   unsigned lang_flag_6 : 1;

1116   unsigned user_align : 1;

1117

1118   unsigned int align;

1119   tree pointer_to;

1120   tree reference_to;

1121   union tree_type_symtab {

1122     int GTY ((tag ("0"))) address;

1123     char * GTY ((tag ("1"))) pointer;

1124     struct die_struct * GTY ((tag ("2"))) die;

1125   } GTY ((desc ("debug_hooks == &sdb_debug_hooks? 1: debug_hooks == &dwarf2_debug_hooks? 2: 0"),

1126         descbits ("2"))) symtab;

1127   tree name;

1128   tree minval;

1129   tree maxval;

1130   tree next_variant;

1131   tree main_variant;

1132   tree binfo;

1133   tree context;

1134   HOST_WIDE_INT alias_set;

1135   /* Points to a structure whose details depend on the language in use.  */

1136   struct lang_type *lang_specific;

1137 };

 

在上面的定义中,下列宏用于访问结构体中的域(红字部分为宏的定义)。

Ø         TYPE_BINFO (TYPE_CHECK (NODE)->type.binfo)

²      对于聚集类型(struct/union/class)节点,该域保存关于类型的信息。如果节点不是RECORD_TYPEQUAL_UNION_TYPE,或者UNION_TYPE,该域的用法取决于前端。

Ø         TYPE_ALIAS_SET (TYPE_CHECK (NODE)->type.alias_set)

 

²      特定于语言的(language-specific),基于类型的(typed-based)别名集(alias set)。含有不同TYPE_ALIAS_SET的对象,不能互为别名(cannot alias each other)。如果 TYPE_ALIAS_SET-1,表明该类型还没有别名集。如果TYPE_ALIAS_SET0,该类型的对象可为任何类型对象的别名(比如char指针)。

Ø         TYPE_ALIAS_SET_KNOWN_P (TYPE_CHECK (NODE)->type.alias_set != -1)

²      对于该类型,如果基于类型的(typed-bases)的别名集(alias set)已被推算,该域为非0值。

 

Ø         TYPE_ATTRIBUTES (TYPE_CHECK (NODE)->type.attributes)

 

²      用于该类型的属性节点链表。GCC提供丰富的属性集,作为C/C++语言的扩展。

Ø         TYPE_ALIGN (TYPE_CHECK (NODE)->type.align)

 

²      该类型的对象所需要的对齐量(alignment)。它的值是整数,单位为比特。

Ø         TYPE_USER_ALIGN (TYPE_CHECK (NODE)->type.user_align)

²      其值为1,如果该类型的对齐要求由"aligned"属性指定。否则为0

Ø         TYPE_ALIGN_UNIT (TYPE_ALIGN (NODE) / BITS_PER_UNIT)

²      单位为字节的对齐量。

 

Ø         TYPE_NO_FORCE_BLK (TYPE_CHECK (NODE)->type.no_force_blk_flag)

 

²      RECORD_TYPEUNION_TYPE或者QUAL_UNION_TYPE节点中,它表示该类型,因为缺少对齐量的要求,拥有BLKmode(参见后面有关tree_mode的章节)。

Ø         TYPE_IS_SIZETYPE (INTEGER_TYPE_CHECK (NODE)->type.no_force_blk_flag)

 

²      INTEGER_TYPE节点中,它表示该节点代表一个尺寸(size)。我们将其用于合法性检验(validity checking)而且它使得对其它类型不安全的优化得以执行。注意到C中的size_t类型不能设置此标识。类型size_t只是一个对普通的整数类型的typedef,这个整数类型恰好是sizeof的返回类型。这个位设置上的所有表达式,都代表真实的尺寸(actual sizes)。

Ø         TYPE_RETURNS_STACK_DEPRESSED

(FUNCTION_TYPE_CHECK (NODE)->type.no_force_blk_flag)

 

²      FUNCTION_TYPE中,表示函数返回时不改变栈指针(with the stack pointer depressed)。

Ø         TYPE_STRING_FLAG (TYPE_CHECK (NODE)->type.string_flag)

²      如果在ARRAY_TYPE中设置表示字符串类型对于区分字符串和字符数组的语言而言)。如果在SET_TYPE中设置表示bitstring类型。

Ø         TYPE_VECTOR_SUBPARTS

GET_MODE_NUNITS (VECTOR_TYPE_CHECK (VECTOR_TYPE)->type.mode)

 

²      对于VECTOR_TYPE,表示vectorsub-part的数目。

Ø         TYPE_NEEDS_CONSTRUCTING

(TYPE_CHECK (NODE)->type.needs_constructing_flag)

 

²      表示该类型的对象,在创建时必须调用相应的函数进行初始化。

Ø         TYPE_TRANSPARENT_UNION

(UNION_TYPE_CHECK (NODE)->type.transparent_union_flag)

 

²      表明该类型(UNION_TYPE)的对象,按该union类型的第一个成员的转递(passed)方法来传递。

Ø         TYPE_NONALIASED_COMPONENT

(ARRAY_TYPE_CHECK (NODE)->type.transparent_union_flag)

 

²      对于ARRAY_TYPE表明该类型的元素的地址不可访问。

Ø         TYPE_PACKED (TYPE_CHECK (NODE)->type.packed_flag)

 

²      表明该类型的对象,有尽可能紧凑的布局。

Ø         TYPE_LANG_FLAG_0 ~ TYPE_LANG_FLAG_6

²      由各前端使用。

Ø         TYPE_NEXT_VARIANT (TYPE_CHECK (NODE)->type.next_variant)

²      用于链接所有通过不同类型修饰符(type modifier),例如:const volatile,来声明的类型。

Ø         TYPE_MAIN_VARIANT (TYPE_CHECK (NODE)->type.main_variant)

²      在上述链表中,对于任意节点,该域指向链表的头(最基本的类型,没有任何类型修饰)。

列表2 tree_type中的标识位

1.2.4.1.2.            节点的创建

指针类型的树节点,由下面的函数来构建。

 

3653 tree

3654 build_pointer_type (tree to_type)                                                                       in tree.c

3655 {

3656   return build_pointer_type_for_mode (to_type, ptr_mode);

3657 }

 

参数to_type给出了对应的指针的类型,而函数build_pointer_type_for_mode的第二个参数是ptr_mode。同时这个函数也被用于创建指向vector 模式(mode)的数据。(参见后端,有关章节,genmodes工具)。

 

3625 tree

3626 build_pointer_type_for_mode (tree to_type, enum machine_mode mode)                     in tree.c

3627 {

3628   tree t = TYPE_POINTER_TO (to_type);

3629

3630   /* First, if we already have a type for pointers to TO_TYPE, use it.  */

3631   if (t != 0 && mode == ptr_mode)

3632     return t;

3633

3634   t = make_node (POINTER_TYPE);

3635

3636   TREE_TYPE (t) = to_type;

3637   TYPE_MODE (t) = mode;

3638

3639   /* Record this type as the pointer to TO_TYPE.  */

3640   if (mode == ptr_mode)

3641     TYPE_POINTER_TO (to_type) = t;

3642

3643   /* Lay out the type. This function has many callers that are concerned

3644     with expression-construction, and this simplifies them all.

3645     Also, it guarantees the TYPE_SIZE is in the same obstack as the type.  */

3646   layout_type (t);

3647

3648   return t;

3649 }

 

对于指针要指向的类型,首先要确保有关该类型的信息已经被收集。这包括:类型占的字节数(域TYPE_SIZE_UNIT),类型占的比特数(域TYPE_SIZE)以及最合适的模式(mode)。而对于聚集类型(比如,C++中的数组类型,struct/union/class),除此之外,它们中的每个数据成员都有自己的对齐要求,离开类起始地址的偏移,加入对齐量后由字节、比特衡量的大小。这些由函数layout_type来实现。

1.2.4.1.3.            类型的布局

下面我们仅看与指针类型有关的代码。其它情形会在有关章节中碰到。注意对于基本类型,所以关于大小,符号,对齐量的信息都由节点的模式(mode)指出(关于模式的概念,可参见)。

 

1516     void

1517     layout_type (tree type)                                                                      in stor-layout..c

1518     {

1519       if (type == 0)

1520         abort ();

1521      

1522       /* Do nothing if type has been laid out before.  */

1523       if (TYPE_SIZE (type))

1524         return;

1525      

1526       switch (TREE_CODE (type))

1527       {

        

1602         case POINTER_TYPE:

1603         case REFERENCE_TYPE:

1604         {

1605      

1606           enum machine_mode mode = ((TREE_CODE (type) == REFERENCE_TYPE

1607                                   && reference_types_internal)

1608                                   ? Pmode : TYPE_MODE (type));

1609      

1610           int nbits = GET_MODE_BITSIZE (mode);

1611      

1612           TYPE_SIZE (type) = bitsize_int (nbits);

1613           TYPE_SIZE_UNIT (type) = size_int (GET_MODE_SIZE (mode));

1614           TREE_UNSIGNED (type) = 1;

1615           TYPE_PRECISION (type) = nbits;

1616         }

1617         break;

         

1795       }

      

1797       if (TREE_CODE (type) != RECORD_TYPE

1798           && TREE_CODE (type) != UNION_TYPE

1799           && TREE_CODE (type) != QUAL_UNION_TYPE)

1800         finalize_type_size (type);

      

1818     }

 

如果所有的REFERENCE_TYPE都是内建类型,第1607行的reference_types_internal是非零值,表明节点应该按Pmode来分配(Pmode是个宏,定义为目标机器上某个模式的别名),而不是ptr_mode模式(大小为POINTER_SIZE的模式)。这个全局变量由被前端调用的函数internal_reference_types设置。在C++,它一直保持为0

1804行的finalize_type_size由指定的模式,算出对齐量。

 

1363     static void

1364     finalize_type_size (tree type)

1365     {

1366       /* Normally, use the alignment corresponding to the mode chosen.

1367         However, where strict alignment is not required, avoid

1368         over-aligning structures, since most compilers do not do this

1369         alignment.  */

1370      

1371       if (TYPE_MODE (type) != BLKmode && TYPE_MODE (type) != VOIDmode

1372          && (STRICT_ALIGNMENT

1373         || (TREE_CODE (type) != RECORD_TYPE && TREE_CODE (type) != UNION_TYPE

1374                && TREE_CODE (type) != QUAL_UNION_TYPE

1375                && TREE_CODE (type) != ARRAY_TYPE)))

1376       {

1377         TYPE_ALIGN (type) = GET_MODE_ALIGNMENT (TYPE_MODE (type));

1378         TYPE_USER_ALIGN (type) = 0;

1379       }

      

1412       /* Also layout any other variants of the type.  */

1413       if (TYPE_NEXT_VARIANT (type)

1414           || type != TYPE_MAIN_VARIANT (type))

1415       {

1416         tree variant;

1417         /* Record layout info of this variant.  */

1418         tree size = TYPE_SIZE (type);

1419         tree size_unit = TYPE_SIZE_UNIT (type);

1420         unsigned int align = TYPE_ALIGN (type);

1421         unsigned int user_align = TYPE_USER_ALIGN (type);

1422         enum machine_mode mode = TYPE_MODE (type);

1423      

1424         /* Copy it into all variants.  */

1425         for (variant = TYPE_MAIN_VARIANT (type);

1426             variant != 0;

1427             variant = TYPE_NEXT_VARIANT (variant))

1428         {

1429           TYPE_SIZE (variant) = size;

1430           TYPE_SIZE_UNIT (variant) = size_unit;

1431           TYPE_ALIGN (variant) = align;

1432           TYPE_USER_ALIGN (variant) = user_align;

1433           TYPE_MODE (variant) = mode;

1434         }

1435       }

1436     }

 

对于由constvolatileregister修饰的类型节点,它们串接在一起,通过TYPE_MAIN_VARIANT指向没有修饰的类型节点,并通过TYPE_NEXT_VARIANT访问彼此。从行1413开始,我们需要对这些类型节点进行更新。

1.2.4.2.    创建引用类型的节点

引用类型与指针类型在编译器内部并无本质的区别,这一点在3693行引用类型的模式是ptr_mode可看出。因此,其创建过程和指针类型非常相似。

 

3690 tree

3691 build_reference_type (tree to_type)                                                                   in tree.c

3692 {

3693   return build_reference_type_for_mode (to_type, ptr_mode);

3694 }

 

3663 tree

3664 build_reference_type_for_mode (tree to_type, enum machine_mode mode)           in tree.c

3665 {

3666   tree t = TYPE_REFERENCE_TO (to_type);

3667

3668   /* First, if we already have a type for pointers to TO_TYPE, use it.  */

3669   if (t != 0 && mode == ptr_mode)

3670     return t;

3671

3672   t = make_node (REFERENCE_TYPE);

3673

3674   TREE_TYPE (t) = to_type;

3675   TYPE_MODE (t) = mode;

3676

3677   /* Record this type as the pointer to TO_TYPE.  */

3678   if (mode == ptr_mode)

3679   TYPE_REFERENCE_TO (to_type) = t;

3680

3681   layout_type (t);

3682

3683   return t;

3684 }

1.2.4.3.      为地址表达式创建节点

如下3683行所示,由build_address返回的类型是tree_exp(更确切的,是地址表达式)。 关于ADDR_EXPR(地址表达式)的描述给出如下:

 

地址表达式(ADDR_EXPR[2]

²        这些节点用于表示一个对象的地址。(这些表达式拥有指针或引用类型)。它们的操作数可能是另一个表达式,或者它可能是一个声明。作为扩展,GCC允许使用标签(label)的地址。在这种情况下,ADDR_EXPR的操作数应该是LABEL_DECL。而这样的表达式的类型是void*。如果被取址的对象不是左值(lvalue),一个临时对象会被创建,这个临时对象的地址被使用。

 

3675 tree

3676 build_address (tree t)                                                                                in typeck.c

3677 {

3678   tree addr;

3679

3680   if (error_operand_p (t) || !cxx_mark_addressable (t))

3681     return error_mark_node;

3682

3683   addr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (t)), t);

3684   if (staticp (t))

3685     TREE_CONSTANT (addr) = 1;

3686

3687   return addr;

3688 }

 

3680行,cxx_mark_addressable标识t以表明我们需要t的地址可被使用。 因此t不能存放在寄存器中。如果标识成功,函数返回true 。而在3684行的函数staticp检查地址是否引用了静态对象的内存(例如,一个字符串常量,标签,或者静态、全局变量)。这样的地址在运行时不会改变;相比之下在函数内的局部变量,在每次调用可能会有不同的地址,这样的地址不是常量。

1.2.4.4.    OFFSET_TYPE创建节点

C/C++的编程中,我们可以使用宏offsetof来确定,特定的数据成员到对应的struct/class 的起始地址的偏移。这个宏得到的就是这里的OFFSET_TYPE节点。我们通过A.bA->b来访问数据成员时,这个节点就会被使用。

 

3956 tree

3957 build_offset_type (tree basetype, tree type)                                                         in tree.c

3958 {

3959   tree t;

3960   unsigned int hashcode;

3961

3962   /* Make a node of the sort we want.  */

3963   t = make_node (OFFSET_TYPE);

3964

3965   TYPE_OFFSET_BASETYPE (t) = TYPE_MAIN_VARIANT (basetype);

3966   TREE_TYPE (t) = type;

3967

3968   /* If we already have such a type, use the old one and free this one.  */

3969   hashcode = TYPE_HASH (basetype) + TYPE_HASH (type);

3970   t = type_hash_canon (hashcode, t);

3971

3972   if (!COMPLETE_TYPE_P (t))

3973     layout_type (t);

3974

3975   return t;

3976 }

 

我们已经看过,TYPE_MAIN_VARIANT返回未修饰的类型节点,而其他被诸如,constvolatile修饰的类型节点通过TYPE_NEXT_VARIANT串接在一起。在确定偏移时,我们使用未修饰的类型。

 

你可能感兴趣的:(vector,tree,layout,Build,reference,alignment)