From 25ebb82a64b527e6f46ad31e3215a5b92504d61d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 12 Jan 2004 13:38:04 -0800 Subject: [PATCH] re PR rtl-optimization/10776 (Large aggregate initializers with a single relocated entry causes excessive compile time regression) PR opt/10776 * typeck2.c (split_nonconstant_init_1, split_nonconstant_init): New. (store_init_value): Use it. * decl.c (check_initializer): Expect full initialization code from store_init_value. * init.c (expand_aggr_init_1): Likewise. * decl2.c (maybe_emit_vtables): Abort if runtime init needed. From-SVN: r75763 --- gcc/cp/ChangeLog | 10 +++++ gcc/cp/decl.c | 10 +++-- gcc/cp/decl2.c | 6 ++- gcc/cp/init.c | 5 ++- gcc/cp/typeck2.c | 111 ++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 131 insertions(+), 11 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 60e7192af11..941176d855b 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,13 @@ +2004-01-12 Richard Henderson + + PR opt/10776 + * typeck2.c (split_nonconstant_init_1, split_nonconstant_init): New. + (store_init_value): Use it. + * decl.c (check_initializer): Expect full initialization code + from store_init_value. + * init.c (expand_aggr_init_1): Likewise. + * decl2.c (maybe_emit_vtables): Abort if runtime init needed. + 2004-01-12 Mark Mitchell * class.c (layout_class_type): For non-POD class types, also copy diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 3ce6d0a4b6c..9a830698bb6 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -4395,6 +4395,7 @@ static tree check_initializer (tree decl, tree init, int flags, tree *cleanup) { tree type = TREE_TYPE (decl); + tree init_code = NULL; /* If `start_decl' didn't like having an initialization, ignore it now. */ if (init != NULL_TREE && DECL_INITIAL (decl) == NULL_TREE) @@ -4511,7 +4512,10 @@ check_initializer (tree decl, tree init, int flags, tree *cleanup) { dont_use_constructor: if (TREE_CODE (init) != TREE_VEC) - init = store_init_value (decl, init); + { + init_code = store_init_value (decl, init); + init = NULL; + } } } else if (DECL_EXTERNAL (decl)) @@ -4534,9 +4538,9 @@ check_initializer (tree decl, tree init, int flags, tree *cleanup) check_for_uninitialized_const_var (decl); if (init && init != error_mark_node) - init = build (INIT_EXPR, type, decl, init); + init_code = build (INIT_EXPR, type, decl, init); - return init; + return init_code; } /* If DECL is not a local variable, give it RTL. */ diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 4068826a9a9..ef8ea6e310c 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -1605,7 +1605,11 @@ maybe_emit_vtables (tree ctype) cgraph_varpool_mark_needed_node (cgraph_varpool_node (vtbl)); if (TREE_TYPE (DECL_INITIAL (vtbl)) == 0) - store_init_value (vtbl, DECL_INITIAL (vtbl)); + { + /* It had better be all done at compile-time. */ + if (store_init_value (vtbl, DECL_INITIAL (vtbl))) + abort (); + } if (write_symbols == DWARF_DEBUG || write_symbols == DWARF2_DEBUG) { diff --git a/gcc/cp/init.c b/gcc/cp/init.c index f717fb23507..1dc5d9d145f 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -1272,8 +1272,9 @@ expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags) /* If store_init_value returns NULL_TREE, the INIT has been record in the DECL_INITIAL for EXP. That means there's nothing more we have to do. */ - if (store_init_value (exp, init)) - finish_expr_stmt (build (INIT_EXPR, type, exp, init)); + init = store_init_value (exp, init); + if (init) + finish_expr_stmt (init); return; } diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index f839dddb300..bad9b1e0ac2 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -284,6 +284,108 @@ cxx_incomplete_type_error (tree value, tree type) } +/* The recursive part of split_nonconstant_init. DEST is an lvalue + expression to which INIT should be assigned. INIT is a CONSTRUCTOR. + PCODE is a pointer to the tail of a chain of statements being emitted. + The return value is the new tail of that chain after new statements + are generated. */ + +static tree * +split_nonconstant_init_1 (tree dest, tree init, tree *pcode) +{ + tree *pelt, elt, type = TREE_TYPE (dest); + tree sub, code, inner_type = NULL; + bool array_type_p = false; + + pelt = &CONSTRUCTOR_ELTS (init); + switch (TREE_CODE (type)) + { + case ARRAY_TYPE: + inner_type = TREE_TYPE (type); + array_type_p = true; + /* FALLTHRU */ + + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + while ((elt = *pelt)) + { + tree field_index = TREE_PURPOSE (elt); + tree value = TREE_VALUE (elt); + + if (!array_type_p) + inner_type = TREE_TYPE (field_index); + + if (TREE_CODE (value) == CONSTRUCTOR) + { + if (array_type_p) + sub = build (ARRAY_REF, inner_type, dest, field_index); + else + sub = build (COMPONENT_REF, inner_type, dest, field_index); + + pcode = split_nonconstant_init_1 (sub, value, pcode); + } + else if (!initializer_constant_valid_p (value, inner_type)) + { + *pelt = TREE_CHAIN (elt); + + if (array_type_p) + sub = build (ARRAY_REF, inner_type, dest, field_index); + else + sub = build (COMPONENT_REF, inner_type, dest, field_index); + + code = build (MODIFY_EXPR, inner_type, sub, value); + code = build_stmt (EXPR_STMT, code); + + *pcode = code; + pcode = &TREE_CHAIN (code); + continue; + } + pelt = &TREE_CHAIN (elt); + } + break; + + case VECTOR_TYPE: + if (!initializer_constant_valid_p (init, type)) + { + CONSTRUCTOR_ELTS (init) = NULL; + code = build (MODIFY_EXPR, type, dest, init); + code = build_stmt (EXPR_STMT, code); + pcode = &TREE_CHAIN (code); + } + break; + + default: + abort (); + } + + return pcode; +} + +/* A subroutine of store_init_value. Splits non-constant static + initializer INIT into a constant part and generates code to + perform the non-constant part of the initialization to DEST. + Returns the code for the runtime init. */ + +static tree +split_nonconstant_init (tree dest, tree init) +{ + tree code; + + if (TREE_CODE (init) == CONSTRUCTOR) + { + code = build_stmt (COMPOUND_STMT, NULL_TREE); + split_nonconstant_init_1 (dest, init, &COMPOUND_BODY (code)); + code = build1 (STMT_EXPR, void_type_node, code); + TREE_SIDE_EFFECTS (code) = 1; + DECL_INITIAL (dest) = init; + } + else + code = build (INIT_EXPR, TREE_TYPE (dest), dest, init); + + return code; +} + /* Perform appropriate conversions on the initial value of a variable, store it in the declaration DECL, and print any error messages that are appropriate. @@ -299,9 +401,8 @@ cxx_incomplete_type_error (tree value, tree type) into a CONSTRUCTOR and use standard initialization techniques. Perhaps a warning should be generated? - Returns value of initializer if initialization could not be - performed for static variable. In that case, caller must do - the storing. */ + Returns code to be executed if initialization could not be performed + for static variable. In that case, caller must emit the code. */ tree store_init_value (tree decl, tree init) @@ -356,11 +457,11 @@ store_init_value (tree decl, tree init) constructing never make it into DECL_INITIAL, and passes 'init' to build_aggr_init without checking DECL_INITIAL. So just return. */ else if (TYPE_NEEDS_CONSTRUCTING (type)) - return value; + return build (INIT_EXPR, type, decl, value); else if (TREE_STATIC (decl) && (! TREE_CONSTANT (value) || ! initializer_constant_valid_p (value, TREE_TYPE (value)))) - return value; + return split_nonconstant_init (decl, value); /* Store the VALUE in DECL_INITIAL. If we're building a statement-tree we will actually expand the initialization later