Fix PR120929:
incorrectly returned the size of *_1 for a
GIMPLE_ASSIGN of type:

 ptr = *_1;

This is only OK when _1 is set to .ACCESS_WITH_SIZE, since that builtin
expresses the size of *_1 in the form of _1.

gcc/ChangeLog:

        * tree-object-size.cc (is_ptr_access_with_size): New routine.
        (collect_object_sizes_for): Propagate size info through GIMPLE_ASSIGN
        for pointers with .ACCESS_WITH_SIZE.

gcc/testsuite/ChangeLog:

        * gcc.dg/pointer-counted-by-4-char.c: New test.
        * gcc.dg/pointer-counted-by-4-float.c: New test.
        * gcc.dg/pointer-counted-by-4-struct.c: New test.
        * gcc.dg/pointer-counted-by-4-union.c: New test.
        * gcc.dg/pointer-counted-by-4.c: New test.
        * gcc.dg/pointer-counted-by-5.c: New test.
        * gcc.dg/pointer-counted-by-6.c: New test.
        * gcc.dg/pointer-counted-by-7.c: New test.
        * gcc.dg/pr120929.c: New test.
---
 .../gcc.dg/pointer-counted-by-4-char.c        |  6 ++
 .../gcc.dg/pointer-counted-by-4-float.c       |  6 ++
 .../gcc.dg/pointer-counted-by-4-struct.c      | 10 +++
 .../gcc.dg/pointer-counted-by-4-union.c       | 10 +++
 gcc/testsuite/gcc.dg/pointer-counted-by-4.c   | 77 +++++++++++++++++++
 gcc/testsuite/gcc.dg/pointer-counted-by-5.c   | 56 ++++++++++++++
 gcc/testsuite/gcc.dg/pointer-counted-by-6.c   | 56 ++++++++++++++
 gcc/testsuite/gcc.dg/pointer-counted-by-7.c   | 32 ++++++++
 gcc/testsuite/gcc.dg/pr120929.c               | 57 ++++++++++++++
 gcc/tree-object-size.cc                       | 28 +++++++
 10 files changed, 338 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-4-char.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-4-float.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-4-struct.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-4-union.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-4.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-5.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-6.c
 create mode 100644 gcc/testsuite/gcc.dg/pointer-counted-by-7.c
 create mode 100644 gcc/testsuite/gcc.dg/pr120929.c

diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-4-char.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-4-char.c
new file mode 100644
index 00000000000..c404e5b8cce
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-4-char.c
@@ -0,0 +1,6 @@
+/* Test the attribute counted_by for pointer field and its usage in
+ * __builtin_dynamic_object_size.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+#define PTR_TYPE char
+#include "pointer-counted-by-4.c"
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-4-float.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-4-float.c
new file mode 100644
index 00000000000..383d8fb656d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-4-float.c
@@ -0,0 +1,6 @@
+/* Test the attribute counted_by for pointer field and its usage in
+ * __builtin_dynamic_object_size.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+#define PTR_TYPE float 
+#include "pointer-counted-by-4.c"
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-4-struct.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-4-struct.c
new file mode 100644
index 00000000000..50246d29477
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-4-struct.c
@@ -0,0 +1,10 @@
+/* Test the attribute counted_by for pointer field and its usage in
+ * __builtin_dynamic_object_size.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+struct A {
+  int a;
+  char *b;
+};
+#define PTR_TYPE struct A 
+#include "pointer-counted-by-4.c"
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-4-union.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-4-union.c
new file mode 100644
index 00000000000..e786d996147
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-4-union.c
@@ -0,0 +1,10 @@
+/* Test the attribute counted_by for pointer field and its usage in
+ * __builtin_dynamic_object_size.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+union A {
+  int a;
+  float b;
+};
+#define PTR_TYPE union A 
+#include "pointer-counted-by-4.c"
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-4.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-4.c
new file mode 100644
index 00000000000..c4b36311c70
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-4.c
@@ -0,0 +1,77 @@
+/* Test the attribute counted_by for pointer field and its usage in
+ * __builtin_dynamic_object_size.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include "builtin-object-size-common.h"
+#ifndef PTR_TYPE
+#define PTR_TYPE int
+#endif
+struct pointer_array {
+  int b;
+  PTR_TYPE *c;
+} *p_array;
+
+struct annotated {
+  PTR_TYPE *c __attribute__ ((counted_by (b)));
+  int b;
+} *p_array_annotated;
+
+struct nested_annotated {
+  PTR_TYPE *c __attribute__ ((counted_by (b)));
+  struct {
+    union {
+      int b;
+      float f; 
+    };
+    int n;
+  };
+} *p_array_nested_annotated;
+
+void __attribute__((__noinline__)) setup (int normal_count, int attr_count)
+{
+  p_array
+    = (struct pointer_array *) malloc (sizeof (struct pointer_array));
+  p_array->c = (PTR_TYPE *) malloc (sizeof (PTR_TYPE) * normal_count);
+  p_array->b = normal_count;
+
+  p_array_annotated
+    = (struct annotated *) malloc (sizeof (struct annotated));
+  p_array_annotated->c = (PTR_TYPE *) malloc (sizeof (PTR_TYPE) * attr_count);
+  p_array_annotated->b = attr_count;
+
+  p_array_nested_annotated
+    = (struct nested_annotated *) malloc (sizeof (struct nested_annotated));
+  p_array_nested_annotated->c = (PTR_TYPE *) malloc (sizeof (PTR_TYPE) * 
attr_count);
+  p_array_nested_annotated->b = attr_count;
+
+  return;
+}
+
+void __attribute__((__noinline__)) test ()
+{
+  EXPECT(__builtin_dynamic_object_size(p_array->c, 1), -1);
+  EXPECT(__builtin_dynamic_object_size(p_array_annotated->c, 1),
+        p_array_annotated->b * sizeof (PTR_TYPE));
+  EXPECT(__builtin_dynamic_object_size(p_array_nested_annotated->c, 1),
+        p_array_nested_annotated->b * sizeof (PTR_TYPE));
+}
+
+void cleanup ()
+{
+  free (p_array->c); 
+  free (p_array);
+  free (p_array_annotated->c);
+  free (p_array_annotated);
+  free (p_array_nested_annotated->c);
+  free (p_array_nested_annotated);
+}
+
+int main(int argc, char *argv[])
+{
+  setup (10,10);   
+  test ();
+  DONE ();
+  cleanup ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-5.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-5.c
new file mode 100644
index 00000000000..b43ffdf4df9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-5.c
@@ -0,0 +1,56 @@
+/* Test the attribute counted_by for pointer fields and its usage in
+ * __builtin_dynamic_object_size: when the counted_by field is negative.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include "builtin-object-size-common.h"
+
+struct annotated {
+  int b;
+  int *c __attribute__ ((counted_by (b)));
+} *array_annotated;
+
+struct nested_annotated {
+  int *c __attribute__ ((counted_by (b)));
+  struct {
+    union {
+      int b;
+      float f; 
+    };
+    int n;
+  };
+} *array_nested_annotated;
+
+void __attribute__((__noinline__)) setup (int attr_count)
+{
+  array_annotated
+    = (struct annotated *)malloc (sizeof (struct annotated));
+  array_annotated->b = attr_count;
+
+  array_nested_annotated
+    = (struct nested_annotated *)malloc (sizeof (struct nested_annotated));
+  array_nested_annotated->b = attr_count - 1;
+
+  return;
+}
+
+void __attribute__((__noinline__)) test ()
+{
+  EXPECT(__builtin_dynamic_object_size(array_annotated->c, 1), 0);
+  EXPECT(__builtin_dynamic_object_size(array_nested_annotated->c, 1), 0);
+}
+
+void cleanup ()
+{
+  free (array_annotated);
+  free (array_nested_annotated);
+}
+
+int main(int argc, char *argv[])
+{
+  setup (-10);   
+  test ();
+  DONE ();
+  cleanup ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-6.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-6.c
new file mode 100644
index 00000000000..355558cd161
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-6.c
@@ -0,0 +1,56 @@
+/* Test the attribute counted_by for pointer fields and its usage in
+ * __builtin_dynamic_object_size: when the type of the pointer  
+ * is casting to another type.  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include "builtin-object-size-common.h"
+
+typedef unsigned short u16;
+
+struct info {
+  u16 data_len;
+  char *data __attribute__((counted_by(data_len)));
+};
+
+struct foo {
+  int a;
+  int b;
+};
+
+static __attribute__((__noinline__))
+struct info *setup ()
+{
+  struct info *p;
+  size_t bytes = 3 * sizeof(struct foo);
+
+  p = (struct info *) malloc (sizeof (struct info));
+  p->data = (char *) malloc (bytes);
+  p->data_len = bytes;
+
+  return p;
+}
+
+static void
+__attribute__((__noinline__)) report (struct info *p)
+{
+  struct foo *bar = (struct foo *)p->data;
+  EXPECT(__builtin_dynamic_object_size((char *)(bar + 1), 1),
+        sizeof (struct foo) * 2);
+  EXPECT(__builtin_dynamic_object_size((char *)(bar + 2), 1),
+        sizeof (struct foo));
+}
+
+void cleanup (struct info *p)
+{
+  free (p->data);
+  free (p);
+}
+
+int main(int argc, char *argv[])
+{
+  struct info *p = setup();
+  report(p);
+  cleanup (p);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/pointer-counted-by-7.c 
b/gcc/testsuite/gcc.dg/pointer-counted-by-7.c
new file mode 100644
index 00000000000..af1ab279400
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pointer-counted-by-7.c
@@ -0,0 +1,32 @@
+/* Additional test of the attribute counted_by for pointer field and its usage
+   in __builtin_dynamic_object_size.  */ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#include "builtin-object-size-common.h"
+
+struct annotated {
+  int b;
+  int *c __attribute__ ((counted_by (b)));
+};
+
+struct annotated __attribute__((__noinline__)) setup (int attr_count)
+{
+  struct annotated p_array_annotated;
+  p_array_annotated.c = (int *) malloc (sizeof (int) * attr_count);
+  p_array_annotated.b = attr_count;
+
+  return p_array_annotated;
+}
+
+int main(int argc, char *argv[])
+{
+  struct annotated x = setup (10);   
+  int *p = x.c;
+  x = setup (20);
+  EXPECT(__builtin_dynamic_object_size (p, 1), 10 * sizeof (int));
+  EXPECT(__builtin_dynamic_object_size (x.c, 1), 20 * sizeof (int));
+  free (p);
+  free (x.c);
+  DONE ();
+}
diff --git a/gcc/testsuite/gcc.dg/pr120929.c b/gcc/testsuite/gcc.dg/pr120929.c
new file mode 100644
index 00000000000..2fbad4f1be1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr120929.c
@@ -0,0 +1,57 @@
+/* { dg-do run } */
+/* { dg-options "-O1" } */
+
+#include "builtin-object-size-common.h"
+
+typedef __SIZE_TYPE__ size_t;
+
+void
+__attribute__ ((noinline))
+pin_pointer(void *)
+{
+}
+
+struct magic_ {
+  char unused[9]; // at least 9
+};
+
+struct magic_map {
+  struct magic_ *magic;
+};
+
+static int
+coalesce_entries(struct magic_ **ma)
+{
+  size_t slen;
+
+  slen = sizeof (**ma);
+  *ma = __builtin_malloc (slen);
+
+  for (unsigned i = 0; i < 1; i++)
+    {
+      char b[1024] = {};
+      struct magic_ *ptr = *ma;
+      /* It should either give the correct size or fail graciously.  */
+      if (__builtin_dynamic_object_size (ptr, 0) != sizeof (**ma)
+         && __builtin_dynamic_object_size (ptr, 0) != (size_t) -1)
+       FAIL ();
+    }
+  return 0;
+}
+
+int
+main (void)
+{
+  char buf[128]; // did not shrink, but needs to be more than 100
+  struct magic_map *map2;
+
+  map2 = __builtin_malloc (sizeof (*map2));
+
+  pin_pointer(&buf);
+  coalesce_entries(&map2->magic);
+  pin_pointer(map2);
+
+  DONE ();
+
+  return 0;
+}
diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
index 8545eff61a3..e6467f499fb 100644
--- a/gcc/tree-object-size.cc
+++ b/gcc/tree-object-size.cc
@@ -1848,6 +1848,20 @@ phi_dynamic_object_size (struct object_size_info *osi, 
tree var)
   object_sizes_set (osi, varno, sizes, wholesizes);
 }
 
+/* Return true if PTR is defined with .ACCESS_WITH_SIZE.  */
+
+static bool
+is_ptr_access_with_size (tree ptr)
+{
+  gcc_assert (TREE_CODE (ptr) == SSA_NAME);
+
+  gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+  if (gimple_code (stmt) != GIMPLE_CALL)
+    return false;
+
+  return gimple_call_internal_p (as_a <gcall *> (stmt), IFN_ACCESS_WITH_SIZE);
+}
+
 /* Compute object sizes for VAR.
    For ADDR_EXPR an object size is the number of remaining bytes
    to the end of the object (where what is considered an object depends on
@@ -1934,6 +1948,20 @@ collect_object_sizes_for (struct object_size_info *osi, 
tree var)
             if (TREE_CODE (rhs) == SSA_NAME
                 && POINTER_TYPE_P (TREE_TYPE (rhs)))
              reexamine = merge_object_sizes (osi, var, rhs);
+            /* If RHS is a MEM_REF of PTR with zero offset, the size may be
+               expressed with the .ACCESS_WITH_SIZE builtin in the form of
+               &PTR, so look for that if available.  This is the sample IR of
+               this situation:
+               1  _1 = .ACCESS_WITH_SIZE (_3, _4, 0B, 4);
+               2  _5 = *_1;
+               3  _6 = __builtin_dynamic_object_size (_5, 1);
+            */
+           else if (TREE_CODE (rhs) == MEM_REF
+                    && POINTER_TYPE_P (TREE_TYPE (rhs))
+                    && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
+                    && integer_zerop (TREE_OPERAND (rhs, 1))
+                    && is_ptr_access_with_size (TREE_OPERAND (rhs, 0)))
+             reexamine = merge_object_sizes (osi, var, TREE_OPERAND (rhs, 0));
             else
               expr_object_size (osi, var, rhs);
           }
-- 
2.31.1

Reply via email to