This patch proposes a fix to PR 100944 by improving the array boundary computation when the flexible array has no clear constructor: if no constructor were found in the input code, we compute the size of the array as:
offset(array begin) - offset(next element in RECORD_TYPE) If no next element is found in the RECORD, simply fall back to: offset(array begin) - offset(RECORD_TYPE ends) We avoid doing this calculation if the RECORD_TYPE is actually an UNION_TYPE, once things get very complicated in this case. gcc/ChangeLog: 2021-13-08 Giuliano Belinassi <giuliano.belina...@usp.br> PR middle-end/100944 * tree-dfa.c (get_ref_base_and_unit_offset): Add option to compute size of next field. * (get_ref_base_and_unit_offset_1): Same as above. * tree-dfa.h (get_ref_base_and_unit_offset): Same as above. (get_ref_base_and_unit_offset_1): Same as above. * tree.c (least_common_record_1): New. (least_common_record): New. (component_ref_size): Improve array size calculation. gcc/testsuite/ChangeLog: 2021-13-08 Giuliano Belinassi <giuliano.belina...@usp.br> PR middle-end/100944 * gcc.dg/Wzero-length-array-bounds.c: Update diagnostic. * gcc.dg/Warray-bounds-71.c: New test. --- gcc/testsuite/gcc.dg/Warray-bounds-71.c | 42 +++++++ .../gcc.dg/Wzero-length-array-bounds.c | 18 +-- gcc/tree-dfa.c | 31 ++++- gcc/tree-dfa.h | 6 +- gcc/tree.c | 115 ++++++++++++++---- 5 files changed, 172 insertions(+), 40 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Warray-bounds-71.c diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-71.c b/gcc/testsuite/gcc.dg/Warray-bounds-71.c new file mode 100644 index 00000000000..cc5b083bc77 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Warray-bounds-71.c @@ -0,0 +1,42 @@ +/* PR middle-end/100944 - missing -Warray-bounds accessing a flexible array + member of a nested struct + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +struct A0 +{ + int i, a[0]; +}; + +struct B0 +{ + struct A0 a; + long x; +} b0; + +void f0 (int i) +{ + long t = b0.x; + b0.a.a[i] = 0; // { dg-warning "\\\[-Warray-bounds" } + if (t != b0.x) // folded to false + __builtin_abort (); +} + +struct Ax +{ + int i, a[]; +}; + +struct Bx +{ + struct Ax a; + long x; +} bx; + +void fx (int i) +{ + long t = bx.x; + bx.a.a[i] = 0; // { dg-warning "\\\[-Warray-bounds" } + if (t != bx.x) // folded to false + __builtin_abort (); +} diff --git a/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c b/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c index 8e880d92dea..117b30ff294 100644 --- a/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c +++ b/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c @@ -68,21 +68,21 @@ extern struct Y y; void access_to_member (int i) { - y.a[0].a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } - y.a[0].a[1] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } - y.a[0].a[2] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } + y.a[0].a[0] = 0; // { dg-warning "\\\[-Warray-bounds" } + y.a[0].a[1] = 0; // { dg-warning "\\\[-Warray-bounds" } + y.a[0].a[2] = 0; // { dg-warning "\\\[-Warray-bounds" } sink (a); - y.a[1].a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } - y.a[1].a[1] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } + y.a[1].a[0] = 0; // { dg-warning "\\\[-Warray-bounds" } + y.a[1].a[1] = 0; // { dg-warning "\\\[-Warray-bounds" } /* Similar to the array case above, accesses to a subsequent member of the "parent" struct seem like a more severe problem than those to the next member of the same struct. */ - y.a[1].a[2] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } + y.a[1].a[2] = 0; // { dg-warning "\\\[-Warray-bounds" } sink (a); - y.b.a[0] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } - y.b.a[1] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } - y.b.a[2] = 0; // { dg-warning "\\\[-Wzero-length-bounds" } + y.b.a[0] = 0; // { dg-warning "\\\[-Warray-bounds" } + y.b.a[1] = 0; // { dg-warning "\\\[-Warray-bounds" } + y.b.a[2] = 0; // { dg-warning "\\\[-Warray-bounds" } y.b.a[3] = 0; // { dg-warning "\\\[-Warray-bounds" } } diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c index 1d20de0c400..dc3c15f11d5 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -772,9 +772,11 @@ get_ref_base_and_extent_hwi (tree exp, HOST_WIDE_INT *poffset, tree get_addr_base_and_unit_offset_1 (tree exp, poly_int64_pod *poffset, - tree (*valueize) (tree)) + tree (*valueize) (tree), + bool of_next_component /* = false. */) { poly_int64 byte_offset = 0; + tree next_field = NULL_TREE; /* Compute cumulative byte-offset for nested component-refs and array-refs, and find the ultimate containing object. */ @@ -797,11 +799,27 @@ get_addr_base_and_unit_offset_1 (tree exp, poly_int64_pod *poffset, case COMPONENT_REF: { tree field = TREE_OPERAND (exp, 1); - tree this_offset = component_ref_field_offset (exp); + tree this_offset; + poly_int64 hthis_offset; + if (of_next_component && !next_field) + { + /* We are looking for the next component of the record. */ + next_field = TREE_CHAIN (field); + if (!next_field) + break; + + /* We found a next component. Flag that we found it and + update the target field. */ + field = next_field; + } + + this_offset = component_ref_field_offset (exp); + if (!this_offset || !poly_int_tree_p (this_offset, &hthis_offset) + || TREE_CODE (field) != FIELD_DECL || (TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field)) % BITS_PER_UNIT)) return NULL_TREE; @@ -904,6 +922,9 @@ get_addr_base_and_unit_offset_1 (tree exp, poly_int64_pod *poffset, done: *poffset = byte_offset; + + if (of_next_component) + return next_field; return exp; } @@ -913,9 +934,11 @@ done: is not BITS_PER_UNIT-aligned. */ tree -get_addr_base_and_unit_offset (tree exp, poly_int64_pod *poffset) +get_addr_base_and_unit_offset (tree exp, poly_int64_pod *poffset, bool + of_next_component /* = false. */) { - return get_addr_base_and_unit_offset_1 (exp, poffset, NULL); + return get_addr_base_and_unit_offset_1 (exp, poffset, NULL, + of_next_component); } /* Returns true if STMT references an SSA_NAME that has diff --git a/gcc/tree-dfa.h b/gcc/tree-dfa.h index b1457ab065c..94e44d9c3f6 100644 --- a/gcc/tree-dfa.h +++ b/gcc/tree-dfa.h @@ -34,8 +34,10 @@ extern tree get_ref_base_and_extent (tree, poly_int64_pod *, poly_int64_pod *, extern tree get_ref_base_and_extent_hwi (tree, HOST_WIDE_INT *, HOST_WIDE_INT *, bool *); extern tree get_addr_base_and_unit_offset_1 (tree, poly_int64_pod *, - tree (*) (tree)); -extern tree get_addr_base_and_unit_offset (tree, poly_int64_pod *); + tree (*) (tree), + bool of_next_component = false); +extern tree get_addr_base_and_unit_offset (tree, poly_int64_pod *, + bool = false); extern bool stmt_references_abnormal_ssa_name (gimple *); extern void replace_abnormal_ssa_names (gimple *); extern void dump_enumerated_decls (FILE *, dump_flags_t); diff --git a/gcc/tree.c b/gcc/tree.c index 1aa6e557a04..45d7fa2ae92 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -12649,6 +12649,47 @@ get_initializer_for (tree init, tree decl) return NULL_TREE; } +static int +least_common_record_1 (tree basetype, tree field1, tree field2, + tree *least_basetype) +{ + int ret = 0; + + for (tree fld = TYPE_FIELDS (basetype); fld; fld = TREE_CHAIN (fld)) + { + if (fld == field1 || fld == field2) + ret++; + + if (TREE_CODE (TREE_TYPE (fld)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (fld)) == RECORD_TYPE) + ret += least_common_record_1 (TREE_TYPE (fld), field1, field2, + least_basetype); + } + + if (ret == 2) + { + *least_basetype = basetype; + ret++; /* Avoid getting in this block again if a common basetype were + found. */ + } + + return ret; +} + +/* Find the least common RECORD type common to two FIELDS from base. */ +static tree +least_common_record (tree basetype, tree field1, tree field2) +{ + if (!(TREE_CODE (basetype) == RECORD_TYPE + || TREE_CODE (basetype) == UNION_TYPE)) + return NULL_TREE; + + tree least_basetype = NULL_TREE; + least_common_record_1 (basetype, field1, field2, &least_basetype); + + return least_basetype; +} + /* Determines the size of the member referenced by the COMPONENT_REF REF, using its initializer expression if necessary in order to determine the size of an initialized flexible array member. @@ -12768,28 +12809,30 @@ component_ref_size (tree ref, special_array_member *sam /* = NULL */) memsize = NULL_TREE; if (typematch) - /* MEMBER is a true flexible array member. Compute its size from - the initializer of the BASE object if it has one. */ - if (tree init = DECL_P (base) ? DECL_INITIAL (base) : NULL_TREE) - if (init != error_mark_node) - { - init = get_initializer_for (init, member); - if (init) - { - memsize = TYPE_SIZE_UNIT (TREE_TYPE (init)); - if (tree refsize = TYPE_SIZE_UNIT (argtype)) - { - /* Use the larger of the initializer size and the tail - padding in the enclosing struct. */ - poly_int64 rsz = tree_to_poly_int64 (refsize); - rsz -= baseoff; - if (known_lt (tree_to_poly_int64 (memsize), rsz)) - memsize = wide_int_to_tree (TREE_TYPE (memsize), rsz); - } - - baseoff = 0; - } - } + { + /* MEMBER is a true flexible array member. Compute its size from + the initializer of the BASE object if it has one. */ + if (tree init = DECL_P (base) ? DECL_INITIAL (base) : NULL_TREE) + if (init != error_mark_node) + { + init = get_initializer_for (init, member); + if (init) + { + memsize = TYPE_SIZE_UNIT (TREE_TYPE (init)); + if (tree refsize = TYPE_SIZE_UNIT (argtype)) + { + /* Use the larger of the initializer size and the tail + padding in the enclosing struct. */ + poly_int64 rsz = tree_to_poly_int64 (refsize); + rsz -= baseoff; + if (known_lt (tree_to_poly_int64 (memsize), rsz)) + memsize = wide_int_to_tree (TREE_TYPE (memsize), rsz); + } + + baseoff = 0; + } + } + } if (!memsize) { @@ -12810,9 +12853,31 @@ component_ref_size (tree ref, special_array_member *sam /* = NULL */) memsize = TYPE_SIZE_UNIT (bt); } else if (DECL_P (base)) - /* Use the size of the BASE object (possibly an array of some - other type such as char used to store the struct). */ - memsize = DECL_SIZE_UNIT (base); + { + /* It is possible that the flexible array has no constructor at all. + In such cases, GCC will allocate the array in some weird way. + Assume that the array size is the difference between the start + address of the array and the next component, if it exists. */ + + tree lcr; + + poly_int64 baseoff2 = 0; + tree next_field = get_addr_base_and_unit_offset (ref, &baseoff2, + /* next_elmnt = */ true); + if (next_field + && (lcr = least_common_record (basetype, member, next_field)) + && TREE_CODE (lcr) == RECORD_TYPE + && known_ge (baseoff2, baseoff)) + { + poly_int64 size = baseoff2 - baseoff; + memsize = wide_int_to_tree (TREE_TYPE (DECL_SIZE_UNIT (base)), + size); + } + else + /* Use the size of the BASE object (possibly an array of some + other type such as char used to store the struct). */ + memsize = DECL_SIZE_UNIT (base); + } else return NULL_TREE; } -- 2.32.0