Studying note of GCC-3.4.6 source (161)

    技术2025-07-22  13

    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.

     

    最新回复(0)