summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ipa-modref-tree.h32
-rw-r--r--gcc/ipa-modref.c39
-rw-r--r--gcc/ipa-modref.h13
-rw-r--r--gcc/testsuite/gcc.dg/torture/ssa-pta-fn-1.c8
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/pta-callused.c2
-rw-r--r--gcc/tree-ssa-structalias.c518
6 files changed, 317 insertions, 295 deletions
diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h
index 8e9b89b3e2c..52f225b1aae 100644
--- a/gcc/ipa-modref-tree.h
+++ b/gcc/ipa-modref-tree.h
@@ -148,7 +148,8 @@ struct GTY(()) modref_access_node
poly_int64 offset1, poly_int64 size1, poly_int64 max_size1,
bool record_adjustments)
{
- if (known_eq (offset, offset1)
+ if (known_eq (parm_offset, parm_offset1)
+ && known_eq (offset, offset1)
&& known_eq (size, size1)
&& known_eq (max_size, max_size1))
return;
@@ -577,6 +578,10 @@ struct GTY((user)) modref_ref_node
}
(*accesses)[best1].forced_merge (best2 < 0 ? a : (*accesses)[best2],
record_adjustments);
+ /* CHeck that merging indeed merged ranges. */
+ gcc_checking_assert ((*accesses)[best1].contains (best2 < 0 ? a : (*accesses)[best2]));
+ /*if (best2 >= 0)
+ accesses->unordered_remove (best2);*/
if (!(*accesses)[best1].useful_p ())
{
collapse ();
@@ -1012,6 +1017,31 @@ struct GTY((user)) modref_tree
return NULL;
}
+ /* Return true if tree contains access to global memory. */
+ bool global_access_p ()
+ {
+ size_t i, j, k;
+ modref_base_node <T> *base_node;
+ modref_ref_node <T> *ref_node;
+ modref_access_node *access_node;
+ if (every_base)
+ return true;
+ FOR_EACH_VEC_SAFE_ELT (bases, i, base_node)
+ {
+ if (base_node->every_ref)
+ return true;
+ FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
+ {
+ if (ref_node->every_access)
+ return true;
+ FOR_EACH_VEC_SAFE_ELT (ref_node->accesses, k, access_node)
+ if (access_node->parm_index < 0)
+ return true;
+ }
+ }
+ return false;
+ }
+
/* Return ggc allocated instance. We explicitly call destructors via
ggc_delete and do not want finalizers to be registered and
called at the garbage collection time. */
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 6d49cc1410e..0bbec8df0a2 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -85,6 +85,7 @@ along with GCC; see the file COPYING3. If not see
#include "ssa-iterators.h"
#include "stringpool.h"
#include "tree-ssanames.h"
+#include "attribs.h"
namespace {
@@ -280,17 +281,6 @@ modref_summary::~modref_summary ()
ggc_delete (stores);
}
-/* All flags that are implied by the ECF_CONST functions. */
-const int implicit_const_eaf_flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE
- | EAF_NODIRECTESCAPE | EAF_NOREAD;
-/* All flags that are implied by the ECF_PURE function. */
-const int implicit_pure_eaf_flags = EAF_NOCLOBBER | EAF_NOESCAPE
- | EAF_NODIRECTESCAPE;
-/* All flags implied when we know we can ignore stores (i.e. when handling
- call to noreturn). */
-const int ignore_stores_eaf_flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE
- | EAF_NODIRECTESCAPE;
-
/* Remove all flags from EAF_FLAGS that are implied by ECF_FLAGS and not
useful to track. If returns_void is true moreover clear
EAF_NOT_RETURNED. */
@@ -305,10 +295,6 @@ remove_useless_eaf_flags (int eaf_flags, int ecf_flags, bool returns_void)
eaf_flags &= ~implicit_pure_eaf_flags;
else if ((ecf_flags & ECF_NORETURN) || returns_void)
eaf_flags &= ~EAF_NOT_RETURNED;
- /* Only NOCLOBBER or DIRECT flags alone are not useful (see comments
- in tree-ssa-alias.c). Give up earlier. */
- if ((eaf_flags & ~(EAF_DIRECT | EAF_NOCLOBBER)) == 0)
- return 0;
return eaf_flags;
}
@@ -345,6 +331,26 @@ modref_summary::useful_p (int ecf_flags, bool check_flags)
return stores && !stores->every_base;
}
+/* Return true if global memory is read
+ (that is loads summary contains global memory access). */
+bool
+modref_summary::global_memory_read_p ()
+{
+ if (!loads)
+ return true;
+ return loads->global_access_p ();
+}
+
+/* Return true if global memory is written. */
+bool
+modref_summary::global_memory_written_p ()
+{
+ if (!stores)
+ return true;
+ return stores->global_access_p ();
+}
+
+
/* Single function summary used for LTO. */
typedef modref_tree <tree> modref_records_lto;
@@ -2016,7 +2022,8 @@ analyze_function (function *f, bool ipa)
DECL_PURE_P (current_function_decl) ? " (pure)" : "");
/* Don't analyze this function if it's compiled with -fno-strict-aliasing. */
- if (!flag_ipa_modref)
+ if (!flag_ipa_modref
+ || lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl)))
return;
/* Compute no-LTO summaries when local optimization is going to happen. */
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index 540fdea8efa..5afa3aa439f 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -37,10 +37,23 @@ struct GTY(()) modref_summary
~modref_summary ();
void dump (FILE *);
bool useful_p (int ecf_flags, bool check_flags = true);
+ bool global_memory_read_p ();
+ bool global_memory_written_p ();
};
modref_summary *get_modref_function_summary (cgraph_node *func);
void ipa_modref_c_finalize ();
void ipa_merge_modref_summary_after_inlining (cgraph_edge *e);
+/* All flags that are implied by the ECF_CONST functions. */
+static const int implicit_const_eaf_flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE
+ | EAF_NODIRECTESCAPE | EAF_NOREAD;
+/* All flags that are implied by the ECF_PURE function. */
+static const int implicit_pure_eaf_flags = EAF_NOCLOBBER | EAF_NOESCAPE
+ | EAF_NODIRECTESCAPE;
+/* All flags implied when we know we can ignore stores (i.e. when handling
+ call to noreturn). */
+static const int ignore_stores_eaf_flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE
+ | EAF_NODIRECTESCAPE;
+
#endif
diff --git a/gcc/testsuite/gcc.dg/torture/ssa-pta-fn-1.c b/gcc/testsuite/gcc.dg/torture/ssa-pta-fn-1.c
index 1f30467b98e..de019a7ecaf 100644
--- a/gcc/testsuite/gcc.dg/torture/ssa-pta-fn-1.c
+++ b/gcc/testsuite/gcc.dg/torture/ssa-pta-fn-1.c
@@ -6,13 +6,13 @@ extern void abort (void);
int *g;
int dummy;
-int * __attribute__((noinline,const))
+int * __attribute__((noinline,const,noipa))
foo_const(int *p) { return p; }
-int * __attribute__((noinline,pure))
+int * __attribute__((noinline,pure,noipa))
foo_pure(int *p) { return p + dummy; }
-int * __attribute__((noinline))
+int * __attribute__((noinline,noipa))
foo_normal(int *p) { g = p; return p; }
void test_const(void)
@@ -58,4 +58,4 @@ int main()
/* { dg-final { scan-tree-dump "q_const_. = { NONLOCAL i }" "alias" } } */
/* { dg-final { scan-tree-dump "q_pure_. = { ESCAPED NONLOCAL i }" "alias" } } */
-/* { dg-final { scan-tree-dump "q_normal_. = { ESCAPED NONLOCAL }" "alias" } } */
+/* { dg-final { scan-tree-dump "q_normal_. = { ESCAPED NONLOCAL i }" "alias" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pta-callused.c b/gcc/testsuite/gcc.dg/tree-ssa/pta-callused.c
index cb85ec18bbc..aa639b45dc2 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pta-callused.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pta-callused.c
@@ -22,5 +22,5 @@ int bar (int b)
return *foo (&q);
}
-/* { dg-final { scan-tree-dump "CALLUSED\\(\[0-9\]+\\) = { f.* i q }" "alias" } } */
+/* { dg-final { scan-tree-dump "CALLUSED\\(\[0-9\]+\\) = { NONLOCAL f.* i q }" "alias" } } */
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index 0b8a81ff113..6f12a66ee0d 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -44,6 +44,9 @@
#include "tree-ssa.h"
#include "tree-cfg.h"
#include "gimple-range.h"
+#include "ipa-modref-tree.h"
+#include "ipa-modref.h"
+#include "attr-fnspec.h"
/* The idea behind this analyzer is to generate set constraints from the
program, then solve the resulting constraints in order to generate the
@@ -4048,98 +4051,203 @@ get_function_part_constraint (varinfo_t fi, unsigned part)
return c;
}
-/* For non-IPA mode, generate constraints necessary for a call on the
- RHS. */
+/* Produce constraints for argument ARG of call STMT with eaf flags
+ FLAGS. RESULTS is array holding constraints for return value.
+ CALLESCAPE_ID is variable where call loocal escapes are added.
+ WRITES_GLOVEL_MEMORY is true if callee may write global memory. */
static void
-handle_rhs_call (gcall *stmt, vec<ce_s> *results)
-{
- struct constraint_expr rhsc;
- unsigned i;
- bool returns_uses = false;
+handle_call_arg (gcall *stmt, tree arg, vec<ce_s> *results, int flags,
+ int callescape_id, bool writes_global_memory)
+{
+ /* 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)))
+ return;
+
+ varinfo_t tem = new_var_info (NULL_TREE, "callarg", true);
+ tem->is_reg_var = true;
+ make_constraint_to (tem->id, arg);
+ make_any_offset_constraints (tem);
+
+ if (!(flags & EAF_DIRECT))
+ make_transitive_closure_constraints (tem);
- for (i = 0; i < gimple_call_num_args (stmt); ++i)
+ if (!(flags & EAF_NOT_RETURNED))
{
- tree arg = gimple_call_arg (stmt, i);
- int flags = gimple_call_arg_flags (stmt, i);
+ struct constraint_expr cexpr;
+ cexpr.var = tem->id;
+ cexpr.type = SCALAR;
+ cexpr.offset = 0;
+ results->safe_push (cexpr);
+ }
- /* 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;
+ if (!(flags & EAF_NOREAD))
+ {
+ varinfo_t uses = get_call_use_vi (stmt);
+ make_copy_constraint (uses, tem->id);
+ }
- /* As we compute ESCAPED context-insensitive we do not gain
- any precision with just EAF_NOCLOBBER but not EAF_NOESCAPE
- set. The argument would still get clobbered through the
- escape solution. */
- if ((flags & EAF_NOCLOBBER)
- && (flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE)))
- {
- varinfo_t uses = get_call_use_vi (stmt);
- varinfo_t tem = new_var_info (NULL_TREE, "callarg", true);
- tem->is_reg_var = true;
- make_constraint_to (tem->id, arg);
- make_any_offset_constraints (tem);
- 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);
- }
- else if (flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE))
+ if (!(flags & EAF_NOCLOBBER))
+ {
+ struct constraint_expr lhs, rhs;
+
+ /* *arg = callescape. */
+ lhs.type = DEREF;
+ lhs.var = tem->id;
+ lhs.offset = 0;
+
+ rhs.type = SCALAR;
+ rhs.var = callescape_id;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+
+ /* callclobbered = arg. */
+ make_copy_constraint (get_call_clobber_vi (stmt), tem->id);
+ }
+
+ if (!(flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE)))
+ {
+ struct constraint_expr lhs, rhs;
+
+ /* callescape = arg; */
+ lhs.var = callescape_id;
+ lhs.offset = 0;
+ lhs.type = SCALAR;
+
+ rhs.var = tem->id;
+ rhs.offset = 0;
+ rhs.type = SCALAR;
+ process_constraint (new_constraint (lhs, rhs));
+
+ if (writes_global_memory)
+ make_escape_constraint (arg);
+ }
+ else if (!(flags & EAF_NOESCAPE))
+ {
+ struct constraint_expr lhs, rhs;
+
+ /* callescape = *(arg + UNKNOWN); */
+ lhs.var = callescape_id;
+ lhs.offset = 0;
+ lhs.type = SCALAR;
+
+ rhs.var = tem->id;
+ rhs.offset = UNKNOWN_OFFSET;
+ rhs.type = DEREF;
+ process_constraint (new_constraint (lhs, rhs));
+
+ if (writes_global_memory)
+ make_indirect_escape_constraint (tem);
+ }
+}
+
+/* Determine global memory access of call STMT and update
+ WRITES_GLOBAL_MEMORY, READS_GLOBAL_MEMORY and USES_GLOBAL_MEMORY. */
+
+static void
+determine_global_memory_access (gcall *stmt,
+ bool *writes_global_memory,
+ bool *reads_global_memory,
+ bool *uses_global_memory)
+{
+ tree callee;
+ cgraph_node *node;
+ modref_summary *summary;
+
+ /* We need to detrmine reads to set uses. */
+ gcc_assert (!uses_global_memory || reads_global_memory);
+
+ if ((callee = gimple_call_fndecl (stmt)) != NULL_TREE
+ && (node = cgraph_node::get (callee)) != NULL
+ && (summary = get_modref_function_summary (node)))
+ {
+ if (writes_global_memory && *writes_global_memory)
+ *writes_global_memory = summary->global_memory_written_p ();
+ if (reads_global_memory && *reads_global_memory)
+ *reads_global_memory = summary->global_memory_read_p ();
+ if (reads_global_memory && uses_global_memory
+ && !*reads_global_memory && node->binds_to_current_def_p ())
+ *uses_global_memory = false;
+ }
+ if ((writes_global_memory && *writes_global_memory)
+ || (uses_global_memory && *uses_global_memory)
+ || (reads_global_memory && *reads_global_memory))
+ {
+ attr_fnspec fnspec = gimple_call_fnspec (stmt);
+ if (fnspec.known_p ())
{
- struct constraint_expr lhs, rhs;
- varinfo_t uses = get_call_use_vi (stmt);
- varinfo_t clobbers = get_call_clobber_vi (stmt);
- varinfo_t tem = new_var_info (NULL_TREE, "callarg", true);
- tem->is_reg_var = true;
- make_constraint_to (tem->id, arg);
- make_any_offset_constraints (tem);
- 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
- and all other uses appear in NONLOCAL as well. */
- lhs.type = DEREF;
- lhs.var = tem->id;
- lhs.offset = 0;
- rhs.type = SCALAR;
- rhs.var = nonlocal_id;
- rhs.offset = 0;
- process_constraint (new_constraint (lhs, rhs));
- if (!(flags & (EAF_NOESCAPE | EAF_DIRECT)))
- make_indirect_escape_constraint (tem);
+ if (writes_global_memory
+ && !fnspec.global_memory_written_p ())
+ *writes_global_memory = false;
+ if (reads_global_memory && !fnspec.global_memory_read_p ())
+ {
+ *reads_global_memory = false;
+ if (uses_global_memory)
+ *uses_global_memory = false;
+ }
}
- else
- make_escape_constraint (arg);
}
+}
+
+/* For non-IPA mode, generate constraints necessary for a call on the
+ RHS and collect return value constraint to RESULTS to be used later in
+ handle_lhs_call.
+
+ IMPLICIT_EAF_FLAGS are added to each function argument. If
+ WRITES_GLOBAL_MEMORY is true function is assumed to possibly write to global
+ memory. Similar for READS_GLOBAL_MEMORY. */
+
+static void
+handle_rhs_call (gcall *stmt, vec<ce_s> *results,
+ int implicit_eaf_flags,
+ bool writes_global_memory,
+ bool reads_global_memory)
+{
+ determine_global_memory_access (stmt, &writes_global_memory,
+ &reads_global_memory,
+ NULL);
+
+ varinfo_t callescape = new_var_info (NULL_TREE, "callescape", true);
+
+ /* If function can use global memory, add it to callescape
+ and to possible return values. If not we can still use/return addresses
+ of global symbols. */
+ struct constraint_expr lhs, rhs;
+
+ lhs.type = SCALAR;
+ lhs.var = callescape->id;
+ lhs.offset = 0;
+
+ rhs.type = reads_global_memory ? SCALAR : ADDRESSOF;
+ rhs.var = nonlocal_id;
+ rhs.offset = 0;
- /* If we added to the calls uses solution make sure we account for
- pointers to it to be returned. */
- if (returns_uses)
+ process_constraint (new_constraint (lhs, rhs));
+ results->safe_push (rhs);
+
+ varinfo_t uses = get_call_use_vi (stmt);
+ make_copy_constraint (uses, callescape->id);
+
+ for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
{
- rhsc.var = get_call_use_vi (stmt)->id;
- rhsc.offset = UNKNOWN_OFFSET;
- rhsc.type = SCALAR;
- results->safe_push (rhsc);
+ tree arg = gimple_call_arg (stmt, i);
+ int flags = gimple_call_arg_flags (stmt, i);
+ handle_call_arg (stmt, arg, results,
+ flags | implicit_eaf_flags,
+ callescape->id, writes_global_memory);
}
/* The static chain escapes as well. */
if (gimple_call_chain (stmt))
- make_escape_constraint (gimple_call_chain (stmt));
+ handle_call_arg (stmt, gimple_call_chain (stmt), results,
+ implicit_eaf_flags,
+ callescape->id, writes_global_memory);
/* And if we applied NRV the address of the return slot escapes as well. */
if (gimple_call_return_slot_opt_p (stmt)
@@ -4147,20 +4255,17 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
&& TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (stmt))))
{
auto_vec<ce_s> tmpc;
- struct constraint_expr lhsc, *c;
+ struct constraint_expr *c;
+ unsigned i;
+
get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc);
- lhsc.var = escaped_id;
- lhsc.offset = 0;
- lhsc.type = SCALAR;
+
+ make_constraints_to (callescape->id, tmpc);
+ if (writes_global_memory)
+ make_constraints_to (escaped_id, tmpc);
FOR_EACH_VEC_ELT (tmpc, i, c)
- process_constraint (new_constraint (lhsc, *c));
+ results->safe_push (*c);
}
-
- /* Regular functions return nonlocal memory. */
- rhsc.var = nonlocal_id;
- rhsc.offset = 0;
- rhsc.type = SCALAR;
- results->safe_push (rhsc);
}
/* For non-IPA mode, generate constraints necessary for a call
@@ -4227,160 +4332,6 @@ handle_lhs_call (gcall *stmt, tree lhs, int flags, vec<ce_s> &rhsc,
process_all_all_constraints (lhsc, rhsc);
}
-/* For non-IPA mode, generate constraints necessary for a call of a
- const function that returns a pointer in the statement STMT. */
-
-static void
-handle_const_call (gcall *stmt, vec<ce_s> *results)
-{
- struct constraint_expr rhsc;
- unsigned int k;
- bool need_uses = false;
-
- /* Treat nested const functions the same as pure functions as far
- as the static chain is concerned. */
- if (gimple_call_chain (stmt))
- {
- varinfo_t uses = get_call_use_vi (stmt);
- make_constraint_to (uses->id, gimple_call_chain (stmt));
- need_uses = true;
- }
-
- /* And if we applied NRV the address of the return slot escapes as well. */
- if (gimple_call_return_slot_opt_p (stmt)
- && gimple_call_lhs (stmt) != NULL_TREE
- && TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (stmt))))
- {
- varinfo_t uses = get_call_use_vi (stmt);
- auto_vec<ce_s> tmpc;
- get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc);
- make_constraints_to (uses->id, tmpc);
- need_uses = true;
- }
-
- if (need_uses)
- {
- varinfo_t uses = get_call_use_vi (stmt);
- make_any_offset_constraints (uses);
- make_transitive_closure_constraints (uses);
- rhsc.var = uses->id;
- rhsc.offset = 0;
- rhsc.type = SCALAR;
- results->safe_push (rhsc);
- }
-
- /* May return offsetted arguments. */
- varinfo_t tem = NULL;
- 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);
- make_constraints_to (tem->id, argc);
- }
- if (tem)
- {
- ce_s ce;
- ce.type = SCALAR;
- ce.var = tem->id;
- ce.offset = UNKNOWN_OFFSET;
- results->safe_push (ce);
- }
-
- /* May return addresses of globals. */
- rhsc.var = nonlocal_id;
- rhsc.offset = 0;
- rhsc.type = ADDRESSOF;
- results->safe_push (rhsc);
-}
-
-/* For non-IPA mode, generate constraints necessary for a call to a
- pure function in statement STMT. */
-
-static void
-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)
- {
- 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)
- || (flags & (EAF_NOT_RETURNED | EAF_NOREAD))
- == (EAF_NOT_RETURNED | EAF_NOREAD))
- continue;
- if (!uses)
- {
- uses = get_call_use_vi (stmt);
- make_any_offset_constraints (uses);
- 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. */
- if (gimple_call_chain (stmt))
- {
- if (!uses)
- {
- uses = get_call_use_vi (stmt);
- make_any_offset_constraints (uses);
- 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. */
- if (gimple_call_return_slot_opt_p (stmt)
- && gimple_call_lhs (stmt) != NULL_TREE
- && TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (stmt))))
- {
- if (!uses)
- {
- uses = get_call_use_vi (stmt);
- make_any_offset_constraints (uses);
- make_transitive_closure_constraints (uses);
- }
- 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 (record_uses)
- {
- rhsc.var = uses->id;
- rhsc.offset = 0;
- rhsc.type = SCALAR;
- results->safe_push (rhsc);
- }
- rhsc.var = nonlocal_id;
- rhsc.offset = 0;
- rhsc.type = SCALAR;
- results->safe_push (rhsc);
-}
-
/* Return the varinfo for the callee of CALL. */
@@ -4931,13 +4882,13 @@ find_func_aliases_for_call (struct function *fn, gcall *t)
if (flags & (ECF_CONST|ECF_NOVOPS))
{
if (gimple_call_lhs (t))
- handle_const_call (t, &rhsc);
+ handle_rhs_call (t, &rhsc, implicit_const_eaf_flags, false, false);
}
/* Pure functions can return addresses in and of memory
reachable from their arguments, but they are not an escape
point for reachable memory of their arguments. */
else if (flags & (ECF_PURE|ECF_LOOPING_CONST_OR_PURE))
- handle_pure_call (t, &rhsc);
+ handle_rhs_call (t, &rhsc, implicit_pure_eaf_flags, true, false);
/* If the call is to a replaceable operator delete and results
from a delete expression as opposed to a direct call to
such operator, then the effects for PTA (in particular
@@ -4947,7 +4898,7 @@ find_func_aliases_for_call (struct function *fn, gcall *t)
&& gimple_call_from_new_or_delete (t))
;
else
- handle_rhs_call (t, &rhsc);
+ handle_rhs_call (t, &rhsc, 0, true, true);
if (gimple_call_lhs (t))
handle_lhs_call (t, gimple_call_lhs (t),
gimple_call_return_flags (t), rhsc, fndecl);
@@ -7582,43 +7533,64 @@ compute_points_to_sets (void)
pt = gimple_call_use_set (stmt);
if (gimple_call_flags (stmt) & ECF_CONST)
memset (pt, 0, sizeof (struct pt_solution));
- else if ((vi = lookup_call_use_vi (stmt)) != NULL)
- {
- *pt = find_what_var_points_to (cfun->decl, vi);
- /* Escaped (and thus nonlocal) variables are always
- implicitly used by calls. */
- /* ??? ESCAPED can be empty even though NONLOCAL
- always escaped. */
- pt->nonlocal = 1;
- pt->escaped = 1;
- }
else
{
- /* If there is nothing special about this call then
- we have made everything that is used also escape. */
- *pt = cfun->gimple_df->escaped;
- pt->nonlocal = 1;
+ bool uses_global_memory = true;
+ bool reads_global_memory = true;
+
+ determine_global_memory_access (stmt, NULL,
+ &reads_global_memory,
+ &uses_global_memory);
+ if (!uses_global_memory)
+ ;
+ else if ((vi = lookup_call_use_vi (stmt)) != NULL)
+ {
+ *pt = find_what_var_points_to (cfun->decl, vi);
+ /* Escaped (and thus nonlocal) variables are always
+ implicitly used by calls. */
+ /* ??? ESCAPED can be empty even though NONLOCAL
+ always escaped. */
+ pt->nonlocal = uses_global_memory;
+ pt->escaped = uses_global_memory;
+ }
+ else if (uses_global_memory)
+ {
+ /* If there is nothing special about this call then
+ we have made everything that is used also escape. */
+ *pt = cfun->gimple_df->escaped;
+ pt->nonlocal = 1;
+ }
}
pt = gimple_call_clobber_set (stmt);
if (gimple_call_flags (stmt) & (ECF_CONST|ECF_PURE|ECF_NOVOPS))
memset (pt, 0, sizeof (struct pt_solution));
- else if ((vi = lookup_call_clobber_vi (stmt)) != NULL)
- {
- *pt = find_what_var_points_to (cfun->decl, vi);
- /* Escaped (and thus nonlocal) variables are always
- implicitly clobbered by calls. */
- /* ??? ESCAPED can be empty even though NONLOCAL
- always escaped. */
- pt->nonlocal = 1;
- pt->escaped = 1;
- }
else
{
- /* If there is nothing special about this call then
- we have made everything that is used also escape. */
- *pt = cfun->gimple_df->escaped;
- pt->nonlocal = 1;
+ bool writes_global_memory = true;
+
+ determine_global_memory_access (stmt, &writes_global_memory,
+ NULL, NULL);
+
+ if (!writes_global_memory)
+ ;
+ else if ((vi = lookup_call_clobber_vi (stmt)) != NULL)
+ {
+ *pt = find_what_var_points_to (cfun->decl, vi);
+ /* Escaped (and thus nonlocal) variables are always
+ implicitly clobbered by calls. */
+ /* ??? ESCAPED can be empty even though NONLOCAL
+ always escaped. */
+ pt->nonlocal = writes_global_memory;
+ pt->escaped = writes_global_memory;
+ }
+ else if (writes_global_memory)
+ {
+ /* If there is nothing special about this call then
+ we have made everything that is used also escape. */
+ *pt = cfun->gimple_df->escaped;
+ pt->nonlocal = 1;
+ }
}
}
}