aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Biener <rguenther@suse.de>2017-04-07 08:20:24 +0000
committerRichard Biener <rguenth@gcc.gnu.org>2017-04-07 08:20:24 +0000
commit2d143ba8cfef7ef480c639882fd5518b7afd822b (patch)
tree88b5a716645aa02574e38f724825464aecb22de9
parent5291ab733b6584b2b0252178625088812f66e689 (diff)
downloadgcc-2d143ba8cfef7ef480c639882fd5518b7afd822b.zip
gcc-2d143ba8cfef7ef480c639882fd5518b7afd822b.tar.gz
gcc-2d143ba8cfef7ef480c639882fd5518b7afd822b.tar.bz2
re PR middle-end/80341 (gcc miscompiles division of signed char)
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
-rw-r--r--gcc/ChangeLog9
-rw-r--r--gcc/convert.c158
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr80341.c19
-rw-r--r--gcc/tree.c20
5 files changed, 136 insertions, 75 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 80ff2ad..e6ad7ce 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,14 @@
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
than aff_tree.
(aff_combination_const): Adjust.
diff --git a/gcc/convert.c b/gcc/convert.c
index f50a113..af8dfda 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -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;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 0cb18d4..fc772c4 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..40cf660
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr80341.c
@@ -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 ();
+}
diff --git a/gcc/tree.c b/gcc/tree.c
index c87b769..c18889c 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -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;
}