If iteration taken by code between line 2573 to 2800 turns out stable – that is nothing new is generated or in other words all objects explicitly or implicitly involved have been processed, it can go ahead towards the goal of emitting machine code.
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 ();
At first, it checks if all used inline functions have definition; otherwise it should give out warning messages.
Next, we have seen that global aggregates with initializers have initialization functions built. Now all such initialization functions have been defined by the compiler, the compile is going to generate to code to invoke these functions according to the priority associated. Note that construction or destruction functions without priority specified will have the lowest priority and their initialization order is determined by the compiler.
At line 2824, as all initialization functions have C linkage, first makes C language context as the current one.
Besides attribue “init_priority”, there is another attribute has similar effect.
constructor [6] destructor constructor (priority) destructor (priority) The constructor attribute causes the function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () has completed or exit () has been called. Functions with these attributes are useful for initializing data that will be used implicitly during the execution of the program. You may provide an optional integer priority to control the order in which constructor and destructor functions are run. A constructor with a smaller priority number runs before a constructor with a larger priority number; the opposite relationship holds for destructors. So, if you have a constructor that allocates a resource and a destructor that deallocates the same resource, both functions typically have the same priority. The priorities for constructor and destructor functions are the same as those specified for namespace-scope C++ objects Note [author]: gcc-3.4.6 doesn’t support the rear two |
If such attribute is seen, it will be stored into static_ctors or static_dtors ; and see in below code, in gcc-3.4.6, constructor / destructor introduced by above attributes only has the default lowest priority. Routine splay_tree_foreach visits the splay tree in-order, and splay tree priority_info_map is ordered by priority value, so the in-order tansversal makes below function executed upon nodes in the order of increasing priority value.
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 }
Then for all objects with the same priority, they are initialized by the declaration order (they are inserted into static_aggregates or pending_statics in reversed order, but the compiler generates the initialization functions reversely again).
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 }
In previous section, void __static_initialization_and_destruction0 (int, int) is defined in the first iteration in which global object(s) needing initialization is(are) found; then void __static_initialization_and_destruction1 (int, int) is defined in the second iteration that has global object needing initialization; and so on. In every function, it accepts priority as second parameter and only initializes objects with the matching priority. Note that all initialization functions have been recorded within ssdf_decls . Among these initialization functions, any pure or constant function will be filtered as no global object can be created. Below function tells the characteristics of function of 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 }
At here, if the function has associated cgraph_rtl_info node generated just uses it. Otherwise, do the judgement according to the *_DECL node. But in cgraph_rtl_info node, pure means only reading entity non-local and non-constant; and constant means only may read entity non-local and constant. Then for the left initialization functions, the compiler needs to generate a function to invoke them for objects recorded in priority_info_map with certain priority, which is declared by 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 under our assuming target is “$”, argument method_type is “I” for constructor and “D” for destructor, so type at line 1905 will be in form of “I$.1”, “I$.2” and so on for constructor or “D$.1”, “D$.2” and so on for destructor.
Below see line 2432 to 2436 in generate_ctor_or_dtor_function generates the staterment like: __static_initialization_and_destruction0 (1, 1) to initialize objects of priority 1. Note that all initialization functions will be invoked with the priority.
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 }
For objects with default priority of DEFAULT_INIT_PRIORITY (0xffff), no initialization functions need be generated, here just needs invoke the registered constructor/destructor in turn.