static enum ssa_prop_result copy_prop_visit_cond_stmt (gimple stmt, edge *taken_edge_p) { enum ssa_prop_result retval = SSA_PROP_VARYING; location_t loc = gimple_location (stmt); tree op0 = valueize_val (gimple_cond_lhs (stmt)); tree op1 = valueize_val (gimple_cond_rhs (stmt)); /* See if we can determine the predicate's value. */ if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Trying to determine truth value of "); fprintf (dump_file, "predicate "); print_gimple_stmt (dump_file, stmt, 0, 0); } /* Fold COND and see whether we get a useful result. */ tree folded_cond = fold_binary_loc (loc, gimple_cond_code (stmt), boolean_type_node, op0, op1); if (folded_cond) { basic_block bb = gimple_bb (stmt); *taken_edge_p = find_taken_edge (bb, folded_cond); if (*taken_edge_p) retval = SSA_PROP_INTERESTING; } if (dump_file && (dump_flags & TDF_DETAILS) && *taken_edge_p) fprintf (dump_file, "\nConditional will always take edge %d->%d\n", (*taken_edge_p)->src->index, (*taken_edge_p)->dest->index); return retval; }
CondExpr::CondExpr(gimple t) : Expression(t) { if (gimple_code(t) != GIMPLE_COND) throw BadGimpleException(t, "cond_expr"); _op = gimple_cond_code(t); _lhs = ValueFactory::INSTANCE.build(gimple_cond_lhs(t)); _rhs = ValueFactory::INSTANCE.build(gimple_cond_rhs(t)); /* tree a = gimple_cond_true_label(t); if (a != NULL && a != NULL_TREE) _then = ValueFactory::INSTANCE.build(gimple_cond_true_label(t)); a = gimple_cond_false_label(t); if (a != NULL && a != NULL_TREE) _else = ValueFactory::INSTANCE.build(gimple_cond_false_label(t)); */ }
static enum ssa_prop_result copy_prop_visit_cond_stmt (gimple stmt, edge *taken_edge_p) { enum ssa_prop_result retval = SSA_PROP_VARYING; location_t loc = gimple_location (stmt); tree op0 = gimple_cond_lhs (stmt); tree op1 = gimple_cond_rhs (stmt); /* The only conditionals that we may be able to compute statically are predicates involving two SSA_NAMEs. */ if (TREE_CODE (op0) == SSA_NAME && TREE_CODE (op1) == SSA_NAME) { op0 = valueize_val (op0); op1 = valueize_val (op1); /* See if we can determine the predicate's value. */ if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Trying to determine truth value of "); fprintf (dump_file, "predicate "); print_gimple_stmt (dump_file, stmt, 0, 0); } /* We can fold COND and get a useful result only when we have the same SSA_NAME on both sides of a comparison operator. */ if (op0 == op1) { tree folded_cond = fold_binary_loc (loc, gimple_cond_code (stmt), boolean_type_node, op0, op1); if (folded_cond) { basic_block bb = gimple_bb (stmt); *taken_edge_p = find_taken_edge (bb, folded_cond); if (*taken_edge_p) retval = SSA_PROP_INTERESTING; } } } if (dump_file && (dump_flags & TDF_DETAILS) && *taken_edge_p) fprintf (dump_file, "\nConditional will always take edge %d->%d\n", (*taken_edge_p)->src->index, (*taken_edge_p)->dest->index); return retval; }
static bool recognize_bits_test (gimple cond, tree *name, tree *bits) { gimple stmt; /* Get at the definition of the result of the bit test. */ if (gimple_cond_code (cond) != NE_EXPR || TREE_CODE (gimple_cond_lhs (cond)) != SSA_NAME || !integer_zerop (gimple_cond_rhs (cond))) return false; stmt = SSA_NAME_DEF_STMT (gimple_cond_lhs (cond)); if (!is_gimple_assign (stmt) || gimple_assign_rhs_code (stmt) != BIT_AND_EXPR) return false; *name = get_name_for_bit_test (gimple_assign_rhs1 (stmt)); *bits = gimple_assign_rhs2 (stmt); return true; }
static bool stmt_simple_for_scop_p (basic_block scop_entry, loop_p outermost_loop, gimple stmt, basic_block bb) { loop_p loop = bb->loop_father; gcc_assert (scop_entry); /* GIMPLE_ASM and GIMPLE_CALL may embed arbitrary side effects. Calls have side-effects, except those to const or pure functions. */ if (gimple_has_volatile_ops (stmt) || (gimple_code (stmt) == GIMPLE_CALL && !(gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE))) || (gimple_code (stmt) == GIMPLE_ASM)) return false; if (is_gimple_debug (stmt)) return true; if (!stmt_has_simple_data_refs_p (outermost_loop, stmt)) return false; switch (gimple_code (stmt)) { case GIMPLE_RETURN: case GIMPLE_LABEL: return true; case GIMPLE_COND: { /* We can handle all binary comparisons. Inequalities are also supported as they can be represented with union of polyhedra. */ enum tree_code code = gimple_cond_code (stmt); if (!(code == LT_EXPR || code == GT_EXPR || code == LE_EXPR || code == GE_EXPR || code == EQ_EXPR || code == NE_EXPR)) return false; for (unsigned i = 0; i < 2; ++i) { tree op = gimple_op (stmt, i); if (!graphite_can_represent_expr (scop_entry, loop, op) /* We can not handle REAL_TYPE. Failed for pr39260. */ || TREE_CODE (TREE_TYPE (op)) == REAL_TYPE) return false; } return true; } case GIMPLE_ASSIGN: case GIMPLE_CALL: return true; default: /* These nodes cut a new scope. */ return false; } return false; }
bool gimple_simplify (gimple stmt, code_helper *rcode, tree *ops, gimple_seq *seq, tree (*valueize)(tree)) { switch (gimple_code (stmt)) { case GIMPLE_ASSIGN: { enum tree_code code = gimple_assign_rhs_code (stmt); tree type = TREE_TYPE (gimple_assign_lhs (stmt)); switch (gimple_assign_rhs_class (stmt)) { case GIMPLE_SINGLE_RHS: if (code == REALPART_EXPR || code == IMAGPART_EXPR || code == VIEW_CONVERT_EXPR) { tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0); if (valueize && TREE_CODE (op0) == SSA_NAME) { tree tem = valueize (op0); if (tem) op0 = tem; } *rcode = code; ops[0] = op0; return gimple_resimplify1 (seq, rcode, type, ops, valueize); } else if (code == BIT_FIELD_REF) { tree rhs1 = gimple_assign_rhs1 (stmt); tree op0 = TREE_OPERAND (rhs1, 0); if (valueize && TREE_CODE (op0) == SSA_NAME) { tree tem = valueize (op0); if (tem) op0 = tem; } *rcode = code; ops[0] = op0; ops[1] = TREE_OPERAND (rhs1, 1); ops[2] = TREE_OPERAND (rhs1, 2); return gimple_resimplify3 (seq, rcode, type, ops, valueize); } else if (code == SSA_NAME && valueize) { tree op0 = gimple_assign_rhs1 (stmt); tree valueized = valueize (op0); if (!valueized || op0 == valueized) return false; ops[0] = valueized; *rcode = TREE_CODE (op0); return true; } break; case GIMPLE_UNARY_RHS: { tree rhs1 = gimple_assign_rhs1 (stmt); if (valueize && TREE_CODE (rhs1) == SSA_NAME) { tree tem = valueize (rhs1); if (tem) rhs1 = tem; } *rcode = code; ops[0] = rhs1; return gimple_resimplify1 (seq, rcode, type, ops, valueize); } case GIMPLE_BINARY_RHS: { tree rhs1 = gimple_assign_rhs1 (stmt); if (valueize && TREE_CODE (rhs1) == SSA_NAME) { tree tem = valueize (rhs1); if (tem) rhs1 = tem; } tree rhs2 = gimple_assign_rhs2 (stmt); if (valueize && TREE_CODE (rhs2) == SSA_NAME) { tree tem = valueize (rhs2); if (tem) rhs2 = tem; } *rcode = code; ops[0] = rhs1; ops[1] = rhs2; return gimple_resimplify2 (seq, rcode, type, ops, valueize); } case GIMPLE_TERNARY_RHS: { tree rhs1 = gimple_assign_rhs1 (stmt); if (valueize && TREE_CODE (rhs1) == SSA_NAME) { tree tem = valueize (rhs1); if (tem) rhs1 = tem; } tree rhs2 = gimple_assign_rhs2 (stmt); if (valueize && TREE_CODE (rhs2) == SSA_NAME) { tree tem = valueize (rhs2); if (tem) rhs2 = tem; } tree rhs3 = gimple_assign_rhs3 (stmt); if (valueize && TREE_CODE (rhs3) == SSA_NAME) { tree tem = valueize (rhs3); if (tem) rhs3 = tem; } *rcode = code; ops[0] = rhs1; ops[1] = rhs2; ops[2] = rhs3; return gimple_resimplify3 (seq, rcode, type, ops, valueize); } default: gcc_unreachable (); } break; } case GIMPLE_CALL: /* ??? This way we can't simplify calls with side-effects. */ if (gimple_call_lhs (stmt) != NULL_TREE) { tree fn = gimple_call_fn (stmt); /* ??? Internal function support missing. */ if (!fn) return false; if (valueize && TREE_CODE (fn) == SSA_NAME) { tree tem = valueize (fn); if (tem) fn = tem; } if (!fn || TREE_CODE (fn) != ADDR_EXPR || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL || DECL_BUILT_IN_CLASS (TREE_OPERAND (fn, 0)) != BUILT_IN_NORMAL || !builtin_decl_implicit (DECL_FUNCTION_CODE (TREE_OPERAND (fn, 0))) || !gimple_builtin_call_types_compatible_p (stmt, TREE_OPERAND (fn, 0))) return false; tree decl = TREE_OPERAND (fn, 0); tree type = TREE_TYPE (gimple_call_lhs (stmt)); switch (gimple_call_num_args (stmt)) { case 1: { tree arg1 = gimple_call_arg (stmt, 0); if (valueize && TREE_CODE (arg1) == SSA_NAME) { tree tem = valueize (arg1); if (tem) arg1 = tem; } *rcode = DECL_FUNCTION_CODE (decl); ops[0] = arg1; return gimple_resimplify1 (seq, rcode, type, ops, valueize); } case 2: { tree arg1 = gimple_call_arg (stmt, 0); if (valueize && TREE_CODE (arg1) == SSA_NAME) { tree tem = valueize (arg1); if (tem) arg1 = tem; } tree arg2 = gimple_call_arg (stmt, 1); if (valueize && TREE_CODE (arg2) == SSA_NAME) { tree tem = valueize (arg2); if (tem) arg2 = tem; } *rcode = DECL_FUNCTION_CODE (decl); ops[0] = arg1; ops[1] = arg2; return gimple_resimplify2 (seq, rcode, type, ops, valueize); } case 3: { tree arg1 = gimple_call_arg (stmt, 0); if (valueize && TREE_CODE (arg1) == SSA_NAME) { tree tem = valueize (arg1); if (tem) arg1 = tem; } tree arg2 = gimple_call_arg (stmt, 1); if (valueize && TREE_CODE (arg2) == SSA_NAME) { tree tem = valueize (arg2); if (tem) arg2 = tem; } tree arg3 = gimple_call_arg (stmt, 2); if (valueize && TREE_CODE (arg3) == SSA_NAME) { tree tem = valueize (arg3); if (tem) arg3 = tem; } *rcode = DECL_FUNCTION_CODE (decl); ops[0] = arg1; ops[1] = arg2; ops[2] = arg3; return gimple_resimplify3 (seq, rcode, type, ops, valueize); } default: return false; } } break; case GIMPLE_COND: { tree lhs = gimple_cond_lhs (stmt); if (valueize && TREE_CODE (lhs) == SSA_NAME) { tree tem = valueize (lhs); if (tem) lhs = tem; } tree rhs = gimple_cond_rhs (stmt); if (valueize && TREE_CODE (rhs) == SSA_NAME) { tree tem = valueize (rhs); if (tem) rhs = tem; } *rcode = gimple_cond_code (stmt); ops[0] = lhs; ops[1] = rhs; return gimple_resimplify2 (seq, rcode, boolean_type_node, ops, valueize); } default: break; } return false; }
static bool ifcombine_iforif (basic_block inner_cond_bb, basic_block outer_cond_bb) { gimple inner_cond, outer_cond; tree name1, name2, bits1, bits2; inner_cond = last_stmt (inner_cond_bb); if (!inner_cond || gimple_code (inner_cond) != GIMPLE_COND) return false; outer_cond = last_stmt (outer_cond_bb); if (!outer_cond || gimple_code (outer_cond) != GIMPLE_COND) return false; /* See if we have two bit tests of the same name in both tests. In that case remove the outer test and change the inner one to test for name & (bits1 | bits2) != 0. */ if (recognize_bits_test (inner_cond, &name1, &bits1) && recognize_bits_test (outer_cond, &name2, &bits2)) { gimple_stmt_iterator gsi; tree t; /* Find the common name which is bit-tested. */ if (name1 == name2) ; else if (bits1 == bits2) { t = name2; name2 = bits2; bits2 = t; t = name1; name1 = bits1; bits1 = t; } else if (name1 == bits2) { t = name2; name2 = bits2; bits2 = t; } else if (bits1 == name2) { t = name1; name1 = bits1; bits1 = t; } else return false; /* As we strip non-widening conversions in finding a common name that is tested make sure to end up with an integral type for building the bit operations. */ if (TYPE_PRECISION (TREE_TYPE (bits1)) >= TYPE_PRECISION (TREE_TYPE (bits2))) { bits1 = fold_convert (unsigned_type_for (TREE_TYPE (bits1)), bits1); name1 = fold_convert (TREE_TYPE (bits1), name1); bits2 = fold_convert (unsigned_type_for (TREE_TYPE (bits2)), bits2); bits2 = fold_convert (TREE_TYPE (bits1), bits2); } else { bits2 = fold_convert (unsigned_type_for (TREE_TYPE (bits2)), bits2); name1 = fold_convert (TREE_TYPE (bits2), name1); bits1 = fold_convert (unsigned_type_for (TREE_TYPE (bits1)), bits1); bits1 = fold_convert (TREE_TYPE (bits2), bits1); } /* Do it. */ gsi = gsi_for_stmt (inner_cond); t = fold_build2 (BIT_IOR_EXPR, TREE_TYPE (name1), bits1, bits2); t = force_gimple_operand_gsi (&gsi, t, true, NULL_TREE, true, GSI_SAME_STMT); t = fold_build2 (BIT_AND_EXPR, TREE_TYPE (name1), name1, t); t = force_gimple_operand_gsi (&gsi, t, true, NULL_TREE, true, GSI_SAME_STMT); t = fold_build2 (NE_EXPR, boolean_type_node, t, build_int_cst (TREE_TYPE (t), 0)); gimple_cond_set_condition_from_tree (inner_cond, t); update_stmt (inner_cond); /* Leave CFG optimization to cfg_cleanup. */ gimple_cond_set_condition_from_tree (outer_cond, boolean_false_node); update_stmt (outer_cond); if (dump_file) { fprintf (dump_file, "optimizing bits or bits test to "); print_generic_expr (dump_file, name1, 0); fprintf (dump_file, " & T != 0\nwith temporary T = "); print_generic_expr (dump_file, bits1, 0); fprintf (dump_file, " | "); print_generic_expr (dump_file, bits2, 0); fprintf (dump_file, "\n"); } return true; } /* See if we have two comparisons that we can merge into one. This happens for C++ operator overloading where for example GE_EXPR is implemented as GT_EXPR || EQ_EXPR. */ else if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison && operand_equal_p (gimple_cond_lhs (inner_cond), gimple_cond_lhs (outer_cond), 0) && operand_equal_p (gimple_cond_rhs (inner_cond), gimple_cond_rhs (outer_cond), 0)) { enum tree_code code1 = gimple_cond_code (inner_cond); enum tree_code code2 = gimple_cond_code (outer_cond); enum tree_code code; tree t; #define CHK(a,b) ((code1 == a ## _EXPR && code2 == b ## _EXPR) \ || (code2 == a ## _EXPR && code1 == b ## _EXPR)) /* Merge the two condition codes if possible. */ if (code1 == code2) code = code1; else if (CHK (EQ, LT)) code = LE_EXPR; else if (CHK (EQ, GT)) code = GE_EXPR; else if (CHK (LT, LE)) code = LE_EXPR; else if (CHK (GT, GE)) code = GE_EXPR; else if (INTEGRAL_TYPE_P (TREE_TYPE (gimple_cond_lhs (inner_cond))) || flag_unsafe_math_optimizations) { if (CHK (LT, GT)) code = NE_EXPR; else if (CHK (LT, NE)) code = NE_EXPR; else if (CHK (GT, NE)) code = NE_EXPR; else return false; } /* We could check for combinations leading to trivial true/false. */ else return false; #undef CHK /* Do it. */ t = fold_build2 (code, boolean_type_node, gimple_cond_lhs (outer_cond), gimple_cond_rhs (outer_cond)); t = canonicalize_cond_expr_cond (t); if (!t) return false; gimple_cond_set_condition_from_tree (inner_cond, t); update_stmt (inner_cond); /* Leave CFG optimization to cfg_cleanup. */ gimple_cond_set_condition_from_tree (outer_cond, boolean_false_node); update_stmt (outer_cond); if (dump_file) { fprintf (dump_file, "optimizing two comparisons to "); print_generic_expr (dump_file, t, 0); fprintf (dump_file, "\n"); } return true; } return false; }
static bool recognize_single_bit_test (gimple cond, tree *name, tree *bit) { gimple stmt; /* Get at the definition of the result of the bit test. */ if (gimple_cond_code (cond) != NE_EXPR || TREE_CODE (gimple_cond_lhs (cond)) != SSA_NAME || !integer_zerop (gimple_cond_rhs (cond))) return false; stmt = SSA_NAME_DEF_STMT (gimple_cond_lhs (cond)); if (!is_gimple_assign (stmt)) return false; /* Look at which bit is tested. One form to recognize is D.1985_5 = state_3(D) >> control1_4(D); D.1986_6 = (int) D.1985_5; D.1987_7 = op0 & 1; if (D.1987_7 != 0) */ if (gimple_assign_rhs_code (stmt) == BIT_AND_EXPR && integer_onep (gimple_assign_rhs2 (stmt)) && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME) { tree orig_name = gimple_assign_rhs1 (stmt); /* Look through copies and conversions to eventually find the stmt that computes the shift. */ stmt = SSA_NAME_DEF_STMT (orig_name); while (is_gimple_assign (stmt) && ((CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt)) && (TYPE_PRECISION (TREE_TYPE (gimple_assign_lhs (stmt))) <= TYPE_PRECISION (TREE_TYPE (gimple_assign_rhs1 (stmt))))) || gimple_assign_ssa_name_copy_p (stmt))) stmt = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt)); /* If we found such, decompose it. */ if (is_gimple_assign (stmt) && gimple_assign_rhs_code (stmt) == RSHIFT_EXPR) { /* op0 & (1 << op1) */ *bit = gimple_assign_rhs2 (stmt); *name = gimple_assign_rhs1 (stmt); } else { /* t & 1 */ *bit = integer_zero_node; *name = get_name_for_bit_test (orig_name); } return true; } /* Another form is D.1987_7 = op0 & (1 << CST) if (D.1987_7 != 0) */ if (gimple_assign_rhs_code (stmt) == BIT_AND_EXPR && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME && integer_pow2p (gimple_assign_rhs2 (stmt))) { *name = gimple_assign_rhs1 (stmt); *bit = build_int_cst (integer_type_node, tree_log2 (gimple_assign_rhs2 (stmt))); return true; } /* Another form is D.1986_6 = 1 << control1_4(D) D.1987_7 = op0 & D.1986_6 if (D.1987_7 != 0) */ if (gimple_assign_rhs_code (stmt) == BIT_AND_EXPR && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME && TREE_CODE (gimple_assign_rhs2 (stmt)) == SSA_NAME) { gimple tmp; /* Both arguments of the BIT_AND_EXPR can be the single-bit specifying expression. */ tmp = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt)); if (is_gimple_assign (tmp) && gimple_assign_rhs_code (tmp) == LSHIFT_EXPR && integer_onep (gimple_assign_rhs1 (tmp))) { *name = gimple_assign_rhs2 (stmt); *bit = gimple_assign_rhs2 (tmp); return true; } tmp = SSA_NAME_DEF_STMT (gimple_assign_rhs2 (stmt)); if (is_gimple_assign (tmp) && gimple_assign_rhs_code (tmp) == LSHIFT_EXPR && integer_onep (gimple_assign_rhs1 (tmp))) { *name = gimple_assign_rhs1 (stmt); *bit = gimple_assign_rhs2 (tmp); return true; } } return false; }
static int forward_propagate_into_gimple_cond (gimple stmt) { int did_something = 0; do { tree tmp = NULL_TREE; tree name, rhs0 = NULL_TREE, rhs1 = NULL_TREE; gimple def_stmt; bool single_use0_p = false, single_use1_p = false; enum tree_code code = gimple_cond_code (stmt); /* We can do tree combining on SSA_NAME and comparison expressions. */ if (TREE_CODE_CLASS (gimple_cond_code (stmt)) == tcc_comparison && TREE_CODE (gimple_cond_lhs (stmt)) == SSA_NAME) { /* For comparisons use the first operand, that is likely to simplify comparisons against constants. */ name = gimple_cond_lhs (stmt); def_stmt = get_prop_source_stmt (name, false, &single_use0_p); if (def_stmt && can_propagate_from (def_stmt)) { tree op1 = gimple_cond_rhs (stmt); rhs0 = rhs_to_tree (TREE_TYPE (op1), def_stmt); tmp = combine_cond_expr_cond (code, boolean_type_node, rhs0, op1, !single_use0_p); } /* If that wasn't successful, try the second operand. */ if (tmp == NULL_TREE && TREE_CODE (gimple_cond_rhs (stmt)) == SSA_NAME) { tree op0 = gimple_cond_lhs (stmt); name = gimple_cond_rhs (stmt); def_stmt = get_prop_source_stmt (name, false, &single_use1_p); if (!def_stmt || !can_propagate_from (def_stmt)) return did_something; rhs1 = rhs_to_tree (TREE_TYPE (op0), def_stmt); tmp = combine_cond_expr_cond (code, boolean_type_node, op0, rhs1, !single_use1_p); } /* If that wasn't successful either, try both operands. */ if (tmp == NULL_TREE && rhs0 != NULL_TREE && rhs1 != NULL_TREE) tmp = combine_cond_expr_cond (code, boolean_type_node, rhs0, fold_convert (TREE_TYPE (rhs0), rhs1), !(single_use0_p && single_use1_p)); } if (tmp) { if (dump_file && tmp) { tree cond = build2 (gimple_cond_code (stmt), boolean_type_node, gimple_cond_lhs (stmt), gimple_cond_rhs (stmt)); fprintf (dump_file, " Replaced '"); print_generic_expr (dump_file, cond, 0); fprintf (dump_file, "' with '"); print_generic_expr (dump_file, tmp, 0); fprintf (dump_file, "'\n"); } gimple_cond_set_condition_from_tree (stmt, unshare_expr (tmp)); update_stmt (stmt); /* Remove defining statements. */ remove_prop_source_from_use (name, NULL); if (is_gimple_min_invariant (tmp)) did_something = 2; else if (did_something == 0) did_something = 1; /* Continue combining. */ continue; } break; } while (1); return did_something; }
static bool ifcombine_ifandif (basic_block inner_cond_bb, bool inner_inv, basic_block outer_cond_bb, bool outer_inv, bool result_inv) { gimple_stmt_iterator gsi; gimple inner_stmt, outer_stmt; gcond *inner_cond, *outer_cond; tree name1, name2, bit1, bit2, bits1, bits2; inner_stmt = last_stmt (inner_cond_bb); if (!inner_stmt || gimple_code (inner_stmt) != GIMPLE_COND) return false; inner_cond = as_a <gcond *> (inner_stmt); outer_stmt = last_stmt (outer_cond_bb); if (!outer_stmt || gimple_code (outer_stmt) != GIMPLE_COND) return false; outer_cond = as_a <gcond *> (outer_stmt); /* See if we test a single bit of the same name in both tests. In that case remove the outer test, merging both else edges, and change the inner one to test for name & (bit1 | bit2) == (bit1 | bit2). */ if (recognize_single_bit_test (inner_cond, &name1, &bit1, inner_inv) && recognize_single_bit_test (outer_cond, &name2, &bit2, outer_inv) && name1 == name2) { tree t, t2; /* Do it. */ gsi = gsi_for_stmt (inner_cond); t = fold_build2 (LSHIFT_EXPR, TREE_TYPE (name1), build_int_cst (TREE_TYPE (name1), 1), bit1); t2 = fold_build2 (LSHIFT_EXPR, TREE_TYPE (name1), build_int_cst (TREE_TYPE (name1), 1), bit2); t = fold_build2 (BIT_IOR_EXPR, TREE_TYPE (name1), t, t2); t = force_gimple_operand_gsi (&gsi, t, true, NULL_TREE, true, GSI_SAME_STMT); t2 = fold_build2 (BIT_AND_EXPR, TREE_TYPE (name1), name1, t); t2 = force_gimple_operand_gsi (&gsi, t2, true, NULL_TREE, true, GSI_SAME_STMT); t = fold_build2 (result_inv ? NE_EXPR : EQ_EXPR, boolean_type_node, t2, t); t = canonicalize_cond_expr_cond (t); if (!t) return false; gimple_cond_set_condition_from_tree (inner_cond, t); update_stmt (inner_cond); /* Leave CFG optimization to cfg_cleanup. */ gimple_cond_set_condition_from_tree (outer_cond, outer_inv ? boolean_false_node : boolean_true_node); update_stmt (outer_cond); if (dump_file) { fprintf (dump_file, "optimizing double bit test to "); print_generic_expr (dump_file, name1, 0); fprintf (dump_file, " & T == T\nwith temporary T = (1 << "); print_generic_expr (dump_file, bit1, 0); fprintf (dump_file, ") | (1 << "); print_generic_expr (dump_file, bit2, 0); fprintf (dump_file, ")\n"); } return true; } /* See if we have two bit tests of the same name in both tests. In that case remove the outer test and change the inner one to test for name & (bits1 | bits2) != 0. */ else if (recognize_bits_test (inner_cond, &name1, &bits1, !inner_inv) && recognize_bits_test (outer_cond, &name2, &bits2, !outer_inv)) { gimple_stmt_iterator gsi; tree t; /* Find the common name which is bit-tested. */ if (name1 == name2) ; else if (bits1 == bits2) { t = name2; name2 = bits2; bits2 = t; t = name1; name1 = bits1; bits1 = t; } else if (name1 == bits2) { t = name2; name2 = bits2; bits2 = t; } else if (bits1 == name2) { t = name1; name1 = bits1; bits1 = t; } else return false; /* As we strip non-widening conversions in finding a common name that is tested make sure to end up with an integral type for building the bit operations. */ if (TYPE_PRECISION (TREE_TYPE (bits1)) >= TYPE_PRECISION (TREE_TYPE (bits2))) { bits1 = fold_convert (unsigned_type_for (TREE_TYPE (bits1)), bits1); name1 = fold_convert (TREE_TYPE (bits1), name1); bits2 = fold_convert (unsigned_type_for (TREE_TYPE (bits2)), bits2); bits2 = fold_convert (TREE_TYPE (bits1), bits2); } else { bits2 = fold_convert (unsigned_type_for (TREE_TYPE (bits2)), bits2); name1 = fold_convert (TREE_TYPE (bits2), name1); bits1 = fold_convert (unsigned_type_for (TREE_TYPE (bits1)), bits1); bits1 = fold_convert (TREE_TYPE (bits2), bits1); } /* Do it. */ gsi = gsi_for_stmt (inner_cond); t = fold_build2 (BIT_IOR_EXPR, TREE_TYPE (name1), bits1, bits2); t = force_gimple_operand_gsi (&gsi, t, true, NULL_TREE, true, GSI_SAME_STMT); t = fold_build2 (BIT_AND_EXPR, TREE_TYPE (name1), name1, t); t = force_gimple_operand_gsi (&gsi, t, true, NULL_TREE, true, GSI_SAME_STMT); t = fold_build2 (result_inv ? NE_EXPR : EQ_EXPR, boolean_type_node, t, build_int_cst (TREE_TYPE (t), 0)); t = canonicalize_cond_expr_cond (t); if (!t) return false; gimple_cond_set_condition_from_tree (inner_cond, t); update_stmt (inner_cond); /* Leave CFG optimization to cfg_cleanup. */ gimple_cond_set_condition_from_tree (outer_cond, outer_inv ? boolean_false_node : boolean_true_node); update_stmt (outer_cond); if (dump_file) { fprintf (dump_file, "optimizing bits or bits test to "); print_generic_expr (dump_file, name1, 0); fprintf (dump_file, " & T != 0\nwith temporary T = "); print_generic_expr (dump_file, bits1, 0); fprintf (dump_file, " | "); print_generic_expr (dump_file, bits2, 0); fprintf (dump_file, "\n"); } return true; } /* See if we have two comparisons that we can merge into one. */ else if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison) { tree t; enum tree_code inner_cond_code = gimple_cond_code (inner_cond); enum tree_code outer_cond_code = gimple_cond_code (outer_cond); /* Invert comparisons if necessary (and possible). */ if (inner_inv) inner_cond_code = invert_tree_comparison (inner_cond_code, HONOR_NANS (gimple_cond_lhs (inner_cond))); if (inner_cond_code == ERROR_MARK) return false; if (outer_inv) outer_cond_code = invert_tree_comparison (outer_cond_code, HONOR_NANS (gimple_cond_lhs (outer_cond))); if (outer_cond_code == ERROR_MARK) return false; /* Don't return false so fast, try maybe_fold_or_comparisons? */ if (!(t = maybe_fold_and_comparisons (inner_cond_code, gimple_cond_lhs (inner_cond), gimple_cond_rhs (inner_cond), outer_cond_code, gimple_cond_lhs (outer_cond), gimple_cond_rhs (outer_cond)))) { tree t1, t2; gimple_stmt_iterator gsi; if (!LOGICAL_OP_NON_SHORT_CIRCUIT) return false; /* Only do this optimization if the inner bb contains only the conditional. */ if (!gsi_one_before_end_p (gsi_start_nondebug_after_labels_bb (inner_cond_bb))) return false; t1 = fold_build2_loc (gimple_location (inner_cond), inner_cond_code, boolean_type_node, gimple_cond_lhs (inner_cond), gimple_cond_rhs (inner_cond)); t2 = fold_build2_loc (gimple_location (outer_cond), outer_cond_code, boolean_type_node, gimple_cond_lhs (outer_cond), gimple_cond_rhs (outer_cond)); t = fold_build2_loc (gimple_location (inner_cond), TRUTH_AND_EXPR, boolean_type_node, t1, t2); if (result_inv) { t = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (t), t); result_inv = false; } gsi = gsi_for_stmt (inner_cond); t = force_gimple_operand_gsi_1 (&gsi, t, is_gimple_condexpr, NULL, true, GSI_SAME_STMT); } if (result_inv) t = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (t), t); t = canonicalize_cond_expr_cond (t); if (!t) return false; gimple_cond_set_condition_from_tree (inner_cond, t); update_stmt (inner_cond); /* Leave CFG optimization to cfg_cleanup. */ gimple_cond_set_condition_from_tree (outer_cond, outer_inv ? boolean_false_node : boolean_true_node); update_stmt (outer_cond); if (dump_file) { fprintf (dump_file, "optimizing two comparisons to "); print_generic_expr (dump_file, t, 0); fprintf (dump_file, "\n"); } return true; } return false; }
expr_hash_elt::expr_hash_elt (gimple *stmt, tree orig_lhs) { enum gimple_code code = gimple_code (stmt); struct hashable_expr *expr = this->expr (); if (code == GIMPLE_ASSIGN) { enum tree_code subcode = gimple_assign_rhs_code (stmt); switch (get_gimple_rhs_class (subcode)) { case GIMPLE_SINGLE_RHS: expr->kind = EXPR_SINGLE; expr->type = TREE_TYPE (gimple_assign_rhs1 (stmt)); expr->ops.single.rhs = gimple_assign_rhs1 (stmt); break; case GIMPLE_UNARY_RHS: expr->kind = EXPR_UNARY; expr->type = TREE_TYPE (gimple_assign_lhs (stmt)); if (CONVERT_EXPR_CODE_P (subcode)) subcode = NOP_EXPR; expr->ops.unary.op = subcode; expr->ops.unary.opnd = gimple_assign_rhs1 (stmt); break; case GIMPLE_BINARY_RHS: expr->kind = EXPR_BINARY; expr->type = TREE_TYPE (gimple_assign_lhs (stmt)); expr->ops.binary.op = subcode; expr->ops.binary.opnd0 = gimple_assign_rhs1 (stmt); expr->ops.binary.opnd1 = gimple_assign_rhs2 (stmt); break; case GIMPLE_TERNARY_RHS: expr->kind = EXPR_TERNARY; expr->type = TREE_TYPE (gimple_assign_lhs (stmt)); expr->ops.ternary.op = subcode; expr->ops.ternary.opnd0 = gimple_assign_rhs1 (stmt); expr->ops.ternary.opnd1 = gimple_assign_rhs2 (stmt); expr->ops.ternary.opnd2 = gimple_assign_rhs3 (stmt); break; default: gcc_unreachable (); } } else if (code == GIMPLE_COND) { expr->type = boolean_type_node; expr->kind = EXPR_BINARY; expr->ops.binary.op = gimple_cond_code (stmt); expr->ops.binary.opnd0 = gimple_cond_lhs (stmt); expr->ops.binary.opnd1 = gimple_cond_rhs (stmt); } else if (gcall *call_stmt = dyn_cast <gcall *> (stmt)) { size_t nargs = gimple_call_num_args (call_stmt); size_t i; gcc_assert (gimple_call_lhs (call_stmt)); expr->type = TREE_TYPE (gimple_call_lhs (call_stmt)); expr->kind = EXPR_CALL; expr->ops.call.fn_from = call_stmt; if (gimple_call_flags (call_stmt) & (ECF_CONST | ECF_PURE)) expr->ops.call.pure = true; else expr->ops.call.pure = false; expr->ops.call.nargs = nargs; expr->ops.call.args = XCNEWVEC (tree, nargs); for (i = 0; i < nargs; i++) expr->ops.call.args[i] = gimple_call_arg (call_stmt, i); } else if (gswitch *swtch_stmt = dyn_cast <gswitch *> (stmt)) { expr->type = TREE_TYPE (gimple_switch_index (swtch_stmt)); expr->kind = EXPR_SINGLE; expr->ops.single.rhs = gimple_switch_index (swtch_stmt); } else if (code == GIMPLE_GOTO) { expr->type = TREE_TYPE (gimple_goto_dest (stmt)); expr->kind = EXPR_SINGLE; expr->ops.single.rhs = gimple_goto_dest (stmt); } else if (code == GIMPLE_PHI) { size_t nargs = gimple_phi_num_args (stmt); size_t i; expr->type = TREE_TYPE (gimple_phi_result (stmt)); expr->kind = EXPR_PHI; expr->ops.phi.nargs = nargs; expr->ops.phi.args = XCNEWVEC (tree, nargs); for (i = 0; i < nargs; i++) expr->ops.phi.args[i] = gimple_phi_arg_def (stmt, i); } else gcc_unreachable (); m_lhs = orig_lhs; m_vop = gimple_vuse (stmt); m_hash = avail_expr_hash (this); m_stamp = this; }
static bool abs_replacement (basic_block cond_bb, basic_block middle_bb, edge e0 ATTRIBUTE_UNUSED, edge e1, gimple phi, tree arg0, tree arg1) { tree result; gimple new_stmt, cond; gimple_stmt_iterator gsi; edge true_edge, false_edge; gimple assign; edge e; tree rhs, lhs; bool negate; enum tree_code cond_code; /* If the type says honor signed zeros we cannot do this optimization. */ if (HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg1)))) return false; /* OTHER_BLOCK must have only one executable statement which must have the form arg0 = -arg1 or arg1 = -arg0. */ assign = last_and_only_stmt (middle_bb); /* If we did not find the proper negation assignment, then we can not optimize. */ if (assign == NULL) return false; /* If we got here, then we have found the only executable statement in OTHER_BLOCK. If it is anything other than arg = -arg1 or arg1 = -arg0, then we can not optimize. */ if (gimple_code (assign) != GIMPLE_ASSIGN) return false; lhs = gimple_assign_lhs (assign); if (gimple_assign_rhs_code (assign) != NEGATE_EXPR) return false; rhs = gimple_assign_rhs1 (assign); /* The assignment has to be arg0 = -arg1 or arg1 = -arg0. */ if (!(lhs == arg0 && rhs == arg1) && !(lhs == arg1 && rhs == arg0)) return false; cond = last_stmt (cond_bb); result = PHI_RESULT (phi); /* Only relationals comparing arg[01] against zero are interesting. */ cond_code = gimple_cond_code (cond); if (cond_code != GT_EXPR && cond_code != GE_EXPR && cond_code != LT_EXPR && cond_code != LE_EXPR) return false; /* Make sure the conditional is arg[01] OP y. */ if (gimple_cond_lhs (cond) != rhs) return false; if (FLOAT_TYPE_P (TREE_TYPE (gimple_cond_rhs (cond))) ? real_zerop (gimple_cond_rhs (cond)) : integer_zerop (gimple_cond_rhs (cond))) ; else return false; /* We need to know which is the true edge and which is the false edge so that we know if have abs or negative abs. */ extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); /* For GT_EXPR/GE_EXPR, if the true edge goes to OTHER_BLOCK, then we will need to negate the result. Similarly for LT_EXPR/LE_EXPR if the false edge goes to OTHER_BLOCK. */ if (cond_code == GT_EXPR || cond_code == GE_EXPR) e = true_edge; else e = false_edge; if (e->dest == middle_bb) negate = true; else negate = false; result = duplicate_ssa_name (result, NULL); if (negate) { tree tmp = create_tmp_var (TREE_TYPE (result), NULL); add_referenced_var (tmp); lhs = make_ssa_name (tmp, NULL); } else lhs = result; /* Build the modify expression with abs expression. */ new_stmt = gimple_build_assign_with_ops (ABS_EXPR, lhs, rhs, NULL); gsi = gsi_last_bb (cond_bb); gsi_insert_before (&gsi, new_stmt, GSI_NEW_STMT); if (negate) { /* Get the right GSI. We want to insert after the recently added ABS_EXPR statement (which we know is the first statement in the block. */ new_stmt = gimple_build_assign_with_ops (NEGATE_EXPR, result, lhs, NULL); gsi_insert_after (&gsi, new_stmt, GSI_NEW_STMT); } replace_phi_edge_with_variable (cond_bb, e1, phi, result); /* Note that we optimized this PHI. */ return true; }
static bool minmax_replacement (basic_block cond_bb, basic_block middle_bb, edge e0, edge e1, gimple phi, tree arg0, tree arg1) { tree result, type; gimple cond, new_stmt; edge true_edge, false_edge; enum tree_code cmp, minmax, ass_code; tree smaller, larger, arg_true, arg_false; gimple_stmt_iterator gsi, gsi_from; type = TREE_TYPE (PHI_RESULT (phi)); /* The optimization may be unsafe due to NaNs. */ if (HONOR_NANS (TYPE_MODE (type))) return false; cond = last_stmt (cond_bb); cmp = gimple_cond_code (cond); result = PHI_RESULT (phi); /* This transformation is only valid for order comparisons. Record which operand is smaller/larger if the result of the comparison is true. */ if (cmp == LT_EXPR || cmp == LE_EXPR) { smaller = gimple_cond_lhs (cond); larger = gimple_cond_rhs (cond); } else if (cmp == GT_EXPR || cmp == GE_EXPR) { smaller = gimple_cond_rhs (cond); larger = gimple_cond_lhs (cond); } else return false; /* We need to know which is the true edge and which is the false edge so that we know if have abs or negative abs. */ extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); /* Forward the edges over the middle basic block. */ if (true_edge->dest == middle_bb) true_edge = EDGE_SUCC (true_edge->dest, 0); if (false_edge->dest == middle_bb) false_edge = EDGE_SUCC (false_edge->dest, 0); if (true_edge == e0) { gcc_assert (false_edge == e1); arg_true = arg0; arg_false = arg1; } else { gcc_assert (false_edge == e0); gcc_assert (true_edge == e1); arg_true = arg1; arg_false = arg0; } if (empty_block_p (middle_bb)) { if (operand_equal_for_phi_arg_p (arg_true, smaller) && operand_equal_for_phi_arg_p (arg_false, larger)) { /* Case if (smaller < larger) rslt = smaller; else rslt = larger; */ minmax = MIN_EXPR; } else if (operand_equal_for_phi_arg_p (arg_false, smaller) && operand_equal_for_phi_arg_p (arg_true, larger)) minmax = MAX_EXPR; else return false; } else { /* Recognize the following case, assuming d <= u: if (a <= u) b = MAX (a, d); x = PHI <b, u> This is equivalent to b = MAX (a, d); x = MIN (b, u); */ gimple assign = last_and_only_stmt (middle_bb); tree lhs, op0, op1, bound; if (!assign || gimple_code (assign) != GIMPLE_ASSIGN) return false; lhs = gimple_assign_lhs (assign); ass_code = gimple_assign_rhs_code (assign); if (ass_code != MAX_EXPR && ass_code != MIN_EXPR) return false; op0 = gimple_assign_rhs1 (assign); op1 = gimple_assign_rhs2 (assign); if (true_edge->src == middle_bb) { /* We got here if the condition is true, i.e., SMALLER < LARGER. */ if (!operand_equal_for_phi_arg_p (lhs, arg_true)) return false; if (operand_equal_for_phi_arg_p (arg_false, larger)) { /* Case if (smaller < larger) { r' = MAX_EXPR (smaller, bound) } r = PHI <r', larger> --> to be turned to MIN_EXPR. */ if (ass_code != MAX_EXPR) return false; minmax = MIN_EXPR; if (operand_equal_for_phi_arg_p (op0, smaller)) bound = op1; else if (operand_equal_for_phi_arg_p (op1, smaller)) bound = op0; else return false; /* We need BOUND <= LARGER. */ if (!integer_nonzerop (fold_build2 (LE_EXPR, boolean_type_node, bound, larger))) return false; } else if (operand_equal_for_phi_arg_p (arg_false, smaller)) { /* Case if (smaller < larger) { r' = MIN_EXPR (larger, bound) } r = PHI <r', smaller> --> to be turned to MAX_EXPR. */ if (ass_code != MIN_EXPR) return false; minmax = MAX_EXPR; if (operand_equal_for_phi_arg_p (op0, larger)) bound = op1; else if (operand_equal_for_phi_arg_p (op1, larger)) bound = op0; else return false; /* We need BOUND >= SMALLER. */ if (!integer_nonzerop (fold_build2 (GE_EXPR, boolean_type_node, bound, smaller))) return false; } else return false; } else { /* We got here if the condition is false, i.e., SMALLER > LARGER. */ if (!operand_equal_for_phi_arg_p (lhs, arg_false)) return false; if (operand_equal_for_phi_arg_p (arg_true, larger)) { /* Case if (smaller > larger) { r' = MIN_EXPR (smaller, bound) } r = PHI <r', larger> --> to be turned to MAX_EXPR. */ if (ass_code != MIN_EXPR) return false; minmax = MAX_EXPR; if (operand_equal_for_phi_arg_p (op0, smaller)) bound = op1; else if (operand_equal_for_phi_arg_p (op1, smaller)) bound = op0; else return false; /* We need BOUND >= LARGER. */ if (!integer_nonzerop (fold_build2 (GE_EXPR, boolean_type_node, bound, larger))) return false; } else if (operand_equal_for_phi_arg_p (arg_true, smaller)) { /* Case if (smaller > larger) { r' = MAX_EXPR (larger, bound) } r = PHI <r', smaller> --> to be turned to MIN_EXPR. */ if (ass_code != MAX_EXPR) return false; minmax = MIN_EXPR; if (operand_equal_for_phi_arg_p (op0, larger)) bound = op1; else if (operand_equal_for_phi_arg_p (op1, larger)) bound = op0; else return false; /* We need BOUND <= SMALLER. */ if (!integer_nonzerop (fold_build2 (LE_EXPR, boolean_type_node, bound, smaller))) return false; } else return false; } /* Move the statement from the middle block. */ gsi = gsi_last_bb (cond_bb); gsi_from = gsi_last_bb (middle_bb); gsi_move_before (&gsi_from, &gsi); } /* Emit the statement to compute min/max. */ result = duplicate_ssa_name (PHI_RESULT (phi), NULL); new_stmt = gimple_build_assign_with_ops (minmax, result, arg0, arg1); gsi = gsi_last_bb (cond_bb); gsi_insert_before (&gsi, new_stmt, GSI_NEW_STMT); replace_phi_edge_with_variable (cond_bb, e1, phi, result); return true; }
static bool value_replacement (basic_block cond_bb, basic_block middle_bb, edge e0, edge e1, gimple phi, tree arg0, tree arg1) { gimple cond; edge true_edge, false_edge; enum tree_code code; /* If the type says honor signed zeros we cannot do this optimization. */ if (HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg1)))) return false; if (!empty_block_p (middle_bb)) return false; cond = last_stmt (cond_bb); code = gimple_cond_code (cond); /* This transformation is only valid for equality comparisons. */ if (code != NE_EXPR && code != EQ_EXPR) return false; /* We need to know which is the true edge and which is the false edge so that we know if have abs or negative abs. */ extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); /* At this point we know we have a COND_EXPR with two successors. One successor is BB, the other successor is an empty block which falls through into BB. The condition for the COND_EXPR is known to be NE_EXPR or EQ_EXPR. There is a single PHI node at the join point (BB) with two arguments. We now need to verify that the two arguments in the PHI node match the two arguments to the equality comparison. */ if ((operand_equal_for_phi_arg_p (arg0, gimple_cond_lhs (cond)) && operand_equal_for_phi_arg_p (arg1, gimple_cond_rhs (cond))) || (operand_equal_for_phi_arg_p (arg1, gimple_cond_lhs (cond)) && operand_equal_for_phi_arg_p (arg0, gimple_cond_rhs (cond)))) { edge e; tree arg; /* For NE_EXPR, we want to build an assignment result = arg where arg is the PHI argument associated with the true edge. For EQ_EXPR we want the PHI argument associated with the false edge. */ e = (code == NE_EXPR ? true_edge : false_edge); /* Unfortunately, E may not reach BB (it may instead have gone to OTHER_BLOCK). If that is the case, then we want the single outgoing edge from OTHER_BLOCK which reaches BB and represents the desired path from COND_BLOCK. */ if (e->dest == middle_bb) e = single_succ_edge (e->dest); /* Now we know the incoming edge to BB that has the argument for the RHS of our new assignment statement. */ if (e0 == e) arg = arg0; else arg = arg1; replace_phi_edge_with_variable (cond_bb, e1, phi, arg); /* Note that we optimized this PHI. */ return true; } return false; }
static bool conditional_replacement (basic_block cond_bb, basic_block middle_bb, edge e0, edge e1, gimple phi, tree arg0, tree arg1) { tree result; gimple stmt, new_stmt; tree cond; gimple_stmt_iterator gsi; edge true_edge, false_edge; tree new_var, new_var2; /* FIXME: Gimplification of complex type is too hard for now. */ if (TREE_CODE (TREE_TYPE (arg0)) == COMPLEX_TYPE || TREE_CODE (TREE_TYPE (arg1)) == COMPLEX_TYPE) return false; /* The PHI arguments have the constants 0 and 1, then convert it to the conditional. */ if ((integer_zerop (arg0) && integer_onep (arg1)) || (integer_zerop (arg1) && integer_onep (arg0))) ; else return false; if (!empty_block_p (middle_bb)) return false; /* At this point we know we have a GIMPLE_COND with two successors. One successor is BB, the other successor is an empty block which falls through into BB. There is a single PHI node at the join point (BB) and its arguments are constants (0, 1). So, given the condition COND, and the two PHI arguments, we can rewrite this PHI into non-branching code: dest = (COND) or dest = COND' We use the condition as-is if the argument associated with the true edge has the value one or the argument associated with the false edge as the value zero. Note that those conditions are not the same since only one of the outgoing edges from the GIMPLE_COND will directly reach BB and thus be associated with an argument. */ stmt = last_stmt (cond_bb); result = PHI_RESULT (phi); /* To handle special cases like floating point comparison, it is easier and less error-prone to build a tree and gimplify it on the fly though it is less efficient. */ cond = fold_build2 (gimple_cond_code (stmt), boolean_type_node, gimple_cond_lhs (stmt), gimple_cond_rhs (stmt)); /* We need to know which is the true edge and which is the false edge so that we know when to invert the condition below. */ extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); if ((e0 == true_edge && integer_zerop (arg0)) || (e0 == false_edge && integer_onep (arg0)) || (e1 == true_edge && integer_zerop (arg1)) || (e1 == false_edge && integer_onep (arg1))) cond = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (cond), cond); /* Insert our new statements at the end of conditional block before the COND_STMT. */ gsi = gsi_for_stmt (stmt); new_var = force_gimple_operand_gsi (&gsi, cond, true, NULL, true, GSI_SAME_STMT); if (!useless_type_conversion_p (TREE_TYPE (result), TREE_TYPE (new_var))) { new_var2 = create_tmp_var (TREE_TYPE (result), NULL); add_referenced_var (new_var2); new_stmt = gimple_build_assign_with_ops (CONVERT_EXPR, new_var2, new_var, NULL); new_var2 = make_ssa_name (new_var2, new_stmt); gimple_assign_set_lhs (new_stmt, new_var2); gsi_insert_before (&gsi, new_stmt, GSI_SAME_STMT); new_var = new_var2; } replace_phi_edge_with_variable (cond_bb, e1, phi, new_var); /* Note that we optimized this PHI. */ return true; }
bool gimple_simplify (gimple *stmt, code_helper *rcode, tree *ops, gimple_seq *seq, tree (*valueize)(tree), tree (*top_valueize)(tree)) { switch (gimple_code (stmt)) { case GIMPLE_ASSIGN: { enum tree_code code = gimple_assign_rhs_code (stmt); tree type = TREE_TYPE (gimple_assign_lhs (stmt)); switch (gimple_assign_rhs_class (stmt)) { case GIMPLE_SINGLE_RHS: if (code == REALPART_EXPR || code == IMAGPART_EXPR || code == VIEW_CONVERT_EXPR) { tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0); bool valueized = false; op0 = do_valueize (op0, top_valueize, valueized); *rcode = code; ops[0] = op0; return (gimple_resimplify1 (seq, rcode, type, ops, valueize) || valueized); } else if (code == BIT_FIELD_REF) { tree rhs1 = gimple_assign_rhs1 (stmt); tree op0 = TREE_OPERAND (rhs1, 0); bool valueized = false; op0 = do_valueize (op0, top_valueize, valueized); *rcode = code; ops[0] = op0; ops[1] = TREE_OPERAND (rhs1, 1); ops[2] = TREE_OPERAND (rhs1, 2); return (gimple_resimplify3 (seq, rcode, type, ops, valueize) || valueized); } else if (code == SSA_NAME && top_valueize) { tree op0 = gimple_assign_rhs1 (stmt); tree valueized = top_valueize (op0); if (!valueized || op0 == valueized) return false; ops[0] = valueized; *rcode = TREE_CODE (op0); return true; } break; case GIMPLE_UNARY_RHS: { tree rhs1 = gimple_assign_rhs1 (stmt); bool valueized = false; rhs1 = do_valueize (rhs1, top_valueize, valueized); *rcode = code; ops[0] = rhs1; return (gimple_resimplify1 (seq, rcode, type, ops, valueize) || valueized); } case GIMPLE_BINARY_RHS: { tree rhs1 = gimple_assign_rhs1 (stmt); tree rhs2 = gimple_assign_rhs2 (stmt); bool valueized = false; rhs1 = do_valueize (rhs1, top_valueize, valueized); rhs2 = do_valueize (rhs2, top_valueize, valueized); *rcode = code; ops[0] = rhs1; ops[1] = rhs2; return (gimple_resimplify2 (seq, rcode, type, ops, valueize) || valueized); } case GIMPLE_TERNARY_RHS: { bool valueized = false; tree rhs1 = gimple_assign_rhs1 (stmt); /* If this is a [VEC_]COND_EXPR first try to simplify an embedded GENERIC condition. */ if (code == COND_EXPR || code == VEC_COND_EXPR) { if (COMPARISON_CLASS_P (rhs1)) { tree lhs = TREE_OPERAND (rhs1, 0); tree rhs = TREE_OPERAND (rhs1, 1); lhs = do_valueize (lhs, top_valueize, valueized); rhs = do_valueize (rhs, top_valueize, valueized); code_helper rcode2 = TREE_CODE (rhs1); tree ops2[3] = {}; ops2[0] = lhs; ops2[1] = rhs; if ((gimple_resimplify2 (seq, &rcode2, TREE_TYPE (rhs1), ops2, valueize) || valueized) && rcode2.is_tree_code ()) { valueized = true; if (TREE_CODE_CLASS ((enum tree_code)rcode2) == tcc_comparison) rhs1 = build2 (rcode2, TREE_TYPE (rhs1), ops2[0], ops2[1]); else if (rcode2 == SSA_NAME || rcode2 == INTEGER_CST) rhs1 = ops2[0]; else valueized = false; } } } tree rhs2 = gimple_assign_rhs2 (stmt); tree rhs3 = gimple_assign_rhs3 (stmt); rhs1 = do_valueize (rhs1, top_valueize, valueized); rhs2 = do_valueize (rhs2, top_valueize, valueized); rhs3 = do_valueize (rhs3, top_valueize, valueized); *rcode = code; ops[0] = rhs1; ops[1] = rhs2; ops[2] = rhs3; return (gimple_resimplify3 (seq, rcode, type, ops, valueize) || valueized); } default: gcc_unreachable (); } break; } case GIMPLE_CALL: /* ??? This way we can't simplify calls with side-effects. */ if (gimple_call_lhs (stmt) != NULL_TREE && gimple_call_num_args (stmt) >= 1 && gimple_call_num_args (stmt) <= 3) { tree fn = gimple_call_fn (stmt); /* ??? Internal function support missing. */ if (!fn) return false; bool valueized = false; fn = do_valueize (fn, top_valueize, valueized); if (TREE_CODE (fn) != ADDR_EXPR || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL) return false; tree decl = TREE_OPERAND (fn, 0); if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL || !builtin_decl_implicit (DECL_FUNCTION_CODE (decl)) || !gimple_builtin_call_types_compatible_p (stmt, decl)) return false; tree type = TREE_TYPE (gimple_call_lhs (stmt)); *rcode = DECL_FUNCTION_CODE (decl); for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i) { tree arg = gimple_call_arg (stmt, i); ops[i] = do_valueize (arg, top_valueize, valueized); } switch (gimple_call_num_args (stmt)) { case 1: return (gimple_resimplify1 (seq, rcode, type, ops, valueize) || valueized); case 2: return (gimple_resimplify2 (seq, rcode, type, ops, valueize) || valueized); case 3: return (gimple_resimplify3 (seq, rcode, type, ops, valueize) || valueized); default: gcc_unreachable (); } } break; case GIMPLE_COND: { tree lhs = gimple_cond_lhs (stmt); tree rhs = gimple_cond_rhs (stmt); bool valueized = false; lhs = do_valueize (lhs, top_valueize, valueized); rhs = do_valueize (rhs, top_valueize, valueized); *rcode = gimple_cond_code (stmt); ops[0] = lhs; ops[1] = rhs; return (gimple_resimplify2 (seq, rcode, boolean_type_node, ops, valueize) || valueized); } default: break; } return false; }
but just replace tests with 0 != 0 resp. 1 != 0. Prevent the infinite loop where we would unswitch again on such a condition. */ if (gimple_cond_true_p (stmt) || gimple_cond_false_p (stmt)) return NULL_TREE; /* Condition must be invariant. */ FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE) { def = SSA_NAME_DEF_STMT (use); def_bb = gimple_bb (def); if (def_bb && flow_bb_inside_loop_p (loop, def_bb)) return NULL_TREE; } cond = build2 (gimple_cond_code (stmt), boolean_type_node, gimple_cond_lhs (stmt), gimple_cond_rhs (stmt)); return cond; } /* Simplifies COND using checks in front of the entry of the LOOP. Just very simplish (sufficient to prevent us from duplicating loop in unswitching unnecessarily). */ static tree simplify_using_entry_checks (struct loop *loop, tree cond) { edge e = loop_preheader_edge (loop); gimple stmt;
static bool ifcombine_ifandif (basic_block inner_cond_bb, basic_block outer_cond_bb) { gimple_stmt_iterator gsi; gimple inner_cond, outer_cond; tree name1, name2, bit1, bit2; inner_cond = last_stmt (inner_cond_bb); if (!inner_cond || gimple_code (inner_cond) != GIMPLE_COND) return false; outer_cond = last_stmt (outer_cond_bb); if (!outer_cond || gimple_code (outer_cond) != GIMPLE_COND) return false; /* See if we test a single bit of the same name in both tests. In that case remove the outer test, merging both else edges, and change the inner one to test for name & (bit1 | bit2) == (bit1 | bit2). */ if (recognize_single_bit_test (inner_cond, &name1, &bit1) && recognize_single_bit_test (outer_cond, &name2, &bit2) && name1 == name2) { tree t, t2; /* Do it. */ gsi = gsi_for_stmt (inner_cond); t = fold_build2 (LSHIFT_EXPR, TREE_TYPE (name1), build_int_cst (TREE_TYPE (name1), 1), bit1); t2 = fold_build2 (LSHIFT_EXPR, TREE_TYPE (name1), build_int_cst (TREE_TYPE (name1), 1), bit2); t = fold_build2 (BIT_IOR_EXPR, TREE_TYPE (name1), t, t2); t = force_gimple_operand_gsi (&gsi, t, true, NULL_TREE, true, GSI_SAME_STMT); t2 = fold_build2 (BIT_AND_EXPR, TREE_TYPE (name1), name1, t); t2 = force_gimple_operand_gsi (&gsi, t2, true, NULL_TREE, true, GSI_SAME_STMT); t = fold_build2 (EQ_EXPR, boolean_type_node, t2, t); t = canonicalize_cond_expr_cond (t); if (!t) return false; gimple_cond_set_condition_from_tree (inner_cond, t); update_stmt (inner_cond); /* Leave CFG optimization to cfg_cleanup. */ gimple_cond_set_condition_from_tree (outer_cond, boolean_true_node); update_stmt (outer_cond); if (dump_file) { fprintf (dump_file, "optimizing double bit test to "); print_generic_expr (dump_file, name1, 0); fprintf (dump_file, " & T == T\nwith temporary T = (1 << "); print_generic_expr (dump_file, bit1, 0); fprintf (dump_file, ") | (1 << "); print_generic_expr (dump_file, bit2, 0); fprintf (dump_file, ")\n"); } return true; } /* See if we have two comparisons that we can merge into one. */ else if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison && operand_equal_p (gimple_cond_lhs (inner_cond), gimple_cond_lhs (outer_cond), 0) && operand_equal_p (gimple_cond_rhs (inner_cond), gimple_cond_rhs (outer_cond), 0)) { enum tree_code code1 = gimple_cond_code (inner_cond); enum tree_code code2 = gimple_cond_code (outer_cond); tree t; if (!(t = combine_comparisons (UNKNOWN_LOCATION, TRUTH_ANDIF_EXPR, code1, code2, boolean_type_node, gimple_cond_lhs (outer_cond), gimple_cond_rhs (outer_cond)))) return false; t = canonicalize_cond_expr_cond (t); if (!t) return false; gimple_cond_set_condition_from_tree (inner_cond, t); update_stmt (inner_cond); /* Leave CFG optimization to cfg_cleanup. */ gimple_cond_set_condition_from_tree (outer_cond, boolean_true_node); update_stmt (outer_cond); if (dump_file) { fprintf (dump_file, "optimizing two comparisons to "); print_generic_expr (dump_file, t, 0); fprintf (dump_file, "\n"); } return true; } return false; }