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

来源:互联网 发布:图文编辑器软件下载 编辑:程序博客网 时间:2024/06/06 03:47

4.3.1.7.8.2.3.2.            type_info的后处理

4.3.1.7.8.2.3.2.1.  安装基本类型type_info对象

前端调用函数finish_file最后完成其工作,并为后端产生RTL形式的代码树。也在这个函数中,逐个为unemitted_tinfo_decls中保存的待后处理的伪type_info,构建真正的对象。不过在创建工作之前,首先要安装基本类型的type_info对象如下。

 

1353   void

1354   emit_support_tinfos (void)                                                                                     in rtti.c

1355   {

1356     static tree *const fundamentals[] =

1357     {

1358       &void_type_node,

1359       &boolean_type_node,

1360       &wchar_type_node,

1361       &char_type_node, &signed_char_type_node, &unsigned_char_type_node,

1362       &short_integer_type_node, &short_unsigned_type_node,

1363       &integer_type_node, &unsigned_type_node,

1364       &long_integer_type_node, &long_unsigned_type_node,

1365       &long_long_integer_type_node, &long_long_unsigned_type_node,

1366       &float_type_node, &double_type_node, &long_double_type_node,

1367       0

1368     };

1369     int ix;

1370     tree bltn_type, dtor;

1371    

1372     push_nested_namespace (abi_node);

1373     bltn_type = xref_tag (class_type,

1374                       get_identifier ("__fundamental_type_info"),

1375                       true, false);

1376     pop_nested_namespace (abi_node);

1377     if (!COMPLETE_TYPE_P (bltn_type))

1378       return;

1379     dtor = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (bltn_type), 1);

1380     if (DECL_EXTERNAL (dtor))

1381       return;

1382     doing_runtime = 1;

1383     for (ix = 0; fundamentals[ix]; ix++)

1384     {

1385       tree bltn = *fundamentals[ix];

1386       tree bltn_ptr = build_pointer_type (bltn);

1387       tree bltn_const_ptr = build_pointer_type

1388                                (build_qualified_type (bltn, TYPE_QUAL_CONST));

1389       tree tinfo;

1390        

1391       tinfo = get_tinfo_decl (bltn);

1392       TREE_USED (tinfo) = 1;

1393       TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (tinfo)) = 1;

1394        

1395       tinfo = get_tinfo_decl (bltn_ptr);

1396       TREE_USED (tinfo) = 1;

1397       TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (tinfo)) = 1;

1398        

1399       tinfo = get_tinfo_decl (bltn_const_ptr);

1400       TREE_USED (tinfo) = 1;

1401       TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (tinfo)) = 1;

1402     }

1403   }

 

基本类型的type_info对象,其类型是__fundamental_type_info,其声明在cxxabiv1名字空间里,其定义则在文件tinfo2.cc中。如果在编译GCC时,包含了tinfo2.cc,而不仅仅只是cxxabi.h,那么上面1373行的bltn_type将是一个完整的RECORD_TYPE节点,将顺利通过1377行和1380行的条件,为所有fundamentals成员构建type_info对象。注意这里构建的还是伪对象。另在1382行,doing_runtime被设为1,表明有运行时库支持。

4.3.1.7.8.2.3.2.2.  构建真实type_info对象

下面函数中的参数decl就是待处理的伪type_info

 

1433   bool

1434   emit_tinfo_decl (tree decl)                                                                               in rtti.c

1435   {

1436     tree type = TREE_TYPE (DECL_NAME (decl));

1437     bool non_public;

1438     int in_library = typeinfo_in_lib_p (type);

1439     tree var_desc, var_init;

1440  

1441     my_friendly_assert (unemitted_tinfo_decl_p (decl), 20030307);

1442    

1443     import_export_tinfo (decl, type, in_library);

1444     if (DECL_REALLY_EXTERN (decl) || !DECL_NEEDED_P (decl))

1445       return false;

1446  

1447     if (!doing_runtime && in_library)

1448       return false;

1449  

1450     non_public = false;

1451     var_desc = get_pseudo_ti_desc (type);

1452     var_init = get_pseudo_ti_init (type, var_desc, &non_public);

1453    

1454     DECL_EXTERNAL (decl) = 0;

1455     TREE_PUBLIC (decl) = !non_public;

1456     if (non_public)

1457       DECL_COMDAT (decl) = 0;

1458  

1459     DECL_INITIAL (decl) = var_init;

1460     mark_used (decl);

1461     cp_finish_decl (decl, var_init, NULL_TREE, 0);

1462    /* cp_finish_decl will have dealt with linkage.  */

1463    

1464    /* Say we've dealt with it.  */

1465     TREE_TYPE (DECL_NAME (decl)) = NULL_TREE;

1466  

1467     return true;

1468   }

 

在构建真实type_info对象前,编译器需要判断这个对象是否真的需要构建。不必要的构建不但会降低编译效率,也会使生成的代码膨胀。

下面的函数实际上是判断是否要求基本类型的type_info对象。因为基本类型的type_info对象已经由编译器自动构建,不存在构建、还是不构建的问题。

 

965    static bool

966    typeinfo_in_lib_p (tree type)                                                                            in rtti.c

967    {

968      /* The typeinfo objects for `T*' and `const T*' are in the runtime

969        library for simple types T.  */

970      if (TREE_CODE (type) == POINTER_TYPE

971          && (cp_type_quals (TREE_TYPE (type)) == TYPE_QUAL_CONST

972              || cp_type_quals (TREE_TYPE (type)) == TYPE_UNQUALIFIED))

973        type = TREE_TYPE (type);

974   

975      switch (TREE_CODE (type))

976      {

977        case INTEGER_TYPE:

978        case BOOLEAN_TYPE:

979        case CHAR_TYPE:

980        case REAL_TYPE:

981        case VOID_TYPE:

982          return true;

983       

984        default:

985          return false;

986      }

987    }

 

下面的函数则是判断type_info所对应的对象是内部的,还是来自外部的。要是来自外部,那么很可能这个type_info对象(注意这可是全局对象)已在别的编译单元产生。因而前端告诉后端,只生成必要的对象。而剩下的引用将由链接器解决。

 

1729   void

1730   import_export_tinfo (tree decl, tree type, bool is_in_library)                        in decl2.c

1731   {

1732     if (DECL_INTERFACE_KNOWN (decl))

1733       return;

1734    

1735     if (IS_AGGR_TYPE (type))

1736       import_export_class (type);

1737        

1738     if (IS_AGGR_TYPE (type) && CLASSTYPE_INTERFACE_KNOWN (type)

1739         && TYPE_POLYMORPHIC_P (type)

1740         /* If -fno-rtti, we're not necessarily emitting this stuff with

1741           the class, so go ahead and emit it now. This can happen when

1742           a class is used in exception handling.  */

1743         && flag_rtti)

1744     {

1745       DECL_NOT_REALLY_EXTERN (decl) = !CLASSTYPE_INTERFACE_ONLY (type);

1746       DECL_COMDAT (decl) = 0;

1747     }

1748     else

1749     {

1750       DECL_NOT_REALLY_EXTERN (decl) = 1;

1751       DECL_COMDAT (decl) = 1;

1752     }

1753  

1754     /* Now override some cases.  */

1755     if (flag_weak)

1756       DECL_COMDAT (decl) = 1;

1757     else if (is_in_library)

1758       DECL_COMDAT (decl) = 0;

1759    

1760     DECL_INTERFACE_KNOWN (decl) = 1;

1761   }

 

如果type_info所对应的对象是类,那么需要下面的函数来进行一些处理。这个函数主要是为类设置CLASSTYPE_INTERFACE_KNOWN,如果这个域是true,表明我们已经确定项虚函数表、tinfo对象等类簿记数据是否在本编译单元生成。对于定义了虚函数的类,其type_info对象将被保存在虚函数表里(只有这样的类编译器才主动为其产生type_info;否则需要dynamic_cast操作符来触发,而且生成的tinfo对象只由dynamic_cast保存)。

 

1484   static void

1485   import_export_class (tree ctype)                                                               in decl2.c

1486   {

1487    /* -1 for imported, 1 for exported.  */

1488     int import_export = 0;

1489  

1490     /* It only makes sense to call this function at EOF. The reason is

1491       that this function looks at whether or not the first non-inline

1492       non-abstract virtual member function has been defined in this

1493       translation unit. But, we can't possibly know that until we've

1494       seen the entire translation unit.  */

1495     my_friendly_assert (at_eof, 20000226);

1496  

1497     if (CLASSTYPE_INTERFACE_KNOWN (ctype))

1498       return;

1499  

1500     /* If MULTIPLE_SYMBOL_SPACES is defined and we saw a #pragma interface,

1501       we will have CLASSTYPE_INTERFACE_ONLY set but not

1502       CLASSTYPE_INTERFACE_KNOWN. In that case, we don't want to use this

1503       heuristic because someone will supply a #pragma implementation

1504       elsewhere, and deducing it here would produce a conflict.  */

1505     if (CLASSTYPE_INTERFACE_ONLY (ctype))

1506       return;

1507  

1508     if (lookup_attribute ("dllimport", TYPE_ATTRIBUTES (ctype)))

1509       import_export = -1;

1510     else if (lookup_attribute ("dllexport", TYPE_ATTRIBUTES (ctype)))

1511       import_export = 1;

1512  

1513     /* If we got -fno-implicit-templates, we import template classes that

1514       weren't explicitly instantiated.  */

1515     if (import_export == 0

1516         && CLASSTYPE_IMPLICIT_INSTANTIATION (ctype)

1517         && ! flag_implicit_templates)

1518       import_export = -1;

1519  

1520     /* Base our import/export status on that of the first non-inline,

1521       non-pure virtual function, if any.  */

1522     if (import_export == 0

1523         && TYPE_POLYMORPHIC_P (ctype))

1524     {

1525       tree method = CLASSTYPE_KEY_METHOD (ctype);

1526       if (method)

1527         import_export = (DECL_REALLY_EXTERN (method) ? -1 : 1);

1528     }

1529  

1530   #ifdef MULTIPLE_SYMBOL_SPACES

1531     if (import_export == -1)

1532       import_export = 0;

1533   #endif

1534  

1535     if (import_export)

1536     {

1537       SET_CLASSTYPE_INTERFACE_KNOWN (ctype);

1538       CLASSTYPE_INTERFACE_ONLY (ctype) = (import_export < 0);

1539     }

1540   }

 

上面1523行的TYPE_POLYMORPHIC_P,如果非0,就表示该类定义有虚函数。另外,1525行的CLASSTYPE_KEY_METHOD返回的是,第一个非内联、非纯虚函数。如果我们已经看到了这个类的定义,毫无疑问,虚函数表、tinfo对象这样的类簿记数据是需要在被编译单元产生的。另外具有“dllexport”属性的类,因为明确声明为导出的,这些数据,如果有的话,需要在本编译单元产生。

而对于普通的类,即没有虚函数表,亦不保存tinfo对象,编译器尽量让其tinfo对象在编译单元间共享。为此,在import_export_class1751行,设置DECL_COMDAT

回到emit_tinfo_decl,在1444行,DECL_REALLY_EXTERN的定义为:

 

2856   #define DECL_REALLY_EXTERN(NODE) /                                            in cp-tree.h

2857     (DECL_EXTERNAL (NODE) && ! DECL_NOT_REALLY_EXTERN (NODE))

 

tinfo被认定为在外部定义的,或者是基本类型的tinfo,但运行时支持没有(1447行条件)那么我们就不再趟这趟浑水。否则,就要往下构建初始化真实tinfo对象的代码。注意下面函数的参数var_desc是从1451行,由get_pseudo_ti_desc取回的伪tinfo对象。

 

995    static tree

996    get_pseudo_ti_init (tree type, tree var_desc, bool *non_public_p)                       in rtti.c

997    {

998      my_friendly_assert (at_eof, 20021120);

999      switch (TREE_CODE (type))

1000     {

1001       case OFFSET_TYPE:

1002         return ptm_initializer (var_desc, type, non_public_p);

1003       case POINTER_TYPE:

1004         return ptr_initializer (var_desc, type, non_public_p);

1005       case ENUMERAL_TYPE:

1006         return generic_initializer (var_desc, type);

1007         break;

1008       case FUNCTION_TYPE:

1009         return generic_initializer (var_desc, type);

1010         break;

1011       case ARRAY_TYPE:

1012         return generic_initializer (var_desc, type);

1013         break;

1014       case UNION_TYPE:

1015       case RECORD_TYPE:

1016         if (TYPE_PTRMEMFUNC_P (type))

1017           return ptm_initializer (var_desc, type, non_public_p);

1018         else if (var_desc == class_desc_type_node)

1019         {

1020           if (!COMPLETE_TYPE_P (type))

1021             /* Emit a non-public class_type_info.  */

1022             *non_public_p = true;

1023             return class_initializer (var_desc, type, NULL_TREE);

1024         }

1025         else if (var_desc == si_class_desc_type_node)

1026         {

1027           tree base_binfos = BINFO_BASETYPES (TYPE_BINFO (type));

1028           tree base_binfo = TREE_VEC_ELT (base_binfos, 0);

1029           tree tinfo = get_tinfo_ptr (BINFO_TYPE (base_binfo));

1030           tree base_inits = tree_cons (NULL_TREE, tinfo, NULL_TREE);

1031         

1032           return class_initializer (var_desc, type, base_inits);

1033         }

1034         else

1035        {

1036           int hint = class_hint_flags (type);

1037           tree binfo = TYPE_BINFO (type);

1038           int nbases = BINFO_N_BASETYPES (binfo);

1039           tree base_binfos = BINFO_BASETYPES (binfo);

1040           tree base_accesses = BINFO_BASEACCESSES (binfo);

1041           tree base_inits = NULL_TREE;

1042           int ix;

1043            

1044           /* Generate the base information initializer.  */

1045           for (ix = nbases; ix--;)

1046           {

1047            tree base_binfo = TREE_VEC_ELT (base_binfos, ix);

1048             tree base_init = NULL_TREE;

1049             int flags = 0;

1050             tree tinfo;

1051             tree offset;

1052                

1053             if (TREE_VEC_ELT (base_accesses, ix) == access_public_node)

1054                flags |= 2;

1055             tinfo = get_tinfo_ptr (BINFO_TYPE (base_binfo));

1056             if (TREE_VIA_VIRTUAL (base_binfo))

1057             {

1058               /* We store the vtable offset at which the virtual

1059                 base offset can be found.  */

1060               offset = BINFO_VPTR_FIELD (base_binfo);

1061               offset = convert (sizetype, offset);

1062               flags |= 1;

1063             }

1064             else

1065               offset = BINFO_OFFSET (base_binfo);

1066                 

1067             /* Combine offset and flags into one field.  */

1068             offset = cp_build_binary_op (LSHIFT_EXPR, offset,

1069                                     build_int_2 (8, 0));

1070             offset = cp_build_binary_op (BIT_IOR_EXPR, offset,

1071                                     build_int_2 (flags, 0));

1072             base_init = tree_cons (NULL_TREE, offset, base_init);

1073             base_init = tree_cons (NULL_TREE, tinfo, base_init);

1074             base_init = build_constructor (NULL_TREE, base_init);

1075             TREE_HAS_CONSTRUCTOR (base_init) = 1;

1076             base_inits = tree_cons (NULL_TREE, base_init, base_inits);

1077           }

1078           base_inits = build_constructor (NULL_TREE, base_inits);

1079           TREE_HAS_CONSTRUCTOR (base_inits) = 1;

1080           base_inits = tree_cons (NULL_TREE, base_inits, NULL_TREE);

1081           /* Prepend the number of bases.  */

1082           base_inits = tree_cons (NULL_TREE,

1083                              build_int_2 (nbases, 0), base_inits);

1084           /* Prepend the hint flags.  */

1085           base_inits = tree_cons (NULL_TREE,

1086                              build_int_2 (hint, 0), base_inits);

1087  

1088           return class_initializer (var_desc, type, base_inits);

1089         }

1090         break;

1091  

1092       default:

1093         return generic_initializer (var_desc, type);

1094     }

1095   }

 

我们以多继承类作为例子来看一下真实tinfo对象的构建。下面的函数首先确定描述类继承树特性的暗示标识。

 

936    static int

937    class_hint_flags (tree type)                                                                               in rtti.c

938    {

939      int hint_flags = 0;

940     

941      dfs_walk (TYPE_BINFO (type), dfs_class_hint_mark, NULL, &hint_flags);

942      dfs_walk (TYPE_BINFO (type), dfs_class_hint_unmark, NULL, NULL);

943     

944      return hint_flags;

945    }  

 

函数dfs_walk将以深度优先的后序遍历来访问类的继承树,这个函数的细节我们在后面再来看。对于该类的每个基类,dfs_class_hint_mark按从最基本的基类到最后的派生类的次序执行。注意到对于非虚拟派生类hint1,而对于包含虚拟基类的类则是3

 

899    static tree

900    dfs_class_hint_mark (tree binfo, void *data)                                                     in rtti.c

901    {

902      tree basetype = BINFO_TYPE (binfo);

903      int *hint = (int *) data;

904     

905      if (TREE_VIA_VIRTUAL (binfo))

906      {

907        if (CLASSTYPE_MARKED (basetype))

908          *hint |= 1;

909        if (CLASSTYPE_MARKED2 (basetype))

910          *hint |= 2;

911         SET_CLASSTYPE_MARKED2 (basetype);

912      }

913      else

914      {

915        if (CLASSTYPE_MARKED (basetype) || CLASSTYPE_MARKED2 (basetype))

916          *hint |= 1;

917        SET_CLASSTYPE_MARKED (basetype);

918      }

919      return NULL_TREE;

920    }

 

多继承类所对应的type_info对象的类型是__vmi_class_type_info。在cxxabi.h文件中可以看到,这个类含有一个__base_class_type_info数组用于描述每个基类。1045行的FOR循环就是初始化这个数组。base_init是用于构建类对象的初始值列表,注意这个链节点的次序正好与初始化的次序相反。还有,cxxabi.h所定义的__vmi_class_type_info的构造函数,并没有对其包含的__base_class_type_info数组进行初始化。但在这里,编译器利用职权之便绕过构造函数,初始化了这个数组,当然前提是这个初始化列表严格匹配类成员出现的次序。

类型__base_class_type_info具有2个数据成员,一个是指向基类tinfo的指针,另一个是综合基类偏移及信息的标识(__offset_flags)。对于指向基类tinfo的指针,其初始值由下面函数获取,是其基类tinfo对象的地址。

 

380    static tree

381    get_tinfo_ptr (tree type)                                                                                  in rtti.c

382    {

383      tree decl = get_tinfo_decl (type);

384   

385      mark_used (decl);

386      return build_nop (type_info_ptr_type,

387                     build_address (decl));

388    }

 

对于基类的binfo对象,域BINFO_OFFSETBINFO_VPTR_FIELD(对于虚拟基类而言)记录了该基类相对于所考查派生类的偏移。那么,明显地,对于__offset_flags,其高24位记录了这个偏移,其低8位则表示其是否为公有派生及虚拟基类。从上面的代码,可以看出,类__vmi_class_type_info的第一个数据成员是class_hint_flags找出标识,第二个成员则是基类数目,第三个成员就是__base_class_type_info数组。准备好了初始化列表后,函数class_initializer构建CONSTRUCTOR节点来表示这个构造函数调用表达式。

 

951    static tree

952    class_initializer (tree desc, tree target, tree trail)                                                        in rtti.c

953    {

954      tree init = tinfo_base_init (desc, target);

955     

956      TREE_CHAIN (init) = trail;

957      init = build_constructor (NULL_TREE, init);

958      TREE_HAS_CONSTRUCTOR (init) = TREE_CONSTANT (init) = TREE_STATIC (init) = 1;

959      return init; 

960    }

 

不过,到了这里,初始化列表其实还没完全准备好。因为与类相关的tinfo类型都是从__class_type_info派生的。根据C++的规范,基类的初始化要在派生类的数据成员之前,而且对于具有虚函数的类,还有初始化虚函数表的问题,而tinfo类型正好都有虚函数。基类初始化要从最底层的基类开始,这里最底层的type_info具有一个“const char*”数据成员,那么745行的代码块为其构建了定义和初始值。

 

738    static tree

739    tinfo_base_init (tree desc, tree target)                                                                in rtti.c

740    {

741      tree init = NULL_TREE;

742      tree name_decl;

743      tree vtable_ptr;

744     

745      {

746        tree name_name;

747       

748        /* Generate the NTBS array variable.  */

749        tree name_type = build_cplus_array_type

750                      (build_qualified_type (char_type_node, TYPE_QUAL_CONST),

751                        NULL_TREE);

752        tree name_string = tinfo_name (target);

753   

754        name_name = mangle_typeinfo_string_for_type (target);

755        name_decl = build_lang_decl (VAR_DECL, name_name, name_type);

756       

757        DECL_ARTIFICIAL (name_decl) = 1;

758        TREE_READONLY (name_decl) = 1;

759        TREE_STATIC (name_decl) = 1;

760        DECL_EXTERNAL (name_decl) = 0;

761        TREE_PUBLIC (name_decl) = 1;

762        import_export_tinfo (name_decl, target, typeinfo_in_lib_p (target));

763        /* External name of the string containing the type's name has a

764          special name.  */

765        SET_DECL_ASSEMBLER_NAME (name_decl,

766                                       mangle_typeinfo_string_for_type (target));

767        DECL_INITIAL (name_decl) = name_string;

768        mark_used (name_decl);

769        pushdecl_top_level_and_finish (name_decl, name_string);

770      }

771   

772      vtable_ptr = TINFO_VTABLE_DECL (desc);

773      if (!vtable_ptr)

774      {

775        tree real_type;

776     

777        push_nested_namespace (abi_node);

778        real_type = xref_tag (class_type, TINFO_REAL_NAME (desc),

779                          true, false);

780        pop_nested_namespace (abi_node);

781     

782        if (!COMPLETE_TYPE_P (real_type))

783        {

784          /* We never saw a definition of this type, so we need to

785            tell the compiler that this is an exported class, as

786            indeed all of the __*_type_info classes are.  */

787          SET_CLASSTYPE_INTERFACE_KNOWN (real_type);

788          CLASSTYPE_INTERFACE_ONLY (real_type) = 1;

789        }

790   

791        vtable_ptr = get_vtable_decl (real_type, /*complete=*/1);

792        vtable_ptr = build_unary_op (ADDR_EXPR, vtable_ptr, 0);

793   

794        /* We need to point into the middle of the vtable.  */

795        vtable_ptr = build

796                   (PLUS_EXPR, TREE_TYPE (vtable_ptr), vtable_ptr,

797                    size_binop (MULT_EXPR,

798                        size_int (2 * TARGET_VTABLE_DATA_ENTRY_DISTANCE),

799                        TYPE_SIZE_UNIT (vtable_entry_type)));

800        TREE_CONSTANT (vtable_ptr) = 1;

801   

802        TINFO_VTABLE_DECL (desc) = vtable_ptr;

803      }

804   

805      init = tree_cons (NULL_TREE, vtable_ptr, init);

806     

807      init = tree_cons (NULL_TREE, decay_conversion (name_decl), init);

808     

809      init = build_constructor (NULL_TREE, nreverse (init));

810      TREE_HAS_CONSTRUCTOR (init) = TREE_CONSTANT (init) = TREE_STATIC (init) = 1;

811       init = tree_cons (NULL_TREE, init, NULL_TREE);

812     

813      return init;

814    }

 

前面看到伪tinfo类型是没有构建vatble的。如果没有vtable,表明真实的tinfo对象还没有构建过。注意778行的TINFO_REAL_NAME保存的是真实tinfo对象类型的名字。而xref_tag的第三个实参是true,它实际在__cxxabiv1名字空间中查找这个指定的类型。Vtable的构建是个相当复杂的过程,我们以后再来看这个过程。这里,只要知道vtable的索引并不是从0开始的,编译器需要vtable的开头2项来放置tinfo对象,及该类作为基类到该考虑派生类基址的偏移。因此vtable指针指向的是vtable的第3项。另外,因为tinfo对象都是类的静态成员,因此在810行设置TREE_STATIC,表明应该为其分配静态内存。