#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "errors.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "tree-chrec.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "flags.h"
static inline bool
tree_is_ge (tree a, tree b, bool *res)
{
tree cmp = fold (build (GE_EXPR, boolean_type_node, a, b));
if (TREE_CODE (cmp) != INTEGER_CST)
return false;
*res = (tree_int_cst_sgn (cmp) != 0);
return true;
}
static inline bool
prove_truth_value_gt (tree type, tree chrec0, tree chrec1, bool *value)
{
tree diff = chrec_fold_minus (type, chrec0, chrec1);
return chrec_is_positive (diff, value);
}
static inline bool
prove_truth_value_lt (tree type, tree chrec0, tree chrec1, bool *value)
{
return prove_truth_value_gt (type, chrec1, chrec0, value);
}
static inline bool
prove_truth_value_le (tree type, tree chrec0, tree chrec1, bool *value)
{
if (prove_truth_value_gt (type, chrec0, chrec1, value))
{
*value = !*value;
return true;
}
return false;
}
static inline bool
prove_truth_value_ge (tree type, tree chrec0, tree chrec1, bool *value)
{
if (prove_truth_value_gt (type, chrec1, chrec0, value))
{
*value = !*value;
return true;
}
return false;
}
static inline bool
prove_truth_value_eq (tree type, tree chrec0, tree chrec1, bool *value)
{
tree diff = chrec_fold_minus (type, chrec0, chrec1);
if (TREE_CODE (diff) == INTEGER_CST)
{
if (integer_zerop (diff))
*value = true;
else
*value = false;
return true;
}
else
return false;
}
static inline bool
prove_truth_value_ne (tree type, tree chrec0, tree chrec1, bool *value)
{
if (prove_truth_value_eq (type, chrec0, chrec1, value))
{
*value = !*value;
return true;
}
return false;
}
static bool
prove_truth_value_symbolic (enum tree_code code, tree chrec0, tree chrec1,
bool *value)
{
tree type0 = chrec_type (chrec0);
tree type1 = chrec_type (chrec1);
return false;
if (type0 != type1)
return false;
switch (code)
{
case EQ_EXPR:
return prove_truth_value_eq (type1, chrec0, chrec1, value);
case NE_EXPR:
return prove_truth_value_ne (type1, chrec0, chrec1, value);
case LT_EXPR:
return prove_truth_value_lt (type1, chrec0, chrec1, value);
case LE_EXPR:
return prove_truth_value_le (type1, chrec0, chrec1, value);
case GT_EXPR:
return prove_truth_value_gt (type1, chrec0, chrec1, value);
case GE_EXPR:
return prove_truth_value_ge (type1, chrec0, chrec1, value);
default:
return false;
}
}
static inline enum tree_code
not_code (enum tree_code code)
{
switch (code)
{
case EQ_EXPR:
return NE_EXPR;
case NE_EXPR:
return EQ_EXPR;
case LT_EXPR:
return GE_EXPR;
case LE_EXPR:
return GT_EXPR;
case GT_EXPR:
return LE_EXPR;
case GE_EXPR:
return LT_EXPR;
default:
return code;
}
}
static bool
prove_truth_value (enum tree_code code,
unsigned loop_nb,
tree chrec0,
tree chrec1,
tree nb_iters_in_loop,
bool *value)
{
bool val = false;
tree nb_iters_in_then, nb_iters_in_else;
if (automatically_generated_chrec_p (nb_iters_in_loop))
return prove_truth_value_symbolic (code, chrec0, chrec1, value);
nb_iters_in_then = first_iteration_non_satisfying
(code, loop_nb, chrec0, chrec1);
nb_iters_in_else = first_iteration_non_satisfying
(not_code (code), loop_nb, chrec0, chrec1);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, " (nb_iters_in_loop = ");
print_generic_expr (dump_file, nb_iters_in_loop, 0);
fprintf (dump_file, ")\n (nb_iters_in_then = ");
print_generic_expr (dump_file, nb_iters_in_then, 0);
fprintf (dump_file, ")\n (nb_iters_in_else = ");
print_generic_expr (dump_file, nb_iters_in_else, 0);
fprintf (dump_file, ")\n");
}
if (chrec_contains_undetermined (nb_iters_in_then)
|| chrec_contains_undetermined (nb_iters_in_else))
return prove_truth_value_symbolic (code, chrec0, chrec1, value);
if (nb_iters_in_then == chrec_known
&& integer_zerop (nb_iters_in_else))
{
*value = true;
return true;
}
if (nb_iters_in_else == chrec_known
&& integer_zerop (nb_iters_in_then))
{
*value = false;
return true;
}
if (TREE_CODE (nb_iters_in_then) == INTEGER_CST
&& TREE_CODE (nb_iters_in_else) == INTEGER_CST)
{
if (integer_zerop (nb_iters_in_then)
&& tree_is_ge (nb_iters_in_else, nb_iters_in_loop, &val)
&& val)
{
*value = false;
return true;
}
if (integer_zerop (nb_iters_in_else)
&& tree_is_ge (nb_iters_in_then, nb_iters_in_loop, &val)
&& val)
{
*value = true;
return true;
}
}
return prove_truth_value_symbolic (code, chrec0, chrec1, value);
}
static inline void
remove_redundant_check (tree cond, bool value)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "Replacing one of the conditions.\n");
if (value == true)
COND_EXPR_COND (cond) = integer_one_node;
else
COND_EXPR_COND (cond) = integer_zero_node;
modify_stmt (cond);
}
static void
try_eliminate_check (tree cond)
{
bool value;
tree test, opnd0, opnd1;
tree chrec0, chrec1;
struct loop *loop = loop_containing_stmt (cond);
tree nb_iters = number_of_iterations_in_loop (loop);
enum tree_code code;
if (automatically_generated_chrec_p (nb_iters))
return;
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "(try_eliminate_check \n");
fprintf (dump_file, " (cond = ");
print_generic_expr (dump_file, cond, 0);
fprintf (dump_file, ")\n");
}
test = COND_EXPR_COND (cond);
code = TREE_CODE (test);
switch (code)
{
case SSA_NAME:
opnd0 = test;
chrec0 = instantiate_parameters
(loop, analyze_scalar_evolution (loop, opnd0));
if (chrec_contains_undetermined (chrec0))
goto end;
chrec1 = convert (TREE_TYPE (opnd0), integer_zero_node);
code = NE_EXPR;
break;
case LT_EXPR:
case LE_EXPR:
case GT_EXPR:
case GE_EXPR:
case EQ_EXPR:
case NE_EXPR:
opnd0 = TREE_OPERAND (test, 0);
opnd1 = TREE_OPERAND (test, 1);
chrec0 = instantiate_parameters
(loop, analyze_scalar_evolution (loop, opnd0));
chrec1 = instantiate_parameters
(loop, analyze_scalar_evolution (loop, opnd1));
if (chrec_contains_undetermined (chrec0)
|| chrec_contains_undetermined (chrec1))
goto end;
break;
default:
goto end;
}
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, " (test = ");
print_generic_expr (dump_file, test, 0);
fprintf (dump_file, ")\n (loop_nb = %d)\n", loop->num);
fprintf (dump_file, " (nb_iters = ");
print_generic_expr (dump_file, nb_iters, 0);
fprintf (dump_file, ")\n (chrec0 = ");
print_generic_expr (dump_file, chrec0, 0);
fprintf (dump_file, ")\n (chrec1 = ");
print_generic_expr (dump_file, chrec1, 0);
fprintf (dump_file, ")\n");
}
if (prove_truth_value (code, loop->num, chrec0, chrec1, nb_iters, &value))
remove_redundant_check (cond, value);
end:;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, ")\n");
}
static void
scan_all_loops_r (struct loop *loop)
{
if (!loop)
return;
scan_all_loops_r (loop->inner);
scan_all_loops_r (loop->next);
flow_loop_scan (loop, LOOP_EXIT_EDGES);
}
void
eliminate_redundant_checks (void)
{
basic_block bb;
block_stmt_iterator bsi;
#if 0
dump_file = stderr;
dump_flags = 31;
#endif
bb = BASIC_BLOCK (0);
if (bb && bb->loop_father)
{
scan_all_loops_r (bb->loop_father);
FOR_EACH_BB (bb)
{
struct loop *loop = bb->loop_father;
if (!loop->exit_edges
|| loop->exit_edges[0]->src == bb)
continue;
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
tree expr = bsi_stmt (bsi);
switch (TREE_CODE (expr))
{
case COND_EXPR:
try_eliminate_check (expr);
break;
default:
break;
}
}
}
}
}