https://gcc.gnu.org/g:f9516f109d89b5183c69d258bf147b0e48fdcf61
commit r15-9954-gf9516f109d89b5183c69d258bf147b0e48fdcf61 Author: Jakub Jelinek <ja...@redhat.com> Date: Fri Jul 4 07:50:12 2025 +0200 c-family: Tweak ptr +- (expr +- cst) FE optimization [PR120837] The following testcase is miscompiled with -fsanitize=undefined but we introduce UB into the IL even without that flag. The optimization ptr +- (expr +- cst) when expr/cst have undefined overflow into (ptr +- cst) +- expr is sometimes simply not valid, without careful analysis on what ptr points to we don't know if it is valid to do (ptr +- cst) pointer arithmetics. E.g. on the testcase, ptr points to start of an array (actually conditionally one or another) and cst is -1, so ptr - 1 is invalid pointer arithmetics, while ptr + (expr - 1) can be valid if expr is at runtime always > 1 and smaller than size of the array ptr points to + 1. Unfortunately, removing this 1992-ish optimization altogether causes FAIL: c-c++-common/restrict-2.c -Wc++-compat scan-tree-dump-times lim2 "Moving statement" 11 FAIL: gcc.dg/tree-ssa/copy-headers-5.c scan-tree-dump ch2 "is now do-while loop" FAIL: gcc.dg/tree-ssa/copy-headers-5.c scan-tree-dump-times ch2 " if " 3 FAIL: gcc.dg/vect/pr57558-2.c scan-tree-dump vect "vectorized 1 loops" FAIL: gcc.dg/vect/pr57558-2.c -flto -ffat-lto-objects scan-tree-dump vect "vectorized 1 loops" regressions (restrict-2.c also for C++ in all std modes). I've been thinking about some match.pd optimization for signed integer addition/subtraction of constant followed by widening integral conversion followed by multiplication or left shift, but that wouldn't help 32-bit arches. So, instead at least for now, the following patch keeps doing the optimization, just doesn't perform it in pointer arithmetics. pointer_int_sum itself actually adds the multiplication by size_exp, so ptr + expr is turned into ptr p+ expr * size_exp, so this patch will try to optimize ptr + (expr +- cst) into ptr p+ ((sizetype)expr * size_exp +- (sizetype)cst * size_exp) and ptr - (expr +- cst) into ptr p+ -((sizetype)expr * size_exp +- (sizetype)cst * size_exp) 2025-07-04 Jakub Jelinek <ja...@redhat.com> PR c/120837 * c-common.cc (pointer_int_sum): Rewrite the intop PLUS_EXPR or MINUS_EXPR optimization into extension of both intop operands, their separate multiplication and then addition/subtraction followed by rest of pointer_int_sum handling after the multiplication. * gcc.dg/ubsan/pr120837.c: New test. (cherry picked from commit e16820d4f7ab1d8a40f70beef722e6f8a4c2392c) Diff: --- gcc/c-family/c-common.cc | 49 +++++++++++++++++++++++++---------- gcc/testsuite/gcc.dg/ubsan/pr120837.c | 32 +++++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index 587d76461e9e..65478c3c37a8 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -3437,20 +3437,41 @@ pointer_int_sum (location_t loc, enum tree_code resultcode, an overflow error if the constant is negative but INTOP is not. */ && (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (intop)) || (TYPE_PRECISION (TREE_TYPE (intop)) - == TYPE_PRECISION (TREE_TYPE (ptrop))))) - { - enum tree_code subcode = resultcode; - tree int_type = TREE_TYPE (intop); - if (TREE_CODE (intop) == MINUS_EXPR) - subcode = (subcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR); - /* Convert both subexpression types to the type of intop, - because weird cases involving pointer arithmetic - can result in a sum or difference with different type args. */ - ptrop = build_binary_op (EXPR_LOCATION (TREE_OPERAND (intop, 1)), - subcode, ptrop, - convert (int_type, TREE_OPERAND (intop, 1)), - true); - intop = convert (int_type, TREE_OPERAND (intop, 0)); + == TYPE_PRECISION (TREE_TYPE (ptrop)))) + && TYPE_PRECISION (TREE_TYPE (intop)) <= TYPE_PRECISION (sizetype)) + { + tree intop0 = TREE_OPERAND (intop, 0); + tree intop1 = TREE_OPERAND (intop, 1); + if (TYPE_PRECISION (TREE_TYPE (intop)) != TYPE_PRECISION (sizetype) + || TYPE_UNSIGNED (TREE_TYPE (intop)) != TYPE_UNSIGNED (sizetype)) + { + tree optype = c_common_type_for_size (TYPE_PRECISION (sizetype), + TYPE_UNSIGNED (sizetype)); + intop0 = convert (optype, intop0); + intop1 = convert (optype, intop1); + } + tree t = fold_build2_loc (loc, MULT_EXPR, TREE_TYPE (intop0), intop0, + convert (TREE_TYPE (intop0), size_exp)); + intop0 = convert (sizetype, t); + if (TREE_OVERFLOW_P (intop0) && !TREE_OVERFLOW (t)) + intop0 = wide_int_to_tree (TREE_TYPE (intop0), wi::to_wide (intop0)); + t = fold_build2_loc (loc, MULT_EXPR, TREE_TYPE (intop1), intop1, + convert (TREE_TYPE (intop1), size_exp)); + intop1 = convert (sizetype, t); + if (TREE_OVERFLOW_P (intop1) && !TREE_OVERFLOW (t)) + intop1 = wide_int_to_tree (TREE_TYPE (intop1), wi::to_wide (intop1)); + intop = build_binary_op (EXPR_LOCATION (intop), TREE_CODE (intop), + intop0, intop1, true); + + /* Create the sum or difference. */ + if (resultcode == MINUS_EXPR) + intop = fold_build1_loc (loc, NEGATE_EXPR, sizetype, intop); + + ret = fold_build_pointer_plus_loc (loc, ptrop, intop); + + fold_undefer_and_ignore_overflow_warnings (); + + return ret; } /* Convert the integer argument to a type the same size as sizetype diff --git a/gcc/testsuite/gcc.dg/ubsan/pr120837.c b/gcc/testsuite/gcc.dg/ubsan/pr120837.c new file mode 100644 index 000000000000..97c85c751790 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/pr120837.c @@ -0,0 +1,32 @@ +/* PR c/120837 */ +/* { dg-do run } */ +/* { dg-options "-O1 -fsanitize=undefined -fno-sanitize-recover=undefined" } */ + +[[gnu::noipa]] void +bar (void **x, void **y) +{ + x[0] = 0; + x[1] = 0; + x[2] = 0; + y[0] = 0; + y[1] = 0; + y[2] = 0; + y[3] = 0; + y[4] = 0; +} + +[[gnu::noipa]] void * +foo (int x, int y) +{ + void *a[3]; + void *b[5]; + bar (a, b); + return (x > y ? b : a)[y - 1]; +} + +int +main () +{ + if (foo (2, 1) != 0) + __builtin_abort (); +}