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

5.13.4.3.              迭代 发布全局聚集类的构造函数/ 析构函数

接着在这个 DO WHILE 循环中,在 2651 static_aggregates 是一个链表,它包含了在全局绑定域中具有构造函数或析构函数的聚集类。正如我们已经看过,编译器需要确保在进入“ main ”函数之前,所有全局变量必须已经分配了内存及被初始化。

5.13.4.3.1.        修整需要初始化的变量

因此在 2651 行, prune_vars_needing_no_initialization 被调用来检查是否有这样的对象。看到 static_aggregates 是一个 tree_list 链表,在其中的节点中, purpose 域是初始值,而 value 是这个对象的 VAR_DECL 。这个函数遍历这个链表并移除不需要初始化的节点,这包括外部声明的对象,及已经出错的对象(记住在遇到错误时,编译器尽可能地完成处理)。

 

2332   static tree

2333   prune_vars_needing_no_initialization (tree *vars)                                         in decl2.c

2334   {

2335     tree *var = vars;

2336     tree result = NULL_TREE;

2337  

2338     while (*var)

2339     {

2340       tree t = *var;

2341       tree decl = TREE_VALUE (t);

2342       tree init = TREE_PURPOSE (t);

2343  

2344       /* Deal gracefully with error.  */

2345       if (decl == error_mark_node)

2346       {

2347         var = &TREE_CHAIN (t);

2348         continue ;

2349       }

2350  

2351       /* The only things that can be initialized are variables.  */

2352       my_friendly_assert (TREE_CODE (decl) == VAR_DECL, 19990420);

2353  

2354       /* If this object is not defined, we don't need to do anything

2355         here.  */

2356       if (DECL_EXTERNAL (decl))

2357       {

2358         var = &TREE_CHAIN (t);

2359         continue ;

2360       }

2361  

2362       /* Also, if the initializer already contains errors, we can bail

2363         out now.  */

2364       if (init && TREE_CODE (init) == TREE_LIST

2365          && value_member (error_mark_node, init))

2366       {

2367         var = &TREE_CHAIN (t);

2368          continue ;

2369       }

2370  

2371       /* This variable is going to need initialization and/or

2372         finalization, so we add it to the list.  */

2373       *var = TREE_CHAIN (t);

2374       TREE_CHAIN (t) = result;

2375       result = t;

2376     }

2377  

2378     return result;

2379   }

 

如果有对象遗留下来,就要调用 start_static_storage_duration_function 来产生一个执行初始化的函数。注意到如果在迭代过程中,新的节点被插入 static_aggregates ,在下一次迭代中将产生一个新的函数。因此在这里为每次全局对象得到处理的迭代递增参数 count

下面的 SSDF_IDENTIFIER 被定义为“ __static_initialization_and_destruction ”,这里所产生函数的名字将是“ __static_initialization_and_destruction0 ”,依此类推。看到这个函数具有原型:“ void __static_initialization_and_destruction0 (int, int) ”。并且这个函数不是全局可见的。

 

2009   static tree

2010   start_static_storage_duration_function (unsigned count)                              in decl2.c

2011   {

2012     tree parm_types;

2013     tree type;

2014     tree body;

2015     char id[sizeof (SSDF_IDENTIFIER) + 1 /* '/0' */ + 32];

2016  

2017     /* Create the identifier for this function. It will be of the form

2018       SSDF_IDENTIFIER_<number>.  */

2019     sprintf (id, "%s_%u", SSDF_IDENTIFIER, count);

2020  

2021     /* Create the parameters.  */

2022     parm_types = void_list_node;

2023     parm_types = tree_cons (NULL_TREE, integer_type_node, parm_types);

2024     parm_types = tree_cons (NULL_TREE, integer_type_node, parm_types);

2025     type = build_function_type (void_type_node, parm_types);

2026  

2027     /* Create the FUNCTION_DECL itself.  */

2028     ssdf_decl = build_lang_decl (FUNCTION_DECL,

2029                             get_identifier (id),

2030                             type);

2031     TREE_PUBLIC (ssdf_decl ) = 0;

2032     DECL_ARTIFICIAL (ssdf_decl ) = 1;

2033  

2034     /* Put this function in the list of functions to be called from the

2035       static constructors and destructors.  */

2036     if (!ssdf_decls )

2037     {

2038       VARRAY_TREE_INIT (ssdf_decls , 32, "ssdf_decls");

2039  

2040       /* Take this opportunity to initialize the map from priority

2041         numbers to information about that priority level.  */

2042       priority_info_map = splay_tree_new (splay_tree_compare_ints,

2043                                      /*delete_key_fn=*/ 0,

2044                                      /*delete_value_fn=*/

2045                                      (splay_tree_delete_value_fn) &free);

2046  

2047       /* We always need to generate functions for the

2048         DEFAULT_INIT_PRIORITY so enter it now. That way when we walk

2049         priorities later, we'll be sure to find the

2050         DEFAULT_INIT_PRIORITY.  */

2051       get_priority_info (DEFAULT_INIT_PRIORITY);

2052     }

2053  

2054     VARRAY_PUSH_TREE (ssdf_decls , ssdf_decl ) ;

2055  

2056     /* Create the argument list.  */

2057     initialize_p_decl = cp_build_parm_decl

2058           (get_identifier (INITIALIZE_P_IDENTIFIER), integer_type_node);

2059     DECL_CONTEXT (initialize_p_decl ) = ssdf_decl ;

2060     TREE_USED (initialize_p_decl ) = 1;

2061     priority_decl = cp_build_parm_decl

2062           (get_identifier (PRIORITY_IDENTIFIER), integer_type_node);

2063     DECL_CONTEXT (priority_decl ) = ssdf_decl ;

2064     TREE_USED (priority_decl ) = 1;

2065  

2066     TREE_CHAIN (initialize_p_decl ) = priority_decl ;

2067     DECL_ARGUMENTS (ssdf_decl ) = initialize_p_decl ;

2068  

2069     /* Put the function in the global scope.  */

2070     pushdecl (ssdf_decl );

2071  

2072     /* Start the function itself. This is equivalent to declaring the

2073       function as:

2074  

2075          static void __ssdf (int __initialize_p, init __priority_p);

2076         

2077       It is static because we only need to call this function from the

2078       various constructor and destructor functions for this module.  */

2079     start_function (/*specs=*/ NULL_TREE,

2080                 ssdf_decl ,

2081                 /*attrs=*/ NULL_TREE,

2082                 SF_PRE_PARSED);

2083  

2084     /* Set up the scope of the outermost block in the function.  */

2085     body = begin_compound_stmt (/*has_no_scope=*/ false);

2086  

2087     /* This function must not be deferred because we are depending on

2088       its compilation to tell us what is TREE_SYMBOL_REFERENCED.  */

2089     current_function_cannot_inline

2090         = "static storage duration functions cannot be inlined";

2091  

2092     return body;

2093   }

 

上面在 2058 行, INITIALIZE_P_IDENTIFIER 定义为“ __initialize_p ”,并且在 2062 行, PRIORITY_IDENTIFIER 定义为“ __priority ”;因此该函数的声明事实上是:“ void __static_initialization_and_destruction0 (int __initialize_p, int __priority) ”。

2051 行, DEFAULT_INIT_PRIORITY 65535 0xffff ),它是最低优先级。而 get_priority_info 在伸展树 priority_info_map 中产生这个优先级的项。

对于需要初始化的全局聚集类变量,还要调用下面的函数来完成前端的处理。

 

2384   static void

2385   write_out_vars (tree vars)                                                                        in decl2c

2386   {

2387     tree v;

2388  

2389     for (v = vars; v; v = TREE_CHAIN (v))

2390       if (!var_finalized_p (TREE_VALUE (v)))

2391         rest_of_decl_compilation (TREE_VALUE (v), 0, 1, 1);

2392   }

 

看到 rest_of_decl_compilation 的最后两个参数都是 1 ,并且这些变量的节点必须是 VAR_DECL ,因此在 rest_of_decl_compilation 中, cgraph_varpool_finalize_decl 被调用来产生相应的 cgraph_varpool_nodes

5.13.4.3.2.        产生初始化代码

对于每个全局变量, do_static_initialization 产生用于初始化的代码。

 

2276   static void

2277   do_static_initialization (tree decl, tree init)                                                  in decl2.c

2278   {

2279     tree guard_if_stmt;

2280  

2281     /* Set up for the initialization.  */

2282     guard_if_stmt

2283       = start_static_initialization_or_destruction (decl,

2284                                          /*initp=*/ 1);

2285  

2286     /* Perform the initialization.  */

2287     if (init)

2288       finish_expr_stmt (init);

2289  

2290     /* If we're using __cxa_atexit, register a a function that calls the

2291       destructor for the object.  */

2292     if (flag_use_cxa_atexit )

2293       register_dtor_fn (decl);

2294  

2295     /* Finsh up.  */

2296     finish_static_initialization_or_destruction (guard_if_stmt);

2297   }

 

下面在 2151 行, DECL_INIT_PRIORITY 通过属性 init_priority 设置。【 6 】给出如下细节。

在标准 C++ 中,定义在名字空间的对象要保证,严格按照在一个给定的编译单元中它们定义出现的次序来初始化。跨编译单元对初始化则没有任何担保。不过, GNU C++ 允许用户,通过 init_priority 属性指定一个相对优先级,来控制定义在一个名字空间中的对象的初始化次序,相对优先级是一个整型常量表达式,目前取值在 101 65535 (包括)之间。越小的数值代表越高的优先级。

在下面的例子中,正常地 A 将在 B 之前被创建,但 init_priority 属性保留了这个次序:

Some_Class A __attribute__ ((init_priority (2000)));

Some_Class B __attribute__ ((init_priority (543)));

注意到优先级的确切值无关紧要;重要的是它们的相对次序。

[ 作者 ] :在 GCC 中,小于 100 的优先级保留作内部用途。

下面,在调用时参数 initp 1 ,表示我们正在进行初始化。

 

2140   static tree

2141   start_static_initialization_or_destruction (tree decl, int initp)                         in decl2.c

2142   {

2143     tree guard_if_stmt = NULL_TREE;

2144     int priority;

2145     tree cond;

2146     tree guard;

2147     tree init_cond;

2148     priority_info pi;

2149  

2150     /* Figure out the priority for this declaration.  */

2151     priority = DECL_INIT_PRIORITY (decl);

2152     if (!priority)

2153       priority = DEFAULT_INIT_PRIORITY;

2154  

2155     /* Remember that we had an initialization or finalization at this

2156       priority.  */

2157     pi = get_priority_info (priority);

2158     if (initp)

2159       pi->initializations_p = 1;

2160     else

2161       pi->destructions_p = 1;

2162  

2163     /* Trick the compiler into thinking we are at the file and line

2164       where DECL was declared so that error-messages make sense, and so

2165       that the debugger will show somewhat sensible file and line

2166       information.  */

2167     input_location = DECL_SOURCE_LOCATION (decl);

2168  

2169     /* Because of:

2170  

2171         [class.access.spec]

2172  

2173         Access control for implicit calls to the constructors,

2174         the conversion functions, or the destructor called to

2175         create and destroy a static data member is performed as

2176         if these calls appeared in the scope of the member's

2177         class. 

2178  

2179       we pretend we are in a static member function of the class of

2180       which the DECL is a member.  */

2181     if (member_p (decl))

2182     {

2183       DECL_CONTEXT (current_function_decl ) = DECL_CONTEXT (decl);

2184       DECL_STATIC_FUNCTION_P (current_function_decl ) = 1;

2185     }

2186    

2187     /* Conditionalize this initialization on being in the right priority

2188       and being initializing/finalizing appropriately.  */

2189     guard_if_stmt = begin_if_stmt ();

2190     cond = cp_build_binary_op (EQ_EXPR,

2191                             priority_decl ,

2192                             build_int_2 (priority, 0));

2193     init_cond = initp ? integer_one_node : integer_zero_node;

2194     init_cond = cp_build_binary_op (EQ_EXPR,

2195                                initialize_p_decl ,

2196                                init_cond);

2197     cond = cp_build_binary_op (TRUTH_ANDIF_EXPR, cond, init_cond);

2198  

2199     /* Assume we don't need a guard.  */

2200     guard = NULL_TREE;

2201     /* We need a guard if this is an object with external linkage that

2202       might be initialized in more than one place. (For example, a

2203       static data member of a template, when the data member requires

2204       construction.)  */

2205     if (TREE_PUBLIC (decl) && (DECL_COMMON (decl)

2206                              || DECL_ONE_ONLY (decl)

2207                              || DECL_WEAK (decl)))

2208     {

2209       tree guard_cond;

2210  

2211       guard = get_guard (decl);

2212  

2213       /* When using __cxa_atexit, we just check the GUARD as we would

2214         for a local static.  */

2215       if (flag_use_cxa_atexit )

2216       {

2217         /* When using __cxa_atexit, we never try to destroy

2218           anything from a static destructor.  */

2219         my_friendly_assert (initp, 20000629);

2220         guard_cond = get_guard_cond (guard);

2221       }

2222       /* If we don't have __cxa_atexit, then we will be running

2223         destructors from .fini sections, or their equivalents. So,

2224         we need to know how many times we've tried to initialize this

2225         object. We do initializations only if the GUARD is zero,

2226         i.e., if we are the first to initialize the variable. We do

2227         destructions only if the GUARD is one, i.e., if we are the

2228         last to destroy the variable.  */

2229       else if (initp)

2230         guard_cond

2231            = cp_build_binary_op (EQ_EXPR,

2232                                build_unary_op (PREINCREMENT_EXPR,

2233                                             guard,

2234                                             /*noconvert=*/ 1),

2235                               integer_one_node);

2236       else

2237         guard_cond

2238            = cp_build_binary_op (EQ_EXPR,

2239                               build_unary_op (PREDECREMENT_EXPR,

2240                                             guard,

2241                                             /*noconvert=*/ 1),

2242                                integer_zero_node);

2243  

2244       cond = cp_build_binary_op (TRUTH_ANDIF_EXPR, cond, guard_cond);

2245     }

2246  

2247     finish_if_stmt_cond (cond, guard_if_stmt);

2248  

2249     /* If we're using __cxa_atexit, we have not already set the GUARD,

2250       so we must do so now.  */

2251     if (guard && initp && flag_use_cxa_atexit )

2252       finish_expr_stmt (set_guard (guard));

2253  

2254     return guard_if_stmt;

2255   }

 

上面 flag_use_cxa_atexit 如果不是 0 ,表示我们使用 _cxa_atexit 为局部静态及全局对象注册析构函数。在我们当前的配置下,这个标记是 1 (它依赖于所使用的库)。

概要

int __cxa_atexit(void (*func) (void *), void * arg, void * dso_handle);

描述

__cxa_atexit() 注册一个将被 exit() ,或一个共享库卸载时,调用的析构函数。当一个共享库被卸载时,任何与这个共享库相关联的,由 dso_handle 确定的析构函数,将使用单个实参 arg 来调用,然后这个函数应该从在 exit() 中执行的函数列表中移除,或者被标记为完成。在对 exit() 的一个调用里,应该使用单个实参 arg 来调用任何剩下的已注册的函数。析构函数应该总是以它们注册的反序来调用(即,最后注册的函数应该被首先调用),

函数 __cxa_atexit() 被用于实现 atexit() ,如在 ISO POSIX (2003) 中所描述的。从一个应用的静态链接部分来调用 atexit(func) 应该等同于 __cxa_atexit(func, NULL, NULL)

__cxa_atexit() 不是代码标准( source standard );它只是二进制标准( binary standard )。

注意: atexit() 不是二进制标准;它只是代码标准。

在上面的函数里, current_function_decl 现在指向这里正在产生的初始化函数(参见在 start_static_storage_duration_function 2079 行调用的 start_function )。因此如果上面 2181 行的条件满足,表示要被初始化的对象是一个静态类成员,需要临时把 current_function_decl 伪装成在类中以静态形式出现。

显然,这个初始化函数需要一个 IF 块来检查该对象是否已经初始化了(这个条件检查是基于每个对象的)。在 C++ 中, IF 块引入一个新的局部绑定域。

 

449    tree

450    begin_if_stmt (void)                                                                         in semantics.c

451    {

452      tree r;

453      do_pushlevel (sk_block);

454      r = build_stmt (IF_STMT, NULL_TREE, NULL_TREE, NULL_TREE);

455      add_stmt (r);

456      return r;

457    }

 

注意到 IF_STMT 节点被返回给 guard_if_stmt 。除了保证这个静态或全局变量只被初始化一次,需要一个约束。

 

1797   tree

1798   get_guard (tree decl)                                                                               in decl2.c

1799   {

1800     tree sname;

1801     tree guard;

1802  

1803     sname = mangle_guard_variable (decl);

1804     guard = IDENTIFIER_GLOBAL_VALUE (sname);

1805     if (! guard)

1806     {

1807       tree guard_type;

1808  

1809       /* We use a type that is big enough to contain a mutex as well

1810         as an integer counter.  */

1811       guard_type = long_long_integer_type_node;

1812       guard = build_decl (VAR_DECL, sname, guard_type);

1813        

1814       /* The guard should have the same linkage as what it guards.  */

1815       TREE_PUBLIC (guard) = TREE_PUBLIC (decl);

1816       TREE_STATIC (guard) = TREE_STATIC (decl);

1817       DECL_COMMON (guard) = DECL_COMMON (decl);

1818       DECL_ONE_ONLY (guard) = DECL_ONE_ONLY (decl);

1819       if (TREE_PUBLIC (decl))

1820         DECL_WEAK (guard) = DECL_WEAK (decl);

1821        

1822       DECL_ARTIFICIAL (guard) = 1;

1823       TREE_USED (guard) = 1;

1824       pushdecl_top_level_and_finish (guard, NULL_TREE);

1825     }

1826     return guard;

1827   }

 

这个约束应该就像:“ static int something = 0; ”。为了给这些约束变量构建唯一的名字,修饰要发布代码的 decl 的名字是一个聪明的主意。看到在上面的代码中,约束的类型是 int64_t ,它足够容纳一个互斥量;另外这些变量被声明在全局名字空间里,如 1824 行所示。

并且如果 __cxa_atexit 是可用的,那么 get_guard_cond 构造了测试例子中的条件“ (!guard) ”的代码节点。

 

1851   tree

1852   get_guard_cond (tree guard)                                                                    in decl2.c

1853   {

1854     tree guard_value;

1855  

1856     /* Check to see if the GUARD is zero.  */

1857     guard = get_guard_bits (guard);

1858     guard_value = integer_zero_node;

1859     if (!same_type_p (TREE_TYPE (guard_value), TREE_TYPE (guard)))

1860       guard_value = convert (TREE_TYPE (guard), guard_value);

1861     return cp_build_binary_op (EQ_EXPR, guard, guard_value);

1862   }

 

在上面 1857 行,该函数构建了语句:“ *((char*) &guard) ”。因此测试条件应该是“ (!*((char*) &guard)) ”,而剩下的空间将被用于互斥量。

 

1832   static tree

1833   get_guard_bits (tree guard)                                                                       in decl2.c

1834   {

1835     /* We only set the first byte of the guard, in order to leave room

1836       for a mutex in the high-order bits.  */

1837     guard = build1 (ADDR_EXPR,

1838                  build_pointer_type (TREE_TYPE (guard)),

1839                  guard);

1840     guard = build1 (NOP_EXPR,

1841                  build_pointer_type (char_type_node),

1842                  guard);

1843     guard = build1 (INDIRECT_REF, char_type_node, guard);

1844  

1845     return guard;

1846   }

 

那么在 2247 行, finish_if_stmt_cond 关闭了这个 IF 块的条件,它应该是一个布尔值,所以 maybe_convert_cond 评估 cond 的条件,并尝试把它转换到一个布尔值。在这里我们跳过这个函数因为它将是另一个漫长的故事。

 

462    void

463    finish_if_stmt_cond (tree cond, tree if_stmt)                                       in semantics.c

464    {

465      cond = maybe_convert_cond (cond);

466      FINISH_COND (cond, if_stmt, IF_COND (if_stmt));

467    }

 

另一方面,如果 __cxa_atexit 不可用,取而代之使用引用计数。接着, set_guard 构建用于语句:“ guard = 1; ”的节点。

 

1867   tree

1868   set_guard (tree guard)                                                                              in decl2.c

1869   {

1870     tree guard_init;

1871  

1872     /* Set the GUARD to one.  */

1873     guard = get_guard_bits (guard);

1874     guard_init = integer_one_node;

1875     if (!same_type_p (TREE_TYPE (guard_init), TREE_TYPE (guard)))

1876       guard_init = convert (TREE_TYPE (guard), guard_init);

1877     return build_modify_expr (guard, NOP_EXPR, guard_init);

1878   }

 

因此要是 flag_use_cxa_atexit 不是 0 ,下面 C++ 形式的语句(事实上,产生的代码具有中间树形式)将被产生:

void __static_initialization_and_destruction0 (int __initialize_p, int __priority)

{

   static int decl0_guard = 0;

if (decl0_priority == __priority && __initialize_p == 1 && *((char*) &decl0_guard == 0)

{

   decl0_guard = 1;

}

回到 do_static_initialization ,如果出现了对象的初始值,象下面那样为之产生代码。注意到整个语句的类型被忽略了,例如,表达式“ int j = 5; ”具有整数类型并其值是 5 ,但这两者都被忽略了。因此在内部,通过把该表达式转换到一个 void 类型,显式地说明了这个忽略。

 

425    tree

426    finish_expr_stmt (tree expr)                                                              in semantics.c

427    {

428      tree r = NULL_TREE;

429   

430      if (expr != NULL_TREE)

431      {

432        if (!processing_template_decl )

433          expr = convert_to_void (expr, "statement");

434        else if (!type_dependent_expression_p (expr))

435          convert_to_void (build_non_dependent_expr (expr), "statement");

436         

437        r = add_stmt (build_stmt (EXPR_STMT, expr));

438      }

439   

440      finish_stmt ();

441   

442      return r;

443    }

 

正如我们在前面章节看到的,聚集类的初始值由节点 CONSTRUCTOR 开头。现在这个函数看起来就像:

void __static_initialization_and_destruction0 (int __initialize_p, int __priority)

{

   static int decl0_guard = 0;

if (decl0_priority == __priority && __initialize_p == 1 && *((char*) &decl0_guard == 0)

{

   decl0_guard = 1;

   decl0_constructor(decl0_init);

}

如果 __cxa_atexit 是可用的,编译器将用这个函数注册该对象的析构函数。下面的 register_dtor_fn 产生用于这个目的的代码。注意平凡析构函数在 5269 行被滤除。

 

5261   void

5262   register_dtor_fn (tree decl)                                                                              in decl.c

5263   {

5264     tree cleanup;

5265     tree compound_stmt;

5266     tree args;

5267     tree fcall;

5268  

5269     if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))

5270       return ;

5271  

5272     /* Call build_cleanup before we enter the anonymous function so that

5273       any access checks will be done relative to the current scope,

5274       rather than the scope of the anonymous function.  */

5275     build_cleanup (decl);

5276  

5277     /* Now start the function.  */

5278     cleanup = start_cleanup_fn ();

5279  

5280     /* Now, recompute the cleanup. It may contain SAVE_EXPRs that refer

5281       to the original function, rather than the anonymous one. That

5282       will make the back-end think that nested functions are in use,

5283       which causes confusion.  */

5284    

5285     push_deferring_access_checks (dk_no_check);

5286     fcall = build_cleanup (decl);

5287     pop_deferring_access_checks ();

5288  

5289     /* Create the body of the anonymous function.  */

5290     compound_stmt = begin_compound_stmt (/*has_no_scope=*/ false);

5291     finish_expr_stmt (fcall);

5292     finish_compound_stmt (compound_stmt);

5293     end_cleanup_fn ();

5294  

5295     /* Call atexit with the cleanup function.  */

5296     cxx_mark_addressable (cleanup);

5297     mark_used (cleanup);

5298     cleanup = build_unary_op (ADDR_EXPR, cleanup, 0);

5299     if (flag_use_cxa_atexit )

5300     {

5301       args = tree_cons (NULL_TREE,

5302                     build_unary_op (ADDR_EXPR, get_dso_handle_node (), 0),

5303                     NULL_TREE);

5304       args = tree_cons (NULL_TREE, null_pointer_node , args);

5305       args = tree_cons (NULL_TREE, cleanup, args);

5306     }

5307     else

5308       args = tree_cons (NULL_TREE, cleanup, NULL_TREE);

5309     finish_expr_stmt (build_function_call (get_atexit_node (), args));

5310   }

 

因为 build_cleanup 使用查找标记 LOOKUP_NORMAL (标识访问违规,并且如果匹配实参的成员函数找不到给出相关信息), LOOKUP_NONVIRTUAL (对找到的成员函数进行直接调用,即以 A::f 的形式,而不是通过 vtable ), LOOKUP_DESTRUCTOR (表示显式调用析构函数)来调用 build_delete 。在这个过程中将执行访问检查。因此 build_cleanup 首先被调用来保证没有访问错误,并且看到在由 start_cleanup_fn 构建的函数作用域中,对该函数的第二次调用之前,在 5285 行关闭访问检查。

 

1767   tree

1768   build_cleanup (tree decl)                                                                           in decl2.c

1769   {

1770     tree temp;

1771     tree type = TREE_TYPE (decl);

1772  

1773     /* This function should only be called for declarations that really

1774       require cleanups.  */

1775     my_friendly_assert (!TYPE_HAS_TRIVIAL_DESTRUCTOR (type), 20030106);

1776  

1777     /* Treat all objects with destructors as used; the destructor may do

1778       something substantive.  */

1779     mark_used (decl);

1780  

1781     if (TREE_CODE (type) == ARRAY_TYPE)

1782       temp = decl;

1783     else

1784     {

1785       cxx_mark_addressable (decl);

1786       temp = build1 (ADDR_EXPR, build_pointer_type (type), decl);

1787     }

1788     temp = build_delete (TREE_TYPE (temp), temp,

1789                      sfk_complete_destructor,

1790         LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR, 0);

1791     return temp;

1792   }

 

能被 __cxa_atexit 注册的函数必须具有原型“ void func (void *) ”,它不会匹配任何类的析构函数。因此 start_cleanup_fn 构建一个适用于这个注册的包装。注意在 5193 行的 push_to_top_level ,这个包装将被声明在全局域中。同样在 5196 行的 push_lang_context 只是把‘ extern “C” ’加在该函数声明的头部。

 

5183   static tree

5184   start_cleanup_fn (void)                                                                                   in decl.c

5185   {

5186     int old_interface_only = interface_only ;

5187     int old_interface_unknown = interface_unknown ;

5188     char name[32];

5189     tree parmtypes;

5190     tree fntype;

5191     tree fndecl;

5192  

5193     push_to_top_level ();

5194  

5195     /* No need to mangle this.  */

5196     push_lang_context (lang_name_c );

5197  

5198     interface_only = 0;

5199     interface_unknown = 1;

5200  

5201     /* Build the parameter-types.  */

5202     parmtypes = void_list_node;

5203     /* Functions passed to __cxa_atexit take an additional parameter.

5204       We'll just ignore it. After we implement the new calling

5205       convention for destructors, we can eliminate the use of

5206       additional cleanup functions entirely in the -fnew-abi case.  */

5207     if (flag_use_cxa_atexit )

5208       parmtypes = tree_cons (NULL_TREE, ptr_type_node, parmtypes);

5209     /* Build the function type itself.  */

5210     fntype = build_function_type (void_type_node, parmtypes);

5211     /* Build the name of the function.  */

5212     sprintf (name, "__tcf_%d", start_cleanup_cnt ++);

5213     /* Build the function declaration.  */

5214     fndecl = build_lang_decl (FUNCTION_DECL, get_identifier (name), fntype);

5215     /* It's a function with internal linkage, generated by the

5216       compiler.  */

5217     TREE_PUBLIC (fndecl) = 0;

5218     DECL_ARTIFICIAL (fndecl) = 1;

5219     /* Make the function `inline' so that it is only emitted if it is

5220       actually needed. It is unlikely that it will be inlined, since

5221       it is only called via a function pointer, but we avoid unnecessary

5222       emissions this way.  */

5223     DECL_INLINE (fndecl) = 1;

5224     DECL_DECLARED_INLINE_P (fndecl) = 1;

5225     DECL_INTERFACE_KNOWN (fndecl) = 1;

5226     /* Build the parameter.  */

5227     if (flag_use_cxa_atexit )

5228     {

5229       tree parmdecl;

5230  

5231       parmdecl = cp_build_parm_decl (NULL_TREE, ptr_type_node );

5232       DECL_CONTEXT (parmdecl) = fndecl;

5233       TREE_USED (parmdecl) = 1;

5234       DECL_ARGUMENTS (fndecl) = parmdecl;

5235     }

5236  

5237     pushdecl (fndecl);

5238     start_function (/*specs=*/ NULL_TREE, fndecl, NULL_TREE, SF_PRE_PARSED);

5239  

5240     interface_unknown = old_interface_unknown;

5241     interface_only = old_interface_only;

5242  

5243     pop_lang_context ();

5244  

5245     return current_function_decl ;

5246   }

 

注意到该包装函数是为每个变量构建的,其名字遵循这样的形式:“ __tcf_0” , “__tcf_1 ”, 以此类推。那么在上面的 5238 行, start_function 把这个函数作用域设为当前绑定域。接下来, 5290 ~ 5292 行把对这个析构函数的调用做成该包装函数的函数体,就像当调用 build_delete 时,使用标记 sfk_complete_destructor 来创建调用析构函数的代码。

 

5250   static void

5251   end_cleanup_fn (void)                                                                                     in decl.c

5252   {

5253     expand_or_defer_fn (finish_function (0));

5254  

5255     pop_from_top_level ();

5256   }

 

通过 end_cleanup_fn 完成了包装函数的定义,并恢复绑定域。在构建了这个包装函数后,它可以通过 __cxa_atexit 被注册。不过在这之前,首先要找到 dso_handle __cxa_atexit 的节点。

 

5165   static tree

5166   get_dso_handle_node (void)                                                                            in decl.c

5167   {

5168     if (dso_handle_node )

5169       return dso_handle_node ;

5170  

5171     /* Declare the variable.  */

5172     dso_handle_node = declare_global_var (get_identifier ("__dso_handle"),

5173                                      ptr_type_node );

5174  

5175     return dso_handle_node ;

5176   }

 

上面函数的参数 dso_handle 事实上是一个外部全局变量,它被定义在库中,并在加载共享库的过程中被使用。而对于应用中静态链接部分,向该参数传入 NULL 就足够了。

 

5181   tree

5182   declare_global_var (tree name, tree type)                                                          in decl.c

5183   {

5184     tree decl;

5185  

5186     push_to_top_level ();

5187     decl = build_decl (VAR_DECL, name, type);

5188     TREE_PUBLIC (decl) = 1;

5189     DECL_EXTERNAL (decl) = 1;

5190     DECL_ARTIFICIAL (decl) = 1;

5191     pushdecl (decl);

5192     cp_finish_decl (decl, NULL_TREE, NULL_TREE, 0);

5193     pop_from_top_level ();

5194  

5195     return decl;

5196   }

 

类似地, __cxa_atexit 的定义也是在库中。在这里 get_atexit_node 仅产生声明这个函数的代码,仿佛我们包含了相关头文件。

 

5102   static tree

5103   get_atexit_node (void)                                                                                    in decl.c

5104   {

5105     tree atexit_fndecl;

5106     tree arg_types;

5107     tree fn_type;

5108     tree fn_ptr_type;

5109     const char *name;

5110  

5111     if (atexit_node )

5112       return atexit_node ;

5113  

5114     if (flag_use_cxa_atexit )

5115     {

5116       /* The declaration for `__cxa_atexit' is:

5117  

5118          int __cxa_atexit (void (*)(void *), void *, void *)

5119  

5120         We build up the argument types and then then function type

5121         itself.  */

5122  

5123       /* First, build the pointer-to-function type for the first

5124         argument.  */

5125       arg_types = tree_cons (NULL_TREE, ptr_type_node , void_list_node);

5126       fn_type = build_function_type (void_type_node, arg_types);

5127       fn_ptr_type = build_function_type (fn_type);

5128       /* Then, build the rest of the argument types.  */

5129       arg_types = tree_cons (NULL_TREE, ptr_type_node , void_list_node);

5130       arg_types = tree_cons (NULL_TREE, ptr_type_node , arg_types);

5131       arg_types = tree_cons (NULL_TREE, fn_ptr_type, arg_types);

5132       /* And the final __cxa_atexit type.  */

5133       fn_type = build_function_type (integer_type_node, arg_types);

5134       fn_ptr_type = build_pointer_type (fn_type);

5135       name = "__cxa_atexit";

5136     }

5137     else

5138     {

5139       /* The declaration for `atexit' is:

5140  

5141          int atexit (void (*)());

5142  

5143         We build up the argument types and then then function type

5144         itself.  */

5145       fn_type = build_function_type (void_type_node, void_list_node);

5146       fn_ptr_type = build_pointer_type (fn_type);

5147       arg_types = tree_cons (NULL_TREE, fn_ptr_type, void_list_node);

5148       /* Build the final atexit type.  */

5149       fn_type = build_function_type (integer_type_node, arg_types);

5150       name = "atexit";

5151     }

5152  

5153     /* Now, build the function declaration.  */

5154     push_lang_context (lang_name_c );

5155     atexit_fndecl = build_library_fn_ptr (name, fn_type);

5156     mark_used (atexit_fndecl);

5157     pop_lang_context ();

5158     atexit_node = decay_conversion (atexit_fndecl);

5159  

5160     return atexit_node;

5161   }

 

然后 register_dtor_fn 的最后一部分为这个调用产生代码,并且在 do_static_initialization 2296 行的 finish_static_initialization_or_destruction 加入“ } ”,因此到目前为止产生的代码是:

extern void* __dso_handle;

int __cxa_atexit (void (*)(void *), void *, void *);

 

void __tcf_0 (void *)

{

  decl0.destructor();

}

// other __tcf_* function

 

void __static_initialization_and_destruction0 (int __initialize_p, int __priority)

{

   static int decl0_guard = 0;

if (decl0_priority == __priority && __initialize_p == 1 && *((char*) &decl0_guard == 0)

{

   decl0_guard = 1;

   decl0_constructor(decl0_init);

   __cxa_atexit(__tcf_0, 0, __dso_handle);

}

} ”使得‘ decl0_constructor ’超出作用域,通过它对象将被具现。并记得 current_function_decl DECL_CONTEXT DECL_STATIC_FUNCTION_P 域,在产生初始化静态类成员的代码时,可能发生改变,因此当完成初始化或析构时,总是恢复这些域。

 

2261   static void

2262   finish_static_initialization_or_destruct ion (tree guard_if_stmt)                      in decl2.c

2263   {

2264     finish_then_clause (guard_if_stmt);

2265     finish_if_stmt ();

2266  

2267     /* Now that we're done with DECL we don't need to pretend to be a

2268       member of its class any longer.  */

2269     DECL_CONTEXT (current_function_decl ) = NULL_TREE;

2270     DECL_STATIC_FUNCTION_P (current_function_decl ) = 0;

2271   }

 

注意到 if_stmt THEN_CLAUSE NULL

 

472    tree

473    finish_then_clause (tree if_stmt)                                                        in semantics.c

474    {

475      RECHAIN_STMTS (if_stmt, THEN_CLAUSE (if_stmt));

476      return if_stmt;

477    }

 

那么 finish_if_stmt 封闭了这个块,并从其退出。

 

497  void

498  finish_if_stmt (void)                                                                            in semantics.c

499  {

500    finish_stmt ();

501    do_poplevel ();

502  }

 

看到 do_static_initialization 被为每个全局 / 静态对象所调用,因此代码片段:

static int decl1_guard = 0;

if (decl1_priority == __priority && __initialize_p == 1 && *((char*) &decl1_guard == 0)

{

   decl1_guard = 1;

   decl1_constructor(decl1_init);

   __cxa_atexit(__tcf_1, 1__dso_handle);

}

为第二个对象所产生,以此类推。当初始化对象的所有代码都产生了,该初始化函数应该被相应地完成,就像如下。

 

2099 static void

2100 finish_static_storage_duration_function (tree body)                                      in decl2.c

2101 {

2102   /* Close out the function.  */

2103    finish_compound_stmt (body);

2104    expand_or_defer_fn (finish_function (0));

2105 }

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值