if (!type)
return false;
+ // Track if all executable arguments are the same.
+ tree single_arg = NULL_TREE;
+ bool seen_arg = false;
+
// Start with an empty range, unioning in each argument's range.
r.set_undefined ();
for (x = 0; x < gimple_phi_num_args (phi); x++)
tree arg = gimple_phi_arg_def (phi, x);
edge e = gimple_phi_arg_edge (phi, x);
- // Register potential dependencies for stale value tracking.
- if (gimple_range_ssa_p (arg) && src.gori ())
- src.gori ()->register_dependency (phi_def, arg);
-
// Get the range of the argument on its edge.
src.get_phi_operand (arg_range, arg, e);
- // If we're recomputing the argument elsewhere, try to refine it.
- r.union_ (arg_range);
+
+ if (!arg_range.undefined_p ())
+ {
+ // Register potential dependencies for stale value tracking.
+ r.union_ (arg_range);
+ if (gimple_range_ssa_p (arg) && src.gori ())
+ src.gori ()->register_dependency (phi_def, arg);
+
+ // Track if all arguments are the same.
+ if (!seen_arg)
+ {
+ seen_arg = true;
+ single_arg = arg;
+ }
+ else if (single_arg != arg)
+ single_arg = NULL_TREE;
+ }
+
// Once the value reaches varying, stop looking.
- if (r.varying_p ())
+ if (r.varying_p () && single_arg == NULL_TREE)
break;
}
+ // If the PHI boils down to a single effective argument, look at it.
+ if (single_arg)
+ {
+ // Symbolic arguments are equivalences.
+ if (gimple_range_ssa_p (single_arg))
+ src.register_relation (phi, EQ_EXPR, phi_def, single_arg);
+ else if (src.get_operand (arg_range, single_arg)
+ && arg_range.singleton_p ())
+ {
+ // Numerical arguments that are a constant can be returned as
+ // the constant. This can help fold later cases where even this
+ // constant might have been UNDEFINED via an unreachable edge.
+ r = arg_range;
+ return true;
+ }
+ }
+
// If SCEV is available, query if this PHI has any knonwn values.
if (scev_initialized_p () && !POINTER_TYPE_P (TREE_TYPE (phi_def)))
{
int_range_max lhs;
unsigned idx;
+ if ((e->flags & EDGE_EXECUTABLE) == 0)
+ {
+ r.set_undefined ();
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Outgoing edge %d->%d unexecutable.\n",
+ e->src->index, e->dest->index);
+ return true;
+ }
+
gcc_checking_assert (gimple_range_ssa_p (name));
// Determine if there is an outgoing edge.
gimple *stmt = outgoing.edge_range_p (lhs, e);
return false;
fur_stmt src (stmt, &q);
-
- // If this edge is never taken, return undefined.
- gcond *gc = dyn_cast<gcond *> (stmt);
- if (gc)
- {
- if (((e->flags & EDGE_TRUE_VALUE) && gimple_cond_false_p (gc))
- || ((e->flags & EDGE_FALSE_VALUE) && gimple_cond_true_p (gc)))
- {
- r.set_undefined ();
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Outgoing edge %d->%d unexecutable.\n",
- e->src->index, e->dest->index);
- return true;
- }
- }
-
// If NAME can be calculated on the edge, use that.
if (is_export_p (name, e->src))
{
#include "cfgloop.h"
#include "tree-scalar-evolution.h"
#include "gimple-range.h"
+#include "domwalk.h"
gimple_ranger::gimple_ranger () : tracer ("")
{
m_oracle = m_cache.oracle ();
if (dump_file && (param_evrp_mode & EVRP_MODE_TRACE))
tracer.enable_trace ();
+ set_all_edges_as_executable (cfun);
}
bool
int_range_max edge_range;
gcc_checking_assert (irange::supports_type_p (TREE_TYPE (name)));
- // PHI arguments can be constants, catch these here.
- if (!gimple_range_ssa_p (name))
- return range_of_expr (r, name);
-
unsigned idx;
if ((idx = tracer.header ("range_on_edge (")))
{
fprintf (dump_file, ") on edge %d->%d\n", e->src->index, e->dest->index);
}
- range_on_exit (r, e->src, name);
- gcc_checking_assert (r.undefined_p ()
- || range_compatible_p (r.type(), TREE_TYPE (name)));
+ // Check to see if the edge is executable.
+ if ((e->flags & EDGE_EXECUTABLE) == 0)
+ {
+ r.set_undefined ();
+ if (idx)
+ tracer.trailer (idx, "range_on_edge [Unexecutable] ", true,
+ name, r);
+ return true;
+ }
- // Check to see if NAME is defined on edge e.
- if (m_cache.range_on_edge (edge_range, e, name))
- r.intersect (edge_range);
+ bool res = true;
+ if (!gimple_range_ssa_p (name))
+ res = range_of_expr (r, name);
+ else
+ {
+ range_on_exit (r, e->src, name);
+ gcc_checking_assert (r.undefined_p ()
+ || range_compatible_p (r.type(), TREE_TYPE (name)));
+
+ // Check to see if NAME is defined on edge e.
+ if (m_cache.range_on_edge (edge_range, e, name))
+ r.intersect (edge_range);
+ }
if (idx)
- tracer.trailer (idx, "range_on_edge", true, name, r);
+ tracer.trailer (idx, "range_on_edge", res, name, r);
return true;
}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp -fno-tree-fre -fdisable-tree-ethread" } */
+
+void kill(void);
+
+void foo (int x, int y, int z)
+{
+ // Establish y = [-INF, 54]
+ if (y < 55)
+ return;
+
+ // Establish z == x
+ if (z != x)
+ return;
+
+ // EVRP should transform this to if (0 != 0)
+ if (y < 30)
+ x = 0;
+
+ // # x_1 = PHI <x_5(D)(6), 0(7)>
+ // The earlier transformation should make the edge from bb7
+ // unexecutable, allowing x_1 == x_5 to be registered, and
+ // then fold away this condition as well.
+ if (x != z)
+ kill();
+
+}
+/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */
return true;
}
+// Clear edge E of EDGE_EXECUTABLE (it is unexecutable). If it wasn't
+// previously clear, propagate to successor blocks if appropriate.
+
+void
+simplify_using_ranges::set_and_propagate_unexecutable (edge e)
+{
+ // If EXECUUTABLE is already clear, we're done.
+ if ((e->flags & EDGE_EXECUTABLE) == 0)
+ return;
+
+ e->flags &= ~EDGE_EXECUTABLE;
+
+ // Check if the destination block needs to propagate the property.
+ basic_block bb = e->dest;
+
+ // If any entry edge is marked EXECUTABLE, we are done.
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (e->flags & EDGE_EXECUTABLE)
+ return;
+
+ // This block is also unexecutable, propagate to all exit edges as well.
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ set_and_propagate_unexecutable (e);
+}
+
/* If COND can be folded entirely as TRUE or FALSE, rewrite the
conditional as such, and return TRUE. */
if (TREE_CODE (gimple_cond_lhs (cond)) != SSA_NAME
&& TREE_CODE (gimple_cond_rhs (cond)) != SSA_NAME)
return false;
-
+ edge e0 = EDGE_SUCC (gimple_bb (cond), 0);
+ edge e1 = EDGE_SUCC (gimple_bb (cond), 1);
if (r.zero_p ())
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\nPredicate evaluates to: 0\n");
gimple_cond_make_false (cond);
+ if (e0->flags & EDGE_TRUE_VALUE)
+ set_and_propagate_unexecutable (e0);
+ else
+ set_and_propagate_unexecutable (e1);
}
else
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\nPredicate evaluates to: 1\n");
gimple_cond_make_true (cond);
+ if (e0->flags & EDGE_FALSE_VALUE)
+ set_and_propagate_unexecutable (e0);
+ else
+ set_and_propagate_unexecutable (e1);
}
update_stmt (cond);
return true;
fprintf (dump_file, "removing unreachable case label\n");
}
to_remove_edges.safe_push (e);
- e->flags &= ~EDGE_EXECUTABLE;
+ set_and_propagate_unexecutable (e);
e->flags |= EDGE_IGNORE;
}
tree vrp_evaluate_conditional_warnv_with_ops_using_ranges (enum tree_code,
tree, tree,
bool *, gimple *s);
+ void set_and_propagate_unexecutable (edge e);
void cleanup_edges_and_switches (void);
/* Vectors of edges that need removing and switch statements that