fprintf (out, " unused");
if (flags & EAF_NOT_RETURNED)
fprintf (out, " not_returned");
+ if (flags & EAF_NOREAD)
+ fprintf (out, " noread");
if (newline)
fprintf (out, "\n");
}
ggc_delete (stores);
}
+/* All flags that are implied by the ECF_CONST functions. */
+const int implicit_const_eaf_flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE
+ | EAF_NODIRECTESCAPE | EAF_NOREAD;
+/* All flags that are implied by the ECF_PURE function. */
+const int implicit_pure_eaf_flags = EAF_NOCLOBBER | EAF_NOESCAPE
+ | EAF_NODIRECTESCAPE;
+/* All flags implied when we know we can ignore stores (i.e. when handling
+ call to noreturn). */
+const int ignore_stores_eaf_flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE
+ | EAF_NODIRECTESCAPE;
+
+/* Remove all flags from EAF_FLAGS that are implied by ECF_FLAGS and not
+ useful to track. If returns_void is true moreover clear
+ EAF_NOT_RETURNED. */
+static int
+remove_useless_eaf_flags (int eaf_flags, int ecf_flags, bool returns_void)
+{
+ if (ecf_flags & ECF_NOVOPS)
+ return 0;
+ if (ecf_flags & ECF_CONST)
+ eaf_flags &= ~implicit_const_eaf_flags;
+ 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;
+ /* Only NOCLOBBER or DIRECT flags alone are not useful (see comments
+ in tree-ssa-alias.c). Give up earlier. */
+ if ((eaf_flags & ~(EAF_DIRECT | EAF_NOCLOBBER)) == 0)
+ return 0;
+ return eaf_flags;
+}
+
/* Return true if FLAGS holds some useful information. */
static bool
eaf_flags_useful_p (vec <eaf_flags_t> &flags, int ecf_flags)
{
for (unsigned i = 0; i < flags.length (); i++)
- if (ecf_flags & ECF_CONST)
- {
- if (flags[i] & (EAF_UNUSED | EAF_NOT_RETURNED))
- return true;
- }
- else if (ecf_flags & ECF_PURE)
- {
- if (flags[i] & (EAF_UNUSED | EAF_DIRECT | EAF_NOT_RETURNED))
- return true;
- }
- else
- {
- if (flags[i])
- return true;
- }
+ if (remove_useless_eaf_flags (flags[i], ecf_flags, false))
+ return true;
return false;
}
deref_flags (int flags, bool ignore_stores)
{
int ret = EAF_NODIRECTESCAPE;
+ /* If argument is unused just account for
+ the read involved in dereference. */
if (flags & EAF_UNUSED)
- ret |= EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE;
+ ret |= EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NOT_RETURNED;
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;
}
- if (flags & EAF_NOT_RETURNED)
- ret |= EAF_NOT_RETURNED;
return ret;
}
{
public:
/* EAF flags of the SSA name. */
- int flags;
+ eaf_flags_t flags;
/* DFS bookkkeeping: we don't do real dataflow yet. */
bool known;
bool open;
void
modref_lattice::init ()
{
- flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED
- | EAF_NODIRECTESCAPE | EAF_NOT_RETURNED;
+ /* All flags we track. */
+ int f = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED
+ | EAF_NODIRECTESCAPE | EAF_NOT_RETURNED | EAF_NOREAD;
+ flags = f;
+ /* Check that eaf_flags_t is wide enough to hold all flags. */
+ gcc_checking_assert (f == flags);
open = true;
known = false;
}
unsigned int i;
/* If we already determined flags to be bad enough,
- * we do not need to record. */
- if ((flags & min_flags) == flags)
+ we do not need to record. */
+ if ((flags & min_flags) == flags || (min_flags & EAF_UNUSED))
return false;
FOR_EACH_VEC_ELT (escape_points, i, ep)
{
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 ((flags & f) != flags)
{
flags &= f;
- /* Only NOCLOBBER or DIRECT flags alone are not useful (see comments
- in tree-ssa-alias.c). Give up earlier. */
- if ((flags & ~(EAF_DIRECT | EAF_NOCLOBBER)) == 0)
- flags = 0;
+ /* Prune obvoiusly useless flags;
+ We do not have ECF_FLAGS handy which is not big problem since
+ we will do final flags cleanup before producing summary.
+ Merging should be fast so it can work well with dataflow. */
+ flags = remove_useless_eaf_flags (flags, 0, false);
if (!flags)
escape_points.release ();
return true;
if (with.escape_points[i].direct)
min_flags = deref_flags (min_flags, ignore_stores);
else if (ignore_stores)
- min_flags |= EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NODIRECTESCAPE;
+ min_flags |= ignore_stores_eaf_flags;
changed |= add_escape_point (with.escape_points[i].call,
with.escape_points[i].arg,
min_flags,
bool
modref_lattice::merge_direct_load ()
{
- return merge (~EAF_UNUSED);
+ return merge (~(EAF_UNUSED | EAF_NOREAD));
}
/* Merge in flags for direct store. */
fprintf (dump_file, "%*s Analyzing stmt: ", depth * 4, "");
print_gimple_stmt (dump_file, use_stmt, 0);
}
+ /* If we see a direct non-debug use, clear unused bit.
+ All dereferneces should be accounted below using deref_flags. */
+ lattice[index].merge (~EAF_UNUSED);
/* Gimple return may load the return value.
Returning name counts as an use by tree-ssa-structalias.c */
else if (memory_access_to (gimple_return_retval (ret), name))
{
lattice[index].merge_direct_load ();
- lattice[index].merge (~EAF_NOT_RETURNED);
+ lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED));
}
}
/* Account for LHS store, arg loads and flags from callee function. */
int call_flags = gimple_call_arg_flags (call, i)
| EAF_NOT_RETURNED;
if (ignore_stores)
- call_flags |= EAF_NOCLOBBER | EAF_NOESCAPE
- | EAF_NODIRECTESCAPE;
+ call_flags |= ignore_stores_eaf_flags;
if (!record_ipa)
lattice[index].merge (call_flags);
analyze_ssa_name_flags (name, lattice, 0, ipa);
int flags = lattice[SSA_NAME_VERSION (name)].flags;
- /* For pure functions we have implicit NOCLOBBER
- and NOESCAPE. */
- if (ecf_flags & ECF_PURE)
- flags &= (EAF_UNUSED | EAF_DIRECT | EAF_NOT_RETURNED);
- /* Only useful flags for const function are EAF_NOT_RETURNED and
- EAF_UNUSED. */
- if (ecf_flags & ECF_CONST)
- flags &= (EAF_UNUSED | EAF_NOT_RETURNED);
+ /* Eliminate useless flags so we do not end up storing unnecessary
+ summaries. */
+
+ flags = remove_useless_eaf_flags
+ (flags, ecf_flags,
+ VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
if (flags)
{
if (!ee->direct)
flags = deref_flags (flags, ignore_stores);
else if (ignore_stores)
- flags |= EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NODIRECTESCAPE;
+ flags |= ignore_stores_eaf_flags;
flags |= ee->min_flags;
to_info->arg_flags[ee->parm_index] &= flags;
if (to_info->arg_flags[ee->parm_index])
if (!ee->direct)
flags = deref_flags (flags, ignore_stores);
else if (ignore_stores)
- flags |= EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NODIRECTESCAPE;
+ flags |= ignore_stores_eaf_flags;
flags |= ee->min_flags;
to_info_lto->arg_flags[ee->parm_index] &= flags;
if (to_info_lto->arg_flags[ee->parm_index])
modref_summary_lto *cur_summary_lto,
modref_summary *summary,
modref_summary_lto *summary_lto,
- bool ignore_stores)
+ tree caller,
+ int ecf_flags)
{
escape_entry *ee;
unsigned int i;
bool changed = false;
+ bool ignore_stores = ignore_stores_p (caller, ecf_flags);
/* If we have no useful info to propagate. */
if ((!cur_summary || !cur_summary->arg_flags.length ())
}
else if (ignore_stores)
{
- flags |= EAF_NOESCAPE | EAF_NOCLOBBER | EAF_NODIRECTESCAPE;
- flags_lto |= EAF_NOESCAPE | EAF_NOCLOBBER | EAF_NODIRECTESCAPE;
+ flags |= ignore_stores_eaf_flags;
+ flags_lto |= ignore_stores_eaf_flags;
}
/* Returning the value is already accounted to at local propagation. */
flags |= ee->min_flags | EAF_NOT_RETURNED;
flags_lto |= ee->min_flags | EAF_NOT_RETURNED;
+ /* 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_UNUSED)
&& cur_summary && ee->parm_index < cur_summary->arg_flags.length ())
{
int f = cur_summary->arg_flags[ee->parm_index];
if ((f & flags) != f)
{
- f = f & flags;
- if ((f & ~(EAF_DIRECT | EAF_NOCLOBBER)) == 0)
- f = 0;
+ f = remove_useless_eaf_flags
+ (f & flags, ecf_flags,
+ VOID_TYPE_P (TREE_TYPE (TREE_TYPE (caller))));
cur_summary->arg_flags[ee->parm_index] = f;
changed = true;
}
int f = cur_summary_lto->arg_flags[ee->parm_index];
if ((f & flags_lto) != f)
{
- f = f & flags;
- if ((f & ~(EAF_DIRECT | EAF_NOCLOBBER)) == 0)
- f = 0;
+ f = remove_useless_eaf_flags
+ (f & flags_lto, ecf_flags,
+ VOID_TYPE_P (TREE_TYPE (TREE_TYPE (caller))));
cur_summary_lto->arg_flags[ee->parm_index] = f;
changed = true;
}
changed |= modref_merge_call_site_flags
(sum, cur_summary, cur_summary_lto,
- NULL, NULL, ignore_stores_p (node->decl,
- e->indirect_info->ecf_flags));
+ NULL, NULL,
+ node->decl, e->indirect_info->ecf_flags);
}
if (!cur_summary && !cur_summary_lto)
for (cgraph_edge *callee_edge = cur->callees; callee_edge;
callee_edge = callee_edge->next_callee)
{
- int flags = flags_from_decl_or_type (callee_edge->callee->decl);
+ int ecf_flags = flags_from_decl_or_type
+ (callee_edge->callee->decl);
modref_summary *callee_summary = NULL;
modref_summary_lto *callee_summary_lto = NULL;
struct cgraph_node *callee;
- if (flags & (ECF_CONST | ECF_NOVOPS)
+ if (ecf_flags & (ECF_CONST | ECF_NOVOPS)
|| !callee_edge->inline_failed)
continue;
/* Get the callee and its summary. */
changed |= modref_merge_call_site_flags
(sum, cur_summary, cur_summary_lto,
callee_summary, callee_summary_lto,
- ignore_stores_p (node->decl, flags));
+ node->decl, ecf_flags);
if (dump_file && changed)
{
if (cur_summary)