aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-structalias.c
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2021-11-10 13:08:41 +0100
committerJan Hubicka <jh@suse.cz>2021-11-10 13:08:41 +0100
commitd70ef65692fced7ab72e0aceeff7407e5a34d96d (patch)
treede1a8f2fb5906a8f448b2b8665b911e78ee335d0 /gcc/tree-ssa-structalias.c
parent0cf6065ce4997774de66db4de83d461013e0f0e1 (diff)
downloadgcc-d70ef65692fced7ab72e0aceeff7407e5a34d96d.zip
gcc-d70ef65692fced7ab72e0aceeff7407e5a34d96d.tar.gz
gcc-d70ef65692fced7ab72e0aceeff7407e5a34d96d.tar.bz2
Make EAF flags more regular (and expressive)
I hoped that I am done with EAF flags related changes, but while looking into the Fortran testcases I noticed that I have designed them in unnecesarily restricted way. I followed the scheme of NOESCAPE and NODIRECTESCAPE which is however the only property tht is naturally transitive. This patch replaces the existing flags by 9 flags: EAF_UNUSED EAF_NO_DIRECT_CLOBBER and EAF_NO_INDIRECT_CLOBBER EAF_NO_DIRECT_READ and EAF_NO_INDIRECT_READ EAF_NO_DIRECT_ESCAPE and EAF_NO_INDIRECT_ESCAPE EAF_NO_DIRECT_READ and EAF_NO_INDIRECT_READ So I have removed the unified EAF_DIRECT flag and made each of the flags to come in direct and indirect variant. Newly the indirect variant is not implied by direct (well except for escape but it is not special cased in the code) Consequently we can analyse i.e. the case where function reads directly and clobber indirectly as in the following testcase: struct wrap { void **array; }; __attribute__ ((noinline)) void write_array (struct wrap *ptr) { ptr->array[0]=0; } int test () { void *arrayval; struct wrap w = {&arrayval}; write_array (&w); return w.array == &arrayval; } This is pretty common in array descriptors and also C++ pointer wrappers or structures containing pointers to arrays. Other advantage is that !binds_to_current_def_p functions we can still track the fact that the value is not clobbered indirectly while previously we implied EAF_DIRECT for all three cases. Finally the propagation becomes more regular and I hope easier to understand because the flags are handled in a symmetric way. In tree-ssa-structalias I now produce "callarg" var_info as before and if necessary also "indircallarg" for the indirect accesses. I added some logic to optimize the common case where we can not make difference between direct and indirect. gcc/ChangeLog: 2021-11-09 Jan Hubicka <hubicka@ucw.cz> * tree-core.h (EAF_DIRECT): Remove. (EAF_NOCLOBBER): Remove. (EAF_UNUSED): Remove. (EAF_NOESCAPE): Remove. (EAF_NO_DIRECT_CLOBBER): New. (EAF_NO_INDIRECT_CLOBBER): New. (EAF_NODIRECTESCAPE): Remove. (EAF_NO_DIRECT_ESCAPE): New. (EAF_NO_INDIRECT_ESCAPE): New. (EAF_NOT_RETURNED): Remove. (EAF_NOT_RETURNED_INDIRECTLY): New. (EAF_NOREAD): Remove. (EAF_NO_DIRECT_READ): New. (EAF_NO_INDIRECT_READ): New. * gimple.c (gimple_call_arg_flags): Update for new flags. (gimple_call_retslot_flags): Update for new flags. * ipa-modref.c (dump_eaf_flags): Likewise. (remove_useless_eaf_flags): Likewise. (deref_flags): Likewise. (modref_lattice::init): Likewise. (modref_lattice::merge): Likewise. (modref_lattice::merge_direct_load): Likewise. (modref_lattice::merge_direct_store): Likewise. (modref_eaf_analysis::merge_call_lhs_flags): Likewise. (callee_to_caller_flags): Likewise. (modref_eaf_analysis::analyze_ssa_name): Likewise. (modref_eaf_analysis::propagate): Likewise. (modref_merge_call_site_flags): Likewise. * ipa-modref.h (interposable_eaf_flags): Likewise. * tree-ssa-alias.c: (ref_maybe_used_by_call_p_1) Likewise. * tree-ssa-structalias.c (handle_call_arg): Likewise. (handle_rhs_call): Likewise. * tree-ssa-uninit.c (maybe_warn_pass_by_reference): Likewise. gcc/testsuite/ChangeLog: * g++.dg/ipa/modref-1.C: Update template. * gcc.dg/ipa/modref-3.c: Update template. * gcc.dg/lto/modref-3_0.c: Update template. * gcc.dg/lto/modref-4_0.c: Update template. * gcc.dg/tree-ssa/modref-10.c: Update template. * gcc.dg/tree-ssa/modref-11.c: Update template. * gcc.dg/tree-ssa/modref-5.c: Update template. * gcc.dg/tree-ssa/modref-6.c: Update template. * gcc.dg/tree-ssa/modref-13.c: New test.
Diffstat (limited to 'gcc/tree-ssa-structalias.c')
-rw-r--r--gcc/tree-ssa-structalias.c142
1 files changed, 115 insertions, 27 deletions
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index c70f5af..153ddf5 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4060,48 +4060,117 @@ static void
handle_call_arg (gcall *stmt, tree arg, vec<ce_s> *results, int flags,
int callescape_id, bool writes_global_memory)
{
+ int relevant_indirect_flags = EAF_NO_INDIRECT_CLOBBER | EAF_NO_INDIRECT_READ
+ | EAF_NO_INDIRECT_ESCAPE;
+ int relevant_flags = relevant_indirect_flags
+ | EAF_NO_DIRECT_CLOBBER
+ | EAF_NO_DIRECT_READ
+ | EAF_NO_DIRECT_ESCAPE;
+ if (gimple_call_lhs (stmt))
+ {
+ relevant_flags |= EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY;
+ relevant_indirect_flags |= EAF_NOT_RETURNED_INDIRECTLY;
+
+ /* If value is never read from it can not be returned indirectly
+ (except through the escape solution).
+ For all flags we get these implications right except for
+ not_returned because we miss return functions in ipa-prop. */
+
+ if (flags & EAF_NO_DIRECT_READ)
+ flags |= EAF_NOT_RETURNED_INDIRECTLY;
+ }
+
/* 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)))
+ if ((flags & EAF_UNUSED) || ((flags & relevant_flags) == relevant_flags))
return;
+ /* Produce varinfo for direct accesses to ARG. */
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);
+ bool callarg_transitive = false;
+
+ /* As an compile time optimization if we make no difference between
+ direct and indirect accesses make arg transitively closed.
+ This avoids the need to build indir arg and do everything twice. */
+ if (((flags & EAF_NO_INDIRECT_CLOBBER) != 0)
+ == ((flags & EAF_NO_DIRECT_CLOBBER) != 0)
+ && (((flags & EAF_NO_INDIRECT_READ) != 0)
+ == ((flags & EAF_NO_DIRECT_READ) != 0))
+ && (((flags & EAF_NO_INDIRECT_ESCAPE) != 0)
+ == ((flags & EAF_NO_DIRECT_ESCAPE) != 0))
+ && (((flags & EAF_NOT_RETURNED_INDIRECTLY) != 0)
+ == ((flags & EAF_NOT_RETURNED_DIRECTLY) != 0)))
+ {
+ make_transitive_closure_constraints (tem);
+ callarg_transitive = true;
+ gcc_checking_assert (!(flags & EAF_NO_DIRECT_READ));
+ }
+
+ /* If necessary, produce varinfo for indirect accesses to ARG. */
+ varinfo_t indir_tem = NULL;
+ if (!callarg_transitive
+ && (flags & relevant_indirect_flags) != relevant_indirect_flags)
+ {
+ struct constraint_expr lhs, rhs;
+ indir_tem = new_var_info (NULL_TREE, "indircallarg", true);
+ indir_tem->is_reg_var = true;
+
+ /* indir_term = *tem. */
+ lhs.type = SCALAR;
+ lhs.var = indir_tem->id;
+ lhs.offset = 0;
+
+ rhs.type = DEREF;
+ rhs.var = tem->id;
+ rhs.offset = UNKNOWN_OFFSET;
+ process_constraint (new_constraint (lhs, rhs));
+
+ make_any_offset_constraints (indir_tem);
- if (!(flags & EAF_NOT_RETURNED))
+ /* If we do not read indirectly there is no need for transitive closure.
+ We know there is only one level of indirection. */
+ if (!(flags & EAF_NO_INDIRECT_READ))
+ make_transitive_closure_constraints (indir_tem);
+ gcc_checking_assert (!(flags & EAF_NO_DIRECT_READ));
+ }
+
+ if (gimple_call_lhs (stmt))
{
- struct constraint_expr cexpr;
- cexpr.var = tem->id;
- if (flags & EAF_NOT_RETURNED_DIRECTLY)
+ if (!(flags & EAF_NOT_RETURNED_DIRECTLY))
{
- cexpr.type = DEREF;
- cexpr.offset = UNKNOWN_OFFSET;
+ struct constraint_expr cexpr;
+ cexpr.var = tem->id;
+ cexpr.type = SCALAR;
+ cexpr.offset = 0;
+ results->safe_push (cexpr);
}
- else
+ if (!callarg_transitive & !(flags & EAF_NOT_RETURNED_INDIRECTLY))
{
+ struct constraint_expr cexpr;
+ cexpr.var = indir_tem->id;
cexpr.type = SCALAR;
cexpr.offset = 0;
+ results->safe_push (cexpr);
}
- results->safe_push (cexpr);
}
- if (!(flags & EAF_NOREAD))
+ if (!(flags & EAF_NO_DIRECT_READ))
{
varinfo_t uses = get_call_use_vi (stmt);
make_copy_constraint (uses, tem->id);
+ if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_READ))
+ make_copy_constraint (uses, indir_tem->id);
}
+ else
+ /* To read indirectly we need to read directly. */
+ gcc_checking_assert (flags & EAF_NO_INDIRECT_READ);
- if (!(flags & EAF_NOCLOBBER))
+ if (!(flags & EAF_NO_DIRECT_CLOBBER))
{
struct constraint_expr lhs, rhs;
@@ -4118,8 +4187,25 @@ handle_call_arg (gcall *stmt, tree arg, vec<ce_s> *results, int flags,
/* callclobbered = arg. */
make_copy_constraint (get_call_clobber_vi (stmt), tem->id);
}
+ if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_CLOBBER))
+ {
+ struct constraint_expr lhs, rhs;
- if (!(flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE)))
+ /* *indir_arg = callescape. */
+ lhs.type = DEREF;
+ lhs.var = indir_tem->id;
+ lhs.offset = 0;
+
+ rhs.type = SCALAR;
+ rhs.var = callescape_id;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+
+ /* callclobbered = indir_arg. */
+ make_copy_constraint (get_call_clobber_vi (stmt), indir_tem->id);
+ }
+
+ if (!(flags & (EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE)))
{
struct constraint_expr lhs, rhs;
@@ -4136,18 +4222,18 @@ handle_call_arg (gcall *stmt, tree arg, vec<ce_s> *results, int flags,
if (writes_global_memory)
make_escape_constraint (arg);
}
- else if (!(flags & EAF_NOESCAPE))
+ else if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_ESCAPE))
{
struct constraint_expr lhs, rhs;
- /* callescape = *(arg + UNKNOWN); */
+ /* callescape = *(indir_arg + UNKNOWN); */
lhs.var = callescape_id;
lhs.offset = 0;
lhs.type = SCALAR;
- rhs.var = tem->id;
- rhs.offset = UNKNOWN_OFFSET;
- rhs.type = DEREF;
+ rhs.var = indir_tem->id;
+ rhs.offset = 0;
+ rhs.type = SCALAR;
process_constraint (new_constraint (lhs, rhs));
if (writes_global_memory)
@@ -4264,20 +4350,22 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results,
&& TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (stmt))))
{
int flags = gimple_call_retslot_flags (stmt);
- if ((flags & (EAF_NOESCAPE | EAF_NOT_RETURNED))
- != (EAF_NOESCAPE | EAF_NOT_RETURNED))
+ const int relevant_flags = EAF_NO_DIRECT_ESCAPE
+ | EAF_NOT_RETURNED_DIRECTLY;
+
+ if (!(flags & EAF_UNUSED) && (flags & relevant_flags) != relevant_flags)
{
auto_vec<ce_s> tmpc;
get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc);
- if (!(flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE)))
+ if (!(flags & EAF_NO_DIRECT_ESCAPE))
{
make_constraints_to (callescape->id, tmpc);
if (writes_global_memory)
make_constraints_to (escaped_id, tmpc);
}
- if (!(flags & EAF_NOT_RETURNED))
+ if (!(flags & EAF_NOT_RETURNED_DIRECTLY))
{
struct constraint_expr *c;
unsigned i;