diff options
author | jakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-10-31 08:45:27 +0000 |
---|---|---|
committer | jakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-10-31 08:45:27 +0000 |
commit | 8a2c774414018bc26dd5a7516a7b4ed6ae4e15aa (patch) | |
tree | 260b507c59290539996f49c796ce84539ec8f3cc /gcc/tree-ssa-reassoc.c | |
parent | b72fb85494e3eb7992cc6ff553524c61f399b923 (diff) | |
download | gcc-8a2c774414018bc26dd5a7516a7b4ed6ae4e15aa.tar.gz |
PR tree-optimization/19105
PR tree-optimization/21643
PR tree-optimization/46309
* tree-ssa-reassoc.c (init_range_entry): Add STMT argument
and use it if EXP is NULL.
(update_range_test): Handle OPCODE equal to ERROR_MARK
and oe->op NULL.
(optimize_range_tests): Likewise.
(final_range_test_p, suitable_cond_bb, no_side_effect_bb, get_ops,
maybe_optimize_range_tests): New functions.
(reassociate_bb): Call maybe_optimize_range_tests if last
stmt of bb is GIMPLE_COND that hasn't been visited yet.
* gcc.dg/pr19105.c: New test.
* gcc.dg/pr21643.c: New test.
* gcc.dg/pr46309-2.c: New test.
* gcc.c-torture/execute/pr46309.c: New test.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@193028 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tree-ssa-reassoc.c')
-rw-r--r-- | gcc/tree-ssa-reassoc.c | 657 |
1 files changed, 627 insertions, 30 deletions
diff --git a/gcc/tree-ssa-reassoc.c b/gcc/tree-ssa-reassoc.c index b54ca39d754..67c5c12e13c 100644 --- a/gcc/tree-ssa-reassoc.c +++ b/gcc/tree-ssa-reassoc.c @@ -1,5 +1,5 @@ /* Reassociation for trees. - Copyright (C) 2005, 2007, 2008, 2009, 2010, 2011 + Copyright (C) 2005, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. Contributed by Daniel Berlin <dan@dberlin.org> @@ -1713,10 +1713,12 @@ struct range_entry }; /* This is similar to make_range in fold-const.c, but on top of - GIMPLE instead of trees. */ + GIMPLE instead of trees. If EXP is non-NULL, it should be + an SSA_NAME and STMT argument is ignored, otherwise STMT + argument should be a GIMPLE_COND. */ static void -init_range_entry (struct range_entry *r, tree exp) +init_range_entry (struct range_entry *r, tree exp, gimple stmt) { int in_p; tree low, high; @@ -1727,7 +1729,8 @@ init_range_entry (struct range_entry *r, tree exp) r->strict_overflow_p = false; r->low = NULL_TREE; r->high = NULL_TREE; - if (TREE_CODE (exp) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (exp))) + if (exp != NULL_TREE + && (TREE_CODE (exp) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (exp)))) return; /* Start with simply saying "EXP != 0" and then look at the code of EXP @@ -1735,12 +1738,14 @@ init_range_entry (struct range_entry *r, tree exp) happen, but it doesn't seem worth worrying about this. We "continue" the outer loop when we've changed something; otherwise we "break" the switch, which will "break" the while. */ - low = build_int_cst (TREE_TYPE (exp), 0); + low = exp ? build_int_cst (TREE_TYPE (exp), 0) : boolean_false_node; high = low; in_p = 0; strict_overflow_p = false; is_bool = false; - if (TYPE_PRECISION (TREE_TYPE (exp)) == 1) + if (exp == NULL_TREE) + is_bool = true; + else if (TYPE_PRECISION (TREE_TYPE (exp)) == 1) { if (TYPE_UNSIGNED (TREE_TYPE (exp))) is_bool = true; @@ -1752,25 +1757,35 @@ init_range_entry (struct range_entry *r, tree exp) while (1) { - gimple stmt; enum tree_code code; tree arg0, arg1, exp_type; tree nexp; location_t loc; - if (TREE_CODE (exp) != SSA_NAME) - break; + if (exp != NULL_TREE) + { + if (TREE_CODE (exp) != SSA_NAME) + break; - stmt = SSA_NAME_DEF_STMT (exp); - if (!is_gimple_assign (stmt)) - break; + stmt = SSA_NAME_DEF_STMT (exp); + if (!is_gimple_assign (stmt)) + break; + + code = gimple_assign_rhs_code (stmt); + arg0 = gimple_assign_rhs1 (stmt); + arg1 = gimple_assign_rhs2 (stmt); + exp_type = TREE_TYPE (exp); + } + else + { + code = gimple_cond_code (stmt); + arg0 = gimple_cond_lhs (stmt); + arg1 = gimple_cond_rhs (stmt); + exp_type = boolean_type_node; + } - code = gimple_assign_rhs_code (stmt); - arg0 = gimple_assign_rhs1 (stmt); if (TREE_CODE (arg0) != SSA_NAME) break; - arg1 = gimple_assign_rhs2 (stmt); - exp_type = TREE_TYPE (exp); loc = gimple_location (stmt); switch (code) { @@ -1916,7 +1931,11 @@ range_entry_cmp (const void *a, const void *b) [EXP, IN_P, LOW, HIGH, STRICT_OVERFLOW_P] is a merged range for RANGE and OTHERRANGE through OTHERRANGE + COUNT - 1 ranges, OPCODE and OPS are arguments of optimize_range_tests. Return - true if the range merge has been successful. */ + true if the range merge has been successful. + If OPCODE is ERROR_MARK, this is called from within + maybe_optimize_range_tests and is performing inter-bb range optimization. + Changes should be then performed right away, and whether an op is + BIT_AND_EXPR or BIT_IOR_EXPR is found in oe->rank. */ static bool update_range_test (struct range_entry *range, struct range_entry *otherrange, @@ -1924,9 +1943,12 @@ update_range_test (struct range_entry *range, struct range_entry *otherrange, VEC (operand_entry_t, heap) **ops, tree exp, bool in_p, tree low, tree high, bool strict_overflow_p) { - tree op = VEC_index (operand_entry_t, *ops, range->idx)->op; - location_t loc = gimple_location (SSA_NAME_DEF_STMT (op)); - tree tem = build_range_check (loc, TREE_TYPE (op), exp, in_p, low, high); + operand_entry_t oe = VEC_index (oeprand_entry_t, *ops, range->idx); + tree op = oe->op; + gimple stmt = op ? SSA_NAME_DEF_STMT (op) : last_stmt (BASIC_BLOCK (oe->id)); + location_t loc = gimple_location (stmt); + tree optype = op ? TREE_TYPE (op) : boolean_type_node; + tree tem = build_range_check (loc, optype, exp, in_p, low, high); enum warn_strict_overflow_code wc = WARN_STRICT_OVERFLOW_COMPARISON; gimple_stmt_iterator gsi; @@ -1961,15 +1983,45 @@ update_range_test (struct range_entry *range, struct range_entry *otherrange, fprintf (dump_file, "\n"); } - if (opcode == BIT_IOR_EXPR) + if (opcode == BIT_IOR_EXPR + || (opcode == ERROR_MARK && oe->rank == BIT_IOR_EXPR)) tem = invert_truthvalue_loc (loc, tem); - tem = fold_convert_loc (loc, TREE_TYPE (op), tem); - gsi = gsi_for_stmt (SSA_NAME_DEF_STMT (op)); + tem = fold_convert_loc (loc, optype, tem); + gsi = gsi_for_stmt (stmt); tem = force_gimple_operand_gsi (&gsi, tem, true, NULL_TREE, true, GSI_SAME_STMT); - VEC_index (operand_entry_t, *ops, range->idx)->op = tem; + /* If doing inter-bb range test optimization, update the + stmts immediately. Start with changing the first range test + immediate use to the new value (TEM), or, if the first range + test is a GIMPLE_COND stmt, change that condition. */ + if (opcode == ERROR_MARK) + { + if (op) + { + imm_use_iterator iter; + use_operand_p use_p; + gimple use_stmt; + + FOR_EACH_IMM_USE_STMT (use_stmt, iter, op) + { + if (is_gimple_debug (use_stmt)) + continue; + FOR_EACH_IMM_USE_ON_STMT (use_p, iter) + SET_USE (use_p, tem); + update_stmt (use_stmt); + } + } + else + { + gimple_cond_set_code (stmt, NE_EXPR); + gimple_cond_set_lhs (stmt, tem); + gimple_cond_set_rhs (stmt, boolean_false_node); + update_stmt (stmt); + } + } + oe->op = tem; range->exp = exp; range->low = low; range->high = high; @@ -1978,7 +2030,84 @@ update_range_test (struct range_entry *range, struct range_entry *otherrange, for (range = otherrange; range < otherrange + count; range++) { - VEC_index (operand_entry_t, *ops, range->idx)->op = error_mark_node; + oe = VEC_index (oeprand_entry_t, *ops, range->idx); + /* Now change all the other range test immediate uses, so that + those tests will be optimized away. */ + if (opcode == ERROR_MARK) + { + if (oe->op) + { + imm_use_iterator iter; + use_operand_p use_p; + gimple use_stmt; + + FOR_EACH_IMM_USE_STMT (use_stmt, iter, oe->op) + { + if (is_gimple_debug (use_stmt)) + continue; + /* If imm use of _8 is a statement like _7 = _8 | _9;, + adjust it into _7 = _9;. */ + if (is_gimple_assign (use_stmt) + && gimple_assign_rhs_code (use_stmt) == oe->rank) + { + tree expr = NULL_TREE; + if (oe->op == gimple_assign_rhs1 (use_stmt)) + expr = gimple_assign_rhs2 (use_stmt); + else if (oe->op == gimple_assign_rhs2 (use_stmt)) + expr = gimple_assign_rhs1 (use_stmt); + if (expr + && expr != oe->op + && TREE_CODE (expr) == SSA_NAME) + { + gimple_stmt_iterator gsi2 = gsi_for_stmt (use_stmt); + gimple_assign_set_rhs_with_ops (&gsi2, SSA_NAME, + expr, NULL_TREE); + update_stmt (use_stmt); + continue; + } + } + /* If imm use of _8 is a statement like _7 = (int) _8;, + adjust it into _7 = 0; or _7 = 1;. */ + if (gimple_assign_cast_p (use_stmt) + && oe->op == gimple_assign_rhs1 (use_stmt)) + { + tree lhs = gimple_assign_lhs (use_stmt); + if (INTEGRAL_TYPE_P (TREE_TYPE (lhs))) + { + gimple_stmt_iterator gsi2 + = gsi_for_stmt (use_stmt); + tree expr = build_int_cst (TREE_TYPE (lhs), + oe->rank == BIT_IOR_EXPR + ? 0 : 1); + gimple_assign_set_rhs_with_ops (&gsi2, + INTEGER_CST, + expr, NULL_TREE); + update_stmt (use_stmt); + continue; + } + } + /* Otherwise replace the use with 0 or 1. */ + FOR_EACH_IMM_USE_ON_STMT (use_p, iter) + SET_USE (use_p, + build_int_cst (TREE_TYPE (oe->op), + oe->rank == BIT_IOR_EXPR + ? 0 : 1)); + update_stmt (use_stmt); + } + } + else + { + /* If range test was a GIMPLE_COND, simply change it + into an always false or always true condition. */ + stmt = last_stmt (BASIC_BLOCK (oe->id)); + if (oe->rank == BIT_IOR_EXPR) + gimple_cond_make_false (stmt); + else + gimple_cond_make_true (stmt); + update_stmt (stmt); + } + } + oe->op = error_mark_node; range->exp = NULL_TREE; } return true; @@ -1986,7 +2115,12 @@ update_range_test (struct range_entry *range, struct range_entry *otherrange, /* Optimize range tests, similarly how fold_range_test optimizes it on trees. The tree code for the binary - operation between all the operands is OPCODE. */ + operation between all the operands is OPCODE. + If OPCODE is ERROR_MARK, optimize_range_tests is called from within + maybe_optimize_range_tests for inter-bb range optimization. + In that case if oe->op is NULL, oe->id is bb->index whose + GIMPLE_COND is && or ||ed into the test, and oe->rank says + the actual opcode. */ static void optimize_range_tests (enum tree_code opcode, @@ -2003,11 +2137,14 @@ optimize_range_tests (enum tree_code opcode, ranges = XNEWVEC (struct range_entry, length); for (i = 0; i < length; i++) { + oe = VEC_index (operand_entry_t, *ops, i); ranges[i].idx = i; - init_range_entry (ranges + i, VEC_index (operand_entry_t, *ops, i)->op); + init_range_entry (ranges + i, oe->op, + oe->op ? NULL : last_stmt (BASIC_BLOCK (oe->id))); /* For | invert it now, we will invert it again before emitting the optimized expression. */ - if (opcode == BIT_IOR_EXPR) + if (opcode == BIT_IOR_EXPR + || (opcode == ERROR_MARK && oe->rank == BIT_IOR_EXPR)) ranges[i].in_p = !ranges[i].in_p; } @@ -2124,7 +2261,7 @@ optimize_range_tests (enum tree_code opcode, } } - if (any_changes) + if (any_changes && opcode != ERROR_MARK) { j = 0; FOR_EACH_VEC_ELT (operand_entry_t, *ops, i, oe) @@ -2141,6 +2278,462 @@ optimize_range_tests (enum tree_code opcode, XDELETEVEC (ranges); } +/* Return true if STMT is a cast like: + <bb N>: + ... + _123 = (int) _234; + + <bb M>: + # _345 = PHI <_123(N), 1(...), 1(...)> + where _234 has bool type, _123 has single use and + bb N has a single successor M. This is commonly used in + the last block of a range test. */ + +static bool +final_range_test_p (gimple stmt) +{ + basic_block bb, rhs_bb; + edge e; + tree lhs, rhs; + use_operand_p use_p; + gimple use_stmt; + + if (!gimple_assign_cast_p (stmt)) + return false; + bb = gimple_bb (stmt); + if (!single_succ_p (bb)) + return false; + e = single_succ_edge (bb); + if (e->flags & EDGE_COMPLEX) + return false; + + lhs = gimple_assign_lhs (stmt); + rhs = gimple_assign_rhs1 (stmt); + if (!INTEGRAL_TYPE_P (TREE_TYPE (lhs)) + || TREE_CODE (rhs) != SSA_NAME + || TREE_CODE (TREE_TYPE (rhs)) != BOOLEAN_TYPE) + return false; + + /* Test whether lhs is consumed only by a PHI in the only successor bb. */ + if (!single_imm_use (lhs, &use_p, &use_stmt)) + return false; + + if (gimple_code (use_stmt) != GIMPLE_PHI + || gimple_bb (use_stmt) != e->dest) + return false; + + /* And that the rhs is defined in the same loop. */ + rhs_bb = gimple_bb (SSA_NAME_DEF_STMT (rhs)); + if (rhs_bb == NULL + || !flow_bb_inside_loop_p (loop_containing_stmt (stmt), rhs_bb)) + return false; + + return true; +} + +/* Return true if BB is suitable basic block for inter-bb range test + optimization. If BACKWARD is true, BB should be the only predecessor + of TEST_BB, and *OTHER_BB is either NULL and filled by the routine, + or compared with to find a common basic block to which all conditions + branch to if true resp. false. If BACKWARD is false, TEST_BB should + be the only predecessor of BB. */ + +static bool +suitable_cond_bb (basic_block bb, basic_block test_bb, basic_block *other_bb, + bool backward) +{ + edge_iterator ei, ei2; + edge e, e2; + gimple stmt; + gimple_stmt_iterator gsi; + bool other_edge_seen = false; + bool is_cond; + + if (test_bb == bb) + return false; + /* Check last stmt first. */ + stmt = last_stmt (bb); + if (stmt == NULL + || (gimple_code (stmt) != GIMPLE_COND + && (backward || !final_range_test_p (stmt))) + || gimple_visited_p (stmt) + || stmt_could_throw_p (stmt) + || *other_bb == bb) + return false; + is_cond = gimple_code (stmt) == GIMPLE_COND; + if (is_cond) + { + /* If last stmt is GIMPLE_COND, verify that one of the succ edges + goes to the next bb (if BACKWARD, it is TEST_BB), and the other + to *OTHER_BB (if not set yet, try to find it out). */ + if (EDGE_COUNT (bb->succs) != 2) + return false; + FOR_EACH_EDGE (e, ei, bb->succs) + { + if (!(e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))) + return false; + if (e->dest == test_bb) + { + if (backward) + continue; + else + return false; + } + if (e->dest == bb) + return false; + if (*other_bb == NULL) + { + FOR_EACH_EDGE (e2, ei2, test_bb->succs) + if (!(e2->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))) + return false; + else if (e->dest == e2->dest) + *other_bb = e->dest; + if (*other_bb == NULL) + return false; + } + if (e->dest == *other_bb) + other_edge_seen = true; + else if (backward) + return false; + } + if (*other_bb == NULL || !other_edge_seen) + return false; + } + else if (single_succ (bb) != *other_bb) + return false; + + /* Now check all PHIs of *OTHER_BB. */ + e = find_edge (bb, *other_bb); + e2 = find_edge (test_bb, *other_bb); + for (gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple phi = gsi_stmt (gsi); + /* If both BB and TEST_BB end with GIMPLE_COND, all PHI arguments + corresponding to BB and TEST_BB predecessor must be the same. */ + if (!operand_equal_p (gimple_phi_arg_def (phi, e->dest_idx), + gimple_phi_arg_def (phi, e2->dest_idx), 0)) + { + /* Otherwise, if one of the blocks doesn't end with GIMPLE_COND, + one of the PHIs should have the lhs of the last stmt in + that block as PHI arg and that PHI should have 0 or 1 + corresponding to it in all other range test basic blocks + considered. */ + if (!is_cond) + { + if (gimple_phi_arg_def (phi, e->dest_idx) + == gimple_assign_lhs (stmt) + && (integer_zerop (gimple_phi_arg_def (phi, e2->dest_idx)) + || integer_onep (gimple_phi_arg_def (phi, + e2->dest_idx)))) + continue; + } + else + { + gimple test_last = last_stmt (test_bb); + if (gimple_code (test_last) != GIMPLE_COND + && gimple_phi_arg_def (phi, e2->dest_idx) + == gimple_assign_lhs (test_last) + && (integer_zerop (gimple_phi_arg_def (phi, e->dest_idx)) + || integer_onep (gimple_phi_arg_def (phi, e->dest_idx)))) + continue; + } + + return false; + } + } + return true; +} + +/* Return true if BB doesn't have side-effects that would disallow + range test optimization, all SSA_NAMEs set in the bb are consumed + in the bb and there are no PHIs. */ + +static bool +no_side_effect_bb (basic_block bb) +{ + gimple_stmt_iterator gsi; + gimple last; + + if (!gimple_seq_empty_p (phi_nodes (bb))) + return false; + last = last_stmt (bb); + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + tree lhs; + imm_use_iterator imm_iter; + use_operand_p use_p; + + if (is_gimple_debug (stmt)) + continue; + if (gimple_has_side_effects (stmt)) + return false; + if (stmt == last) + return true; + if (!is_gimple_assign (stmt)) + return false; + lhs = gimple_assign_lhs (stmt); + if (TREE_CODE (lhs) != SSA_NAME) + return false; + if (gimple_assign_rhs_could_trap_p (stmt)) + return false; + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, lhs) + { + gimple use_stmt = USE_STMT (use_p); + if (is_gimple_debug (use_stmt)) + continue; + if (gimple_bb (use_stmt) != bb) + return false; + } + } + return false; +} + +/* If VAR is set by CODE (BIT_{AND,IOR}_EXPR) which is reassociable, + return true and fill in *OPS recursively. */ + +static bool +get_ops (tree var, enum tree_code code, VEC(operand_entry_t, heap) **ops, + struct loop *loop) +{ + gimple stmt = SSA_NAME_DEF_STMT (var); + tree rhs[2]; + int i; + + if (!is_reassociable_op (stmt, code, loop)) + return false; + + rhs[0] = gimple_assign_rhs1 (stmt); + rhs[1] = gimple_assign_rhs2 (stmt); + gimple_set_visited (stmt, true); + for (i = 0; i < 2; i++) + if (TREE_CODE (rhs[i]) == SSA_NAME + && !get_ops (rhs[i], code, ops, loop) + && has_single_use (rhs[i])) + { + operand_entry_t oe = (operand_entry_t) pool_alloc (operand_entry_pool); + + oe->op = rhs[i]; + oe->rank = code; + oe->id = 0; + oe->count = 1; + VEC_safe_push (operand_entry_t, heap, *ops, oe); + } + return true; +} + +/* Inter-bb range test optimization. */ + +static void +maybe_optimize_range_tests (gimple stmt) +{ + basic_block first_bb = gimple_bb (stmt); + basic_block last_bb = first_bb; + basic_block other_bb = NULL; + basic_block bb; + edge_iterator ei; + edge e; + VEC(operand_entry_t, heap) *ops = NULL; + + /* Consider only basic blocks that end with GIMPLE_COND or + a cast statement satisfying final_range_test_p. All + but the last bb in the first_bb .. last_bb range + should end with GIMPLE_COND. */ + if (gimple_code (stmt) == GIMPLE_COND) + { + if (EDGE_COUNT (first_bb->succs) != 2) + return; + } + else if (final_range_test_p (stmt)) + other_bb = single_succ (first_bb); + else + return; + + if (stmt_could_throw_p (stmt)) + return; + + /* As relative ordering of post-dominator sons isn't fixed, + maybe_optimize_range_tests can be called first on any + bb in the range we want to optimize. So, start searching + backwards, if first_bb can be set to a predecessor. */ + while (single_pred_p (first_bb)) + { + basic_block pred_bb = single_pred (first_bb); + if (!suitable_cond_bb (pred_bb, first_bb, &other_bb, true)) + break; + if (!no_side_effect_bb (first_bb)) + break; + first_bb = pred_bb; + } + /* If first_bb is last_bb, other_bb hasn't been computed yet. + Before starting forward search in last_bb successors, find + out the other_bb. */ + if (first_bb == last_bb) + { + other_bb = NULL; + /* As non-GIMPLE_COND last stmt always terminates the range, + if forward search didn't discover anything, just give up. */ + if (gimple_code (stmt) != GIMPLE_COND) + return; + /* Look at both successors. Either it ends with a GIMPLE_COND + and satisfies suitable_cond_bb, or ends with a cast and + other_bb is that cast's successor. */ + FOR_EACH_EDGE (e, ei, first_bb->succs) + if (!(e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)) + || e->dest == first_bb) + return; + else if (single_pred_p (e->dest)) + { + stmt = last_stmt (e->dest); + if (stmt + && gimple_code (stmt) == GIMPLE_COND + && EDGE_COUNT (e->dest->succs) == 2) + { + if (suitable_cond_bb (first_bb, e->dest, &other_bb, true)) + break; + else + other_bb = NULL; + } + else if (stmt + && final_range_test_p (stmt) + && find_edge (first_bb, single_succ (e->dest))) + { + other_bb = single_succ (e->dest); + if (other_bb == first_bb) + other_bb = NULL; + } + } + if (other_bb == NULL) + return; + } + /* Now do the forward search, moving last_bb to successor bbs + that aren't other_bb. */ + while (EDGE_COUNT (last_bb->succs) == 2) + { + FOR_EACH_EDGE (e, ei, last_bb->succs) + if (e->dest != other_bb) + break; + if (e == NULL) + break; + if (!single_pred_p (e->dest)) + break; + if (!suitable_cond_bb (e->dest, last_bb, &other_bb, false)) + break; + if (!no_side_effect_bb (e->dest)) + break; + last_bb = e->dest; + } + if (first_bb == last_bb) + return; + /* Here basic blocks first_bb through last_bb's predecessor + end with GIMPLE_COND, all of them have one of the edges to + other_bb and another to another block in the range, + all blocks except first_bb don't have side-effects and + last_bb ends with either GIMPLE_COND, or cast satisfying + final_range_test_p. */ + for (bb = last_bb; ; bb = single_pred (bb)) + { + enum tree_code code; + tree lhs, rhs; + + e = find_edge (bb, other_bb); + stmt = last_stmt (bb); + gimple_set_visited (stmt, true); + if (gimple_code (stmt) != GIMPLE_COND) + { + use_operand_p use_p; + gimple phi; + edge e2; + unsigned int d; + + lhs = gimple_assign_lhs (stmt); + rhs = gimple_assign_rhs1 (stmt); + gcc_assert (bb == last_bb); + + /* stmt is + _123 = (int) _234; + + followed by: + <bb M>: + # _345 = PHI <_123(N), 1(...), 1(...)> + + or 0 instead of 1. If it is 0, the _234 + range test is anded together with all the + other range tests, if it is 1, it is ored with + them. */ + single_imm_use (lhs, &use_p, &phi); + gcc_assert (gimple_code (phi) == GIMPLE_PHI); + e2 = find_edge (first_bb, other_bb); + d = e2->dest_idx; + gcc_assert (gimple_phi_arg_def (phi, e->dest_idx) == lhs); + if (integer_zerop (gimple_phi_arg_def (phi, d))) + code = BIT_AND_EXPR; + else + { + gcc_checking_assert (integer_onep (gimple_phi_arg_def (phi, d))); + code = BIT_IOR_EXPR; + } + + /* If _234 SSA_NAME_DEF_STMT is + _234 = _567 | _789; + (or &, corresponding to 1/0 in the phi arguments, + push into ops the individual range test arguments + of the bitwise or resp. and, recursively. */ + if (!get_ops (rhs, code, &ops, + loop_containing_stmt (stmt)) + && has_single_use (rhs)) + { + /* Otherwise, push the _234 range test itself. */ + operand_entry_t oe + = (operand_entry_t) pool_alloc (operand_entry_pool); + + oe->op = rhs; + oe->rank = code; + oe->id = 0; + oe->count = 1; + VEC_safe_push (operand_entry_t, heap, ops, oe); + } + continue; + } + /* Otherwise stmt is GIMPLE_COND. */ + code = gimple_cond_code (stmt); + lhs = gimple_cond_lhs (stmt); + rhs = gimple_cond_rhs (stmt); + if (TREE_CODE (lhs) == SSA_NAME + && INTEGRAL_TYPE_P (TREE_TYPE (lhs)) + && ((code != EQ_EXPR && code != NE_EXPR) + || rhs != boolean_false_node + /* Either push into ops the individual bitwise + or resp. and operands, depending on which + edge is other_bb. */ + || !get_ops (lhs, (((e->flags & EDGE_TRUE_VALUE) == 0) + ^ (code == EQ_EXPR)) + ? BIT_AND_EXPR : BIT_IOR_EXPR, &ops, + loop_containing_stmt (stmt)))) + { + /* Or push the GIMPLE_COND stmt itself. */ + operand_entry_t oe + = (operand_entry_t) pool_alloc (operand_entry_pool); + + oe->op = NULL; + oe->rank = (e->flags & EDGE_TRUE_VALUE) + ? BIT_IOR_EXPR : BIT_AND_EXPR; + /* oe->op = NULL signs that there is no SSA_NAME + for the range test, and oe->id instead is the + basic block number, at which's end the GIMPLE_COND + is. */ + oe->id = bb->index; + oe->count = 1; + VEC_safe_push (operand_entry_t, heap, ops, oe); + } + if (bb == first_bb) + break; + } + if (VEC_length (operand_entry_t, ops) > 1) + optimize_range_tests (ERROR_MARK, &ops); + VEC_free (operand_entry_t, heap, ops); +} + /* Return true if OPERAND is defined by a PHI node which uses the LHS of STMT in it's operands. This is also known as a "destructive update" operation. */ @@ -3427,10 +4020,14 @@ reassociate_bb (basic_block bb) { gimple_stmt_iterator gsi; basic_block son; + gimple stmt = last_stmt (bb); + + if (stmt && !gimple_visited_p (stmt)) + maybe_optimize_range_tests (stmt); for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi)) { - gimple stmt = gsi_stmt (gsi); + stmt = gsi_stmt (gsi); if (is_gimple_assign (stmt) && !stmt_could_throw_p (stmt)) |