On 2025-06-25 10:07, Qing Zhao wrote:
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 for doing this, it's a strong step forward I think, but I have
some design suggestions below. Apologies for dragging my feet on this :/
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.
Another newline here to make it a new paragraph?
+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);
}
Please also add tests (or run these same tests) without --param
objsz-allow-dereference-input=1 and also with --param
objsz-allow-dereference-input=0 to make sure that they do the right thing.
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;
Instead of making this an OSI flag with a true/false value to support
this specific change, how about adding a new member to the object_size
struct called null_size? This makes sure that the idea of NULL-checked
sizes more general purpose and allow future extension to it. I can see
this being useful for, e.g. pr121786 and maybe more in future.
You would need:
1. A new member null_size
2. Add a new optional boolean argument null_checked to object_sizes_set,
set by default to false. When the argument is true, object_sizes_set
should set null_size to size_unknown (object_size_type).
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)
This would then become something like:
if (osize.null_size != NULL_TREE)
{
insert_null_checked (stmt, size_expr, osize.null_size);
}
else
...
and then you clean up insert_cond_and_size above to specifically build
an expression that bails out if PTR is NULL. I suppose
insert_null_checked will need the pointer too then.
+ {
+ 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);
+ }
Instead of a COND_EXPR, how about building a MAX(0, size) when size is
signed?
+
+ /* 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);
With the new null_size, this is then not needed...
+
+ 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);
... and you can just call
object_sizes_set (osi, SSA_NAME_VERSION (var), size, size, true);
to let gimplify_size_expressions generate the NULL check.
+ 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.
Suggest: Since a structure with FAM cannot 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)
@@ -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;
+
Updates the bb of the iterator so that we can continue iterating safely.
Seems legit, but I'm not 100% confident about it.
gimplify_and_update_call_from_tree (i, result);
if (dump_file && (dump_flags & TDF_DETAILS))
Thanks,
Sid