Studying note of GCC-3.4.6 source (158)

来源:互联网 发布:局域网传输软件 编辑:程序博客网 时间:2024/06/08 06:14

5.13.4.3.             Iterate – emitting code for ctor/dtor of globalaggregate

Next in the DO WHILEloop, at line 2651 static_aggregates is a list that holds aggregateshaving constructor or destructor and reside in the global scope. As we havelearnt, the compiler should ensure that before entering the “main” function,all global variables must have memory allocated and initialized.

5.13.4.3.1.       Prunevariable needing initializatoin

So prune_vars_needing_no_initializationis invoked at line 2651 to see if there is any such object. See that static_aggregatesis a list of tree_list, in which node, purpose field is the initializer and value isthe VAR_DECL of the object. The function transverses this list and removes nodesneedn’t be initialized, which include objects that declared external, andobjects that already in error (remember compiler should go as far as possiblein 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       /* 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   }

 

If there is any object left, start_static_storage_duration_function isinvoked to generate a function to do the initailization. Note that if duringthe iteration, new nodes are inserted into static_aggregates, a new function will begenerated in next iteration. So here argument count increased for every iterationin which global objects get handled.

Below SSDF_IDENTIFIER is defined as"__static_initialization_and_destruction", the name of functiongenerated 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 notglobal 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 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   }

 

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 thelowest priority. And get_priority_info generates the entry of thispriority into splay tree priority_info_map.

For global variables need be initialized, it needs invoke belowfunction 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 areboth 1, and the node of variables must be VAR_DECL, so in the rest_of_decl_compilation,cgraph_varpool_finalize_declis invoked to generate corresponding cgraph_varpool_nodes.

5.13.4.3.2.       Generatecode for initialization

For every global variable, do_static_initialization generates code forthe 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   }

 

Below at line2151, DECL_INIT_PRIORITY is set by attribute init_priority. [6] gives thedetail.

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 atinvocation 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 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 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     /* 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   }

 

Above flag_use_cxa_atexitif 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 inused).

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_declnow refers to the initialziation funciton being generated here (see start_functioninvoked in start_static_storage_duration_functionat line 2079). So if condition at line 2181 is satisified above, it means theobject to be initialized is a static class member, it needs temperarily pretendthat current_function_declalso appears statically within the class.

Obviously, the initialization function needs a IF block to check if the object is initialized already (thecondition testing is per object). In C++, IFblock introduces a new local binding scope.

 

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    }

 

Note that the node of IF_STMT is returned to guard_if_stmt. Besides to ensurethis static or global object should only be initialized once, a guard isneeded.

 

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   }

 

The guard should be a variable like: “static int something = 0;”. Tocreate a unique name for these guard variables, it is a clever idea to manglename of decl being code generated. See in code above, the type of guard isint64_t to be big enough to hold a mutex; further these variables are declaredwithin global namespace as line 1824 indicates.

And if __cxa_atexit is available, then get_guard_cond constructs the nodes of code totest 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 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   }

 

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)                                                                       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   }

 

Then at line 2247, finish_if_stmt_cond closes the condition ofthe IF block which should be a Boolean value, so maybe_convert_cond evaluatescondand tries to convert it to a Boolean value. Here we skip this function as itwould be another long story.

 

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    }

 

On the other hand, if __cxa_atexit is unavailable, reference counting is used instead. Next, set_guardconstructs nodes for statement: “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   }

 

So with flag_use_cxa_atexitnonzero, following statement in C++ form (in fact, the code generated is inintermediate 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, ifinitializer is present for the object, generates code for it as below. Notethat 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 ininternal, this ignore should be explicitly stated by converting the expressioninto a void type ones.

 

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    }

 

As we haveseen in previous section, initializer for aggregate type is head by node ofCONSTRUCTOR. 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 willregister the destructor of the object with the function. Below register_dtor_fngenerates the code for this purpose. Note that trivial destructor is filteredout 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     /* 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   }

 

As build_cleanupinvokes build_deletewith lookup flag LOOKUP_NORMAL (flags access violations, and complains if nosuitable member function matching the arguments is found), LOOKUP_NONVIRTUAL(makes a direct call to the member function found, i.e in form A::f, insteadvia vtable), LOOKUP_DESTRUCTOR (means explicit call to destructor). Accesschecking will be taken during the procedure. So build_cleanup is invokedfirst to ensure no access error, and see no access checking is enforced at line5285 before invoking the function second time within the function scope builtby 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 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   }

 

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 constructsa 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”’ athead 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 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   }

 

Note that the wrapper function is created per variable, its namefollows the form: “__tcf_0”, “__tcf_1”, and soon. Then at line 5238 above, start_function makes the function scope asthe current binding scope. Following, line 5290 ~ 5292 makes the destructorinvocation into the body of the wrapper function, the same as when invoking build_deleteit uses flag sfk_complete_destructorto 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 scopewith end_cleanup_fn.After constructing the wrapper, it can be registered with __cxa_atexit. But beforethat, node of dso_handleand __cxa_atexitshould 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 thevariable.  */

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 externalglobal variable which is defined within the library and used during unloadingshared library. While for statically linked part of application, just NULL bepassed as the argument is good enough.

 

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   }

 

Similarly, __cxa_atexit has its deifnition definedwithin library. Here get_atexit_node just generates code todeclare 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       /* 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       /* The declarationfor `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   }

 

The last partof register_dtor_fnthen generates the code for the invocation, and finish_static_initialization_or_destructioninvoked at line 2296 in do_static_initializationadds 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_declmay be changed when generating code to initialize static class member, soalways restores these fields when finishing the initialization or destruction.

 

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 anylonger.  */

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      returnif_stmt;

477    }

 

Then finish_if_stmt closes the block and exitsfrom it.

 

497  void

498  finish_if_stmt (void)                                                                            in semantics.c

499  {

500   finish_stmt ();

501   do_poplevel ();

502  }

 

See do_static_initializationis 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 forinitializing objects are generated, the initialization function should be finishedaccordingly as below.

 

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 }

 

原创粉丝点击