@@ -2311,6 +2311,20 @@ expand_gimple_stmt_1 (gimple stmt)
if (temp == target)
;
+ /* If the value in SUBREG of temp fits that SUBREG (does not
+ overflow) and is assigned to target SUBREG of the same mode
+ without sign convertion, we can skip the SUBREG
+ and extension. */
+ else if (promoted
+ && gimple_assign_is_zero_sign_ext_redundant (stmt)
+ && (GET_CODE (temp) == SUBREG)
+ && (GET_MODE_PRECISION (GET_MODE (SUBREG_REG (temp)))
+ >= GET_MODE_PRECISION (GET_MODE (target)))
+ && (GET_MODE (SUBREG_REG (target))
+ == GET_MODE (SUBREG_REG (temp))))
+ {
+ emit_move_insn (SUBREG_REG (target), SUBREG_REG (temp));
+ }
else if (promoted)
{
int unsignedp = SUBREG_PROMOTED_UNSIGNED_P (target);
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see
#include "ggc.h"
#include "basic-block.h"
#include "tm_p.h"
+#include "gimple.h"
static bool prefer_and_bit_test (enum machine_mode, int);
static void do_jump_by_parts_greater (tree, tree, int, rtx, rtx, int);
@@ -1108,6 +1109,64 @@ do_compare_and_jump (tree treeop0, tree treeop1, enum rtx_code signed_code,
type = TREE_TYPE (treeop0);
mode = TYPE_MODE (type);
+
+ /* Is zero/sign extension redundant. */
+ bool op0_ext_redundant = false;
+ bool op1_ext_redundant = false;
+
+ /* If promoted and the value in SUBREG of op0 fits (does not overflow),
+ it is a candidate for extension elimination. */
+ if (GET_CODE (op0) == SUBREG && SUBREG_PROMOTED_VAR_P (op0))
+ op0_ext_redundant =
+ gimple_assign_is_zero_sign_ext_redundant (SSA_NAME_DEF_STMT (treeop0));
+
+ /* If promoted and the value in SUBREG of op1 fits (does not overflow),
+ it is a candidate for extension elimination. */
+ if (GET_CODE (op1) == SUBREG && SUBREG_PROMOTED_VAR_P (op1))
+ op1_ext_redundant =
+ gimple_assign_is_zero_sign_ext_redundant (SSA_NAME_DEF_STMT (treeop1));
+
+ /* If zero/sign extension is redundant, generate RTL
+ for operands without zero/sign extension. */
+ if ((op0_ext_redundant || TREE_CODE (treeop0) == INTEGER_CST)
+ && (op1_ext_redundant || TREE_CODE (treeop1) == INTEGER_CST))
+ {
+ if ((TREE_CODE (treeop1) == INTEGER_CST)
+ && (!mode_signbit_p (GET_MODE (op1), op1)))
+ {
+ /* First operand is constant and signbit is not set (not
+ represented in RTL as a negative constant). */
+ rtx new_op0 = gen_reg_rtx (GET_MODE (SUBREG_REG (op0)));
+ emit_move_insn (new_op0, SUBREG_REG (op0));
+ op0 = new_op0;
+ }
+ else if ((TREE_CODE (treeop0) == INTEGER_CST)
+ && (!mode_signbit_p (GET_MODE (op0), op0)))
+ {
+ /* Other operand is constant and signbit is not set (not
+ represented in RTL as a negative constant). */
+ rtx new_op1 = gen_reg_rtx (GET_MODE (SUBREG_REG (op1)));
+
+ emit_move_insn (new_op1, SUBREG_REG (op1));
+ op1 = new_op1;
+ }
+ else if ((TREE_CODE (treeop0) != INTEGER_CST)
+ && (TREE_CODE (treeop1) != INTEGER_CST)
+ && (GET_MODE (op0) == GET_MODE (op1))
+ && (GET_MODE (SUBREG_REG (op0)) == GET_MODE (SUBREG_REG (op1))))
+ {
+ /* Compare registers fits SUBREG and of the
+ same mode. */
+ rtx new_op0 = gen_reg_rtx (GET_MODE (SUBREG_REG (op0)));
+ rtx new_op1 = gen_reg_rtx (GET_MODE (SUBREG_REG (op1)));
+
+ emit_move_insn (new_op0, SUBREG_REG (op0));
+ emit_move_insn (new_op1, SUBREG_REG (op1));
+ op0 = new_op0;
+ op1 = new_op1;
+ }
+ }
+
if (TREE_CODE (treeop0) == INTEGER_CST
&& (TREE_CODE (treeop1) != INTEGER_CST
|| (GET_MODE_BITSIZE (mode)
@@ -200,6 +200,102 @@ gimple_call_reset_alias_info (gimple s)
pt_solution_reset (gimple_call_clobber_set (s));
}
+/* Check gimple assign stmt and see if zero/sign extension is
+ redundant. i.e. if an assignment gimple statement has RHS expression
+ value that can fit in LHS type, subreg and extension to fit can be
+ redundant. Zero/sign extensions in this case can be removed.
+
+ If the assignment is
+ 1) NOP_EXPR or CONVERT_EXPR:
+ Check value range of RHS to see if it fits LHS type. If value fits type,
+ extension is redundant.
+
+ 2) For all other types:
+ If the value range of LHS is less than the LHS TYPE_MAX and greater than
+ LHS TYPE_MIN, RHS expresion (from which LHS value range is derived)
+ will also have the same value range. Therefore extension is redundant. */
+
+bool
+gimple_assign_is_zero_sign_ext_redundant (gimple stmt)
+{
+ double_int type_min, type_max;
+ double_int min, max;
+ enum value_range_type range_type;
+ tree int_val = NULL_TREE;
+ enum tree_code stmt_code;
+ tree lhs, rhs1;
+
+ if (!is_gimple_assign (stmt))
+ return false;
+
+ stmt_code = gimple_assign_rhs_code (stmt);
+ lhs = gimple_assign_lhs (stmt);
+ rhs1 = gimple_assign_rhs1 (stmt);
+
+ /* We remove extension for non-pointer and integral stmts. */
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (lhs))
+ || POINTER_TYPE_P (TREE_TYPE (lhs)))
+ return false;
+
+ type_max = tree_to_double_int (TYPE_MAX_VALUE (TREE_TYPE (lhs)));
+ type_min = tree_to_double_int (TYPE_MIN_VALUE (TREE_TYPE (lhs)));
+
+ if ((stmt_code == NOP_EXPR || stmt_code == CONVERT_EXPR))
+ {
+ if ((TREE_CODE (rhs1) != SSA_NAME)
+ || (TYPE_UNSIGNED (TREE_TYPE (lhs))
+ != TYPE_UNSIGNED (TREE_TYPE (rhs1))))
+ return false;
+
+ range_type = get_range_info (rhs1, &min, &max);
+
+ /* For NOP_EXPR and CONVERT_EXPR, if rhs value range fits lhs
+ type, zero/sign extension is redundant. */
+ if (range_type == VR_RANGE
+ && max.cmp (type_max, TYPE_UNSIGNED (TREE_TYPE (lhs))) != 1
+ && (type_min.cmp (min, TYPE_UNSIGNED (TREE_TYPE (lhs))) != 1))
+ return true;
+ }
+
+ /* For binary expressions, if one of the argument is constant and is
+ larger than signed maximum, it can be interpreted as negative
+ number and sign extended in RTL, so return false in this case. */
+ if (TREE_CODE_CLASS (stmt_code) == tcc_binary)
+ {
+ tree rhs1 = gimple_assign_rhs1 (stmt);
+ tree rhs2 = gimple_assign_rhs2 (stmt);
+
+ if (TREE_CODE (rhs1) == INTEGER_CST)
+ int_val = rhs1;
+ else if (TREE_CODE (rhs2) == INTEGER_CST)
+ int_val = rhs2;
+
+ if (int_val != NULL_TREE)
+ {
+ tree max = TYPE_MIN_VALUE (TREE_TYPE (lhs));
+
+ /* If type is unsigned, get the max for signed equivalent. */
+ if (!INT_CST_LT (TYPE_MIN_VALUE (TREE_TYPE (lhs)), integer_zero_node))
+ max = int_const_binop (RSHIFT_EXPR,
+ max, build_int_cst (TREE_TYPE (max), 1));
+ if (!INT_CST_LT (int_val, max))
+ return false;
+ }
+ }
+
+ /* Get the value range. */
+ range_type = get_range_info (lhs, &min, &max);
+
+ /* Value range fits type. */
+ if (range_type == VR_RANGE
+ && (max.cmp (type_max, TYPE_UNSIGNED (TREE_TYPE (lhs))) == -1)
+ && ((type_min.cmp (min, TYPE_UNSIGNED (TREE_TYPE (lhs))) == -1)
+ || min.is_zero ()))
+ return true;
+ return false;
+}
+
+
/* Helper for gimple_build_call, gimple_build_call_valist,
gimple_build_call_vec and gimple_build_call_from_tree. Build the basic
components of a GIMPLE_CALL statement to function FN with NARGS
@@ -832,6 +832,7 @@ int gimple_call_flags (const_gimple);
int gimple_call_return_flags (const_gimple);
int gimple_call_arg_flags (const_gimple, unsigned);
void gimple_call_reset_alias_info (gimple);
+bool gimple_assign_is_zero_sign_ext_redundant (gimple);
bool gimple_assign_copy_p (gimple);
bool gimple_assign_ssa_name_copy_p (gimple);
bool gimple_assign_unary_nop_p (gimple);