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