GCC-3.4.6源代码学习笔记(158)

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

5.13.4.3.             迭代发布全局聚集类的构造函数/析构函数

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

5.13.4.3.1.       修整需要初始化的变量

因此在2651行,prune_vars_needing_no_initialization被调用来检查是否有这样的对象。看到static_aggregates是一个tree_list链表,在其中的节点中,purpose域是初始值,而value是这个对象的VAR_DECL。这个函数遍历这个链表并移除不需要初始化的节点,这包括外部声明的对象,及已经出错的对象(记住在遇到错误时,编译器尽可能地完成处理)。

 

2332   static tree

2333   prune_vars_needing_no_initialization(tree *vars)                                         in decl2.c

2334   {

2335     tree *var = vars;

2336     tree result = NULL_TREE;

2337  

2338     while (*var)

2339     {

2340      tree t = *var;

2341       tree decl = TREE_VALUE (t);

2342       tree init = TREE_PURPOSE (t);

2343  

2344       /* Dealgracefully with error.  */

2345       if (decl == error_mark_node)

2346       {

2347         var = &TREE_CHAIN (t);

2348         continue;

2349       }

2350  

2351       /* The onlythings that can be initialized are variables. */

2352       my_friendly_assert (TREE_CODE (decl) ==VAR_DECL, 19990420);

2353  

2354       /* If this objectis not defined, we don't need to do anything

2355         here.  */

2356       if (DECL_EXTERNAL (decl))

2357       {

2358         var = &TREE_CHAIN (t);

2359         continue;

2360       }

2361  

2362       /* Also, if theinitializer already contains errors, we can bail

2363         out now.  */

2364       if (init && TREE_CODE (init) ==TREE_LIST

2365          && value_member(error_mark_node, init))

2366       {

2367         var = &TREE_CHAIN (t);

2368         continue;

2369       }

2370  

2371       /* This variableis going to need initialization and/or

2372         finalization, so we add itto the list.  */

2373       *var = TREE_CHAIN (t);

2374       TREE_CHAIN (t) = result;

2375       result = t;

2376     }

2377  

2378     returnresult;

2379   }

 

如果有对象遗留下来,就要调用start_static_storage_duration_function来产生一个执行初始化的函数。注意到如果在迭代过程中,新的节点被插入static_aggregates,在下一次迭代中将产生一个新的函数。因此在这里为每次全局对象得到处理的迭代递增参数count

下面的SSDF_IDENTIFIER被定义为“__static_initialization_and_destruction”,这里所产生函数的名字将是“__static_initialization_and_destruction0”,依此类推。看到这个函数具有原型:“void__static_initialization_and_destruction0 (int, int)”。并且这个函数不是全局可见的。

 

2009   static tree

2010   start_static_storage_duration_function (unsigned count)                              in decl2.c

2011   {

2012     tree parm_types;

2013     tree type;

2014     tree body;

2015     char id[sizeof (SSDF_IDENTIFIER) + 1 /* '/0' */ + 32];

2016  

2017     /* Create theidentifier for this function. It will be of the form

2018      SSDF_IDENTIFIER_<number>. */

2019     sprintf (id, "%s_%u",SSDF_IDENTIFIER, count);

2020  

2021    /* Create theparameters.  */

2022     parm_types = void_list_node;

2023     parm_types = tree_cons (NULL_TREE,integer_type_node, parm_types);

2024     parm_types = tree_cons (NULL_TREE,integer_type_node, parm_types);

2025     type = build_function_type(void_type_node, parm_types);

2026  

2027     /* Create theFUNCTION_DECL itself.  */

2028     ssdf_decl = build_lang_decl(FUNCTION_DECL,

2029                             get_identifier (id),

2030                             type);

2031     TREE_PUBLIC (ssdf_decl) = 0;

2032     DECL_ARTIFICIAL (ssdf_decl) = 1;

2033  

2034    /* Put this functionin the list of functions to be called from the

2035       static constructors anddestructors.  */

2036     if (!ssdf_decls)

2037     {

2038       VARRAY_TREE_INIT (ssdf_decls, 32,"ssdf_decls");

2039  

2040       /* Take thisopportunity to initialize the map from priority

2041         numbers to information aboutthat priority level.  */

2042       priority_info_map = splay_tree_new(splay_tree_compare_ints,

2043                                      /*delete_key_fn=*/0,

2044                                      /*delete_value_fn=*/

2045                                     (splay_tree_delete_value_fn) &free);

2046  

2047       /* We always needto generate functions for the

2048         DEFAULT_INIT_PRIORITY soenter it now. That way when we walk

2049         priorities later, we'll besure to find the

2050         DEFAULT_INIT_PRIORITY.  */

2051       get_priority_info (DEFAULT_INIT_PRIORITY);

2052     }

2053  

2054     VARRAY_PUSH_TREE (ssdf_decls, ssdf_decl);

2055  

2056     /* Create theargument list.  */

2057     initialize_p_decl = cp_build_parm_decl

2058           (get_identifier(INITIALIZE_P_IDENTIFIER), integer_type_node);

2059     DECL_CONTEXT (initialize_p_decl) = ssdf_decl;

2060     TREE_USED (initialize_p_decl) = 1;

2061     priority_decl = cp_build_parm_decl

2062           (get_identifier (PRIORITY_IDENTIFIER),integer_type_node);

2063     DECL_CONTEXT (priority_decl) = ssdf_decl;

2064     TREE_USED (priority_decl) = 1;

2065  

2066     TREE_CHAIN (initialize_p_decl) = priority_decl;

2067     DECL_ARGUMENTS (ssdf_decl) = initialize_p_decl;

2068  

2069     /* Put the functionin the global scope.  */

2070     pushdecl (ssdf_decl);

2071  

2072     /* Start thefunction itself. This is equivalent to declaring the

2073       function as:

2074  

2075          static void __ssdf (int__initialize_p, init __priority_p);

2076         

2077       It is static because we onlyneed to call this function from the

2078       various constructor anddestructor functions for this module.  */

2079     start_function(/*specs=*/NULL_TREE,

2080                 ssdf_decl,

2081                 /*attrs=*/NULL_TREE,

2082                 SF_PRE_PARSED);

2083  

2084     /* Set up the scopeof the outermost block in the function. */

2085     body = begin_compound_stmt(/*has_no_scope=*/false);

2086  

2087     /* This functionmust not be deferred because we are depending on

2088       its compilation to tell uswhat is TREE_SYMBOL_REFERENCED.  */

2089     current_function_cannot_inline

2090         = "static storage duration functionscannot be inlined";

2091  

2092     return body;

2093   }

 

上面在2058行,INITIALIZE_P_IDENTIFIER定义为“__initialize_p”,并且在2062行,PRIORITY_IDENTIFIER定义为“__priority”;因此该函数的声明事实上是:“void__static_initialization_and_destruction0 (int __initialize_p, int __priority)”。

2051行,DEFAULT_INIT_PRIORITY655350xffff),它是最低优先级。而get_priority_info在伸展树priority_info_map中产生这个优先级的项。

对于需要初始化的全局聚集类变量,还要调用下面的函数来完成前端的处理。

 

2384   static void

2385   write_out_vars (tree vars)                                                                        in decl2c

2386   {

2387     tree v;

2388  

2389     for (v =vars; v; v = TREE_CHAIN (v))

2390       if (!var_finalized_p(TREE_VALUE (v)))

2391         rest_of_decl_compilation(TREE_VALUE (v), 0, 1, 1);

2392   }

 

看到rest_of_decl_compilation的最后两个参数都是1,并且这些变量的节点必须是VAR_DECL,因此在rest_of_decl_compilation中,cgraph_varpool_finalize_decl被调用来产生相应的cgraph_varpool_nodes

5.13.4.3.2.       产生初始化代码

对于每个全局变量,do_static_initialization产生用于初始化的代码。

 

2276   static void

2277   do_static_initialization (tree decl,tree init)                                                  in decl2.c

2278   {

2279     tree guard_if_stmt;

2280  

2281     /* Set up for theinitialization.  */

2282     guard_if_stmt

2283       = start_static_initialization_or_destruction(decl,

2284                                          /*initp=*/1);

2285  

2286     /* Perform theinitialization.  */

2287     if (init)

2288       finish_expr_stmt(init);

2289  

2290     /* If we're using__cxa_atexit, register a a function that calls the

2291       destructor for theobject.  */

2292     if (flag_use_cxa_atexit)

2293       register_dtor_fn(decl);

2294  

2295     /* Finsh up.  */

2296     finish_static_initialization_or_destruction(guard_if_stmt);

2297   }

 

下面在2151行,DECL_INIT_PRIORITY通过属性init_priority设置。【6】给出如下细节。

在标准C++中,定义在名字空间的对象要保证,严格按照在一个给定的编译单元中它们定义出现的次序来初始化。跨编译单元对初始化则没有任何担保。不过,GNU C++允许用户,通过init_priority属性指定一个相对优先级,来控制定义在一个名字空间中的对象的初始化次序,相对优先级是一个整型常量表达式,目前取值在10165535(包括)之间。越小的数值代表越高的优先级。

在下面的例子中,正常地A将在B之前被创建,但init_priority属性保留了这个次序:

Some_Class A __attribute__ ((init_priority (2000)));

Some_Class B __attribute__ ((init_priority (543)));

注意到优先级的确切值无关紧要;重要的是它们的相对次序。

[作者]:在GCC中,小于100的优先级保留作内部用途。

下面,在调用时参数initp1,表示我们正在进行初始化。

 

2140   static tree

2141   start_static_initialization_or_destruction (tree decl, int initp)                         in decl2.c

2142   {

2143     tree guard_if_stmt = NULL_TREE;

2144     int priority;

2145     tree cond;

2146     tree guard;

2147     tree init_cond;

2148     priority_info pi;

2149  

2150     /* Figure out thepriority for this declaration.  */

2151     priority = DECL_INIT_PRIORITY (decl);

2152     if (!priority)

2153       priority = DEFAULT_INIT_PRIORITY;

2154  

2155     /* Remember that wehad an initialization or finalization at this

2156       priority.  */

2157     pi = get_priority_info (priority);

2158     if (initp)

2159       pi->initializations_p = 1;

2160     else

2161       pi->destructions_p = 1;

2162  

2163     /* Trick thecompiler into thinking we are at the file and line

2164       where DECL was declared sothat error-messages make sense, and so

2165       that the debugger will showsomewhat sensible file and line

2166       information.  */

2167     input_location = DECL_SOURCE_LOCATION (decl);

2168  

2169    /* Because of:

2170  

2171         [class.access.spec]

2172  

2173         Access control for implicitcalls to the constructors,

2174         the conversion functions, orthe destructor called to

2175         create and destroy a staticdata member is performed as

2176         if these calls appeared inthe scope of the member's

2177         class. 

2178  

2179       we pretend we are in a staticmember function of the class of

2180       which the DECL is amember.  */

2181     if (member_p (decl))

2182     {

2183       DECL_CONTEXT (current_function_decl) =DECL_CONTEXT (decl);

2184       DECL_STATIC_FUNCTION_P (current_function_decl)= 1;

2185     }

2186    

2187     /* Conditionalizethis initialization on being in the right priority

2188       and beinginitializing/finalizing appropriately. */

2189     guard_if_stmt = begin_if_stmt();

2190     cond = cp_build_binary_op (EQ_EXPR,

2191                             priority_decl,

2192                             build_int_2(priority, 0));

2193     init_cond = initp ? integer_one_node :integer_zero_node;

2194     init_cond = cp_build_binary_op (EQ_EXPR,

2195                                initialize_p_decl,

2196                                init_cond);

2197     cond = cp_build_binary_op (TRUTH_ANDIF_EXPR,cond, init_cond);

2198  

2199     /* Assume we don'tneed a guard.  */

2200     guard = NULL_TREE;

2201     /* We need a guardif this is an object with external linkage that

2202       might be initialized in morethan one place. (For example, a

2203       static data member of atemplate, when the data member requires

2204       construction.)  */

2205     if (TREE_PUBLIC (decl) &&(DECL_COMMON (decl)

2206                              || DECL_ONE_ONLY(decl)

2207                              || DECL_WEAK(decl)))

2208     {

2209       tree guard_cond;

2210  

2211       guard = get_guard(decl);

2212  

2213       /* When using__cxa_atexit, we just check the GUARD as we would

2214         for a local static.  */

2215       if (flag_use_cxa_atexit)

2216       {

2217         /* When using__cxa_atexit, we never try to destroy

2218           anything from a staticdestructor.  */

2219         my_friendly_assert (initp, 20000629);

2220         guard_cond = get_guard_cond(guard);

2221       }

2222       /* If we don'thave __cxa_atexit, then we will be running

2223         destructors from .finisections, or their equivalents. So,

2224         we need to know how manytimes we've tried to initialize this

2225         object. We doinitializations only if the GUARD is zero,

2226         i.e., if we are the first toinitialize the variable. We do

2227         destructions only if theGUARD is one, i.e., if we are the

2228         last to destroy thevariable.  */

2229       else if (initp)

2230         guard_cond

2231            = cp_build_binary_op (EQ_EXPR,

2232                               build_unary_op(PREINCREMENT_EXPR,

2233                                            guard,

2234                                             /*noconvert=*/1),

2235                               integer_one_node);

2236       else

2237         guard_cond

2238            = cp_build_binary_op (EQ_EXPR,

2239                               build_unary_op(PREDECREMENT_EXPR,

2240                                            guard,

2241                                            /*noconvert=*/1),

2242                               integer_zero_node);

2243  

2244       cond = cp_build_binary_op(TRUTH_ANDIF_EXPR, cond, guard_cond);

2245     }

2246  

2247     finish_if_stmt_cond(cond, guard_if_stmt);

2248  

2249    /* If we're using__cxa_atexit, we have not already set the GUARD,

2250       so we must do so now.  */

2251     if (guard && initp && flag_use_cxa_atexit)

2252       finish_expr_stmt(set_guard (guard));

2253  

2254     returnguard_if_stmt;

2255   }

 

上面flag_use_cxa_atexit如果不是0,表示我们使用_cxa_atexit为局部静态及全局对象注册析构函数。在我们当前的配置下,这个标记是1(它依赖于所使用的库)。

概要

int __cxa_atexit(void (*func) (void *), void * arg, void * dso_handle);

描述

__cxa_atexit()注册一个将被exit(),或一个共享库卸载时,调用的析构函数。当一个共享库被卸载时,任何与这个共享库相关联的,由dso_handle确定的析构函数,将使用单个实参arg来调用,然后这个函数应该从在exit()中执行的函数列表中移除,或者被标记为完成。在对exit()的一个调用里,应该使用单个实参arg来调用任何剩下的已注册的函数。析构函数应该总是以它们注册的反序来调用(即,最后注册的函数应该被首先调用),

函数__cxa_atexit()被用于实现atexit(),如在ISO POSIX (2003)中所描述的。从一个应用的静态链接部分来调用atexit(func)应该等同于__cxa_atexit(func, NULL, NULL)

__cxa_atexit()不是代码标准(source standard);它只是二进制标准(binary standard)。

注意:atexit()不是二进制标准;它只是代码标准。

在上面的函数里,current_function_decl现在指向这里正在产生的初始化函数(参见在start_static_storage_duration_function2079行调用的start_function)。因此如果上面2181行的条件满足,表示要被初始化的对象是一个静态类成员,需要临时把current_function_decl伪装成在类中以静态形式出现。

显然,这个初始化函数需要一个IF块来检查该对象是否已经初始化了(这个条件检查是基于每个对象的)。在C++中,IF块引入一个新的局部绑定域。

 

449    tree

450    begin_if_stmt (void)                                                                         insemantics.c

451    {

452      tree r;

453      do_pushlevel(sk_block);

454      r = build_stmt (IF_STMT, NULL_TREE,NULL_TREE, NULL_TREE);

455      add_stmt (r);

456      return r;

457    }

 

注意到IF_STMT节点被返回给guard_if_stmt。除了保证这个静态或全局变量只被初始化一次,需要一个约束。

 

1797   tree

1798   get_guard (tree decl)                                                                               indecl2.c

1799   {

1800     tree sname;

1801     tree guard;

1802  

1803     sname = mangle_guard_variable (decl);

1804     guard = IDENTIFIER_GLOBAL_VALUE (sname);

1805     if (! guard)

1806     {

1807       tree guard_type;

1808  

1809       /* We use a typethat is big enough to contain a mutex as well

1810         as an integer counter.  */

1811       guard_type = long_long_integer_type_node;

1812       guard = build_decl(VAR_DECL, sname, guard_type);

1813        

1814       /* The guardshould have the same linkage as what it guards. */

1815       TREE_PUBLIC (guard) = TREE_PUBLIC (decl);

1816       TREE_STATIC (guard) = TREE_STATIC (decl);

1817       DECL_COMMON (guard) = DECL_COMMON (decl);

1818       DECL_ONE_ONLY (guard) = DECL_ONE_ONLY(decl);

1819       if (TREE_PUBLIC (decl))

1820         DECL_WEAK (guard) = DECL_WEAK (decl);

1821        

1822       DECL_ARTIFICIAL (guard) = 1;

1823       TREE_USED (guard) = 1;

1824       pushdecl_top_level_and_finish (guard,NULL_TREE);

1825     }

1826     return guard;

1827   }

 

这个约束应该就像:“static int something = 0;”。为了给这些约束变量构建唯一的名字,修饰要发布代码的decl的名字是一个聪明的主意。看到在上面的代码中,约束的类型是int64_t,它足够容纳一个互斥量;另外这些变量被声明在全局名字空间里,如1824行所示。

并且如果__cxa_atexit是可用的,那么get_guard_cond构造了测试例子中的条件“(!guard)”的代码节点。

 

1851   tree

1852   get_guard_cond (tree guard)                                                                    in decl2.c

1853   {

1854     tree guard_value;

1855  

1856     /* Check to see ifthe GUARD is zero.  */

1857     guard = get_guard_bits(guard);

1858     guard_value = integer_zero_node;

1859     if (!same_type_p (TREE_TYPE (guard_value),TREE_TYPE (guard)))

1860       guard_value = convert (TREE_TYPE (guard),guard_value);

1861     returncp_build_binary_op (EQ_EXPR, guard, guard_value);

1862   }

 

在上面1857行,该函数构建了语句:“*((char*) &guard)”。因此测试条件应该是“(!*((char*) &guard))”,而剩下的空间将被用于互斥量。

 

1832   static tree

1833   get_guard_bits (tree guard)                                                                       indecl2.c

1834   {

1835     /* We only set thefirst byte of the guard, in order to leave room

1836       for a mutex in the high-orderbits.  */

1837     guard = build1 (ADDR_EXPR,

1838                  build_pointer_type(TREE_TYPE (guard)),

1839                  guard);

1840     guard = build1 (NOP_EXPR,

1841                  build_pointer_type(char_type_node),

1842                  guard);

1843     guard = build1 (INDIRECT_REF,char_type_node, guard);

1844  

1845     return guard;

1846   }

 

那么在2247行,finish_if_stmt_cond关闭了这个IF块的条件,它应该是一个布尔值,所以maybe_convert_cond评估cond的条件,并尝试把它转换到一个布尔值。在这里我们跳过这个函数因为它将是另一个漫长的故事。

 

462    void

463    finish_if_stmt_cond (tree cond, treeif_stmt)                                       insemantics.c

464    {

465      cond = maybe_convert_cond (cond);

466      FINISH_COND (cond, if_stmt, IF_COND(if_stmt));

467    }

 

另一方面,如果__cxa_atexit不可用,取而代之使用引用计数。接着,set_guard构建用于语句:“guard = 1;”的节点。

 

1867   tree

1868   set_guard (tree guard)                                                                              indecl2.c

1869   {

1870     tree guard_init;

1871  

1872     /* Set the GUARD toone.  */

1873     guard = get_guard_bits(guard);

1874     guard_init = integer_one_node;

1875     if (!same_type_p (TREE_TYPE (guard_init),TREE_TYPE (guard)))

1876       guard_init = convert (TREE_TYPE (guard),guard_init);

1877     returnbuild_modify_expr (guard, NOP_EXPR, guard_init);

1878   }

 

因此要是flag_use_cxa_atexit不是0,下面C++形式的语句(事实上,产生的代码具有中间树形式)将被产生:

void__static_initialization_and_destruction0 (int __initialize_p, int __priority)

{

  static int decl0_guard = 0;

if (decl0_priority == __priority&& __initialize_p == 1 && *((char*) &decl0_guard == 0)

{

   decl0_guard = 1;

}

回到do_static_initialization,如果出现了对象的初始值,象下面那样为之产生代码。注意到整个语句的类型被忽略了,例如,表达式“int j = 5;”具有整数类型并其值是5,但这两者都被忽略了。因此在内部,通过把该表达式转换到一个void类型,显式地说明了这个忽略。

 

425    tree

426    finish_expr_stmt (tree expr)                                                              insemantics.c

427    {

428      tree r = NULL_TREE;

429   

430      if (expr != NULL_TREE)

431      {

432        if (!processing_template_decl)

433          expr = convert_to_void (expr,"statement");

434        else if (!type_dependent_expression_p(expr))

435          convert_to_void (build_non_dependent_expr(expr), "statement");

436         

437        r = add_stmt(build_stmt (EXPR_STMT, expr));

438      }

439   

440      finish_stmt ();

441   

442      return r;

443    }

 

正如我们在前面章节看到的,聚集类的初始值由节点CONSTRUCTOR开头。现在这个函数看起来就像:

void__static_initialization_and_destruction0 (int __initialize_p, int __priority)

{

  static int decl0_guard = 0;

if (decl0_priority == __priority&& __initialize_p == 1 && *((char*) &decl0_guard == 0)

{

   decl0_guard = 1;

  decl0_constructor(decl0_init);

}

如果__cxa_atexit是可用的,编译器将用这个函数注册该对象的析构函数。下面的register_dtor_fn产生用于这个目的的代码。注意平凡析构函数在5269行被滤除。

 

5261   void

5262   register_dtor_fn (tree decl)                                                                              in decl.c

5263   {

5264     tree cleanup;

5265     tree compound_stmt;

5266     tree args;

5267     tree fcall;

5268  

5269     if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE(decl)))

5270       return;

5271  

5272     /* Callbuild_cleanup before we enter the anonymous function so that

5273       any access checks will be donerelative to the current scope,

5274       rather than the scope of theanonymous function.  */

5275     build_cleanup(decl);

5276  

5277     /* Now start thefunction.  */

5278     cleanup = start_cleanup_fn();

5279  

5280     /* Now, recomputethe cleanup. It may contain SAVE_EXPRs that refer

5281       to the original function,rather than the anonymous one. That

5282       will make the back-end thinkthat nested functions are in use,

5283       which causes confusion.  */

5284    

5285     push_deferring_access_checks(dk_no_check);

5286     fcall = build_cleanup(decl);

5287     pop_deferring_access_checks();

5288  

5289     /* Create the bodyof the anonymous function.  */

5290     compound_stmt = begin_compound_stmt(/*has_no_scope=*/false);

5291     finish_expr_stmt(fcall);

5292     finish_compound_stmt(compound_stmt);

5293     end_cleanup_fn();

5294  

5295     /* Call atexit withthe cleanup function.  */

5296     cxx_mark_addressable (cleanup);

5297     mark_used (cleanup);

5298     cleanup = build_unary_op (ADDR_EXPR, cleanup,0);

5299     if (flag_use_cxa_atexit)

5300     {

5301       args = tree_cons (NULL_TREE,

5302                     build_unary_op (ADDR_EXPR, get_dso_handle_node (), 0),

5303                     NULL_TREE);

5304       args = tree_cons (NULL_TREE, null_pointer_node,args);

5305       args = tree_cons (NULL_TREE, cleanup,args);

5306     }

5307     else

5308       args = tree_cons (NULL_TREE, cleanup,NULL_TREE);

5309     finish_expr_stmt(build_function_call (get_atexit_node (), args));

5310   }

 

因为build_cleanup使用查找标记LOOKUP_NORMAL(标识访问违规,并且如果匹配实参的成员函数找不到给出相关信息),LOOKUP_NONVIRTUAL(对找到的成员函数进行直接调用,即以A::f的形式,而不是通过vtable),LOOKUP_DESTRUCTOR(表示显式调用析构函数)来调用build_delete。在这个过程中将执行访问检查。因此build_cleanup首先被调用来保证没有访问错误,并且看到在由start_cleanup_fn构建的函数作用域中,对该函数的第二次调用之前,在5285行关闭访问检查。

 

1767   tree

1768   build_cleanup (tree decl)                                                                           in decl2.c

1769   {

1770     tree temp;

1771     tree type = TREE_TYPE (decl);

1772  

1773     /* This functionshould only be called for declarations that really

1774       require cleanups.  */

1775     my_friendly_assert (!TYPE_HAS_TRIVIAL_DESTRUCTOR(type), 20030106);

1776  

1777     /* Treat allobjects with destructors as used; the destructor may do

1778       something substantive.  */

1779     mark_used (decl);

1780  

1781     if (TREE_CODE (type) == ARRAY_TYPE)

1782       temp = decl;

1783     else

1784     {

1785       cxx_mark_addressable (decl);

1786       temp = build1(ADDR_EXPR, build_pointer_type (type), decl);

1787     }

1788     temp = build_delete(TREE_TYPE (temp), temp,

1789                      sfk_complete_destructor,

1790        LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR, 0);

1791     return temp;

1792   }

 

能被__cxa_atexit注册的函数必须具有原型“void func (void *)”,它不会匹配任何类的析构函数。因此start_cleanup_fn构建一个适用于这个注册的包装。注意在5193行的push_to_top_level,这个包装将被声明在全局域中。同样在5196行的push_lang_context只是把‘extern “C”’加在该函数声明的头部。

 

5183   static tree

5184   start_cleanup_fn (void)                                                                                   in decl.c

5185   {

5186     int old_interface_only = interface_only;

5187     int old_interface_unknown = interface_unknown;

5188     char name[32];

5189     tree parmtypes;

5190     tree fntype;

5191     tree fndecl;

5192  

5193     push_to_top_level();

5194  

5195    /* No need to manglethis.  */

5196     push_lang_context (lang_name_c);

5197  

5198     interface_only = 0;

5199     interface_unknown = 1;

5200  

5201    /* Build theparameter-types.  */

5202     parmtypes = void_list_node;

5203     /* Functions passedto __cxa_atexit take an additional parameter.

5204       We'll just ignore it. After weimplement the new calling

5205       convention for destructors, wecan eliminate the use of

5206       additional cleanup functionsentirely in the -fnew-abi case.  */

5207     if (flag_use_cxa_atexit)

5208       parmtypes = tree_cons (NULL_TREE,ptr_type_node, parmtypes);

5209     /* Build thefunction type itself.  */

5210     fntype = build_function_type(void_type_node, parmtypes);

5211     /* Build the nameof the function.  */

5212     sprintf (name, "__tcf_%d", start_cleanup_cnt++);

5213     /* Build thefunction declaration.  */

5214     fndecl = build_lang_decl(FUNCTION_DECL, get_identifier (name), fntype);

5215     /* It's a functionwith internal linkage, generated by the

5216       compiler.  */

5217     TREE_PUBLIC (fndecl) = 0;

5218     DECL_ARTIFICIAL (fndecl) = 1;

5219     /* Make thefunction `inline' so that it is only emitted if it is

5220       actually needed. It isunlikely that it will be inlined, since

5221       it is only called via afunction pointer, but we avoid unnecessary

5222       emissions this way.  */

5223     DECL_INLINE (fndecl) = 1;

5224     DECL_DECLARED_INLINE_P (fndecl) = 1;

5225     DECL_INTERFACE_KNOWN (fndecl) = 1;

5226     /* Build theparameter.  */

5227     if (flag_use_cxa_atexit)

5228     {

5229       tree parmdecl;

5230  

5231       parmdecl = cp_build_parm_decl(NULL_TREE, ptr_type_node);

5232       DECL_CONTEXT (parmdecl) = fndecl;

5233       TREE_USED (parmdecl) = 1;

5234       DECL_ARGUMENTS (fndecl) = parmdecl;

5235     }

5236  

5237     pushdecl (fndecl);

5238     start_function(/*specs=*/NULL_TREE, fndecl, NULL_TREE,SF_PRE_PARSED);

5239  

5240     interface_unknown = old_interface_unknown;

5241     interface_only = old_interface_only;

5242  

5243     pop_lang_context ();

5244  

5245     return current_function_decl;

5246   }

 

注意到该包装函数是为每个变量构建的,其名字遵循这样的形式:“__tcf_0” , “__tcf_1”,以此类推。那么在上面的5238行,start_function把这个函数作用域设为当前绑定域。接下来,5290 ~ 5292行把对这个析构函数的调用做成该包装函数的函数体,就像当调用build_delete时,使用标记sfk_complete_destructor来创建调用析构函数的代码。

 

5250   static void

5251   end_cleanup_fn (void)                                                                                     in decl.c

5252   {

5253     expand_or_defer_fn(finish_function (0));

5254  

5255     pop_from_top_level();

5256   }

 

通过end_cleanup_fn完成了包装函数的定义,并恢复绑定域。在构建了这个包装函数后,它可以通过__cxa_atexit被注册。不过在这之前,首先要找到dso_handle__cxa_atexit的节点。

 

5165   static tree

5166   get_dso_handle_node (void)                                                                            in decl.c

5167   {

5168     if (dso_handle_node)

5169       return dso_handle_node;

5170  

5171     /* Declare thevariable.  */

5172     dso_handle_node = declare_global_var (get_identifier("__dso_handle"),

5173                                      ptr_type_node);

5174  

5175     return dso_handle_node;

5176   }

 

上面函数的参数dso_handle事实上是一个外部全局变量,它被定义在库中,并在加载共享库的过程中被使用。而对于应用中静态链接部分,向该参数传入NULL就足够了。

 

5181   tree

5182   declare_global_var (tree name, treetype)                                                          in decl.c

5183   {

5184     tree decl;

5185  

5186     push_to_top_level();

5187     decl = build_decl(VAR_DECL, name, type);

5188     TREE_PUBLIC (decl) = 1;

5189     DECL_EXTERNAL (decl) = 1;

5190     DECL_ARTIFICIAL (decl) = 1;

5191     pushdecl (decl);

5192     cp_finish_decl(decl, NULL_TREE, NULL_TREE, 0);

5193     pop_from_top_level();

5194  

5195     return decl;

5196   }

 

类似地,__cxa_atexit的定义也是在库中。在这里get_atexit_node仅产生声明这个函数的代码,仿佛我们包含了相关头文件。

 

5102   static tree

5103   get_atexit_node (void)                                                                                    in decl.c

5104   {

5105     tree atexit_fndecl;

5106     tree arg_types;

5107     tree fn_type;

5108     tree fn_ptr_type;

5109     const char*name;

5110  

5111     if (atexit_node)

5112       return atexit_node;

5113  

5114     if (flag_use_cxa_atexit)

5115     {

5116       /* Thedeclaration for `__cxa_atexit' is:

5117  

5118          int __cxa_atexit (void (*)(void *), void*, void *)

5119  

5120         We build up the argumenttypes and then then function type

5121         itself.  */

5122  

5123       /* First, buildthe pointer-to-function type for the first

5124         argument.  */

5125       arg_types = tree_cons (NULL_TREE, ptr_type_node,void_list_node);

5126       fn_type = build_function_type(void_type_node, arg_types);

5127       fn_ptr_type = build_function_type(fn_type);

5128       /* Then, buildthe rest of the argument types.  */

5129       arg_types = tree_cons (NULL_TREE, ptr_type_node,void_list_node);

5130       arg_types = tree_cons (NULL_TREE, ptr_type_node,arg_types);

5131       arg_types = tree_cons (NULL_TREE,fn_ptr_type, arg_types);

5132       /* And the final__cxa_atexit type.  */

5133       fn_type = build_function_type(integer_type_node, arg_types);

5134       fn_ptr_type = build_pointer_type(fn_type);

5135       name = "__cxa_atexit";

5136     }

5137    else

5138     {

5139       /* Thedeclaration for `atexit' is:

5140  

5141          int atexit (void (*)());

5142  

5143         We build up the argumenttypes and then then function type

5144         itself.  */

5145       fn_type = build_function_type(void_type_node, void_list_node);

5146       fn_ptr_type = build_pointer_type(fn_type);

5147       arg_types = tree_cons (NULL_TREE,fn_ptr_type, void_list_node);

5148       /* Build thefinal atexit type.  */

5149       fn_type = build_function_type(integer_type_node, arg_types);

5150       name = "atexit";

5151     }

5152  

5153     /* Now, build thefunction declaration.  */

5154     push_lang_context (lang_name_c);

5155     atexit_fndecl = build_library_fn_ptr (name,fn_type);

5156     mark_used(atexit_fndecl);

5157     pop_lang_context ();

5158     atexit_node = decay_conversion(atexit_fndecl);

5159  

5160     returnatexit_node;

5161   }

 

然后register_dtor_fn的最后一部分为这个调用产生代码,并且在do_static_initialization2296行的finish_static_initialization_or_destruction加入“}”,因此到目前为止产生的代码是:

extern void* __dso_handle;

int __cxa_atexit (void (*)(void *),void *, void *);

 

void __tcf_0 (void *)

{

  decl0.destructor();

}

// other __tcf_* function

 

void__static_initialization_and_destruction0 (int __initialize_p, int __priority)

{

  static int decl0_guard = 0;

if (decl0_priority == __priority&& __initialize_p == 1 && *((char*) &decl0_guard == 0)

{

   decl0_guard = 1;

  decl0_constructor(decl0_init);

   __cxa_atexit(__tcf_0, 0,__dso_handle);

}

}”使得‘decl0_constructor’超出作用域,通过它对象将被具现。并记得current_function_declDECL_CONTEXTDECL_STATIC_FUNCTION_P域,在产生初始化静态类成员的代码时,可能发生改变,因此当完成初始化或析构时,总是恢复这些域。

 

2261   static void

2262   finish_static_initialization_or_destruction (tree guard_if_stmt)                      in decl2.c

2263   {

2264     finish_then_clause(guard_if_stmt);

2265     finish_if_stmt();

2266  

2267     /* Now that we'redone with DECL we don't need to pretend to be a

2268       member of its class any longer.  */

2269     DECL_CONTEXT (current_function_decl) = NULL_TREE;

2270     DECL_STATIC_FUNCTION_P (current_function_decl) = 0;

2271   }

 

注意到if_stmtTHEN_CLAUSENULL

 

472    tree

473    finish_then_clause (tree if_stmt)                                                        in semantics.c

474    {

475      RECHAIN_STMTS (if_stmt, THEN_CLAUSE(if_stmt));

476      returnif_stmt;

477    }

 

那么finish_if_stmt封闭了这个块,并从其退出。

 

497  void

498  finish_if_stmt (void)                                                                            in semantics.c

499  {

500   finish_stmt ();

501   do_poplevel ();

502  }

 

看到do_static_initialization被为每个全局/静态对象所调用,因此代码片段:

static int decl1_guard = 0;

if (decl1_priority == __priority&& __initialize_p == 1 && *((char*) &decl1_guard == 0)

{

   decl1_guard = 1;

   decl1_constructor(decl1_init);

   __cxa_atexit(__tcf_1,1__dso_handle);

}

为第二个对象所产生,以此类推。当初始化对象的所有代码都产生了,该初始化函数应该被相应地完成,就像如下。

 

2099 static void

2100 finish_static_storage_duration_function (tree body)                                      in decl2.c

2101 {

2102  /* Close out thefunction.  */

2103   finish_compound_stmt(body);

2104   expand_or_defer_fn(finish_function (0));

2105 }

 

 

原创粉丝点击