Hi, This is the 3rd version of the patch for:
Evaluate the object size by the size of the pointee type when the type is a structure with flexible array member which is annotated with counted_by. Compared to the 2nd version of the patch at: https://gcc.gnu.org/pipermail/gcc-patches/2025-May/682923.html The major changes include: A. Add a new --param objsz-allow-dereference-input=0|1 to control this feature; B. Some code reorg to the routine "insert_cond_and_size" to make it more readable. The patch has been bootstrapped and regression tested on both x86 and aarch64. Okay for trunk? 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_info): Add a new field insert_cf. (insert_cond_and_size): New function. (gimplify_size_expressions): Handle new field insert_cf. (compute_builtin_object_size): Init the new field to false; (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 insert_cf. 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-10.c: New test. --- gcc/c/c-lang.cc | 3 + gcc/c/c-tree.h | 1 + gcc/c/c-typeck.cc | 6 +- gcc/doc/invoke.texi | 13 + gcc/langhooks-def.h | 4 +- gcc/langhooks.h | 5 + gcc/params.opt | 4 + .../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 | 36 ++- .../gcc.dg/flex-array-counted-by-5.c | 6 +- gcc/tree-object-size.cc | 305 +++++++++++++++++- 12 files changed, 406 insertions(+), 25 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by-10.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 364f51df58c..627791551b4 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -777,6 +777,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 e24629be918..44031ca1ae3 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -2940,8 +2940,8 @@ should_suggest_deref_p (tree datum_type) &(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); if (!c_flexible_array_member_type_p (TREE_TYPE (subdatum))) @@ -3039,7 +3039,7 @@ handle_counted_by_for_component_ref (location_t loc, tree ref) tree datum = TREE_OPERAND (ref, 0); tree subdatum = TREE_OPERAND (ref, 1); tree counted_by_type = NULL_TREE; - tree counted_by_ref = build_counted_by_ref (datum, subdatum, + 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, diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 693bd57691e..1d3277d5e71 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -17566,6 +17566,19 @@ 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 members 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 010aaedd9de..98f3d0027d9 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 cb03c8348e3..4f750955920 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 31aa0bd5753..d48367ef9ea 100644 --- a/gcc/params.opt +++ b/gcc/params.opt @@ -840,6 +840,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.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..03bfcfd26eb 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,13 @@ 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. */ + EXPECT(__builtin_dynamic_object_size(p, 0), 19); + EXPECT(__builtin_dynamic_object_size(p, 1), 19); + EXPECT(__builtin_dynamic_object_size(p, 2), 19); + EXPECT(__builtin_dynamic_object_size(p, 3), 19); /* When checking the access p->array, we only have info on the counted-by value. */ @@ -168,11 +178,13 @@ 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. */ + EXPECT(__builtin_dynamic_object_size(q, 0), 29); + EXPECT(__builtin_dynamic_object_size(q, 1), 29); + EXPECT(__builtin_dynamic_object_size(q, 2), 29); + EXPECT(__builtin_dynamic_object_size(q, 3), 29); DONE (); } 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 f348673ae75..6220c16df21 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" @@ -45,6 +48,7 @@ struct object_size_info int object_size_type; unsigned char pass; bool changed; + bool insert_cf; bitmap visited, reexamine; unsigned int *depths; unsigned int *stack, *tos; @@ -1086,6 +1090,153 @@ 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 likely) + tmp_size_1 = THEN_SIZE; + goto cur_bb_post; + + else_bb: (unlikely) + 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_likely (); + then_bb->count = e0->count (); + edge e1 = make_edge (cur_bb, else_bb, EDGE_FALSE_VALUE); + e1->probability = profile_probability::very_unlikely (); + 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); + + return; +} + /* Walk through size expressions that need reexamination and generate statements for them. */ @@ -1167,14 +1318,30 @@ 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 (osi->insert_cf) + { + gcc_assert (TREE_CODE (size_expr) == MODIFY_EXPR); + tree result = TREE_OPERAND (size_expr, 0); + size_expr = TREE_OPERAND (size_expr, 1); + + gcc_assert (TREE_CODE (size_expr) == COND_EXPR); + tree cond = COND_EXPR_COND (size_expr); + tree then_size = COND_EXPR_THEN (size_expr); + tree else_size = COND_EXPR_ELSE (size_expr); + insert_cond_and_size (stmt, cond, + then_size, else_size, result); + } 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); + } } } @@ -1279,6 +1446,7 @@ compute_builtin_object_size (tree ptr, int object_size_type, expression needs to be gimplified. */ osi.pass = 0; osi.changed = false; + osi.insert_cf = false; collect_object_sizes_for (&osi, ptr); if (object_size_type & OST_DYNAMIC) @@ -1403,6 +1571,109 @@ 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)) + { + tree cond_expr = fold_build2 (LT_EXPR, boolean_type_node, + unshare_expr (size), + build_zero_cst (counted_by_type)); + size = fold_build3 (COND_EXPR, integer_type_node, cond_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); + + /* 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_expr = fold_build2 (EQ_EXPR, boolean_type_node, var, + build_zero_cst (TREE_TYPE (var))); + size = fold_build3 (COND_EXPR, integer_type_node, cond_expr, + size_unknown (object_size_type), size); + size = fold_convert (sizetype, 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. */ + osi->insert_cf = true; + object_sizes_set (osi, SSA_NAME_VERSION (var), size, size); + return; +} /* Compute object_sizes for PTR, defined to the result of a call. */ @@ -1922,6 +2193,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. So, + PTR must point to an single object with this strucure with FAM. + 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) @@ -2188,6 +2472,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 insert_cf is true, the cfg is changed, and the ogiginal 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.31.1