Adapt subobject computation logic from tree-object-size to make it work with variable sizes.
gcc/ChangeLog: * tree-dynamic-object-size.c (build_cond_branch): New function. (compute_object_offset): Use it. (get_closest_subobject): New function. (addr_dyn_object_size): Call it. Support subobject size computation. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c (test_dynarray_struct_subobj): New test. (main): Call it. Signed-off-by: Siddhesh Poyarekar <siddh...@gotplt.org> --- .../gcc.dg/builtin-dynamic-object-size-0.c | 34 ++++ gcc/tree-dynamic-object-size.c | 172 ++++++++++++++++-- 2 files changed, 194 insertions(+), 12 deletions(-) diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c index c72fa0508db..94f8e071e2c 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -237,6 +237,33 @@ test_dynarray_struct (size_t sz, size_t off) return __builtin_dynamic_object_size (&bin[off].c, 0); } +size_t +__attribute__ ((noinline)) +test_dynarray_struct_subobj (size_t sz, size_t off) +{ + struct dynarray_struct bin[sz]; + + return __builtin_dynamic_object_size (&bin[off].c[4], 1); +} + +size_t +__attribute__ ((noinline)) +test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz) +{ + struct dynarray_struct2 + { + long a; + int b; + char c[sz]; + }; + + struct dynarray_struct2 bin; + + *objsz = sizeof (bin); + + return __builtin_dynamic_object_size (&bin.c[off], 1); +} + size_t __attribute__ ((noinline)) test_substring (size_t sz, size_t off) @@ -334,6 +361,13 @@ main (int argc, char **argv) FAIL (); if (test_substring (128, 142) != 0) FAIL (); + if (test_dynarray_struct_subobj (42, 4) != 16 - 4) + FAIL (); + if (test_dynarray_struct_subobj (42, 48) != 0) + FAIL (); + size_t objsz = 0; + if (test_dynarray_struct_subobj2 (42, 4, &objsz) != objsz - 4 - 12) + FAIL (); if (test_substring_ptrplus (128, 4) != (128 - 4) * sizeof (int)) FAIL (); if (test_substring_ptrplus (128, 142) != 0) diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c index 8d7283623dc..ebc2fad7a87 100644 --- a/gcc/tree-dynamic-object-size.c +++ b/gcc/tree-dynamic-object-size.c @@ -102,12 +102,21 @@ static bitmap computed[4]; bool compute_builtin_dyn_object_size (tree, int, tree *, struct function *fun = NULL); +static tree +build_cond_branch (tree t, tree low_bound, tree unit_size) +{ + tree br = fold_build2 (MINUS_EXPR, TREE_TYPE (t), t, low_bound); + br = fold_convert (sizetype, br); + br = fold_build2 (MULT_EXPR, sizetype, unit_size, br); + + return br; +} + /* Compute offset of EXPR within VAR. Return error_mark_node if unknown. */ static tree compute_object_offset (const_tree expr, const_tree var) { - enum tree_code code = PLUS_EXPR; tree base, off, t; if (expr == var) @@ -150,12 +159,16 @@ compute_object_offset (const_tree expr, const_tree var) low_bound = array_ref_low_bound (CONST_CAST_TREE (expr)); unit_size = array_ref_element_size (CONST_CAST_TREE (expr)); if (! integer_zerop (low_bound)) - t = fold_build2 (MINUS_EXPR, TREE_TYPE (t), t, low_bound); - if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0) { - code = MINUS_EXPR; - t = fold_build1 (NEGATE_EXPR, TREE_TYPE (t), t); + tree cond = fold_build2 (GE_EXPR, TREE_TYPE (t), t, low_bound); + tree then_br = build_cond_branch (t, low_bound, unit_size); + tree else_br = build_cond_branch (low_bound, t, unit_size); + + then_br = fold_build2 (PLUS_EXPR, sizetype, base, then_br); + else_br = fold_build2 (MINUS_EXPR, sizetype, base, else_br); + return fold_build3 (COND_EXPR, sizetype, cond, then_br, else_br); } + t = fold_convert (sizetype, t); off = fold_build2 (MULT_EXPR, sizetype, unit_size, t); break; @@ -168,7 +181,7 @@ compute_object_offset (const_tree expr, const_tree var) return error_mark_node; } - return fold_build2 (code, sizetype, base, off); + return fold_build2 (PLUS_EXPR, sizetype, base, off); } /* Given an object size SZ and offset OFF into it, compute the usable object @@ -328,6 +341,105 @@ whole_var_size (struct object_size_info *osi, tree pt_var, return true; } +/* Get the most immediate subobject encapsulating the pointer PTR, given the + whole_var object WHOLE_VAR. */ + +static tree +get_closest_subobject (const_tree ptr, tree whole_var) +{ + tree var = TREE_OPERAND (ptr, 0); + + while (var != whole_var + && TREE_CODE (var) != BIT_FIELD_REF + && TREE_CODE (var) != COMPONENT_REF + && TREE_CODE (var) != ARRAY_REF + && TREE_CODE (var) != ARRAY_RANGE_REF + && TREE_CODE (var) != REALPART_EXPR + && TREE_CODE (var) != IMAGPART_EXPR) + var = TREE_OPERAND (var, 0); + + if (var != whole_var && TREE_CODE (var) == ARRAY_REF) + var = TREE_OPERAND (var, 0); + + if (! TYPE_SIZE_UNIT (TREE_TYPE (var))) + var = whole_var; + else if (var != whole_var && TREE_CODE (whole_var) == MEM_REF) + { + tree v = var; + while (v && v != whole_var) + switch (TREE_CODE (v)) + { + case ARRAY_REF: + if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0)))) + { + tree domain + = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0))); + if (domain && TYPE_MAX_VALUE (domain)) + { + v = NULL_TREE; + break; + } + } + v = TREE_OPERAND (v, 0); + break; + case REALPART_EXPR: + case IMAGPART_EXPR: + v = NULL_TREE; + break; + case COMPONENT_REF: + if (TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE) + { + v = NULL_TREE; + break; + } + while (v != whole_var && TREE_CODE (v) == COMPONENT_REF) + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + != UNION_TYPE + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + != QUAL_UNION_TYPE) + break; + else + v = TREE_OPERAND (v, 0); + if (TREE_CODE (v) == COMPONENT_REF + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + == RECORD_TYPE) + { + tree fld_chain = DECL_CHAIN (TREE_OPERAND (v, 1)); + for (; fld_chain; fld_chain = DECL_CHAIN (fld_chain)) + if (TREE_CODE (fld_chain) == FIELD_DECL) + break; + + if (fld_chain) + { + v = NULL_TREE; + break; + } + v = TREE_OPERAND (v, 0); + } + while (v != whole_var && TREE_CODE (v) == COMPONENT_REF) + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + != UNION_TYPE + && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) + != QUAL_UNION_TYPE) + break; + else + v = TREE_OPERAND (v, 0); + if (v != whole_var) + v = NULL_TREE; + else + v = whole_var; + break; + default: + v = whole_var; + break; + } + if (v == whole_var) + var = whole_var; + } + + return var; +} + /* Compute an object size estimate for PTR, which is a ADDR_EXPR. OBJECT_SIZE_TYPE is the second argument from __builtin_dynamic_object_size. If unknown, return false, setting PSIZE to NULL_TREE. */ @@ -337,7 +449,8 @@ addr_dyn_object_size (struct object_size_info *osi, const_tree ptr, int object_size_type, tree *psize, tree *wholesizep = NULL) { - tree pt_var, pt_var_size = NULL_TREE, bytes; + struct function *fun = osi ? osi->fun : cfun; + tree pt_var, pt_var_size = NULL_TREE, var_size, bytes; gcc_assert (TREE_CODE (ptr) == ADDR_EXPR); @@ -353,22 +466,57 @@ addr_dyn_object_size (struct object_size_info *osi, const_tree ptr, if (!whole_var_size (osi, pt_var, object_size_type, &pt_var_size)) return false; - if (!pt_var_size) - return false; + var_size = pt_var_size; /* PTR points to a subobject of whole variable PT_VAR. */ if (pt_var != TREE_OPERAND (ptr, 0)) { - bytes = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var); + tree var; + + if (object_size_type & 1) + var = get_closest_subobject (ptr, pt_var); + else + var = pt_var; + + if (var != pt_var) + { + var_size = TYPE_SIZE_UNIT (TREE_TYPE (var)); + if (!TREE_CONSTANT (var_size)) + var_size = get_or_create_ssa_default_def (fun, var_size); + if (!var_size) + return false; + } + else if (!pt_var_size) + return false; + + bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var); if (bytes != error_mark_node) - bytes = size_for_offset (pt_var_size, bytes, pt_var_size); + { + tree cap_size = pt_var_size; + + /* For subobject sizes where the whole object is a structure, + restrict size to the size of the struct less the offset of the + subobject in the struct. */ + if (var != pt_var && pt_var_size && TREE_CODE (pt_var) == MEM_REF) + { + tree off = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var); + if (off == error_mark_node) + off = size_zero_node; + else + off = fold_build2 (MIN_EXPR, sizetype, pt_var_size, off); + cap_size = fold_build2 (MINUS_EXPR, sizetype, pt_var_size, off); + } + bytes = size_for_offset (var_size, bytes, cap_size); + } } + else if (!pt_var_size) + return false; else bytes = pt_var_size; *psize = bytes == error_mark_node ? NULL_TREE : bytes; if (wholesizep) - *wholesizep = pt_var_size; + *wholesizep = var_size; return true; } -- 2.31.1