aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-structalias.c
diff options
context:
space:
mode:
authorThomas Koenig <tkoenig@gcc.gnu.org>2021-09-13 19:49:49 +0200
committerThomas Koenig <tkoenig@gcc.gnu.org>2021-09-13 19:49:49 +0200
commitb18a97e5dd0935e1c4a626c230f21457d0aad3d5 (patch)
treec1818f41af6fe780deafb6cd6a183f32085fe654 /gcc/tree-ssa-structalias.c
parente76a53644c9d70e998c0d050e9a456af388c6b61 (diff)
downloadgcc-b18a97e5dd0935e1c4a626c230f21457d0aad3d5.zip
gcc-b18a97e5dd0935e1c4a626c230f21457d0aad3d5.tar.gz
gcc-b18a97e5dd0935e1c4a626c230f21457d0aad3d5.tar.bz2
Merged current trunk to branch.
Diffstat (limited to 'gcc/tree-ssa-structalias.c')
-rw-r--r--gcc/tree-ssa-structalias.c135
1 files changed, 98 insertions, 37 deletions
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index cf653be..0b8a81f 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -1,5 +1,5 @@
/* Tree based points-to analysis
- Copyright (C) 2005-2020 Free Software Foundation, Inc.
+ Copyright (C) 2005-2021 Free Software Foundation, Inc.
Contributed by Daniel Berlin <dberlin@dberlin.org>
This file is part of GCC.
@@ -43,6 +43,7 @@
#include "attribs.h"
#include "tree-ssa.h"
#include "tree-cfg.h"
+#include "gimple-range.h"
/* The idea behind this analyzer is to generate set constraints from the
program, then solve the resulting constraints in order to generate the
@@ -280,6 +281,9 @@ struct variable_info
/* True if this represents a IPA function info. */
unsigned int is_fn_info : 1;
+ /* True if this appears as RHS in a ADDRESSOF constraint. */
+ unsigned int address_taken : 1;
+
/* ??? Store somewhere better. */
unsigned short ruid;
@@ -393,6 +397,7 @@ new_var_info (tree t, const char *name, bool add_id)
ret->is_global_var = (t == NULL_TREE);
ret->is_ipa_escape_point = false;
ret->is_fn_info = false;
+ ret->address_taken = false;
if (t && DECL_P (t))
ret->is_global_var = (is_global_var (t)
/* We have to treat even local register variables
@@ -674,7 +679,10 @@ dump_constraint (FILE *file, constraint_t c)
fprintf (file, "&");
else if (c->lhs.type == DEREF)
fprintf (file, "*");
- fprintf (file, "%s", get_varinfo (c->lhs.var)->name);
+ if (dump_file)
+ fprintf (file, "%s", get_varinfo (c->lhs.var)->name);
+ else
+ fprintf (file, "V%d", c->lhs.var);
if (c->lhs.offset == UNKNOWN_OFFSET)
fprintf (file, " + UNKNOWN");
else if (c->lhs.offset != 0)
@@ -684,7 +692,10 @@ dump_constraint (FILE *file, constraint_t c)
fprintf (file, "&");
else if (c->rhs.type == DEREF)
fprintf (file, "*");
- fprintf (file, "%s", get_varinfo (c->rhs.var)->name);
+ if (dump_file)
+ fprintf (file, "%s", get_varinfo (c->rhs.var)->name);
+ else
+ fprintf (file, "V%d", c->rhs.var);
if (c->rhs.offset == UNKNOWN_OFFSET)
fprintf (file, " + UNKNOWN");
else if (c->rhs.offset != 0)
@@ -1185,6 +1196,22 @@ add_graph_edge (constraint_graph_t graph, unsigned int to,
if (!graph->succs[from])
graph->succs[from] = BITMAP_ALLOC (&pta_obstack);
+
+ /* The graph solving process does not avoid "triangles", thus
+ there can be multiple paths from a node to another involving
+ intermediate other nodes. That causes extra copying which is
+ most difficult to avoid when the intermediate node is ESCAPED
+ because there are no edges added from ESCAPED. Avoid
+ adding the direct edge FROM -> TO when we have FROM -> ESCAPED
+ and TO contains ESCAPED.
+ ??? Note this is only a heuristic, it does not prevent the
+ situation from occuring. The heuristic helps PR38474 and
+ PR99912 significantly. */
+ if (to < FIRST_REF_NODE
+ && bitmap_bit_p (graph->succs[from], find (escaped_id))
+ && bitmap_bit_p (get_varinfo (find (to))->solution, escaped_id))
+ return false;
+
if (bitmap_set_bit (graph->succs[from], to))
{
r = true;
@@ -3101,6 +3128,8 @@ process_constraint (constraint_t t)
else
{
gcc_assert (rhs.type != ADDRESSOF || rhs.offset == 0);
+ if (rhs.type == ADDRESSOF)
+ get_varinfo (get_varinfo (rhs.var)->head)->address_taken = true;
constraints.safe_push (t);
}
}
@@ -3684,8 +3713,8 @@ get_constraint_for_rhs (tree t, vec<ce_s> *results)
entries in *LHSC. */
static void
-process_all_all_constraints (vec<ce_s> lhsc,
- vec<ce_s> rhsc)
+process_all_all_constraints (const vec<ce_s> &lhsc,
+ const vec<ce_s> &rhsc)
{
struct constraint_expr *lhsp, *rhsp;
unsigned i, j;
@@ -3785,7 +3814,7 @@ do_structure_copy (tree lhsop, tree rhsop)
/* Create constraints ID = { rhsc }. */
static void
-make_constraints_to (unsigned id, vec<ce_s> rhsc)
+make_constraints_to (unsigned id, const vec<ce_s> &rhsc)
{
struct constraint_expr *c;
struct constraint_expr includes;
@@ -4034,8 +4063,14 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
tree arg = gimple_call_arg (stmt, i);
int flags = gimple_call_arg_flags (stmt, i);
- /* If the argument is not used we can ignore it. */
- if (flags & EAF_UNUSED)
+ /* If the argument is not used we can ignore it.
+ Similarly argument is invisile for us if it not clobbered, does not
+ escape, is not read and can not be returned. */
+ if ((flags & EAF_UNUSED)
+ || ((flags & (EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NOREAD
+ | EAF_NOT_RETURNED))
+ == (EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NOREAD
+ | EAF_NOT_RETURNED)))
continue;
/* As we compute ESCAPED context-insensitive we do not gain
@@ -4053,9 +4088,12 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
if (!(flags & EAF_DIRECT))
make_transitive_closure_constraints (tem);
make_copy_constraint (uses, tem->id);
+ /* TODO: This is overly conservative when some parameters are
+ returned while others are not. */
+ if (!(flags & EAF_NOT_RETURNED))
+ returns_uses = true;
if (!(flags & (EAF_NOESCAPE | EAF_DIRECT)))
make_indirect_escape_constraint (tem);
- returns_uses = true;
}
else if (flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE))
{
@@ -4069,6 +4107,8 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
if (!(flags & EAF_DIRECT))
make_transitive_closure_constraints (tem);
make_copy_constraint (uses, tem->id);
+ if (!(flags & EAF_NOT_RETURNED))
+ returns_uses = true;
make_copy_constraint (clobbers, tem->id);
/* Add *tem = nonlocal, do not add *tem = callused as
EAF_NOESCAPE parameters do not escape to other parameters
@@ -4082,7 +4122,6 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
process_constraint (new_constraint (lhs, rhs));
if (!(flags & (EAF_NOESCAPE | EAF_DIRECT)))
make_indirect_escape_constraint (tem);
- returns_uses = true;
}
else
make_escape_constraint (arg);
@@ -4129,7 +4168,7 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
the LHS point to global and escaped variables. */
static void
-handle_lhs_call (gcall *stmt, tree lhs, int flags, vec<ce_s> rhsc,
+handle_lhs_call (gcall *stmt, tree lhs, int flags, vec<ce_s> &rhsc,
tree fndecl)
{
auto_vec<ce_s> lhsc;
@@ -4232,13 +4271,18 @@ handle_const_call (gcall *stmt, vec<ce_s> *results)
/* May return offsetted arguments. */
varinfo_t tem = NULL;
- if (gimple_call_num_args (stmt) != 0)
- {
- tem = new_var_info (NULL_TREE, "callarg", true);
- tem->is_reg_var = true;
- }
for (k = 0; k < gimple_call_num_args (stmt); ++k)
{
+ int flags = gimple_call_arg_flags (stmt, k);
+
+ /* If the argument is not used or not returned we can ignore it. */
+ if (flags & (EAF_UNUSED | EAF_NOT_RETURNED))
+ continue;
+ if (!tem)
+ {
+ tem = new_var_info (NULL_TREE, "callarg", true);
+ tem->is_reg_var = true;
+ }
tree arg = gimple_call_arg (stmt, k);
auto_vec<ce_s> argc;
get_constraint_for_rhs (arg, &argc);
@@ -4269,6 +4313,7 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
struct constraint_expr rhsc;
unsigned i;
varinfo_t uses = NULL;
+ bool record_uses = false;
/* Memory reached from pointer arguments is call-used. */
for (i = 0; i < gimple_call_num_args (stmt); ++i)
@@ -4277,7 +4322,9 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
int flags = gimple_call_arg_flags (stmt, i);
/* If the argument is not used we can ignore it. */
- if (flags & EAF_UNUSED)
+ if ((flags & EAF_UNUSED)
+ || (flags & (EAF_NOT_RETURNED | EAF_NOREAD))
+ == (EAF_NOT_RETURNED | EAF_NOREAD))
continue;
if (!uses)
{
@@ -4286,6 +4333,8 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
make_transitive_closure_constraints (uses);
}
make_constraint_to (uses->id, arg);
+ if (!(flags & EAF_NOT_RETURNED))
+ record_uses = true;
}
/* The static chain is used as well. */
@@ -4298,6 +4347,7 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
make_transitive_closure_constraints (uses);
}
make_constraint_to (uses->id, gimple_call_chain (stmt));
+ record_uses = true;
}
/* And if we applied NRV the address of the return slot. */
@@ -4314,10 +4364,11 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
auto_vec<ce_s> tmpc;
get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc);
make_constraints_to (uses->id, tmpc);
+ record_uses = true;
}
/* Pure functions may return call-used and nonlocal memory. */
- if (uses)
+ if (record_uses)
{
rhsc.var = uses->id;
rhsc.offset = 0;
@@ -4580,9 +4631,10 @@ find_func_aliases_for_builtin_call (struct function *fn, gcall *t)
case BUILT_IN_REALLOC:
if (gimple_call_lhs (t))
{
+ auto_vec<ce_s> rhsc;
handle_lhs_call (t, gimple_call_lhs (t),
gimple_call_return_flags (t) | ERF_NOALIAS,
- vNULL, fndecl);
+ rhsc, fndecl);
get_constraint_for_ptr_offset (gimple_call_lhs (t),
NULL_TREE, &lhsc);
get_constraint_for_ptr_offset (gimple_call_arg (t, 0),
@@ -4864,6 +4916,9 @@ find_func_aliases_for_call (struct function *fn, gcall *t)
&& find_func_aliases_for_builtin_call (fn, t))
return;
+ if (gimple_call_internal_p (t, IFN_DEFERRED_INIT))
+ return;
+
fi = get_fi_for_callee (t);
if (!in_ipa_mode
|| (fi->decl && fndecl && !fi->is_fn_info))
@@ -5653,7 +5708,7 @@ fieldoff_compare (const void *pa, const void *pb)
/* Sort a fieldstack according to the field offset and sizes. */
static void
-sort_fieldstack (vec<fieldoff_s> fieldstack)
+sort_fieldstack (vec<fieldoff_s> &fieldstack)
{
fieldstack.qsort (fieldoff_compare);
}
@@ -6063,7 +6118,7 @@ create_function_info_for (tree decl, const char *name, bool add_id,
FIELDSTACK is assumed to be sorted by offset. */
static bool
-check_for_overlaps (vec<fieldoff_s> fieldstack)
+check_for_overlaps (const vec<fieldoff_s> &fieldstack)
{
fieldoff_s *fo = NULL;
unsigned int i;
@@ -6712,7 +6767,9 @@ find_what_p_points_to (tree fndecl, tree p)
struct ptr_info_def *pi;
tree lookup_p = p;
varinfo_t vi;
- bool nonnull = get_ptr_nonnull (p);
+ value_range vr;
+ get_range_query (DECL_STRUCT_FUNCTION (fndecl))->range_of_expr (vr, p);
+ bool nonnull = vr.nonzero_p ();
/* For parameters, get at the points-to set for the actual parm
decl. */
@@ -6730,8 +6787,7 @@ find_what_p_points_to (tree fndecl, tree p)
pi->pt = find_what_var_points_to (fndecl, vi);
/* Conservatively set to NULL from PTA (to true). */
pi->pt.null = 1;
- /* Preserve pointer nonnull computed by VRP. See get_ptr_nonnull
- in gcc/tree-ssaname.c for more information. */
+ /* Preserve pointer nonnull globally computed. */
if (nonnull)
set_ptr_nonnull (p);
}
@@ -7288,15 +7344,14 @@ solve_constraints (void)
unsigned int *map = XNEWVEC (unsigned int, varmap.length ());
for (unsigned i = 0; i < integer_id + 1; ++i)
map[i] = i;
- /* Start with non-register vars (as possibly address-taken), followed
- by register vars as conservative set of vars never appearing in
- the points-to solution bitmaps. */
+ /* Start with address-taken vars, followed by not address-taken vars
+ to move vars never appearing in the points-to solution bitmaps last. */
unsigned j = integer_id + 1;
for (unsigned i = integer_id + 1; i < varmap.length (); ++i)
- if (! varmap[i]->is_reg_var)
+ if (varmap[varmap[i]->head]->address_taken)
map[i] = j++;
for (unsigned i = integer_id + 1; i < varmap.length (); ++i)
- if (varmap[i]->is_reg_var)
+ if (! varmap[varmap[i]->head]->address_taken)
map[i] = j++;
/* Shuffle varmap according to map. */
for (unsigned i = integer_id + 1; i < varmap.length (); ++i)
@@ -8168,10 +8223,12 @@ ipa_pta_execute (void)
FOR_EACH_DEFINED_FUNCTION (node)
{
varinfo_t vi;
- /* Nodes without a body are not interesting. Especially do not
- visit clones at this point for now - we get duplicate decls
- there for inline clones at least. */
- if (!node->has_gimple_body_p () || node->inlined_to)
+ /* Nodes without a body in this partition are not interesting.
+ Especially do not visit clones at this point for now - we
+ get duplicate decls there for inline clones at least. */
+ if (!node->has_gimple_body_p ()
+ || node->in_other_partition
+ || node->inlined_to)
continue;
node->get_body ();
@@ -8249,8 +8306,10 @@ ipa_pta_execute (void)
struct function *func;
basic_block bb;
- /* Nodes without a body are not interesting. */
- if (!node->has_gimple_body_p () || node->clone_of)
+ /* Nodes without a body in this partition are not interesting. */
+ if (!node->has_gimple_body_p ()
+ || node->in_other_partition
+ || node->clone_of)
continue;
if (dump_file)
@@ -8379,8 +8438,10 @@ ipa_pta_execute (void)
unsigned i;
basic_block bb;
- /* Nodes without a body are not interesting. */
- if (!node->has_gimple_body_p () || node->clone_of)
+ /* Nodes without a body in this partition are not interesting. */
+ if (!node->has_gimple_body_p ()
+ || node->in_other_partition
+ || node->clone_of)
continue;
fn = DECL_STRUCT_FUNCTION (node->decl);