aboutsummaryrefslogtreecommitdiff
path: root/gcc/ipa-modref.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/ipa-modref.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/ipa-modref.c')
-rw-r--r--gcc/ipa-modref.c204
1 files changed, 107 insertions, 97 deletions
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index e3cad3e..14c3894 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -148,22 +148,24 @@ struct escape_entry
static void
dump_eaf_flags (FILE *out, int flags, bool newline = true)
{
- if (flags & EAF_DIRECT)
- fprintf (out, " direct");
- if (flags & EAF_NOCLOBBER)
- fprintf (out, " noclobber");
- if (flags & EAF_NOESCAPE)
- fprintf (out, " noescape");
- if (flags & EAF_NODIRECTESCAPE)
- fprintf (out, " nodirectescape");
if (flags & EAF_UNUSED)
fprintf (out, " unused");
- if (flags & EAF_NOT_RETURNED)
- fprintf (out, " not_returned");
+ if (flags & EAF_NO_DIRECT_CLOBBER)
+ fprintf (out, " no_direct_clobber");
+ if (flags & EAF_NO_INDIRECT_CLOBBER)
+ fprintf (out, " no_indirect_clobber");
+ if (flags & EAF_NO_DIRECT_ESCAPE)
+ fprintf (out, " no_direct_escape");
+ if (flags & EAF_NO_INDIRECT_ESCAPE)
+ fprintf (out, " no_indirect_escape");
if (flags & EAF_NOT_RETURNED_DIRECTLY)
fprintf (out, " not_returned_directly");
- if (flags & EAF_NOREAD)
- fprintf (out, " noread");
+ if (flags & EAF_NOT_RETURNED_INDIRECTLY)
+ fprintf (out, " not_returned_indirectly");
+ if (flags & EAF_NO_DIRECT_READ)
+ fprintf (out, " no_direct_read");
+ if (flags & EAF_NO_INDIRECT_READ)
+ fprintf (out, " no_indirect_read");
if (newline)
fprintf (out, "\n");
}
@@ -296,7 +298,7 @@ remove_useless_eaf_flags (int eaf_flags, int ecf_flags, bool returns_void)
else if (ecf_flags & ECF_PURE)
eaf_flags &= ~implicit_pure_eaf_flags;
else if ((ecf_flags & ECF_NORETURN) || returns_void)
- eaf_flags &= ~(EAF_NOT_RETURNED | EAF_NOT_RETURNED_DIRECTLY);
+ eaf_flags &= ~(EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY);
return eaf_flags;
}
@@ -1412,35 +1414,32 @@ memory_access_to (tree op, tree ssa_name)
static int
deref_flags (int flags, bool ignore_stores)
{
- int ret = EAF_NODIRECTESCAPE | EAF_NOT_RETURNED_DIRECTLY;
+ /* Dereference is also a direct read but dereferenced value does not
+ yield any other direct use. */
+ int ret = EAF_NO_DIRECT_CLOBBER | EAF_NO_DIRECT_ESCAPE
+ | EAF_NOT_RETURNED_DIRECTLY;
/* If argument is unused just account for
the read involved in dereference. */
if (flags & EAF_UNUSED)
- ret |= EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NOT_RETURNED;
+ ret |= EAF_NO_INDIRECT_READ | EAF_NO_INDIRECT_CLOBBER
+ | EAF_NO_INDIRECT_ESCAPE;
else
{
- if ((flags & EAF_NOCLOBBER) || ignore_stores)
- ret |= EAF_NOCLOBBER;
- if ((flags & EAF_NOESCAPE) || ignore_stores)
- ret |= EAF_NOESCAPE;
- /* If the value dereferenced is not used for another load or store
- we can still consider ARG as used only directly.
-
- Consider
-
- int
- test (int *a)
- {
- return *a!=0;
- }
-
- */
- if ((flags & (EAF_NOREAD | EAF_NOT_RETURNED | EAF_NOESCAPE | EAF_DIRECT))
- == (EAF_NOREAD | EAF_NOT_RETURNED | EAF_NOESCAPE | EAF_DIRECT)
- && ((flags & EAF_NOCLOBBER) || ignore_stores))
- ret |= EAF_DIRECT;
- if (flags & EAF_NOT_RETURNED)
- ret |= EAF_NOT_RETURNED;
+ /* Direct or indirect accesses leads to indirect accesses. */
+ if (((flags & EAF_NO_DIRECT_CLOBBER)
+ && (flags & EAF_NO_INDIRECT_CLOBBER))
+ || ignore_stores)
+ ret |= EAF_NO_INDIRECT_CLOBBER;
+ if (((flags & EAF_NO_DIRECT_ESCAPE)
+ && (flags & EAF_NO_INDIRECT_ESCAPE))
+ || ignore_stores)
+ ret |= EAF_NO_INDIRECT_ESCAPE;
+ if ((flags & EAF_NO_DIRECT_READ)
+ && (flags & EAF_NO_INDIRECT_READ))
+ ret |= EAF_NO_INDIRECT_READ;
+ if ((flags & EAF_NOT_RETURNED_DIRECTLY)
+ && (flags & EAF_NOT_RETURNED_INDIRECTLY))
+ ret |= EAF_NOT_RETURNED_INDIRECTLY;
}
return ret;
}
@@ -1508,9 +1507,11 @@ void
modref_lattice::init ()
{
/* All flags we track. */
- int f = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED
- | EAF_NODIRECTESCAPE | EAF_NOT_RETURNED |
- EAF_NOT_RETURNED_DIRECTLY | EAF_NOREAD;
+ int f = EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER
+ | EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE
+ | EAF_NO_DIRECT_READ | EAF_NO_INDIRECT_READ
+ | EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY
+ | EAF_UNUSED;
flags = f;
/* Check that eaf_flags_t is wide enough to hold all flags. */
gcc_checking_assert (f == flags);
@@ -1589,12 +1590,6 @@ modref_lattice::merge (int f)
{
if (f & EAF_UNUSED)
return false;
- /* Noescape implies that value also does not escape directly.
- Fnspec machinery does set both so compensate for this. */
- if (f & EAF_NOESCAPE)
- f |= EAF_NODIRECTESCAPE;
- if (f & EAF_NOT_RETURNED)
- f |= EAF_NOT_RETURNED_DIRECTLY;
if ((flags & f) != flags)
{
flags &= f;
@@ -1664,7 +1659,7 @@ modref_lattice::merge_deref (const modref_lattice &with, bool ignore_stores)
bool
modref_lattice::merge_direct_load ()
{
- return merge (~(EAF_UNUSED | EAF_NOREAD));
+ return merge (~(EAF_UNUSED | EAF_NO_DIRECT_READ));
}
/* Merge in flags for direct store. */
@@ -1672,7 +1667,7 @@ modref_lattice::merge_direct_load ()
bool
modref_lattice::merge_direct_store ()
{
- return merge (~(EAF_UNUSED | EAF_NOCLOBBER));
+ return merge (~(EAF_UNUSED | EAF_NO_DIRECT_CLOBBER));
}
/* Analyzer of EAF flags.
@@ -1729,22 +1724,30 @@ private:
auto_vec<int> m_names_to_propagate;
void merge_with_ssa_name (tree dest, tree src, bool deref);
- void merge_call_lhs_flags (gcall *call, int arg, tree name, bool deref);
+ void merge_call_lhs_flags (gcall *call, int arg, tree name, bool direct,
+ bool deref);
};
-/* Call statements may return their parameters. Consider argument number
+/* Call statements may return tgeir parameters. Consider argument number
ARG of USE_STMT and determine flags that can needs to be cleared
in case pointer possibly indirectly references from ARG I is returned.
+ If DIRECT is true consider direct returns and if INDIRECT consider
+ indirect returns.
LATTICE, DEPTH and ipa are same as in analyze_ssa_name.
ARG is set to -1 for static chain. */
void
modref_eaf_analysis::merge_call_lhs_flags (gcall *call, int arg,
- tree name, bool deref)
+ tree name, bool direct,
+ bool indirect)
{
int index = SSA_NAME_VERSION (name);
+ /* If value is not returned at all, do nothing. */
+ if (!direct && !indirect)
+ return;
+
/* If there is no return value, no flags are affected. */
if (!gimple_call_lhs (call))
return;
@@ -1763,10 +1766,13 @@ modref_eaf_analysis::merge_call_lhs_flags (gcall *call, int arg,
if (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME)
{
tree lhs = gimple_call_lhs (call);
- merge_with_ssa_name (name, lhs, deref);
+ if (direct)
+ merge_with_ssa_name (name, lhs, false);
+ if (indirect)
+ merge_with_ssa_name (name, lhs, true);
}
/* In the case of memory store we can do nothing. */
- else if (deref)
+ else if (!direct)
m_lattice[index].merge (deref_flags (0, false));
else
m_lattice[index].merge (0);
@@ -1782,18 +1788,19 @@ callee_to_caller_flags (int call_flags, bool ignore_stores,
{
/* call_flags is about callee returning a value
that is not the same as caller returning it. */
- call_flags |= EAF_NOT_RETURNED
- | EAF_NOT_RETURNED_DIRECTLY;
+ call_flags |= EAF_NOT_RETURNED_DIRECTLY
+ | EAF_NOT_RETURNED_INDIRECTLY;
/* TODO: We miss return value propagation.
Be conservative and if value escapes to memory
also mark it as escaping. */
if (!ignore_stores && !(call_flags & EAF_UNUSED))
{
- if (!(call_flags & EAF_NOESCAPE))
- lattice.merge (~(EAF_NOT_RETURNED | EAF_UNUSED));
- if (!(call_flags & (EAF_NODIRECTESCAPE | EAF_NOESCAPE)))
+ if (!(call_flags & EAF_NO_DIRECT_ESCAPE))
lattice.merge (~(EAF_NOT_RETURNED_DIRECTLY
- | EAF_NOT_RETURNED
+ | EAF_NOT_RETURNED_INDIRECTLY
+ | EAF_UNUSED));
+ if (!(call_flags & EAF_NO_INDIRECT_ESCAPE))
+ lattice.merge (~(EAF_NOT_RETURNED_INDIRECTLY
| EAF_UNUSED));
}
else
@@ -1869,13 +1876,13 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
&& DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
;
else if (gimple_return_retval (ret) == name)
- m_lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED
+ m_lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED_DIRECTLY
| EAF_NOT_RETURNED_DIRECTLY));
else if (memory_access_to (gimple_return_retval (ret), name))
{
m_lattice[index].merge_direct_load ();
- m_lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED
- | EAF_NOT_RETURNED_DIRECTLY));
+ m_lattice[index].merge (~(EAF_UNUSED
+ | EAF_NOT_RETURNED_INDIRECTLY));
}
}
/* Account for LHS store, arg loads and flags from callee function. */
@@ -1889,7 +1896,7 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
is on since that would allow propagation of this from -fno-ipa-pta
to -fipa-pta functions. */
if (gimple_call_fn (use_stmt) == name)
- m_lattice[index].merge (~(EAF_NOCLOBBER | EAF_UNUSED));
+ m_lattice[index].merge (~(EAF_NO_DIRECT_CLOBBER | EAF_UNUSED));
/* Recursion would require bit of propagation; give up for now. */
if (callee && !m_ipa && recursive_call_p (current_function_decl,
@@ -1932,14 +1939,14 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
arg is written to itself which is an escape. */
if (!isretslot)
{
- if (!(call_flags & (EAF_NOT_RETURNED | EAF_UNUSED)))
- m_lattice[index].merge (~(EAF_NOESCAPE
- | EAF_UNUSED));
if (!(call_flags & (EAF_NOT_RETURNED_DIRECTLY
- | EAF_UNUSED
- | EAF_NOT_RETURNED)))
- m_lattice[index].merge (~(EAF_NODIRECTESCAPE
- | EAF_NOESCAPE
+ | EAF_UNUSED)))
+ m_lattice[index].merge (~(EAF_NO_DIRECT_ESCAPE
+ | EAF_NO_INDIRECT_ESCAPE
+ | EAF_UNUSED));
+ if (!(call_flags & (EAF_NOT_RETURNED_INDIRECTLY
+ | EAF_UNUSED)))
+ m_lattice[index].merge (~(EAF_NO_INDIRECT_ESCAPE
| EAF_UNUSED));
call_flags = callee_to_caller_flags
(call_flags, false,
@@ -1953,9 +1960,11 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
&& (gimple_call_chain (call) == name))
{
int call_flags = gimple_call_static_chain_flags (call);
- if (!ignore_retval
- && !(call_flags & (EAF_NOT_RETURNED | EAF_UNUSED)))
- merge_call_lhs_flags (call, -1, name, false);
+ if (!ignore_retval && !(call_flags & EAF_UNUSED))
+ merge_call_lhs_flags
+ (call, -1, name,
+ !(call_flags & EAF_NOT_RETURNED_DIRECTLY),
+ !(call_flags & EAF_NOT_RETURNED_INDIRECTLY));
call_flags = callee_to_caller_flags
(call_flags, ignore_stores,
m_lattice[index]);
@@ -1974,11 +1983,11 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
if (gimple_call_arg (call, i) == name)
{
int call_flags = gimple_call_arg_flags (call, i);
- if (!ignore_retval && !(call_flags
- & (EAF_NOT_RETURNED | EAF_UNUSED)))
+ if (!ignore_retval && !(call_flags & EAF_UNUSED))
merge_call_lhs_flags
(call, i, name,
- call_flags & EAF_NOT_RETURNED_DIRECTLY);
+ !(call_flags & EAF_NOT_RETURNED_DIRECTLY),
+ !(call_flags & EAF_NOT_RETURNED_INDIRECTLY));
if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS)))
{
call_flags = callee_to_caller_flags
@@ -1996,9 +2005,10 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
{
int call_flags = deref_flags
(gimple_call_arg_flags (call, i), ignore_stores);
- if (!ignore_retval
- && !(call_flags & (EAF_NOT_RETURNED | EAF_UNUSED)))
- merge_call_lhs_flags (call, i, name, true);
+ if (!ignore_retval && !(call_flags & EAF_UNUSED)
+ && !(call_flags & EAF_NOT_RETURNED_DIRECTLY)
+ && !(call_flags & EAF_NOT_RETURNED_INDIRECTLY))
+ merge_call_lhs_flags (call, i, name, false, true);
if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
m_lattice[index].merge_direct_load ();
else
@@ -2281,8 +2291,7 @@ modref_eaf_analysis::propagate ()
fprintf (dump_file, " New lattice: ");
m_lattice[target].dump (dump_file);
}
- if (target <= (int)i)
- changed = true;
+ changed = true;
m_lattice[target].changed = true;
}
}
@@ -2820,6 +2829,14 @@ modref_generate (void)
} /* ANON namespace. */
+/* Debugging helper. */
+
+void
+debug_eaf_flags (int flags)
+{
+ dump_eaf_flags (stderr, flags, true);
+}
+
/* Called when a new function is inserted to callgraph late. */
void
@@ -4232,7 +4249,8 @@ modref_merge_call_site_flags (escape_summary *sum,
int flags = 0;
int flags_lto = 0;
/* Returning the value is already accounted to at local propagation. */
- int implicit_flags = EAF_NOT_RETURNED | EAF_NOT_RETURNED_DIRECTLY;
+ int implicit_flags = EAF_NOT_RETURNED_DIRECTLY
+ | EAF_NOT_RETURNED_INDIRECTLY;
if (summary && ee->arg < summary->arg_flags.length ())
flags = summary->arg_flags[ee->arg];
@@ -4263,11 +4281,15 @@ modref_merge_call_site_flags (escape_summary *sum,
else
{
if (fnspec.arg_direct_p (ee->arg))
- fnspec_flags |= EAF_DIRECT;
+ fnspec_flags |= EAF_NO_INDIRECT_READ
+ | EAF_NO_INDIRECT_ESCAPE
+ | EAF_NOT_RETURNED_INDIRECTLY
+ | EAF_NO_INDIRECT_CLOBBER;
if (fnspec.arg_noescape_p (ee->arg))
- fnspec_flags |= EAF_NOESCAPE | EAF_NODIRECTESCAPE;
+ fnspec_flags |= EAF_NO_DIRECT_ESCAPE
+ | EAF_NO_INDIRECT_ESCAPE;
if (fnspec.arg_readonly_p (ee->arg))
- fnspec_flags |= EAF_NOCLOBBER;
+ flags |= EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER;
}
}
implicit_flags |= fnspec_flags;
@@ -4281,16 +4303,6 @@ modref_merge_call_site_flags (escape_summary *sum,
flags = interposable_eaf_flags (flags, implicit_flags);
flags_lto = interposable_eaf_flags (flags_lto, implicit_flags);
}
- /* Noescape implies that value also does not escape directly.
- Fnspec machinery does set both so compensate for this. */
- if (flags & EAF_NOESCAPE)
- flags |= EAF_NODIRECTESCAPE;
- if (flags_lto & EAF_NOESCAPE)
- flags_lto |= EAF_NODIRECTESCAPE;
- if (flags & EAF_NOT_RETURNED)
- flags |= EAF_NOT_RETURNED_DIRECTLY;
- if (flags_lto & EAF_NOT_RETURNED)
- flags_lto |= EAF_NOT_RETURNED_DIRECTLY;
if (!(flags & EAF_UNUSED)
&& cur_summary && ee->parm_index < (int)cur_summary->arg_flags.length ())
{
@@ -4695,8 +4707,6 @@ ipa_modref_c_finalize ()
if (optimization_summaries)
ggc_delete (optimization_summaries);
optimization_summaries = NULL;
- gcc_checking_assert (!summaries
- || flag_incremental_link == INCREMENTAL_LINK_LTO);
if (summaries_lto)
ggc_delete (summaries_lto);
summaries_lto = NULL;