From 2d143ba8cfef7ef480c639882fd5518b7afd822b Mon Sep 17 00:00:00 2001 From: Richard Biener Date: Fri, 7 Apr 2017 08:20:24 +0000 Subject: [PATCH] re PR middle-end/80341 (gcc miscompiles division of signed char) 2017-04-07 Richard Biener 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 | 9 ++ gcc/convert.c | 158 ++++++++++++++----------- gcc/testsuite/ChangeLog | 5 + gcc/testsuite/gcc.dg/torture/pr80341.c | 19 +++ gcc/tree.c | 20 +++- 5 files changed, 136 insertions(+), 75 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/torture/pr80341.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 80ff2add49d..e6ad7cefd92 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2017-04-07 Richard Biener + + 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 * tree-affine.c (wide_int_ext_for_comb): Take type rather 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; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 0cb18d4cd4c..fc772c46723 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-04-07 Richard Biener + + PR middle-end/80341 + * gcc.dg/torture/pr80341.c: New testcase. + 2017-04-06 Jakub Jelinek 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 00000000000..40cf660dff9 --- /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 c87b7695c82..c18889caeb5 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; } -- 2.30.2