https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83269
--- Comment #5 from Jakub Jelinek <jakub at gcc dot gnu.org> --- With -sanitize=signed-integer-overflow, we get in *.original: volatile unsigned char a = 1; long long int b = 2147483648; int c = (int) a * -2147483647 - (int) -b; instead of what we get without it: volatile unsigned char a = 1; long long int b = 2147483648; int c = (int) b + (int) a * -2147483647; and the testcase works in the former case, while fails with the latter. I see fold_binary_loc called with MINUS_EXPR on op0: (int) -(unsigned int) b op1: (int) a * -2147483647 and it returns (int) a * 2147483647 - (int) b which looks wrong if (int) b is INT_MIN. I believe it is the: /* (-A) - B -> (-B) - A where B is easily negated and we can swap. */ if (TREE_CODE (arg0) == NEGATE_EXPR && negate_expr_p (op1)) return fold_build2_loc (loc, MINUS_EXPR, type, negate_expr (op1), fold_convert_loc (loc, type, TREE_OPERAND (arg0, 0))); case in fold-const.c, supposedly we should either punt it TYPE_OVERFLOW_UNDEFINED (type) && !TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (arg0)), or do it in that case in unsigned type instead?