From patchwork Mon Dec 6 17:32:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 48544 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E0E323858436 for ; Mon, 6 Dec 2021 17:35:57 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E0E323858436 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1638812157; bh=n7pUa5qynPpQphe5V9LeJQjN1Yfj5wahmzxogwxKdWE=; h=Subject:To:References:Date:In-Reply-To:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=U0TQi/wgl+mXnPBQOUaE4DGVNEgmWzcdo5lDWARNLKcEggGNb0jt7A9wPKmxHeWCh 9IuepyPR8EN5D6e10zMyz3LbxP/dZstWdFxH5KSFpmxDROx+PkC/GeiWEULmsN0xbv Skb2U56vqztGovXgoZ3tIta9m+c+11tr9JADFjUU= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-oi1-x22a.google.com (mail-oi1-x22a.google.com [IPv6:2607:f8b0:4864:20::22a]) by sourceware.org (Postfix) with ESMTPS id 03F9D3858401 for ; Mon, 6 Dec 2021 17:32:40 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 03F9D3858401 Received: by mail-oi1-x22a.google.com with SMTP id be32so22671409oib.11 for ; Mon, 06 Dec 2021 09:32:39 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=n7pUa5qynPpQphe5V9LeJQjN1Yfj5wahmzxogwxKdWE=; b=5GZOaqXEpycwHw+dHDqH/dqxfF5nQbEekCi98uI9IZ2cb3yUxsyePQa6B0YrDMQ/B3 xi/5JWD1xs3KTCcmJeKbj+OxkqAILEI5sRxE/hBzbIvs4xyFp6uTQxW8IJ5i+KHi8HgI ysIlX370EsIiCTDdfWLOr+/4TURJe8JYza1EXVl/Fm3IGVKfpjn/f3w/qhJ1yN0iCgOL PiD8SO3gLUAm38lZ087Uh6id6ykxixS575vNUieUjM++2oO2k4SzhTj77M/irzwYwJBb xKy+BsorcyVWKIJRc+/Sol3ty5gT8Gn29GimQEcCYjmpgridYB/oPFvK7qLpQRbNbAQl h6jw== X-Gm-Message-State: AOAM532m+7V2Pyr/2UyN3eHBAvx6Wqml/GBbDGLTt4BgvxBhoRS7GPGp gBmDDgkV/UAFhzXaRzzOQacUx/WPV4s= X-Google-Smtp-Source: ABdhPJy5HQF7PaLsOqdxIJ0srRNWuoGy2andU1pv/HxFgTSb7wisbBZc/vqfC4M1Xxq5tLwB6UQKgw== X-Received: by 2002:aca:2b02:: with SMTP id i2mr24827160oik.140.1638811959319; Mon, 06 Dec 2021 09:32:39 -0800 (PST) Received: from [192.168.0.41] (184-96-227-137.hlrn.qwest.net. [184.96.227.137]) by smtp.gmail.com with ESMTPSA id u40sm2922799oiw.56.2021.12.06.09.32.38 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 06 Dec 2021 09:32:39 -0800 (PST) Subject: [PATCH v2 4/5] fix up compute_objsize: refactor it into helpers To: Jeff Law , gcc-patches References: <65d1e530-a4cc-de27-1198-0dcaa08274bd@gmail.com> Message-ID: Date: Mon, 6 Dec 2021 10:32:38 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.2.2 MIME-Version: 1.0 In-Reply-To: Content-Language: en-US X-Spam-Status: No, score=-10.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Martin Sebor via Gcc-patches From: Martin Sebor Reply-To: Martin Sebor Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Attached is the subset of the patch in part (2) below: refactor compute_objsize_r into helpers. It applies on top of patch 3/5. On 12/3/21 5:00 PM, Jeff Law wrote: > > > On 11/8/2021 7:34 PM, Martin Sebor via Gcc-patches wrote: >> The pointer-query code that implements compute_objsize() that's >> in turn used by most middle end access warnings now has a few >> warts in it and (at least) one bug.  With the exception of >> the bug the warts aren't behind any user-visible bugs that >> I know of but they do cause problems in new code I've been >> implementing on top of it.  Besides fixing the one bug (just >> a typo) the attached patch cleans up these latent issues: >> >> 1) It moves the bndrng member from the access_ref class to >>    access_data.  As a FIXME in the code notes, the member never >>    did belong in the former and only takes up space in the cache. >> >> 2) The compute_objsize_r() function is big, unwieldy, and tedious >>    to step through because of all the if statements that are better >>    coded as one switch statement.  This change factors out more >>    of its code into smaller handler functions as has been suggested >>    and done a few times before. >> >> 3) (2) exposed a few places where I fail to pass the current >>    GIMPLE statement down to ranger.  This leads to worse quality >>    range info, including possible false positives and negatives. >>    I just spotted these problems in code review but I haven't >>    taken the time to come up with test cases.  This change fixes >>    these oversights as well. >> >> 4) The handling of PHI statements is also in one big, hard-to- >>    follow function.  This change moves the handling of each PHI >>    argument into its own handler which merges it into the previous >>    argument.  This makes the code easier to work with and opens it >>    to reuse also for MIN_EXPR and MAX_EXPR.  (This is primarily >>    used to print informational notes after warnings.) >> >> 5) Finally, the patch factors code to dump each access_ref >>    cached by the pointer_query cache out of pointer_query::dump >>    and into access_ref::dump.  This helps with debugging. >> >> These changes should have no user-visible effect and other than >> a regression test for the typo (PR 103143) come with no tests. >> They've been tested on x86_64-linux. > Sigh.  You've identified 6 distinct changes above.  The 5 you've > enumerated plus a typo fix somewhere.  There's no reason why they need > to be a single patch and many reasons why they should be a series of > independent patches.    Combining them into a single patch isn't how we > do things and it hides the actual bugfix in here. > > Please send a fix for the typo first since that should be able to > trivially go forward.  Then  a patch for item #1.  That should be > trivial to review when it's pulled out from teh rest of the patch. > Beyond that, your choice on ordering, but you need to break this down. > > > > > Jeff > commit 7d1d8cc18678ccaec5f274919e0c9910ccfea86e Author: Martin Sebor Date: Mon Dec 6 09:33:32 2021 -0700 Refactor compute_objsize_r into helpers. gcc/ChangeLog: * pointer-query.cc (compute_objsize_r): Add an argument. (gimple_call_return_array): Pass a new argument to compute_objsize_r. (access_ref::merge_ref): Same. (access_ref::inform_access): Add an argument and use it. (access_data::access_data): Initialize new member. (handle_min_max_size): Pass a new argument to compute_objsize_r. (handle_decl): New function. (handle_array_ref): Pass a new argument to compute_objsize_r. Avoid incrementing deref. (set_component_ref_size): New function. (handle_component_ref): New function. (handle_mem_ref): Pass a new argument to compute_objsize_r. Only increment deref after successfully computing object size. (handle_ssa_name): New function. (compute_objsize_r): Move code into helpers and call them. (compute_objsize): Pass a new argument to compute_objsize_r. * pointer-query.h (access_ref::inform_access): Add an argument. (access_data::ostype): New member. diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc index 24fbac84ec4..3f583110c71 100644 --- a/gcc/pointer-query.cc +++ b/gcc/pointer-query.cc @@ -43,7 +43,7 @@ #include "tree-ssanames.h" #include "target.h" -static bool compute_objsize_r (tree, gimple *, int, access_ref *, +static bool compute_objsize_r (tree, gimple *, bool, int, access_ref *, ssa_name_limit_t &, pointer_query *); /* Wrapper around the wide_int overload of get_range that accepts @@ -199,7 +199,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, of the source object. */ access_ref aref; tree src = gimple_call_arg (stmt, 1); - if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry) + if (compute_objsize_r (src, stmt, false, 1, &aref, snlim, qry) && aref.sizrng[1] < offrng[1]) offrng[1] = aref.sizrng[1]; } @@ -233,7 +233,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, { access_ref aref; tree src = gimple_call_arg (stmt, 1); - if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry)) + if (compute_objsize_r (src, stmt, false, 1, &aref, snlim, qry)) offrng[1] = aref.sizrng[1] - 1; else offrng[1] = HOST_WIDE_INT_M1U; @@ -258,7 +258,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, of the source object. */ access_ref aref; tree src = gimple_call_arg (stmt, 1); - if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry) + if (compute_objsize_r (src, stmt, false, 1, &aref, snlim, qry) && aref.sizrng[1] < offrng[1]) offrng[1] = aref.sizrng[1]; } @@ -635,7 +635,7 @@ access_ref::merge_ref (vec *all_refs, tree arg, gimple *stmt, ssa_name_limit_t &snlim, pointer_query &qry) { access_ref aref; - if (!compute_objsize_r (arg, stmt, ostype, &aref, snlim, &qry) + if (!compute_objsize_r (arg, stmt, false, ostype, &aref, snlim, &qry) || aref.sizrng[0] < 0) /* This may be a PHI with all null pointer arguments. */ return false; @@ -997,42 +997,45 @@ void access_ref::add_offset (const offset_int &min, const offset_int &max) WRITE is set for a write access and clear for a read access. */ void -access_ref::inform_access (access_mode mode) const +access_ref::inform_access (access_mode mode, int ostype /* = 1 */) const { const access_ref &aref = *this; if (!aref.ref) return; - if (aref.phi ()) + if (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 all_refs; - if (!get_ref (&all_refs, &maxref)) + if (!get_ref (&all_refs, &maxref, ostype)) 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[] = + if (all_refs.length ()) { - offrng[0] - maxref.offrng[0], - wi::smax (offrng[1] - maxref.offrng[1], offrng[0]), - }; + /* 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; + /* 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); + all_refs[i].add_offset (orng[0], orng[1]); + all_refs[i].inform_access (mode, ostype); + } + return; } - return; } /* Convert offset range and avoid including a zero range since it @@ -1244,7 +1247,7 @@ access_data::access_data (range_query *query, gimple *stmt, access_mode mode, bool minwrite /* = false */, tree maxread /* = NULL_TREE */, bool minread /* = false */) - : stmt (stmt), call (), dst (), src (), mode (mode) + : stmt (stmt), call (), dst (), src (), mode (mode), ostype () { set_bound (dst_bndrng, maxwrite, minwrite, query, stmt); set_bound (src_bndrng, maxread, minread, query, stmt); @@ -1257,7 +1260,7 @@ access_data::access_data (range_query *query, tree expr, access_mode mode, bool minwrite /* = false */, tree maxread /* = NULL_TREE */, bool minread /* = false */) - : stmt (), call (expr), dst (), src (), mode (mode) + : stmt (), call (expr), dst (), src (), mode (mode), ostype () { set_bound (dst_bndrng, maxwrite, minwrite, query, stmt); set_bound (src_bndrng, maxread, minread, query, stmt); @@ -1406,7 +1409,8 @@ pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const there or compute it and insert it into the cache if it's nonnonull. */ bool -pointer_query::get_ref (tree ptr, gimple *stmt, access_ref *pref, int ostype /* = 1 */) +pointer_query::get_ref (tree ptr, gimple *stmt, access_ref *pref, + int ostype /* = 1 */) { const unsigned version = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0; @@ -1606,7 +1610,7 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref, for the expression. */ access_ref aref[2] = { *pref, *pref }; tree arg1 = gimple_assign_rhs1 (stmt); - if (!compute_objsize_r (arg1, stmt, ostype, &aref[0], snlim, qry)) + if (!compute_objsize_r (arg1, stmt, false, ostype, &aref[0], snlim, qry)) { aref[0].base0 = false; aref[0].offrng[0] = aref[0].offrng[1] = 0; @@ -1615,7 +1619,7 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref, } tree arg2 = gimple_assign_rhs2 (stmt); - if (!compute_objsize_r (arg2, stmt, ostype, &aref[1], snlim, qry)) + if (!compute_objsize_r (arg2, stmt, false, ostype, &aref[1], snlim, qry)) { aref[1].base0 = false; aref[1].offrng[0] = aref[1].offrng[1] = 0; @@ -1678,6 +1682,46 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref, return true; } +/* A helper of compute_objsize_r() to determine the size of a DECL. + Return true on success and (possibly in the future) false on failure. */ + +static bool +handle_decl (tree decl, bool addr, access_ref *pref) +{ + tree decl_type = TREE_TYPE (decl); + + pref->ref = decl; + + /* Reset the offset in case it was set by a prior call and not + cleared by the caller. The offset is only adjusted after + the identity of the object has been determined. */ + pref->offrng[0] = pref->offrng[1] = 0; + + if (!addr && POINTER_TYPE_P (decl_type)) + { + /* Set the maximum size if the reference is to the pointer + itself (as opposed to what it points to), and clear + BASE0 since the offset isn't necessarily zero-based. */ + pref->set_max_size_range (); + pref->base0 = false; + return true; + } + + /* Valid offsets into the object are nonnegative. */ + pref->base0 = true; + + if (tree size = decl_init_size (decl, false)) + if (TREE_CODE (size) == INTEGER_CST) + { + pref->sizrng[0] = wi::to_offset (size); + pref->sizrng[1] = pref->sizrng[0]; + return true; + } + + pref->set_max_size_range (); + return true; +} + /* A helper of compute_objsize_r() to determine the size from ARRAY_REF AREF. ADDR is true if PTR is the operand of ADDR_EXPR. Return true on success and false on failure. */ @@ -1689,8 +1733,6 @@ handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype, { gcc_assert (TREE_CODE (aref) == ARRAY_REF); - ++pref->deref; - tree arefop = TREE_OPERAND (aref, 0); tree reftype = TREE_TYPE (arefop); if (!addr && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE) @@ -1698,7 +1740,7 @@ handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype, of known bound. */ return false; - if (!compute_objsize_r (arefop, stmt, ostype, pref, snlim, qry)) + if (!compute_objsize_r (arefop, stmt, addr, ostype, pref, snlim, qry)) return false; offset_int orng[2]; @@ -1759,6 +1801,96 @@ handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype, return true; } +/* Given a COMPONENT_REF CREF, set *PREF size to the size of the referenced + member. */ + +static void +set_component_ref_size (tree cref, access_ref *pref) +{ + const tree base = TREE_OPERAND (cref, 0); + const tree base_type = TREE_TYPE (base); + + /* SAM is set for array members that might need special treatment. */ + special_array_member sam; + tree size = component_ref_size (cref, &sam); + if (sam == special_array_member::int_0) + pref->sizrng[0] = pref->sizrng[1] = 0; + else if (!pref->trail1special && sam == special_array_member::trail_1) + pref->sizrng[0] = pref->sizrng[1] = 1; + else if (size && TREE_CODE (size) == INTEGER_CST) + pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); + else + { + /* When the size of the member is unknown it's either a flexible + array member or a trailing special array member (either zero + length or one-element). Set the size to the maximum minus + the constant size of the base object's type. */ + pref->sizrng[0] = 0; + pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node)); + if (tree base_size = TYPE_SIZE_UNIT (base_type)) + if (TREE_CODE (base_size) == INTEGER_CST) + pref->sizrng[1] -= wi::to_offset (base_size); + } +} + +/* A helper of compute_objsize_r() to determine the size from COMPONENT_REF + CREF. Return true on success and false on failure. */ + +static bool +handle_component_ref (tree cref, gimple *stmt, bool addr, int ostype, + access_ref *pref, ssa_name_limit_t &snlim, + pointer_query *qry) +{ + gcc_assert (TREE_CODE (cref) == COMPONENT_REF); + + const tree base = TREE_OPERAND (cref, 0); + const tree base_type = TREE_TYPE (base); + if (TREE_CODE (base_type) == UNION_TYPE) + /* In accesses through union types consider the entire unions + rather than just their members. */ + ostype = 0; + + tree field = TREE_OPERAND (cref, 1); + + if (ostype == 0) + { + /* 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_r (base, stmt, addr, ostype, pref, snlim, qry)) + return false; + + /* Otherwise, use the size of the enclosing object and add + the offset of the member to the offset computed so far. */ + tree offset = byte_position (field); + if (TREE_CODE (offset) == INTEGER_CST) + pref->add_offset (wi::to_offset (offset)); + else + pref->add_max_offset (); + + if (!pref->ref) + /* PREF->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 = base; + + return true; + } + + pref->ref = field; + + if (!addr && POINTER_TYPE_P (TREE_TYPE (field))) + { + /* Set maximum size if the reference is to the pointer member + itself (as opposed to what it points to). */ + pref->set_max_size_range (); + return true; + } + + set_component_ref_size (cref, pref); + return true; +} + /* A helper of compute_objsize_r() to determine the size from MEM_REF MREF. Return true on success and false on failure. */ @@ -1768,10 +1900,9 @@ handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref, { gcc_assert (TREE_CODE (mref) == MEM_REF); - ++pref->deref; - - if (VECTOR_TYPE_P (TREE_TYPE (mref))) - { + tree mreftype = TYPE_MAIN_VARIANT (TREE_TYPE (mref)); + if (VECTOR_TYPE_P (mreftype)) + { /* Hack: Handle MEM_REFs of vector types as those to complete objects; those may be synthesized from multiple assignments to consecutive data members (see PR 93200 and 96963). @@ -1785,9 +1916,11 @@ handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref, } tree mrefop = TREE_OPERAND (mref, 0); - if (!compute_objsize_r (mrefop, stmt, ostype, pref, snlim, qry)) + if (!compute_objsize_r (mrefop, stmt, false, ostype, pref, snlim, qry)) return false; + ++pref->deref; + offset_int orng[2]; tree off = pref->eval (TREE_OPERAND (mref, 1)); range_query *const rvals = qry ? qry->rvals : NULL; @@ -1802,168 +1935,283 @@ handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref, 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). - On success, sets PREF->REF to the DECL of the referenced object - 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). - 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. - - The function is intended for diagnostics and should not be used - to influence code generation or optimization. */ +/* A helper of compute_objsize_r() to determine the size from SSA_NAME + PTR. Return true on success and false on failure. */ static bool -compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref, - ssa_name_limit_t &snlim, pointer_query *qry) +handle_ssa_name (tree ptr, bool addr, int ostype, + access_ref *pref, ssa_name_limit_t &snlim, + pointer_query *qry) { - STRIP_NOPS (ptr); + if (!snlim.next ()) + return false; - const bool addr = TREE_CODE (ptr) == ADDR_EXPR; - if (addr) + /* Only process an SSA_NAME if the recursion limit has not yet + been reached. */ + if (qry) { - --pref->deref; - ptr = TREE_OPERAND (ptr, 0); + if (++qry->depth > qry->max_depth) + qry->max_depth = qry->depth; + if (const access_ref *cache_ref = qry->get_ref (ptr, ostype)) + { + /* Add the number of DEREFerences accummulated so far. */ + const int deref = pref->deref; + *pref = *cache_ref; + pref->deref += deref; + return true; + } } - if (DECL_P (ptr)) - { - pref->ref = ptr; - - /* Reset the offset in case it was set by a prior call and not - cleared by the caller. The offset is only adjusted after - the identity of the object has been determined. */ - pref->offrng[0] = pref->offrng[1] = 0; + gimple *stmt = SSA_NAME_DEF_STMT (ptr); + if (is_gimple_call (stmt)) + { + /* If STMT is a call to an allocation function get the size + from its argument(s). If successful, also set *PREF->REF + to PTR for the caller to include in diagnostics. */ + wide_int wr[2]; + range_query *const rvals = qry ? qry->rvals : NULL; + if (gimple_call_alloc_size (stmt, wr, rvals)) + { + pref->ref = ptr; + pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED); + pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED); + /* Constrain both bounds to a valid size. */ + offset_int maxsize = wi::to_offset (max_object_size ()); + if (pref->sizrng[0] > maxsize) + pref->sizrng[0] = maxsize; + if (pref->sizrng[1] > maxsize) + pref->sizrng[1] = maxsize; + } + else + { + /* For functions known to return one of their pointer arguments + try to determine what the returned pointer points to, and on + success add OFFRNG which was set to the offset added by + the function (e.g., memchr) to the overall offset. */ + bool past_end; + offset_int offrng[2]; + if (tree ret = gimple_call_return_array (stmt, offrng, &past_end, + snlim, qry)) + { + if (!compute_objsize_r (ret, stmt, addr, ostype, pref, snlim, qry)) + return false; + + /* Cap OFFRNG[1] to at most the remaining size of + the object. */ + offset_int remrng[2]; + remrng[1] = pref->size_remaining (remrng); + if (remrng[1] != 0 && !past_end) + /* Decrement the size for functions that never return + a past-the-end pointer. */ + remrng[1] -= 1; + + if (remrng[1] < offrng[1]) + offrng[1] = remrng[1]; + pref->add_offset (offrng[0], offrng[1]); + } + else + { + /* For other calls that might return arbitrary pointers + including into the middle of objects set the size + range to maximum, clear PREF->BASE0, and also set + PREF->REF to include in diagnostics. */ + pref->set_max_size_range (); + pref->base0 = false; + pref->ref = ptr; + } + } + qry->put_ref (ptr, *pref, ostype); + return true; + } - if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr))) + if (gimple_nop_p (stmt)) + { + /* For a function argument try to determine the byte size + of the array from the current function declaratation + (e.g., attribute access or related). */ + wide_int wr[2]; + bool static_array = false; + if (tree ref = gimple_parm_array_size (ptr, wr, &static_array)) { - /* Set the maximum size if the reference is to the pointer - itself (as opposed to what it points to), and clear - BASE0 since the offset isn't necessarily zero-based. */ - pref->set_max_size_range (); - pref->base0 = false; + 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; + qry->put_ref (ptr, *pref, ostype); return true; } - /* Valid offsets into the object are nonnegative. */ - pref->base0 = true; - - if (tree size = decl_init_size (ptr, false)) - if (TREE_CODE (size) == INTEGER_CST) - { - pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); - return true; - } - pref->set_max_size_range (); + pref->base0 = false; + pref->ref = ptr; + qry->put_ref (ptr, *pref, ostype); return true; } - const tree_code code = TREE_CODE (ptr); - range_query *const rvals = qry ? qry->rvals : NULL; - - if (code == BIT_FIELD_REF) + if (gimple_code (stmt) == GIMPLE_PHI) { - tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry)) + /* Pass PTR to get_ref() via PREF. If all PHI arguments refer + to the same object the function will replace it with it. */ + pref->ref = ptr; + access_ref phi_ref = *pref; + if (!pref->get_ref (NULL, &phi_ref, ostype, &snlim, qry)) return false; + *pref = phi_ref; + qry->put_ref (ptr, *pref, ostype); + return true; + } - offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2))); - pref->add_offset (off / BITS_PER_UNIT); + if (!is_gimple_assign (stmt)) + { + /* Clear BASE0 since the assigned pointer might point into + the middle of the object, set the maximum size range and, + if the SSA_NAME refers to a function argumnent, set + PREF->REF to it. */ + pref->base0 = false; + pref->set_max_size_range (); + pref->ref = ptr; return true; } - if (code == COMPONENT_REF) + tree_code code = gimple_assign_rhs_code (stmt); + + if (code == MAX_EXPR || code == MIN_EXPR) { - 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 (!handle_min_max_size (ptr, ostype, pref, snlim, qry)) + return false; - if (ostype == 0) - { - /* 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_r (ref, stmt, ostype, pref, snlim, qry)) - return false; - - /* Otherwise, use the size of the enclosing object and add - the offset of the member to the offset computed so far. */ - tree offset = byte_position (field); - if (TREE_CODE (offset) == INTEGER_CST) - pref->add_offset (wi::to_offset (offset)); - else - pref->add_max_offset (); + qry->put_ref (ptr, *pref, ostype); + return true; + } - 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; - } + tree rhs = gimple_assign_rhs1 (stmt); - pref->ref = field; + if (code == ASSERT_EXPR) + { + rhs = TREE_OPERAND (rhs, 0); + return compute_objsize_r (rhs, stmt, addr, ostype, pref, snlim, qry); + } - if (!addr && POINTER_TYPE_P (TREE_TYPE (field))) - { - /* Set maximum size if the reference is to the pointer member - itself (as opposed to what it points to). */ - pref->set_max_size_range (); - return true; - } + if (code == POINTER_PLUS_EXPR + && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE) + { + /* Compute the size of the object first. */ + if (!compute_objsize_r (rhs, stmt, addr, ostype, pref, snlim, qry)) + return false; - /* SAM is set for array members that might need special treatment. */ - special_array_member sam; - tree size = component_ref_size (ptr, &sam); - if (sam == special_array_member::int_0) - pref->sizrng[0] = pref->sizrng[1] = 0; - else if (!pref->trail1special && sam == special_array_member::trail_1) - pref->sizrng[0] = pref->sizrng[1] = 1; - else if (size && TREE_CODE (size) == INTEGER_CST) - pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); + offset_int orng[2]; + tree off = gimple_assign_rhs2 (stmt); + range_query *const rvals = qry ? qry->rvals : NULL; + if (get_offset_range (off, stmt, orng, rvals)) + pref->add_offset (orng[0], orng[1]); else - { - /* When the size of the member is unknown it's either a flexible - array member or a trailing special array member (either zero - length or one-element). Set the size to the maximum minus - the constant size of the type. */ - pref->sizrng[0] = 0; - pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node)); - if (tree recsize = TYPE_SIZE_UNIT (TREE_TYPE (ref))) - if (TREE_CODE (recsize) == INTEGER_CST) - pref->sizrng[1] -= wi::to_offset (recsize); - } + pref->add_max_offset (); + + qry->put_ref (ptr, *pref, ostype); return true; } - if (code == ARRAY_REF) - return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry); - - if (code == MEM_REF) - return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry); + if (code == ADDR_EXPR || code == SSA_NAME) + { + if (!compute_objsize_r (rhs, stmt, addr, ostype, pref, snlim, qry)) + return false; + qry->put_ref (ptr, *pref, ostype); + return true; + } - if (code == TARGET_MEM_REF) + if (ostype > 1 && POINTER_TYPE_P (TREE_TYPE (rhs))) { - tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry)) + /* When determining the qualifiers follow the pointer but + avoid caching the result. As the pointer is added to + and/or dereferenced the computed size and offset need + not be meaningful for other queries involving the same + pointer. */ + if (!compute_objsize_r (rhs, stmt, addr, ostype, pref, snlim, qry)) return false; - /* TODO: Handle remaining operands. Until then, add maximum offset. */ - pref->ref = ptr; - pref->add_max_offset (); - return true; + rhs = pref->ref; } - if (code == INTEGER_CST) + /* (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 = rhs; + pref->base0 = false; + pref->set_max_size_range (); + 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). + On success, sets PREF->REF to the DECL of the referenced object + 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). + ADDR is true for an enclosing ADDR_EXPR. + 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. + + The function is intended for diagnostics and should not be used + to influence code generation or optimization. */ + +static bool +compute_objsize_r (tree ptr, gimple *stmt, bool addr, int ostype, + access_ref *pref, ssa_name_limit_t &snlim, + pointer_query *qry) +{ + STRIP_NOPS (ptr); + + if (DECL_P (ptr)) + return handle_decl (ptr, addr, pref); + + switch (TREE_CODE (ptr)) { + case ADDR_EXPR: + { + tree ref = TREE_OPERAND (ptr, 0); + if (!compute_objsize_r (ref, stmt, true, ostype, pref, snlim, qry)) + return false; + + --pref->deref; + return true; + } + + case BIT_FIELD_REF: + { + tree ref = TREE_OPERAND (ptr, 0); + if (!compute_objsize_r (ref, stmt, addr, ostype, pref, snlim, qry)) + return false; + + offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2))); + pref->add_offset (off / BITS_PER_UNIT); + return true; + } + + case ARRAY_REF: + return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry); + + case COMPONENT_REF: + return handle_component_ref (ptr, stmt, addr, ostype, pref, snlim, qry); + + case MEM_REF: + return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry); + + case TARGET_MEM_REF: + { + tree ref = TREE_OPERAND (ptr, 0); + if (!compute_objsize_r (ref, stmt, addr, ostype, pref, snlim, qry)) + return false; + + /* TODO: Handle remaining operands. Until then, add maximum offset. */ + pref->ref = ptr; + pref->add_max_offset (); + return true; + } + + case INTEGER_CST: /* Pointer constants other than null are most likely the result of erroneous null pointer addition/subtraction. Unless zero is a valid address set size to zero. For null pointers, set @@ -1984,21 +2232,17 @@ compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref, pref->sizrng[0] = pref->sizrng[1] = 0; pref->ref = ptr; - return true; - } - if (code == STRING_CST) - { + case STRING_CST: pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr); pref->ref = ptr; return true; - } - if (code == POINTER_PLUS_EXPR) + case POINTER_PLUS_EXPR: { tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry)) + if (!compute_objsize_r (ref, stmt, addr, ostype, pref, snlim, qry)) return false; /* Clear DEREF since the offset is being applied to the target @@ -2007,200 +2251,22 @@ compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref, offset_int orng[2]; tree off = pref->eval (TREE_OPERAND (ptr, 1)); - if (get_offset_range (off, stmt, orng, rvals)) + if (get_offset_range (off, stmt, orng, qry->rvals)) pref->add_offset (orng[0], orng[1]); else pref->add_max_offset (); return true; } - if (code == VIEW_CONVERT_EXPR) - { + case VIEW_CONVERT_EXPR: ptr = TREE_OPERAND (ptr, 0); - return compute_objsize_r (ptr, stmt, ostype, pref, snlim, qry); - } + return compute_objsize_r (ptr, stmt, addr, ostype, pref, snlim, qry); - if (code == SSA_NAME) - { - if (!snlim.next ()) - return false; + case SSA_NAME: + return handle_ssa_name (ptr, addr, ostype, pref, snlim, qry); - /* Only process an SSA_NAME if the recursion limit has not yet - been reached. */ - if (qry) - { - if (++qry->depth) - qry->max_depth = qry->depth; - if (const access_ref *cache_ref = qry->get_ref (ptr)) - { - /* If the pointer is in the cache set *PREF to what it refers - to and return success. */ - *pref = *cache_ref; - return true; - } - } - - stmt = SSA_NAME_DEF_STMT (ptr); - if (is_gimple_call (stmt)) - { - /* If STMT is a call to an allocation function get the size - from its argument(s). If successful, also set *PREF->REF - to PTR for the caller to include in diagnostics. */ - wide_int wr[2]; - if (gimple_call_alloc_size (stmt, wr, rvals)) - { - pref->ref = ptr; - pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED); - pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED); - /* Constrain both bounds to a valid size. */ - offset_int maxsize = wi::to_offset (max_object_size ()); - if (pref->sizrng[0] > maxsize) - pref->sizrng[0] = maxsize; - if (pref->sizrng[1] > maxsize) - pref->sizrng[1] = maxsize; - } - else - { - /* For functions known to return one of their pointer arguments - try to determine what the returned pointer points to, and on - success add OFFRNG which was set to the offset added by - the function (e.g., memchr) to the overall offset. */ - bool past_end; - offset_int offrng[2]; - if (tree ret = gimple_call_return_array (stmt, offrng, - &past_end, snlim, qry)) - { - if (!compute_objsize_r (ret, stmt, ostype, pref, snlim, qry)) - return false; - - /* Cap OFFRNG[1] to at most the remaining size of - the object. */ - offset_int remrng[2]; - remrng[1] = pref->size_remaining (remrng); - if (remrng[1] != 0 && !past_end) - /* Decrement the size for functions that never return - a past-the-end pointer. */ - remrng[1] -= 1; - - if (remrng[1] < offrng[1]) - offrng[1] = remrng[1]; - pref->add_offset (offrng[0], offrng[1]); - } - else - { - /* For other calls that might return arbitrary pointers - including into the middle of objects set the size - range to maximum, clear PREF->BASE0, and also set - PREF->REF to include in diagnostics. */ - pref->set_max_size_range (); - pref->base0 = false; - pref->ref = ptr; - } - } - qry->put_ref (ptr, *pref); - return true; - } - - if (gimple_nop_p (stmt)) - { - /* For a function argument try to determine the byte size - of the array from the current function declaratation - (e.g., attribute access or related). */ - wide_int wr[2]; - 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; - qry->put_ref (ptr, *pref); - return true; - } - - pref->set_max_size_range (); - pref->base0 = false; - pref->ref = ptr; - qry->put_ref (ptr, *pref); - return true; - } - - if (gimple_code (stmt) == GIMPLE_PHI) - { - pref->ref = ptr; - access_ref phi_ref = *pref; - if (!pref->get_ref (NULL, &phi_ref, ostype, &snlim, qry)) - return false; - *pref = phi_ref; - pref->ref = ptr; - qry->put_ref (ptr, *pref); - return true; - } - - if (!is_gimple_assign (stmt)) - { - /* Clear BASE0 since the assigned pointer might point into - the middle of the object, set the maximum size range and, - if the SSA_NAME refers to a function argumnent, set - PREF->REF to it. */ - pref->base0 = false; - pref->set_max_size_range (); - pref->ref = ptr; - return true; - } - - tree_code code = gimple_assign_rhs_code (stmt); - - if (code == MAX_EXPR || code == MIN_EXPR) - { - if (!handle_min_max_size (ptr, ostype, pref, snlim, qry)) - return false; - - qry->put_ref (ptr, *pref); - return true; - } - - tree rhs = gimple_assign_rhs1 (stmt); - - if (code == ASSERT_EXPR) - { - rhs = TREE_OPERAND (rhs, 0); - return compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry); - } - - if (code == POINTER_PLUS_EXPR - && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE) - { - /* Compute the size of the object first. */ - if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry)) - return false; - - offset_int orng[2]; - tree off = gimple_assign_rhs2 (stmt); - if (get_offset_range (off, stmt, orng, rvals)) - pref->add_offset (orng[0], orng[1]); - else - pref->add_max_offset (); - - qry->put_ref (ptr, *pref); - return true; - } - - if (code == ADDR_EXPR || code == SSA_NAME) - { - if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry)) - return false; - qry->put_ref (ptr, *pref); - return true; - } - - /* (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 = rhs; - pref->base0 = false; - pref->set_max_size_range (); - return true; + default: + break; } /* Assume all other expressions point into an unknown object @@ -2231,7 +2297,7 @@ compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref, pref->sizrng[0] = pref->sizrng[1] = -1; ssa_name_limit_t snlim; - if (!compute_objsize_r (ptr, stmt, ostype, pref, snlim, ptr_qry)) + if (!compute_objsize_r (ptr, stmt, false, ostype, pref, snlim, ptr_qry)) return NULL_TREE; offset_int maxsize = pref->size_remaining (); diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h index fe46f711e96..a7ac7d34370 100644 --- a/gcc/pointer-query.h +++ b/gcc/pointer-query.h @@ -122,7 +122,7 @@ struct access_ref /* Issue an informational message describing the target of an access with the given mode. */ - void inform_access (access_mode) const; + void inform_access (access_mode, int = 1) const; /* Reference to the accessed object(s). */ tree ref; @@ -234,6 +234,8 @@ struct access_data /* Read-only for functions like memcmp or strlen, write-only for memset, read-write for memcpy or strcat. */ access_mode mode; + /* The object size type. */ + int ostype; }; enum size_range_flags