@@ -2279,6 +2279,7 @@ expand_gimple_stmt_1 (gimple stmt)
bool nontemporal = gimple_assign_nontemporal_move_p (stmt);
struct separate_ops ops;
bool promoted = false;
+ bool ext_redundant = false;
target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
@@ -2309,8 +2310,34 @@ expand_gimple_stmt_1 (gimple stmt)
temp = expand_expr_real_2 (&ops, temp, GET_MODE (target),
EXPAND_NORMAL);
+ if (promoted
+ && !POINTER_TYPE_P (TREE_TYPE (lhs))
+ && tree_ssa_is_extension_redundant (lhs))
+ {
+ /* VRP detected extension as redundant. Remove it. */
+ rtx src = (GET_CODE (temp) == SUBREG)
+ ? SUBREG_REG (temp) : temp;
+ rtx dst = (GET_CODE (target) == SUBREG)
+ ? SUBREG_REG (target) : target;
+
+ if (GET_MODE (src) == GET_MODE (dst))
+ {
+ promoted = false;
+ ext_redundant = true;
+ }
+ }
+
if (temp == target)
;
+ else if (ext_redundant)
+ {
+ rtx src = (GET_CODE (temp) == SUBREG)
+ ? SUBREG_REG (temp) : temp;
+ rtx dst = (GET_CODE (target) == SUBREG)
+ ? SUBREG_REG (target) : target;
+
+ emit_move_insn (dst, src);
+ }
else if (promoted)
{
int unsignedp = SUBREG_PROMOTED_UNSIGNED_P (target);
@@ -1118,6 +1118,58 @@ do_compare_and_jump (tree treeop0, tree treeop1, enum rtx_code signed_code,
type = TREE_TYPE (treeop1);
mode = TYPE_MODE (type);
}
+
+ /* Is zero/sign extension redundant as per VRP. */
+ bool op0_extension_redundant = false;
+ bool op1_extension_redundant = false;
+
+ if (GET_CODE (op0) == SUBREG && SUBREG_PROMOTED_VAR_P (op0))
+ op0_extension_redundant = tree_ssa_is_extension_redundant (treeop0);
+
+ if (GET_CODE (op1) == SUBREG && SUBREG_PROMOTED_VAR_P (op1))
+ op1_extension_redundant = tree_ssa_is_extension_redundant (treeop1);
+
+ /* If zero/sign extension is redundant, generate RTL
+ for operands without zero/sign extension. */
+ if ((op0_extension_redundant || TREE_CODE (treeop0) == INTEGER_CST)
+ && (op1_extension_redundant || TREE_CODE (treeop1) == INTEGER_CST))
+ {
+ if ((TREE_CODE (treeop1) == INTEGER_CST)
+ && (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (treeop1))) <= UNITS_PER_WORD))
+ {
+ /* First operand is 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)
+ && (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (treeop0)))
+ <= UNITS_PER_WORD))
+ {
+ /* Other operand is 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))
+ {
+ /* both operands are not constant. */
+ 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));
+ if (GET_MODE (new_op0) == GET_MODE (new_op1))
+ {
+ op0 = new_op0;
+ op1 = new_op1;
+ }
+ }
+ }
+
unsignedp = TYPE_UNSIGNED (type);
code = unsignedp ? unsigned_code : signed_code;
@@ -147,6 +147,15 @@ struct GTY(()) ptr_info_def
unsigned int misalign;
};
+struct GTY (()) range_info_def {
+ double_int min;
+ double_int max;
+ /* if an assignment gimple statement has RHS expression that can fit
+ LHS type, zero/sign extension to truncate is redundant.
+ Set this if we detect extension as redundant during VRP. */
+ bool sign_zero_ext_redundant;
+};
+
/* It is advantageous to avoid things like life analysis for variables which
do not need PHI nodes. This enum describes whether or not a particular
@@ -532,6 +541,7 @@ extern void replace_ssa_name_symbol (tree, tree);
extern bool get_ptr_info_alignment (struct ptr_info_def *, unsigned int *,
unsigned int *);
extern void mark_ptr_info_alignment_unknown (struct ptr_info_def *);
+extern void mark_range_info_unknown (struct range_info_def *);
extern void set_ptr_info_alignment (struct ptr_info_def *, unsigned int,
unsigned int);
extern void adjust_ptr_info_misalignment (struct ptr_info_def *,
@@ -151,7 +151,11 @@ make_ssa_name_fn (struct function *fn, tree var, gimple stmt)
SET_SSA_NAME_VAR_OR_IDENTIFIER (t, var);
}
SSA_NAME_DEF_STMT (t) = stmt;
- SSA_NAME_PTR_INFO (t) = NULL;
+ if (POINTER_TYPE_P (TREE_TYPE (t)))
+ SSA_NAME_PTR_INFO (t) = NULL;
+ else
+ SSA_NAME_RANGE_INFO (t) = NULL;
+
SSA_NAME_IN_FREE_LIST (t) = 0;
SSA_NAME_IS_DEFAULT_DEF (t) = 0;
imm = &(SSA_NAME_IMM_USE_NODE (t));
@@ -266,6 +270,14 @@ mark_ptr_info_alignment_unknown (struct ptr_info_def *pi)
pi->misalign = 0;
}
+/* State that the range described by RI has default values. */
+
+void
+mark_range_info_unknown (struct range_info_def *ri)
+{
+ ri->sign_zero_ext_redundant = false;
+}
+
/* Store the the power-of-two byte alignment and the deviation from that
alignment of pointer described by PI to ALIOGN and MISALIGN
respectively. */
@@ -359,6 +371,26 @@ duplicate_ssa_name_ptr_info (tree name, struct ptr_info_def *ptr_info)
SSA_NAME_PTR_INFO (name) = new_ptr_info;
}
+/* Creates a duplicate of the range_info_def at RANGE_INFO for use by
+ the SSA name NAME. */
+void
+duplicate_ssa_name_range_info (tree name, struct range_info_def *range_info)
+{
+ struct range_info_def *new_range_info;
+
+ gcc_assert (!POINTER_TYPE_P (TREE_TYPE (name)));
+ gcc_assert (!SSA_NAME_RANGE_INFO (name));
+
+ if (!range_info)
+ return;
+
+ new_range_info = ggc_alloc_range_info_def ();
+ *new_range_info = *range_info;
+
+ SSA_NAME_RANGE_INFO (name) = new_range_info;
+}
+
+
/* Creates a duplicate of a ssa name NAME tobe defined by statement STMT
in function FN. */
@@ -367,10 +399,20 @@ tree
duplicate_ssa_name_fn (struct function *fn, tree name, gimple stmt)
{
tree new_name = copy_ssa_name_fn (fn, name, stmt);
- struct ptr_info_def *old_ptr_info = SSA_NAME_PTR_INFO (name);
+ if (POINTER_TYPE_P (TREE_TYPE (name)))
+ {
+ struct ptr_info_def *old_ptr_info = SSA_NAME_PTR_INFO (name);
+
+ if (old_ptr_info)
+ duplicate_ssa_name_ptr_info (new_name, old_ptr_info);
+ }
+ else
+ {
+ struct range_info_def *old_range_info = SSA_NAME_RANGE_INFO (name);
- if (old_ptr_info)
- duplicate_ssa_name_ptr_info (new_name, old_ptr_info);
+ if (old_range_info)
+ duplicate_ssa_name_range_info (new_name, old_range_info);
+ }
return new_name;
}
@@ -8925,6 +8925,165 @@ simplify_float_conversion_using_ranges (gimple_stmt_iterator *gsi, gimple stmt)
return true;
}
+/* Check to see if the value range can be safely contained in
+ with the type. i.e. zero/sign extension to truncate is redyndant. */
+static bool
+range_fits_type (value_range_t *vr, tree type)
+{
+ tree type_min = TYPE_MINVAL (type);
+ tree type_max = TYPE_MAXVAL (type);
+ bool unsigned_cmp = false;
+
+ /* if value range overflows. */
+ if (vr->type != VR_RANGE
+ || is_overflow_infinity (vr->min)
+ || is_overflow_infinity (vr->max)
+ || is_negative_overflow_infinity (vr->min)
+ || is_negative_overflow_infinity (vr->max))
+ return false;
+
+ /* Unsigged type and value range is positive. */
+ if (TYPE_UNSIGNED (type)
+ && value_range_nonnegative_p (vr))
+ unsigned_cmp = true;
+
+ if (unsigned_cmp)
+ {
+ /* unsigned type and positive value range. */
+ return INT_CST_LT_UNSIGNED (vr->max, type_max)
+ || operand_equal_p (vr->max, type_max, 0);
+ }
+ else
+ {
+ bool min_ok, max_ok;
+ /* signed comparision. */
+ min_ok = INT_CST_LT (type_min, vr->min)
+ || operand_equal_p (type_min, vr->min, 0);
+ max_ok = INT_CST_LT (vr->max, type_max)
+ || operand_equal_p (vr->max, type_max, 0);
+
+ return (min_ok && max_ok);
+ }
+}
+
+/* Check if the most significant bit in the value for the type is set.
+ if we are sure that msb is not set, return false.
+ If in doubt return true. */
+
+static bool
+is_msb_set (tree val, tree type)
+{
+ tree msb;
+
+ if (TREE_CODE (val) != INTEGER_CST || TYPE_SIZE (type) == NULL_TREE)
+ return true;
+
+ switch (GET_MODE_SIZE (TYPE_MODE (type)))
+ {
+ case 1:
+ msb = build_int_cstu (integer_type_node, 0x80);
+ break;
+ case 2:
+ msb = build_int_cstu (integer_type_node, 0x8000);
+ break;
+ case 4:
+ msb = build_int_cstu (integer_type_node, 0x80000000);
+ break;
+ default:
+ return true;
+ }
+
+ if (operand_less_p (val, msb) == 1)
+ return false;
+
+ return true;
+}
+
+
+/* process gimple assign stmts and see if the sign/zero extensions are
+ redundant. i.e. if an assignment gimple statement has RHS expression
+ value that can fit in LHS type, truncation is redundant. Zero/sign
+ extensions in this case can be removed.
+
+ Example, if the register 117 in the following rtl has a value range
+ that can fit in HImode, zero_extend here is redundant.
+ (set (reg:SI 110)
+ (zero_extend:SI (subreg:HI (reg:SI 117) 0)))
+
+ Therefore the zero_extend can be droped and we can generate the following
+ rtl instead
+ (set (reg:SI 110)
+ (reg:SI 117))
+
+ process GIMPLE_ASSIGN stmts based on the VRP information and return true if
+ tree_ssa assigned with this statement has redundant zero/sign extension;
+ false otherwise. */
+
+static bool
+is_zero_sign_extension_redundant (gimple stmt)
+{
+ enum tree_code stmt_code = gimple_assign_rhs_code (stmt);
+ tree lhs = gimple_get_lhs (stmt);
+ tree rhs1 = gimple_assign_rhs1 (stmt);
+
+ /* Skip if the lhs is not integeral. */
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (lhs))
+ || CONSTANT_CLASS_P (lhs))
+ return false;
+
+ /* Process unary assign stmt. */
+ if (TREE_CODE_CLASS (stmt_code) == tcc_unary)
+ {
+ value_range_t vr = VR_INITIALIZER;
+ value_range_t *p_vr = &vr;
+
+ /* Get the value range for the expression. */
+ if ((stmt_code == NOP_EXPR || stmt_code == CONVERT_EXPR)
+ && (TREE_CODE (rhs1) == SSA_NAME))
+ p_vr = get_value_range (rhs1);
+ else
+ extract_range_from_unary_expr (p_vr,
+ gimple_assign_rhs_code (stmt),
+ TREE_TYPE (rhs1), rhs1);
+ if (range_int_cst_p (p_vr))
+ {
+ if (range_fits_type (p_vr, TREE_TYPE (lhs)))
+ return true;
+ }
+ }
+ /* Process binary assign stmt. */
+ else if (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt))
+ == tcc_binary)
+ {
+ value_range_t vr = VR_INITIALIZER;
+ tree rhs2 = gimple_assign_rhs2 (stmt);
+ gcc_assert (rhs1);
+ gcc_assert (rhs2);
+
+ /* If MSB is set for the constant operand, RTL will
+ sign extend even when it is positive.
+ Skip such stmts for zero/sign extension processing. */
+
+ if ((CONSTANT_CLASS_P (rhs1)
+ && is_msb_set (rhs1, TREE_TYPE (lhs)))
+ || (CONSTANT_CLASS_P(rhs2)
+ && is_msb_set (rhs2, TREE_TYPE (lhs))))
+ return false;
+
+ /* Get the value range for the expression. */
+ extract_range_from_binary_expr (&vr,
+ gimple_assign_rhs_code (stmt),
+ TREE_TYPE (rhs1), rhs1, rhs2);
+ if (range_int_cst_p (&vr))
+ {
+ if (range_fits_type (&vr, TREE_TYPE (lhs)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Simplify STMT using ranges if possible. */
static bool
@@ -8936,6 +9095,12 @@ simplify_stmt_using_ranges (gimple_stmt_iterator *gsi)
enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
tree rhs1 = gimple_assign_rhs1 (stmt);
+ /* check if zero/sign extension is redundant for ssa defined
+ statement and update if it is. */
+ if (!POINTER_TYPE_P (TREE_TYPE (gimple_assign_lhs (stmt)))
+ && is_zero_sign_extension_redundant (stmt))
+ tree_ssa_set_extension_redundant (gimple_assign_lhs (stmt), true);
+
switch (rhs_code)
{
case EQ_EXPR:
@@ -269,6 +269,63 @@ const char * const omp_clause_code_name[] =
"mergeable"
};
+/* Return the value range info type for ssa. */
+
+int
+tree_ssa_vrp_info_type (tree_ssa_name *ssa)
+{
+ gcc_assert (ssa);
+ gimple stmt = ssa->def_stmt;
+
+ /* if not a pointer type, it is range_info. */
+ if (stmt && is_gimple_assign (stmt))
+ {
+ tree lhs = gimple_assign_lhs (stmt);
+ if (!POINTER_TYPE_P (TREE_TYPE (lhs)))
+ return TREE_SSA_RANGE_INFO;
+ }
+
+ return TREE_SSA_PTR_INFO;
+}
+
+/* Is zero/sign extension redundant for an ssa def stmt. */
+
+bool
+tree_ssa_is_extension_redundant (tree ssa)
+{
+ gcc_assert (TREE_CODE (ssa) == SSA_NAME);
+ gimple stmt = SSA_NAME_DEF_STMT (ssa);
+
+ if (is_gimple_assign (stmt))
+ {
+ range_info_def *ri = SSA_NAME_RANGE_INFO (ssa);
+
+ if (ri && ri->sign_zero_ext_redundant)
+ return true;
+ }
+
+ return false;
+}
+
+/* Set zero/sign extension redundant for ssa def stmt. */
+
+void
+tree_ssa_set_extension_redundant (tree ssa, bool val)
+{
+ gimple stmt = SSA_NAME_DEF_STMT (ssa);
+ gcc_assert (!POINTER_TYPE_P (TREE_TYPE (ssa)));
+ gcc_assert (TREE_CODE (ssa) == SSA_NAME);
+ range_info_def *ri = SSA_NAME_RANGE_INFO (ssa);
+
+ if (ri == NULL)
+ {
+ ri = ggc_alloc_cleared_range_info_def ();
+ mark_range_info_unknown (ri);
+ SSA_NAME_RANGE_INFO (ssa) = ri;
+ }
+
+ ri->sign_zero_ext_redundant = val;
+}
/* Return the tree node structure used by tree code CODE. */
@@ -1950,10 +1950,15 @@ struct GTY(()) tree_exp {
/* Attributes for SSA_NAMEs for pointer-type variables. */
#define SSA_NAME_PTR_INFO(N) \
- SSA_NAME_CHECK (N)->ssa_name.ptr_info
+ SSA_NAME_CHECK (N)->ssa_name.vrp.ptr_info
+
+/* Range info Attributes for SSA_NAMEs of non pointer-type variables. */
+#define SSA_NAME_RANGE_INFO(N) \
+ SSA_NAME_CHECK (N)->ssa_name.vrp.range_info
/* Defined in tree-flow.h. */
struct ptr_info_def;
+struct range_info_def;
/* Immediate use linking structure. This structure is used for maintaining
a doubly linked list of uses of an SSA_NAME. */
@@ -1969,6 +1974,12 @@ typedef struct GTY(()) ssa_use_operand_d {
tree *GTY((skip(""))) use;
} ssa_use_operand_t;
+
+/* The garbage collector needs to know the interpretation of the
+ value range info in the tree_ssa_name. */
+#define TREE_SSA_PTR_INFO (0)
+#define TREE_SSA_RANGE_INFO (1)
+
/* Return the immediate_use information for an SSA_NAME. */
#define SSA_NAME_IMM_USE_NODE(NODE) SSA_NAME_CHECK (NODE)->ssa_name.imm_uses
@@ -1981,8 +1992,13 @@ struct GTY(()) tree_ssa_name {
/* Statement that defines this SSA name. */
gimple def_stmt;
- /* Pointer attributes used for alias analysis. */
- struct ptr_info_def *ptr_info;
+ /* value range information. */
+ union vrp_info_type {
+ /* Pointer attributes used for alias analysis. */
+ struct GTY ((tag ("TREE_SSA_PTR_INFO"))) ptr_info_def *ptr_info;
+ /* range attributes used for zero/sign extension elimination. */
+ struct GTY ((tag ("TREE_SSA_RANGE_INFO"))) range_info_def *range_info;
+ } GTY ((desc ("tree_ssa_vrp_info_type (&%1)"))) vrp;
/* Immediate uses list for this SSA_NAME. */
struct ssa_use_operand_d imm_uses;
@@ -5361,6 +5377,15 @@ extern tree skip_simple_constant_arithmetic (tree);
enum tree_node_structure_enum tree_node_structure (const_tree);
+/* Return the value range info type for ssa. */
+int tree_ssa_vrp_info_type (tree_ssa_name *);
+
+/* Is zero/sign extension redundant for an ssa def stmt. */
+bool tree_ssa_is_extension_redundant (tree);
+
+/* set zero/sign extension redundant for an ssa def stmt. */
+void tree_ssa_set_extension_redundant (tree ssa, bool val);
+
/* Return true if EXP contains a PLACEHOLDER_EXPR, i.e. if it represents a
size or offset that depends on a field within a record. */