mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-24 19:33:59 +08:00
c++: Reimplement static init/fini generation
Currently we generate static init/fini code by generating a set of functions taking an 'initp' bool and an unsigned priority. (There can be more than one, as we repeat the end-of-compile loop.) We then generate a set of real init or fini functions for each needed prioroty, calling the previous set of functions. This is of course very tangled, but excitingly the value-range-propagator is clever enough to unentangle it. However, the current arrangement makes generation awkward, particularly as to how to optimize the module-global-init generation. This reimplements the generation to generate a set of separate init/fini functions for each needed priority, and then call them from the real inits previously mentioned. This replaces a splay tree, recording which priority/init combos we needed, with a pair of hash tables, mapping priority to init functions. Much simpler. While there, rename several of the functions as they are only dealing with part of the init/fini generation, not the whole set. gcc/cp/ * decl2.cc (struct priority_info_s, priority_info): Delete. (priority_map_traits, priority_map_t): New. (static_init_fini_fns): New. (INITIALIZE_P_IDENTIFIER, PRIORITY_IDENTIFIER): Delete. (initialize_p_decl, priority_decl): Delete. (ssdf_decls, priority_info_map): Delete. (start_static_storage_duration_function): Rename to ... (start_partial_init_fini_fn): ... here. Create a void arg fn. Add it to the slot in the appropriate static_init_fini_fns hash table. (finish_static_storage_duration_function): Rename to ... (finish_partial_init_fini_fn): ... here. (get_priority_info): Delete. (one_static_initialization_or_destruction): Assert not trivial dtor. (do_static_initialization_or_destruction): Rename to ... (emit_partial_init_fini_fn) ... here. Start & finish the fn. Simply init/fini each var. (partition_vars_for_init_fini): Partition vars according to priority and add to init and/or fini list. (generate_ctor_or_dtor_function): Start and finish the function. Do santitizer calls here. (generate_ctor_and_dtor_functions_for_priority): Delete. (c_parse_final_cleanups): Reimplement global init/fini processing. gcc/testsuite/ * g++.dg/init/static-cdtor1.C: New.
This commit is contained in:
parent
d8c2580941
commit
90a6c3b6d6
482
gcc/cp/decl2.cc
482
gcc/cp/decl2.cc
@ -55,27 +55,14 @@ int raw_dump_id;
|
||||
|
||||
extern cpp_reader *parse_in;
|
||||
|
||||
/* This structure contains information about the initializations
|
||||
and/or destructions required for a particular priority level. */
|
||||
typedef struct priority_info_s {
|
||||
/* Nonzero if there have been any initializations at this priority
|
||||
throughout the translation unit. */
|
||||
int initializations_p;
|
||||
/* Nonzero if there have been any destructions at this priority
|
||||
throughout the translation unit. */
|
||||
int destructions_p;
|
||||
} *priority_info;
|
||||
|
||||
static tree start_objects (bool, unsigned);
|
||||
static tree finish_objects (bool, unsigned, tree);
|
||||
static tree start_static_storage_duration_function (unsigned);
|
||||
static void finish_static_storage_duration_function (tree);
|
||||
static priority_info get_priority_info (int);
|
||||
static void do_static_initialization_or_destruction (bool, tree);
|
||||
static tree start_partial_init_fini_fn (bool, unsigned, unsigned);
|
||||
static void finish_partial_init_fini_fn (tree);
|
||||
static void emit_partial_init_fini_fn (bool, unsigned, tree,
|
||||
unsigned, location_t);
|
||||
static void one_static_initialization_or_destruction (bool, tree, tree);
|
||||
static void generate_ctor_or_dtor_function (bool, unsigned, location_t *);
|
||||
static int generate_ctor_and_dtor_functions_for_priority (splay_tree_node,
|
||||
void *);
|
||||
static void generate_ctor_or_dtor_function (bool, unsigned, tree, location_t);
|
||||
static tree prune_vars_needing_no_initialization (tree *);
|
||||
static void write_out_vars (tree);
|
||||
static void import_export_class (tree);
|
||||
@ -136,6 +123,51 @@ struct mangled_decl_hash : ggc_remove <tree>
|
||||
we need compatibility aliases. */
|
||||
static GTY(()) hash_table<mangled_decl_hash> *mangled_decls;
|
||||
|
||||
// Hash table mapping priority to lists of variables or functions.
|
||||
struct priority_map_traits
|
||||
{
|
||||
typedef unsigned key_type;
|
||||
typedef tree value_type;
|
||||
static const bool maybe_mx = true;
|
||||
static hashval_t hash (key_type v)
|
||||
{
|
||||
return hashval_t (v);
|
||||
}
|
||||
static bool equal_keys (key_type k1, key_type k2)
|
||||
{
|
||||
return k1 == k2;
|
||||
}
|
||||
template <typename T> static void remove (T &)
|
||||
{
|
||||
}
|
||||
// Zero is not a priority
|
||||
static const bool empty_zero_p = true;
|
||||
template <typename T> static bool is_empty (const T &entry)
|
||||
{
|
||||
return entry.m_key == 0;
|
||||
}
|
||||
template <typename T> static void mark_empty (T &entry)
|
||||
{
|
||||
entry.m_key = 0;
|
||||
}
|
||||
// Entries are not deleteable
|
||||
template <typename T> static bool is_deleted (const T &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
template <typename T> static void mark_deleted (T &entry)
|
||||
{
|
||||
gcc_unreachable ();
|
||||
}
|
||||
};
|
||||
|
||||
typedef hash_map<unsigned/*Priority*/, tree/*List*/,
|
||||
priority_map_traits> priority_map_t;
|
||||
|
||||
/* A pair of such hash tables, indexed by initp -- one for fini and
|
||||
one for init. The fini table is only ever used when !cxa_atexit. */
|
||||
static GTY(()) priority_map_t *static_init_fini_fns[2];
|
||||
|
||||
/* Nonzero if we're done parsing and into end-of-file activities. */
|
||||
|
||||
int at_eof;
|
||||
@ -3927,45 +3959,18 @@ finish_objects (bool initp, unsigned priority, tree body)
|
||||
return fn;
|
||||
}
|
||||
|
||||
/* The names of the parameters to the function created to handle
|
||||
initializations and destructions for objects with static storage
|
||||
duration. */
|
||||
#define INITIALIZE_P_IDENTIFIER "__initialize_p"
|
||||
#define PRIORITY_IDENTIFIER "__priority"
|
||||
|
||||
/* The name of the function we create to handle initializations and
|
||||
destructions for objects with static storage duration. */
|
||||
#define SSDF_IDENTIFIER "__static_initialization_and_destruction"
|
||||
|
||||
/* The declaration for the __INITIALIZE_P argument. */
|
||||
static GTY(()) tree initialize_p_decl;
|
||||
|
||||
/* The declaration for the __PRIORITY argument. */
|
||||
static GTY(()) tree priority_decl;
|
||||
|
||||
/* All the static storage duration functions created in this
|
||||
translation unit. */
|
||||
static GTY(()) vec<tree, va_gc> *ssdf_decls;
|
||||
|
||||
/* A map from priority levels to information about that priority
|
||||
level. There may be many such levels, so efficient lookup is
|
||||
important. */
|
||||
static splay_tree priority_info_map;
|
||||
|
||||
/* Begins the generation of the function that will handle all
|
||||
initialization and destruction of objects with static storage
|
||||
duration. The function generated takes two parameters of type
|
||||
`int': __INITIALIZE_P and __PRIORITY. If __INITIALIZE_P is
|
||||
nonzero, it performs initializations. Otherwise, it performs
|
||||
destructions. It only performs those initializations or
|
||||
destructions with the indicated __PRIORITY. The generated function
|
||||
returns no value.
|
||||
initialization or destruction of objects with static storage
|
||||
duration at PRIORITY.
|
||||
|
||||
It is assumed that this function will only be called once per
|
||||
translation unit. */
|
||||
It is assumed that this function will only be called once. */
|
||||
|
||||
static tree
|
||||
start_static_storage_duration_function (unsigned count)
|
||||
start_partial_init_fini_fn (bool initp, unsigned priority, unsigned count)
|
||||
{
|
||||
char id[sizeof (SSDF_IDENTIFIER) + 1 /* '\0' */ + 32];
|
||||
|
||||
@ -3973,9 +3978,7 @@ start_static_storage_duration_function (unsigned count)
|
||||
SSDF_IDENTIFIER_<number>. */
|
||||
sprintf (id, "%s_%u", SSDF_IDENTIFIER, count);
|
||||
|
||||
tree type = build_function_type_list (void_type_node,
|
||||
integer_type_node, integer_type_node,
|
||||
NULL_TREE);
|
||||
tree type = build_function_type (void_type_node, void_list_node);
|
||||
|
||||
/* Create the FUNCTION_DECL itself. */
|
||||
tree fn = build_lang_decl (FUNCTION_DECL, get_identifier (id), type);
|
||||
@ -3984,36 +3987,10 @@ start_static_storage_duration_function (unsigned count)
|
||||
|
||||
/* Put this function in the list of functions to be called from the
|
||||
static constructors and destructors. */
|
||||
if (!ssdf_decls)
|
||||
{
|
||||
vec_alloc (ssdf_decls, 32);
|
||||
|
||||
/* Take this opportunity to initialize the map from priority
|
||||
numbers to information about that priority level. */
|
||||
priority_info_map = splay_tree_new (splay_tree_compare_ints,
|
||||
/*delete_key_fn=*/0,
|
||||
/*delete_value_fn=*/
|
||||
splay_tree_delete_pointers);
|
||||
|
||||
/* We always need to generate functions for the
|
||||
DEFAULT_INIT_PRIORITY so enter it now. That way when we walk
|
||||
priorities later, we'll be sure to find the
|
||||
DEFAULT_INIT_PRIORITY. */
|
||||
get_priority_info (DEFAULT_INIT_PRIORITY);
|
||||
}
|
||||
|
||||
vec_safe_push (ssdf_decls, fn);
|
||||
|
||||
/* Create the argument list. */
|
||||
initialize_p_decl = cp_build_parm_decl
|
||||
(fn, get_identifier (INITIALIZE_P_IDENTIFIER), integer_type_node);
|
||||
TREE_USED (initialize_p_decl) = 1;
|
||||
priority_decl = cp_build_parm_decl
|
||||
(fn, get_identifier (PRIORITY_IDENTIFIER), integer_type_node);
|
||||
TREE_USED (priority_decl) = 1;
|
||||
|
||||
DECL_CHAIN (initialize_p_decl) = priority_decl;
|
||||
DECL_ARGUMENTS (fn) = initialize_p_decl;
|
||||
if (!static_init_fini_fns[initp])
|
||||
static_init_fini_fns[initp] = priority_map_t::create_ggc ();
|
||||
auto &slot = static_init_fini_fns[initp]->get_or_insert (priority);
|
||||
slot = tree_cons (fn, NULL_TREE, slot);
|
||||
|
||||
/* Put the function in the global scope. */
|
||||
pushdecl (fn);
|
||||
@ -4032,46 +4009,16 @@ start_static_storage_duration_function (unsigned count)
|
||||
}
|
||||
|
||||
/* Finish the generation of the function which performs initialization
|
||||
and destruction of objects with static storage duration. After
|
||||
this point, no more such objects can be created. */
|
||||
or destruction of objects with static storage duration. */
|
||||
|
||||
static void
|
||||
finish_static_storage_duration_function (tree body)
|
||||
finish_partial_init_fini_fn (tree body)
|
||||
{
|
||||
/* Close out the function. */
|
||||
finish_compound_stmt (body);
|
||||
expand_or_defer_fn (finish_function (/*inline_p=*/false));
|
||||
}
|
||||
|
||||
/* Return the information about the indicated PRIORITY level. If no
|
||||
code to handle this level has yet been generated, generate the
|
||||
appropriate prologue. */
|
||||
|
||||
static priority_info
|
||||
get_priority_info (int priority)
|
||||
{
|
||||
priority_info pi;
|
||||
splay_tree_node n;
|
||||
|
||||
n = splay_tree_lookup (priority_info_map,
|
||||
(splay_tree_key) priority);
|
||||
if (!n)
|
||||
{
|
||||
/* Create a new priority information structure, and insert it
|
||||
into the map. */
|
||||
pi = XNEW (struct priority_info_s);
|
||||
pi->initializations_p = 0;
|
||||
pi->destructions_p = 0;
|
||||
splay_tree_insert (priority_info_map,
|
||||
(splay_tree_key) priority,
|
||||
(splay_tree_value) pi);
|
||||
}
|
||||
else
|
||||
pi = (priority_info) n->value;
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
/* The effective initialization priority of a DECL. */
|
||||
|
||||
#define DECL_EFFECTIVE_INIT_PRIORITY(decl) \
|
||||
@ -4117,9 +4064,7 @@ one_static_initialization_or_destruction (bool initp, tree decl, tree init)
|
||||
{
|
||||
/* If we are supposed to destruct and there's a trivial destructor,
|
||||
nothing has to be done. */
|
||||
if (!initp
|
||||
&& TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
|
||||
return;
|
||||
gcc_checking_assert (init || !TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)));
|
||||
|
||||
/* Trick the compiler into thinking we are at the file and line
|
||||
where DECL was declared so that error-messages make sense, and so
|
||||
@ -4241,81 +4186,21 @@ one_static_initialization_or_destruction (bool initp, tree decl, tree init)
|
||||
Whether initialization or destruction is performed is specified by INITP. */
|
||||
|
||||
static void
|
||||
do_static_initialization_or_destruction (bool initp, tree vars)
|
||||
emit_partial_init_fini_fn (bool initp, unsigned priority, tree vars,
|
||||
unsigned counter, location_t locus)
|
||||
{
|
||||
/* Build the outer if-stmt to check for initialization or destruction. */
|
||||
tree init_if_stmt = begin_if_stmt ();
|
||||
tree cond = initp ? integer_one_node : integer_zero_node;
|
||||
cond = cp_build_binary_op (input_location,
|
||||
EQ_EXPR,
|
||||
initialize_p_decl,
|
||||
cond,
|
||||
tf_warning_or_error);
|
||||
finish_if_stmt_cond (cond, init_if_stmt);
|
||||
input_location = locus;
|
||||
tree body = start_partial_init_fini_fn (initp, priority, counter);
|
||||
|
||||
/* To make sure dynamic construction doesn't access globals from other
|
||||
compilation units where they might not be yet constructed, for
|
||||
-fsanitize=address insert __asan_before_dynamic_init call that
|
||||
prevents access to either all global variables that need construction
|
||||
in other compilation units, or at least those that haven't been
|
||||
initialized yet. Variables that need dynamic construction in
|
||||
the current compilation unit are kept accessible. */
|
||||
if (initp && (flag_sanitize & SANITIZE_ADDRESS))
|
||||
finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/false));
|
||||
for (tree node = vars; node; node = TREE_CHAIN (node))
|
||||
/* Do one initialization or destruction. */
|
||||
one_static_initialization_or_destruction (initp, TREE_VALUE (node),
|
||||
TREE_PURPOSE (node));
|
||||
|
||||
tree node = vars;
|
||||
do {
|
||||
tree decl = TREE_VALUE (node);
|
||||
|
||||
/* If we don't need a destructor, there's nothing to do. Avoid
|
||||
creating a possibly empty if-stmt. */
|
||||
if (!initp && TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
|
||||
{
|
||||
node = TREE_CHAIN (node);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Remember that we had an initialization or finalization at this
|
||||
priority. */
|
||||
int priority = DECL_EFFECTIVE_INIT_PRIORITY (decl);
|
||||
priority_info pi = get_priority_info (priority);
|
||||
if (initp)
|
||||
pi->initializations_p = 1;
|
||||
else
|
||||
pi->destructions_p = 1;
|
||||
|
||||
/* Conditionalize this initialization on being in the right priority
|
||||
and being initializing/finalizing appropriately. */
|
||||
tree priority_if_stmt = begin_if_stmt ();
|
||||
cond = cp_build_binary_op (input_location,
|
||||
EQ_EXPR,
|
||||
priority_decl,
|
||||
build_int_cst (NULL_TREE, priority),
|
||||
tf_warning_or_error);
|
||||
finish_if_stmt_cond (cond, priority_if_stmt);
|
||||
|
||||
/* Process initializers with same priority. */
|
||||
for (; node
|
||||
&& DECL_EFFECTIVE_INIT_PRIORITY (TREE_VALUE (node)) == priority;
|
||||
node = TREE_CHAIN (node))
|
||||
/* Do one initialization or destruction. */
|
||||
one_static_initialization_or_destruction (initp, TREE_VALUE (node),
|
||||
TREE_PURPOSE (node));
|
||||
|
||||
/* Finish up the priority if-stmt body. */
|
||||
finish_then_clause (priority_if_stmt);
|
||||
finish_if_stmt (priority_if_stmt);
|
||||
|
||||
} while (node);
|
||||
|
||||
/* Revert what __asan_before_dynamic_init did by calling
|
||||
__asan_after_dynamic_init. */
|
||||
if (initp && (flag_sanitize & SANITIZE_ADDRESS))
|
||||
finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/true));
|
||||
|
||||
/* Finish up the init/destruct if-stmt body. */
|
||||
finish_then_clause (init_if_stmt);
|
||||
finish_if_stmt (init_if_stmt);
|
||||
/* Finish up the static storage duration function for this
|
||||
round. */
|
||||
input_location = locus;
|
||||
finish_partial_init_fini_fn (body);
|
||||
}
|
||||
|
||||
/* VARS is a list of variables with static storage duration which may
|
||||
@ -4323,8 +4208,7 @@ do_static_initialization_or_destruction (bool initp, tree vars)
|
||||
that don't really need to be initialized or finalized, and return
|
||||
the resulting list. The order in which the variables appear in
|
||||
VARS is in reverse order of the order in which they should actually
|
||||
be initialized. The list we return is in the unreversed order;
|
||||
i.e., the first variable should be initialized first. */
|
||||
be initialized. That order is preserved. */
|
||||
|
||||
static tree
|
||||
prune_vars_needing_no_initialization (tree *vars)
|
||||
@ -4375,6 +4259,39 @@ prune_vars_needing_no_initialization (tree *vars)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Split VAR_LIST by init priority and add into PARTS hash table.
|
||||
This reverses the variable ordering. */
|
||||
|
||||
void
|
||||
partition_vars_for_init_fini (tree var_list, priority_map_t *(&parts)[2])
|
||||
{
|
||||
for (auto node = var_list; node; node = TREE_CHAIN (node))
|
||||
{
|
||||
tree decl = TREE_VALUE (node);
|
||||
tree init = TREE_PURPOSE (node);
|
||||
bool has_cleanup = !TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl));
|
||||
unsigned priority = DECL_EFFECTIVE_INIT_PRIORITY (decl);
|
||||
|
||||
if (init || (flag_use_cxa_atexit && has_cleanup))
|
||||
{
|
||||
// Add to initialization list.
|
||||
if (!parts[true])
|
||||
parts[true] = priority_map_t::create_ggc ();
|
||||
auto &slot = parts[true]->get_or_insert (priority);
|
||||
slot = tree_cons (init, decl, slot);
|
||||
}
|
||||
|
||||
if (!flag_use_cxa_atexit && has_cleanup)
|
||||
{
|
||||
// Add to finalization list.
|
||||
if (!parts[false])
|
||||
parts[false] = priority_map_t::create_ggc ();
|
||||
auto &slot = parts[false]->get_or_insert (priority);
|
||||
slot = tree_cons (NULL_TREE, decl, slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure we have told the back end about all the variables in
|
||||
VARS. */
|
||||
|
||||
@ -4399,76 +4316,51 @@ write_out_vars (tree vars)
|
||||
storage duration having the indicated PRIORITY. */
|
||||
|
||||
static void
|
||||
generate_ctor_or_dtor_function (bool initp, unsigned priority, location_t *locus)
|
||||
generate_ctor_or_dtor_function (bool initp, unsigned priority,
|
||||
tree fns, location_t locus)
|
||||
{
|
||||
input_location = *locus;
|
||||
input_location = locus;
|
||||
|
||||
/* We emit the function lazily, to avoid generating empty
|
||||
global constructors and destructors. */
|
||||
tree body = NULL_TREE;
|
||||
tree body = start_objects (initp, priority);
|
||||
|
||||
if (initp && priority == DEFAULT_INIT_PRIORITY)
|
||||
/* To make sure dynamic construction doesn't access globals from other
|
||||
compilation units where they might not be yet constructed, for
|
||||
-fsanitize=address insert __asan_before_dynamic_init call that
|
||||
prevents access to either all global variables that need construction
|
||||
in other compilation units, or at least those that haven't been
|
||||
initialized yet. Variables that need dynamic construction in
|
||||
the current compilation unit are kept accessible. */
|
||||
if (initp && (flag_sanitize & SANITIZE_ADDRESS))
|
||||
finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/false));
|
||||
|
||||
if (initp && priority == DEFAULT_INIT_PRIORITY
|
||||
&& c_dialect_objc () && objc_static_init_needed_p ())
|
||||
/* For Objective-C++, we may need to initialize metadata found in
|
||||
this module. This must be done _before_ any other static
|
||||
initializations. */
|
||||
objc_generate_static_init_call (NULL_TREE);
|
||||
|
||||
/* Call the static init/fini functions. */
|
||||
for (tree node = fns; node; node = TREE_CHAIN (node))
|
||||
{
|
||||
bool objc = c_dialect_objc () && objc_static_init_needed_p ();
|
||||
tree fn = TREE_PURPOSE (node);
|
||||
|
||||
/* We may have module initialization to emit and/or insert
|
||||
before other intializations. */
|
||||
if (module_initializer_kind () || objc)
|
||||
body = start_objects (initp, priority);
|
||||
// We should never find a pure or constant cdtor.
|
||||
gcc_checking_assert (!(flags_from_decl_or_type (fn)
|
||||
& (ECF_CONST | ECF_PURE)));
|
||||
|
||||
/* For Objective-C++, we may need to initialize metadata found
|
||||
in this module. This must be done _before_ any other static
|
||||
initializations. */
|
||||
if (objc)
|
||||
objc_generate_static_init_call (NULL_TREE);
|
||||
tree call = cp_build_function_call_nary (fn, tf_warning_or_error,
|
||||
NULL_TREE);
|
||||
finish_expr_stmt (call);
|
||||
}
|
||||
|
||||
/* Call the static storage duration function with appropriate
|
||||
arguments. */
|
||||
tree fndecl;
|
||||
size_t i;
|
||||
FOR_EACH_VEC_SAFE_ELT (ssdf_decls, i, fndecl)
|
||||
{
|
||||
/* Calls to pure or const functions will expand to nothing. */
|
||||
if (! (flags_from_decl_or_type (fndecl) & (ECF_CONST | ECF_PURE)))
|
||||
{
|
||||
if (! body)
|
||||
body = start_objects (initp, priority);
|
||||
|
||||
tree call = cp_build_function_call_nary (fndecl, tf_warning_or_error,
|
||||
build_int_cst (NULL_TREE,
|
||||
initp),
|
||||
build_int_cst (NULL_TREE,
|
||||
priority),
|
||||
NULL_TREE);
|
||||
finish_expr_stmt (call);
|
||||
}
|
||||
}
|
||||
/* Revert what __asan_before_dynamic_init did by calling
|
||||
__asan_after_dynamic_init. */
|
||||
if (initp && (flag_sanitize & SANITIZE_ADDRESS))
|
||||
finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/true));
|
||||
|
||||
/* Close out the function. */
|
||||
if (body)
|
||||
expand_or_defer_fn (finish_objects (initp, priority, body));
|
||||
}
|
||||
|
||||
/* Generate constructor and destructor functions for the priority
|
||||
indicated by N. */
|
||||
|
||||
static int
|
||||
generate_ctor_and_dtor_functions_for_priority (splay_tree_node n, void * data)
|
||||
{
|
||||
location_t *locus = (location_t *) data;
|
||||
int priority = (int) n->key;
|
||||
priority_info pi = (priority_info) n->value;
|
||||
|
||||
/* Generate the functions themselves, but only if they are really
|
||||
needed. */
|
||||
if (pi->initializations_p)
|
||||
generate_ctor_or_dtor_function (/*initp=*/true, priority, locus);
|
||||
if (pi->destructions_p)
|
||||
generate_ctor_or_dtor_function (/*initp=*/false, priority, locus);
|
||||
|
||||
/* Keep iterating. */
|
||||
return 0;
|
||||
expand_or_defer_fn (finish_objects (initp, priority, body));
|
||||
}
|
||||
|
||||
/* Return C++ property of T, based on given operation OP. */
|
||||
@ -5124,44 +5016,29 @@ c_parse_final_cleanups (void)
|
||||
/* Make sure the back end knows about all the variables. */
|
||||
write_out_vars (vars);
|
||||
|
||||
/* We need to start a new initialization function each time
|
||||
through the loop. That's because we need to know which
|
||||
vtables have been referenced, and TREE_SYMBOL_REFERENCED
|
||||
isn't computed until a function is finished, and written
|
||||
out. That's a deficiency in the back end. When this is
|
||||
fixed, these initialization functions could all become
|
||||
inline, with resulting performance improvements. */
|
||||
function_depth++; // Disable GC
|
||||
priority_map_t *parts[2] = {nullptr, nullptr};
|
||||
partition_vars_for_init_fini (vars, parts);
|
||||
|
||||
/* Set the line and file, so that it is obviously not from
|
||||
the source file. */
|
||||
input_location = locus_at_end_of_parsing;
|
||||
tree ssdf_body = start_static_storage_duration_function (ssdf_count);
|
||||
|
||||
/* First generate code to do all the initializations. */
|
||||
do_static_initialization_or_destruction (/*initp=*/true, vars);
|
||||
|
||||
/* Then, generate code to do all the destructions. Do these
|
||||
in reverse order so that the most recently constructed
|
||||
variable is the first destroyed. If we're using
|
||||
__cxa_atexit, then we don't need to do this; functions
|
||||
were registered at initialization time to destroy the
|
||||
local statics. */
|
||||
if (!flag_use_cxa_atexit)
|
||||
{
|
||||
vars = nreverse (vars);
|
||||
do_static_initialization_or_destruction (/*initp=*/false, vars);
|
||||
}
|
||||
|
||||
/* Finish up the static storage duration function for this
|
||||
round. */
|
||||
input_location = locus_at_end_of_parsing;
|
||||
finish_static_storage_duration_function (ssdf_body);
|
||||
for (unsigned initp = 2; initp--;)
|
||||
if (parts[initp])
|
||||
for (auto iter : *parts[initp])
|
||||
{
|
||||
auto list = iter.second;
|
||||
if (initp)
|
||||
// Partitioning kept the vars in reverse order.
|
||||
// We only want that for dtors.
|
||||
list = nreverse (list);
|
||||
emit_partial_init_fini_fn (initp, iter.first, list,
|
||||
ssdf_count++,
|
||||
locus_at_end_of_parsing);
|
||||
}
|
||||
function_depth--; // Re-enable GC
|
||||
|
||||
/* All those initializations and finalizations might cause
|
||||
us to need more inline functions, more template
|
||||
instantiations, etc. */
|
||||
reconsider = true;
|
||||
ssdf_count++;
|
||||
}
|
||||
|
||||
/* Now do the same for thread_local variables. */
|
||||
@ -5327,22 +5204,33 @@ c_parse_final_cleanups (void)
|
||||
/* We give C linkage to static constructors and destructors. */
|
||||
push_lang_context (lang_name_c);
|
||||
|
||||
if ((c_dialect_objc () && objc_static_init_needed_p ())
|
||||
|| module_initializer_kind ())
|
||||
{
|
||||
// Make sure there's a default priority entry.
|
||||
if (!static_init_fini_fns[true])
|
||||
static_init_fini_fns[true] = priority_map_t::create_ggc ();
|
||||
static_init_fini_fns[true]->get_or_insert (DEFAULT_INIT_PRIORITY);
|
||||
}
|
||||
|
||||
/* Generate initialization and destruction functions for all
|
||||
priorities for which they are required. */
|
||||
if (priority_info_map)
|
||||
splay_tree_foreach (priority_info_map,
|
||||
generate_ctor_and_dtor_functions_for_priority,
|
||||
/*data=*/&locus_at_end_of_parsing);
|
||||
else if ((c_dialect_objc () && objc_static_init_needed_p ())
|
||||
|| module_initializer_kind ())
|
||||
generate_ctor_or_dtor_function (/*constructor_p=*/true,
|
||||
DEFAULT_INIT_PRIORITY,
|
||||
&locus_at_end_of_parsing);
|
||||
|
||||
/* We're done with the splay-tree now. */
|
||||
if (priority_info_map)
|
||||
splay_tree_delete (priority_info_map);
|
||||
|
||||
for (unsigned initp = 2; initp--;)
|
||||
if (static_init_fini_fns[initp])
|
||||
{
|
||||
for (auto iter : *static_init_fini_fns[initp])
|
||||
{
|
||||
tree fns = iter.second;
|
||||
// The list of functions was constructed in reverse
|
||||
// order, which we only want for dtors.
|
||||
if (initp)
|
||||
fns = nreverse (fns);
|
||||
generate_ctor_or_dtor_function (initp, iter.first, fns,
|
||||
locus_at_end_of_parsing);
|
||||
}
|
||||
static_init_fini_fns[initp] = nullptr;
|
||||
}
|
||||
|
||||
fini_modules ();
|
||||
|
||||
/* Generate any missing aliases. */
|
||||
|
17
gcc/testsuite/g++.dg/init/static-cdtor1.C
Normal file
17
gcc/testsuite/g++.dg/init/static-cdtor1.C
Normal file
@ -0,0 +1,17 @@
|
||||
// { dg-do compile { target { lp64 && { i?86-*-linux* x86_64-*-linux* } } } }
|
||||
// { dg-additional-options -fno-use-cxa-atexit }
|
||||
// Make sure we emit initializers in the correct order.
|
||||
|
||||
// ctors
|
||||
// { dg-final { scan-assembler {_Z41__static_initialization_and_destruction_0v:.*movl \$var1[^\n]*\n[^\n]*_ZN5LeelaC1Ev[^\n]*\n[^\n]*movl \$var2[^\n]*\n[^\n]*_ZN5LeelaC1Ev[^\n]*\n[^\n]*movl \$var3[^\n]*\n[^\n]*_ZN5LeelaC1Ev} } }
|
||||
// dtors
|
||||
// { dg-final { scan-assembler {_Z41__static_initialization_and_destruction_1v:.*movl \$var3[^\n]*\n[^\n]*_ZN5LeelaD1Ev[^\n]*\n[^\n]*movl \$var2[^\n]*\n[^\n]*_ZN5LeelaD1Ev[^\n]*\n[^\n]*movl \$var1[^\n]*\n[^\n]*_ZN5LeelaD1Ev} } }
|
||||
|
||||
struct Leela {
|
||||
Leela ();
|
||||
~Leela ();
|
||||
};
|
||||
|
||||
Leela var1;
|
||||
Leela var2;
|
||||
Leela var3;
|
Loading…
Reference in New Issue
Block a user