return t;
}
+/* Combine the comparison OP0 CODE OP1 at LOC with the defining statements
+ of its operand. Return a new comparison tree or NULL_TREE if there
+ were no simplifying combines. */
+
+static tree
+forward_propagate_into_comparison_1 (location_t loc,
+ enum tree_code code, tree type,
+ tree op0, tree op1)
+{
+ tree tmp = NULL_TREE;
+ tree rhs0 = NULL_TREE, rhs1 = NULL_TREE;
+ bool single_use0_p = false, single_use1_p = false;
+
+ /* For comparisons use the first operand, that is likely to
+ simplify comparisons against constants. */
+ if (TREE_CODE (op0) == SSA_NAME)
+ {
+ gimple def_stmt = get_prop_source_stmt (op0, false, &single_use0_p);
+ if (def_stmt && can_propagate_from (def_stmt))
+ {
+ rhs0 = rhs_to_tree (TREE_TYPE (op1), def_stmt);
+ tmp = combine_cond_expr_cond (loc, code, type,
+ rhs0, op1, !single_use0_p);
+ if (tmp)
+ return tmp;
+ }
+ }
+
+ /* If that wasn't successful, try the second operand. */
+ if (TREE_CODE (op1) == SSA_NAME)
+ {
+ gimple def_stmt = get_prop_source_stmt (op1, false, &single_use1_p);
+ if (def_stmt && can_propagate_from (def_stmt))
+ {
+ rhs1 = rhs_to_tree (TREE_TYPE (op0), def_stmt);
+ tmp = combine_cond_expr_cond (loc, code, type,
+ op0, rhs1, !single_use1_p);
+ if (tmp)
+ return tmp;
+ }
+ }
+
+ /* If that wasn't successful either, try both operands. */
+ if (rhs0 != NULL_TREE
+ && rhs1 != NULL_TREE)
+ tmp = combine_cond_expr_cond (loc, code, type,
+ rhs0, rhs1,
+ !(single_use0_p && single_use1_p));
+
+ return tmp;
+}
+
+/* Propagate from the ssa name definition statements of the assignment
+ from a comparison at *GSI into the conditional if that simplifies it.
+ Returns true if the stmt was modified, false if not. */
+
+static bool
+forward_propagate_into_comparison (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ tree tmp;
+
+ /* Combine the comparison with defining statements. */
+ tmp = forward_propagate_into_comparison_1 (gimple_location (stmt),
+ gimple_assign_rhs_code (stmt),
+ TREE_TYPE
+ (gimple_assign_lhs (stmt)),
+ gimple_assign_rhs1 (stmt),
+ gimple_assign_rhs2 (stmt));
+ if (tmp)
+ {
+ gimple_assign_set_rhs_from_tree (gsi, tmp);
+ update_stmt (stmt);
+ return true;
+ }
+
+ return false;
+}
+
/* Propagate from the ssa name definition statements of COND_EXPR
in GIMPLE_COND statement STMT into the conditional if that simplifies it.
Returns zero if no statement was changed, one if there were
{
int did_something = 0;
location_t loc = gimple_location (stmt);
+ tree tmp;
+ 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)
+ return 0;
+
+ tmp = forward_propagate_into_comparison_1 (loc, code,
+ boolean_type_node,
+ gimple_cond_lhs (stmt),
+ gimple_cond_rhs (stmt));
+ 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");
+ }
- do {
- tree tmp = NULL_TREE;
- tree name = NULL_TREE, 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)
- {
- /* For comparisons use the first operand, that is likely to
- simplify comparisons against constants. */
- if (TREE_CODE (gimple_cond_lhs (stmt)) == SSA_NAME)
- {
- 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 (loc, 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 (loc, 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 (loc, code, boolean_type_node, rhs0,
- fold_convert_loc (loc,
- 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. */
- if (remove_prop_source_from_use (name)
- || is_gimple_min_invariant (tmp))
- did_something = 2;
- else if (did_something == 0)
- did_something = 1;
-
- /* Continue combining. */
- continue;
- }
+ gimple_cond_set_condition_from_tree (stmt, unshare_expr (tmp));
+ update_stmt (stmt);
- break;
- } while (1);
+ /* Remove defining statements. */
+ if (is_gimple_min_invariant (tmp))
+ did_something = 2;
+ else if (did_something == 0)
+ did_something = 1;
+ }
return did_something;
}
gimple stmt = gsi_stmt (*gsi_p);
location_t loc = gimple_location (stmt);
int did_something = 0;
+ tree tmp = NULL_TREE;
+ tree cond = gimple_assign_rhs1 (stmt);
+
+ /* We can do tree combining on SSA_NAME and comparison expressions. */
+ if (COMPARISON_CLASS_P (cond))
+ tmp = forward_propagate_into_comparison_1 (loc, TREE_CODE (cond),
+ boolean_type_node,
+ TREE_OPERAND (cond, 0),
+ TREE_OPERAND (cond, 1));
+ else if (TREE_CODE (cond) == SSA_NAME)
+ {
+ tree name = cond, rhs0;
+ gimple 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 (loc, NE_EXPR, boolean_type_node, rhs0,
+ build_int_cst (TREE_TYPE (rhs0), 0),
+ false);
+ }
- 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 (loc, 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 (loc, 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 (loc, TREE_CODE (cond),
- boolean_type_node,
- rhs0,
- fold_convert_loc (loc,
- 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 (loc, 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. */
- if (remove_prop_source_from_use (name)
- || is_gimple_min_invariant (tmp))
- did_something = 2;
- else if (did_something == 0)
- did_something = 1;
+ 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");
+ }
- /* Continue combining. */
- continue;
- }
+ gimple_assign_set_rhs_from_tree (gsi_p, unshare_expr (tmp));
+ stmt = gsi_stmt (*gsi_p);
+ update_stmt (stmt);
- break;
- } while (1);
+ /* Remove defining statements. */
+ if (is_gimple_min_invariant (tmp))
+ did_something = 2;
+ else if (did_something == 0)
+ did_something = 1;
+ }
return did_something;
}
new_base = TREE_OPERAND (*def_rhs_basep, 0);
new_offset
= int_const_binop (PLUS_EXPR, TREE_OPERAND (lhs, 1),
- TREE_OPERAND (*def_rhs_basep, 1), 0);
+ TREE_OPERAND (*def_rhs_basep, 1));
}
else
{
new_base = TREE_OPERAND (*def_rhs_basep, 0);
new_offset
= int_const_binop (PLUS_EXPR, TREE_OPERAND (rhs, 1),
- TREE_OPERAND (*def_rhs_basep, 1), 0);
+ TREE_OPERAND (*def_rhs_basep, 1));
}
else
{
return all && has_zero_uses (name);
}
+
/* Forward propagate the comparison defined in STMT like
cond_1 = x CMP y to uses of the form
a_1 = (T')cond_1
return false;
}
+
/* If we have lhs = ~x (STMT), look and see if earlier we had x = ~y.
If so, we can change STMT into lhs = y which can later be copy
propagated. Similarly for negation.
there's less work to do for each NOT/NEG expression we find.
Backwards propagation needs to look at the statement in a single
backlink. Forward propagation needs to look at potentially more
- than one forward link. */
+ than one forward link.
-static void
+ Returns true when the statement was changed. */
+
+static bool
simplify_not_neg_expr (gimple_stmt_iterator *gsi_p)
{
gimple stmt = gsi_stmt (*gsi_p);
gimple_assign_set_rhs_from_tree (gsi_p, rhs_def_operand);
stmt = gsi_stmt (*gsi_p);
update_stmt (stmt);
+ return true;
}
}
+
+ return false;
}
/* STMT is a SWITCH_EXPR for which we attempt to find equivalent forms of
the condition which we may be able to optimize better. */
-static void
+static bool
simplify_gimple_switch (gimple stmt)
{
tree cond = gimple_switch_index (stmt);
{
gimple_switch_set_index (stmt, def);
update_stmt (stmt);
+ return true;
}
}
}
}
+
+ return false;
}
/* For pointers p2 and p1 return p2 - p1 if the
return false;
}
-/* Run bitwise and assignments throug the folder. If the first argument is an
- ssa name that is itself a result of a typecast of an ADDR_EXPR to an
- integer, feed the ADDR_EXPR to the folder rather than the ssa name.
-*/
+/* Simplify bitwise binary operations.
+ Return true if a transformation applied, otherwise return false. */
-static void
-simplify_bitwise_and (gimple_stmt_iterator *gsi, gimple stmt)
+static bool
+simplify_bitwise_binary (gimple_stmt_iterator *gsi)
{
- tree res;
+ gimple stmt = gsi_stmt (*gsi);
tree arg1 = gimple_assign_rhs1 (stmt);
tree arg2 = gimple_assign_rhs2 (stmt);
+ enum tree_code code = gimple_assign_rhs_code (stmt);
+ tree res;
+ gimple def1 = NULL, def2 = NULL;
+ tree def1_arg1, def2_arg1;
+ enum tree_code def1_code, def2_code;
+
+ /* If the first argument is an SSA name that is itself a result of a
+ typecast of an ADDR_EXPR to an integer, feed the ADDR_EXPR to the
+ folder rather than the ssa name. */
+ if (code == BIT_AND_EXPR
+ && TREE_CODE (arg2) == INTEGER_CST
+ && TREE_CODE (arg1) == SSA_NAME)
+ {
+ gimple def = SSA_NAME_DEF_STMT (arg1);
+ tree op = arg1;
- if (TREE_CODE (arg2) != INTEGER_CST)
- return;
+ /* ??? This looks bogus - the conversion could be truncating. */
+ if (is_gimple_assign (def)
+ && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def))
+ && INTEGRAL_TYPE_P (TREE_TYPE (arg1)))
+ {
+ tree opp = gimple_assign_rhs1 (def);
+ if (TREE_CODE (opp) == ADDR_EXPR)
+ op = opp;
+ }
- if (TREE_CODE (arg1) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (arg1))
+ res = fold_binary_loc (gimple_location (stmt),
+ BIT_AND_EXPR, TREE_TYPE (gimple_assign_lhs (stmt)),
+ op, arg2);
+ if (res && is_gimple_min_invariant (res))
+ {
+ gimple_assign_set_rhs_from_tree (gsi, res);
+ update_stmt (stmt);
+ return true;
+ }
+ }
+
+ def1_code = TREE_CODE (arg1);
+ def1_arg1 = arg1;
+ if (TREE_CODE (arg1) == SSA_NAME)
{
- gimple def = SSA_NAME_DEF_STMT (arg1);
+ def1 = SSA_NAME_DEF_STMT (arg1);
+ if (is_gimple_assign (def1))
+ {
+ def1_code = gimple_assign_rhs_code (def1);
+ def1_arg1 = gimple_assign_rhs1 (def1);
+ }
+ }
- if (gimple_assign_cast_p (def)
- && INTEGRAL_TYPE_P (gimple_expr_type (def)))
+ def2_code = TREE_CODE (arg2);
+ def2_arg1 = arg2;
+ if (TREE_CODE (arg2) == SSA_NAME)
+ {
+ def2 = SSA_NAME_DEF_STMT (arg2);
+ if (is_gimple_assign (def2))
{
- tree op = gimple_assign_rhs1 (def);
+ def2_code = gimple_assign_rhs_code (def2);
+ def2_arg1 = gimple_assign_rhs1 (def2);
+ }
+ }
+
+ /* For bitwise binary operations apply operand conversions to the
+ binary operation result instead of to the operands. This allows
+ to combine successive conversions and bitwise binary operations. */
+ if (CONVERT_EXPR_CODE_P (def1_code)
+ && CONVERT_EXPR_CODE_P (def2_code)
+ && types_compatible_p (TREE_TYPE (def1_arg1), TREE_TYPE (def2_arg1))
+ /* Make sure that the conversion widens the operands or that it
+ changes the operation to a bitfield precision. */
+ && ((TYPE_PRECISION (TREE_TYPE (def1_arg1))
+ < TYPE_PRECISION (TREE_TYPE (arg1)))
+ || (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (arg1)))
+ != MODE_INT)
+ || (TYPE_PRECISION (TREE_TYPE (arg1))
+ != GET_MODE_PRECISION (TYPE_MODE (TREE_TYPE (arg1))))))
+ {
+ gimple newop;
+ tree tem = create_tmp_reg (TREE_TYPE (def1_arg1),
+ NULL);
+ newop = gimple_build_assign_with_ops (code, tem, def1_arg1, def2_arg1);
+ tem = make_ssa_name (tem, newop);
+ gimple_assign_set_lhs (newop, tem);
+ gsi_insert_before (gsi, newop, GSI_SAME_STMT);
+ gimple_assign_set_rhs_with_ops_1 (gsi, NOP_EXPR,
+ tem, NULL_TREE, NULL_TREE);
+ update_stmt (gsi_stmt (*gsi));
+ return true;
+ }
- if (TREE_CODE (op) == ADDR_EXPR)
- arg1 = op;
+ /* (a | CST1) & CST2 -> (a & CST2) | (CST1 & CST2). */
+ if (code == BIT_AND_EXPR
+ && def1_code == BIT_IOR_EXPR
+ && TREE_CODE (arg2) == INTEGER_CST
+ && TREE_CODE (gimple_assign_rhs2 (def1)) == INTEGER_CST)
+ {
+ tree cst = fold_build2 (BIT_AND_EXPR, TREE_TYPE (arg2),
+ arg2, gimple_assign_rhs2 (def1));
+ tree tem;
+ gimple newop;
+ if (integer_zerop (cst))
+ {
+ gimple_assign_set_rhs1 (stmt, def1_arg1);
+ update_stmt (stmt);
+ return true;
}
+ tem = create_tmp_reg (TREE_TYPE (arg2), NULL);
+ newop = gimple_build_assign_with_ops (BIT_AND_EXPR,
+ tem, def1_arg1, arg2);
+ tem = make_ssa_name (tem, newop);
+ gimple_assign_set_lhs (newop, tem);
+ /* Make sure to re-process the new stmt as it's walking upwards. */
+ gsi_insert_before (gsi, newop, GSI_NEW_STMT);
+ gimple_assign_set_rhs1 (stmt, tem);
+ gimple_assign_set_rhs2 (stmt, cst);
+ gimple_assign_set_rhs_code (stmt, BIT_IOR_EXPR);
+ update_stmt (stmt);
+ return true;
}
- res = fold_binary_loc (gimple_location (stmt),
- BIT_AND_EXPR, TREE_TYPE (gimple_assign_lhs (stmt)),
- arg1, arg2);
- if (res && is_gimple_min_invariant (res))
+ /* Combine successive equal operations with constants. */
+ if ((code == BIT_AND_EXPR
+ || code == BIT_IOR_EXPR
+ || code == BIT_XOR_EXPR)
+ && def1_code == code
+ && TREE_CODE (arg2) == INTEGER_CST
+ && TREE_CODE (gimple_assign_rhs2 (def1)) == INTEGER_CST)
{
- gimple_assign_set_rhs_from_tree (gsi, res);
+ tree cst = fold_build2 (code, TREE_TYPE (arg2),
+ arg2, gimple_assign_rhs2 (def1));
+ gimple_assign_set_rhs1 (stmt, def1_arg1);
+ gimple_assign_set_rhs2 (stmt, cst);
update_stmt (stmt);
+ return true;
}
- return;
+
+ return false;
}
{
/* ~A + A -> -1. */
code = INTEGER_CST;
- rhs1 = build_int_cst (TREE_TYPE (rhs2), -1);
+ rhs1 = build_int_cst_type (TREE_TYPE (rhs2), -1);
rhs2 = NULL_TREE;
gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
gcc_assert (gsi_stmt (gsi) == stmt);
{
/* A + ~A -> -1. */
code = INTEGER_CST;
- rhs1 = build_int_cst (TREE_TYPE (rhs1), -1);
+ rhs1 = build_int_cst_type (TREE_TYPE (rhs1), -1);
rhs2 = NULL_TREE;
gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
gcc_assert (gsi_stmt (gsi) == stmt);
return false;
}
-/* Main entry point for the forward propagation optimizer. */
+/* Combine two conversions in a row for the second conversion at *GSI.
+ Returns true if there were any changes made. */
+
+static bool
+combine_conversions (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ gimple def_stmt;
+ tree op0, lhs;
+ enum tree_code code = gimple_assign_rhs_code (stmt);
+
+ gcc_checking_assert (CONVERT_EXPR_CODE_P (code)
+ || code == FLOAT_EXPR
+ || code == FIX_TRUNC_EXPR);
+
+ lhs = gimple_assign_lhs (stmt);
+ op0 = gimple_assign_rhs1 (stmt);
+ if (useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (op0)))
+ {
+ gimple_assign_set_rhs_code (stmt, TREE_CODE (op0));
+ return true;
+ }
+
+ if (TREE_CODE (op0) != SSA_NAME)
+ return false;
+
+ def_stmt = SSA_NAME_DEF_STMT (op0);
+ if (!is_gimple_assign (def_stmt))
+ return false;
+
+ if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt)))
+ {
+ tree defop0 = gimple_assign_rhs1 (def_stmt);
+ tree type = TREE_TYPE (lhs);
+ tree inside_type = TREE_TYPE (defop0);
+ tree inter_type = TREE_TYPE (op0);
+ int inside_int = INTEGRAL_TYPE_P (inside_type);
+ int inside_ptr = POINTER_TYPE_P (inside_type);
+ int inside_float = FLOAT_TYPE_P (inside_type);
+ int inside_vec = TREE_CODE (inside_type) == VECTOR_TYPE;
+ unsigned int inside_prec = TYPE_PRECISION (inside_type);
+ int inside_unsignedp = TYPE_UNSIGNED (inside_type);
+ int inter_int = INTEGRAL_TYPE_P (inter_type);
+ int inter_ptr = POINTER_TYPE_P (inter_type);
+ int inter_float = FLOAT_TYPE_P (inter_type);
+ int inter_vec = TREE_CODE (inter_type) == VECTOR_TYPE;
+ unsigned int inter_prec = TYPE_PRECISION (inter_type);
+ int inter_unsignedp = TYPE_UNSIGNED (inter_type);
+ int final_int = INTEGRAL_TYPE_P (type);
+ int final_ptr = POINTER_TYPE_P (type);
+ int final_float = FLOAT_TYPE_P (type);
+ int final_vec = TREE_CODE (type) == VECTOR_TYPE;
+ unsigned int final_prec = TYPE_PRECISION (type);
+ int final_unsignedp = TYPE_UNSIGNED (type);
+
+ /* In addition to the cases of two conversions in a row
+ handled below, if we are converting something to its own
+ type via an object of identical or wider precision, neither
+ conversion is needed. */
+ if (useless_type_conversion_p (type, inside_type)
+ && (((inter_int || inter_ptr) && final_int)
+ || (inter_float && final_float))
+ && inter_prec >= final_prec)
+ {
+ gimple_assign_set_rhs1 (stmt, unshare_expr (defop0));
+ gimple_assign_set_rhs_code (stmt, TREE_CODE (defop0));
+ update_stmt (stmt);
+ return true;
+ }
+
+ /* Likewise, if the intermediate and initial types are either both
+ float or both integer, we don't need the middle conversion if the
+ former is wider than the latter and doesn't change the signedness
+ (for integers). Avoid this if the final type is a pointer since
+ then we sometimes need the middle conversion. Likewise if the
+ final type has a precision not equal to the size of its mode. */
+ if (((inter_int && inside_int)
+ || (inter_float && inside_float)
+ || (inter_vec && inside_vec))
+ && inter_prec >= inside_prec
+ && (inter_float || inter_vec
+ || inter_unsignedp == inside_unsignedp)
+ && ! (final_prec != GET_MODE_BITSIZE (TYPE_MODE (type))
+ && TYPE_MODE (type) == TYPE_MODE (inter_type))
+ && ! final_ptr
+ && (! final_vec || inter_prec == inside_prec))
+ {
+ gimple_assign_set_rhs1 (stmt, defop0);
+ update_stmt (stmt);
+ return true;
+ }
+
+ /* If we have a sign-extension of a zero-extended value, we can
+ replace that by a single zero-extension. */
+ if (inside_int && inter_int && final_int
+ && inside_prec < inter_prec && inter_prec < final_prec
+ && inside_unsignedp && !inter_unsignedp)
+ {
+ gimple_assign_set_rhs1 (stmt, defop0);
+ update_stmt (stmt);
+ return true;
+ }
+
+ /* Two conversions in a row are not needed unless:
+ - some conversion is floating-point (overstrict for now), or
+ - some conversion is a vector (overstrict for now), or
+ - the intermediate type is narrower than both initial and
+ final, or
+ - the intermediate type and innermost type differ in signedness,
+ and the outermost type is wider than the intermediate, or
+ - the initial type is a pointer type and the precisions of the
+ intermediate and final types differ, or
+ - the final type is a pointer type and the precisions of the
+ initial and intermediate types differ. */
+ if (! inside_float && ! inter_float && ! final_float
+ && ! inside_vec && ! inter_vec && ! final_vec
+ && (inter_prec >= inside_prec || inter_prec >= final_prec)
+ && ! (inside_int && inter_int
+ && inter_unsignedp != inside_unsignedp
+ && inter_prec < final_prec)
+ && ((inter_unsignedp && inter_prec > inside_prec)
+ == (final_unsignedp && final_prec > inter_prec))
+ && ! (inside_ptr && inter_prec != final_prec)
+ && ! (final_ptr && inside_prec != inter_prec)
+ && ! (final_prec != GET_MODE_BITSIZE (TYPE_MODE (type))
+ && TYPE_MODE (type) == TYPE_MODE (inter_type)))
+ {
+ gimple_assign_set_rhs1 (stmt, defop0);
+ update_stmt (stmt);
+ return true;
+ }
+
+ /* A truncation to an unsigned type should be canonicalized as
+ bitwise and of a mask. */
+ if (final_int && inter_int && inside_int
+ && final_prec == inside_prec
+ && final_prec > inter_prec
+ && inter_unsignedp)
+ {
+ tree tem;
+ tem = fold_build2 (BIT_AND_EXPR, inside_type,
+ defop0,
+ double_int_to_tree
+ (inside_type, double_int_mask (inter_prec)));
+ if (!useless_type_conversion_p (type, inside_type))
+ {
+ tem = force_gimple_operand_gsi (gsi, tem, true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ gimple_assign_set_rhs1 (stmt, tem);
+ }
+ else
+ gimple_assign_set_rhs_from_tree (gsi, tem);
+ update_stmt (gsi_stmt (*gsi));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Main entry point for the forward propagation and statement combine
+ optimizer. */
static unsigned int
-tree_ssa_forward_propagate_single_use_vars (void)
+ssa_forward_propagate_and_combine (void)
{
basic_block bb;
unsigned int todoflags = 0;
{
gimple_stmt_iterator gsi;
- /* Note we update GSI within the loop as necessary. */
+ /* Apply forward propagation to all stmts in the basic-block.
+ Note we update GSI within the loop as necessary. */
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
{
gimple stmt = gsi_stmt (gsi);
+ tree lhs, rhs;
+ enum tree_code code;
+
+ if (!is_gimple_assign (stmt))
+ {
+ gsi_next (&gsi);
+ continue;
+ }
+
+ lhs = gimple_assign_lhs (stmt);
+ rhs = gimple_assign_rhs1 (stmt);
+ code = gimple_assign_rhs_code (stmt);
+ if (TREE_CODE (lhs) != SSA_NAME
+ || has_zero_uses (lhs))
+ {
+ gsi_next (&gsi);
+ continue;
+ }
/* If this statement sets an SSA_NAME to an address,
try to propagate the address into the uses of the SSA_NAME. */
- if (is_gimple_assign (stmt))
+ if (code == ADDR_EXPR
+ /* Handle pointer conversions on invariant addresses
+ as well, as this is valid gimple. */
+ || (CONVERT_EXPR_CODE_P (code)
+ && TREE_CODE (rhs) == ADDR_EXPR
+ && POINTER_TYPE_P (TREE_TYPE (lhs))))
{
- tree lhs = gimple_assign_lhs (stmt);
- tree rhs = gimple_assign_rhs1 (stmt);
-
- if (TREE_CODE (lhs) != SSA_NAME)
+ tree base = get_base_address (TREE_OPERAND (rhs, 0));
+ if ((!base
+ || !DECL_P (base)
+ || decl_address_invariant_p (base))
+ && !stmt_references_abnormal_ssa_name (stmt)
+ && forward_propagate_addr_expr (lhs, rhs))
{
- gsi_next (&gsi);
- continue;
+ release_defs (stmt);
+ todoflags |= TODO_remove_unused_locals;
+ gsi_remove (&gsi, true);
}
-
- if (gimple_assign_rhs_code (stmt) == ADDR_EXPR
- /* Handle pointer conversions on invariant addresses
- as well, as this is valid gimple. */
- || (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
- && TREE_CODE (rhs) == ADDR_EXPR
- && POINTER_TYPE_P (TREE_TYPE (lhs))))
+ else
+ gsi_next (&gsi);
+ }
+ else if (code == POINTER_PLUS_EXPR
+ && can_propagate_from (stmt))
+ {
+ if (TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST
+ /* ??? Better adjust the interface to that function
+ instead of building new trees here. */
+ && forward_propagate_addr_expr
+ (lhs,
+ build1 (ADDR_EXPR,
+ TREE_TYPE (rhs),
+ fold_build2 (MEM_REF,
+ TREE_TYPE (TREE_TYPE (rhs)),
+ rhs,
+ fold_convert
+ (ptr_type_node,
+ gimple_assign_rhs2 (stmt))))))
{
- tree base = get_base_address (TREE_OPERAND (rhs, 0));
- if ((!base
- || !DECL_P (base)
- || decl_address_invariant_p (base))
- && !stmt_references_abnormal_ssa_name (stmt)
- && forward_propagate_addr_expr (lhs, rhs))
- {
- release_defs (stmt);
- todoflags |= TODO_remove_unused_locals;
- gsi_remove (&gsi, true);
- }
- else
- gsi_next (&gsi);
+ release_defs (stmt);
+ todoflags |= TODO_remove_unused_locals;
+ gsi_remove (&gsi, true);
}
- else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
- && can_propagate_from (stmt))
+ else if (is_gimple_min_invariant (rhs))
{
- if (TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST
- /* ??? Better adjust the interface to that function
- instead of building new trees here. */
- && forward_propagate_addr_expr
- (lhs,
- build1 (ADDR_EXPR,
- TREE_TYPE (rhs),
- fold_build2 (MEM_REF,
- TREE_TYPE (TREE_TYPE (rhs)),
- rhs,
- fold_convert
- (ptr_type_node,
- gimple_assign_rhs2 (stmt))))))
- {
- release_defs (stmt);
- todoflags |= TODO_remove_unused_locals;
- gsi_remove (&gsi, true);
- }
- else if (is_gimple_min_invariant (rhs))
- {
- /* Make sure to fold &a[0] + off_1 here. */
- fold_stmt_inplace (stmt);
- update_stmt (stmt);
- if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
- gsi_next (&gsi);
- }
- else
+ /* Make sure to fold &a[0] + off_1 here. */
+ fold_stmt_inplace (stmt);
+ update_stmt (stmt);
+ if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
gsi_next (&gsi);
}
- else if ((gimple_assign_rhs_code (stmt) == BIT_NOT_EXPR
- || gimple_assign_rhs_code (stmt) == NEGATE_EXPR)
- && TREE_CODE (rhs) == SSA_NAME)
- {
- simplify_not_neg_expr (&gsi);
- gsi_next (&gsi);
- }
- else if (gimple_assign_rhs_code (stmt) == COND_EXPR)
- {
- /* In this case the entire COND_EXPR is in rhs1. */
- int did_something;
- fold_defer_overflow_warnings ();
- did_something = forward_propagate_into_cond (&gsi);
- stmt = gsi_stmt (gsi);
- if (did_something == 2)
- cfg_changed = true;
- fold_undefer_overflow_warnings (!TREE_NO_WARNING (rhs)
- && did_something, stmt, WARN_STRICT_OVERFLOW_CONDITIONAL);
- gsi_next (&gsi);
- }
- else if (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt))
- == tcc_comparison)
- {
- if (forward_propagate_comparison (stmt))
- cfg_changed = true;
- gsi_next (&gsi);
- }
- else if (gimple_assign_rhs_code (stmt) == BIT_AND_EXPR)
- {
- simplify_bitwise_and (&gsi, stmt);
- gsi_next (&gsi);
- }
- else if (gimple_assign_rhs_code (stmt) == PLUS_EXPR
- || gimple_assign_rhs_code (stmt) == MINUS_EXPR)
- {
- cfg_changed |= associate_plusminus (stmt);
- gsi_next (&gsi);
- }
else
gsi_next (&gsi);
}
- else if (gimple_code (stmt) == GIMPLE_SWITCH)
- {
- simplify_gimple_switch (stmt);
- gsi_next (&gsi);
- }
- else if (gimple_code (stmt) == GIMPLE_COND)
+ else if (TREE_CODE_CLASS (code) == tcc_comparison)
{
- int did_something;
- fold_defer_overflow_warnings ();
- did_something = forward_propagate_into_gimple_cond (stmt);
- if (did_something == 2)
- cfg_changed = true;
- fold_undefer_overflow_warnings (did_something, stmt,
- WARN_STRICT_OVERFLOW_CONDITIONAL);
+ forward_propagate_comparison (stmt);
gsi_next (&gsi);
}
- else if (is_gimple_call (stmt))
- {
- tree callee = gimple_call_fndecl (stmt);
- if (callee == NULL_TREE
- || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
- || !simplify_builtin_call (&gsi, callee))
- gsi_next (&gsi);
- }
else
gsi_next (&gsi);
}
+
+ /* Combine stmts with the stmts defining their operands.
+ Note we update GSI within the loop as necessary. */
+ for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi);)
+ {
+ gimple stmt = gsi_stmt (gsi);
+ bool changed = false;
+
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_ASSIGN:
+ {
+ tree rhs1 = gimple_assign_rhs1 (stmt);
+ enum tree_code code = gimple_assign_rhs_code (stmt);
+
+ if ((code == BIT_NOT_EXPR
+ || code == NEGATE_EXPR)
+ && TREE_CODE (rhs1) == SSA_NAME)
+ changed = simplify_not_neg_expr (&gsi);
+ else if (code == COND_EXPR)
+ {
+ /* In this case the entire COND_EXPR is in rhs1. */
+ int did_something;
+ fold_defer_overflow_warnings ();
+ did_something = forward_propagate_into_cond (&gsi);
+ stmt = gsi_stmt (gsi);
+ if (did_something == 2)
+ cfg_changed = true;
+ fold_undefer_overflow_warnings
+ (!TREE_NO_WARNING (rhs1) && did_something, stmt,
+ WARN_STRICT_OVERFLOW_CONDITIONAL);
+ changed = did_something != 0;
+ }
+ else if (TREE_CODE_CLASS (code) == tcc_comparison)
+ {
+ bool no_warning = gimple_no_warning_p (stmt);
+ fold_defer_overflow_warnings ();
+ changed = forward_propagate_into_comparison (&gsi);
+ fold_undefer_overflow_warnings
+ (!no_warning && changed,
+ stmt, WARN_STRICT_OVERFLOW_CONDITIONAL);
+ }
+ else if (code == BIT_AND_EXPR
+ || code == BIT_IOR_EXPR
+ || code == BIT_XOR_EXPR)
+ changed = simplify_bitwise_binary (&gsi);
+ else if (code == PLUS_EXPR
+ || code == MINUS_EXPR)
+ changed = associate_plusminus (stmt);
+ else if (CONVERT_EXPR_CODE_P (code)
+ || code == FLOAT_EXPR
+ || code == FIX_TRUNC_EXPR)
+ changed = combine_conversions (&gsi);
+ break;
+ }
+
+ case GIMPLE_SWITCH:
+ changed = simplify_gimple_switch (stmt);
+ break;
+
+ case GIMPLE_COND:
+ {
+ int did_something;
+ fold_defer_overflow_warnings ();
+ did_something = forward_propagate_into_gimple_cond (stmt);
+ if (did_something == 2)
+ cfg_changed = true;
+ fold_undefer_overflow_warnings
+ (did_something, stmt, WARN_STRICT_OVERFLOW_CONDITIONAL);
+ changed = did_something != 0;
+ break;
+ }
+
+ case GIMPLE_CALL:
+ {
+ tree callee = gimple_call_fndecl (stmt);
+ if (callee != NULL_TREE
+ && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
+ changed = simplify_builtin_call (&gsi, callee);
+ break;
+ }
+
+ default:;
+ }
+
+ /* If the stmt changed try combining it again. */
+ if (!changed)
+ gsi_prev (&gsi);
+ }
}
if (cfg_changed)
todoflags |= TODO_cleanup_cfg;
+
return todoflags;
}
GIMPLE_PASS,
"forwprop", /* name */
gate_forwprop, /* gate */
- tree_ssa_forward_propagate_single_use_vars, /* execute */
+ ssa_forward_propagate_and_combine, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */