re PR middle-end/80341 (gcc miscompiles division of signed char)
authorRichard Biener <rguenther@suse.de>
Fri, 7 Apr 2017 08:20:24 +0000 (08:20 +0000)
committerRichard Biener <rguenth@gcc.gnu.org>
Fri, 7 Apr 2017 08:20:24 +0000 (08:20 +0000)
2017-04-07  Richard Biener  <rguenther@suse.de>

PR middle-end/80341
* tree.c (get_unwidened): Also handle ! for_type case for
INTEGER_CSTs.
* convert.c (do_narrow): Split out from ...
(convert_to_integer_1): ... here.  Do not pass final truncation
type to get_unwidened for TRUNC_DIV_EXPR.

* gcc.dg/torture/pr80341.c: New testcase.

From-SVN: r246756

gcc/ChangeLog
gcc/convert.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/torture/pr80341.c [new file with mode: 0644]
gcc/tree.c

index 80ff2add49d0b3f5de6b9c40c47171c5532c12f2..e6ad7cefd9284163e0a28fa633ff446e648bb53e 100644 (file)
@@ -1,3 +1,12 @@
+2017-04-07  Richard Biener  <rguenther@suse.de>
+
+       PR middle-end/80341
+       * tree.c (get_unwidened): Also handle ! for_type case for
+       INTEGER_CSTs.
+       * convert.c (do_narrow): Split out from ...
+       (convert_to_integer_1): ... here.  Do not pass final truncation
+       type to get_unwidened for TRUNC_DIV_EXPR.
+
 2017-04-07  Richard Biener  <rguenther@suse.de>
 
        * tree-affine.c (wide_int_ext_for_comb): Take type rather
index f50a1132de9ad765555e90c519c61cfcb80c0fd0..af8dfda0eb44769885d2f06b92aac8b36c665ca5 100644 (file)
@@ -413,6 +413,83 @@ convert_to_real_maybe_fold (tree type, tree expr, bool dofold)
   return convert_to_real_1 (type, expr, dofold || CONSTANT_CLASS_P (expr));
 }
 
+/* Try to narrow EX_FORM ARG0 ARG1 in narrowed arg types producing a
+   result in TYPE.  */
+
+static tree
+do_narrow (location_t loc,
+          enum tree_code ex_form, tree type, tree arg0, tree arg1,
+          tree expr, unsigned inprec, unsigned outprec, bool dofold)
+{
+  /* Do the arithmetic in type TYPEX,
+     then convert result to TYPE.  */
+  tree typex = type;
+
+  /* Can't do arithmetic in enumeral types
+     so use an integer type that will hold the values.  */
+  if (TREE_CODE (typex) == ENUMERAL_TYPE)
+    typex = lang_hooks.types.type_for_size (TYPE_PRECISION (typex),
+                                           TYPE_UNSIGNED (typex));
+
+  /* But now perhaps TYPEX is as wide as INPREC.
+     In that case, do nothing special here.
+     (Otherwise would recurse infinitely in convert.  */
+  if (TYPE_PRECISION (typex) != inprec)
+    {
+      /* Don't do unsigned arithmetic where signed was wanted,
+        or vice versa.
+        Exception: if both of the original operands were
+        unsigned then we can safely do the work as unsigned.
+        Exception: shift operations take their type solely
+        from the first argument.
+        Exception: the LSHIFT_EXPR case above requires that
+        we perform this operation unsigned lest we produce
+        signed-overflow undefinedness.
+        And we may need to do it as unsigned
+        if we truncate to the original size.  */
+      if (TYPE_UNSIGNED (TREE_TYPE (expr))
+         || (TYPE_UNSIGNED (TREE_TYPE (arg0))
+             && (TYPE_UNSIGNED (TREE_TYPE (arg1))
+                 || ex_form == LSHIFT_EXPR
+                 || ex_form == RSHIFT_EXPR
+                 || ex_form == LROTATE_EXPR
+                 || ex_form == RROTATE_EXPR))
+         || ex_form == LSHIFT_EXPR
+         /* If we have !flag_wrapv, and either ARG0 or
+            ARG1 is of a signed type, we have to do
+            PLUS_EXPR, MINUS_EXPR or MULT_EXPR in an unsigned
+            type in case the operation in outprec precision
+            could overflow.  Otherwise, we would introduce
+            signed-overflow undefinedness.  */
+         || ((!TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg0))
+              || !TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg1)))
+             && ((TYPE_PRECISION (TREE_TYPE (arg0)) * 2u
+                  > outprec)
+                 || (TYPE_PRECISION (TREE_TYPE (arg1)) * 2u
+                     > outprec))
+             && (ex_form == PLUS_EXPR
+                 || ex_form == MINUS_EXPR
+                 || ex_form == MULT_EXPR)))
+       {
+         if (!TYPE_UNSIGNED (typex))
+           typex = unsigned_type_for (typex);
+       }
+      else
+       {
+         if (TYPE_UNSIGNED (typex))
+           typex = signed_type_for (typex);
+       }
+      /* We should do away with all this once we have a proper
+        type promotion/demotion pass, see PR45397.  */
+      expr = maybe_fold_build2_loc (dofold, loc, ex_form, typex,
+                                   convert (typex, arg0),
+                                   convert (typex, arg1));
+      return convert (type, expr);
+    }
+  
+  return NULL_TREE;
+}
+
 /* Convert EXPR to some integer (or enum) type TYPE.
 
    EXPR must be pointer, integer, discrete (enum, char, or bool), float,
@@ -719,8 +796,8 @@ convert_to_integer_1 (tree type, tree expr, bool dofold)
 
          case TRUNC_DIV_EXPR:
            {
-             tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type);
-             tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type);
+             tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), NULL_TREE);
+             tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), NULL_TREE);
 
              /* Don't distribute unless the output precision is at least as
                 big as the actual inputs and it has the same signedness.  */
@@ -738,7 +815,12 @@ convert_to_integer_1 (tree type, tree expr, bool dofold)
                  && (TYPE_UNSIGNED (TREE_TYPE (arg0))
                      || (TREE_CODE (arg1) == INTEGER_CST
                          && !integer_all_onesp (arg1))))
-               goto trunc1;
+               {
+                 tree tem = do_narrow (loc, ex_form, type, arg0, arg1,
+                                       expr, inprec, outprec, dofold);
+                 if (tem)
+                   return tem;
+               }
              break;
            }
 
@@ -786,72 +868,10 @@ convert_to_integer_1 (tree type, tree expr, bool dofold)
                  || inprec > TYPE_PRECISION (TREE_TYPE (arg0))
                  || inprec > TYPE_PRECISION (TREE_TYPE (arg1)))
                {
-                 /* Do the arithmetic in type TYPEX,
-                    then convert result to TYPE.  */
-                 tree typex = type;
-
-                 /* Can't do arithmetic in enumeral types
-                    so use an integer type that will hold the values.  */
-                 if (TREE_CODE (typex) == ENUMERAL_TYPE)
-                   typex
-                     = lang_hooks.types.type_for_size (TYPE_PRECISION (typex),
-                                                       TYPE_UNSIGNED (typex));
-
-                 /* But now perhaps TYPEX is as wide as INPREC.
-                    In that case, do nothing special here.
-                    (Otherwise would recurse infinitely in convert.  */
-                 if (TYPE_PRECISION (typex) != inprec)
-                   {
-                     /* Don't do unsigned arithmetic where signed was wanted,
-                        or vice versa.
-                        Exception: if both of the original operands were
-                        unsigned then we can safely do the work as unsigned.
-                        Exception: shift operations take their type solely
-                        from the first argument.
-                        Exception: the LSHIFT_EXPR case above requires that
-                        we perform this operation unsigned lest we produce
-                        signed-overflow undefinedness.
-                        And we may need to do it as unsigned
-                        if we truncate to the original size.  */
-                     if (TYPE_UNSIGNED (TREE_TYPE (expr))
-                         || (TYPE_UNSIGNED (TREE_TYPE (arg0))
-                             && (TYPE_UNSIGNED (TREE_TYPE (arg1))
-                                 || ex_form == LSHIFT_EXPR
-                                 || ex_form == RSHIFT_EXPR
-                                 || ex_form == LROTATE_EXPR
-                                 || ex_form == RROTATE_EXPR))
-                         || ex_form == LSHIFT_EXPR
-                         /* If we have !flag_wrapv, and either ARG0 or
-                            ARG1 is of a signed type, we have to do
-                            PLUS_EXPR, MINUS_EXPR or MULT_EXPR in an unsigned
-                            type in case the operation in outprec precision
-                            could overflow.  Otherwise, we would introduce
-                            signed-overflow undefinedness.  */
-                         || ((!TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg0))
-                              || !TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg1)))
-                             && ((TYPE_PRECISION (TREE_TYPE (arg0)) * 2u
-                                  > outprec)
-                                 || (TYPE_PRECISION (TREE_TYPE (arg1)) * 2u
-                                     > outprec))
-                             && (ex_form == PLUS_EXPR
-                                 || ex_form == MINUS_EXPR
-                                 || ex_form == MULT_EXPR)))
-                       {
-                         if (!TYPE_UNSIGNED (typex))
-                           typex = unsigned_type_for (typex);
-                       }
-                     else
-                       {
-                         if (TYPE_UNSIGNED (typex))
-                           typex = signed_type_for (typex);
-                       }
-                     /* We should do away with all this once we have a proper
-                        type promotion/demotion pass, see PR45397.  */
-                     expr = maybe_fold_build2_loc (dofold, loc, ex_form, typex,
-                                                   convert (typex, arg0),
-                                                   convert (typex, arg1));
-                     return convert (type, expr);
-                   }
+                 tree tem = do_narrow (loc, ex_form, type, arg0, arg1,
+                                       expr, inprec, outprec, dofold);
+                 if (tem)
+                   return tem;
                }
            }
            break;
index 0cb18d4cd4cf28bd873a77bd827d304c6bbe6b26..fc772c467239b846004ffb24cba21b5e86fcedf8 100644 (file)
@@ -1,3 +1,8 @@
+2017-04-07  Richard Biener  <rguenther@suse.de>
+
+       PR middle-end/80341
+       * gcc.dg/torture/pr80341.c: New testcase.
+
 2017-04-06  Jakub Jelinek  <jakub@redhat.com>
 
        PR debug/80234
diff --git a/gcc/testsuite/gcc.dg/torture/pr80341.c b/gcc/testsuite/gcc.dg/torture/pr80341.c
new file mode 100644 (file)
index 0000000..40cf660
--- /dev/null
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-additional-options "-Wno-overflow" } */
+
+const signed char c = -84;
+signed char s;
+
+void
+foo ()
+{
+  s = (unsigned short) c / -55;
+}
+
+int
+main ()
+{
+  foo ();
+  if (s != 90)
+    __builtin_abort ();
+}
index c87b7695c82ad2a342c4949a7e324e9d020a5184..c18889caeb564c1d599b3a0e7821b3ab11a9b2a2 100644 (file)
@@ -9033,13 +9033,21 @@ get_unwidened (tree op, tree for_type)
        }
     }
 
-  /* If we finally reach a constant see if it fits in for_type and
+  /* If we finally reach a constant see if it fits in sth smaller and
      in that case convert it.  */
-  if (for_type
-      && TREE_CODE (win) == INTEGER_CST
-      && TREE_TYPE (win) != for_type
-      && int_fits_type_p (win, for_type))
-    win = fold_convert (for_type, win);
+  if (TREE_CODE (win) == INTEGER_CST)
+    {
+      tree wtype = TREE_TYPE (win);
+      unsigned prec = wi::min_precision (win, TYPE_SIGN (wtype));
+      if (for_type)
+       prec = MAX (prec, final_prec);
+      if (prec < TYPE_PRECISION (wtype))
+       {
+         tree t = lang_hooks.types.type_for_size (prec, TYPE_UNSIGNED (wtype));
+         if (t && TYPE_PRECISION (t) < TYPE_PRECISION (wtype))
+           win = fold_convert (t, win);
+       }
+    }
 
   return win;
 }