Next in the DO WHILE loop, at line 2651 static_aggregates is a list that holds aggregates having constructor or destructor and reside in the global scope. As we have learnt, the compiler should ensure that before entering the “main” function, all global variables must have memory allocated and initialized.
So prune_vars_needing_no_initialization is invoked at line 2651 to see if there is any such object. See that static_aggregates is a list of tree_list, in which node, purpose field is the initializer and value is the VAR_DECL of the object. The function transverses this list and removes nodes needn’t be initialized, which include objects that declared external, and objects that already in error (remember compiler should go as far as possible in case of encountering error).
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 }
If there is any object left, start_static_storage_duration_function is invoked to generate a function to do the initailization. Note that if during the iteration, new nodes are inserted into static_aggregates , a new function will be generated in next iteration. So here argument count increased for every iteration in which global objects get handled.
Below SSDF_IDENTIFIER is defined as "__static_initialization_and_destruction", the name of function generated here would be "__static_initialization_and_destruction0" and so on. See that the function has prototype as: “void __static_initialization_and_destruction0 (int, int)”. And this function is not global visible.
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 }
Above at line 2058, INITIALIZE_P_IDENTIFIER is defined as "__initialize_p", and at line 2062, PRIORITY_IDENTIFIER is defined as "__priority"; so the declaration of the function in fact is: “void __static_initialization_and_destruction0 (int __initialize_p, int __priority)”.
At line 2051, DEFAULT_INIT_PRIORITY is 65535 (0xffff), it is the lowest priority. And get_priority_info generates the entry of this priority into splay tree priority_info_map .
For global variables need be initialized, it needs invoke below function to finish the handling by the front-end.
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 }
Note that the last two arguments of rest_of_decl_compilation are both 1, and the node of variables must be VAR_DECL, so in the rest_of_decl_compilation , cgraph_varpool_finalize_decl is invoked to generate corresponding cgraph_varpool_nodes.
For every global variable, do_static_initialization generates code for the 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 }
Below at line 2151, DECL_INIT_PRIORITY is set by attribute init_priority. [6] gives the detail.
In Standard C++, objects defined at namespace scope are guaranteed to be initialized in an order in strict accordance with that of their definitions in a given translation unit. No guarantee is made for initializations across translation units. However, GNU C++ allows users to control the order of initialization of objects defined at namespace scope with the init_priority attribute by specifying a relative priority, a constant integral expression currently bounded between 101 and 65535 inclusive. Lower numbers indicate a higher priority. In the following example, A would normally be created before B, but the init_priority attribute has reversed that order: Some_Class A __attribute__ ((init_priority (2000))); Some_Class B __attribute__ ((init_priority (543))); Note that the particular values of priority do not matter; only their relative ordering. Note [author]: in GCC, priority less than 100 is reserved for internal use. |
Below argument initp at invocation is 1 which means we are doing initialization.
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 }
Above flag_use_cxa_atexit if nonzero means we use __cxa_atexit to register destructors for local statics and global objects. Under my current configuration, this flag is 1 (it depends on the library in used).
Synopsis int __cxa_atexit(void (*func) (void *), void * arg, void * dso_handle); Description __cxa_atexit() registers a destructor function to be called by exit() or when a shared library is unloaded. When a shared library is unloaded, any destructor function associated with that shared library, identified by dso_handle, shall be called with the single argument arg, and then that function shall be removed, or marked as complete, from the list of functions to run at exit(). On a call to exit(), any remaining functions registered shall be called with the single argument arg. Destructor functions shall always be called in the reverse order to their registration (i.e. the most recently registered function shall be called first), The __cxa_atexit() function is used to implement atexit(), as described in ISO POSIX (2003) . Calling atexit(func) from the statically linked part of an application shall be equivalent to __cxa_atexit(func, NULL, NULL). __cxa_atexit() is not in the source standard; it is only in the binary standard. Note: atexit() is not in the binary standard; it is only in the source standard. |
In above function, current_function_decl now refers to the initialziation funciton being generated here (see start_function invoked in start_static_storage_duration_function at line 2079). So if condition at line 2181 is satisified above, it means the object to be initialized is a static class member, it needs temperarily pretend that current_function_decl also appears statically within the class.
Obviously, the initialization function needs a IF block to check if the object is initialized already (the condition testing is per object). In C++, IF block introduces a new local binding scope.
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 }
Note that the node of IF_STMT is returned to guard_if_stmt . Besides to ensure this static or global object should only be initialized once, a guard is needed.
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 }
The guard should be a variable like: “static int something = 0;”. To create a unique name for these guard variables, it is a clever idea to mangle name of decl being code generated. See in code above, the type of guard is int64_t to be big enough to hold a mutex; further these variables are declared within global namespace as line 1824 indicates.
And if __cxa_atexit is available, then get_guard_cond constructs the nodes of code to test the condition of “(!guard)” in the example
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 }
And above at line 1857, the function constructs the statement: “*((char*) &guard)”. So the test condition should be “(!*((char*) &guard))”, and left space will be used for mutex.
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 }
Then at line 2247, finish_if_stmt_cond closes the condition of the IF block which should be a Boolean value, so maybe_convert_cond evaluates cond and tries to convert it to a Boolean value. Here we skip this function as it would be another long story.
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 }
On the other hand, if __cxa_atexit is unavailable, reference counting is used instead. Next, set_guard constructs nodes for statement: “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 }
So with flag_use_cxa_atexit nonzero, following statement in C++ form (in fact, the code generated is in intermediate tree form) will be generated:
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;
}
Back do_static_initialization , if initializer is present for the object, generates code for it as below. Note that the type of the whole statement is ignored, for example, expression “int j = 5;” has type of integer and value 5, but they are both ignored. Then in internal, this ignore should be explicitly stated by converting the expression into a void type ones.
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 }
As we have seen in previous section, initializer for aggregate type is head by node of CONSTRUCTOR. Now the function looks like:
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);
}
If __cxa_atexit is available, the compiler will register the destructor of the object with the function. Below register_dtor_fn generates the code for this purpose. Note that trivial destructor is filtered out at line 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 }
As build_cleanup invokes build_delete with lookup flag LOOKUP_NORMAL (flags access violations, and complains if no suitable member function matching the arguments is found), LOOKUP_NONVIRTUAL (makes a direct call to the member function found, i.e in form A::f, instead via vtable), LOOKUP_DESTRUCTOR (means explicit call to destructor). Access checking will be taken during the procedure. So build_cleanup is invoked first to ensure no access error, and see no access checking is enforced at line 5285 before invoking the function second time within the function scope built by start_cleanup_fn .
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 }
The function that can be registered by __cxa_atexit must have prototype “void func (void *)” which can not match any destructor of any class. So start_cleanup_fn constructs a wrapper that is suitable for the registration. Note push_to_top_level at line 5193, this wrapper function will be declared within global scope. Also push_lang_context at line 5196 just exactly puts ‘extern “C”’ at head of the function delcaration.
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 }
Note that the wrapper function is created per variable, its name follows the form: “__tcf_0” , “__tcf_1”, and so on. Then at line 5238 above, start_function makes the function scope as the current binding scope. Following, line 5290 ~ 5292 makes the destructor invocation into the body of the wrapper function, the same as when invoking build_delete it uses flag sfk_complete_destructor to build code to invoke 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 }
It finishes the wrapper definition and restores the binding scope with end_cleanup_fn . After constructing the wrapper, it can be registered with __cxa_atexit . But before that, node of dso_handle and __cxa_atexit should be found out first.
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 }
Argument dso_handle of the function in fact is an external global variable which is defined within the library and used during unloading shared library. While for statically linked part of application, just NULL be passed as the argument is good enough.
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 }
Similarly, __cxa_atexit has its deifnition defined within library. Here get_atexit_node just generates code to declare the function as if we include the related header file.
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 }
The last part of register_dtor_fn then generates the code for the invocation, and finish_static_initialization_or_destruction invoked at line 2296 in do_static_initialization adds closing “}”, so the code generated so far is:
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);
}
The closing “}” makes ‘decl0_constructor’ out of range, with which the object would be instantiated. And remember that fields DECL_CONTEXT and DECL_STATIC_FUNCTION_P of current_function_decl may be changed when generating code to initialize static class member, so always restores these fields when finishing the initialization or destruction.
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 }
Note that THEN_CLAUSE of if_stmt is 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 }
Then finish_if_stmt closes the block and exits from it.
497 void
498 finish_if_stmt (void) in semantics.c
499 {
500 finish_stmt ();
501 do_poplevel ();
502 }
See do_static_initialization is invoked for every global/static object, so code segment:
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);
}
is generated for second object, and so on. When all code for initializing objects are generated, the initialization function should be finished accordingly as below.
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 }