diff options
author | Martin Sebor <msebor@redhat.com> | 2018-02-20 20:22:01 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2018-02-20 13:22:01 -0700 |
commit | 5e27f0d5d54d83e13d8b845b965b96fd10706f46 (patch) | |
tree | 6f9d63e7d0aab885e612d0f3546e51175425c258 /gcc/gimple-ssa-warn-restrict.c | |
parent | 75b81dcdad7b41fb6cc9d0fa6064966c2016ce96 (diff) |
PR middle-end/84095 - false-positive -Wrestrict warnings for memcpy within array
gcc/ChangeLog:
PR middle-end/84095
* gimple-ssa-warn-restrict.c (builtin_memref::extend_offset_range): New.
(builtin_memref::set_base_and_offset): Same. Handle inner references.
(builtin_memref::builtin_memref): Factor out parts into
set_base_and_offset and call it.
gcc/testsuite/ChangeLog:
PR middle-end/84095
* c-c++-common/Warray-bounds-3.c: Adjust text of expected warnings.
* c-c++-common/Wrestrict.c: Same.
* gcc.dg/Wrestrict-6.c: Same.
* gcc.dg/Warray-bounds-27.c: New test.
* gcc.dg/Wrestrict-8.c: New test.
* gcc.dg/Wrestrict-9.c: New test.
* gcc.dg/pr84095.c: New test.
From-SVN: r257860
Diffstat (limited to 'gcc/gimple-ssa-warn-restrict.c')
-rw-r--r-- | gcc/gimple-ssa-warn-restrict.c | 307 |
1 files changed, 183 insertions, 124 deletions
diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c index d0e18541275..9d5c6e04370 100644 --- a/gcc/gimple-ssa-warn-restrict.c +++ b/gcc/gimple-ssa-warn-restrict.c @@ -158,6 +158,14 @@ struct builtin_memref builtin_memref (tree, tree); tree offset_out_of_bounds (int, offset_int[2]) const; + +private: + + /* Ctor helper to set or extend OFFRANGE based on argument. */ + void extend_offset_range (tree); + + /* Ctor helper to determine BASE and OFFRANGE from argument. */ + void set_base_and_offset (tree); }; /* Description of a memory access by a raw memory or string built-in @@ -235,11 +243,120 @@ builtin_memref::builtin_memref (tree expr, tree size) const offset_int maxobjsize = tree_to_shwi (max_object_size ()); + /* Find the BASE object or pointer referenced by EXPR and set + the offset range OFFRANGE in the process. */ + set_base_and_offset (expr); + + if (size) + { + tree range[2]; + /* Determine the size range, allowing for the result to be [0, 0] + for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX. */ + get_size_range (size, range, true); + sizrange[0] = wi::to_offset (range[0]); + sizrange[1] = wi::to_offset (range[1]); + /* get_size_range returns SIZE_MAX for the maximum size. + Constrain it to the real maximum of PTRDIFF_MAX. */ + if (sizrange[1] > maxobjsize) + sizrange[1] = maxobjsize; + } + else + sizrange[1] = maxobjsize; + + tree basetype = TREE_TYPE (base); + if (DECL_P (base) && TREE_CODE (basetype) == ARRAY_TYPE) + { + /* If the offset could be in range of the referenced object + constrain its bounds so neither exceeds those of the object. */ + if (offrange[0] < 0 && offrange[1] > 0) + offrange[0] = 0; + + offset_int maxoff = maxobjsize; + if (ref && array_at_struct_end_p (ref)) + ; /* Use the maximum possible offset for last member arrays. */ + else if (tree basesize = TYPE_SIZE_UNIT (basetype)) + maxoff = wi::to_offset (basesize); + + if (offrange[0] >= 0) + { + if (offrange[1] < 0) + offrange[1] = offrange[0] <= maxoff ? maxoff : maxobjsize; + else if (offrange[0] <= maxoff && offrange[1] > maxoff) + offrange[1] = maxoff; + } + } +} + +/* Ctor helper to set or extend OFFRANGE based on the OFFSET argument. */ + +void +builtin_memref::extend_offset_range (tree offset) +{ + const offset_int maxobjsize = tree_to_shwi (max_object_size ()); + + if (TREE_CODE (offset) == INTEGER_CST) + { + offset_int off = int_cst_value (offset); + if (off != 0) + { + offrange[0] += off; + offrange[1] += off; + } + return; + } + + if (TREE_CODE (offset) == SSA_NAME) + { + wide_int min, max; + value_range_type rng = get_range_info (offset, &min, &max); + if (rng == VR_RANGE) + { + offrange[0] += offset_int::from (min, SIGNED); + offrange[1] += offset_int::from (max, SIGNED); + } + else if (rng == VR_ANTI_RANGE) + { + offrange[0] += offset_int::from (max + 1, SIGNED); + offrange[1] += offset_int::from (min - 1, SIGNED); + } + else + { + gimple *stmt = SSA_NAME_DEF_STMT (offset); + tree type; + if (is_gimple_assign (stmt) + && gimple_assign_rhs_code (stmt) == NOP_EXPR + && (type = TREE_TYPE (gimple_assign_rhs1 (stmt))) + && INTEGRAL_TYPE_P (type)) + { + /* Use the bounds of the type of the NOP_EXPR operand + even if it's signed. The result doesn't trigger + warnings but makes their output more readable. */ + offrange[0] += wi::to_offset (TYPE_MIN_VALUE (type)); + offrange[1] += wi::to_offset (TYPE_MAX_VALUE (type)); + } + else + offrange[1] += maxobjsize; + } + return; + } + + offrange[1] += maxobjsize; +} + +/* Determines the base object or pointer of the reference EXPR + and the offset range from the beginning of the base. */ + +void +builtin_memref::set_base_and_offset (tree expr) +{ + const offset_int maxobjsize = tree_to_shwi (max_object_size ()); + if (TREE_CODE (expr) == SSA_NAME) { /* Try to tease the offset out of the pointer. */ gimple *stmt = SSA_NAME_DEF_STMT (expr); - if (gimple_assign_single_p (stmt) + if (!base + && gimple_assign_single_p (stmt) && gimple_assign_rhs_code (stmt) == ADDR_EXPR) expr = gimple_assign_rhs1 (stmt); else if (is_gimple_assign (stmt)) @@ -250,154 +367,88 @@ builtin_memref::builtin_memref (tree expr, tree size) tree rhs = gimple_assign_rhs1 (stmt); if (POINTER_TYPE_P (TREE_TYPE (rhs))) expr = gimple_assign_rhs1 (stmt); + else + { + base = expr; + return; + } } else if (code == POINTER_PLUS_EXPR) { expr = gimple_assign_rhs1 (stmt); tree offset = gimple_assign_rhs2 (stmt); - if (TREE_CODE (offset) == INTEGER_CST) - { - offset_int off = int_cst_value (offset); - offrange[0] = off; - offrange[1] = off; - - if (TREE_CODE (expr) == SSA_NAME) - { - gimple *stmt = SSA_NAME_DEF_STMT (expr); - if (gimple_assign_single_p (stmt) - && gimple_assign_rhs_code (stmt) == ADDR_EXPR) - expr = gimple_assign_rhs1 (stmt); - } - } - else if (TREE_CODE (offset) == SSA_NAME) - { - wide_int min, max; - value_range_type rng = get_range_info (offset, &min, &max); - if (rng == VR_RANGE) - { - offrange[0] = offset_int::from (min, SIGNED); - offrange[1] = offset_int::from (max, SIGNED); - } - else if (rng == VR_ANTI_RANGE) - { - offrange[0] = offset_int::from (max + 1, SIGNED); - offrange[1] = offset_int::from (min - 1, SIGNED); - } - else - { - gimple *stmt = SSA_NAME_DEF_STMT (offset); - tree type; - if (is_gimple_assign (stmt) - && gimple_assign_rhs_code (stmt) == NOP_EXPR - && (type = TREE_TYPE (gimple_assign_rhs1 (stmt))) - && INTEGRAL_TYPE_P (type)) - { - /* Use the bounds of the type of the NOP_EXPR operand - even if it's signed. The result doesn't trigger - warnings but makes their output more readable. */ - offrange[0] = wi::to_offset (TYPE_MIN_VALUE (type)); - offrange[1] = wi::to_offset (TYPE_MAX_VALUE (type)); - } - else - offrange[1] = maxobjsize; - } - } - else - offrange[1] = maxobjsize; + extend_offset_range (offset); } + else + { + base = expr; + return; + } + } + else + { + base = expr; + return; } } if (TREE_CODE (expr) == ADDR_EXPR) - { - poly_int64 off; - tree op = TREE_OPERAND (expr, 0); + expr = TREE_OPERAND (expr, 0); - /* Determine the base object or pointer of the reference - and its constant offset from the beginning of the base. */ - base = get_addr_base_and_unit_offset (op, &off); + poly_int64 bitsize, bitpos; + tree var_off; + machine_mode mode; + int sign, reverse, vol; - HOST_WIDE_INT const_off; - if (base && off.is_constant (&const_off)) - { - offrange[0] += const_off; - offrange[1] += const_off; + /* Determine the base object or pointer of the reference and + the constant bit offset from the beginning of the base. + If the offset has a non-constant component, it will be in + VAR_OFF. MODE, SIGN, REVERSE, and VOL are write only and + unused here. */ + base = get_inner_reference (expr, &bitsize, &bitpos, &var_off, + &mode, &sign, &reverse, &vol); - /* Stash the reference for offset validation. */ - ref = op; + poly_int64 bytepos = exact_div (bitpos, BITS_PER_UNIT); - /* Also stash the constant offset for offset validation. */ - if (TREE_CODE (op) == COMPONENT_REF) - refoff = const_off; - } - else - { - size = NULL_TREE; - base = get_base_address (TREE_OPERAND (expr, 0)); - } + HOST_WIDE_INT const_off; + if (!base || !bytepos.is_constant (&const_off)) + { + base = get_base_address (TREE_OPERAND (expr, 0)); + return; } - if (!base) - base = build2 (MEM_REF, char_type_node, expr, null_pointer_node); + offrange[0] += const_off; + offrange[1] += const_off; - if (TREE_CODE (base) == MEM_REF) + if (var_off) { - offset_int off; - if (mem_ref_offset (base).is_constant (&off)) + if (TREE_CODE (var_off) == INTEGER_CST) { - refoff += off; - offrange[0] += off; - offrange[1] += off; + offset_int cstoff = wi::to_offset (var_off); + offrange[0] += cstoff; + offrange[1] += cstoff; } else - size = NULL_TREE; - base = TREE_OPERAND (base, 0); + offrange[1] += maxobjsize; } - if (TREE_CODE (base) == SSA_NAME) - if (gimple *stmt = SSA_NAME_DEF_STMT (base)) - { - enum gimple_code code = gimple_code (stmt); - if (code == GIMPLE_ASSIGN) - if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) - { - base = gimple_assign_rhs1 (stmt); + /* Stash the reference for offset validation. */ + ref = expr; - tree offset = gimple_assign_rhs2 (stmt); - if (TREE_CODE (offset) == INTEGER_CST) - { - offset_int off = int_cst_value (offset); - refoff += off; - offrange[0] += off; - offrange[1] += off; - } - } - } + /* Also stash the constant offset for offset validation. */ + if (TREE_CODE (expr) == COMPONENT_REF) + refoff = const_off; - if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE) + if (TREE_CODE (base) == MEM_REF) { - /* For array objects, where a negative offset wouldn't make - sense, use zero instead if the upper bound is positive. */ - if (offrange[0] < 0 && offrange[1] > 0) - offrange[0] = 0; + tree memrefoff = TREE_OPERAND (base, 1); + extend_offset_range (memrefoff); + base = TREE_OPERAND (base, 0); } - if (size) - { - tree range[2]; - /* Determine the size range, allowing for the result to be [0, 0] - for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX. */ - get_size_range (size, range, true); - sizrange[0] = wi::to_offset (range[0]); - sizrange[1] = wi::to_offset (range[1]); - /* get_size_range returns SIZE_MAX for the maximum size. - Constrain it to the real maximum of PTRDIFF_MAX. */ - if (sizrange[1] > maxobjsize) - sizrange[1] = maxobjsize; - } - else - sizrange[1] = maxobjsize; + if (TREE_CODE (base) == SSA_NAME) + set_base_and_offset (base); } /* Return error_mark_node if the signed offset exceeds the bounds @@ -864,10 +915,18 @@ builtin_access::generic_overlap () the other. */ bool depends_p = detect_overlap != &builtin_access::generic_overlap; - if (!overlap_certain - && !dstref->strbounded_p - && !depends_p) - return false; + if (!overlap_certain) + { + if (!dstref->strbounded_p && !depends_p) + return false; + + /* There's no way to distinguish an access to the same member + of a structure from one to two distinct members of the same + structure. Give up to avoid excessive false positives. */ + tree basetype = TREE_TYPE (TREE_TYPE (dstref->base)); + if (RECORD_OR_UNION_TYPE_P (basetype)) + return false; + } /* True for stpcpy and strcpy. */ bool stxcpy_p = (!dstref->strbounded_p |