In PTR + OFFSET cases, try harder to see if the target offset could result in a constant. Specifically, if the offset is a PHI node with all constant branches, return the minimum (or maximum for OST_MINIMUM) of the possible values.
gcc/ChangeLog: PR tree-optimization/116556 * tree-object-size.cc (try_collapsing_offset): New function. (plus_stmt_object_size): Use it. * gcc/testsuite/gcc.dg/builtin-object-size-1.c (test12, test13): New functions. (main): Call them. * gcc/testsuite/gcc.dg/builtin-object-size-3.c (test12, test13): New functions. (main): Call them. Signed-off-by: Siddhesh Poyarekar <siddh...@gotplt.org> --- gcc/testsuite/gcc.dg/builtin-object-size-1.c | 63 ++++++++++++++++++++ gcc/testsuite/gcc.dg/builtin-object-size-3.c | 63 ++++++++++++++++++++ gcc/tree-object-size.cc | 60 +++++++++++++++++++ 3 files changed, 186 insertions(+) diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c index d6d13c5ef7a..1f5cd5d99a1 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c @@ -712,6 +712,65 @@ test11 (void) } #endif +void +__attribute__ ((noinline)) +test12 (unsigned cond) +{ + char *buf2 = malloc (10); + char *p; + size_t t; + + if (cond) + t = 8; + else + t = 4; + + p = &buf2[t]; + +#ifdef __builtin_object_size + if (__builtin_object_size (p, 0) != (cond ? 2 : 6)) + FAIL (); +#else + if (__builtin_object_size (p, 0) != 6) + FAIL (); +#endif +} + +void +__attribute__ ((noinline)) +test13 (unsigned cond) +{ + char *buf2 = malloc (10); + char *p = &buf2[4]; + int t; + + if (cond) + t = -1; + else + t = -2; + +#ifdef __builtin_object_size + if (__builtin_object_size (&p[t], 0) != (cond ? 7 : 8)) + FAIL (); +#else + if (__builtin_object_size (&p[t], 0) != 8) + FAIL (); +#endif + + if (cond) + t = 1; + else + t = -3; + +#ifdef __builtin_object_size + if (__builtin_object_size (&p[t], 0) != (cond ? 5 : 9)) + FAIL (); +#else + if (__builtin_object_size (&p[t], 0) != 9) + FAIL (); +#endif +} + int main (void) { @@ -729,6 +788,10 @@ main (void) test10 (); #ifndef SKIP_STRNDUP test11 (); + test12 (1); + test12 (0); + test13 (1); + test13 (0); #endif DONE (); } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c index ec2c62c9640..7fad106fc27 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c @@ -720,6 +720,65 @@ test11 (void) } #endif +void +__attribute__ ((noinline)) +test12 (unsigned cond) +{ + char *buf2 = malloc (10); + char *p; + size_t t; + + if (cond) + t = 8; + else + t = 4; + + p = &buf2[t]; + +#ifdef __builtin_object_size + if (__builtin_object_size (p, 2) != (cond ? 2 : 6)) + FAIL (); +#else + if (__builtin_object_size (p, 2) != 2) + FAIL (); +#endif +} + +void +__attribute__ ((noinline)) +test13 (unsigned cond) +{ + char *buf2 = malloc (10); + char *p = &buf2[4]; + size_t t; + + if (cond) + t = -1; + else + t = -2; + +#ifdef __builtin_object_size + if (__builtin_object_size (&p[t], 2) != (cond ? 7 : 8)) + FAIL (); +#else + if (__builtin_object_size (&p[t], 2) != 7) + FAIL (); +#endif + + if (cond) + t = 1; + else + t = -3; + +#ifdef __builtin_object_size + if (__builtin_object_size (&p[t], 2) != (cond ? 5 : 9)) + FAIL (); +#else + if (__builtin_object_size (&p[t], 2) != 5) + FAIL (); +#endif +} + int main (void) { @@ -738,5 +797,9 @@ main (void) #ifndef SKIP_STRNDUP test11 (); #endif + test12 (1); + test12 (0); + test13 (1); + test13 (0); DONE (); } diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc index f8fae0cbc82..2dfc28407ab 100644 --- a/gcc/tree-object-size.cc +++ b/gcc/tree-object-size.cc @@ -1468,6 +1468,63 @@ merge_object_sizes (struct object_size_info *osi, tree dest, tree orig) return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig)); } +/* For constant sizes, try collapsing a non-constant offset to a constant if + possible. The case handled at the moment is when the offset is a PHI node + with all of its targets are constants. */ + +static tree +try_collapsing_offset (tree op, int object_size_type) +{ + gcc_assert (!(object_size_type & OST_DYNAMIC)); + + if (TREE_CODE (op) != SSA_NAME) + return op; + + gimple *stmt = SSA_NAME_DEF_STMT (op); + + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + /* Peek through casts. */ + if (gimple_assign_rhs_code (stmt) == NOP_EXPR) + { + tree ret = try_collapsing_offset (gimple_assign_rhs1 (stmt), + object_size_type); + if (TREE_CODE (ret) == INTEGER_CST) + return ret; + } + break; + case GIMPLE_PHI: + { + tree off = ((object_size_type & OST_MINIMUM) + ? TYPE_MIN_VALUE (ptrdiff_type_node) + : TYPE_MAX_VALUE (ptrdiff_type_node)); + + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + { + tree rhs = gimple_phi_arg_def (stmt, i); + + if (TREE_CODE (rhs) != INTEGER_CST) + return op; + + /* Note that this is the *opposite* of what we usually do with + sizes, because the maximum offset estimate here will give us a + minimum size estimate and vice versa. */ + enum tree_code code = (object_size_type & OST_MINIMUM + ? MAX_EXPR : MIN_EXPR); + + off = fold_build2 (code, ptrdiff_type_node, off, + fold_convert (ptrdiff_type_node, rhs)); + } + return fold_convert (sizetype, off); + } + default: + break; + } + + /* Nothing worked, so return OP untouched. */ + return op; +} /* Compute object_sizes for VAR, defined to the result of an assignment with operator POINTER_PLUS_EXPR. Return true if the object size might @@ -1500,6 +1557,9 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt) if (object_sizes_unknown_p (object_size_type, varno)) return false; + if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (op1) != INTEGER_CST) + op1 = try_collapsing_offset (op1, object_size_type); + /* Handle PTR + OFFSET here. */ if (size_valid_p (op1, object_size_type) && (TREE_CODE (op0) == SSA_NAME || TREE_CODE (op0) == ADDR_EXPR)) -- 2.45.1