天天看點

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   

1136   struct lang_type *lang_specific;

1137 };

在上面的定義中,下列宏用于通路結構體中的域(紅字部分為宏的定義)。

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

²      對于聚集類型(struct/union/class)節點,該域儲存關于類型的資訊。如果節點不是RECORD_TYPE,QUAL_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_SET是0,該類型的對象可為任何類型對象的别名(比如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_TYPE,UNION_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,表示vector的sub-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   

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   

3640   if (mode == ptr_mode)

3641     TYPE_POINTER_TO (to_type) = t;

3642

3643   

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       

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       

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       

1413       if (TYPE_NEXT_VARIANT (type)

1414           || type != TYPE_MAIN_VARIANT (type))

1415       {

1416         tree variant;

1417         

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         

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     }

對于由const,volatile,register修飾的類型節點,它們串接在一起,通過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   

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   

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.b或A->b來通路資料成員時,這個節點就會被使用。

3956 tree

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

3958 {

3959   tree t;

3960   unsigned int hashcode;

3961

3962   

3963   t = make_node (OFFSET_TYPE);

3964

3965   TYPE_OFFSET_BASETYPE (t) = TYPE_MAIN_VARIANT (basetype);

3966   TREE_TYPE (t) = type;

3967

3968   

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傳回未修飾的類型節點,而其他被諸如,const和volatile修飾的類型節點通過TYPE_NEXT_VARIANT串接在一起。在确定偏移時,我們使用未修飾的類型。

繼續閱讀