diff options
-rw-r--r-- | gcc/gimple.c | 9 | ||||
-rw-r--r-- | gcc/ipa-modref.c | 204 | ||||
-rw-r--r-- | gcc/ipa-modref.h | 42 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ipa/modref-1.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/ipa/modref-3.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/lto/modref-3_0.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/lto/modref-4_0.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/modref-10.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/modref-11.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/modref-13.c | 21 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/modref-5.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/modref-6.c | 6 | ||||
-rw-r--r-- | gcc/tree-core.h | 31 | ||||
-rw-r--r-- | gcc/tree-ssa-alias.c | 2 | ||||
-rw-r--r-- | gcc/tree-ssa-structalias.c | 142 | ||||
-rw-r--r-- | gcc/tree-ssa-uninit.c | 3 |
16 files changed, 302 insertions, 172 deletions
diff --git a/gcc/gimple.c b/gcc/gimple.c index 9e65fa6..1e0fad9 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -1575,11 +1575,12 @@ gimple_call_arg_flags (const gcall *stmt, unsigned arg) else { if (fnspec.arg_direct_p (arg)) - flags |= EAF_DIRECT; + flags |= EAF_NO_INDIRECT_READ | EAF_NO_INDIRECT_ESCAPE + | EAF_NOT_RETURNED_INDIRECTLY | EAF_NO_INDIRECT_CLOBBER; if (fnspec.arg_noescape_p (arg)) - flags |= EAF_NOESCAPE | EAF_NODIRECTESCAPE; + flags |= EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE; if (fnspec.arg_readonly_p (arg)) - flags |= EAF_NOCLOBBER; + flags |= EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER; } } tree callee = gimple_call_fndecl (stmt); @@ -1608,7 +1609,7 @@ gimple_call_arg_flags (const gcall *stmt, unsigned arg) int gimple_call_retslot_flags (const gcall *stmt) { - int flags = EAF_DIRECT | EAF_NOREAD; + int flags = implicit_retslot_eaf_flags; tree callee = gimple_call_fndecl (stmt); if (callee) 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; diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h index 20170a6..482c4e4 100644 --- a/gcc/ipa-modref.h +++ b/gcc/ipa-modref.h @@ -21,7 +21,7 @@ along with GCC; see the file COPYING3. If not see #define IPA_MODREF_H typedef modref_tree <alias_set_type> modref_records; -typedef unsigned char eaf_flags_t; +typedef unsigned short eaf_flags_t; /* Single function summary. */ @@ -48,15 +48,28 @@ 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; +static const int implicit_const_eaf_flags + = 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_INDIRECTLY; + /* All flags that are implied by the ECF_PURE function. */ -static const int implicit_pure_eaf_flags = EAF_NOCLOBBER | EAF_NOESCAPE - | EAF_NODIRECTESCAPE; +static const int implicit_pure_eaf_flags + = EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER + | EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE; + /* 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; +static const int ignore_stores_eaf_flags + = EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER + | EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE; + +/* Return slot is write-only. */ +static const int implicit_retslot_eaf_flags + = EAF_NO_DIRECT_READ | EAF_NO_INDIRECT_READ + | EAF_NO_INDIRECT_ESCAPE | EAF_NO_INDIRECT_CLOBBER + | EAF_NOT_RETURNED_INDIRECTLY; /* If function does not bind to current def (i.e. it is inline in comdat section), the modref analysis may not match the behaviour of function @@ -74,16 +87,15 @@ interposable_eaf_flags (int modref_flags, int flags) if ((modref_flags & EAF_UNUSED) && !(flags & EAF_UNUSED)) { modref_flags &= ~EAF_UNUSED; - modref_flags |= EAF_NOESCAPE | EAF_NOT_RETURNED - | EAF_NOT_RETURNED_DIRECTLY | EAF_NOCLOBBER; + modref_flags |= EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE + | EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY + | EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER; } /* We can not deterine that value is not read at all. */ - if ((modref_flags & EAF_NOREAD) && !(flags & EAF_NOREAD)) - modref_flags &= ~EAF_NOREAD; - /* Clear direct flags so we also know that value is possibly read - indirectly. */ - if ((modref_flags & EAF_DIRECT) && !(flags & EAF_DIRECT)) - modref_flags &= ~EAF_DIRECT; + if ((modref_flags & EAF_NO_DIRECT_READ) && !(flags & EAF_NO_DIRECT_READ)) + modref_flags &= ~EAF_NO_DIRECT_READ; + if ((modref_flags & EAF_NO_INDIRECT_READ) && !(flags & EAF_NO_INDIRECT_READ)) + modref_flags &= ~EAF_NO_INDIRECT_READ; return modref_flags; } diff --git a/gcc/testsuite/g++.dg/ipa/modref-1.C b/gcc/testsuite/g++.dg/ipa/modref-1.C index eaa14ea..c57aaca 100644 --- a/gcc/testsuite/g++.dg/ipa/modref-1.C +++ b/gcc/testsuite/g++.dg/ipa/modref-1.C @@ -31,5 +31,5 @@ int main() return 0; } /* { dg-final { scan-tree-dump "Function found to be const: {anonymous}::B::genB" "local-pure-const1" } } */ -/* { dg-final { scan-tree-dump "Retslot flags: direct noescape nodirectescape not_returned not_returned_directly noread" "modref1" } } */ +/* { dg-final { scan-tree-dump "Retslot flags: no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly not_returned_indirectly no_direct_read no_indirect_read" "modref1" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/modref-3.c b/gcc/testsuite/gcc.dg/ipa/modref-3.c index 8401354..9a20e01 100644 --- a/gcc/testsuite/gcc.dg/ipa/modref-3.c +++ b/gcc/testsuite/gcc.dg/ipa/modref-3.c @@ -17,4 +17,4 @@ main () linker_error (); return 0; } -/* { dg-final { scan-ipa-dump "Static chain flags: noclobber noescape nodirectescape" "modref" } } */ +/* { dg-final { scan-ipa-dump "Static chain flags: no_direct_clobber no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly no_indirect_read" "modref" } } */ diff --git a/gcc/testsuite/gcc.dg/lto/modref-3_0.c b/gcc/testsuite/gcc.dg/lto/modref-3_0.c index bd8f96f..0210d11 100644 --- a/gcc/testsuite/gcc.dg/lto/modref-3_0.c +++ b/gcc/testsuite/gcc.dg/lto/modref-3_0.c @@ -14,4 +14,4 @@ main() __builtin_abort (); return 0; } -/* { dg-final { scan-wpa-ipa-dump "parm 1 flags: nodirectescape" "modref" } } */ +/* { dg-final { scan-wpa-ipa-dump "parm 1 flags: no_direct_clobber no_direct_escape" "modref" } } */ diff --git a/gcc/testsuite/gcc.dg/lto/modref-4_0.c b/gcc/testsuite/gcc.dg/lto/modref-4_0.c index db90b4f..9437585 100644 --- a/gcc/testsuite/gcc.dg/lto/modref-4_0.c +++ b/gcc/testsuite/gcc.dg/lto/modref-4_0.c @@ -14,4 +14,4 @@ main() __builtin_abort (); return 0; } -/* { dg-final { scan-wpa-ipa-dump "parm 1 flags: nodirectescape" "modref" } } */ +/* { dg-final { scan-wpa-ipa-dump "parm 1 flags: no_direct_clobber no_direct_escape" "modref" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-10.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-10.c index c608408..4a6d9e5 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-10.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-10.c @@ -17,4 +17,4 @@ main() linker_error (); return 0; } -/* { dg-final { scan-tree-dump "parm 0 flags: noclobber noescape nodirectescape not_returned_directly" "modref1"} } */ +/* { dg-final { scan-tree-dump "no_direct_clobber no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly no_indirect_read" "modref1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c index de9ad16..cafb4f3 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c @@ -10,4 +10,4 @@ find_last (struct linkedlist *l) l = l->next; return l; } -/* { dg-final { scan-tree-dump "noclobber noescape nodirectescape" "modref1"} } */ +/* { dg-final { scan-tree-dump "parm 0 flags: no_direct_clobber no_indirect_clobber no_direct_escape no_indirect_escape" "modref1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-13.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-13.c new file mode 100644 index 0000000..5a57504 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-13.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-release_ssa" } */ +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; +} +/* We should deterine that write_array writes to PTR only indirectly. */ +/* { dg-final { scan-tree-dump "return 1" "releae_ssa" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-5.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-5.c index fde3177..0bee79d 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-5.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-5.c @@ -24,4 +24,4 @@ main() __builtin_abort (); return 0; } -/* { dg-final { scan-tree-dump "parm 1 flags: nodirectescape" "modref1" } } */ +/* { dg-final { scan-tree-dump "parm 1 flags: no_direct_clobber no_direct_escape" "modref1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c index 2d97a49..7146389 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c @@ -28,10 +28,10 @@ int test2() return a; } /* Flags for normal call. */ -/* { dg-final { scan-tree-dump "parm 0 flags: direct noclobber noescape nodirectescape not_returned" "modref1" } } */ +/* { dg-final { scan-tree-dump "parm 0 flags: no_direct_clobber no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly not_returned_indirectly no_indirect_read" "modref1" } } */ /* Flags for pure call. */ -/* { dg-final { scan-tree-dump "parm 0 flags: direct not_returned" "modref1" } } */ +/* { dg-final { scan-tree-dump "parm 0 flags: not_returned_directly not_returned_indirectly no_indirect_read" "modref1" } } */ /* Flags for const call. */ -/* { dg-final { scan-tree-dump "parm 0 flags: not_returned" "modref1" } } */ +/* { dg-final { scan-tree-dump "parm 0 flags: not_returned_directly" "modref1" } } */ /* Overall we want to make "int a" non escaping. */ /* { dg-final { scan-tree-dump "return 42" "optimized" } } */ diff --git a/gcc/tree-core.h b/gcc/tree-core.h index f0c65a2..8ab119d 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -97,32 +97,29 @@ struct die_struct; #define ECF_COLD (1 << 15) /* Call argument flags. */ -/* Nonzero if the argument is not dereferenced recursively, thus only - directly reachable memory is read or written. */ -#define EAF_DIRECT (1 << 0) -/* Nonzero if memory reached by the argument is not clobbered. */ -#define EAF_NOCLOBBER (1 << 1) +/* Nonzero if the argument is not used by the function. */ +#define EAF_UNUSED (1 << 1) -/* Nonzero if the argument does not escape. */ -#define EAF_NOESCAPE (1 << 2) +/* Following flags come in pairs. First one is about direct dereferences + from the parameter, while the second is about memory reachable by + recursive dereferences. */ -/* Nonzero if the argument is not used by the function. */ -#define EAF_UNUSED (1 << 3) +/* Nonzero if memory reached by the argument is not clobbered. */ +#define EAF_NO_DIRECT_CLOBBER (1 << 2) +#define EAF_NO_INDIRECT_CLOBBER (1 << 3) -/* Nonzero if the argument itself does not escape but memory - referenced by it can escape. */ -#define EAF_NODIRECTESCAPE (1 << 4) +/* Nonzero if the argument does not escape. */ +#define EAF_NO_DIRECT_ESCAPE (1 << 4) +#define EAF_NO_INDIRECT_ESCAPE (1 << 5) /* Nonzero if the argument does not escape to return value. */ -#define EAF_NOT_RETURNED (1 << 5) - -/* Nonzero if the argument itself does not escape - to return value but memory referenced by it may escape. */ #define EAF_NOT_RETURNED_DIRECTLY (1 << 6) +#define EAF_NOT_RETURNED_INDIRECTLY (1 << 7) /* Nonzero if the argument is not read. */ -#define EAF_NOREAD (1 << 7) +#define EAF_NO_DIRECT_READ (1 << 8) +#define EAF_NO_INDIRECT_READ (1 << 9) /* Call return flags. */ /* Mask for the argument number that is returned. Lower two bits of diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index eabf680..17ff6bb 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -2874,7 +2874,7 @@ process_args: tree op = gimple_call_arg (call, i); int flags = gimple_call_arg_flags (call, i); - if (flags & (EAF_UNUSED | EAF_NOREAD)) + if (flags & (EAF_UNUSED | EAF_NO_DIRECT_READ)) continue; if (TREE_CODE (op) == WITH_SIZE_EXPR) 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; diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c index d67534f..1df0bcc 100644 --- a/gcc/tree-ssa-uninit.c +++ b/gcc/tree-ssa-uninit.c @@ -744,7 +744,8 @@ maybe_warn_pass_by_reference (gcall *stmt, wlimits &wlims) wlims.always_executed = false; /* Ignore args we are not going to read from. */ - if (gimple_call_arg_flags (stmt, argno - 1) & (EAF_UNUSED | EAF_NOREAD)) + if (gimple_call_arg_flags (stmt, argno - 1) + & (EAF_UNUSED | EAF_NO_DIRECT_READ)) continue; tree arg = gimple_call_arg (stmt, argno - 1); |