summaryrefslogtreecommitdiff
path: root/gcc/convert.c
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 /gcc/convert.c
parent5291ab733b6584b2b0252178625088812f66e689 (diff)
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
Diffstat (limited to 'gcc/convert.c')
-rw-r--r--gcc/convert.c158
1 files changed, 89 insertions, 69 deletions
diff --git a/gcc/convert.c b/gcc/convert.c
index f50a1132de9..af8dfda0eb4 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;