#include "system.h"
#include "coretypes.h"
#include "backend.h"
+#include "target.h"
#include "tree.h"
#include "gimple.h"
-#include "hard-reg-set.h"
-#include "alias.h"
-#include "fold-const.h"
-#include "print-tree.h"
+#include "tree-pass.h"
+#include "tree-streamer.h"
+#include "cgraph.h"
+#include "diagnostic.h"
#include "calls.h"
#include "cfganal.h"
-#include "internal-fn.h"
#include "tree-eh.h"
#include "gimple-iterator.h"
#include "gimple-walk.h"
#include "tree-cfg.h"
#include "tree-ssa-loop-niter.h"
-#include "tree-inline.h"
-#include "tree-pass.h"
#include "langhooks.h"
-#include "cgraph.h"
#include "ipa-utils.h"
-#include "flags.h"
-#include "diagnostic.h"
#include "gimple-pretty-print.h"
-#include "langhooks.h"
-#include "target.h"
-#include "lto-streamer.h"
-#include "data-streamer.h"
-#include "tree-streamer.h"
#include "cfgloop.h"
#include "tree-scalar-evolution.h"
#include "intl.h"
#include "opts.h"
-#include "varasm.h"
/* Lattice values for const and pure functions. Everything starts out
being const, then may drop to pure and then neither depending on
/* Wrapper around check_decl for loads in local more. */
static bool
-check_load (gimple, tree op, tree, void *data)
+check_load (gimple *, tree op, tree, void *data)
{
if (DECL_P (op))
check_decl ((funct_state)data, op, false, false);
/* Wrapper around check_decl for stores in local more. */
static bool
-check_store (gimple, tree op, tree, void *data)
+check_store (gimple *, tree op, tree, void *data)
{
if (DECL_P (op))
check_decl ((funct_state)data, op, true, false);
/* Wrapper around check_decl for loads in ipa mode. */
static bool
-check_ipa_load (gimple, tree op, tree, void *data)
+check_ipa_load (gimple *, tree op, tree, void *data)
{
if (DECL_P (op))
check_decl ((funct_state)data, op, false, true);
/* Wrapper around check_decl for stores in ipa mode. */
static bool
-check_ipa_store (gimple, tree op, tree, void *data)
+check_ipa_store (gimple *, tree op, tree, void *data)
{
if (DECL_P (op))
check_decl ((funct_state)data, op, true, true);
static void
check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
{
- gimple stmt = gsi_stmt (*gsip);
+ gimple *stmt = gsi_stmt (*gsip);
if (is_gimple_debug (stmt))
return;
}
}
+/* We only propagate across edges that can throw externally and their callee
+ is not interposable. */
static bool
-ignore_edge (struct cgraph_edge *e)
+ignore_edge_for_nothrow (struct cgraph_edge *e)
{
- return (!e->can_throw_external);
+ if (!e->can_throw_external || TREE_NOTHROW (e->callee->decl))
+ return true;
+
+ enum availability avail;
+ cgraph_node *n = e->callee->function_or_virtual_thunk_symbol (&avail);
+ return (avail <= AVAIL_INTERPOSABLE || TREE_NOTHROW (n->decl));
}
/* Return true if NODE is self recursive function.
return false;
}
+/* We only propagate across edges with non-interposable callee. */
+
+static bool
+ignore_edge_for_pure_const (struct cgraph_edge *e)
+{
+ enum availability avail;
+ e->callee->function_or_virtual_thunk_symbol (&avail);
+ return (avail <= AVAIL_INTERPOSABLE);
+}
+
+
/* Produce transitive closure over the callgraph and compute pure/const
attributes. */
struct ipa_dfs_info * w_info;
bool remove_p = false;
- order_pos = ipa_reduced_postorder (order, true, false, NULL);
+ order_pos = ipa_reduced_postorder (order, true, false,
+ ignore_edge_for_pure_const);
if (dump_file)
{
cgraph_node::dump_cgraph (dump_file);
if (pure_const_state == IPA_NEITHER)
break;
- /* For overwritable nodes we can not assume anything. */
+ /* For interposable nodes we can not assume anything. */
if (w->get_availability () == AVAIL_INTERPOSABLE)
{
worse_state (&pure_const_state, &looping,
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file,
- " Overwritable. state %s looping %i\n",
+ " Interposable. state %s looping %i\n",
pure_const_names[w_l->state_previously_known],
w_l->looping_previously_known);
}
looping = true;
/* Now walk the edges and merge in callee properties. */
- for (e = w->callees; e; e = e->next_callee)
+ for (e = w->callees; e && pure_const_state != IPA_NEITHER;
+ e = e->next_callee)
{
enum availability avail;
struct cgraph_node *y = e->callee->
if (pure_const_state == IPA_NEITHER)
break;
}
- if (pure_const_state == IPA_NEITHER)
- break;
/* Now process the indirect call. */
- for (ie = w->indirect_calls; ie; ie = ie->next_callee)
+ for (ie = w->indirect_calls;
+ ie && pure_const_state != IPA_NEITHER; ie = ie->next_callee)
{
enum pure_const_state_e edge_state = IPA_CONST;
bool edge_looping = false;
if (pure_const_state == IPA_NEITHER)
break;
}
- if (pure_const_state == IPA_NEITHER)
- break;
/* And finally all loads and stores. */
- for (i = 0; w->iterate_reference (i, ref); i++)
+ for (i = 0; w->iterate_reference (i, ref)
+ && pure_const_state != IPA_NEITHER; i++)
{
enum pure_const_state_e ref_state = IPA_CONST;
bool ref_looping = false;
&& this_state > w_l->state_previously_known)
{
this_state = w_l->state_previously_known;
- this_looping |= w_l->looping_previously_known;
+ if (this_state == IPA_NEITHER)
+ this_looping = w_l->looping_previously_known;
}
if (!this_looping && self_recursive_p (w))
this_looping = true;
int i;
struct ipa_dfs_info * w_info;
- order_pos = ipa_reduced_postorder (order, true, false, ignore_edge);
+ order_pos = ipa_reduced_postorder (order, true, false,
+ ignore_edge_for_nothrow);
if (dump_file)
{
cgraph_node::dump_cgraph (dump_file);
while (w && !can_throw)
{
struct cgraph_edge *e, *ie;
- funct_state w_l = get_function_state (w);
-
- if (w_l->can_throw
- || w->get_availability () == AVAIL_INTERPOSABLE)
- can_throw = true;
- for (e = w->callees; e && !can_throw; e = e->next_callee)
+ if (!TREE_NOTHROW (w->decl))
{
- enum availability avail;
- struct cgraph_node *y = e->callee->
- function_or_virtual_thunk_symbol (&avail);
+ funct_state w_l = get_function_state (w);
- if (avail > AVAIL_INTERPOSABLE)
+ if (w_l->can_throw
+ || w->get_availability () == AVAIL_INTERPOSABLE)
+ can_throw = true;
+
+ for (e = w->callees; e && !can_throw; e = e->next_callee)
{
- funct_state y_l = get_function_state (y);
+ enum availability avail;
+
+ if (!e->can_throw_external || TREE_NOTHROW (e->callee->decl))
+ continue;
+
+ struct cgraph_node *y = e->callee->
+ function_or_virtual_thunk_symbol (&avail);
- if (y_l->can_throw && !TREE_NOTHROW (w->decl)
- && e->can_throw_external)
+ /* We can use info about the callee only if we know it can
+ not be interposed. */
+ if (avail <= AVAIL_INTERPOSABLE
+ || (!TREE_NOTHROW (y->decl)
+ && get_function_state (y)->can_throw))
can_throw = true;
}
- else if (e->can_throw_external && !TREE_NOTHROW (y->decl))
- can_throw = true;
+ for (ie = w->indirect_calls; ie && !can_throw;
+ ie = ie->next_callee)
+ if (ie->can_throw_external
+ && !(ie->indirect_info->ecf_flags & ECF_NOTHROW))
+ can_throw = true;
}
- for (ie = w->indirect_calls; ie && !can_throw; ie = ie->next_callee)
- if (ie->can_throw_external)
- can_throw = true;
w_info = (struct ipa_dfs_info *) w->aux;
w = w_info->next_cycle;
}
if (node->get_availability () <= AVAIL_INTERPOSABLE)
{
if (dump_file)
- fprintf (dump_file, "Function is not available or overwritable; not analyzing.\n");
+ fprintf (dump_file, "Function is not available or interposable; not analyzing.\n");
return true;
}
return false;