This fixes an old bug I introduced.  We may not pass in the final
truncation type to get_unwidened for divisions but need to preserve
the original values.  That means the INTEGER_CST handling needs
extension for the !for_type case as well.  And some refactoring.

(as much as I hate working on this premature folding code...)

Bootstrap and regtest running on x86_64-unknown-linux-gnu.

Richard.

2017-04-06  Richard Biener  <rguent...@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.

Index: gcc/tree.c
===================================================================
*** gcc/tree.c  (revision 246728)
--- gcc/tree.c  (working copy)
*************** get_unwidened (tree op, tree for_type)
*** 9033,9045 ****
        }
      }
  
!   /* If we finally reach a constant see if it fits in for_type 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);
  
    return win;
  }
--- 9033,9053 ----
        }
      }
  
!   /* If we finally reach a constant see if it fits in sth smaller and
       in that case convert it.  */
!   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;
  }
Index: gcc/convert.c
===================================================================
*** gcc/convert.c       (revision 246728)
--- gcc/convert.c       (working copy)
*************** convert_to_real_maybe_fold (tree type, t
*** 413,418 ****
--- 413,495 ----
    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,
*************** convert_to_integer_1 (tree type, tree ex
*** 719,726 ****
  
          case TRUNC_DIV_EXPR:
            {
!             tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type);
!             tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type);
  
              /* Don't distribute unless the output precision is at least as
                 big as the actual inputs and it has the same signedness.  */
--- 796,803 ----
  
          case TRUNC_DIV_EXPR:
            {
!             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.  */
*************** convert_to_integer_1 (tree type, tree ex
*** 738,744 ****
                  && (TYPE_UNSIGNED (TREE_TYPE (arg0))
                      || (TREE_CODE (arg1) == INTEGER_CST
                          && !integer_all_onesp (arg1))))
!               goto trunc1;
              break;
            }
  
--- 815,826 ----
                  && (TYPE_UNSIGNED (TREE_TYPE (arg0))
                      || (TREE_CODE (arg1) == INTEGER_CST
                          && !integer_all_onesp (arg1))))
!               {
!                 tree tem = do_narrow (loc, ex_form, type, arg0, arg1,
!                                       expr, inprec, outprec, dofold);
!                 if (tem)
!                   return tem;
!               }
              break;
            }
  
*************** convert_to_integer_1 (tree type, tree ex
*** 786,857 ****
                  || 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);
!                   }
                }
            }
            break;
--- 868,877 ----
                  || inprec > TYPE_PRECISION (TREE_TYPE (arg0))
                  || inprec > TYPE_PRECISION (TREE_TYPE (arg1)))
                {
!                 tree tem = do_narrow (loc, ex_form, type, arg0, arg1,
!                                       expr, inprec, outprec, dofold);
!                 if (tem)
!                   return tem;
                }
            }
            break;
Index: gcc/testsuite/gcc.dg/torture/pr80341.c
===================================================================
*** gcc/testsuite/gcc.dg/torture/pr80341.c      (nonexistent)
--- gcc/testsuite/gcc.dg/torture/pr80341.c      (working copy)
***************
*** 0 ****
--- 1,18 ----
+ /* { dg-do run } */
+ 
+ const signed char c = -84;
+ signed char s;
+ 
+ void
+ foo ()
+ {
+   s = (unsigned short) c / -55;
+ }
+ 
+ int
+ main ()
+ {
+   foo ();
+   if (s != 90)
+     __builtin_abort ();
+ }

Reply via email to