diff options
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r-- | gcc/builtins.c | 955 |
1 files changed, 740 insertions, 215 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c index 40e77c7..83b0c1c 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -185,6 +185,8 @@ static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function); static void maybe_emit_free_warning (tree); static tree fold_builtin_object_size (tree, tree); static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1); +static bool compute_objsize (tree, int, access_ref *, ssa_name_limit_t &, + range_query *); unsigned HOST_WIDE_INT target_newline; unsigned HOST_WIDE_INT target_percent; @@ -199,7 +201,8 @@ static void expand_builtin_sync_synchronize (void); access_ref::access_ref (tree bound /* = NULL_TREE */, bool minaccess /* = false */) -: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true) +: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true), + parmarray () { /* Set to valid. */ offrng[0] = offrng[1] = 0; @@ -222,6 +225,175 @@ access_ref::access_ref (tree bound /* = NULL_TREE */, } } +/* Return the PHI node REF refers to or null if it doesn't. */ + +gphi * +access_ref::phi () const +{ + if (!ref || TREE_CODE (ref) != SSA_NAME) + return NULL; + + gimple *def_stmt = SSA_NAME_DEF_STMT (ref); + if (gimple_code (def_stmt) != GIMPLE_PHI) + return NULL; + + return as_a <gphi *> (def_stmt); +} + +/* Determine and return the largest object to which *THIS. If *THIS + refers to a PHI and PREF is nonnull, fill *PREF with the details + of the object determined by compute_objsize(ARG, OSTYPE) for each + PHI argument ARG. */ + +tree +access_ref::get_ref (vec<access_ref> *all_refs, + access_ref *pref /* = NULL */, + int ostype /* = 1 */, + ssa_name_limit_t *psnlim /* = NULL */, + range_query *rvals /* = NULL */) const +{ + gphi *phi_stmt = this->phi (); + if (!phi_stmt) + return ref; + + /* FIXME: Calling get_ref() with a null PSNLIM is dangerous and might + cause unbounded recursion. */ + ssa_name_limit_t snlim_buf; + if (!psnlim) + psnlim = &snlim_buf; + + if (!psnlim->visit_phi (ref)) + return NULL_TREE; + + /* Reflects the range of offsets of all PHI arguments refer to the same + object (i.e., have the same REF). */ + access_ref same_ref; + /* The conservative result of the PHI reflecting the offset and size + of the largest PHI argument, regardless of whether or not they all + refer to the same object. */ + access_ref phi_ref; + if (pref) + { + phi_ref = *pref; + same_ref = *pref; + } + + /* Set if any argument is a function array (or VLA) parameter not + declared [static]. */ + bool parmarray = false; + /* The size of the smallest object referenced by the PHI arguments. */ + offset_int minsize = 0; + const offset_int maxobjsize = wi::to_offset (max_object_size ()); + /* The offset of the PHI, not reflecting those of its arguments. */ + const offset_int orng[2] = { phi_ref.offrng[0], phi_ref.offrng[1] }; + + const unsigned nargs = gimple_phi_num_args (phi_stmt); + for (unsigned i = 0; i < nargs; ++i) + { + access_ref phi_arg_ref; + tree arg = gimple_phi_arg_def (phi_stmt, i); + if (!compute_objsize (arg, ostype, &phi_arg_ref, *psnlim, rvals) + || phi_arg_ref.sizrng[0] < 0) + /* A PHI with all null pointer arguments. */ + return NULL_TREE; + + /* Add PREF's offset to that of the argument. */ + phi_arg_ref.add_offset (orng[0], orng[1]); + + if (all_refs) + all_refs->safe_push (phi_arg_ref); + + const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0 + || phi_arg_ref.sizrng[1] != maxobjsize); + + parmarray |= phi_arg_ref.parmarray; + + const bool nullp = integer_zerop (arg) && (i || i + 1 < nargs); + + if (phi_ref.sizrng[0] < 0) + { + if (!nullp) + same_ref = phi_arg_ref; + phi_ref = phi_arg_ref; + if (arg_known_size) + minsize = phi_arg_ref.sizrng[0]; + continue; + } + + const bool phi_known_size = (phi_ref.sizrng[0] != 0 + || phi_ref.sizrng[1] != maxobjsize); + + if (phi_known_size && phi_arg_ref.sizrng[0] < minsize) + minsize = phi_arg_ref.sizrng[0]; + + /* Disregard null pointers in PHIs with two or more arguments. + TODO: Handle this better! */ + if (nullp) + continue; + + /* Determine the amount of remaining space in the argument. */ + offset_int argrem[2]; + argrem[1] = phi_arg_ref.size_remaining (argrem); + + /* Determine the amount of remaining space computed so far and + if the remaining space in the argument is more use it instead. */ + offset_int phirem[2]; + phirem[1] = phi_ref.size_remaining (phirem); + + if (phi_arg_ref.ref != same_ref.ref) + same_ref.ref = NULL_TREE; + + if (phirem[1] < argrem[1] + || (phirem[1] == argrem[1] + && phi_ref.sizrng[1] < phi_arg_ref.sizrng[1])) + /* Use the argument with the most space remaining as the result, + or the larger one if the space is equal. */ + phi_ref = phi_arg_ref; + + /* Set SAME_REF.OFFRNG to the maximum range of all arguments. */ + if (phi_arg_ref.offrng[0] < same_ref.offrng[0]) + same_ref.offrng[0] = phi_arg_ref.offrng[0]; + if (same_ref.offrng[1] < phi_arg_ref.offrng[1]) + same_ref.offrng[1] = phi_arg_ref.offrng[1]; + } + + if (phi_ref.sizrng[0] < 0) + { + /* Fail if none of the PHI's arguments resulted in updating PHI_REF + (perhaps because they have all been already visited by prior + recursive calls). */ + psnlim->leave_phi (ref); + return NULL_TREE; + } + + if (!same_ref.ref && same_ref.offrng[0] != 0) + /* Clear BASE0 if not all the arguments refer to the same object and + if not all their offsets are zero-based. This allows the final + PHI offset to out of bounds for some arguments but not for others + (or negative even of all the arguments are BASE0), which is overly + permissive. */ + phi_ref.base0 = false; + + if (same_ref.ref) + phi_ref = same_ref; + else + { + /* Replace the lower bound of the largest argument with the size + of the smallest argument, and set PARMARRAY if any argument + was one. */ + phi_ref.sizrng[0] = minsize; + phi_ref.parmarray = parmarray; + } + + /* Avoid changing *THIS. */ + if (pref && pref != this) + *pref = phi_ref; + + psnlim->leave_phi (ref); + + return phi_ref.ref; +} + /* Return the maximum amount of space remaining and if non-null, set argument to the minimum. */ @@ -318,7 +490,6 @@ void access_ref::add_offset (const offset_int &min, const offset_int &max) return; } - offrng[1] = maxoff; offset_int absmax = wi::abs (max); if (offrng[0] < absmax) { @@ -353,6 +524,75 @@ void access_ref::add_offset (const offset_int &min, const offset_int &max) } } +/* Set a bit for the PHI in VISITED and return true if it wasn't + already set. */ + +bool +ssa_name_limit_t::visit_phi (tree ssa_name) +{ + if (!visited) + visited = BITMAP_ALLOC (NULL); + + /* Return false if SSA_NAME has already been visited. */ + return bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name)); +} + +/* Clear a bit for the PHI in VISITED. */ + +void +ssa_name_limit_t::leave_phi (tree ssa_name) +{ + /* Return false if SSA_NAME has already been visited. */ + bitmap_clear_bit (visited, SSA_NAME_VERSION (ssa_name)); +} + +/* Return false if the SSA_NAME chain length counter has reached + the limit, otherwise increment the counter and return true. */ + +bool +ssa_name_limit_t::next () +{ + /* Return a negative value to let caller avoid recursing beyond + the specified limit. */ + if (ssa_def_max == 0) + return false; + + --ssa_def_max; + + return true; +} + +/* If the SSA_NAME has already been "seen" return a positive value. + Otherwise add it to VISITED. If the SSA_NAME limit has been + reached, return a negative value. Otherwise return zero. */ + +int +ssa_name_limit_t::next_phi (tree ssa_name) +{ + { + gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name); + /* Return a positive value if the PHI has already been visited. */ + if (gimple_code (def_stmt) == GIMPLE_PHI + && !visit_phi (ssa_name)) + return 1; + } + + /* Return a negative value to let caller avoid recursing beyond + the specified limit. */ + if (ssa_def_max == 0) + return -1; + + --ssa_def_max; + + return 0; +} + +ssa_name_limit_t::~ssa_name_limit_t () +{ + if (visited) + BITMAP_FREE (visited); +} + /* Return true if NAME starts with __builtin_ or __sync_. */ static bool @@ -3561,28 +3801,42 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func, if (opt == OPT_Wstringop_overread) { + bool maybe = pad && pad->src.phi (); + if (tree_int_cst_lt (maxobjsize, bndrng[0])) { if (bndrng[0] == bndrng[1]) warned = (func ? warning_at (loc, opt, - "%K%qD specified bound %E " - "exceeds maximum object size %E", + (maybe + ? G_("%K%qD specified bound %E may " + "exceed maximum object size %E") + : G_("%K%qD specified bound %E " + "exceeds maximum object size %E")), exp, func, bndrng[0], maxobjsize) : warning_at (loc, opt, - "%Kspecified bound %E " - "exceeds maximum object size %E", + (maybe + ? G_("%Kspecified bound %E may " + "exceed maximum object size %E") + : G_("%Kspecified bound %E " + "exceeds maximum object size %E")), exp, bndrng[0], maxobjsize)); else warned = (func ? warning_at (loc, opt, - "%K%qD specified bound [%E, %E] " - "exceeds maximum object size %E", + (maybe + ? G_("%K%qD specified bound [%E, %E] may " + "exceed maximum object size %E") + : G_("%K%qD specified bound [%E, %E] " + "exceeds maximum object size %E")), exp, func, bndrng[0], bndrng[1], maxobjsize) : warning_at (loc, opt, - "%Kspecified bound [%E, %E] " - "exceeds maximum object size %E", + (maybe + ? G_("%Kspecified bound [%E, %E] may " + "exceed maximum object size %E") + : G_("%Kspecified bound [%E, %E] " + "exceeds maximum object size %E")), exp, bndrng[0], bndrng[1], maxobjsize)); } else if (!size || tree_int_cst_le (bndrng[0], size)) @@ -3590,22 +3844,34 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func, else if (tree_int_cst_equal (bndrng[0], bndrng[1])) warned = (func ? warning_at (loc, opt, - "%K%qD specified bound %E exceeds " - "source size %E", + (maybe + ? G_("%K%qD specified bound %E may exceed " + "source size %E") + : G_("%K%qD specified bound %E exceeds " + "source size %E")), exp, func, bndrng[0], size) : warning_at (loc, opt, - "%Kspecified bound %E exceeds " - "source size %E", + (maybe + ? G_("%Kspecified bound %E may exceed " + "source size %E") + : G_("%Kspecified bound %E exceeds " + "source size %E")), exp, bndrng[0], size)); else warned = (func ? warning_at (loc, opt, - "%K%qD specified bound [%E, %E] exceeds " - "source size %E", + (maybe + ? G_("%K%qD specified bound [%E, %E] may " + "exceed source size %E") + : G_("%K%qD specified bound [%E, %E] exceeds " + "source size %E")), exp, func, bndrng[0], bndrng[1], size) : warning_at (loc, opt, - "%Kspecified bound [%E, %E] exceeds " - "source size %E", + (maybe + ? G_("%Kspecified bound [%E, %E] may exceed " + "source size %E") + : G_("%Kspecified bound [%E, %E] exceeds " + "source size %E")), exp, bndrng[0], bndrng[1], size)); if (warned) { @@ -3624,28 +3890,41 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func, return warned; } + bool maybe = pad && pad->dst.phi (); if (tree_int_cst_lt (maxobjsize, bndrng[0])) { if (bndrng[0] == bndrng[1]) warned = (func ? warning_at (loc, opt, - "%K%qD specified size %E " - "exceeds maximum object size %E", + (maybe + ? G_("%K%qD specified size %E may " + "exceed maximum object size %E") + : G_("%K%qD specified size %E " + "exceeds maximum object size %E")), exp, func, bndrng[0], maxobjsize) : warning_at (loc, opt, - "%Kspecified size %E " - "exceeds maximum object size %E", + (maybe + ? G_("%Kspecified size %E may exceed " + "maximum object size %E") + : G_("%Kspecified size %E exceeds " + "maximum object size %E")), exp, bndrng[0], maxobjsize)); else warned = (func ? warning_at (loc, opt, - "%K%qD specified size between %E and %E " - "exceeds maximum object size %E", + (maybe + ? G_("%K%qD specified size between %E and %E " + "may exceed maximum object size %E") + : G_("%K%qD specified size between %E and %E " + "exceeds maximum object size %E")), exp, func, bndrng[0], bndrng[1], maxobjsize) : warning_at (loc, opt, - "%Kspecified size between %E and %E " - "exceeds maximum object size %E", + (maybe + ? G_("%Kspecified size between %E and %E " + "may exceed maximum object size %E") + : G_("%Kspecified size between %E and %E " + "exceeds maximum object size %E")), exp, bndrng[0], bndrng[1], maxobjsize)); } else if (!size || tree_int_cst_le (bndrng[0], size)) @@ -3653,22 +3932,34 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func, else if (tree_int_cst_equal (bndrng[0], bndrng[1])) warned = (func ? warning_at (loc, OPT_Wstringop_overflow_, - "%K%qD specified bound %E exceeds " - "destination size %E", + (maybe + ? G_("%K%qD specified bound %E may exceed " + "destination size %E") + : G_("%K%qD specified bound %E exceeds " + "destination size %E")), exp, func, bndrng[0], size) : warning_at (loc, OPT_Wstringop_overflow_, - "%Kspecified bound %E exceeds " - "destination size %E", + (maybe + ? G_("%Kspecified bound %E may exceed " + "destination size %E") + : G_("%Kspecified bound %E exceeds " + "destination size %E")), exp, bndrng[0], size)); else warned = (func ? warning_at (loc, OPT_Wstringop_overflow_, - "%K%qD specified bound [%E, %E] exceeds " - "destination size %E", + (maybe + ? G_("%K%qD specified bound [%E, %E] may exceed " + "destination size %E") + : G_("%K%qD specified bound [%E, %E] exceeds " + "destination size %E")), exp, func, bndrng[0], bndrng[1], size) : warning_at (loc, OPT_Wstringop_overflow_, - "%Kspecified bound [%E, %E] exceeds " - "destination size %E", + (maybe + ? G_("%Kspecified bound [%E, %E] exceeds " + "destination size %E") + : G_("%Kspecified bound [%E, %E] exceeds " + "destination size %E")), exp, bndrng[0], bndrng[1], size)); if (warned) @@ -3697,7 +3988,7 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func, static bool warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], - tree size, bool write, bool read) + tree size, bool write, bool read, bool maybe) { bool warned = false; @@ -3706,40 +3997,64 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], if (tree_int_cst_equal (range[0], range[1])) warned = (func ? warning_n (loc, opt, tree_to_uhwi (range[0]), - "%K%qD accessing %E byte in a region " - "of size %E", - "%K%qD accessing %E bytes in a region " - "of size %E", + (maybe + ? G_("%K%qD may access %E byte in a region " + "of size %E") + : G_("%K%qD accessing %E byte in a region " + "of size %E")), + (maybe + ? G_ ("%K%qD may access %E bytes in a region " + "of size %E") + : G_ ("%K%qD accessing %E bytes in a region " + "of size %E")), exp, func, range[0], size) : warning_n (loc, opt, tree_to_uhwi (range[0]), - "%Kaccessing %E byte in a region " - "of size %E", - "%Kaccessing %E bytes in a region " - "of size %E", + (maybe + ? G_("%Kmay access %E byte in a region " + "of size %E") + : G_("%Kaccessing %E byte in a region " + "of size %E")), + (maybe + ? G_("%Kmay access %E bytes in a region " + "of size %E") + : G_("%Kaccessing %E bytes in a region " + "of size %E")), exp, range[0], size)); else if (tree_int_cst_sign_bit (range[1])) { /* Avoid printing the upper bound if it's invalid. */ warned = (func ? warning_at (loc, opt, - "%K%qD accessing %E or more bytes in " - "a region of size %E", + (maybe + ? G_("%K%qD may access %E or more bytes " + "in a region of size %E") + : G_("%K%qD accessing %E or more bytes " + "in a region of size %E")), exp, func, range[0], size) : warning_at (loc, opt, - "%Kaccessing %E or more bytes in " - "a region of size %E", + (maybe + ? G_("%Kmay access %E or more bytes " + "in a region of size %E") + : G_("%Kaccessing %E or more bytes " + "in a region of size %E")), exp, range[0], size)); } else warned = (func ? warning_at (loc, opt, - "%K%qD accessing between %E and %E bytes " - "in a region of size %E", + (maybe + ? G_("%K%qD may access between %E and %E " + "bytes in a region of size %E") + : G_("%K%qD accessing between %E and %E " + "bytes in a region of size %E")), exp, func, range[0], range[1], size) : warning_at (loc, opt, - "%Kaccessing between %E and %E bytes " - "in a region of size %E", + (maybe + ? G_("%Kmay access between %E and %E bytes " + "in a region of size %E") + : G_("%Kaccessing between %E and %E bytes " + "in a region of size %E")), exp, range[0], range[1], size)); return warned; @@ -3750,44 +4065,69 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], if (tree_int_cst_equal (range[0], range[1])) warned = (func ? warning_n (loc, opt, tree_to_uhwi (range[0]), - "%K%qD writing %E byte into a region " - "of size %E overflows the destination", - "%K%qD writing %E bytes into a region " - "of size %E overflows the destination", + (maybe + ? G_("%K%qD may write %E byte into a region " + "of size %E") + : G_("%K%qD writing %E byte into a region " + "of size %E overflows the destination")), + (maybe + ? G_("%K%qD may write %E bytes into a region " + "of size %E") + : G_("%K%qD writing %E bytes into a region " + "of size %E overflows the destination")), exp, func, range[0], size) : warning_n (loc, opt, tree_to_uhwi (range[0]), - "%Kwriting %E byte into a region " - "of size %E overflows the destination", - "%Kwriting %E bytes into a region " - "of size %E overflows the destination", + (maybe + ? G_("%Kmay write %E byte into a region " + "of size %E") + : G_("%Kwriting %E byte into a region " + "of size %E overflows the destination")), + (maybe + ? G_("%Kmay write %E bytes into a region " + "of size %E") + : G_("%Kwriting %E bytes into a region " + "of size %E overflows the destination")), exp, range[0], size)); else if (tree_int_cst_sign_bit (range[1])) { /* Avoid printing the upper bound if it's invalid. */ warned = (func ? warning_at (loc, opt, - "%K%qD writing %E or more bytes into " - "a region of size %E overflows " - "the destination", + (maybe + ? G_("%K%qD may write %E or more bytes " + "into a region of size %E " + "the destination") + : G_("%K%qD writing %E or more bytes " + "into a region of size %E overflows " + "the destination")), exp, func, range[0], size) : warning_at (loc, opt, - "%Kwriting %E or more bytes into " - "a region of size %E overflows " - "the destination", + (maybe + ? G_("%Kmay write %E or more bytes into " + "a region of size %E") + : G_("%Kwriting %E or more bytes into " + "a region of size %E overflows " + "the destination")), exp, range[0], size)); } else warned = (func ? warning_at (loc, opt, - "%K%qD writing between %E and %E bytes " - "into a region of size %E overflows " - "the destination", + (maybe + ? G_("%K%qD may write between %E and %E bytes " + "into a region of size %E") + : G_("%K%qD writing between %E and %E bytes " + "into a region of size %E overflows " + "the destination")), exp, func, range[0], range[1], size) : warning_at (loc, opt, - "%Kwriting between %E and %E bytes " - "into a region of size %E overflows " - "the destination", + (maybe + ? G_("%Kmay write between %E and %E bytes " + "into a region of size %E") + : G_("%Kwriting between %E and %E bytes " + "into a region of size %E overflows " + "the destination")), exp, range[0], range[1], size)); return warned; @@ -3799,35 +4139,64 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], warned = (func ? warning_n (loc, OPT_Wstringop_overread, tree_to_uhwi (range[0]), - "%K%qD reading %E byte from a region of size %E", - "%K%qD reading %E bytes from a region of size %E", exp, func, range[0], size) + (maybe + ? G_("%K%qD may reade %E byte from a region " + "of size %E") + : G_("%K%qD reading %E byte from a region " + "of size %E")), + (maybe + ? G_("%K%qD may read %E bytes from a region " + "of size %E") + : G_("%K%qD reading %E bytes from a region " + "of size %E")), + exp, func, range[0], size) : warning_n (loc, OPT_Wstringop_overread, tree_to_uhwi (range[0]), - "%Kreading %E byte from a region of size %E", - "%Kreading %E bytes from a region of size %E", + (maybe + ? G_("%Kmay read %E byte from a region " + "of size %E") + : G_("%Kreading %E byte from a region " + "of size %E")), + (maybe + ? G_("%Kmay read %E bytes from a region " + "of size %E") + : G_("%Kreading %E bytes from a region " + "of size %E")), exp, range[0], size)); else if (tree_int_cst_sign_bit (range[1])) { /* Avoid printing the upper bound if it's invalid. */ warned = (func ? warning_at (loc, OPT_Wstringop_overread, - "%K%qD reading %E or more bytes from " - "a region of size %E", + (maybe + ? G_("%K%qD may read %E or more bytes " + "from a region of size %E") + : G_("%K%qD reading %E or more bytes " + "from a region of size %E")), exp, func, range[0], size) : warning_at (loc, OPT_Wstringop_overread, - "%Kreading %E or more bytes from a region " - "of size %E", + (maybe + ? G_("%Kmay read %E or more bytes " + "from a region of size %E") + : G_("%Kreading %E or more bytes " + "from a region of size %E")), exp, range[0], size)); } else warned = (func ? warning_at (loc, OPT_Wstringop_overread, - "%K%qD reading between %E and %E bytes from " - "a region of size %E", + (maybe + ? G_("%K%qD may read between %E and %E bytes " + "from a region of size %E") + : G_("%K%qD reading between %E and %E bytes " + "from a region of size %E")), exp, func, range[0], range[1], size) : warning_at (loc, opt, - "%K reading between %E and %E bytes from " - "a region of size %E", + (maybe + ? G_("%Kmay read between %E and %E bytes " + "from a region of size %E") + : G_("%Kreading between %E and %E bytes " + "from a region of size %E")), exp, range[0], range[1], size)); if (warned) @@ -3879,28 +4248,61 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], return warned; } -/* Issue an inform message describing the target of an access REF. +/* Issue one inform message describing each target of an access REF. WRITE is set for a write access and clear for a read access. */ -static void -inform_access (const access_ref &ref, access_mode mode) +void +access_ref::inform_access (access_mode mode) const { - if (!ref.ref) + const access_ref &aref = *this; + if (!aref.ref) return; + if (aref.phi ()) + { + /* Set MAXREF to refer to the largest object and fill ALL_REFS + with data for all objects referenced by the PHI arguments. */ + access_ref maxref; + auto_vec<access_ref> all_refs; + if (!get_ref (&all_refs, &maxref)) + return; + + /* Except for MAXREF, the rest of the arguments' offsets need not + reflect one added to the PHI itself. Determine the latter from + MAXREF on which the result is based. */ + const offset_int orng[] = + { + offrng[0] - maxref.offrng[0], + wi::smax (offrng[1] - maxref.offrng[1], offrng[0]), + }; + + /* Add the final PHI's offset to that of each of the arguments + and recurse to issue an inform message for it. */ + for (unsigned i = 0; i != all_refs.length (); ++i) + { + /* Skip any PHIs; those could lead to infinite recursion. */ + if (all_refs[i].phi ()) + continue; + + all_refs[i].add_offset (orng[0], orng[1]); + all_refs[i].inform_access (mode); + } + return; + } + /* Convert offset range and avoid including a zero range since it isn't necessarily meaningful. */ HOST_WIDE_INT diff_min = tree_to_shwi (TYPE_MIN_VALUE (ptrdiff_type_node)); HOST_WIDE_INT diff_max = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node)); HOST_WIDE_INT minoff; HOST_WIDE_INT maxoff = diff_max; - if (wi::fits_shwi_p (ref.offrng[0])) - minoff = ref.offrng[0].to_shwi (); + if (wi::fits_shwi_p (aref.offrng[0])) + minoff = aref.offrng[0].to_shwi (); else - minoff = ref.offrng[0] < 0 ? diff_min : diff_max; + minoff = aref.offrng[0] < 0 ? diff_min : diff_max; - if (wi::fits_shwi_p (ref.offrng[1])) - maxoff = ref.offrng[1].to_shwi (); + if (wi::fits_shwi_p (aref.offrng[1])) + maxoff = aref.offrng[1].to_shwi (); if (maxoff <= diff_min || maxoff >= diff_max) /* Avoid mentioning an upper bound that's equal to or in excess @@ -3910,110 +4312,127 @@ inform_access (const access_ref &ref, access_mode mode) /* Convert size range and always include it since all sizes are meaningful. */ unsigned long long minsize = 0, maxsize = 0; - if (wi::fits_shwi_p (ref.sizrng[0]) - && wi::fits_shwi_p (ref.sizrng[1])) + if (wi::fits_shwi_p (aref.sizrng[0]) + && wi::fits_shwi_p (aref.sizrng[1])) { - minsize = ref.sizrng[0].to_shwi (); - maxsize = ref.sizrng[1].to_shwi (); + minsize = aref.sizrng[0].to_shwi (); + maxsize = aref.sizrng[1].to_shwi (); } + /* SIZRNG doesn't necessarily have the same range as the allocation + size determined by gimple_call_alloc_size (). */ char sizestr[80]; - location_t loc; - tree allocfn = NULL_TREE; - if (TREE_CODE (ref.ref) == SSA_NAME) - { - gimple *stmt = SSA_NAME_DEF_STMT (ref.ref); - gcc_assert (is_gimple_call (stmt)); - loc = gimple_location (stmt); - allocfn = gimple_call_fndecl (stmt); - if (!allocfn) - /* Handle calls through pointers to functions. */ - allocfn = gimple_call_fn (stmt); - - /* SIZRNG doesn't necessarily have the same range as the allocation - size determined by gimple_call_alloc_size (). */ + if (minsize == maxsize) + sprintf (sizestr, "%llu", minsize); + else + sprintf (sizestr, "[%llu, %llu]", minsize, maxsize); + + char offstr[80]; + if (minoff == 0 + && (maxoff == 0 || aref.sizrng[1] <= maxoff)) + offstr[0] = '\0'; + else if (minoff == maxoff) + sprintf (offstr, "%lli", (long long) minoff); + else + sprintf (offstr, "[%lli, %lli]", (long long) minoff, (long long) maxoff); - if (minsize == maxsize) - sprintf (sizestr, "%llu", minsize); - else - sprintf (sizestr, "[%llu, %llu]", minsize, maxsize); + location_t loc = UNKNOWN_LOCATION; + tree ref = this->ref; + tree allocfn = NULL_TREE; + if (TREE_CODE (ref) == SSA_NAME) + { + gimple *stmt = SSA_NAME_DEF_STMT (ref); + if (is_gimple_call (stmt)) + { + loc = gimple_location (stmt); + if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) + { + /* Strip the SSA_NAME suffix from the variable name and + recreate an identifier with the VLA's original name. */ + ref = gimple_call_lhs (stmt); + ref = SSA_NAME_IDENTIFIER (ref); + const char *id = IDENTIFIER_POINTER (ref); + size_t len = strcspn (id, ".$"); + if (!len) + len = strlen (id); + ref = get_identifier_with_length (id, len); + } + else + { + /* Except for VLAs, retrieve the allocation function. */ + allocfn = gimple_call_fndecl (stmt); + if (!allocfn) + allocfn = gimple_call_fn (stmt); + if (TREE_CODE (allocfn) == SSA_NAME) + { + /* For an ALLOC_CALL via a function pointer make a small + effort to determine the destination of the pointer. */ + gimple *def = SSA_NAME_DEF_STMT (allocfn); + if (gimple_assign_single_p (def)) + { + tree rhs = gimple_assign_rhs1 (def); + if (DECL_P (rhs)) + allocfn = rhs; + else if (TREE_CODE (rhs) == COMPONENT_REF) + allocfn = TREE_OPERAND (rhs, 1); + } + } + } + } + else if (gimple_nop_p (stmt)) + /* Handle DECL_PARM below. */ + ref = SSA_NAME_VAR (ref); } - else if (DECL_P (ref.ref)) - loc = DECL_SOURCE_LOCATION (ref.ref); - else if (EXPR_P (ref.ref) && EXPR_HAS_LOCATION (ref.ref)) - loc = EXPR_LOCATION (ref.ref); - else + + if (DECL_P (ref)) + loc = DECL_SOURCE_LOCATION (ref); + else if (EXPR_P (ref) && EXPR_HAS_LOCATION (ref)) + loc = EXPR_LOCATION (ref); + else if (TREE_CODE (ref) != IDENTIFIER_NODE + && TREE_CODE (ref) != SSA_NAME) return; if (mode == access_read_write || mode == access_write_only) { if (allocfn == NULL_TREE) { - if (minoff == maxoff) - { - if (minoff == 0) - inform (loc, "destination object %qE", ref.ref); - else - inform (loc, "at offset %wi into destination object %qE", - minoff, ref.ref); - } + if (*offstr) + inform (loc, "at offset %s into destination object %qE of size %s", + offstr, ref, sizestr); else - inform (loc, "at offset [%wi, %wi] into destination object %qE", - minoff, maxoff, ref.ref); + inform (loc, "destination object %qE of size %s", ref, sizestr); return; } - if (minoff == maxoff) - { - if (minoff == 0) - inform (loc, "destination object of size %s allocated by %qE", - sizestr, allocfn); - else - inform (loc, - "at offset %wi into destination object of size %s " - "allocated by %qE", minoff, sizestr, allocfn); - } - else + if (*offstr) inform (loc, - "at offset [%wi, %wi] into destination object of size %s " - "allocated by %qE", - minoff, maxoff, sizestr, allocfn); - + "at offset %s into destination object of size %s " + "allocated by %qE", offstr, sizestr, allocfn); + else + inform (loc, "destination object of size %s allocated by %qE", + sizestr, allocfn); return; } - if (DECL_P (ref.ref)) + if (DECL_P (ref)) { - if (minoff == maxoff) - { - if (minoff == 0) - inform (loc, "source object %qD", ref.ref); - else - inform (loc, "at offset %wi into source object %qD", - minoff, ref.ref); - } + if (*offstr) + inform (loc, "at offset %s into source object %qD of size %s", + offstr, ref, sizestr); else - inform (loc, "at offset [%wi, %wi] into source object %qD", - minoff, maxoff, ref.ref); + inform (loc, "source object %qD of size %s", ref, sizestr); + return; } - if (minoff == maxoff) - { - if (minoff == 0) - inform (loc, "source object of size %s allocated by %qE", - sizestr, allocfn); - else - inform (loc, - "at offset %wi into source object of size %s " - "allocated by %qE", minoff, sizestr, allocfn); - } - else + if (*offstr) inform (loc, - "at offset [%wi, %wi] into source object of size %s " - "allocated by %qE", - minoff, maxoff, sizestr, allocfn); + "at offset %s into source object of size %s allocated by %qE", + offstr, sizestr, allocfn); + else + inform (loc, "source object of size %s allocated by %qE", + sizestr, allocfn); } /* Helper to set RANGE to the range of BOUND if it's nonnull, bounded @@ -4233,17 +4652,18 @@ check_access (tree exp, tree dstwrite, = mode == access_read_only || mode == access_read_write; const bool write = mode == access_write_only || mode == access_read_write; + const bool maybe = pad && pad->dst.parmarray; warned = warn_for_access (loc, func, exp, OPT_Wstringop_overflow_, range, dstsize, - write, read && !builtin); + write, read && !builtin, maybe); } if (warned) { TREE_NO_WARNING (exp) = true; if (pad) - inform_access (pad->dst, pad->mode); + pad->dst.inform_access (pad->mode); } /* Return error when an overflow has been detected. */ @@ -4326,12 +4746,13 @@ check_access (tree exp, tree dstwrite, const bool read = mode == access_read_only || mode == access_read_write; + const bool maybe = pad && pad->dst.parmarray; if (warn_for_access (loc, func, exp, OPT_Wstringop_overread, range, - slen, false, read)) + slen, false, read, maybe)) { TREE_NO_WARNING (exp) = true; if (pad) - inform_access (pad->src, access_read_only); + pad->src.inform_access (access_read_only); } return false; } @@ -4463,11 +4884,12 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, /* For an access to an object referenced to by the function parameter PTR of pointer type, and set RNG[] to the range of sizes of the object obtainedfrom the attribute access specification for the current function. + Set STATIC_ARRAY if the array parameter has been declared [static]. Return the function parameter on success and null otherwise. */ tree gimple_parm_array_size (tree ptr, wide_int rng[2], - range_query * /* = NULL */) + bool *static_array /* = NULL */) { /* For a function argument try to determine the byte size of the array from the current function declaratation (e.g., attribute access or @@ -4499,6 +4921,9 @@ gimple_parm_array_size (tree ptr, wide_int rng[2], if (warn_array_parameter < 2 && !access->static_p) return NULL_TREE; + if (static_array) + *static_array = access->static_p; + rng[0] = wi::zero (prec); rng[1] = wi::uhwi (access->minsize, prec); /* Multiply the array bound encoded in the attribute by the size @@ -4646,6 +5071,84 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], return NULL_TREE; } +/* A helper of compute_objsize() to determine the size from an assignment + statement STMT with the RHS of either MIN_EXPR or MAX_EXPR. */ + +static bool +handle_min_max_size (gimple *stmt, int ostype, access_ref *pref, + ssa_name_limit_t &snlim, range_query *rvals) +{ + tree_code code = gimple_assign_rhs_code (stmt); + + tree ptr = gimple_assign_rhs1 (stmt); + + /* In a valid MAX_/MIN_EXPR both operands must refer to the same array. + Determine the size/offset of each and use the one with more or less + space remaining, respectively. If either fails, use the information + determined from the other instead, adjusted up or down as appropriate + for the expression. */ + access_ref aref[2] = { *pref, *pref }; + if (!compute_objsize (ptr, ostype, &aref[0], snlim, rvals)) + { + aref[0].base0 = false; + aref[0].offrng[0] = aref[0].offrng[1] = 0; + aref[0].add_max_offset (); + aref[0].set_max_size_range (); + } + + ptr = gimple_assign_rhs2 (stmt); + if (!compute_objsize (ptr, ostype, &aref[1], snlim, rvals)) + { + aref[1].base0 = false; + aref[1].offrng[0] = aref[1].offrng[1] = 0; + aref[1].add_max_offset (); + aref[1].set_max_size_range (); + } + + if (!aref[0].ref && !aref[1].ref) + /* Fail if the identity of neither argument could be determined. */ + return false; + + bool i0 = false; + if (aref[0].ref && aref[0].base0) + { + if (aref[1].ref && aref[1].base0) + { + /* If the object referenced by both arguments has been determined + set *PREF to the one with more or less space remainng, whichever + is appopriate for CODE. + TODO: Indicate when the objects are distinct so it can be + diagnosed. */ + i0 = code == MAX_EXPR; + const bool i1 = !i0; + + if (aref[i0].size_remaining () < aref[i1].size_remaining ()) + *pref = aref[i1]; + else + *pref = aref[i0]; + return true; + } + + /* If only the object referenced by one of the arguments could be + determined, use it and... */ + *pref = aref[0]; + i0 = true; + } + else + *pref = aref[1]; + + const bool i1 = !i0; + /* ...see if the offset obtained from the other pointer can be used + to tighten up the bound on the offset obtained from the first. */ + if ((code == MAX_EXPR && aref[i1].offrng[1] < aref[i0].offrng[0]) + || (code == MIN_EXPR && aref[i0].offrng[0] < aref[i1].offrng[1])) + { + pref->offrng[0] = aref[i0].offrng[0]; + pref->offrng[1] = aref[i0].offrng[1]; + } + return true; +} + /* Helper to compute the size of the object referenced by the PTR expression which must have pointer type, using Object Size type OSTYPE (only the least significant 2 bits are used). @@ -4653,7 +5156,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], if it's unique, otherwise to null, PREF->OFFRNG to the range of offsets into it, and PREF->SIZRNG to the range of sizes of the object(s). - VISITED is used to avoid visiting the same PHI operand multiple + SNLIM is used to avoid visiting the same PHI operand multiple times, and, when nonnull, RVALS to determine range information. Returns true on success, false when a meaningful size (or range) cannot be determined. @@ -4662,8 +5165,8 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], to influence code generation or optimization. */ static bool -compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, - range_query *rvals) +compute_objsize (tree ptr, int ostype, access_ref *pref, + ssa_name_limit_t &snlim, range_query *rvals) { STRIP_NOPS (ptr); @@ -4699,7 +5202,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, if (code == BIT_FIELD_REF) { tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize (ref, ostype, pref, visited, rvals)) + if (!compute_objsize (ref, ostype, pref, snlim, rvals)) return false; offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2))); @@ -4710,6 +5213,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, if (code == COMPONENT_REF) { tree ref = TREE_OPERAND (ptr, 0); + if (TREE_CODE (TREE_TYPE (ref)) == UNION_TYPE) + /* In accesses through union types consider the entire unions + rather than just their members. */ + ostype = 0; tree field = TREE_OPERAND (ptr, 1); if (ostype == 0) @@ -4717,7 +5224,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, /* In OSTYPE zero (for raw memory functions like memcpy), use the maximum size instead if the identity of the enclosing object cannot be determined. */ - if (!compute_objsize (ref, ostype, pref, visited, rvals)) + if (!compute_objsize (ref, ostype, pref, snlim, rvals)) return false; /* Otherwise, use the size of the enclosing object and add @@ -4727,9 +5234,17 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, pref->add_offset (wi::to_offset (offset)); else pref->add_max_offset (); + + if (!pref->ref) + /* REF may have been already set to an SSA_NAME earlier + to provide better context for diagnostics. In that case, + leave it unchanged. */ + pref->ref = ref; return true; } + pref->ref = field; + if (!addr && POINTER_TYPE_P (TREE_TYPE (field))) { /* Set maximum size if the reference is to the pointer member @@ -4738,8 +5253,6 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, return true; } - pref->ref = field; - /* SAM is set for array members that might need special treatment. */ special_array_member sam; tree size = component_ref_size (ptr, &sam); @@ -4768,7 +5281,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, { tree ref = TREE_OPERAND (ptr, 0); tree reftype = TREE_TYPE (ref); - if (code == ARRAY_REF + if (!addr && code == ARRAY_REF && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE) /* Avoid arrays of pointers. FIXME: Hande pointers to arrays of known bound. */ @@ -4786,7 +5299,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, return false; } - if (!compute_objsize (ref, ostype, pref, visited, rvals)) + if (!compute_objsize (ref, ostype, pref, snlim, rvals)) return false; offset_int orng[2]; @@ -4852,7 +5365,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, if (code == TARGET_MEM_REF) { tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize (ref, ostype, pref, visited, rvals)) + if (!compute_objsize (ref, ostype, pref, snlim, rvals)) return false; /* TODO: Handle remaining operands. Until then, add maximum offset. */ @@ -4879,13 +5392,14 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, if (code == STRING_CST) { pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr); + pref->ref = ptr; return true; } if (code == POINTER_PLUS_EXPR) { tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize (ref, ostype, pref, visited, rvals)) + if (!compute_objsize (ref, ostype, pref, snlim, rvals)) return false; offset_int orng[2]; @@ -4900,11 +5414,16 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, if (code == VIEW_CONVERT_EXPR) { ptr = TREE_OPERAND (ptr, 0); - return compute_objsize (ptr, ostype, pref, visited, rvals); + return compute_objsize (ptr, ostype, pref, snlim, rvals); } - if (TREE_CODE (ptr) == SSA_NAME) + if (code == SSA_NAME) { + if (!snlim.next ()) + return false; + + /* Only process an SSA_NAME if the recursion limit has not yet + been reached. */ gimple *stmt = SSA_NAME_DEF_STMT (ptr); if (is_gimple_call (stmt)) { @@ -4933,7 +5452,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, offset_int offrng[2]; if (tree ret = gimple_call_return_array (stmt, offrng, rvals)) { - if (!compute_objsize (ret, ostype, pref, visited, rvals)) + if (!compute_objsize (ret, ostype, pref, snlim, rvals)) return false; /* Cap OFFRNG[1] to at most the remaining size of @@ -4964,8 +5483,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, of the array from the current function declaratation (e.g., attribute access or related). */ wide_int wr[2]; - if (tree ref = gimple_parm_array_size (ptr, wr, rvals)) + bool static_array = false; + if (tree ref = gimple_parm_array_size (ptr, wr, &static_array)) { + pref->parmarray = !static_array; pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED); pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED); pref->ref = ref; @@ -4975,14 +5496,19 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, pref->set_max_size_range (); pref->base0 = false; pref->ref = ptr; - if (tree var = SSA_NAME_VAR (ptr)) - if (TREE_CODE (var) == PARM_DECL) - pref->ref = var; - return true; } - /* TODO: Handle PHI. */ + if (gimple_code (stmt) == GIMPLE_PHI) + { + pref->ref = ptr; + access_ref phi_ref = *pref; + if (!pref->get_ref (NULL, &phi_ref, ostype, &snlim, rvals)) + return false; + *pref = phi_ref; + pref->ref = ptr; + return true; + } if (!is_gimple_assign (stmt)) { @@ -4992,21 +5518,22 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, PREF->REF to it. */ pref->base0 = false; pref->set_max_size_range (); - if (tree var = SSA_NAME_VAR (ptr)) - if (TREE_CODE (var) == PARM_DECL) - pref->ref = var; + pref->ref = ptr; return true; } - ptr = gimple_assign_rhs1 (stmt); - tree_code code = gimple_assign_rhs_code (stmt); + if (code == MAX_EXPR || code == MIN_EXPR) + return handle_min_max_size (stmt, ostype, pref, snlim, rvals); + + tree rhs = gimple_assign_rhs1 (stmt); + if (code == POINTER_PLUS_EXPR - && TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE) + && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE) { /* Compute the size of the object first. */ - if (!compute_objsize (ptr, ostype, pref, visited, rvals)) + if (!compute_objsize (rhs, ostype, pref, snlim, rvals)) return false; offset_int orng[2]; @@ -5018,17 +5545,22 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited, return true; } - if (code == ADDR_EXPR) - return compute_objsize (ptr, ostype, pref, visited, rvals); + if (code == ADDR_EXPR + || code == SSA_NAME) + return compute_objsize (rhs, ostype, pref, snlim, rvals); - /* This could be an assignment from a nonlocal pointer. Save PTR - to mention in diagnostics but otherwise treat it as a pointer + /* (This could also be an assignment from a nonlocal pointer.) Save + PTR to mention in diagnostics but otherwise treat it as a pointer to an unknown object. */ - pref->ref = ptr; + pref->ref = rhs; + pref->base0 = false; + pref->set_max_size_range (); + return true; } /* Assume all other expressions point into an unknown object of the maximum valid size. */ + pref->ref = ptr; pref->base0 = false; pref->set_max_size_range (); return true; @@ -5041,15 +5573,8 @@ tree compute_objsize (tree ptr, int ostype, access_ref *pref, range_query *rvals /* = NULL */) { - bitmap visited = NULL; - - bool success - = compute_objsize (ptr, ostype, pref, &visited, rvals); - - if (visited) - BITMAP_FREE (visited); - - if (!success) + ssa_name_limit_t snlim; + if (!compute_objsize (ptr, ostype, pref, snlim, rvals)) return NULL_TREE; offset_int maxsize = pref->size_remaining (); |