/* Callgraph based analysis of static variables.
- Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
This file is part of GCC.
#include "pointer-set.h"
#include "ggc.h"
#include "ipa-utils.h"
-#include "c-common.h"
#include "gimple.h"
#include "cgraph.h"
#include "output.h"
#include "diagnostic.h"
#include "langhooks.h"
#include "target.h"
+#include "cfgloop.h"
+#include "tree-scalar-evolution.h"
static struct pointer_set_t *visited_nodes;
/* See above. */
enum pure_const_state_e pure_const_state;
/* What user set here; we can be always sure about this. */
- enum pure_const_state_e state_set_in_source;
+ enum pure_const_state_e state_previously_known;
+ bool looping_previously_known;
/* True if the function could possibly infinite loop. There are a
lot of ways that this could be determined. We are pretty
static inline void
check_op (funct_state local, tree t, bool checking_write)
{
- if (TREE_THIS_VOLATILE (t))
+ t = get_base_address (t);
+ if (t && TREE_THIS_VOLATILE (t))
{
local->pure_const_state = IPA_NEITHER;
if (dump_file)
fprintf (dump_file, " Volatile indirect ref is not const/pure\n");
return;
}
+ else if (t
+ && INDIRECT_REF_P (t)
+ && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME
+ && !ptr_deref_may_alias_global_p (TREE_OPERAND (t, 0)))
+ {
+ if (dump_file)
+ fprintf (dump_file, " Indirect ref to local memory is OK\n");
+ return;
+ }
else if (checking_write)
{
local->pure_const_state = IPA_NEITHER;
{
if (dump_file)
{
- fprintf (dump_file, " can throw externally in region %i\n",
- lookup_stmt_eh_region (call));
+ fprintf (dump_file, " can throw externally to lp %i\n",
+ lookup_stmt_eh_lp (call));
if (callee_t)
fprintf (dump_file, " callee:%s\n",
IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (callee_t)));
gimple stmt = gsi_stmt (*gsip);
unsigned int i = 0;
+ if (is_gimple_debug (stmt))
+ return;
+
if (dump_file)
{
fprintf (dump_file, " scanning: ");
l = XCNEW (struct funct_state_d);
l->pure_const_state = IPA_CONST;
- l->state_set_in_source = IPA_NEITHER;
+ l->state_previously_known = IPA_NEITHER;
+ l->looping_previously_known = true;
l->looping = false;
l->can_throw = false;
indication of possible infinite loop side
effect. */
if (mark_dfs_back_edges ())
- l->looping = true;
-
+ {
+ /* Preheaders are needed for SCEV to work.
+ Simple lateches and recorded exits improve chances that loop will
+ proved to be finite in testcases such as in loop-15.c and loop-24.c */
+ loop_optimizer_init (LOOPS_NORMAL
+ | LOOPS_HAVE_RECORDED_EXITS);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ flow_loops_dump (dump_file, NULL, 0);
+ if (mark_irreducible_loops ())
+ {
+ if (dump_file)
+ fprintf (dump_file, " has irreducible loops\n");
+ l->looping = true;
+ }
+ else
+ {
+ loop_iterator li;
+ struct loop *loop;
+ scev_initialize ();
+ FOR_EACH_LOOP (li, loop, 0)
+ if (!finite_loop_p (loop))
+ {
+ if (dump_file)
+ fprintf (dump_file, " can not prove finiteness of loop %i\n", loop->num);
+ l->looping =true;
+ break;
+ }
+ scev_finalize ();
+ }
+ loop_optimizer_finalize ();
+ }
}
if (TREE_READONLY (decl))
{
l->pure_const_state = IPA_CONST;
- l->state_set_in_source = IPA_CONST;
+ l->state_previously_known = IPA_CONST;
if (!DECL_LOOPING_CONST_OR_PURE_P (decl))
- l->looping = false;
+ l->looping = false, l->looping_previously_known = false;
}
if (DECL_PURE_P (decl))
{
if (l->pure_const_state != IPA_CONST)
l->pure_const_state = IPA_PURE;
- l->state_set_in_source = IPA_PURE;
+ l->state_previously_known = IPA_PURE;
if (!DECL_LOOPING_CONST_OR_PURE_P (decl))
- l->looping = false;
+ l->looping = false, l->looping_previously_known = false;
}
if (TREE_NOTHROW (decl))
l->can_throw = false;
return (!e->can_throw_external);
}
+/* Return true if NODE is self recursive function. */
+
+static bool
+self_recursive_p (struct cgraph_node *node)
+{
+ struct cgraph_edge *e;
+ for (e = node->callees; e; e = e->next_callee)
+ if (e->callee == node)
+ return true;
+ return false;
+}
+
/* Produce the global information by preforming a transitive closure
on the local information that was produced by generate_summary.
Note that there is no function_transform pass since this only
enum pure_const_state_e this_state = pure_const_state;
bool this_looping = looping;
- if (w_l->state_set_in_source != IPA_NEITHER)
- {
- if (this_state > w_l->state_set_in_source)
- this_state = w_l->state_set_in_source;
- this_looping = false;
- }
+ if (w_l->state_previously_known != IPA_NEITHER
+ && this_state > w_l->state_previously_known)
+ this_state = w_l->state_previously_known;
+ if (!this_looping && self_recursive_p (w))
+ this_looping = true;
+ if (!w_l->looping_previously_known)
+ this_looping = false;
/* All nodes within a cycle share the same info. */
w_l->pure_const_state = this_state;
w = node;
while (w)
{
+ funct_state w_l = get_function_state (w);
if (!can_throw && !TREE_NOTHROW (w->decl))
{
struct cgraph_edge *e;
fprintf (dump_file, "Function found to be nothrow: %s\n",
cgraph_node_name (w));
}
+ else if (can_throw && !TREE_NOTHROW (w->decl))
+ w_l->can_throw = true;
w_info = (struct ipa_dfs_info *) w->aux;
w = w_info->next_cycle;
}
&& !(errorcount || sorrycount));
}
-struct ipa_opt_pass pass_ipa_pure_const =
+struct ipa_opt_pass_d pass_ipa_pure_const =
{
{
IPA_PASS,