The PR shows a case in which fold introduces undefined behavior in a valid program, because what it does here is -(long int) (ul + ULONG_MAX) - 1 -> ~(long int) (ul + ULONG_MAX) -> -(long int) ul But the latter transformation is wrong if ul is unsigned long and equals LONG_MAX + 1UL, because that introduces -LONG_MIN, and ubsan/-ftrapv errors on that, even though the program is valid. (Converting LONG_MAX + 1UL to long is implementation-defined.)
I tried to limit the folding a bit so that it doesn't creep in UB, but I would really appreciate second pair of eyes here... Bootstrapped/regtested on ppc64-linux, ok for trunk? 2014-12-04 Marek Polacek <pola...@redhat.com> PR middle-end/56917 * fold-const.c (fold_unary_loc): Don't introduce undefined behavior when transforming ~ (A - 1) or ~ (A + -1) to -A. * c-c++-common/ubsan/pr56917.c: New test. diff --git gcc/fold-const.c gcc/fold-const.c index 17eb5bb..1e56b28 100644 --- gcc/fold-const.c +++ gcc/fold-const.c @@ -8140,7 +8140,16 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) && ((TREE_CODE (arg0) == MINUS_EXPR && integer_onep (TREE_OPERAND (arg0, 1))) || (TREE_CODE (arg0) == PLUS_EXPR - && integer_all_onesp (TREE_OPERAND (arg0, 1))))) + && integer_all_onesp (TREE_OPERAND (arg0, 1)))) + /* Be careful not to introduce new overflows when + transforming to -A. That might happen if type of A + is unsigned type for TYPE and the value of A is equal + TYPE_MAX + 1. */ + && (TYPE_PRECISION (type) + != TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg0, 1))) + || TYPE_OVERFLOW_WRAPS (type) + || TYPE_UNSIGNED (type) + == TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg0, 1))))) return fold_build1_loc (loc, NEGATE_EXPR, type, fold_convert_loc (loc, type, TREE_OPERAND (arg0, 0))); diff --git gcc/testsuite/c-c++-common/ubsan/pr56917.c gcc/testsuite/c-c++-common/ubsan/pr56917.c index e69de29..e676962 100644 --- gcc/testsuite/c-c++-common/ubsan/pr56917.c +++ gcc/testsuite/c-c++-common/ubsan/pr56917.c @@ -0,0 +1,34 @@ +/* PR middle-end/56917 */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */ + +#define INT_MIN (-__INT_MAX__ - 1) +#define LONG_MIN (-__LONG_MAX__ - 1L) +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1LL) + +int __attribute__ ((noinline,noclone)) +fn1 (unsigned int u) +{ + return (-(int) (u - 1U)) - 1; +} + +long __attribute__ ((noinline,noclone)) +fn2 (unsigned long int ul) +{ + return (-(long) (ul - 1UL)) - 1L; +} + +long long __attribute__ ((noinline,noclone)) +fn3 (unsigned long long int ull) +{ + return (-(long long) (ull - 1ULL)) - 1LL; +} + +int +main (void) +{ + if (fn1 (__INT_MAX__ + 1U) != INT_MIN + || fn2 (__LONG_MAX__ + 1UL) != LONG_MIN + || fn3 (__LONG_LONG_MAX__ + 1ULL) != LLONG_MIN) + __builtin_abort (); +} Marek