>>>>   tem = (char) 255 + (char) 1;
>>
>> tem is always of type 'char' in GIMPLE (even if later promoted
>> via PROMOTE_MODE) the value-range is a 'char' value-range and thus
>> never will exceed [CHAR_MIN, CHAR_MAX].  The only way you can
>> use that directly is if you can rely on undefined behavior
>> happening for signed overflow - but if you argue that way you
>> can simply _always_ drop the (sext:SI (subreg:QI part and you
>> do not need value ranges for this.  For unsigned operations
>> for example [250, 254] + [8, 10] will simply wrap to [3, 7]
>> (if I got the math correct) which is inside your [CHAR_MIN + 1,
>> CHAR_MAX - 1] but if performed in SImode you can get 259 and
>> thus clearly you cannot drop the (zext:SI (subreg:QI parts.
>> The same applies to signed types if you do not want to rely
>> on signed overflow being undefined of course.
>>
> 
> Thanks for the explanation. I now get it and I will rework the patch.
> 

I have attempted to implement what Richard suggested. If you think this
is what you want, I will go ahead and implement the missing gimple
binary statements.

Thanks again.
Kugan

diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 98983f4..60ce54b 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -3236,6 +3236,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 conversion, we can skip the SUBREG
+              and extension.  */
+           else if (promoted
+                    && is_assigned_exp_fit_type (lhs)
+                    && (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);
diff --git a/gcc/dojump.c b/gcc/dojump.c
index 2aef34d..0f3aeae 100644
--- a/gcc/dojump.c
+++ b/gcc/dojump.c
@@ -1143,6 +1143,62 @@ 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 = is_assigned_exp_fit_type (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 = is_assigned_exp_fit_type (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))))
+       {
+         /* If both comapre 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)
diff --git a/gcc/tree.c b/gcc/tree.c
index d363cfc..d22630a 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -408,6 +408,156 @@ tree_node_structure_for_code (enum tree_code code)
     }
 }
 
+/* Check expression value for overflow in tcc_binary.  */
+static bool
+is_binary_expr_fit_type (enum tree_code stmt_code,
+                        tree lhs, double_int min1, double_int max1,
+                        double_int min2, double_int max2)
+{
+  double_int dmin, dmax;
+  double_int type_max = tree_to_double_int (TYPE_MAX_VALUE (TREE_TYPE (lhs)));
+  double_int type_min = tree_to_double_int (TYPE_MIN_VALUE (TREE_TYPE (lhs)));
+  bool uns = TYPE_UNSIGNED (TREE_TYPE (lhs));
+
+  switch (stmt_code)
+    {
+    case PLUS_EXPR:
+      dmin = min1 + min2;
+      dmax = max1 + max2;
+      break;
+    case MINUS_EXPR:
+      dmin = min1 - min2;
+      dmax = max1 - max2;
+      break;
+    default:
+      return false;
+    }
+
+  if (dmin.cmp (type_min, uns) == -1)
+      return false;
+  else if (dmin.cmp (type_max, uns) == 1)
+      return false;
+  if (dmax.cmp (type_min, uns) == -1)
+      return false;
+  else if (dmax.cmp (type_max, uns) == 1)
+      return false;
+  return true;
+}
+
+/* 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.  */
+
+bool
+is_assigned_exp_fit_type (tree lhs)
+{
+  double_int type_min, type_max;
+  double_int min1, max1, min2, max2;
+  enum tree_code stmt_code;
+  tree rhs1, rhs2;
+  gimple stmt = SSA_NAME_DEF_STMT (lhs);
+  double_int msb_val;
+
+  if (gimple_code (stmt) != GIMPLE_ASSIGN)
+    return false;
+
+  stmt_code = gimple_assign_rhs_code (stmt);
+  rhs1 = gimple_assign_rhs1 (stmt);
+  bool uns = TYPE_UNSIGNED (TREE_TYPE (lhs));
+
+  /* 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 (uns)
+    msb_val = type_max.rshift (1);
+  else
+    msb_val = type_max + double_int_one;
+
+  if (TREE_CODE_CLASS (stmt_code) == tcc_unary)
+    {
+      /* Get the value range.  */
+      if (TREE_CODE (rhs1) == INTEGER_CST)
+       {
+         min1 = tree_to_double_int (rhs1);
+         max1 = tree_to_double_int (rhs1);
+       }
+      else if (get_range_info (rhs1, &min1, &max1) != VR_RANGE)
+       {
+         return false;
+       }
+
+      switch (stmt_code)
+       {
+       case VIEW_CONVERT_EXPR:
+       case CONVERT_EXPR:
+       case NOP_EXPR:
+         if ((TYPE_UNSIGNED (TREE_TYPE (lhs))
+              != TYPE_UNSIGNED (TREE_TYPE (rhs1)))
+             && (TYPE_PRECISION (TREE_TYPE (lhs))
+                 != TYPE_PRECISION (TREE_TYPE (rhs1))))
+           return false;
+
+         if (!uns && min1.cmp (msb_val, uns) == 1
+             && max1.cmp (msb_val, uns) == 1)
+           {
+             min1 = min1.sext (TYPE_PRECISION (TREE_TYPE (rhs1)));
+             max1 = max1.sext (TYPE_PRECISION (TREE_TYPE (rhs1)));
+           }
+
+         /* If rhs value range fits lhs type, zero/sign extension is
+           redundant.  */
+         if (max1.cmp (type_max, uns) != 1
+             && (type_min.cmp (min1, uns)) != 1)
+           return true;
+         else
+           return false;
+
+       case NEGATE_EXPR:
+         return is_binary_expr_fit_type (MINUS_EXPR, lhs, double_int_zero,
+                                         double_int_zero, min1, max1);
+       default:
+         return false;
+       }
+    }
+
+  if (TREE_CODE_CLASS (stmt_code) == tcc_binary)
+    {
+      double_int const_val;
+      rhs2 = gimple_assign_rhs2 (stmt);
+
+      /* Get the value range.  */
+      if (TREE_CODE (rhs1) == INTEGER_CST)
+       {
+         min1 = tree_to_double_int (rhs1);
+         max1 = tree_to_double_int (rhs1);
+       }
+      else if (get_range_info (rhs1, &min1, &max1) != VR_RANGE)
+       {
+         return false;
+       }
+
+      /* Get the value range.  */
+      if (TREE_CODE (rhs2) == INTEGER_CST)
+       {
+         min2 = tree_to_double_int (rhs2);
+         max2 = tree_to_double_int (rhs2);
+       }
+      else if (get_range_info (rhs2, &min2, &max2) != VR_RANGE)
+       {
+         return false;
+       }
+
+      return is_binary_expr_fit_type (stmt_code, lhs, min1, max1, min2, max2);
+    }
+
+  return false;
+}
 
 /* Initialize tree_contains_struct to describe the hierarchy of tree
    nodes.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 88c8d56..11a286c 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4134,6 +4134,7 @@ extern tree lhd_gcc_personality (void);
 extern void assign_assembler_name_if_neeeded (tree);
 extern void warn_deprecated_use (tree, tree);
 extern void cache_integer_cst (tree);
+extern bool is_assigned_exp_fit_type (tree lhs);
 
 /* Compare and hash for any structure which begins with a canonical
    pointer.  Assumes all pointers are interchangeable, which is sort

Reply via email to