Hi, Jakub,

Happy new year!

Could you please review the middle-end part of the change of this patch?

This patch has been waiting for the middle-end approval for a long time (it has 
been reviewed several rounds by Sid.)

I am hoping that this feature can be added into GCC16.

Thanks a lot.

Qing

> On Dec 4, 2025, at 09:22, Qing Zhao <[email protected]> wrote:
> 
> This is a gentle ping of this patch.
> 
> Thanks a lot.
> 
> Qing
> 
>> On Nov 20, 2025, at 16:46, Qing Zhao <[email protected]> wrote:
>> 
>> Hi, Jakub,
>> 
>> Could you please review the middle-end part of the change of this patch? 
>> (The C FE part has been approved by Joseph already. 
>> And the middle-end part has been reviewed by Sid.)
>> 
>> Thanks a lot.
>> 
>> Qing
>> 
>> 
>>> On Oct 31, 2025, at 09:45, Qing Zhao <[email protected]> wrote:
>>> 
>>> Hi,
>>> 
>>> this is the 5th version of the patch.
>>> compared to the 4th version, the difference are updates based on
>>> Sid's comments.
>>> 
>>> https://gcc.gnu.org/pipermail/gcc-patches/2025-October/698959.html
>>> 
>>> bootstrapped and regression tested on both x86 and aarch64.
>>> 
>>> Okay for committing?
>>> 
>>> thanks.
>>> 
>>> Qing
>>> 
>>> ===============================
>>> 
>>> In tree-object-size.cc, if the size is UNKNOWN after evaluating use-def
>>> chain, We can evaluate the SIZE of the pointee TYPE ONLY when this TYPE
>>> is a structure type with flexible array member which is attached a
>>> counted_by attribute, since a structure with FAM can not be an element
>>> of an array, so, the pointer must point to a single object with this
>>> structure with FAM.
>>> 
>>> Control this behavior with a new --param objsz-allow-dereference-input=0|1
>>> Default is 0.
>>> 
>>> This is only available for C now.
>>> 
>>> gcc/c/ChangeLog:
>>> 
>>> * c-lang.cc (LANG_HOOKS_BUILD_COUNTED_BY_REF):
>>> Define to below function.
>>> * c-tree.h (c_build_counted_by_ref): New extern function.
>>> * c-typeck.cc (build_counted_by_ref): Rename to ...
>>> (c_build_counted_by_ref): ...this.
>>> (handle_counted_by_for_component_ref): Call the renamed function.
>>> 
>>> gcc/ChangeLog:
>>> 
>>> * doc/invoke.texi: Add documentation for the new option
>>> --param objsz-allow-dereference-input.
>>> * langhooks-def.h (LANG_HOOKS_BUILD_COUNTED_BY_REF):
>>> New language hook.
>>> * langhooks.h (struct lang_hooks_for_types): Add
>>> build_counted_by_ref.
>>> * params.opt: New param objsz-allow-dereference-input.
>>> * tree-object-size.cc (struct object_size): Add a new field
>>> nullsize for the size when the pointer is NULL.
>>> (object_sizes_initialize): Initialize new field nullsize.
>>> (object_sizes_set): Add a new parameter need_nullcheck, when it
>>> is true, set the value of the new field nullsize to size_unknown.
>>> (insert_null_check_and_size): New function.
>>> (gimplify_size_expressions): When nullsize is not NULL_TREE, call
>>> insert_null_check_and_size.
>>> (insert_cond_and_size): New function.
>>> (is_pointee_fam_struct_with_counted_by): New function.
>>> (record_with_fam_object_size): New function.
>>> (collect_object_sizes_for): Call record_with_fam_object_size.
>>> (dynamic_object_sizes_execute_one): Special handling for NULL pointer
>>> checking.
>>> 
>>> gcc/testsuite/ChangeLog:
>>> 
>>> * gcc.dg/flex-array-counted-by-3.c: Update test for whole object size;
>>> * gcc.dg/flex-array-counted-by-4.c: Likewise.
>>> * gcc.dg/flex-array-counted-by-5.c: Likewise.
>>> * gcc.dg/flex-array-counted-by-5-b.c: New test.
>>> * gcc.dg/flex-array-counted-by-5-c.c: New test.
>>> * gcc.dg/flex-array-counted-by-10.c: New test.
>>> * gcc.dg/flex-array-counted-by-10-b.c: New test.
>>> ---
>>> gcc/c/c-lang.cc                               |   3 +
>>> gcc/c/c-tree.h                                |   1 +
>>> gcc/c/c-typeck.cc                             |  10 +-
>>> gcc/doc/invoke.texi                           |  14 +
>>> gcc/langhooks-def.h                           |   4 +-
>>> gcc/langhooks.h                               |   5 +
>>> gcc/params.opt                                |   4 +
>>> .../gcc.dg/flex-array-counted-by-10-b.c       |   7 +
>>> .../gcc.dg/flex-array-counted-by-10.c         |  41 +++
>>> .../gcc.dg/flex-array-counted-by-3.c          |   7 +-
>>> .../gcc.dg/flex-array-counted-by-4.c          |  45 ++-
>>> .../gcc.dg/flex-array-counted-by-5-b.c        |  51 +++
>>> .../gcc.dg/flex-array-counted-by-5-c.c        |   7 +
>>> .../gcc.dg/flex-array-counted-by-5.c          |   6 +-
>>> gcc/tree-object-size.cc                       | 317 +++++++++++++++++-
>>> 15 files changed, 493 insertions(+), 29 deletions(-)
>>> create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by-10-b.c
>>> create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by-10.c
>>> create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by-5-b.c
>>> create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by-5-c.c
>>> 
>>> diff --git a/gcc/c/c-lang.cc b/gcc/c/c-lang.cc
>>> index c69077b2a93..e9ec9e6e64a 100644
>>> --- a/gcc/c/c-lang.cc
>>> +++ b/gcc/c/c-lang.cc
>>> @@ -51,6 +51,9 @@ enum c_language_kind c_language = clk_c;
>>> #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
>>> #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE c_get_sarif_source_language
>>> 
>>> +#undef LANG_HOOKS_BUILD_COUNTED_BY_REF
>>> +#define LANG_HOOKS_BUILD_COUNTED_BY_REF c_build_counted_by_ref
>>> +
>>> /* Each front end provides its own lang hook initializer.  */
>>> struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
>>> 
>>> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
>>> index f367cda35d7..943da558488 100644
>>> --- a/gcc/c/c-tree.h
>>> +++ b/gcc/c/c-tree.h
>>> @@ -799,6 +799,7 @@ extern struct c_switch *c_switch_stack;
>>> 
>>> extern bool null_pointer_constant_p (const_tree);
>>> 
>>> +extern tree c_build_counted_by_ref (tree, tree, tree *);
>>> 
>>> inline bool
>>> c_type_variably_modified_p (tree t)
>>> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
>>> index 371583bd64e..ac400716c91 100644
>>> --- a/gcc/c/c-typeck.cc
>>> +++ b/gcc/c/c-typeck.cc
>>> @@ -3086,9 +3086,9 @@ check_counted_by_attribute (location_t loc, tree ref)
>>>   &(p->k)
>>> */
>>> 
>>> -static tree
>>> -build_counted_by_ref (tree datum, tree subdatum,
>>> -      tree *counted_by_type)
>>> +tree
>>> +c_build_counted_by_ref (tree datum, tree subdatum,
>>> + tree *counted_by_type)
>>> {
>>> tree type = TREE_TYPE (datum);
>>> tree sub_type = TREE_TYPE (subdatum);
>>> @@ -3221,8 +3221,8 @@ handle_counted_by_for_component_ref (location_t loc, 
>>> tree ref)
>>> || TREE_CODE (TREE_TYPE (ref)) == POINTER_TYPE))
>>>   return ref;
>>> 
>>> -  tree counted_by_ref = build_counted_by_ref (datum, subdatum,
>>> -      &counted_by_type);
>>> +  tree counted_by_ref = c_build_counted_by_ref (datum, subdatum,
>>> + &counted_by_type);
>>> if (counted_by_ref)
>>>   ref = build_access_with_size_for_counted_by (loc, ref,
>>> counted_by_ref,
>>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>>> index 32b9c48f155..6c4060393b1 100644
>>> --- a/gcc/doc/invoke.texi
>>> +++ b/gcc/doc/invoke.texi
>>> @@ -17811,6 +17811,20 @@ Work bound when discovering transitive relations 
>>> from existing relations.
>>> @item min-pagesize
>>> Minimum page size for warning and early break vectorization purposes.
>>> 
>>> +@item objsz-allow-dereference-input
>>> +Use this option to allow the size expressions generated by the builtin
>>> +@code{__builtin_dynamic_object_size} to dereference its input pointer.
>>> +This may allow the builtin function to get the size of an object when
>>> +the size information is embedded in the object itself.  For example,
>>> +if a structure with a flexible array member at its end is annotated with
>>> +the @code{counted_by} attribute, the size information of an object with
>>> +such structure type is embedded in the object itself.
>>> +
>>> +Use this parameter with caution because in cases where a non-NULL input
>>> +pointer is not known to be valid, e.g. when it points to memory that is
>>> +either protected or freed, enabling this parameter may result in 
>>> dereferencing
>>> +that invalid pointer, potentially introducing additional undefined 
>>> behavior.
>>> +
>>> @item openacc-kernels
>>> Specify mode of OpenACC `kernels' constructs handling.
>>> With @option{--param=openacc-kernels=decompose}, OpenACC `kernels'
>>> diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
>>> index 518509ae8f6..6335c80274c 100644
>>> --- a/gcc/langhooks-def.h
>>> +++ b/gcc/langhooks-def.h
>>> @@ -224,6 +224,7 @@ extern tree lhd_unit_size_without_reusable_padding 
>>> (tree);
>>> #define LANG_HOOKS_TYPE_DWARF_ATTRIBUTE lhd_type_dwarf_attribute
>>> #define LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING 
>>> lhd_unit_size_without_reusable_padding
>>> #define LANG_HOOKS_CLASSTYPE_AS_BASE hook_tree_const_tree_null
>>> +#define LANG_HOOKS_BUILD_COUNTED_BY_REF NULL
>>> 
>>> #define LANG_HOOKS_FOR_TYPES_INITIALIZER { \
>>> LANG_HOOKS_MAKE_TYPE, \
>>> @@ -251,7 +252,8 @@ extern tree lhd_unit_size_without_reusable_padding 
>>> (tree);
>>> LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO, \
>>> LANG_HOOKS_TYPE_DWARF_ATTRIBUTE, \
>>> LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING, \
>>> -  LANG_HOOKS_CLASSTYPE_AS_BASE \
>>> +  LANG_HOOKS_CLASSTYPE_AS_BASE, \
>>> +  LANG_HOOKS_BUILD_COUNTED_BY_REF \
>>> }
>>> 
>>> /* Declaration hooks.  */
>>> diff --git a/gcc/langhooks.h b/gcc/langhooks.h
>>> index 3bf41522732..b569242d70d 100644
>>> --- a/gcc/langhooks.h
>>> +++ b/gcc/langhooks.h
>>> @@ -190,6 +190,11 @@ struct lang_hooks_for_types
>>>    i.e., type without virtual base classes or tail padding.  Returns
>>>    NULL_TREE otherwise.  */
>>> tree (*classtype_as_base) (const_tree);
>>> +
>>> +  /* Build a REF to the object that represents the counted_by per the 
>>> attribute
>>> +     counted_by attached to the field.  Otherwise return NULL_TREE.  Set 
>>> the
>>> +     TYPE of the counted_by field to the last parameter.  */
>>> +  tree (*build_counted_by_ref) (tree, tree, tree *);
>>> };
>>> 
>>> /* Language hooks related to decls and the symbol table.  */
>>> diff --git a/gcc/params.opt b/gcc/params.opt
>>> index f8884e976e7..fbfa6475b00 100644
>>> --- a/gcc/params.opt
>>> +++ b/gcc/params.opt
>>> @@ -848,6 +848,10 @@ The minimum probability of reaching a source block for 
>>> interblock speculative sc
>>> Common Joined UInteger Var(param_min_vect_loop_bound) Param Optimization
>>> If -ftree-vectorize is used, the minimal loop bound of a loop to be 
>>> considered for vectorization.
>>> 
>>> +-param=objsz-allow-dereference-input=
>>> +Common Joined UInteger Var(param_objsz_allow_dereference_input) 
>>> IntegerRange(0, 1) Param
>>> +Allow the size expression generated by the __builtin_dynamic_object_size 
>>> function to dereference its input pointer.
>>> +
>>> -param=openacc-kernels=
>>> Common Joined Enum(openacc_kernels) Var(param_openacc_kernels) 
>>> Init(OPENACC_KERNELS_PARLOOPS) Param
>>> --param=openacc-kernels=[decompose|parloops] Specify mode of OpenACC 
>>> 'kernels' constructs handling.
>>> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-10-b.c 
>>> b/gcc/testsuite/gcc.dg/flex-array-counted-by-10-b.c
>>> new file mode 100644
>>> index 00000000000..571921be5e3
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-10-b.c
>>> @@ -0,0 +1,7 @@
>>> +/* Test the attribute counted_by and its usage in
>>> +   __builtin_dynamic_object_size for whole object: when the pointer to the 
>>> whole
>>> +   object is NULL, but the objsz-allow-dereference-input=0.  */
>>> +/* { dg-do run } */
>>> +/* { dg-options "-O2 --param objsz-allow-dereference-input=0" } */
>>> +
>>> +#include "flex-array-counted-by-10.c"
>>> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-10.c 
>>> b/gcc/testsuite/gcc.dg/flex-array-counted-by-10.c
>>> new file mode 100644
>>> index 00000000000..d46874fc816
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-10.c
>>> @@ -0,0 +1,41 @@
>>> +/* Test the attribute counted_by and its usage in
>>> +   __builtin_dynamic_object_size for whole object: when the pointer to the 
>>> whole
>>> +   object is NULL.  */
>>> +/* { dg-do run } */
>>> +/* { dg-options "-O2 --param objsz-allow-dereference-input=1" } */
>>> +
>>> +#include "builtin-object-size-common.h"
>>> +struct annotated {
>>> +  int count;
>>> +  char array[] __attribute__((counted_by (count)));
>>> +};
>>> +
>>> +static int __attribute__((__noinline__,__noipa__)) size_of (struct 
>>> annotated * obj)
>>> +{
>>> +  return __builtin_dynamic_object_size (obj, 0);
>>> +}
>>> +
>>> +static int __attribute__((__noinline__,__noipa__)) size_of_1 (struct 
>>> annotated * obj)
>>> +{
>>> +  return __builtin_dynamic_object_size (obj, 1);
>>> +}
>>> +
>>> +static int __attribute__((__noinline__,__noipa__)) size_of_2 (struct 
>>> annotated * obj)
>>> +{
>>> +  return __builtin_dynamic_object_size (obj, 2);
>>> +}
>>> +
>>> +static int __attribute__((__noinline__,__noipa__)) size_of_3 (struct 
>>> annotated * obj)
>>> +{
>>> +  return __builtin_dynamic_object_size (obj, 3);
>>> +}
>>> +
>>> +int main()
>>> +{
>>> +  EXPECT (size_of (0), -1);
>>> +  EXPECT (size_of_1 (0), -1);
>>> +  EXPECT (size_of_2 (0), 0);
>>> +  EXPECT (size_of_3 (0), 0);
>>> +  return 0;
>>> +}
>>> +
>>> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-3.c 
>>> b/gcc/testsuite/gcc.dg/flex-array-counted-by-3.c
>>> index 78f50230e89..dba1ca646b8 100644
>>> --- a/gcc/testsuite/gcc.dg/flex-array-counted-by-3.c
>>> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-3.c
>>> @@ -1,7 +1,7 @@
>>> /* Test the attribute counted_by and its usage in
>>> * __builtin_dynamic_object_size.  */ 
>>> /* { dg-do run } */
>>> -/* { dg-options "-O2" } */
>>> +/* { dg-options "-O2 --param objsz-allow-dereference-input=1" } */
>>> 
>>> #include "builtin-object-size-common.h"
>>> 
>>> @@ -53,6 +53,11 @@ void __attribute__((__noinline__)) test ()
>>> array_annotated->b * sizeof (int));
>>>   EXPECT(__builtin_dynamic_object_size(array_nested_annotated->c, 1),
>>> array_nested_annotated->b * sizeof (int));
>>> +    EXPECT(__builtin_dynamic_object_size(array_annotated, 1),
>>> +   sizeof (struct annotated) + array_annotated->b * sizeof (int));
>>> +    EXPECT(__builtin_dynamic_object_size(array_nested_annotated, 1),
>>> +   sizeof (struct nested_annotated) 
>>> +   + array_nested_annotated->b * sizeof (int));
>>> }
>>> 
>>> int main(int argc, char *argv[])
>>> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-4.c 
>>> b/gcc/testsuite/gcc.dg/flex-array-counted-by-4.c
>>> index 20103d58ef5..5295ed1e15b 100644
>>> --- a/gcc/testsuite/gcc.dg/flex-array-counted-by-4.c
>>> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-4.c
>>> @@ -4,7 +4,7 @@ allocation size mismatched with the value of counted_by 
>>> attribute?
>>> We should always use the latest value that is hold by the counted_by
>>> field.  */
>>> /* { dg-do run } */
>>> -/* { dg-options "-O -fstrict-flex-arrays=3" } */
>>> +/* { dg-options "-O -fstrict-flex-arrays=3 --param 
>>> objsz-allow-dereference-input=1" } */
>>> 
>>> #include "builtin-object-size-common.h"
>>> 
>>> @@ -23,7 +23,15 @@ struct annotated {
>>>  So, __builtin_object_size can not directly use the type of the pointee
>>>  to decide the size of the object the pointer points to.
>>> 
>>> -   There are only two reliable ways:
>>> +   However, if the pointer points to a strucure with FAM, and the FAM is
>>> +   annotated with counted_by attribute, we can get the size of the pointee
>>> +   object by the size of the structure type and the counted_by attribute
>>> +   since structure with FAM cannot be an element of an array, the pointer
>>> +   that points to such type must point to a single object with the type,
>>> +   therefore, we can decide the size of the object from the size of the
>>> +   type for the pointee.
>>> +
>>> +   There are other two reliable ways:
>>>  A. observed allocations  (call to the allocation functions in the routine)
>>>  B. observed accesses     (read or write access to the location of the
>>>                            pointer points to)
>>> @@ -155,11 +163,18 @@ int main ()
>>> EXPECT(__builtin_dynamic_object_size(p->array, 2), p->foo * sizeof(char));
>>> EXPECT(__builtin_dynamic_object_size(p->array, 3), p->foo * sizeof(char));
>>> /* When checking the pointer p, we have no observed allocation nor observed
>>> -    access, therefore, we cannot determine the size info here.  */
>>> -  EXPECT(__builtin_dynamic_object_size(p, 0), -1);
>>> -  EXPECT(__builtin_dynamic_object_size(p, 1), -1);
>>> -  EXPECT(__builtin_dynamic_object_size(p, 2), 0);
>>> -  EXPECT(__builtin_dynamic_object_size(p, 3), 0);
>>> +    access, however, since the pointer points to a strucure with FAM, and 
>>> the
>>> +    FAM is annotated with counted_by attribute, we can get the size of the
>>> +    pointee object by the size of the TYPE and the counted_by attribute.  
>>> */ 
>>> +
>>> +  size_t expected_size
>>> +    = MAX (sizeof (struct annotated),
>>> +   (__builtin_offsetof (struct annotated, array[0])
>>> +    + p->foo * sizeof (char)));
>>> +  EXPECT(__builtin_dynamic_object_size(p, 0), expected_size);
>>> +  EXPECT(__builtin_dynamic_object_size(p, 1), expected_size);
>>> +  EXPECT(__builtin_dynamic_object_size(p, 2), expected_size);
>>> +  EXPECT(__builtin_dynamic_object_size(p, 3), expected_size);
>>> 
>>> /* When checking the access p->array, we only have info on the counted-by
>>>   value.  */ 
>>> @@ -168,11 +183,17 @@ int main ()
>>> EXPECT(__builtin_dynamic_object_size(q->array, 2), q->foo * sizeof(char));
>>> EXPECT(__builtin_dynamic_object_size(q->array, 3), q->foo * sizeof(char));
>>> /* When checking the pointer p, we have no observed allocation nor observed
>>> -    access, therefore, we cannot determine the size info here.  */
>>> -  EXPECT(__builtin_dynamic_object_size(q, 0), -1);
>>> -  EXPECT(__builtin_dynamic_object_size(q, 1), -1);
>>> -  EXPECT(__builtin_dynamic_object_size(q, 2), 0);
>>> -  EXPECT(__builtin_dynamic_object_size(q, 3), 0);
>>> +    access, however, since the pointer points to a strucure with FAM, and 
>>> the
>>> +    FAM is annotated with counted_by attribute, we can get the size of the
>>> +    pointee object by the size of the TYPE and the counted_by attribute.  
>>> */
>>> +  expected_size
>>> +    = MAX (sizeof (struct annotated),
>>> +   (__builtin_offsetof (struct annotated, array[0])
>>> +    + q->foo * sizeof (char)));
>>> +  EXPECT(__builtin_dynamic_object_size(q, 0), expected_size);
>>> +  EXPECT(__builtin_dynamic_object_size(q, 1), expected_size);
>>> +  EXPECT(__builtin_dynamic_object_size(q, 2), expected_size);
>>> +  EXPECT(__builtin_dynamic_object_size(q, 3), expected_size);
>>> 
>>> DONE ();
>>> }
>>> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-5-b.c 
>>> b/gcc/testsuite/gcc.dg/flex-array-counted-by-5-b.c
>>> new file mode 100644
>>> index 00000000000..d459959c623
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-5-b.c
>>> @@ -0,0 +1,51 @@
>>> +/* Test the attribute counted_by and its usage in
>>> + * __builtin_dynamic_object_size: when the counted_by field is negative,
>>> + * and objsz-allow-dereference-input=0.  */
>>> +/* { dg-do run } */
>>> +/* { dg-options "-O2 --param objsz-allow-dereference-input=0" } */
>>> +
>>> +#include "builtin-object-size-common.h"
>>> +
>>> +struct annotated {
>>> +  int b;
>>> +  int c[] __attribute__ ((counted_by (b)));
>>> +} *array_annotated;
>>> +
>>> +struct nested_annotated {
>>> +  struct {
>>> +    union {
>>> +      int b;
>>> +      float f; 
>>> +    };
>>> +    int n;
>>> +  };
>>> +  int c[] __attribute__ ((counted_by (b)));
>>> +} *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, 1), -1);
>>> +    EXPECT(__builtin_dynamic_object_size(array_nested_annotated, 1), -1);
>>> +    EXPECT(__builtin_dynamic_object_size(array_annotated->c, 1), 0);
>>> +    EXPECT(__builtin_dynamic_object_size(array_nested_annotated->c, 1), 0);
>>> +}
>>> +
>>> +int main(int argc, char *argv[])
>>> +{
>>> +  setup (-10);   
>>> +  test ();
>>> +  DONE ();
>>> +}
>>> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-5-c.c 
>>> b/gcc/testsuite/gcc.dg/flex-array-counted-by-5-c.c
>>> new file mode 100644
>>> index 00000000000..2de89c54be8
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-5-c.c
>>> @@ -0,0 +1,7 @@
>>> +/* Test the attribute counted_by and its usage in
>>> + * __builtin_dynamic_object_size: when the counted_by field is negative,
>>> + * and no objsz-allow-dereference-input option.  */
>>> +/* { dg-do run } */
>>> +/* { dg-options "-O2 " } */
>>> +
>>> +#include "flex-array-counted-by-5-b.c"
>>> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by-5.c 
>>> b/gcc/testsuite/gcc.dg/flex-array-counted-by-5.c
>>> index 68f9b0f7c8d..89e1857206f 100644
>>> --- a/gcc/testsuite/gcc.dg/flex-array-counted-by-5.c
>>> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-5.c
>>> @@ -1,7 +1,7 @@
>>> /* Test the attribute counted_by and its usage in
>>> * __builtin_dynamic_object_size: when the counted_by field is negative.  */
>>> /* { dg-do run } */
>>> -/* { dg-options "-O2" } */
>>> +/* { dg-options "-O2 --param objsz-allow-dereference-input=1" } */
>>> 
>>> #include "builtin-object-size-common.h"
>>> 
>>> @@ -36,6 +36,10 @@ void __attribute__((__noinline__)) setup (int attr_count)
>>> 
>>> void __attribute__((__noinline__)) test ()
>>> {
>>> +    EXPECT(__builtin_dynamic_object_size(array_annotated, 1),
>>> +   sizeof (struct annotated));
>>> +    EXPECT(__builtin_dynamic_object_size(array_nested_annotated, 1),
>>> +   sizeof (struct nested_annotated));
>>>   EXPECT(__builtin_dynamic_object_size(array_annotated->c, 1), 0);
>>>   EXPECT(__builtin_dynamic_object_size(array_nested_annotated->c, 1), 0);
>>> }
>>> diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
>>> index 65bcdd57325..fbe4ee4b13e 100644
>>> --- a/gcc/tree-object-size.cc
>>> +++ b/gcc/tree-object-size.cc
>>> @@ -24,12 +24,15 @@ along with GCC; see the file COPYING3.  If not see
>>> #include "backend.h"
>>> #include "tree.h"
>>> #include "gimple.h"
>>> +#include "cfghooks.h"
>>> #include "tree-pass.h"
>>> #include "ssa.h"
>>> #include "gimple-pretty-print.h"
>>> #include "fold-const.h"
>>> +#include "cfgloop.h"
>>> #include "tree-object-size.h"
>>> #include "gimple-iterator.h"
>>> +#include "langhooks.h"
>>> #include "gimple-fold.h"
>>> #include "tree-cfg.h"
>>> #include "tree-dfa.h"
>>> @@ -56,6 +59,8 @@ struct GTY(()) object_size
>>> tree size;
>>> /* Estimate of the size of the whole object.  */
>>> tree wholesize;
>>> +  /* Estimate of the size when the pointer is NULL.  */
>>> +  tree nullsize;
>>> };
>>> 
>>> static tree compute_object_offset (tree, const_tree);
>>> @@ -224,6 +229,7 @@ object_sizes_initialize (struct object_size_info *osi, 
>>> unsigned varno,
>>> 
>>> object_sizes[object_size_type][varno].size = val;
>>> object_sizes[object_size_type][varno].wholesize = wholeval;
>>> +  object_sizes[object_size_type][varno].nullsize = NULL_TREE;
>>> }
>>> 
>>> /* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type.  
>>> The
>>> @@ -250,11 +256,12 @@ bundle_sizes (tree name, tree expr)
>>>  gimple variable, a MODIFY_EXPR or a TREE_VEC.  The MODIFY_EXPR is for
>>>  expressions that need to be gimplified.  TREE_VECs are special, they're
>>>  emitted only for GIMPLE_PHI and the PHI result variable is the last element
>>> -   of the vector.  */
>>> +   of the vector.  Set the value of nullsize to size_unknown when
>>> +   NEED_NULLCHECK is true.  */
>>> 
>>> static bool
>>> object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
>>> -  tree wholeval)
>>> +  tree wholeval, bool need_nullcheck = false)
>>> {
>>> int object_size_type = osi->object_size_type;
>>> object_size osize = object_sizes[object_size_type][varno];
>>> @@ -309,6 +316,9 @@ object_sizes_set (struct object_size_info *osi, 
>>> unsigned varno, tree val,
>>> 
>>> object_sizes[object_size_type][varno].size = val;
>>> object_sizes[object_size_type][varno].wholesize = wholeval;
>>> +  if (need_nullcheck)
>>> +    object_sizes[object_size_type][varno].nullsize
>>> +      = size_unknown (object_size_type);
>>> 
>>> return changed;
>>> }
>>> @@ -1166,6 +1176,180 @@ propagate_unknowns (object_size_info *osi, tree 
>>> expr, bitmap unknowns)
>>>   }
>>> }
>>> 
>>> +/* Given a DEF_STMT, which is the DEF_STMT of the SSA_NAME whose
>>> +   object size is queried, and COND, THEN_SIZE, ELSE_SIZE, the final
>>> +   RESULT.
>>> +
>>> +   Generate the gimple sequence for the following:
>>> +
>>> + result = COND ? THEN_SIZE : ELSE_SIZE;
>>> +
>>> +   and then insert this new sequence in the proper position based on
>>> +   DEF_STMT:
>>> +
>>> +   A.  If DEF_STMT is valid, the generated gimple sequence should be
>>> +   inserted AFTER the DEF_STMT;
>>> +
>>> +   B.  If DEF_STMT is GIMPLE_NOP, i.e.,  no def is available, such as
>>> +   the parameter case.  Under such situation, the new generated
>>> +   gimple sequence should be inserted in the very beginning of the
>>> +   current basic block.
>>> +
>>> +   The following is the new generated IR if DEF_STMT is valid:
>>> +
>>> +   OLD:
>>> +    cur_bb:
>>> + DEF_STMT;
>>> +
>>> +   NEW:
>>> +    cur_bb:
>>> + DEF_STMT;
>>> + if (COND) then goto then_bb
>>> +  else goto else_bb
>>> +
>>> +    then_bb: (very unlikely)
>>> + tmp_size_1 = THEN_SIZE;
>>> + goto cur_bb_post;
>>> +
>>> +    else_bb: (very likely)
>>> + tmp_size_2 = ELSE_SIZE;
>>> + goto cur_bb_post;
>>> +
>>> +    cur_bb_post:
>>> + tmp_size_3 = PHI (tmp_size_1, tmp_size_2);
>>> + result = tmp_size_3;
>>> + */
>>> +
>>> +static void
>>> +insert_cond_and_size (gimple *def_stmt, tree cond,
>>> +      tree then_size, tree else_size,
>>> +      tree result)
>>> +{
>>> +  enum gimple_code code = gimple_code (def_stmt);
>>> +  basic_block cur_bb;
>>> +  edge e;
>>> +  gimple_stmt_iterator gsi;
>>> +  /* Split the cur_bb to cur_bb and cur_bb_post based on DEF_STMT.
>>> +     if DEF_STMT is NOT GIMPLE_NOP, split it after DEF_STMT, otherwise,
>>> +     split it after the label of the block.  */
>>> +  if (code == GIMPLE_NOP)
>>> +    {
>>> +      cur_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
>>> +      e = split_block (cur_bb, (gimple *) NULL);
>>> +      gsi = gsi_end_bb (cur_bb);
>>> +    }
>>> +  else
>>> +    {
>>> +      cur_bb = gimple_bb (def_stmt);
>>> +      e = split_block (cur_bb, def_stmt);
>>> +      gsi = gsi_for_stmt (def_stmt);
>>> +    }
>>> +
>>> +  basic_block cur_bb_post = e->dest;
>>> +
>>> +  /* Create new basic blocks then_bb and else_bb.  */
>>> +  basic_block then_bb = create_empty_bb (cur_bb);
>>> +  basic_block else_bb = create_empty_bb (then_bb);
>>> +  add_bb_to_loop (then_bb, cur_bb->loop_father);
>>> +  add_bb_to_loop (else_bb, cur_bb->loop_father);
>>> +  loops_state_set (LOOPS_NEED_FIXUP);
>>> +
>>> +  /* Set up the edges between cur_bb, then_bb, else_bb, and cur_bb_post.
>>> +   OLD:
>>> + cur_bb
>>> +  |e
>>> +  V
>>> + cur_bb_post
>>> +
>>> +   NEW:
>>> + cur_bb
>>> + /e0  \e1
>>> +       V      V
>>> +    then_bb else_bb
>>> + \e2 /e3
>>> +  V
>>> + cur_bb_post
>>> +  */
>>> +  cur_bb_post->count = e->count ();
>>> +  remove_edge (e);
>>> +  edge e0 = make_edge (cur_bb, then_bb, EDGE_TRUE_VALUE);
>>> +  e0->probability = profile_probability::very_unlikely ();
>>> +  then_bb->count = e0->count ();
>>> +  edge e1 = make_edge (cur_bb, else_bb, EDGE_FALSE_VALUE);
>>> +  e1->probability = profile_probability::very_likely ();
>>> +  else_bb->count = e1->count ();
>>> +  edge e2 = make_single_succ_edge (then_bb, cur_bb_post, EDGE_FALLTHRU);
>>> +  edge e3 = make_single_succ_edge (else_bb, cur_bb_post, EDGE_FALLTHRU);
>>> +
>>> +  /* Update dominance info for the newly created blocks.  */
>>> +  if (dom_info_available_p (CDI_DOMINATORS))
>>> +    {
>>> +      set_immediate_dominator (CDI_DOMINATORS, then_bb, cur_bb);
>>> +      set_immediate_dominator (CDI_DOMINATORS, else_bb, cur_bb);
>>> +      set_immediate_dominator (CDI_DOMINATORS, cur_bb_post, cur_bb);
>>> +    }
>>> +
>>> +  /* Insert new gimples into corresponding blocks.  */
>>> +
>>> +  /* Insert the new COND gimple into cur_bb after GSI.  */
>>> +  gcond *cond_stmt = gimple_build_cond_from_tree (cond, NULL_TREE, 
>>> NULL_TREE);
>>> +  gsi_insert_after (&gsi, cond_stmt, GSI_NEW_STMT);
>>> +
>>> +  /* Insert the new assign gimple tmp_size_1 = THEN_SIZE into then_bb.  */
>>> +  tree tmp_size = create_tmp_var (TREE_TYPE (result), "tmp_size");
>>> +  gimple_seq seq = NULL;
>>> +  tree tmp_size_1 = force_gimple_operand (then_size, &seq, true, tmp_size);
>>> +  gimple_stmt_iterator new_gsi = gsi_start_bb (then_bb);
>>> +  gsi_insert_seq_after (&new_gsi, seq, GSI_LAST_NEW_STMT);
>>> +
>>> +  /* Insert the new assign gimple tmp_size_2 = ELSE_SIZE into else_bb.  */
>>> +  seq = NULL;
>>> +  tree tmp_size_2 = force_gimple_operand (else_size, &seq, true, tmp_size);
>>> +  new_gsi = gsi_start_bb (else_bb);
>>> +  gsi_insert_seq_after (&new_gsi, seq, GSI_LAST_NEW_STMT);
>>> +
>>> +  /* Insert the new phi gimple tmp_size_3 = phi (tmp_size_1, tmp_size_2)
>>> +     into cur_bb_post.  */
>>> +  tree tmp_size_3 = make_ssa_name (tmp_size);
>>> +  gphi *phi = create_phi_node (tmp_size_3, cur_bb_post);
>>> +  add_phi_arg (phi, tmp_size_1, e2, UNKNOWN_LOCATION);
>>> +  add_phi_arg (phi, tmp_size_2, e3, UNKNOWN_LOCATION);
>>> +
>>> +  /* Then insert a new assign gimple result = tmp_size_3 into cur_bb_post. 
>>>  */
>>> +  gassign *assign_stmt = gimple_build_assign (result, tmp_size_3);
>>> +  new_gsi = gsi_start_bb (cur_bb_post);
>>> +  gsi_insert_before (&new_gsi, assign_stmt, GSI_NEW_STMT);
>>> +
>>> +}
>>> +
>>> +/* Given a DEF_STMT, which is the DEF_STMT of the VAR whose object size
>>> +   is queried, the SIZE_EXPR when VAR is not NULL, and the NULL_SIZE
>>> +   when VAR is NULL.
>>> +
>>> +   Generate the gimple sequence for the following:
>>> +
>>> + result = VAR == NULL ? NULL_SIZE : SIZE_EXPR;
>>> +
>>> +   and then insert this new sequence in the proper position based on
>>> +   DEF_STMT.  */
>>> +
>>> +static void
>>> +insert_null_check_and_size (gimple *def_stmt, tree size_expr,
>>> +    tree var, tree nullsize)
>>> +{
>>> +  gcc_assert (TREE_CODE (size_expr) == MODIFY_EXPR);
>>> +  tree result = TREE_OPERAND (size_expr, 0);
>>> +  size_expr = TREE_OPERAND (size_expr, 1);
>>> +
>>> +  /* We should guard the size expression with the check to see whether the
>>> +     original pointer is NULL or not since a NULL pointer might be passed
>>> +     and this is valid.  */
>>> +  tree cond = fold_build2 (EQ_EXPR, boolean_type_node, var,
>>> +   build_zero_cst (TREE_TYPE (var)));
>>> +
>>> +  insert_cond_and_size (def_stmt, cond, nullsize, size_expr, result);
>>> +}
>>> +
>>> /* Walk through size expressions that need reexamination and generate
>>>  statements for them.  */
>>> 
>>> @@ -1247,14 +1431,20 @@ gimplify_size_expressions (object_size_info *osi)
>>> 
>>> if (size_expr)
>>>  {
>>> -      gimple_stmt_iterator gsi;
>>> -      if (code == GIMPLE_NOP)
>>> - gsi = gsi_start_bb (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
>>> +      if (osize.nullsize != NULL_TREE)
>>> + insert_null_check_and_size (stmt, size_expr,
>>> +    ssa_name (i), osize.nullsize);
>>>    else
>>> - gsi = gsi_for_stmt (stmt);
>>> -
>>> -      force_gimple_operand (size_expr, &seq, true, NULL);
>>> -      gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
>>> + {
>>> +  gimple_stmt_iterator gsi;
>>> +  if (code == GIMPLE_NOP)
>>> +    gsi = gsi_start_bb (single_succ
>>> + (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
>>> +  else
>>> +    gsi = gsi_for_stmt (stmt);
>>> +  force_gimple_operand (size_expr, &seq, true, NULL);
>>> +  gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
>>> + }
>>>  }
>>> }
>>> 
>>> @@ -1483,6 +1673,95 @@ expr_object_size (struct object_size_info *osi, tree 
>>> ptr, tree value)
>>> object_sizes_set (osi, varno, bytes, wholesize);
>>> }
>>> 
>>> +/* Check whether the pointee type of the VAR is a structure with flexible
>>> +   array member attached a counted_by attribute.  */
>>> +
>>> +static bool
>>> +is_pointee_fam_struct_with_counted_by (tree var)
>>> +{
>>> +  if (!POINTER_TYPE_P (TREE_TYPE (var)))
>>> +    return false;
>>> +
>>> +  const_tree pointee_type = TREE_TYPE (TREE_TYPE (var));
>>> +  if (!flexible_array_type_p (pointee_type))
>>> +    return false;
>>> +
>>> +  if (TREE_CODE (pointee_type) != RECORD_TYPE)
>>> +    return false;
>>> +
>>> +  tree last = last_field (pointee_type);
>>> +  /* Check whether the last FAM field has a counted_by attribute.  */
>>> +  if (last && lookup_attribute ("counted_by", DECL_ATTRIBUTES (last)))
>>> +    return true;
>>> +
>>> +  return false;
>>> +}
>>> +
>>> +/* Compute an object size expression for VAR, whose pointee TYPE is a
>>> +   structure with flexible array member.  */
>>> +
>>> +static void
>>> +record_with_fam_object_size (struct object_size_info *osi, tree var)
>>> +{
>>> +  int object_size_type = osi->object_size_type;
>>> +  tree size = size_unknown (object_size_type);
>>> +  gcc_assert (is_pointee_fam_struct_with_counted_by (var));
>>> +
>>> +  tree pointee_type = TREE_TYPE (TREE_TYPE (var));
>>> +  tree counted_by_ref = NULL_TREE;
>>> +  tree counted_by_type = NULL_TREE;
>>> +  tree last = last_field (pointee_type);
>>> +
>>> +  /* build a counted_by reference.  */
>>> +  if (lang_hooks.types.build_counted_by_ref)
>>> +    {
>>> +      tree datum = build1 (INDIRECT_REF, pointee_type, var);
>>> +      counted_by_ref
>>> + = lang_hooks.types.build_counted_by_ref (datum, last,
>>> + &counted_by_type);
>>> +    }
>>> +
>>> +  /* If the counted_by reference is available, the size of the whole 
>>> structure
>>> +     can be computed.  */
>>> +  if (counted_by_ref)
>>> +    {
>>> +      tree element_type = TREE_TYPE (TREE_TYPE (last));
>>> +      tree element_size = TYPE_SIZE_UNIT (element_type);
>>> +      size = fold_build2 (MEM_REF, counted_by_type, counted_by_ref,
>>> +  build_int_cst (ptr_type_node, 0));
>>> +      /* If counted_by is a negative value, treat it as zero.  */
>>> +      if (!TYPE_UNSIGNED (counted_by_type))
>>> + size = size_binop (MAX_EXPR,
>>> +   build_zero_cst (counted_by_type),
>>> +   size);
>>> +
>>> +      /* The total size of the whole object is computed as:
>>> + MAX (sizeof (pointee_type),
>>> +      offsetof (pointee_type, last) + counted_by * element_size).  */
>>> +      size = size_binop (MULT_EXPR,
>>> + fold_convert (sizetype, size),
>>> + fold_convert (sizetype, element_size));
>>> +      size = size_binop (PLUS_EXPR,
>>> + byte_position (last),
>>> + size);
>>> +      size = size_binop (MAX_EXPR,
>>> + TYPE_SIZE_UNIT (pointee_type),
>>> + size);
>>> +
>>> +      if (!todo)
>>> + todo = TODO_update_ssa | TODO_cleanup_cfg;
>>> +    }
>>> +  /* Initialize to 0 for maximum size and M1U for minimum size so that
>>> +     it gets immediately overridden.  */
>>> +  object_sizes_initialize (osi, SSA_NAME_VERSION (var),
>>> +   size_initval (object_size_type),
>>> +   size_initval (object_size_type));
>>> +
>>> +  /* The size expression should be put into a new block in the new added
>>> +     conditional control flow.  */
>>> +  object_sizes_set (osi, SSA_NAME_VERSION (var), size, size, true);
>>> +  return;
>>> +}
>>> 
>>> /* Compute object_sizes for PTR, defined to the result of a call.  */
>>> 
>>> @@ -2002,6 +2281,19 @@ collect_object_sizes_for (struct object_size_info 
>>> *osi, tree var)
>>>     gcc_unreachable ();
>>>   }
>>> 
>>> +  /* If the size is UNKNOWN after evaluating use-def chain, We can evaluate
>>> +     the SIZE of the pointee TYPE ONLY when this TYPE is a structure type
>>> +     with flexible array member that is attached a counted_by attribute,
>>> +     Since a structure with FAM can not be an element of an array, PTR
>>> +     must point to at most a single object.
>>> +     Control this with param_objsz_allow_dereference_input since doing this
>>> +     might add additional undefined behavior to the original source code.  
>>> */
>>> +  if (object_sizes_unknown_p (object_size_type, varno)
>>> +      && object_size_type & OST_DYNAMIC
>>> +      && param_objsz_allow_dereference_input
>>> +      && is_pointee_fam_struct_with_counted_by (var))
>>> + record_with_fam_object_size (osi, var);
>>> +
>>> /* Dynamic sizes use placeholder temps to return an answer, so it is always
>>>    safe to set COMPUTED for them.  */
>>> if ((object_size_type & OST_DYNAMIC)
>>> @@ -2267,6 +2559,13 @@ dynamic_object_sizes_execute_one 
>>> (gimple_stmt_iterator *i, gimple *call)
>>> /* fold_builtin_call_array may wrap the result inside a
>>>    NOP_EXPR.  */
>>> STRIP_NOPS (result);
>>> +
>>> +  /* when NULL pointer checking is added, the cfg is changed, and the
>>> +   * original basic block associted with the iterator i might be different
>>> +   * than the new basic block associated with the corresponding stmt.  */
>>> +  if (gsi_stmt (*i)->bb != gsi_bb (*i))
>>> +    i->bb = gsi_stmt (*i)->bb;
>>> +
>>> gimplify_and_update_call_from_tree (i, result);
>>> 
>>> if (dump_file && (dump_flags & TDF_DETAILS))
>>> -- 
>>> 2.43.5
>>> 
>> 
> 

Reply via email to