diff options
author | Jason Merrill <jason@redhat.com> | 2022-12-05 15:19:27 -0500 |
---|---|---|
committer | Jason Merrill <jason@redhat.com> | 2022-12-08 13:32:11 -0500 |
commit | 1e1847612d7f169f82c985b0b3a5e3301d6fe999 (patch) | |
tree | 43886abb4a1ab759fb6ebaf3d7472a7819de2c77 /gcc/cp | |
parent | 3da5ae7a347b7d74765053f4a08eaf7ec58f8735 (diff) | |
download | gcc-1e1847612d7f169f82c985b0b3a5e3301d6fe999.zip gcc-1e1847612d7f169f82c985b0b3a5e3301d6fe999.tar.gz gcc-1e1847612d7f169f82c985b0b3a5e3301d6fe999.tar.bz2 |
c++: fewer allocator temps [PR105838]
In this PR, initializing the array of std::string to pass to the vector
initializer_list constructor gets very confusing to the optimizers as the
number of elements increases, primarily because of all the std::allocator
temporaries passed to all the string constructors. Instead of creating one
for each string, let's share an allocator between all the strings; we can do
this safely because we know that std::allocator is stateless and that string
doesn't care about the object identity of its allocator parameter.
PR c++/105838
gcc/cp/ChangeLog:
* cp-tree.h (is_std_allocator): Declare.
* constexpr.cc (is_std_allocator): Split out from...
(is_std_allocator_allocate): ...here.
* init.cc (find_temps_r): New.
(find_allocator_temp): New.
(build_vec_init): Use it.
gcc/testsuite/ChangeLog:
* g++.dg/tree-ssa/allocator-opt1.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/constexpr.cc | 27 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/init.cc | 59 |
3 files changed, 76 insertions, 11 deletions
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 23a27a96..e43d928 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -2214,28 +2214,35 @@ is_std_construct_at (const constexpr_call *call) && is_std_construct_at (call->fundef->decl)); } -/* Return true if FNDECL is std::allocator<T>::{,de}allocate. */ +/* True if CTX is an instance of std::allocator. */ -static inline bool -is_std_allocator_allocate (tree fndecl) +bool +is_std_allocator (tree ctx) { - tree name = DECL_NAME (fndecl); - if (name == NULL_TREE - || !(id_equal (name, "allocate") || id_equal (name, "deallocate"))) - return false; - - tree ctx = DECL_CONTEXT (fndecl); if (ctx == NULL_TREE || !CLASS_TYPE_P (ctx) || !TYPE_MAIN_DECL (ctx)) return false; tree decl = TYPE_MAIN_DECL (ctx); - name = DECL_NAME (decl); + tree name = DECL_NAME (decl); if (name == NULL_TREE || !id_equal (name, "allocator")) return false; return decl_in_std_namespace_p (decl); } +/* Return true if FNDECL is std::allocator<T>::{,de}allocate. */ + +static inline bool +is_std_allocator_allocate (tree fndecl) +{ + tree name = DECL_NAME (fndecl); + if (name == NULL_TREE + || !(id_equal (name, "allocate") || id_equal (name, "deallocate"))) + return false; + + return is_std_allocator (DECL_CONTEXT (fndecl)); +} + /* Overload for the above taking constexpr_call*. */ static inline bool diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index addd26e..581ac2b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8472,6 +8472,7 @@ extern bool is_rvalue_constant_expression (tree); extern bool is_nondependent_constant_expression (tree); extern bool is_nondependent_static_init_expression (tree); extern bool is_static_init_expression (tree); +extern bool is_std_allocator (tree); extern bool potential_rvalue_constant_expression (tree); extern bool require_potential_constant_expression (tree); extern bool require_constant_expression (tree); diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index 2fff4ad..428fac5 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -4308,6 +4308,51 @@ finish_length_check (tree atype, tree iterator, tree obase, unsigned n) } } +/* walk_tree callback to collect temporaries in an expression. */ + +tree +find_temps_r (tree *tp, int *walk_subtrees, void *data) +{ + vec<tree*> &temps = *static_cast<auto_vec<tree*> *>(data); + tree t = *tp; + if (TREE_CODE (t) == TARGET_EXPR + && !TARGET_EXPR_ELIDING_P (t)) + temps.safe_push (tp); + else if (TYPE_P (t)) + *walk_subtrees = 0; + + return NULL_TREE; +} + +/* If INIT initializes a standard library class, and involves a temporary + std::allocator<T>, return a pointer to the temp. + + Used by build_vec_init when initializing an array of e.g. strings to reuse + the same temporary allocator for all of the strings. We can do this because + std::allocator has no data and the standard library doesn't care about the + address of allocator objects. + + ??? Add an attribute to allow users to assert the same property for other + classes, i.e. one object of the type is interchangeable with any other? */ + +static tree* +find_allocator_temp (tree init) +{ + if (TREE_CODE (init) == EXPR_STMT) + init = EXPR_STMT_EXPR (init); + if (TREE_CODE (init) == CONVERT_EXPR) + init = TREE_OPERAND (init, 0); + tree type = TREE_TYPE (init); + if (!CLASS_TYPE_P (type) || !decl_in_std_namespace_p (TYPE_NAME (type))) + return NULL; + auto_vec<tree*> temps; + cp_walk_tree_without_duplicates (&init, find_temps_r, &temps); + for (tree *p : temps) + if (is_std_allocator (TREE_TYPE (*p))) + return p; + return NULL; +} + /* `build_vec_init' returns tree structure that performs initialization of a vector of aggregate types. @@ -4589,6 +4634,8 @@ build_vec_init (tree base, tree maxindex, tree init, if (try_const) vec_alloc (const_vec, CONSTRUCTOR_NELTS (init)); + tree alloc_obj = NULL_TREE; + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, field, elt) { tree baseref = build1 (INDIRECT_REF, type, base); @@ -4638,7 +4685,17 @@ build_vec_init (tree base, tree maxindex, tree init, } if (one_init) - finish_expr_stmt (one_init); + { + /* Only create one std::allocator temporary. */ + if (tree *this_alloc = find_allocator_temp (one_init)) + { + if (alloc_obj) + *this_alloc = alloc_obj; + else + alloc_obj = TARGET_EXPR_SLOT (*this_alloc); + } + finish_expr_stmt (one_init); + } one_init = cp_build_unary_op (PREINCREMENT_EXPR, base, false, complain); |