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