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.
This commit is contained in:
Jan Hubicka 2021-11-10 13:08:41 +01:00
parent 0cf6065ce4
commit d70ef65692
16 changed files with 309 additions and 179 deletions

View File

@ -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)

View File

@ -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,7 +2291,6 @@ modref_eaf_analysis::propagate ()
fprintf (dump_file, " New lattice: ");
m_lattice[target].dump (dump_file);
}
if (target <= (int)i)
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;

View File

@ -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;
}

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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"} } */

View File

@ -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"} } */

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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 does not escape. */
#define EAF_NOESCAPE (1 << 2)
/* Nonzero if the argument is not used by the function. */
#define EAF_UNUSED (1 << 3)
#define EAF_UNUSED (1 << 1)
/* Nonzero if the argument itself does not escape but memory
referenced by it can escape. */
#define EAF_NODIRECTESCAPE (1 << 4)
/* 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 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 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

View File

@ -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)

View File

@ -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;
if (!(flags & EAF_NOT_RETURNED))
/* 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 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))
{
if (!(flags & EAF_NOT_RETURNED_DIRECTLY))
{
struct constraint_expr cexpr;
cexpr.var = tem->id;
if (flags & EAF_NOT_RETURNED_DIRECTLY)
{
cexpr.type = DEREF;
cexpr.offset = UNKNOWN_OFFSET;
}
else
{
cexpr.type = SCALAR;
cexpr.offset = 0;
}
results->safe_push (cexpr);
}
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);
}
}
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;

View File

@ -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);