diff options
author | Jan Hubicka <jh@suse.cz> | 2021-11-10 13:08:41 +0100 |
---|---|---|
committer | Jan Hubicka <jh@suse.cz> | 2021-11-10 13:08:41 +0100 |
commit | d70ef65692fced7ab72e0aceeff7407e5a34d96d (patch) | |
tree | de1a8f2fb5906a8f448b2b8665b911e78ee335d0 /gcc/ipa-modref.c | |
parent | 0cf6065ce4997774de66db4de83d461013e0f0e1 (diff) | |
download | gcc-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.c | 204 |
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; |