From 3f3430400b2ce552a5dbdae49a3a482687b96afa Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Fri, 3 Feb 2017 16:38:15 +0000 Subject: [PATCH] PR tree-optimization/79352 - -fprintf-return-value doesn't handle flexible-like array members properly gcc/ChangeLog: PR tree-optimization/79352 * gimple-fold.c (get_range_strlen): Add argument. (get_range_strlen): Change return type to bool. (get_maxval_strlen): Pass in a dummy argument. * gimple-fold.h (get_range_strlen): Change return type to bool. * gimple-ssa-sprintf.c (get_string_length): Set unlikely counter. * tree.h (array_at_struct_end_p): Add argument. * tree.c (array_at_struct_end_p): Handle it. gcc/testsuite/ChangeLog: PR tree-optimization/79352 * gcc.dg/tree-ssa/pr79352.c: New test. From-SVN: r245156 --- gcc/ChangeLog | 11 ++++++ gcc/gimple-fold.c | 44 +++++++++++++++++------- gcc/gimple-fold.h | 2 +- gcc/gimple-ssa-sprintf.c | 8 +++-- gcc/testsuite/ChangeLog | 5 +++ gcc/testsuite/gcc.dg/tree-ssa/pr79352.c | 45 +++++++++++++++++++++++++ gcc/tree.c | 9 +++-- gcc/tree.h | 6 ++-- 8 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr79352.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 269d6197e06..a8203a43b42 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2017-02-03 Martin Sebor + + PR tree-optimization/79352 + * gimple-fold.c (get_range_strlen): Add argument. + (get_range_strlen): Change return type to bool. + (get_maxval_strlen): Pass in a dummy argument. + * gimple-fold.h (get_range_strlen): Change return type to bool. + * gimple-ssa-sprintf.c (get_string_length): Set unlikely counter. + * tree.h (array_at_struct_end_p): Add argument. + * tree.c (array_at_struct_end_p): Handle it. + 2017-02-03 Martin Liska PR lto/66295 diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index ef1afd10a00..1cd22a8bfdd 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -1177,11 +1177,15 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len) length and 2 for maximum value ARG can have. When FUZZY is set and the length of a string cannot be determined, the function instead considers as the maximum possible length the - size of a character array it may refer to. */ + size of a character array it may refer to. + Set *FLEXP to true if the range of the string lengths has been + obtained from the upper bound of an array at the end of a struct. + Such an array may hold a string that's longer than its upper bound + due to it being used as a poor-man's flexible array member. */ static bool get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, - bool fuzzy) + bool fuzzy, bool *flexp) { tree var, val; gimple *def_stmt; @@ -1202,7 +1206,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, if (TREE_CODE (aop0) == INDIRECT_REF && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME) return get_range_strlen (TREE_OPERAND (aop0, 0), - length, visited, type, fuzzy); + length, visited, type, fuzzy, flexp); } if (type == 2) @@ -1219,7 +1223,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, { if (TREE_CODE (arg) == ADDR_EXPR) return get_range_strlen (TREE_OPERAND (arg, 0), length, - visited, type, fuzzy); + visited, type, fuzzy, flexp); if (TREE_CODE (arg) == COMPONENT_REF && TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) == ARRAY_TYPE) @@ -1228,7 +1232,12 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, bound on the length of the array. This may be overly optimistic if the array itself isn't NUL-terminated and the caller relies on the subsequent member to contain - the NUL. */ + the NUL. + Set *FLEXP to true if the array whose bound is being + used is at the end of a struct. */ + if (array_at_struct_end_p (arg, true)) + *flexp = true; + arg = TREE_OPERAND (arg, 1); val = TYPE_SIZE_UNIT (TREE_TYPE (arg)); if (!val || integer_zerop (val)) @@ -1295,14 +1304,14 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, || gimple_assign_unary_nop_p (def_stmt)) { tree rhs = gimple_assign_rhs1 (def_stmt); - return get_range_strlen (rhs, length, visited, type, fuzzy); + return get_range_strlen (rhs, length, visited, type, fuzzy, flexp); } else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR) { tree op2 = gimple_assign_rhs2 (def_stmt); tree op3 = gimple_assign_rhs3 (def_stmt); - return get_range_strlen (op2, length, visited, type, fuzzy) - && get_range_strlen (op3, length, visited, type, fuzzy); + return get_range_strlen (op2, length, visited, type, fuzzy, flexp) + && get_range_strlen (op3, length, visited, type, fuzzy, flexp); } return false; @@ -1325,7 +1334,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, if (arg == gimple_phi_result (def_stmt)) continue; - if (!get_range_strlen (arg, length, visited, type, fuzzy)) + if (!get_range_strlen (arg, length, visited, type, fuzzy, flexp)) { if (fuzzy) *maxlen = build_all_ones_cst (size_type_node); @@ -1349,19 +1358,26 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, and array declared as 'char array[8]', MINMAXLEN[0] will be set to 3 and MINMAXLEN[1] to 7, the longest string that could be stored in array. -*/ + Return true if the range of the string lengths has been obtained + from the upper bound of an array at the end of a struct. Such + an array may hold a string that's longer than its upper bound + due to it being used as a poor-man's flexible array member. */ -void get_range_strlen (tree arg, tree minmaxlen[2]) +bool +get_range_strlen (tree arg, tree minmaxlen[2]) { bitmap visited = NULL; minmaxlen[0] = NULL_TREE; minmaxlen[1] = NULL_TREE; - get_range_strlen (arg, minmaxlen, &visited, 1, true); + bool flexarray = false; + get_range_strlen (arg, minmaxlen, &visited, 1, true, &flexarray); if (visited) BITMAP_FREE (visited); + + return flexarray; } tree @@ -1369,7 +1385,9 @@ get_maxval_strlen (tree arg, int type) { bitmap visited = NULL; tree len[2] = { NULL_TREE, NULL_TREE }; - if (!get_range_strlen (arg, len, &visited, type, false)) + + bool dummy; + if (!get_range_strlen (arg, len, &visited, type, false, &dummy)) len[1] = NULL_TREE; if (visited) BITMAP_FREE (visited); diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h index 38fb8e74e4d..e4931a1af52 100644 --- a/gcc/gimple-fold.h +++ b/gcc/gimple-fold.h @@ -24,7 +24,7 @@ along with GCC; see the file COPYING3. If not see extern tree canonicalize_constructor_val (tree, tree); extern tree get_symbol_constant_value (tree); -extern void get_range_strlen (tree, tree[2]); +extern bool get_range_strlen (tree, tree[2]); extern tree get_maxval_strlen (tree, int); extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree); extern bool fold_stmt (gimple_stmt_iterator *); diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 10c6d8e9719..3670bacd1f9 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -1800,7 +1800,7 @@ get_string_length (tree str) aren't known to point any such arrays result in LENRANGE[1] set to SIZE_MAX. */ tree lenrange[2]; - get_range_strlen (str, lenrange); + bool flexarray = get_range_strlen (str, lenrange); if (lenrange [0] || lenrange [1]) { @@ -1843,7 +1843,11 @@ get_string_length (tree str) res.range.min = 0; } - res.range.unlikely = res.range.max; + /* If the range of string length has been estimated from the size + of an array at the end of a struct assume that it's longer than + the array bound says it is in case it's used as a poor man's + flexible array member, such as in struct S { char a[4]; }; */ + res.range.unlikely = flexarray ? HOST_WIDE_INT_MAX : res.range.max; return res; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c5e31c75279..ff8ce70fed2 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-02-03 Martin Sebor + + PR tree-optimization/79352 + * gcc.dg/tree-ssa/pr79352.c: New test. + 2017-02-03 Martin Liska PR lto/66295 diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79352.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79352.c new file mode 100644 index 00000000000..4a153b71e43 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79352.c @@ -0,0 +1,45 @@ +/* PR tree-optimization/79352 - -fprintf-return-value doesn't handle + flexible-like array members properly + { dg-compile } + { dg-options "-O2 -fdump-tree-optimized" } */ + +struct A { int i; char a1[1]; }; +struct B { int i; char a3[3]; }; +struct C { int i; char ax[]; }; + +int test_array_1 (int i, struct A *a) +{ + return __builtin_snprintf (0, 0, "%-s", a->a1); +} + +int test_array_3 (int i, struct B *b) +{ + return __builtin_snprintf (0, 0, "%-s", b->a3); +} + +int test_array_1_3 (int i, struct A *a, struct B *b) +{ + return __builtin_snprintf (0, 0, "%-s", i ? a->a1 : b->a3); +} + +int test_string_and_array_3 (int i, struct B *b) +{ + return __builtin_snprintf (0, 0, "%-s", i ? "123" : b->a3); +} + +int test_flexarray (struct C *c) +{ + return __builtin_snprintf (0, 0, "%-s", c->ax); +} + +int test_array_and_flexarray (int i, struct B *b, struct C *c) +{ + return __builtin_snprintf (0, 0, "%-s", i ? b->a3 : c->ax); +} + +int test_string_and_flexarray (int i, struct C *c) +{ + return __builtin_snprintf (0, 0, "%-s", i ? "123" : c->ax); +} + +/* { dg-final { scan-tree-dump-times "snprintf" 7 "optimized"} } */ diff --git a/gcc/tree.c b/gcc/tree.c index 8426834342e..804ab5ed58a 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -13195,13 +13195,16 @@ array_ref_up_bound (tree exp) /* Returns true if REF is an array reference to an array at the end of a structure. If this is the case, the array may be allocated larger - than its upper bound implies. */ + than its upper bound implies. When ALLOW_COMPREF is true considers + REF when it's a COMPONENT_REF in addition ARRAY_REF and + ARRAY_RANGE_REF. */ bool -array_at_struct_end_p (tree ref) +array_at_struct_end_p (tree ref, bool allow_compref) { if (TREE_CODE (ref) != ARRAY_REF - && TREE_CODE (ref) != ARRAY_RANGE_REF) + && TREE_CODE (ref) != ARRAY_RANGE_REF + && (!allow_compref || TREE_CODE (ref) != COMPONENT_REF)) return false; while (handled_component_p (ref)) diff --git a/gcc/tree.h b/gcc/tree.h index 6341446245d..f63a678216e 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -4855,8 +4855,10 @@ extern tree array_ref_low_bound (tree); /* Returns true if REF is an array reference to an array at the end of a structure. If this is the case, the array may be allocated larger - than its upper bound implies. */ -extern bool array_at_struct_end_p (tree); + than its upper bound implies. When second argument is true considers + REF when it's a COMPONENT_REF in addition ARRAY_REF and + ARRAY_RANGE_REF. */ +extern bool array_at_struct_end_p (tree, bool = false); /* Return a tree representing the offset, in bytes, of the field referenced by EXP. This does not include any offset in DECL_FIELD_BIT_OFFSET. */ -- 2.30.2