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

原创 2011年01月15日 11:09:00

5.12.5.2.2.2.1.4.    完成具现

现在继续我们模板具现例子的处理,在 前端部分的初始化 一节,我们已经看到当使用命令行选项 -frepo 时, init_repo 会产生一个 .rpo 文件,或读入一个已经存在的 .rpo 文件。下面的 repo_template_used 将在对应的 .rpo 文件中记录该模板具现的使用,这个信息将被编译器及 collect2 GCC 自带的链接器)在以后使用。

 

instantiate_class_template (continue)

 

5473     /* Clear this now so repo_template_used is happy.  */

5474     TYPE_BEING_DEFINED (type) = 0;

5475     repo_template_used (type);

5476  

5477     /* Now that the class is complete, instantiate default arguments for

5478       any member functions. We don't do this earlier because the

5479       default arguments may reference members of the class.  */

5480     if (!PRIMARY_TEMPLATE_P (template))

5481       for (t = TYPE_METHODS (type); t; t = TREE_CHAIN (t))

5482         if (TREE_CODE (t) == FUNCTION_DECL

5483            /* Implicitly generated member functions will not have template

5484              information; they are not instantiations, but instead are

5485              created "fresh" for each instantiation.  */

5486            && DECL_TEMPLATE_INFO (t))

5487           tsubst_default_arguments (t);

5488  

5489     popclass ();

5490     pop_from_top_level ();

5491     pop_deferring_access_checks ();

5492     pop_tinst_level ();

5493  

5494     if (TYPE_CONTAINS_VPTR_P (type))

5495       keyed_classes = tree_cons (NULL_TREE, type, keyed_classes);

5496  

5497     return type;

5498   }

 

然后记得通过 popclass 退出类的作用域;接着通过 pop_from_top_level 恢复原有的作用域。还有递减 tinst_depth 并通过 pop_tinst_level 更新 current_tinst_level ,它是 EXPR_WITH_FILE_LOCATION 类型的节点。因为类 C 包含 vtable ,它被视为锁定类( keyed class )。

instantiate_class_template 退出,我们回到 complete_type ,然后 layout_var_decl 。这个模板是在函数 main 中具现,因此 current_function_decl 指向该函数的作用域,那么在 layout_var_decl 4100 行, push_local_name 把这个变量加入函数的作用域。

 

948    static void

949    push_local_name (tree decl)                                                                            in decl.c

950    {

951      size_t i, nelts;

952      tree t, name;

953   

954      timevar_push (TV_NAME_LOOKUP);

955      if (!local_names)

956        VARRAY_TREE_INIT (local_names, 8, "local_names");

957   

958      name = DECL_NAME (decl);

959   

960      nelts = VARRAY_ACTIVE_SIZE (local_names);

961      for (i = 0; i < nelts; i++)

962      {

963        t = VARRAY_TREE (local_names, i);

964        if (DECL_NAME (t) == name)

965        {

966           if (!DECL_LANG_SPECIFIC (decl))

967             retrofit_lang_decl (decl);

968           DECL_LANG_SPECIFIC (decl)->decl_flags.u2sel = 1;

969           if (DECL_LANG_SPECIFIC (t))

970              DECL_DISCRIMINATOR (decl) = DECL_DISCRIMINATOR (t) + 1;

971           else

972             DECL_DISCRIMINATOR (decl) = 1;

973   

974           VARRAY_TREE (local_names, i) = decl;

975           timevar_pop (TV_NAME_LOOKUP);

976           return ;

977        }

978      }

979   

980      VARRAY_PUSH_TREE (local_names, decl);

981      timevar_pop (TV_NAME_LOOKUP);

982    }

 

上面的 local_names 是一个访问 cfun language 部分 x_local_names 域的宏,这个域是一个可变的数组,保存了在由 cfun 所表示的函数中声明的布局名字。

5.12.5.2.2.2.1.5.    完成 VAR_DECL

现在我们回到了 cp_finish_decl ,曙光就在前面!

 

cp_finish_decl (continue)

 

4962     /* Output the assembler code and/or RTL code for variables and functions,

4963       unless the type is an undefined structure or union.

4964       If not, it will get done when the type is completed.  */

4965     if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL)

4966     {

4967       if (TREE_CODE (decl) == VAR_DECL)

4968         maybe_commonize_var (decl);

4969  

4970       make_rtl_for_nonlocal_decl (decl, init, asmspec);

4971  

4972       if (TREE_CODE (type) == FUNCTION_TYPE

4973          || TREE_CODE (type) == METHOD_TYPE)

4974        abstract_virtuals_error (decl,

4975                           strip_array_types (TREE_TYPE (type)));

4976       else if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)

4977       {

           

4986       }

4987       else

4988         abstract_virtuals_error (decl, type);

4989  

4990       if (TREE_CODE (decl) == FUNCTION_DECL

4991          || TREE_TYPE (decl) == error_mark_node)

4992         /* No initialization required.  */

4993         ;

4994       else if (DECL_EXTERNAL (decl)

4995             && ! (DECL_LANG_SPECIFIC (decl)

4996                  && DECL_NOT_REALLY_EXTERN (decl)))

4997       {

4998         if (init)

4999           DECL_INITIAL (decl) = init;

5000       }

5001       else

5002       {

5003         /* A variable definition.  */

5004          if (DECL_FUNCTION_SCOPE_P (decl))

5005         {

5006           /* This is a local declaration.  */

5007           maybe_inject_for_scope_var (decl);

5008           /* Initialize the local variable.  */

5009           if (processing_template_decl)

5010           {

5011             if (init || DECL_INITIAL (decl) == error_mark_node)

5012               DECL_INITIAL (decl) = init;

5013           }

5014           else if (!TREE_STATIC (decl))

5015             initialize_local_var (decl, init);

5016         }

5017  

5018         /* If a variable is defined, and then a subsequent

5019           definintion with external linkage is encountered, we will

5020           get here twice for the same variable. We want to avoid

5021           calling expand_static_init more than once. For variables

5022           that are not static data members, we can call

5023           expand_static_init only when we actually process the

5024           initializer. It is not legal to redeclare a static data

5025           member, so this issue does not arise in that case.  */

5026         if (var_definition_p && TREE_STATIC (decl))

5027           expand_static_init (decl, init);

5028       }

5029   finish_end0:

5030  

5031       /* Undo call to `pushclass' that was done in `start_decl'

5032          due to initialization of qualified member variable.

5033          i.e., Foo::x = 10;  */

5034       {

5035         tree context = CP_DECL_CONTEXT (decl);

5036         if (context

5037            && TYPE_P (context)

5038            && (TREE_CODE (decl) == VAR_DECL

5039                /* We also have a pushclass done that we need to undo here

5040                     if we're at top level and declare a method.  */

5041                || TREE_CODE (decl) == FUNCTION_DECL)

5042            /* If size hasn't been set, we're still defining it,

5043               and therefore inside the class body; don't pop

5044               the binding level..  */

5045            && COMPLETE_TYPE_P (context)

5046            && context == current_class_type )

5047           pop_nested_class ();

5048       }

5049     }

5050  

5051     /* If a CLEANUP_STMT was created to destroy a temporary bound to a

5052       reference, insert it in the statement-tree now.  */

5053     if (cleanup)

5054       add_stmt (cleanup);

5055  

5056   finish_end:

5057  

5058     if (was_readonly)

5059       TREE_READONLY (decl) = 1;

5060  

5061     /* If this was marked 'used', be sure it will be output.  */

5062     if (lookup_attribute ("used", DECL_ATTRIBUTES (decl)))

5063       mark_referenced (DECL_ASSEMBLER_NAME (decl));

5064   }

 

在函数剩余的代码里,为了把模板的具现 obj_ 按照局部变量来处理,在 5015 行调用了 initialize_local_var

 

698    static void

4699   initialize_local_var (tree decl, tree init)                                                              in decl.c

4700   {

4701     tree type = TREE_TYPE (decl);

4702     tree cleanup;

4703  

4704     my_friendly_assert (TREE_CODE (decl) == VAR_DECL

4705                       || TREE_CODE (decl) == RESULT_DECL,

4706                      20021010);

4707     my_friendly_assert (!TREE_STATIC (decl), 20021010);

4708  

4709     if (DECL_SIZE (decl) == NULL_TREE)

4710     {

4711       /* If we used it already as memory, it must stay in memory.  */

4712       DECL_INITIAL (decl) = NULL_TREE;

4713       TREE_ADDRESSABLE (decl) = TREE_USED (decl);

4714     }

4715  

4716     if (DECL_SIZE (decl) && type != error_mark_node)

4717     {

4718       int already_used;

4719  

4720        /* Compute and store the initial value.  */

4721       already_used = TREE_USED (decl) || TREE_USED (type);

4722  

4723       /* Perform the initialization.  */

4724       if (init)

4725       {

4726         int saved_stmts_are_full_exprs_p;

4727  

4728         my_friendly_assert (building_stmt_tree (), 20000906);

4729         saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p ();

4730         current_stmt_tree ()->stmts_are_full_exprs_p = 1;

4731         finish_expr_stmt (init);

4732         current_stmt_tree ()->stmts_are_full_exprs_p =

4733            saved_stmts_are_full_exprs_p;

4734          }

4735  

4736       /* Set this to 0 so we can tell whether an aggregate which was

4737         initialized was ever used. Don't do this if it has a

4738         destructor, so we don't complain about the 'resource

4739         allocation is initialization' idiom. Now set

4740         attribute((unused)) on types so decls of that type will be

4741         marked used. (see TREE_USED, above.)  */

4742       if (TYPE_NEEDS_CONSTRUCTING (type)

4743          && ! already_used

4744          && TYPE_HAS_TRIVIAL_DESTRUCTOR (type)

4745          && DECL_NAME (decl))

4746         TREE_USED (decl) = 0;

4747       else if (already_used)

4748         TREE_USED (decl) = 1;

4749     }

4750  

4751     /* Generate a cleanup, if necessary.  */

4752     cleanup = cxx_maybe_build_cleanup (decl);

4753     if (DECL_SIZE (decl) && cleanup)

4754       finish_decl_cleanup (decl, cleanup);

4755   }

 

这里参数 init 来自声明符中跟在“ = ”后面的部分,它是由程序员指定的初始值,而不是在前面我们看到的由编译器指定的。显然,对于我们的例子, init NULL

接着看 4752 行的 cxx_maybe_build_cleanup 。编译器可能会插入代码来清除局部变量,在它退出其作用域时。

 

11215 tree

11216 cxx_maybe_build_cleanup (tree decl)                                                               in decl.c

11217 {

11218   tree type = TREE_TYPE (decl);

11219

11220   if (type != error_mark_node && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))

11221   {

11222     int flags = LOOKUP_NORMAL|LOOKUP_DESTRUCTOR;

11223     tree rval;

11224

11225     if (TREE_CODE (type) == ARRAY_TYPE)

11226       rval = decl;

11227     else

11228     {

11229       cxx_mark_addressable (decl);

11230       rval = build_unary_op (ADDR_EXPR, decl, 0);

11231     }

11232

11233     /* Optimize for space over speed here.  */

11234     if (! TYPE_USES_VIRTUAL_BASECLASSES (type)

11235          || flag_expensive_optimizations )

11236       flags |= LOOKUP_NONVIRTUAL;

11237

11238     rval = build_delete (TREE_TYPE (rval), rval,

11239                     sfk_complete_destructor, flags, 0);

11240

11241     if (TYPE_USES_VIRTUAL_BASECLASSES (type)

11242         && ! TYPE_HAS_DESTRUCTOR (type))

11243       rval = build_compound_expr (rval, build_vbase_delete (type, decl));

11244

11245     return rval;

11246   }

11247   return NULL_TREE;

11248 }

 

断言 TYPE_HAS_NONTRIVIAL_DESTRUCTOR true ,如果该类型的析构函数不是平凡的(对于某个类,如果我们没有定义析构函数,并且对于所有的基类及非静态数据成员, TYPE_HAS_NONTRIVIAL_DESTRUCTOR false ,那么对于这个类, TYPE_HAS_NONTRIVIAL_DESTRUCTOR 返回 false )。

这就是为什么如果析构函数不做任何事就不要定义它。虽然编译器会为你添加一个隐含的析构函数,但只要它的基类及非静态数据成员都具有平凡析构函数,它就会被标记为平凡。结果,在这里不会产生用于清除的代码。

如果期望清除操作,首先从局部变量 decl 构建出一个 ADDR_EXPR ,因为 delete 操作符总是作用于指针。在程序员使用 register 强制把变量放入寄存器的情形下, cxx_mark_addressable 将修改所产生的代码以表示把这个局部变量放入栈中。

 

2866   tree

2867   build_delete (tree type, tree addr, special_function_kind auto_delete,                    in init.c

2868              int flags, int use_global_delete)

2869   {

2870     tree expr;

2871  

2872     if (addr == error_mark_node)

2873       return error_mark_node;

2874  

2875     /* Can happen when CURRENT_EXCEPTION_OBJECT gets its type

2876       set to `error_mark_node' before it gets properly cleaned up.  */

2877     if (type == error_mark_node)

2878       return error_mark_node;

2879  

2880     type = TYPE_MAIN_VARIANT (type);

2881  

2882     if (TREE_CODE (type) == POINTER_TYPE)

2883     {

         

2918     }

2919     else if (TREE_CODE (type) == ARRAY_TYPE)

2920     {

         

2930     }

2931     else

2932     {

2933       /* Don't check PROTECT here; leave that decision to the

2934          destructor. If the destructor is accessible, call it,

2935          else report error.  */

2936       addr = build_unary_op (ADDR_EXPR, addr, 0);

2937       if (TREE_SIDE_EFFECTS (addr))

2938         addr = save_expr (addr);

2939  

2940       addr = convert_force (build_pointer_type (type), addr, 0);

2941     }

2942  

2943     my_friendly_assert (IS_AGGR_TYPE (type), 220);

2944  

2945     if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))

2946     {

2947       if (auto_delete != sfk_deleting_destructor)

2948         return void_zero_node;

2949  

2950       return build_op_delete_call

2951              (DELETE_EXPR, addr, cxx_sizeof_nowarn (type),

2952               LOOKUP_NORMAL | (use_global_delete * LOOKUP_GLOBAL),

2953               NULL_TREE);

2954     }

2955     else

2956     {

2957       tree do_delete = NULL_TREE;

2958       tree ifexp;

2959  

2960       my_friendly_assert (TYPE_HAS_DESTRUCTOR (type), 20011213);

2961  

2962       /* For `::delete x', we must not use the deleting destructor

2963          since then we would not be sure to get the global `operator

2964          delete'.  */

2965       if (use_global_delete && auto_delete == sfk_deleting_destructor)

2966       {

2967         /* We will use ADDR multiple times so we must save it.  */

2968         addr = save_expr (addr);

2969         /* Delete the object.  */

2970         do_delete = build_builtin_delete_call (addr);

2971         /* Otherwise, treat this like a complete object destructor

2972              call.  */

2973         auto_delete = sfk_complete_destructor;

2974       }

2975       /* If the destructor is non-virtual, there is no deleting

2976         variant. Instead, we must explicitly call the appropriate

2977          `operator delete' here.  */

2978       else if (!DECL_VIRTUAL_P (CLASSTYPE_DESTRUCTORS (type))

2979             && auto_delete == sfk_deleting_destructor)

2980       {

2981         /* We will use ADDR multiple times so we must save it.  */

2982         addr = save_expr (addr);

2983         /* Build the call.  */

2984         do_delete = build_op_delete_call (DELETE_EXPR,

2985                                     addr,

2986                                     cxx_sizeof_nowarn (type),

2987                                      LOOKUP_NORMAL,

2988                                     NULL_TREE);

2989         /* Call the complete object destructor.  */

2990         auto_delete = sfk_complete_destructor;

2991       }

2992       else if (auto_delete == sfk_deleting_destructor

2993              && TYPE_GETS_REG_DELETE (type))

2994       {

2995         /* Make sure we have access to the member op delete, even though

2996            we'll actually be calling it from the destructor.  */

2997         build_op_delete_call (DELETE_EXPR, addr, cxx_sizeof_nowarn (type),

2998                           LOOKUP_NORMAL, NULL_TREE);

2999       }

3000  

3001       expr = build_dtor_call (build_indirect_ref (addr, NULL),

3002                           auto_delete, flags);

3003       if (do_delete)

3004         expr = build (COMPOUND_EXPR, void_type_node, expr, do_delete);

3005  

3006       if (flags & LOOKUP_DESTRUCTOR)

3007         /* Explicit destructor call; don't check for null pointer.  */

3008         ifexp = integer_one_node;

3009       else

3010         /* Handle deleting a null pointer.  */

3011         ifexp = fold (cp_build_binary_op (NE_EXPR, addr, integer_zero_node));

3012  

3013       if (ifexp != integer_one_node)

3014         expr = build (COND_EXPR, void_type_node,

3015                    ifexp, expr, void_zero_node);

3016  

3017       return expr;

3018     }

3019   }

 

指示符 sfk_complete_destructor 表示析构对象但不回收内存,而 sfk_deleting_destructor 则要求回收对象的内存。如果 type 是一个类, delete 操作符可以在类里被重载( 2993 行的断言 TYPE_GETS_REG_DELETE 会识别出这个情形)。

2945 行是对平凡析构函数一个特殊处理。注意到这个函数不仅用于清除越出其作用域的局部变量,还被 delete 表达式用于释放堆上分配的对象。如果没有内存需要被回收,它不做任何事(而是返回 void_zero_node ),否则仅是为 delete 表达式构建节点(类可能会重载 delete 操作符, build_op_delete_call 将找出适合使用的操作符)。

然后在调用 3001 行的 build_dtor_call 之前,首先要设置好参数 auto_delete 。看到在 2973 2990 行,它被设置为 sfk_complete_destructor ,表示 build_dtor_call 要找出一个析构函数来析构包括虚拟基类的整个对象。而同时 do_delete 指向这个 delete 表达式。注意到断言 TYPE_GETS_REG_DELETE 只是显示在派生树中是否有重载的 delete 操作符;它可能在基类中定义,而如果这个 delete 操作符被声明为 private ,则它不能从其派生类中访问,因此在 2997 行, build_op_delete_call 将执行这个检查。

回到 cxx_maybe_build_cleanup ,如果一个类包含虚拟基类但没有定义析构函数,那么它不会析构这些虚拟基类。但对虚拟基类的析构是必须的,因此对应的析构操作由 11243 行的 build_vbase_delete 来产生。

 

3108   tree

3109   build_vbase_delete (tree type, tree decl)                                                             in init.c

3110   {

3111     tree vbases = CLASSTYPE_VBASECLASSES (type);

3112     tree result;

3113     tree addr = build_unary_op (ADDR_EXPR, decl, 0);

3114  

3115     my_friendly_assert (addr != error_mark_node, 222);

3116  

3117     for (result = convert_to_void (integer_zero_node, NULL);

3118         vbases; vbases = TREE_CHAIN (vbases))

3119     {

3120       tree base_addr = convert_force

3121             (build_pointer_type (BINFO_TYPE (TREE_VALUE (vbases))), addr, 0);

3122       tree base_delete = build_delete

3123              TREE_TYPE (base_addr), base_addr, sfk_base_destructor,

3124              LOOKUP_NORMAL|LOOKUP_DESTRUCTOR, 0);

3125        

3126       result = build_compound_expr (result, base_delete);

3127     }

3128     return result;

3129   }

 

再一次看到,如果我们不定义析构函数,或析构函数是平凡的,不会产生任何东西,除了回收内存的行动。而如果产生了某些代码,它被构建入一个 CLEANUP_STMT ,并插入正在构建的语句树中。

 

1164   void

1165   finish_decl_cleanup (tree decl, tree cleanup)                                        in semantics.c

1166   {

1167     add_stmt (build_stmt (CLEANUP_STMT, decl, cleanup));

1168   }

 

相关文章推荐

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

5.13.4.3.              迭代 – 发布全局聚集类的构造函数/ 析构函数接着在这个DO WHILE循环中,在2651行static_aggregates是一个链表,它包含了...

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

5.13.3.      为基本类型产生tinfo操持完PCH文件,回到finish_file中继续为汇编代码的生成而奋斗。接下来编译器将为基本类型准备tinfo。 finish_file (cont...

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

5.13.5.2.2.1.1.3.    发布汇编代码回到output_constant_def_contents,如果变量有大于1字节的对齐要求,还要调用ASM_OUTPUT_ALIGN来输出这个...

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

5.13.5.3.2.2.3.          普通的内联函数处理完了强制内联函数(使用“always_inline”)后,剩下的函数由编译器便宜行事。只要我们不通过编译选项-fno-inline...

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

5.13.5.2.2.2.  变量输出初始值后,回到assemble_variable,接下来处理对应的变量。 assemble_variable (continue) 1468  resolve_...

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

5.13.1.1.3.2.  对确定类型的引用对于已确定的类型,reference_binding用下面的代码来处理。 reference_binding (continue) 958      ...

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

5.13.1.1.2.        用户定义转换序列如果标准转换不奏效,在implicit_conversion的1107行conv将是NULL。它将要看是否有可用的用户定义的转换。这涉及重载解析...

GCC-3.4.6源代码学习笔记

大约4年前,我加入了GDNT - 北电网络在中国的合资企业,参与3G UMTS无线接入网的研发工作。与GCC有了第一次亲密的接触(之前使用的是MS的VC)。彼时,北电在其诸如,UMTS、CDMA、及自...

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

5.13.5.3.2.2.1.          按调用次序排序为了下面处理的方便起见,在1253行,把所有的cgraph_node按对应函数调用的次序排序,把有向图转化为队列(其满足拓扑排序(to...

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

5.13.5.      代码分析及优化5.13.5.1.              预备知识- 别名集分析5.13.5.1.1.        别名集概念在维基百科(wikipedia)中,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:GCC-3.4.6源代码学习笔记(143)
举报原因:
原因补充:

(最多只允许输入30个字)