diff options
author | Martin Sebor <msebor@redhat.com> | 2021-10-26 14:40:33 -0600 |
---|---|---|
committer | Martin Sebor <msebor@redhat.com> | 2021-10-26 16:53:54 -0600 |
commit | 1ff4dbddcf74203a1e16316b18e12f9e1b5085f0 (patch) | |
tree | 5f6066e4ea74c6ac5a9a3e6e52ae5abb0a6cc95b /gcc/pointer-query.cc | |
parent | 9a27acc30a34b7854db32eac562306cebac6fa1e (diff) | |
download | gcc-1ff4dbddcf74203a1e16316b18e12f9e1b5085f0.zip gcc-1ff4dbddcf74203a1e16316b18e12f9e1b5085f0.tar.gz gcc-1ff4dbddcf74203a1e16316b18e12f9e1b5085f0.tar.bz2 |
Improve/correct detection of overlapping aggregates [PR102238, PR102919].
Resolves:
PR tree-optimization/102238 - alias_offset in gimple-ssa-sprintf.c is broken
PR tree-optimization/102919 - spurious -Wrestrict warning for sprintf into the same member array as argument plus offset
gcc/ChangeLog:
PR tree-optimization/102238
PR tree-optimization/102919
* gimple-ssa-sprintf.c (get_string_length): Add an argument.
(array_elt_at_offset): Move to pointer-query.
(set_aggregate_size_and_offset): New function.
(field_at_offset): Move to pointer-query.
(get_origin_and_offset): Rename...
(get_origin_and_offset_r): this. Add an argument. Make aggregate
handling more robust.
(get_origin_and_offset): New.
(alias_offset): Add an argument.
(format_string): Use subobject size determined by get_origin_and_offset.
* pointer-query.cc (field_at_offset): Move from gimple-ssa-sprintf.c.
Improve/correct handling of aggregates.
(array_elt_at_offset): Same.
* pointer-query.h (field_at_offset): Declare.
(array_elt_at_offset): Declare.
gcc/testsuite/ChangeLog:
PR tree-optimization/102238
PR tree-optimization/102919
* gcc.dg/tree-ssa/builtin-sprintf-warn-23.c: Remove warnings.
* gcc.dg/Wrestrict-23.c: New test.
Diffstat (limited to 'gcc/pointer-query.cc')
-rw-r--r-- | gcc/pointer-query.cc | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc index 3b9f970..a0e4543 100644 --- a/gcc/pointer-query.cc +++ b/gcc/pointer-query.cc @@ -2194,3 +2194,167 @@ compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */, return size; } + +/* Determine the offset *FLDOFF of the first byte of a struct member + of TYPE (possibly recursively) into which the byte offset OFF points, + starting after the field START_AFTER if it's non-null. On success, + if nonnull, set *FLDOFF to the offset of the first byte, and return + the field decl. If nonnull, set *NEXTOFF to the offset of the next + field (which reflects any padding between the returned field and + the next). Otherwise, if no such member can be found, return null. */ + +tree +field_at_offset (tree type, tree start_after, HOST_WIDE_INT off, + HOST_WIDE_INT *fldoff /* = nullptr */, + HOST_WIDE_INT *nextoff /* = nullptr */) +{ + tree first_fld = TYPE_FIELDS (type); + + HOST_WIDE_INT offbuf = 0, nextbuf = 0; + if (!fldoff) + fldoff = &offbuf; + if (!nextoff) + nextoff = &nextbuf; + + *nextoff = 0; + + /* The field to return. */ + tree last_fld = NULL_TREE; + /* The next field to advance to. */ + tree next_fld = NULL_TREE; + + /* NEXT_FLD's cached offset. */ + HOST_WIDE_INT next_pos = -1; + + for (tree fld = first_fld; fld; fld = next_fld) + { + next_fld = fld; + do + /* Advance to the next relevant data member. */ + next_fld = TREE_CHAIN (next_fld); + while (next_fld + && (TREE_CODE (next_fld) != FIELD_DECL + || DECL_ARTIFICIAL (next_fld))); + + if (TREE_CODE (fld) != FIELD_DECL || DECL_ARTIFICIAL (fld)) + continue; + + if (fld == start_after) + continue; + + tree fldtype = TREE_TYPE (fld); + /* The offset of FLD within its immediately enclosing structure. */ + HOST_WIDE_INT fldpos = next_pos < 0 ? int_byte_position (fld) : next_pos; + + /* If the size is not available the field is a flexible array + member. Treat this case as success. */ + tree typesize = TYPE_SIZE_UNIT (fldtype); + HOST_WIDE_INT fldsize = (tree_fits_uhwi_p (typesize) + ? tree_to_uhwi (typesize) + : off); + + /* If OFF is beyond the end of the current field continue. */ + HOST_WIDE_INT fldend = fldpos + fldsize; + if (fldend < off) + continue; + + if (next_fld) + { + /* If OFF is equal to the offset of the next field continue + to it and skip the array/struct business below. */ + next_pos = int_byte_position (next_fld); + *nextoff = *fldoff + next_pos; + if (*nextoff == off && TREE_CODE (type) != UNION_TYPE) + continue; + } + else + *nextoff = HOST_WIDE_INT_MAX; + + /* OFF refers somewhere into the current field or just past its end, + which could mean it refers to the next field. */ + if (TREE_CODE (fldtype) == ARRAY_TYPE) + { + /* Will be set to the offset of the first byte of the array + element (which may be an array) of FLDTYPE into which + OFF - FLDPOS points (which may be past ELTOFF). */ + HOST_WIDE_INT eltoff = 0; + if (tree ft = array_elt_at_offset (fldtype, off - fldpos, &eltoff)) + fldtype = ft; + else + continue; + + /* Advance the position to include the array element above. + If OFF - FLPOS refers to a member of FLDTYPE, the member + will be determined below. */ + fldpos += eltoff; + } + + *fldoff += fldpos; + + if (TREE_CODE (fldtype) == RECORD_TYPE) + /* Drill down into the current field if it's a struct. */ + fld = field_at_offset (fldtype, start_after, off - fldpos, + fldoff, nextoff); + + last_fld = fld; + + /* Unless the offset is just past the end of the field return it. + Otherwise save it and return it only if the offset of the next + next field is greater (i.e., there is padding between the two) + or if there is no next field. */ + if (off < fldend) + break; + } + + if (*nextoff == HOST_WIDE_INT_MAX && next_fld) + *nextoff = next_pos; + + return last_fld; +} + +/* Determine the offset *ELTOFF of the first byte of the array element + of array ARTYPE into which the byte offset OFF points. On success + set *ELTOFF to the offset of the first byte and return type. + Otherwise, if no such element can be found, return null. */ + +tree +array_elt_at_offset (tree artype, HOST_WIDE_INT off, + HOST_WIDE_INT *eltoff /* = nullptr */, + HOST_WIDE_INT *subar_size /* = nullptr */) +{ + gcc_assert (TREE_CODE (artype) == ARRAY_TYPE); + + HOST_WIDE_INT dummy; + if (!eltoff) + eltoff = &dummy; + if (!subar_size) + subar_size = &dummy; + + tree eltype = artype; + while (TREE_CODE (TREE_TYPE (eltype)) == ARRAY_TYPE) + eltype = TREE_TYPE (eltype); + + tree subartype = eltype; + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (eltype)) + || TYPE_MODE (TREE_TYPE (eltype)) != TYPE_MODE (char_type_node)) + eltype = TREE_TYPE (eltype); + + *subar_size = int_size_in_bytes (subartype); + + if (eltype == artype) + { + *eltoff = 0; + return artype; + } + + HOST_WIDE_INT artype_size = int_size_in_bytes (artype); + HOST_WIDE_INT eltype_size = int_size_in_bytes (eltype); + + if (off < artype_size)// * eltype_size) + { + *eltoff = (off / eltype_size) * eltype_size; + return TREE_CODE (eltype) == ARRAY_TYPE ? TREE_TYPE (eltype) : eltype; + } + + return NULL_TREE; +} |