GCC-3.4.6源代码学习笔记(158)
来源:互联网 发布:局域网传输软件 编辑:程序博客网 时间:2024/06/08 17:26
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 /* Dealgracefully with error. */
2345 if (decl == error_mark_node)
2346 {
2347 var = &TREE_CHAIN (t);
2348 continue;
2349 }
2350
2351 /* The onlythings that can be initialized are variables. */
2352 my_friendly_assert (TREE_CODE (decl) ==VAR_DECL, 19990420);
2353
2354 /* If this objectis 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 theinitializer 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 variableis going to need initialization and/or
2372 finalization, so we add itto the list. */
2373 *var = TREE_CHAIN (t);
2374 TREE_CHAIN (t) = result;
2375 result = t;
2376 }
2377
2378 returnresult;
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 theidentifier for this function. It will be of the form
2018 SSDF_IDENTIFIER_<number>. */
2019 sprintf (id, "%s_%u",SSDF_IDENTIFIER, count);
2020
2021 /* Create theparameters. */
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 theFUNCTION_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 functionin the list of functions to be called from the
2035 static constructors anddestructors. */
2036 if (!ssdf_decls)
2037 {
2038 VARRAY_TREE_INIT (ssdf_decls, 32,"ssdf_decls");
2039
2040 /* Take thisopportunity to initialize the map from priority
2041 numbers to information aboutthat 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 needto generate functions for the
2048 DEFAULT_INIT_PRIORITY soenter it now. That way when we walk
2049 priorities later, we'll besure 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 theargument 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 functionin the global scope. */
2070 pushdecl (ssdf_decl);
2071
2072 /* Start thefunction 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 onlyneed to call this function from the
2078 various constructor anddestructor 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 scopeof the outermost block in the function. */
2085 body = begin_compound_stmt(/*has_no_scope=*/false);
2086
2087 /* This functionmust not be deferred because we are depending on
2088 its compilation to tell uswhat is TREE_SYMBOL_REFERENCED. */
2089 current_function_cannot_inline
2090 = "static storage duration functionscannot 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 theinitialization. */
2282 guard_if_stmt
2283 = start_static_initialization_or_destruction(decl,
2284 /*initp=*/1);
2285
2286 /* Perform theinitialization. */
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 theobject. */
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 thepriority for this declaration. */
2151 priority = DECL_INIT_PRIORITY (decl);
2152 if (!priority)
2153 priority = DEFAULT_INIT_PRIORITY;
2154
2155 /* Remember that wehad 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 thecompiler into thinking we are at the file and line
2164 where DECL was declared sothat error-messages make sense, and so
2165 that the debugger will showsomewhat 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 implicitcalls to the constructors,
2174 the conversion functions, orthe destructor called to
2175 create and destroy a staticdata member is performed as
2176 if these calls appeared inthe scope of the member's
2177 class.
2178
2179 we pretend we are in a staticmember function of the class of
2180 which the DECL is amember. */
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 /* Conditionalizethis initialization on being in the right priority
2188 and beinginitializing/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'tneed a guard. */
2200 guard = NULL_TREE;
2201 /* We need a guardif this is an object with external linkage that
2202 might be initialized in morethan one place. (For example, a
2203 static data member of atemplate, 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 staticdestructor. */
2219 my_friendly_assert (initp, 20000629);
2220 guard_cond = get_guard_cond(guard);
2221 }
2222 /* If we don'thave __cxa_atexit, then we will be running
2223 destructors from .finisections, or their equivalents. So,
2224 we need to know how manytimes we've tried to initialize this
2225 object. We doinitializations only if the GUARD is zero,
2226 i.e., if we are the first toinitialize the variable. We do
2227 destructions only if theGUARD is one, i.e., if we are the
2228 last to destroy thevariable. */
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 returnguard_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) insemantics.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) indecl2.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 typethat 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 guardshould 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 ifthe 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 returncp_build_binary_op (EQ_EXPR, guard, guard_value);
1862 }
在上面1857行,该函数构建了语句:“*((char*) &guard)”。因此测试条件应该是“(!*((char*) &guard))”,而剩下的空间将被用于互斥量。
1832 static tree
1833 get_guard_bits (tree guard) indecl2.c
1834 {
1835 /* We only set thefirst byte of the guard, in order to leave room
1836 for a mutex in the high-orderbits. */
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, treeif_stmt) insemantics.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) indecl2.c
1869 {
1870 tree guard_init;
1871
1872 /* Set the GUARD toone. */
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 returnbuild_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) insemantics.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 /* Callbuild_cleanup before we enter the anonymous function so that
5273 any access checks will be donerelative to the current scope,
5274 rather than the scope of theanonymous function. */
5275 build_cleanup(decl);
5276
5277 /* Now start thefunction. */
5278 cleanup = start_cleanup_fn();
5279
5280 /* Now, recomputethe 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 thinkthat 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 bodyof 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 withthe 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 functionshould only be called for declarations that really
1774 require cleanups. */
1775 my_friendly_assert (!TYPE_HAS_TRIVIAL_DESTRUCTOR(type), 20030106);
1776
1777 /* Treat allobjects 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 manglethis. */
5196 push_lang_context (lang_name_c);
5197
5198 interface_only = 0;
5199 interface_unknown = 1;
5200
5201 /* Build theparameter-types. */
5202 parmtypes = void_list_node;
5203 /* Functions passedto __cxa_atexit take an additional parameter.
5204 We'll just ignore it. After weimplement the new calling
5205 convention for destructors, wecan eliminate the use of
5206 additional cleanup functionsentirely in the -fnew-abi case. */
5207 if (flag_use_cxa_atexit)
5208 parmtypes = tree_cons (NULL_TREE,ptr_type_node, parmtypes);
5209 /* Build thefunction type itself. */
5210 fntype = build_function_type(void_type_node, parmtypes);
5211 /* Build the nameof the function. */
5212 sprintf (name, "__tcf_%d", start_cleanup_cnt++);
5213 /* Build thefunction declaration. */
5214 fndecl = build_lang_decl(FUNCTION_DECL, get_identifier (name), fntype);
5215 /* It's a functionwith internal linkage, generated by the
5216 compiler. */
5217 TREE_PUBLIC (fndecl) = 0;
5218 DECL_ARTIFICIAL (fndecl) = 1;
5219 /* Make thefunction `inline' so that it is only emitted if it is
5220 actually needed. It isunlikely that it will be inlined, since
5221 it is only called via afunction 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 theparameter. */
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 thevariable. */
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, treetype) 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 /* Thedeclaration for `__cxa_atexit' is:
5117
5118 int __cxa_atexit (void (*)(void *), void*, void *)
5119
5120 We build up the argumenttypes and then then function type
5121 itself. */
5122
5123 /* First, buildthe 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, buildthe 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 /* Thedeclaration for `atexit' is:
5140
5141 int atexit (void (*)());
5142
5143 We build up the argumenttypes 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 thefinal atexit type. */
5149 fn_type = build_function_type(integer_type_node, arg_types);
5150 name = "atexit";
5151 }
5152
5153 /* Now, build thefunction 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 returnatexit_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_destruction (tree guard_if_stmt) in decl2.c
2263 {
2264 finish_then_clause(guard_if_stmt);
2265 finish_if_stmt();
2266
2267 /* Now that we'redone 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 returnif_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 thefunction. */
2103 finish_compound_stmt(body);
2104 expand_or_defer_fn(finish_function (0));
2105 }
- GCC-3.4.6源代码学习笔记(158)
- GCC-3.4.6源代码学习笔记 (100)
- GCC-3.4.6源代码学习笔记 (101)
- GCC-3.4.6源代码学习笔记 (102)
- GCC-3.4.6源代码学习笔记 (103)
- GCC-3.4.6源代码学习笔记 (104)
- GCC-3.4.6源代码学习笔记 (105)
- GCC-3.4.6源代码学习笔记 (106)
- GCC-3.4.6源代码学习笔记(166)
- GCC-3.4.6源代码学习笔记
- GCC-3.4.6源代码学习笔记(6)
- GCC-3.4.6源代码学习笔记(1)
- GCC-3.4.6源代码学习笔记(2)
- GCC-3.4.6源代码学习笔记(3)
- GCC-3.4.6源代码学习笔记(4)
- GCC-3.4.6源代码学习笔记(5)
- GCC-3.4.6源代码学习笔记(7)
- GCC-3.4.6源代码学习笔记(8)
- 很好很强大!
- vb picturebox 加载网络图片的两种方法,分无缓存加载和有缓存加载
- 数据库系统的模型和结构
- GCC-3.4.6源代码学习笔记(157)
- Studying note of GCC-3.4.6 source (157)
- GCC-3.4.6源代码学习笔记(158)
- Studying note of GCC-3.4.6 source (158)
- GCC-3.4.6源代码学习笔记(159)
- Studying note of GCC-3.4.6 source (159)
- GCC-3.4.6源代码学习笔记(160)
- Studying note of GCC-3.4.6 source (160)
- java中的单实例模式
- printf输出各种格式(转)
- C/C++获取当前时间(转)