From 684595188dea02d246edb66106d82bb7a9a22d79 Mon Sep 17 00:00:00 2001 From: Siddhesh Poyarekar <siddhesh@gotplt.org> Date: Tue, 19 Nov 2024 22:51:31 -0500 Subject: [PATCH] tree-optimization/117355: object size for PHI nodes with negative offsets When the object size estimate is returned for a PHI node, it is the maximum possible value, which is fine in isolation. When combined with negative offsets however, it may sometimes end up in zero size because the resultant size was larger than the wholesize, leading size_for_offset to conclude that there's a potential underflow. Fix this by allowing a non-strict mode to size_for_offset, which conservatively returns the size (or wholesize) in case of a negative offset. gcc/ChangeLog: PR tree-optimization/117355 * tree-object-size.cc (size_for_offset): New argument STRICT, return SZ if it is set to false. (plus_stmt_object_size): Adjust call to SIZE_FOR_OFFSET. gcc/testsuite/ChangeLog: PR tree-optimization/117355 * g++.dg/ext/builtin-object-size2.C (test9): New test. (main): Call it. * gcc.dg/builtin-object-size-3.c (test10): Adjust expected size. Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org> --- .../g++.dg/ext/builtin-object-size2.C | 27 ++++++++++++++++++ gcc/testsuite/gcc.dg/builtin-object-size-3.c | 2 +- gcc/tree-object-size.cc | 28 +++++++++++++++---- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/gcc/testsuite/g++.dg/ext/builtin-object-size2.C b/gcc/testsuite/g++.dg/ext/builtin-object-size2.C index 7a8f4e627332..45401b5a9c13 100644 --- a/gcc/testsuite/g++.dg/ext/builtin-object-size2.C +++ b/gcc/testsuite/g++.dg/ext/builtin-object-size2.C @@ -406,6 +406,32 @@ test8 (union F *f) FAIL (); } +// PR117355 +#define STR "bbbbbbbbbbbbbbbbbbbbbbbbbbb" + +void +__attribute__ ((noinline)) +test9 (void) +{ + char line[256]; + const char *p = STR; + const char *q = p + sizeof (STR) - 1; + + char *q1 = line; + for (const char *p1 = p; p1 < q;) + { + *q1++ = *p1++; + + if (p1 < q && (*q1++ = *p1++) != '\0') + { + if (__builtin_object_size (q1 - 2, 0) == 0) + __builtin_abort (); + if (__builtin_object_size (q1 - 2, 1) == 0) + __builtin_abort (); + } + } +} + int main (void) { @@ -430,5 +456,6 @@ main (void) union F f, *fp = &f; __asm ("" : "+r" (fp)); test8 (fp); + test9 (); DONE (); } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c index ec2c62c96401..e0c967e003f6 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c @@ -619,7 +619,7 @@ test10 (void) if (__builtin_object_size (p - 3, 2) != sizeof (buf) - i + 3) FAIL (); #else - if (__builtin_object_size (p - 3, 2) != 0) + if (__builtin_object_size (p - 3, 2) != 3) FAIL (); #endif break; diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc index 09aad88498ea..6413ebcca37c 100644 --- a/gcc/tree-object-size.cc +++ b/gcc/tree-object-size.cc @@ -344,7 +344,8 @@ init_offset_limit (void) be positive and hence, be within OFFSET_LIMIT for valid offsets. */ static tree -size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE) +size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE, + bool strict = true) { gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype)); @@ -377,9 +378,17 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE) return sz; /* Negative or too large offset even after adjustment, cannot be within - bounds of an object. */ + bounds of an object. The exception here is when the base object size + has been overestimated (e.g. through PHI nodes or a COND_EXPR) and the + adjusted offset remains negative. If the caller wants to be + permissive, return the base size. */ if (compare_tree_int (offset, offset_limit) > 0) - return size_zero_node; + { + if (strict) + return size_zero_node; + else + return sz; + } } return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset); @@ -1521,16 +1530,23 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt) addr_object_size (osi, op0, object_size_type, &bytes, &wholesize); } + bool pos_offset = (size_valid_p (op1, 0) + && compare_tree_int (op1, offset_limit) <= 0); + /* size_for_offset doesn't make sense for -1 size, but it does for size 0 since the wholesize could be non-zero and a negative offset could give a non-zero size. */ if (size_unknown_p (bytes, 0)) ; + /* In the static case, We want SIZE_FOR_OFFSET to go a bit easy on us if + it sees a negative offset since BYTES could have been + overestimated. */ else if ((object_size_type & OST_DYNAMIC) || bytes != wholesize - || (size_valid_p (op1, object_size_type) - && compare_tree_int (op1, offset_limit) <= 0)) - bytes = size_for_offset (bytes, op1, wholesize); + || pos_offset) + bytes = size_for_offset (bytes, op1, wholesize, + ((object_size_type & OST_DYNAMIC) + || pos_offset)); /* In the static case, with a negative offset, the best estimate for minimum size is size_unknown but for maximum size, the wholesize is a better estimate than size_unknown. */ -- GitLab