diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c index c65b2f65372..fafd804d4ba 100644 --- a/gcc/ipa-modref.c +++ b/gcc/ipa-modref.c @@ -158,6 +158,8 @@ dump_eaf_flags (FILE *out, int flags, bool newline = true) fprintf (out, " unused"); if (flags & EAF_NOT_RETURNED) fprintf (out, " not_returned"); + if (flags & EAF_NOREAD) + fprintf (out, " noread"); if (newline) fprintf (out, "\n"); } @@ -278,27 +280,46 @@ modref_summary::~modref_summary () 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 &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; } @@ -1320,17 +1341,35 @@ static int 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; } @@ -1355,7 +1394,7 @@ class modref_lattice { 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; @@ -1379,8 +1418,12 @@ public: 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; } @@ -1424,8 +1467,8 @@ modref_lattice::add_escape_point (gcall *call, int arg, int min_flags, 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) @@ -1455,13 +1498,18 @@ 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 ((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; @@ -1509,7 +1557,7 @@ modref_lattice::merge_deref (const modref_lattice &with, bool ignore_stores) 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, @@ -1523,7 +1571,7 @@ modref_lattice::merge_deref (const modref_lattice &with, bool ignore_stores) bool modref_lattice::merge_direct_load () { - return merge (~EAF_UNUSED); + return merge (~(EAF_UNUSED | EAF_NOREAD)); } /* Merge in flags for direct store. */ @@ -1632,6 +1680,9 @@ analyze_ssa_name_flags (tree name, vec &lattice, int depth, 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 */ @@ -1642,7 +1693,7 @@ analyze_ssa_name_flags (tree name, vec &lattice, int depth, 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. */ @@ -1697,8 +1748,7 @@ analyze_ssa_name_flags (tree name, vec &lattice, int depth, 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); @@ -1890,14 +1940,12 @@ analyze_parms (modref_summary *summary, modref_summary_lto *summary_lto, 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) { @@ -3176,7 +3224,7 @@ ipa_merge_modref_summary_after_inlining (cgraph_edge *edge) 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]) @@ -3190,7 +3238,7 @@ ipa_merge_modref_summary_after_inlining (cgraph_edge *edge) 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]) @@ -3673,11 +3721,13 @@ modref_merge_call_site_flags (escape_summary *sum, 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 ()) @@ -3701,21 +3751,27 @@ modref_merge_call_site_flags (escape_summary *sum, } 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; } @@ -3727,9 +3783,9 @@ modref_merge_call_site_flags (escape_summary *sum, 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; } @@ -3780,8 +3836,8 @@ modref_propagate_flags_in_scc (cgraph_node *component_node) 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) @@ -3790,12 +3846,13 @@ modref_propagate_flags_in_scc (cgraph_node *component_node) 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. */ @@ -3832,7 +3889,7 @@ modref_propagate_flags_in_scc (cgraph_node *component_node) 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) diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h index 498cc2414ac..540fdea8efa 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 modref_records; -typedef unsigned short eaf_flags_t; +typedef unsigned char eaf_flags_t; /* Single function summary. */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c index 8db9a1d619c..2d97a4903ff 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c @@ -32,6 +32,6 @@ int test2() /* Flags for pure call. */ /* { dg-final { scan-tree-dump "parm 0 flags: direct not_returned" "modref1" } } */ /* Flags for const call. */ -/* { dg-final { scan-tree-dump "parm 0 flags: unused not_returned" "modref1" } } */ +/* { dg-final { scan-tree-dump "parm 0 flags: not_returned" "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 9e901ce4945..239a3a3f80b 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -115,7 +115,10 @@ struct die_struct; #define EAF_NODIRECTESCAPE (1 << 4) /* Nonzero if the argument does not escape to return value. */ -#define EAF_NOT_RETURNED (1 << 8) +#define EAF_NOT_RETURNED (1 << 5) + +/* Nonzero if the argument is not read. */ +#define EAF_NOREAD (1 << 6) /* 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 cbd51ac4d7c..ce667ff32b9 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -2870,7 +2870,7 @@ process_args: tree op = gimple_call_arg (call, i); int flags = gimple_call_arg_flags (call, i); - if (flags & EAF_UNUSED) + if (flags & (EAF_UNUSED | EAF_NOREAD)) continue; if (TREE_CODE (op) == WITH_SIZE_EXPR) diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index c6949268881..fb0e4299703 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -4063,8 +4063,14 @@ handle_rhs_call (gcall *stmt, vec *results) tree arg = gimple_call_arg (stmt, i); int flags = gimple_call_arg_flags (stmt, i); - /* If the argument is not used we can ignore it. */ - if (flags & EAF_UNUSED) + /* 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))) continue; /* As we compute ESCAPED context-insensitive we do not gain @@ -4316,7 +4322,9 @@ handle_pure_call (gcall *stmt, vec *results) int flags = gimple_call_arg_flags (stmt, i); /* If the argument is not used we can ignore it. */ - if (flags & EAF_UNUSED) + if ((flags & EAF_UNUSED) + || (flags & (EAF_NOT_RETURNED | EAF_NOREAD)) + == (EAF_NOT_RETURNED | EAF_NOREAD)) continue; if (!uses) {