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