5.13.4.7. 发布代码根据优先级调用初始化函数
如果由代码 2573 至 2800 行执行的迭代稳定了下来——即不再产生新的东西,或者换句话说,所有显式或隐式涉及的对象都已经处理了,就可以继续完成机器代码分别这个目标。
finish_file (continue)
2801 /* All used inline functions must have a definition at this point. */
2802 for (i = 0; i < deferred_fns_used; ++i)
2803 {
2804 tree decl = VARRAY_TREE (deferred_fns , i);
2805
2806 if (TREE_USED (decl) && DECL_DECLARED_INLINE_P (decl)
2807 && !(TREE_ASM_WRITTEN (decl) || DECL_SAVED_TREE (decl)
2808 /* An explicit instantiation can be used to specify
2809 that the body is in another unit. It will have
2810 already verified there was a definition. */
2811 || DECL_EXPLICIT_INSTANTIATION (decl)))
2812 {
2813 cp_warning_at ("inline function `%D' used but never defined", decl);
2814 /* This symbol is effectively an "extern" declaration now.
2815 This is not strictly necessary, but removes a duplicate
2816 warning. */
2817 TREE_PUBLIC (decl) = 1;
2818 }
2819
2820 }
2821
2822 /* We give C linkage to static constructors and destructors. */
2823 push_lang_context (lang_name_c);
2824
2825 /* Generate initialization and destruction functions for all
2826 priorities for which they are required. */
2827 if (priority_info_map )
2828 splay_tree_foreach (priority_info_map ,
2829 generate_ctor_and_dtor_functions_for_priority ,
2830 /*data=*/ &locus);
2831 else
2832 {
2833
2834 if (static_ctors )
2835 generate_ctor_or_dtor_function (/*constructor_p=*/ true,
2836 DEFAULT_INIT_PRIORITY, &locus);
2837 if (static_dtors )
2838 generate_ctor_or_dtor_function (/*constructor_p=*/ false,
2839 DEFAULT_INIT_PRIORITY, &locus);
2840 }
2841
2842 /* We're done with the splay-tree now. */
2843 if (priority_info_map)
2844 splay_tree_delete (priority_info_map);
2845
2846 /* We're done with static constructors, so we can go back to "C++"
2847 linkage now. */
2848 pop_lang_context ();
首先,检查是否所有使用的内联函数都具有定义;否则就应该给出警告信息。
接着,我们已经看到具有初始值的全局聚集对象都已经构建了初始化函数。现在编译器已经定义了这样的初始化函数,编译器将要产生根据相关优先级调用这些函数的代码。注意到没有指定优先级的构造或析构函数将具有最低优先级,并且它们的初始化次序由编译器决定。
在 2824 行,因为所以初始化函数具有 C 链接性,首先使得 C 语言上下文成为当前的语言上下文。
除了属性“ init_priority ”,还有另一个属性具有类似的效果。
constructor [6] destructor constructor (priority) destructor (priority) constructor 属性使得该函数在执行进入 main () 之前被自动调用。相似地, destructor 属性使得这个函数,在 main () 完成或 exit () 被调用后,被自动调用。具有这些属性的函数,对于初始化在程序执行过程中被隐式使用的数据,是有用的。 你可以提供一个可选的整数优先级来控制构造函数及析构函数的运行次序。具有较小优先级数值的构造函数在具有较大优先级数值的构造函数之前运行;相反的关系对于析构函数成立。因此,如果你具有一个分配资源的构造函数,一个回收相同资源的析构函数,两者通常具有相同的优先级。构造函数及析构函数的优先级与那些为名字空间域中的 C++ 对象指定的相同。 注 [ 作者 ] : gcc-3.4.6不支持后两者 |
如果看到这样的属性,它将被保存入 static_ctors 或 static_dtors ;并且看到在下面的代码中,在 gcc-3.4.6 里,由上面属性引入的构造函数 / 析构函数仅具有默认的最低优先级。函数 splay_tree_foreach 依次访问伸展树 priority_info_map ,这棵树以优先级值排序,因此中序遍历使得下面的函数,以优先级值升序的次序,在节点上执行。
2471 static int
2472 generate_ctor_and_dtor_functions_for_priority (splay_tree_node n, void * data) in decl2.c
2473 {
2474 location_t *locus = data;
2475 int priority = (int) n->key;
2476 priority_info pi = (priority_info) n->value;
2477
2478 /* Generate the functions themselves, but only if they are really
2479 needed. */
2480 if (pi->initializations_p
2481 || (priority == DEFAULT_INIT_PRIORITY && static_ctors ))
2482 generate_ctor_or_dtor_function (/*constructor_p=*/ true, priority, locus);
2483 if (pi->destructions_p
2484 || (priority == DEFAULT_INIT_PRIORITY && static_dtors ))
2485 generate_ctor_or_dtor_function (/*constructor_p=*/ false, priority, locus);
2486
2487 /* Keep iterating. */
2488 return 0;
2489 }
那么对于所有具有相同优先级的对象,它们按声明次序初始化(它们以反序插入 static_aggregates 或 pending_statics ,但编译器再反向地产生初始化函数)。
2398 static void
2399 generate_ctor_or_dtor_function (bool constructor_p, int priority, in decl2.c
2400 location_t *locus)
2401 {
2402 char function_key;
2403 tree arguments;
2404 tree fndecl;
2405 tree body;
2406 size_t i;
2407
2408 input_location = *locus;
2409 locus->line++;
2410
2411 /* We use `I' to indicate initialization and `D' to indicate
2412 destruction. */
2413 function_key = constructor_p ? 'I' : 'D';
2414
2415 /* We emit the function lazily, to avoid generating empty
2416 global constructors and destructors. */
2417 body = NULL_TREE;
2418
2419 /* Call the static storage duration function with appropriate
2420 arguments. */
2421 if (ssdf_decls )
2422 for (i = 0; i < ssdf_decls ->elements_used; ++i)
2423 {
2424 fndecl = VARRAY_TREE (ssdf_decls , i);
2425
2426 /* Calls to pure or const functions will expand to nothing. */
2427 if (! (flags_from_decl_or_type (fndecl) & (ECF_CONST | ECF_PURE)))
2428 {
2429 if (! body)
2430 body = start_objects (function_key, priority);
2431
2432 arguments = tree_cons (NULL_TREE, build_int_2 (priority, 0),
2433 NULL_TREE);
2434 arguments = tree_cons (NULL_TREE, build_int_2 (constructor_p, 0),
2435 arguments);
2436 finish_expr_stmt (build_function_call (fndecl, arguments));
2437 }
2438 }
在前面的章节, void __static_initialization_and_destruction0 (int, int) 被定义在第一次迭代中,其中找到需要初始化的全局对象;然后 void __static_initialization_and_destruction1 (int, int) 被定义在第二次迭代中,其中具有需要初始化的全局对象;依此类推。在每个函数中,它把优先级接受为第二个参数,并仅初始化具有匹配优先级的对象。注意到所有初始化函数已经被记录在 ssdf_decls 中。在这些初始化函数里,任意纯的或常量函数将被滤除,因为将没有全局对象会被构建。下面的函数区分 exp 指定函数的特性。
698 int
699 flags_from_decl_or_type (tree exp) in call.c
700 {
701 int flags = 0;
702 tree type = exp;
703
704 if (DECL_P (exp))
705 {
706 struct cgraph_rtl_info *i = cgraph_rtl_info (exp);
707 type = TREE_TYPE (exp);
708
709 if (i)
710 {
711 if (i->pure_function)
712 flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
713 if (i->const_function)
714 flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
715 }
716
717 /* The function exp may have the `malloc' attribute. */
718 if (DECL_IS_MALLOC (exp))
719 flags |= ECF_MALLOC;
720
721 /* The function exp may have the `pure' attribute. */
722 if (DECL_IS_PURE (exp))
723 flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
724
725 if (TREE_NOTHROW (exp))
726 flags |= ECF_NOTHROW;
727
728 if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
729 flags |= ECF_LIBCALL_BLOCK;
730 }
731
732 if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
733 flags |= ECF_CONST;
734
735 if (TREE_THIS_VOLATILE (exp))
736 flags |= ECF_NORETURN;
737
738 /* Mark if the function returns with the stack pointer depressed. We
739 cannot consider it pure or constant in that case. */
740 if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))
741 {
742 flags |= ECF_SP_DEPRESSED;
743 flags &= ~(ECF_PURE | ECF_CONST | ECF_LIBCALL_BLOCK);
744 }
745
746 return flags;
747 }
在这里,如果该函数具有关联的 cgraph_rtl_info 节点,就使用这个节点。否则,根据这个 *_DECL 节点执行调整。但是在 cgraph_rtl_info 节点中, pure 意味着仅读入非局部,非常量的实体;而 constant 意味着仅读入非局部及常量实体。那么对于留下的初始化函数,编译器需要产生一个函数来,为记录在 priority_info_map 中具有特定优先级的对象,调用这些函数。它们由 start_objects 来声明。
1883 static tree
1884 start_objects (int method_type, int initp) in decl2.c
1885 {
1886 tree fnname;
1887 tree body;
1888 char type[10];
1889
1890 /* Make ctor or dtor function. METHOD_TYPE may be 'I' or 'D'. */
1891
1892 if (initp != DEFAULT_INIT_PRIORITY)
1893 {
1894 char joiner;
1895
1896 #ifdef JOINER
1897 joiner = JOINER;
1898 #else
1899 joiner = '_';
1900 #endif
1901
1902 sprintf (type, "%c%c%.5u", method_type, joiner, initp);
1903 }
1904 else
1905 sprintf (type, "%c", method_type);
1906
1907 fnname = get_file_function_name_long (type);
1908
1909 start_function (void_list_node,
1910 make_call_declarator (fnname, void_list_node, NULL_TREE,
1911 NULL_TREE),
1912 NULL_TREE, SF_DEFAULT);
1913
1914 /* It can be a static function as long as collect2 does not have
1915 to scan the object file to find its ctor/dtor routine. */
1916 TREE_PUBLIC (current_function_decl ) = ! targetm .have_ctors_dtors;
1917
1918 /* Mark this declaration as used to avoid spurious warnings. */
1919 TREE_USED (current_function_decl ) = 1;
1920
1921 /* Mark this function as a global constructor or destructor. */
1922 if (method_type == 'I')
1923 DECL_GLOBAL_CTOR_P (current_function_decl ) = 1;
1924 else
1925 DECL_GLOBAL_DTOR_P (current_function_decl ) = 1;
1926 DECL_LANG_SPECIFIC (current_function_decl )->decl_flags.u2sel = 1;
1927
1928 body = begin_compound_stmt (/*has_no_scope=*/ false);
1929
1940 /* We cannot allow these functions to be elided, even if they do not
1941 have external linkage. And, there's no point in deferring
1942 compilation of thes functions; they're all going to have to be
1943 out anyhow. */
1944 current_function_cannot_inline
1945 = "static constructors and destructors cannot be inlined";
1946
1947 return body;
1948 }
JOINER 在我们假定的平台下是“ $ ”,实参 method_type 对于构造函数是“ I ”,对于析构函数是“ D ”,因此 1905 行的 type ,对于构造函数将具有形式“ I$.1” ,“ I$.2” 等,对于析构函数将具有形式“ D$.1” ,“ D$.2” 等。
下面看到在 generate_ctor_or_dtor_function 的 2432 到 2436 行产生类似: __static_initialization_and_destruction0 (1, 1) 的语句来初始化具有优先级 1 的对象。注意到所有的初始化函数调用将使用优先级参数。
generate_ctor_or_dtor_function (continue)
2440 /* If we're generating code for the DEFAULT_INIT_PRIORITY, throw in
2441 calls to any functions marked with attributes indicating that
2442 they should be called at initialization- or destruction-time. */
2443 if (priority == DEFAULT_INIT_PRIORITY)
2444 {
2445 tree fns;
2446
2447 for (fns = constructor_p ? static_ctors : static_dtors ;
2448 fns;
2449 fns = TREE_CHAIN (fns))
2450 {
2451 fndecl = TREE_VALUE (fns);
2452
2453 /* Calls to pure/const functions will expand to nothing. */
2454 if (! (flags_from_decl_or_type (fndecl) & (ECF_CONST | ECF_PURE)))
2455 {
2456 if (! body)
2457 body = start_objects (function_key, priority);
2458 finish_expr_stmt (build_function_call (fndecl, NULL_TREE));
2459 }
2460 }
2461 }
2462
2463 /* Close out the function. */
2464 if (body)
2465 finish_objects (function_key, priority, body);
2466 }
对于具有默认优先级 DEFAULT_INIT_PRIORITY ( 0xffff )的对象,不需要产生初始化函数,这里仅需要依次调用注册的构造函数 / 析构函数。