Hi, This is the 2nd 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. Per the following discussion: (Questions on replacing a structure pointer reference to a call to .ACCESS_WITH_SIZE in C FE) https://gcc.gnu.org/pipermail/gcc-patches/2025-April/680540.html https://gcc.gnu.org/pipermail/gcc-patches/2025-April/681229.html The summary of the above discussion: A. It’s not safe in general to replace a structure pointer reference to a call to .ACCESS_WITH_SIZE in C FE. Since data-flow analysis is needed to make sure that the access to the size member is valid, i.e, the structure is accessible and initialized, etc. B. It should be safe to generate the reference to field member when we evaluate the BDOS builtin as the 1st version of the patch . And doing this in tree-object-size should also cover -fsanitize=object-size. C. When generating the reference to the field member in tree-object-size, we should guard this reference with a checking on the pointer to the structure is valid. Compare to the 1st version, the major changes are based on the above: 1. Update the comments per Sid's suggestions. 2. Reorg the code to make it easily to be understood: Add a new routine "is_pointee_fam_struct_with_counted_by" to make the checking easier; 3. Add one more cond_expr to guard the size_expr as: (prt == NULL) ? SIZE_UNKNOWN : SIZE_EXPR 4. In order to gimplify the above COND_EXPR (the current force_gimple_operand doesnot work for the new control flow), Add one more new routine "insert_cond_and_size" to construct the whole control flow graph for the above cond_expr. Please refer to: https://gcc.gnu.org/pipermail/gcc-patches/2025-April/682021.html for this discussion. 5. Add new testing cases for testing the prt==NULL cases. 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. 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: * langhooks-def.h (LANG_HOOKS_BUILD_COUNTED_BY_REF): New language hook. * langhooks.h (struct lang_hooks_for_types): Add build_counted_by_ref. * 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/langhooks-def.h | 4 +- gcc/langhooks.h | 5 + .../gcc.dg/flex-array-counted-by-10.c | 41 +++ .../gcc.dg/flex-array-counted-by-3.c | 5 + .../gcc.dg/flex-array-counted-by-4.c | 34 +- .../gcc.dg/flex-array-counted-by-5.c | 4 + gcc/tree-object-size.cc | 302 +++++++++++++++++- 10 files changed, 383 insertions(+), 22 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 2098120de29..648fb0551e6 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -776,6 +776,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 55d896e02df..dffabacc3c5 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -2946,8 +2946,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))) @@ -3045,7 +3045,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/langhooks-def.h b/gcc/langhooks-def.h index 6b34d324ab5..7909ea8a92c 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -221,6 +221,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, \ @@ -248,7 +249,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 dc8d878c7fb..797c8bcc0c0 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/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..27d21f214f9 --- /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" } */ + +#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..88ad50bea6b 100644 --- a/gcc/testsuite/gcc.dg/flex-array-counted-by-3.c +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-3.c @@ -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..6ac2be4471d 100644 --- a/gcc/testsuite/gcc.dg/flex-array-counted-by-4.c +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-4.c @@ -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..11c6ac326da 100644 --- a/gcc/testsuite/gcc.dg/flex-array-counted-by-5.c +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by-5.c @@ -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..a0b440b4700 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, SIZE_UNKNOWN, SIZE, the final + RESULT. + + Generate the gimple sequence for the following: + + result = COND ? SIZE_UNKNOWN : 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 = SIZE; + goto cur_bb_post; + + else_bb: (unlikely) + tmp_size_2 = SIZE_UNKNOWN; + 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 size_unknown, tree 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 = 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 (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 = SIZE_UNKNOWN into else_bb. */ + tree tmp_size_2 = make_ssa_name (tmp_size); + gassign *assign_stmt = gimple_build_assign (tmp_size_2, size_unknown); + gimple_set_location (assign_stmt, UNKNOWN_LOCATION); + new_gsi = gsi_start_bb (else_bb); + gsi_insert_after (&new_gsi, assign_stmt, 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. */ + 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 size = COND_EXPR_THEN (size_expr); + tree size_unknown = COND_EXPR_ELSE (size_expr); + insert_cond_and_size (stmt, cond, + size_unknown, 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,16 @@ 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 by 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. */ + if (object_sizes_unknown_p (object_size_type, varno) + && object_size_type & OST_DYNAMIC + && 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 +2469,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