Studying note of GCC-3.4.6 source (158)

5.13.4.3.              Iterate – emitting code for ctor/dtor of global aggregate

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.

5.13.4.3.1.        Prune variable needing initializatoin

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.

5.13.4.3.2.        Generate code for initialization

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 }

 

你可能感兴趣的:(function,tree,Integer,Build,initialization,destructor)