Siddhesh, Thanks. I will update the testing case per your change.
Qing > On Feb 1, 2023, at 11:48 AM, Siddhesh Poyarekar <siddh...@gotplt.org> wrote: > > On 2023-01-31 09:11, Qing Zhao wrote: >> GCC extension accepts the case when a struct with a flexible array member >> is embedded into another struct (possibly recursively). >> __builtin_object_size should treat such struct as flexible size per >> -fstrict-flex-arrays. >> PR tree-optimization/101832 >> gcc/ChangeLog: >> PR tree-optimization/101832 >> * tree-object-size.cc (flexible_size_type_p): New function. >> (addr_object_size): Handle structure/union type when it has >> flexible size. >> gcc/testsuite/ChangeLog: >> PR tree-optimization/101832 >> * gcc.dg/builtin-object-size-pr101832-2.c: New test. >> * gcc.dg/builtin-object-size-pr101832-3.c: New test. >> * gcc.dg/builtin-object-size-pr101832-4.c: New test. >> * gcc.dg/builtin-object-size-pr101832.c: New test. >> --- >> .../gcc.dg/builtin-object-size-pr101832-2.c | 135 ++++++++++++++++++ >> .../gcc.dg/builtin-object-size-pr101832-3.c | 135 ++++++++++++++++++ >> .../gcc.dg/builtin-object-size-pr101832-4.c | 135 ++++++++++++++++++ >> .../gcc.dg/builtin-object-size-pr101832.c | 119 +++++++++++++++ >> gcc/tree-object-size.cc | 115 +++++++++++---- >> 5 files changed, 611 insertions(+), 28 deletions(-) >> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c >> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c >> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c >> create mode 100644 gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c >> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c >> b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c >> new file mode 100644 >> index 00000000000..f38babc5415 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-2.c >> @@ -0,0 +1,135 @@ >> +/* PR 101832: >> + GCC extension accepts the case when a struct with a flexible array member >> + is embedded into another struct (possibly recursively). >> + __builtin_object_size will treat such struct as flexible size per >> + -fstrict-flex-arrays. */ >> +/* { dg-do run } */ >> +/* { dg-options "-O2 -fstrict-flex-arrays=1" } */ >> + >> +#include <stdio.h> >> + >> +unsigned n_fails = 0; >> + >> +#define expect(p, _v) do { \ >> + size_t v = _v; \ >> + if (p == v) \ >> + printf("ok: %s == %zd\n", #p, p); \ >> + else {\ >> + printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \ >> + n_fails++; \ > > I just pushed my testsuite fix, so you could use the macros in > gcc.dg/builtin-object-size-common.h instead of accounting this yourself. > > Also if you use __builtin_printf, you won't have to include stdio.h. > > Thanks, > Sid > >> + } \ >> +} while (0); >> + >> +struct A { >> + int n; >> + char data[];/* Content following header */ >> +}; >> + >> +struct B { >> + int m; >> + struct A a; >> +}; >> + >> +struct C { >> + int q; >> + struct B b; >> +}; >> + >> +struct A0 { >> + int n; >> + char data[0];/* Content following header */ >> +}; >> + >> +struct B0 { >> + int m; >> + struct A0 a; >> +}; >> + >> +struct C0 { >> + int q; >> + struct B0 b; >> +}; >> + >> +struct A1 { >> + int n; >> + char data[1];/* Content following header */ >> +}; >> + >> +struct B1 { >> + int m; >> + struct A1 a; >> +}; >> + >> +struct C1 { >> + int q; >> + struct B1 b; >> +}; >> + >> +struct An { >> + int n; >> + char data[8];/* Content following header */ >> +}; >> + >> +struct Bn { >> + int m; >> + struct An a; >> +}; >> + >> +struct Cn { >> + int q; >> + struct Bn b; >> +}; >> + >> +volatile void *magic1, *magic2; >> + >> +int main(int argc, char *argv[]) >> +{ >> + struct B *outer; >> + struct C *outest; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer = (void *)magic1; >> + outest = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer->a, 1), -1); >> + expect(__builtin_object_size(&outest->b, 1), -1); >> + expect(__builtin_object_size(&outest->b.a, 1), -1); >> + >> + struct B0 *outer0; >> + struct C0 *outest0; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer0 = (void *)magic1; >> + outest0 = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer0->a, 1), -1); >> + expect(__builtin_object_size(&outest0->b, 1), -1); >> + expect(__builtin_object_size(&outest0->b.a, 1), -1); >> + >> + struct B1 *outer1; >> + struct C1 *outest1; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer1 = (void *)magic1; >> + outest1 = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer1->a, 1), -1); >> + expect(__builtin_object_size(&outest1->b, 1), -1); >> + expect(__builtin_object_size(&outest1->b.a, 1), -1); >> + >> + struct Bn *outern; >> + struct Cn *outestn; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outern = (void *)magic1; >> + outestn = (void *)magic2; >> + >> + expect(__builtin_object_size(&outern->a, 1), sizeof(outern->a)); >> + expect(__builtin_object_size(&outestn->b, 1), sizeof(outestn->b)); >> + expect(__builtin_object_size(&outestn->b.a, 1), sizeof(outestn->b.a)); >> + >> + if (n_fails > 0) >> + __builtin_abort (); >> + >> + return 0; >> +} >> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c >> b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c >> new file mode 100644 >> index 00000000000..aaae99b8d67 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-3.c >> @@ -0,0 +1,135 @@ >> +/* PR 101832: >> + GCC extension accepts the case when a struct with a flexible array member >> + is embedded into another struct (possibly recursively). >> + __builtin_object_size will treat such struct as flexible size per >> + -fstrict-flex-arrays. */ >> +/* { dg-do run } */ >> +/* { dg-options "-O2 -fstrict-flex-arrays=2" } */ >> + >> +#include <stdio.h> >> + >> +unsigned n_fails = 0; >> + >> +#define expect(p, _v) do { \ >> + size_t v = _v; \ >> + if (p == v) \ >> + printf("ok: %s == %zd\n", #p, p); \ >> + else {\ >> + printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \ >> + n_fails++; \ >> + } \ >> +} while (0); >> + >> +struct A { >> + int n; >> + char data[];/* Content following header */ >> +}; >> + >> +struct B { >> + int m; >> + struct A a; >> +}; >> + >> +struct C { >> + int q; >> + struct B b; >> +}; >> + >> +struct A0 { >> + int n; >> + char data[0];/* Content following header */ >> +}; >> + >> +struct B0 { >> + int m; >> + struct A0 a; >> +}; >> + >> +struct C0 { >> + int q; >> + struct B0 b; >> +}; >> + >> +struct A1 { >> + int n; >> + char data[1];/* Content following header */ >> +}; >> + >> +struct B1 { >> + int m; >> + struct A1 a; >> +}; >> + >> +struct C1 { >> + int q; >> + struct B1 b; >> +}; >> + >> +struct An { >> + int n; >> + char data[8];/* Content following header */ >> +}; >> + >> +struct Bn { >> + int m; >> + struct An a; >> +}; >> + >> +struct Cn { >> + int q; >> + struct Bn b; >> +}; >> + >> +volatile void *magic1, *magic2; >> + >> +int main(int argc, char *argv[]) >> +{ >> + struct B *outer; >> + struct C *outest; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer = (void *)magic1; >> + outest = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer->a, 1), -1); >> + expect(__builtin_object_size(&outest->b, 1), -1); >> + expect(__builtin_object_size(&outest->b.a, 1), -1); >> + >> + struct B0 *outer0; >> + struct C0 *outest0; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer0 = (void *)magic1; >> + outest0 = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer0->a, 1), -1); >> + expect(__builtin_object_size(&outest0->b, 1), -1); >> + expect(__builtin_object_size(&outest0->b.a, 1), -1); >> + >> + struct B1 *outer1; >> + struct C1 *outest1; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer1 = (void *)magic1; >> + outest1 = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer1->a, 1), sizeof(outer1->a)); >> + expect(__builtin_object_size(&outest1->b, 1), sizeof(outest1->b)); >> + expect(__builtin_object_size(&outest1->b.a, 1), sizeof(outest1->b.a)); >> + >> + struct Bn *outern; >> + struct Cn *outestn; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outern = (void *)magic1; >> + outestn = (void *)magic2; >> + >> + expect(__builtin_object_size(&outern->a, 1), sizeof(outern->a)); >> + expect(__builtin_object_size(&outestn->b, 1), sizeof(outestn->b)); >> + expect(__builtin_object_size(&outestn->b.a, 1), sizeof(outestn->b.a)); >> + >> + if (n_fails > 0) >> + __builtin_abort (); >> + >> + return 0; >> +} >> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c >> b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c >> new file mode 100644 >> index 00000000000..424264e2acd >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832-4.c >> @@ -0,0 +1,135 @@ >> +/* PR 101832: >> + GCC extension accepts the case when a struct with a flexible array member >> + is embedded into another struct (possibly recursively). >> + __builtin_object_size will treat such struct as flexible size per >> + -fstrict-flex-arrays. */ >> +/* { dg-do run } */ >> +/* { dg-options "-O2 -fstrict-flex-arrays=3" } */ >> + >> +#include <stdio.h> >> + >> +unsigned n_fails = 0; >> + >> +#define expect(p, _v) do { \ >> + size_t v = _v; \ >> + if (p == v) \ >> + printf("ok: %s == %zd\n", #p, p); \ >> + else {\ >> + printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \ >> + n_fails++; \ >> + } \ >> +} while (0); >> + >> +struct A { >> + int n; >> + char data[];/* Content following header */ >> +}; >> + >> +struct B { >> + int m; >> + struct A a; >> +}; >> + >> +struct C { >> + int q; >> + struct B b; >> +}; >> + >> +struct A0 { >> + int n; >> + char data[0];/* Content following header */ >> +}; >> + >> +struct B0 { >> + int m; >> + struct A0 a; >> +}; >> + >> +struct C0 { >> + int q; >> + struct B0 b; >> +}; >> + >> +struct A1 { >> + int n; >> + char data[1];/* Content following header */ >> +}; >> + >> +struct B1 { >> + int m; >> + struct A1 a; >> +}; >> + >> +struct C1 { >> + int q; >> + struct B1 b; >> +}; >> + >> +struct An { >> + int n; >> + char data[8];/* Content following header */ >> +}; >> + >> +struct Bn { >> + int m; >> + struct An a; >> +}; >> + >> +struct Cn { >> + int q; >> + struct Bn b; >> +}; >> + >> +volatile void *magic1, *magic2; >> + >> +int main(int argc, char *argv[]) >> +{ >> + struct B *outer; >> + struct C *outest; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer = (void *)magic1; >> + outest = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer->a, 1), -1); >> + expect(__builtin_object_size(&outest->b, 1), -1); >> + expect(__builtin_object_size(&outest->b.a, 1), -1); >> + >> + struct B0 *outer0; >> + struct C0 *outest0; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer0 = (void *)magic1; >> + outest0 = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer0->a, 1), sizeof(outer0->a)); >> + expect(__builtin_object_size(&outest0->b, 1), sizeof(outest0->b)); >> + expect(__builtin_object_size(&outest0->b.a, 1), sizeof(outest0->b.a)); >> + >> + struct B1 *outer1; >> + struct C1 *outest1; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outer1 = (void *)magic1; >> + outest1 = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer1->a, 1), sizeof(outer1->a)); >> + expect(__builtin_object_size(&outest1->b, 1), sizeof(outest1->b)); >> + expect(__builtin_object_size(&outest1->b.a, 1), sizeof(outest1->b.a)); >> + >> + struct Bn *outern; >> + struct Cn *outestn; >> + >> + /* Make sure optimization can't find some other object size. */ >> + outern = (void *)magic1; >> + outestn = (void *)magic2; >> + >> + expect(__builtin_object_size(&outern->a, 1), sizeof(outern->a)); >> + expect(__builtin_object_size(&outestn->b, 1), sizeof(outestn->b)); >> + expect(__builtin_object_size(&outestn->b.a, 1), sizeof(outestn->b.a)); >> + >> + if (n_fails > 0) >> + __builtin_abort (); >> + >> + return 0; >> +} >> diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c >> b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c >> new file mode 100644 >> index 00000000000..8ed6980edf0 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-pr101832.c >> @@ -0,0 +1,119 @@ >> +/* PR 101832: >> + GCC extension accepts the case when a struct with a flexible array member >> + is embedded into another struct (possibly recursively). >> + __builtin_object_size will treat such struct as flexible size per >> + -fstrict-flex-arrays. */ >> +/* { dg-do run } */ >> +/* { dg-options "-O2" } */ >> + >> +#include <stdio.h> >> + >> +unsigned n_fails = 0; >> + >> +#define expect(p, _v) do { \ >> + size_t v = _v; \ >> + if (p == v) \ >> + printf("ok: %s == %zd\n", #p, p); \ >> + else {\ >> + printf("WAT: %s == %zd (expected %zd)\n", #p, p, v); \ >> + n_fails++; \ >> + } \ >> +} while (0); >> + >> +struct A { >> + int n; >> + char data[];/* Content following header */ >> +}; >> + >> +struct B { >> + int m; >> + struct A a; >> +}; >> + >> +struct C { >> + int q; >> + struct B b; >> +}; >> + >> +struct A0 { >> + int n; >> + char data[0];/* Content following header */ >> +}; >> + >> +struct B0 { >> + int m; >> + struct A0 a; >> +}; >> + >> +struct C0 { >> + int q; >> + struct B0 b; >> +}; >> + >> +struct A1 { >> + int n; >> + char data[1];/* Content following header */ >> +}; >> + >> +struct B1 { >> + int m; >> + struct A1 a; >> +}; >> + >> +struct C1 { >> + int q; >> + struct B1 b; >> +}; >> + >> +struct An { >> + int n; >> + char data[8];/* Content following header */ >> +}; >> + >> +struct Bn { >> + int m; >> + struct An a; >> +}; >> + >> +struct Cn { >> + int q; >> + struct Bn b; >> +}; >> + >> +volatile void *magic1, *magic2; >> + >> +int main(int argc, char *argv[]) >> +{ >> + struct B *outer = (void *)magic1; >> + struct C *outest = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer->a, 1), -1); >> + expect(__builtin_object_size(&outest->b, 1), -1); >> + expect(__builtin_object_size(&outest->b.a, 1), -1); >> + >> + struct B0 *outer0 = (void *)magic1; >> + struct C0 *outest0 = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer0->a, 1), -1); >> + expect(__builtin_object_size(&outest0->b, 1), -1); >> + expect(__builtin_object_size(&outest0->b.a, 1), -1); >> + >> + struct B1 *outer1 = (void *)magic1; >> + struct C1 *outest1 = (void *)magic2; >> + >> + expect(__builtin_object_size(&outer1->a, 1), -1); >> + expect(__builtin_object_size(&outest1->b, 1), -1); >> + expect(__builtin_object_size(&outest1->b.a, 1), -1); >> + >> + struct Bn *outern = (void *)magic1; >> + struct Cn *outestn = (void *)magic2; >> + >> + expect(__builtin_object_size(&outern->a, 1), -1); >> + expect(__builtin_object_size(&outestn->b, 1), -1); >> + expect(__builtin_object_size(&outestn->b.a, 1), -1); >> + >> + if (n_fails > 0) >> + __builtin_abort (); >> + >> + return 0; >> +} >> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc >> index 9a936a91983..56b78ca2a8c 100644 >> --- a/gcc/tree-object-size.cc >> +++ b/gcc/tree-object-size.cc >> @@ -500,6 +500,42 @@ decl_init_size (tree decl, bool min) >> return size; >> } >> +/* Determine whether TYPE is a structure with a flexible array member >> + per -fstrict-flex-array or a union containing such a structure >> + (possibly recursively). */ >> +static bool >> +flexible_size_type_p (const_tree type) >> +{ >> + tree x = NULL_TREE; >> + tree last = NULL_TREE; >> + switch (TREE_CODE (type)) >> + { >> + case RECORD_TYPE: >> + for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x)) >> + if (TREE_CODE (x) == FIELD_DECL) >> + last = x; >> + if (last == NULL_TREE) >> + return false; >> + if (TREE_CODE (TREE_TYPE (last)) == ARRAY_TYPE >> + && !DECL_NOT_FLEXARRAY (last)) >> + return true; >> + else if (TREE_CODE (TREE_TYPE (last)) == RECORD_TYPE >> + || TREE_CODE (TREE_TYPE (last)) == UNION_TYPE) >> + return flexible_size_type_p (TREE_TYPE (last)); >> + return false; >> + case UNION_TYPE: >> + for (x = TYPE_FIELDS (type); x != NULL_TREE; x = DECL_CHAIN (x)) >> + { >> + if (TREE_CODE (x) == FIELD_DECL >> + && flexible_array_type_p (TREE_TYPE (x))) >> + return true; >> + } >> + return false; >> + default: >> + return false; >> + } >> +} >> + >> /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR. >> OBJECT_SIZE_TYPE is the second argument from __builtin_object_size. >> If unknown, return size_unknown (object_size_type). */ >> @@ -633,45 +669,68 @@ addr_object_size (struct object_size_info *osi, >> const_tree ptr, >> v = NULL_TREE; >> break; >> case COMPONENT_REF: >> - if (TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE) >> + /* When the ref is not to an array, a record or a union, it >> + will not have flexible size, compute the object size >> + directly. */ >> + if ((TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE) >> + && (TREE_CODE (TREE_TYPE (v)) != RECORD_TYPE) >> + && (TREE_CODE (TREE_TYPE (v)) != UNION_TYPE)) >> { >> v = NULL_TREE; >> break; >> } >> - is_flexible_array_mem_ref = array_ref_flexible_size_p (v); >> - while (v != pt_var && TREE_CODE (v) == COMPONENT_REF) >> - if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> - != UNION_TYPE >> - && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> - != QUAL_UNION_TYPE) >> - break; >> - else >> - v = TREE_OPERAND (v, 0); >> - if (TREE_CODE (v) == COMPONENT_REF >> - && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> - == RECORD_TYPE) >> + /* if the record or union does not have flexible size >> + compute the object size directly. */ >> + if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE >> + || TREE_CODE (TREE_TYPE (v)) == UNION_TYPE) >> { >> - /* compute object size only if v is not a >> - flexible array member. */ >> - if (!is_flexible_array_mem_ref) >> + if (!flexible_size_type_p (TREE_TYPE (v))) >> { >> v = NULL_TREE; >> break; >> } >> - v = TREE_OPERAND (v, 0); >> + else >> + v = TREE_OPERAND (v, 0); >> } >> - while (v != pt_var && TREE_CODE (v) == COMPONENT_REF) >> - if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> - != UNION_TYPE >> - && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> - != QUAL_UNION_TYPE) >> - break; >> - else >> - v = TREE_OPERAND (v, 0); >> - if (v != pt_var) >> - v = NULL_TREE; >> else >> - v = pt_var; >> + { >> + /* Now the ref is to an array type. */ >> + is_flexible_array_mem_ref >> + = array_ref_flexible_size_p (v); >> + while (v != pt_var && TREE_CODE (v) == COMPONENT_REF) >> + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> + != UNION_TYPE >> + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> + != QUAL_UNION_TYPE) >> + break; >> + else >> + v = TREE_OPERAND (v, 0); >> + if (TREE_CODE (v) == COMPONENT_REF >> + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> + == RECORD_TYPE) >> + { >> + /* compute object size only if v is not a >> + flexible array member. */ >> + if (!is_flexible_array_mem_ref) >> + { >> + v = NULL_TREE; >> + break; >> + } >> + v = TREE_OPERAND (v, 0); >> + } >> + while (v != pt_var && TREE_CODE (v) == COMPONENT_REF) >> + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> + != UNION_TYPE >> + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) >> + != QUAL_UNION_TYPE) >> + break; >> + else >> + v = TREE_OPERAND (v, 0); >> + if (v != pt_var) >> + v = NULL_TREE; >> + else >> + v = pt_var; >> + } >> break; >> default: >> v = pt_var;