Bug 79352 points out that the gimple-ssa-printf pass doesn't allow
for an array at the end of a struct to be treated as a poor man's
flexible array member and hold a string that's longer than the upper
bound of the array.  Rather, the pass assumes that the string's
length must at most as long as the upper bound of the array - 1.

To allow for this the attached patch adjusts the get_range_strlen
function the pass uses to obtain the range of string lengths to
expose that bit of information.  The patch introduces a call to
array_at_struct_end_p to determine this but since the array is
in a COMPONENT_REF that array_at_struct_end_p doesn't consider
the patch extends the function to allow it to handle that case.
When the string can reference such an array the pass considers
it to be potentially unbounded in the worst (unlikely) case.

Martin
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.

diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index ef1afd1..7d21592 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,25 @@ 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 +1384,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 38fb8e7..e4931a1 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/tree.c b/gcc/tree.c
index 8426834..804ab5e 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 6341446..f63a678 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.  */
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 9e099f0..93747f9 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -1796,7 +1816,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])
     {
@@ -1839,7 +1859,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/gcc.dg/tree-ssa/pr79352.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79352.c
new file mode 100644
index 0000000..4a153b7
--- /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"} } */

Reply via email to