Bug 109557 - __builtin_dynamic_object_size() does not work for simple testing case
Summary: __builtin_dynamic_object_size() does not work for simple testing case
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 13.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-04-19 16:21 UTC by qinzhao
Modified: 2023-07-17 21:11 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description qinzhao 2023-04-19 16:21:27 UTC
during my work for PR108896, I found that for the following small testing case:

[opc@qinzhao-ol8u3-x86 108896]$ cat test.c
#include <stdlib.h>
#include <assert.h>
struct P {
  int k;
  int x[10]; 
} *p;

void store(int a, int b) 
{
  p = (struct P *)malloc (sizeof (struct P));
  p->k = a;
  p->x[b] = 0;
  assert (__builtin_dynamic_object_size (p, 0) == sizeof (struct P));
  return;
}

int main()
{
  store(7, 7);
  assert (__builtin_dynamic_object_size (p, 0) == sizeof (struct P));
  free (p);
}

with gcc13, compiled with -O, the above first assertion succeed, but the second one failed.

when checking the tree-object-size.cc, I found:
1377 static void
1378 expr_object_size (struct object_size_info *osi, tree ptr, tree value)
1379 {
1380   int object_size_type = osi->object_size_type;
1381   unsigned int varno = SSA_NAME_VERSION (ptr);
1382   tree bytes, wholesize;
1383 
1384   gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
1385   gcc_assert (osi->pass == 0);
1386 
1387   if (TREE_CODE (value) == WITH_SIZE_EXPR)
1388     value = TREE_OPERAND (value, 0);
1389 
1390   /* Pointer variables should have been handled by merge_object_sizes.  */
1391   gcc_assert (TREE_CODE (value) != SSA_NAME
1392               || !POINTER_TYPE_P (TREE_TYPE (value)));
1393 
1394   if (TREE_CODE (value) == ADDR_EXPR)
1395     addr_object_size (osi, value, object_size_type, &bytes, &wholesize);
1396   else
1397     bytes = wholesize = size_unknown (object_size_type);
1398 
1399   object_sizes_set (osi, varno, bytes, wholesize);
1400 }

in the above, for the 2nd __builtin_dynamic_object_size, the above line 1397 is called, therefore size_unknown was returned for it.

I am wondering for 
p.3_1 = p;
_2 = __builtin_object_size (p.3_1, 0);

why the size of p.3_1 cannot use the TYPE_SIZE of the pointee of p when its size can be determined (i.e, not a structure with a flexible array member, etc)?
Comment 1 Siddhesh Poyarekar 2023-04-19 16:34:15 UTC
The __bdos call itself cannot succeed in main() because it cannot see the allocation in store().  One way it could succeed is if store() was inlined, but for some reason it doesn't, even if you make the function static inline.

If I decorate store() with __attribute__((inline)) I get the warning:

foo.c:10:1: warning: ‘always_inline’ function might not be inlinable [-Wattributes]

but it seems to proceed to inline the call because the assert in main() is no longer hit.

So from the __bdos context I'm inclined to say NOTABUG.
Comment 2 Siddhesh Poyarekar 2023-04-19 16:36:18 UTC
(In reply to qinzhao from comment #0)
> I am wondering for 
> p.3_1 = p;
> _2 = __builtin_object_size (p.3_1, 0);
> 
> why the size of p.3_1 cannot use the TYPE_SIZE of the pointee of p when its
> size can be determined (i.e, not a structure with a flexible array member,
> etc)?

To answer this specific question, it's because the compiler can't see in main() if p is pointing to any actual storage.
Comment 3 Martin Uecker 2023-04-19 16:38:03 UTC
I general the pointer could point to the first object of an array that has more elements, or to an object of a different type.  The semantics of C are not strong enough here, but it would be good to have some kind of annotation for the pointer that would allow this.
Comment 4 Siddhesh Poyarekar 2023-04-19 16:50:39 UTC
(In reply to Martin Uecker from comment #3)
> I general the pointer could point to the first object of an array that has
> more elements, or to an object of a different type.

How so?  p in comment 0 is just a NULL-initialized pointer.  It gets assigned to a malloc'd storage in store() (which the code in main() cannot see) but until then, it's a NULL pointer.
Comment 5 Martin Uecker 2023-04-19 16:56:59 UTC
(In reply to Siddhesh Poyarekar from comment #4)
> (In reply to Martin Uecker from comment #3)
> > I general the pointer could point to the first object of an array that has
> > more elements, or to an object of a different type.
> 
> How so?  p in comment 0 is just a NULL-initialized pointer.  It gets
> assigned to a malloc'd storage in store() (which the code in main() cannot
> see) but until then, it's a NULL pointer.

With "in general" I meant in a different program.  From just knowing the type of the pointer you can not derive the object size.  This is how I understood the original question.
Comment 6 Siddhesh Poyarekar 2023-04-19 17:00:25 UTC
(In reply to Martin Uecker from comment #5)
> With "in general" I meant in a different program.  From just knowing the
> type of the pointer you can not derive the object size.  This is how I
> understood the original question.

Ah ok, agreed.  Closing this as invalid then; I noticed I was missing the inline keyword when I tried forcing inlining, so that was also a PEBKAC.
Comment 7 qinzhao 2023-04-19 18:23:51 UTC
Okay, thanks for the comment. I see why this should not work.
Comment 8 qinzhao 2023-07-17 20:42:18 UTC
with the following slightly modified testing case, the same issue:
#include <stdlib.h>
#include <assert.h>
struct P {
  int k;
  int x[10]; 
} *p;

void store(int a, int b) 
{
  p = (struct P *)malloc (sizeof (struct P));
  p->k = a;
  p->x[b] = 0;
  assert (__builtin_dynamic_object_size (p, 1) == sizeof (struct P));
  return;
}

int main()
{
  store(7, 7);
  assert (__builtin_dynamic_object_size (p, 1) == sizeof (struct P));
  free (p);
}
[opc@qinzhao-ol8u3-x86 109557]$ sh t
/home/opc/Install/latest/bin/gcc -O -fsanitize=bounds -fsanitize=object-size -fstrict-flex-arrays=3 -fdump-tree-all t.c
a.out: t.c:20: main: Assertion `__builtin_dynamic_object_size (p, 1) == sizeof (struct P)' failed.
t: line 19: 629958 Aborted                 (core dumped) ./a.out