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.
This commit is contained in:
parent
3da5ae7a34
commit
1e1847612d
4 changed files with 88 additions and 11 deletions
|
@ -2214,6 +2214,22 @@ is_std_construct_at (const constexpr_call *call)
|
|||
&& is_std_construct_at (call->fundef->decl));
|
||||
}
|
||||
|
||||
/* True if CTX is an instance of std::allocator. */
|
||||
|
||||
bool
|
||||
is_std_allocator (tree ctx)
|
||||
{
|
||||
if (ctx == NULL_TREE || !CLASS_TYPE_P (ctx) || !TYPE_MAIN_DECL (ctx))
|
||||
return false;
|
||||
|
||||
tree decl = TYPE_MAIN_DECL (ctx);
|
||||
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
|
||||
|
@ -2224,16 +2240,7 @@ is_std_allocator_allocate (tree fndecl)
|
|||
|| !(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);
|
||||
if (name == NULL_TREE || !id_equal (name, "allocator"))
|
||||
return false;
|
||||
|
||||
return decl_in_std_namespace_p (decl);
|
||||
return is_std_allocator (DECL_CONTEXT (fndecl));
|
||||
}
|
||||
|
||||
/* Overload for the above taking constexpr_call*. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
12
gcc/testsuite/g++.dg/tree-ssa/allocator-opt1.C
Normal file
12
gcc/testsuite/g++.dg/tree-ssa/allocator-opt1.C
Normal file
|
@ -0,0 +1,12 @@
|
|||
// PR c++/105838
|
||||
// { dg-additional-options -fdump-tree-gimple }
|
||||
|
||||
// Check that there's only one allocator (temporary) variable.
|
||||
// Currently the dump doesn't print the allocator template arg in this context.
|
||||
// { dg-final { scan-tree-dump-times "struct allocator D" 1 "gimple" } }
|
||||
|
||||
#include <string>
|
||||
void f (const char *p)
|
||||
{
|
||||
std::string lst[] = { p, p, p, p };
|
||||
}
|
Loading…
Add table
Reference in a new issue