void gimple_cond_get_ops_from_tree (tree cond, enum tree_code *code_p, tree *lhs_p, tree *rhs_p) { gcc_assert (COMPARISON_CLASS_P (cond) || TREE_CODE (cond) == TRUTH_NOT_EXPR || is_gimple_min_invariant (cond) || SSA_VAR_P (cond)); extract_ops_from_tree (cond, code_p, lhs_p, rhs_p); /* Canonicalize conditionals of the form 'if (!VAL)'. */ if (*code_p == TRUTH_NOT_EXPR) { *code_p = EQ_EXPR; gcc_assert (*lhs_p && *rhs_p == NULL_TREE); *rhs_p = build_zero_cst (TREE_TYPE (*lhs_p)); } /* Canonicalize conditionals of the form 'if (VAL)' */ else if (TREE_CODE_CLASS (*code_p) != tcc_comparison) { *code_p = NE_EXPR; gcc_assert (*lhs_p && *rhs_p == NULL_TREE); *rhs_p = build_zero_cst (TREE_TYPE (*lhs_p)); } }
bool c_omp_check_loop_iv_exprs (location_t stmt_loc, tree declv, tree decl, tree init, tree cond, walk_tree_lh lh) { hash_set<tree> pset; struct c_omp_check_loop_iv_data data; data.declv = declv; data.fail = false; data.stmt_loc = stmt_loc; data.lh = lh; data.ppset = &pset; if (init) { data.expr_loc = EXPR_LOCATION (init); data.kind = 0; walk_tree_1 (&init, c_omp_check_loop_iv_r, &data, &pset, lh); } if (cond) { gcc_assert (COMPARISON_CLASS_P (cond)); data.expr_loc = EXPR_LOCATION (init); data.kind = 1; if (TREE_OPERAND (cond, 0) == decl) walk_tree_1 (&TREE_OPERAND (cond, 1), c_omp_check_loop_iv_r, &data, &pset, lh); else walk_tree_1 (&TREE_OPERAND (cond, 0), c_omp_check_loop_iv_r, &data, &pset, lh); } return !data.fail; }
tree gfc_truthvalue_conversion (tree expr) { switch (TREE_CODE (TREE_TYPE (expr))) { case BOOLEAN_TYPE: if (TREE_TYPE (expr) == boolean_type_node) return expr; else if (COMPARISON_CLASS_P (expr)) { TREE_TYPE (expr) = boolean_type_node; return expr; } else if (TREE_CODE (expr) == NOP_EXPR) return fold_build1_loc (input_location, NOP_EXPR, boolean_type_node, TREE_OPERAND (expr, 0)); else return fold_build1_loc (input_location, NOP_EXPR, boolean_type_node, expr); case INTEGER_TYPE: if (TREE_CODE (expr) == INTEGER_CST) return integer_zerop (expr) ? boolean_false_node : boolean_true_node; else return fold_build2_loc (input_location, NE_EXPR, boolean_type_node, expr, build_int_cst (TREE_TYPE (expr), 0)); default: internal_error ("Unexpected type in truthvalue_conversion"); } }
bool is_gimple_condexpr (tree t) { return (is_gimple_val (t) || (COMPARISON_CLASS_P (t) && !tree_could_throw_p (t) && is_gimple_val (TREE_OPERAND (t, 0)) && is_gimple_val (TREE_OPERAND (t, 1)))); }
bool c_omp_check_loop_iv (tree stmt, tree declv, walk_tree_lh lh) { hash_set<tree> pset; struct c_omp_check_loop_iv_data data; int i; data.declv = declv; data.fail = false; data.stmt_loc = EXPR_LOCATION (stmt); data.lh = lh; data.ppset = &pset; for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (stmt)); i++) { tree init = TREE_VEC_ELT (OMP_FOR_INIT (stmt), i); gcc_assert (TREE_CODE (init) == MODIFY_EXPR); tree decl = TREE_OPERAND (init, 0); tree cond = TREE_VEC_ELT (OMP_FOR_COND (stmt), i); gcc_assert (COMPARISON_CLASS_P (cond)); gcc_assert (TREE_OPERAND (cond, 0) == decl); tree incr = TREE_VEC_ELT (OMP_FOR_INCR (stmt), i); data.expr_loc = EXPR_LOCATION (TREE_OPERAND (init, 1)); data.kind = 0; walk_tree_1 (&TREE_OPERAND (init, 1), c_omp_check_loop_iv_r, &data, &pset, lh); /* Don't warn for C++ random access iterators here, the expression then involves the subtraction and always refers to the original value. The C++ FE needs to warn on those earlier. */ if (decl == TREE_VEC_ELT (declv, i)) { data.expr_loc = EXPR_LOCATION (cond); data.kind = 1; walk_tree_1 (&TREE_OPERAND (cond, 1), c_omp_check_loop_iv_r, &data, &pset, lh); } if (TREE_CODE (incr) == MODIFY_EXPR) { gcc_assert (TREE_OPERAND (incr, 0) == decl); incr = TREE_OPERAND (incr, 1); data.kind = 2; if (TREE_CODE (incr) == PLUS_EXPR && TREE_OPERAND (incr, 1) == decl) { data.expr_loc = EXPR_LOCATION (TREE_OPERAND (incr, 0)); walk_tree_1 (&TREE_OPERAND (incr, 0), c_omp_check_loop_iv_r, &data, &pset, lh); } else { data.expr_loc = EXPR_LOCATION (TREE_OPERAND (incr, 1)); walk_tree_1 (&TREE_OPERAND (incr, 1), c_omp_check_loop_iv_r, &data, &pset, lh); } } } return !data.fail; }
static bool ssa_name_defined_by_comparison_p (tree var) { tree def = SSA_NAME_DEF_STMT (var); if (TREE_CODE (def) == MODIFY_EXPR) { tree rhs = TREE_OPERAND (def, 1); return COMPARISON_CLASS_P (rhs); } return 0; }
void initialize_expr_from_cond (tree cond, struct hashable_expr *expr) { expr->type = boolean_type_node; if (COMPARISON_CLASS_P (cond)) { expr->kind = EXPR_BINARY; expr->ops.binary.op = TREE_CODE (cond); expr->ops.binary.opnd0 = TREE_OPERAND (cond, 0); expr->ops.binary.opnd1 = TREE_OPERAND (cond, 1); } else if (TREE_CODE (cond) == TRUTH_NOT_EXPR) { expr->kind = EXPR_UNARY; expr->ops.unary.op = TRUTH_NOT_EXPR; expr->ops.unary.opnd = TREE_OPERAND (cond, 0); } else gcc_unreachable (); }
static int forward_propagate_into_cond (gimple_stmt_iterator *gsi_p) { gimple stmt = gsi_stmt (*gsi_p); int did_something = 0; do { tree tmp = NULL_TREE; tree cond = gimple_assign_rhs1 (stmt); tree name, rhs0 = NULL_TREE, rhs1 = NULL_TREE; gimple def_stmt; bool single_use0_p = false, single_use1_p = false; /* We can do tree combining on SSA_NAME and comparison expressions. */ if (COMPARISON_CLASS_P (cond) && TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME) { /* For comparisons use the first operand, that is likely to simplify comparisons against constants. */ name = TREE_OPERAND (cond, 0); def_stmt = get_prop_source_stmt (name, false, &single_use0_p); if (def_stmt && can_propagate_from (def_stmt)) { tree op1 = TREE_OPERAND (cond, 1); rhs0 = rhs_to_tree (TREE_TYPE (op1), def_stmt); tmp = combine_cond_expr_cond (TREE_CODE (cond), boolean_type_node, rhs0, op1, !single_use0_p); } /* If that wasn't successful, try the second operand. */ if (tmp == NULL_TREE && TREE_CODE (TREE_OPERAND (cond, 1)) == SSA_NAME) { tree op0 = TREE_OPERAND (cond, 0); name = TREE_OPERAND (cond, 1); 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 (TREE_CODE (cond), 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 (TREE_CODE (cond), boolean_type_node, rhs0, fold_convert (TREE_TYPE (rhs0), rhs1), !(single_use0_p && single_use1_p)); } else if (TREE_CODE (cond) == SSA_NAME) { name = cond; def_stmt = get_prop_source_stmt (name, true, NULL); if (def_stmt || !can_propagate_from (def_stmt)) return did_something; rhs0 = gimple_assign_rhs1 (def_stmt); tmp = combine_cond_expr_cond (NE_EXPR, boolean_type_node, rhs0, build_int_cst (TREE_TYPE (rhs0), 0), false); } if (tmp) { if (dump_file && tmp) { 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_assign_set_rhs_from_tree (gsi_p, unshare_expr (tmp)); stmt = gsi_stmt (*gsi_p); 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; }
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; }
bool is_gimple_condexpr (tree t) { return (is_gimple_val (t) || COMPARISON_CLASS_P (t)); }
static tree forward_propagate_into_cond_1 (tree cond, tree *test_var_p) { tree new_cond = NULL_TREE; enum tree_code cond_code = TREE_CODE (cond); tree test_var = NULL_TREE; tree def; tree def_rhs; /* If the condition is not a lone variable or an equality test of an SSA_NAME against an integral constant, then we do not have an optimizable case. Note these conditions also ensure the COND_EXPR has no virtual operands or other side effects. */ if (cond_code != SSA_NAME && !((cond_code == EQ_EXPR || cond_code == NE_EXPR) && TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME && CONSTANT_CLASS_P (TREE_OPERAND (cond, 1)) && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (cond, 1))))) return NULL_TREE; /* Extract the single variable used in the test into TEST_VAR. */ if (cond_code == SSA_NAME) test_var = cond; else test_var = TREE_OPERAND (cond, 0); /* Now get the defining statement for TEST_VAR. Skip this case if it's not defined by some MODIFY_EXPR. */ def = SSA_NAME_DEF_STMT (test_var); if (TREE_CODE (def) != MODIFY_EXPR) return NULL_TREE; def_rhs = TREE_OPERAND (def, 1); /* If TEST_VAR is set by adding or subtracting a constant from an SSA_NAME, then it is interesting to us as we can adjust the constant in the conditional and thus eliminate the arithmetic operation. */ if (TREE_CODE (def_rhs) == PLUS_EXPR || TREE_CODE (def_rhs) == MINUS_EXPR) { tree op0 = TREE_OPERAND (def_rhs, 0); tree op1 = TREE_OPERAND (def_rhs, 1); /* The first operand must be an SSA_NAME and the second operand must be a constant. */ if (TREE_CODE (op0) != SSA_NAME || !CONSTANT_CLASS_P (op1) || !INTEGRAL_TYPE_P (TREE_TYPE (op1))) return NULL_TREE; /* Don't propagate if the first operand occurs in an abnormal PHI. */ if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0)) return NULL_TREE; if (has_single_use (test_var)) { enum tree_code new_code; tree t; /* If the variable was defined via X + C, then we must subtract C from the constant in the conditional. Otherwise we add C to the constant in the conditional. The result must fold into a valid gimple operand to be optimizable. */ new_code = (TREE_CODE (def_rhs) == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR); t = int_const_binop (new_code, TREE_OPERAND (cond, 1), op1, 0); if (!is_gimple_val (t)) return NULL_TREE; new_cond = build (cond_code, boolean_type_node, op0, t); } } /* These cases require comparisons of a naked SSA_NAME or comparison of an SSA_NAME against zero or one. */ else if (TREE_CODE (cond) == SSA_NAME || integer_zerop (TREE_OPERAND (cond, 1)) || integer_onep (TREE_OPERAND (cond, 1))) { /* If TEST_VAR is set from a relational operation between two SSA_NAMEs or a combination of an SSA_NAME and a constant, then it is interesting. */ if (COMPARISON_CLASS_P (def_rhs)) { tree op0 = TREE_OPERAND (def_rhs, 0); tree op1 = TREE_OPERAND (def_rhs, 1); /* Both operands of DEF_RHS must be SSA_NAMEs or constants. */ if ((TREE_CODE (op0) != SSA_NAME && !is_gimple_min_invariant (op0)) || (TREE_CODE (op1) != SSA_NAME && !is_gimple_min_invariant (op1))) return NULL_TREE; /* Don't propagate if the first operand occurs in an abnormal PHI. */ if (TREE_CODE (op0) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0)) return NULL_TREE; /* Don't propagate if the second operand occurs in an abnormal PHI. */ if (TREE_CODE (op1) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op1)) return NULL_TREE; if (has_single_use (test_var)) { /* TEST_VAR was set from a relational operator. */ new_cond = build (TREE_CODE (def_rhs), boolean_type_node, op0, op1); /* Invert the conditional if necessary. */ if ((cond_code == EQ_EXPR && integer_zerop (TREE_OPERAND (cond, 1))) || (cond_code == NE_EXPR && integer_onep (TREE_OPERAND (cond, 1)))) { new_cond = invert_truthvalue (new_cond); /* If we did not get a simple relational expression or bare SSA_NAME, then we can not optimize this case. */ if (!COMPARISON_CLASS_P (new_cond) && TREE_CODE (new_cond) != SSA_NAME) new_cond = NULL_TREE; } } } /* If TEST_VAR is set from a TRUTH_NOT_EXPR, then it is interesting. */ else if (TREE_CODE (def_rhs) == TRUTH_NOT_EXPR) { enum tree_code new_code; def_rhs = TREE_OPERAND (def_rhs, 0); /* DEF_RHS must be an SSA_NAME or constant. */ if (TREE_CODE (def_rhs) != SSA_NAME && !is_gimple_min_invariant (def_rhs)) return NULL_TREE; /* Don't propagate if the operand occurs in an abnormal PHI. */ if (TREE_CODE (def_rhs) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def_rhs)) return NULL_TREE; if (cond_code == SSA_NAME || (cond_code == NE_EXPR && integer_zerop (TREE_OPERAND (cond, 1))) || (cond_code == EQ_EXPR && integer_onep (TREE_OPERAND (cond, 1)))) new_code = EQ_EXPR; else new_code = NE_EXPR; new_cond = build2 (new_code, boolean_type_node, def_rhs, fold_convert (TREE_TYPE (def_rhs), integer_zero_node)); } /* If TEST_VAR was set from a cast of an integer type to a boolean type or a cast of a boolean to an integral, then it is interesting. */ else if (TREE_CODE (def_rhs) == NOP_EXPR || TREE_CODE (def_rhs) == CONVERT_EXPR) { tree outer_type; tree inner_type; outer_type = TREE_TYPE (def_rhs); inner_type = TREE_TYPE (TREE_OPERAND (def_rhs, 0)); if ((TREE_CODE (outer_type) == BOOLEAN_TYPE && INTEGRAL_TYPE_P (inner_type)) || (TREE_CODE (inner_type) == BOOLEAN_TYPE && INTEGRAL_TYPE_P (outer_type))) ; else if (INTEGRAL_TYPE_P (outer_type) && INTEGRAL_TYPE_P (inner_type) && TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME && ssa_name_defined_by_comparison_p (TREE_OPERAND (def_rhs, 0))) ; else return NULL_TREE; /* Don't propagate if the operand occurs in an abnormal PHI. */ if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (def_rhs, 0))) return NULL_TREE; if (has_single_use (test_var)) { enum tree_code new_code; tree new_arg; if (cond_code == SSA_NAME || (cond_code == NE_EXPR && integer_zerop (TREE_OPERAND (cond, 1))) || (cond_code == EQ_EXPR && integer_onep (TREE_OPERAND (cond, 1)))) new_code = NE_EXPR; else new_code = EQ_EXPR; new_arg = TREE_OPERAND (def_rhs, 0); new_cond = build2 (new_code, boolean_type_node, new_arg, fold_convert (TREE_TYPE (new_arg), integer_zero_node)); } } } *test_var_p = test_var; return new_cond; }
void record_conditions (vec<cond_equivalence> *p, tree cond, tree inverted) { tree op0, op1; cond_equivalence c; if (!COMPARISON_CLASS_P (cond)) return; op0 = TREE_OPERAND (cond, 0); op1 = TREE_OPERAND (cond, 1); switch (TREE_CODE (cond)) { case LT_EXPR: case GT_EXPR: if (FLOAT_TYPE_P (TREE_TYPE (op0))) { build_and_record_new_cond (ORDERED_EXPR, op0, op1, p); build_and_record_new_cond (LTGT_EXPR, op0, op1, p); } build_and_record_new_cond ((TREE_CODE (cond) == LT_EXPR ? LE_EXPR : GE_EXPR), op0, op1, p); build_and_record_new_cond (NE_EXPR, op0, op1, p); build_and_record_new_cond (EQ_EXPR, op0, op1, p, false); break; case GE_EXPR: case LE_EXPR: if (FLOAT_TYPE_P (TREE_TYPE (op0))) { build_and_record_new_cond (ORDERED_EXPR, op0, op1, p); } break; case EQ_EXPR: if (FLOAT_TYPE_P (TREE_TYPE (op0))) { build_and_record_new_cond (ORDERED_EXPR, op0, op1, p); } build_and_record_new_cond (LE_EXPR, op0, op1, p); build_and_record_new_cond (GE_EXPR, op0, op1, p); break; case UNORDERED_EXPR: build_and_record_new_cond (NE_EXPR, op0, op1, p); build_and_record_new_cond (UNLE_EXPR, op0, op1, p); build_and_record_new_cond (UNGE_EXPR, op0, op1, p); build_and_record_new_cond (UNEQ_EXPR, op0, op1, p); build_and_record_new_cond (UNLT_EXPR, op0, op1, p); build_and_record_new_cond (UNGT_EXPR, op0, op1, p); break; case UNLT_EXPR: case UNGT_EXPR: build_and_record_new_cond ((TREE_CODE (cond) == UNLT_EXPR ? UNLE_EXPR : UNGE_EXPR), op0, op1, p); build_and_record_new_cond (NE_EXPR, op0, op1, p); break; case UNEQ_EXPR: build_and_record_new_cond (UNLE_EXPR, op0, op1, p); build_and_record_new_cond (UNGE_EXPR, op0, op1, p); break; case LTGT_EXPR: build_and_record_new_cond (NE_EXPR, op0, op1, p); build_and_record_new_cond (ORDERED_EXPR, op0, op1, p); break; default: break; } /* Now store the original true and false conditions into the first two slots. */ initialize_expr_from_cond (cond, &c.cond); c.value = boolean_true_node; p->safe_push (c); /* It is possible for INVERTED to be the negation of a comparison, and not a valid RHS or GIMPLE_COND condition. This happens because invert_truthvalue may return such an expression when asked to invert a floating-point comparison. These comparisons are not assumed to obey the trichotomy law. */ initialize_expr_from_cond (inverted, &c.cond); c.value = boolean_false_node; p->safe_push (c); }
tree maybe_push_res_to_seq (code_helper rcode, tree type, tree *ops, gimple_seq *seq, tree res) { if (rcode.is_tree_code ()) { if (!res && gimple_simplified_result_is_gimple_val (rcode, ops)) return ops[0]; if (mprts_hook) { tree tem = mprts_hook (rcode, type, ops); if (tem) return tem; } if (!seq) return NULL_TREE; /* Play safe and do not allow abnormals to be mentioned in newly created statements. */ if ((TREE_CODE (ops[0]) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[0])) || (ops[1] && TREE_CODE (ops[1]) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[1])) || (ops[2] && TREE_CODE (ops[2]) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2])) || (COMPARISON_CLASS_P (ops[0]) && ((TREE_CODE (TREE_OPERAND (ops[0], 0)) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (ops[0], 0))) || (TREE_CODE (TREE_OPERAND (ops[0], 1)) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (ops[0], 1)))))) return NULL_TREE; if (!res) { if (gimple_in_ssa_p (cfun)) res = make_ssa_name (type); else res = create_tmp_reg (type); } maybe_build_generic_op (rcode, type, ops); gimple *new_stmt = gimple_build_assign (res, rcode, ops[0], ops[1], ops[2]); gimple_seq_add_stmt_without_update (seq, new_stmt); return res; } else { if (!seq) return NULL_TREE; combined_fn fn = rcode; /* Play safe and do not allow abnormals to be mentioned in newly created statements. */ unsigned nargs; for (nargs = 0; nargs < 3; ++nargs) { if (!ops[nargs]) break; if (TREE_CODE (ops[nargs]) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[nargs])) return NULL_TREE; } gcc_assert (nargs != 0); gcall *new_stmt = NULL; if (internal_fn_p (fn)) { /* Generate the given function if we can. */ internal_fn ifn = as_internal_fn (fn); new_stmt = build_call_internal (ifn, type, nargs, ops); if (!new_stmt) return NULL_TREE; } else { /* Find the function we want to call. */ tree decl = builtin_decl_implicit (as_builtin_fn (fn)); if (!decl) return NULL; /* We can't and should not emit calls to non-const functions. */ if (!(flags_from_decl_or_type (decl) & ECF_CONST)) return NULL; new_stmt = gimple_build_call (decl, nargs, ops[0], ops[1], ops[2]); } if (!res) { if (gimple_in_ssa_p (cfun)) res = make_ssa_name (type); else res = create_tmp_reg (type); } gimple_call_set_lhs (new_stmt, res); gimple_seq_add_stmt_without_update (seq, new_stmt); return res; } }
static void substitute_single_use_vars (varray_type *cond_worklist, varray_type vars_worklist) { while (VARRAY_ACTIVE_SIZE (vars_worklist) > 0) { tree test_var = VARRAY_TOP_TREE (vars_worklist); tree def = SSA_NAME_DEF_STMT (test_var); dataflow_t df; int j, num_uses, propagated_uses; VARRAY_POP (vars_worklist); /* Now compute the immediate uses of TEST_VAR. */ df = get_immediate_uses (def); num_uses = num_immediate_uses (df); propagated_uses = 0; /* If TEST_VAR is used more than once and is not a boolean set via TRUTH_NOT_EXPR with another SSA_NAME as its argument, then we can not optimize. */ if (num_uses == 1 || (TREE_CODE (TREE_TYPE (test_var)) == BOOLEAN_TYPE && TREE_CODE (TREE_OPERAND (def, 1)) == TRUTH_NOT_EXPR && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (def, 1), 0)) == SSA_NAME))) ; else continue; /* Walk over each use and try to forward propagate the RHS of DEF into the use. */ for (j = 0; j < num_uses; j++) { tree cond_stmt; tree cond; enum tree_code cond_code; tree def_rhs; enum tree_code def_rhs_code; tree new_cond; cond_stmt = immediate_use (df, j); /* For now we can only propagate into COND_EXPRs. */ if (TREE_CODE (cond_stmt) != COND_EXPR) continue; cond = COND_EXPR_COND (cond_stmt); cond_code = TREE_CODE (cond); def_rhs = TREE_OPERAND (def, 1); def_rhs_code = TREE_CODE (def_rhs); /* If the definition of the single use variable was from an arithmetic operation, then we just need to adjust the constant in the COND_EXPR_COND and update the variable tested. */ if (def_rhs_code == PLUS_EXPR || def_rhs_code == MINUS_EXPR) { tree op0 = TREE_OPERAND (def_rhs, 0); tree op1 = TREE_OPERAND (def_rhs, 1); enum tree_code new_code; tree t; /* If the variable was defined via X + C, then we must subtract C from the constant in the conditional. Otherwise we add C to the constant in the conditional. The result must fold into a valid gimple operand to be optimizable. */ new_code = def_rhs_code == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR; t = int_const_binop (new_code, TREE_OPERAND (cond, 1), op1, 0); if (!is_gimple_val (t)) continue; new_cond = build (cond_code, boolean_type_node, op0, t); } /* If the variable is defined by a conditional expression... */ else if (TREE_CODE_CLASS (def_rhs_code) == tcc_comparison) { /* TEST_VAR was set from a relational operator. */ tree op0 = TREE_OPERAND (def_rhs, 0); tree op1 = TREE_OPERAND (def_rhs, 1); new_cond = build (def_rhs_code, boolean_type_node, op0, op1); /* Invert the conditional if necessary. */ if ((cond_code == EQ_EXPR && integer_zerop (TREE_OPERAND (cond, 1))) || (cond_code == NE_EXPR && integer_onep (TREE_OPERAND (cond, 1)))) { new_cond = invert_truthvalue (new_cond); /* If we did not get a simple relational expression or bare SSA_NAME, then we can not optimize this case. */ if (!COMPARISON_CLASS_P (new_cond) && TREE_CODE (new_cond) != SSA_NAME) continue; } } else { bool invert = false; enum tree_code new_code; tree new_arg; /* TEST_VAR was set from a TRUTH_NOT_EXPR or a NOP_EXPR. */ if (def_rhs_code == TRUTH_NOT_EXPR) invert = true; /* If we don't have <NE_EXPR/EQ_EXPR x INT_CST>, then we cannot optimize this case. */ if ((cond_code == NE_EXPR || cond_code == EQ_EXPR) && TREE_CODE (TREE_OPERAND (cond, 1)) != INTEGER_CST) continue; if (cond_code == SSA_NAME || (cond_code == NE_EXPR && integer_zerop (TREE_OPERAND (cond, 1))) || (cond_code == EQ_EXPR && integer_onep (TREE_OPERAND (cond, 1)))) new_code = NE_EXPR; else new_code = EQ_EXPR; if (invert) new_code = (new_code == EQ_EXPR ? NE_EXPR : EQ_EXPR); new_arg = TREE_OPERAND (def_rhs, 0); new_cond = build2 (new_code, boolean_type_node, new_arg, fold_convert (TREE_TYPE (new_arg), integer_zero_node)); } /* Dump details. */ if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, " Replaced '"); print_generic_expr (dump_file, cond, dump_flags); fprintf (dump_file, "' with '"); print_generic_expr (dump_file, new_cond, dump_flags); fprintf (dump_file, "'\n"); } /* Replace the condition. */ COND_EXPR_COND (cond_stmt) = new_cond; modify_stmt (cond_stmt); propagated_uses++; VARRAY_PUSH_TREE (*cond_worklist, cond_stmt); } /* If we propagated into all the uses, then we can delete DEF. Unfortunately, we have to find the defining statement in whatever block it might be in. */ if (num_uses && num_uses == propagated_uses) { block_stmt_iterator bsi = bsi_for_stmt (def); bsi_remove (&bsi); } } }
static void record_single_argument_cond_exprs (varray_type cond_worklist, varray_type *vars_worklist, bitmap vars) { /* The first pass over the blocks gathers the set of variables we need immediate uses for as well as the set of interesting COND_EXPRs. A simpler implementation may be appropriate if/when we have a lower overhead means of getting immediate use information. */ while (VARRAY_ACTIVE_SIZE (cond_worklist) > 0) { tree last = VARRAY_TOP_TREE (cond_worklist); VARRAY_POP (cond_worklist); /* See if this block ends in a COND_EXPR. */ if (last && TREE_CODE (last) == COND_EXPR) { tree cond = COND_EXPR_COND (last); enum tree_code cond_code = TREE_CODE (cond); /* If the condition is a lone variable or an equality test of an SSA_NAME against an integral constant, then we may have an optimizable case. Note these conditions also ensure the COND_EXPR has no virtual operands or other side effects. */ if (cond_code == SSA_NAME || ((cond_code == EQ_EXPR || cond_code == NE_EXPR) && TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME && CONSTANT_CLASS_P (TREE_OPERAND (cond, 1)) && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (cond, 1))))) { tree def; tree test_var; /* Extract the single variable used in the test into TEST_VAR. */ if (cond_code == SSA_NAME) test_var = cond; else test_var = TREE_OPERAND (cond, 0); /* If we have already recorded this SSA_NAME as interesting, do not do so again. */ if (bitmap_bit_p (vars, SSA_NAME_VERSION (test_var))) continue; /* Now get the defining statement for TEST_VAR and see if it something we are interested in. */ def = SSA_NAME_DEF_STMT (test_var); if (TREE_CODE (def) == MODIFY_EXPR) { tree def_rhs = TREE_OPERAND (def, 1); /* If TEST_VAR is set by adding or subtracting a constant from an SSA_NAME, then it is interesting to us as we can adjust the constant in the conditional and thus eliminate the arithmetic operation. */ if (TREE_CODE (def_rhs) == PLUS_EXPR || TREE_CODE (def_rhs) == MINUS_EXPR) { tree op0 = TREE_OPERAND (def_rhs, 0); tree op1 = TREE_OPERAND (def_rhs, 1); /* The first operand must be an SSA_NAME and the second operand must be a constant. */ if (TREE_CODE (op0) != SSA_NAME || !CONSTANT_CLASS_P (op1) || !INTEGRAL_TYPE_P (TREE_TYPE (op1))) continue; /* Don't propagate if the first operand occurs in an abnormal PHI. */ if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0)) continue; } /* These cases require comparisons of a naked SSA_NAME or comparison of an SSA_NAME against zero or one. */ else if (TREE_CODE (cond) == SSA_NAME || integer_zerop (TREE_OPERAND (cond, 1)) || integer_onep (TREE_OPERAND (cond, 1))) { /* If TEST_VAR is set from a relational operation between two SSA_NAMEs or a combination of an SSA_NAME and a constant, then it is interesting. */ if (COMPARISON_CLASS_P (def_rhs)) { tree op0 = TREE_OPERAND (def_rhs, 0); tree op1 = TREE_OPERAND (def_rhs, 1); /* Both operands of DEF_RHS must be SSA_NAMEs or constants. */ if ((TREE_CODE (op0) != SSA_NAME && !is_gimple_min_invariant (op0)) || (TREE_CODE (op1) != SSA_NAME && !is_gimple_min_invariant (op1))) continue; /* Don't propagate if the first operand occurs in an abnormal PHI. */ if (TREE_CODE (op0) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0)) continue; /* Don't propagate if the second operand occurs in an abnormal PHI. */ if (TREE_CODE (op1) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op1)) continue; } /* If TEST_VAR is set from a TRUTH_NOT_EXPR, then it is interesting. */ else if (TREE_CODE (def_rhs) == TRUTH_NOT_EXPR) { def_rhs = TREE_OPERAND (def_rhs, 0); /* DEF_RHS must be an SSA_NAME or constant. */ if (TREE_CODE (def_rhs) != SSA_NAME && !is_gimple_min_invariant (def_rhs)) continue; /* Don't propagate if the operand occurs in an abnormal PHI. */ if (TREE_CODE (def_rhs) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def_rhs)) continue; } /* If TEST_VAR was set from a cast of an integer type to a boolean type or a cast of a boolean to an integral, then it is interesting. */ else if (TREE_CODE (def_rhs) == NOP_EXPR || TREE_CODE (def_rhs) == CONVERT_EXPR) { tree outer_type; tree inner_type; outer_type = TREE_TYPE (def_rhs); inner_type = TREE_TYPE (TREE_OPERAND (def_rhs, 0)); if ((TREE_CODE (outer_type) == BOOLEAN_TYPE && INTEGRAL_TYPE_P (inner_type)) || (TREE_CODE (inner_type) == BOOLEAN_TYPE && INTEGRAL_TYPE_P (outer_type))) ; else continue; /* Don't propagate if the operand occurs in an abnormal PHI. */ if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (def_rhs, 0))) continue; } else continue; } else continue; /* All the tests passed, record TEST_VAR as interesting. */ VARRAY_PUSH_TREE (*vars_worklist, test_var); bitmap_set_bit (vars, SSA_NAME_VERSION (test_var)); } } } } }