Studying note of GCC-3.4.6 source (161)

5.13.4.7.              Generate code for invoking initializer function according to priority

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.

 

你可能感兴趣的:(function,tree,Constructor,initialization,destructor,Instantiation)