From patchwork Sun Jul 16 23:47:39 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 789179 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3x9jmF68zNz9sRg for ; Mon, 17 Jul 2017 09:48:12 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="hNV1vMyD"; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; q=dns; s=default; b=GXahwG+a9wTfdg7mXI+5Kku2xgQXSNiYrq4n0qjG5uRgnvVnZe i535ue5V7WRz74saA8m6U90fTaM9OeDZswEfbgCbrE/tGSPMKrfdl5qAd1Hdj7Bg 11v85BTcWj2y3hhHB/66yClx6gHPHP12X+e+G5npO9eb17SBpwspr7Y+8= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; s= default; bh=nlFX7AyjAUP8kuYvGMWMI5OiQ9M=; b=hNV1vMyDPjPCq09osMcH zAK9GzyHYynxR4WgLnx1JrO/SEbBx85lUOG6mODB2QM2a0S9X6cMDmMY2y+0LIa0 +7MCHKrAOFufYqiByd1TVMCLCrrjTGo1q+DuFwco8aNAj2Pr5kC+6SWhAGsOR+z+ F/zn4lcotW2JabWrCWQetAo= Received: (qmail 12795 invoked by alias); 16 Jul 2017 23:47:55 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 11397 invoked by uid 89); 16 Jul 2017 23:47:54 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-23.4 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_LOW, RCVD_IN_SORBS_SPAM, SPF_PASS, UNSUBSCRIBE_BODY autolearn=ham version=3.3.2 spammy=Dispatch, 36527, Oracle X-HELO: mail-qt0-f180.google.com Received: from mail-qt0-f180.google.com (HELO mail-qt0-f180.google.com) (209.85.216.180) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 16 Jul 2017 23:47:44 +0000 Received: by mail-qt0-f180.google.com with SMTP id 21so19710030qtx.3 for ; Sun, 16 Jul 2017 16:47:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version; bh=2I6qxw4dSVFgSnCUNYY2gnpzEFossZKpL55b47GIryU=; b=Gdt4oE7azk0rgJV81ob+dzpx2W9o2JbAIspIP6Zn2qblgztLLoBrPBN/dJRbQI2zlH yRwolzJ7GPzW3Y6+0C+aRoeWpo9VP1gLh5463oFV8SYZzgQBkNm1oAfRmfyYWW0N5XrU f83FV1/fBT142S6DZmS/UuUxZjGpGh6md5WJYuFqyZKG5kVnJe44mIKMllHSA8SU97Tb 1bW3WPDPrXwnH4e+zjxarXIRhaCrqird5/6zExvXhPMcpJlDIkM+iNDwaD042Qs3zMtq 7mlR8ZEpbUfDtVJ9Z6uBlV/KRqe9rvm04eEIOHwJz/u6D2II+3AddPogC5h9hr4QXlho fU1A== X-Gm-Message-State: AIVw112uBjPntI2oUwGkc0hfF/I5RJn7aRmMOUkOvAqnj5sp4JKf5vuu GqNpNo9DKfSUGMHk X-Received: by 10.200.38.133 with SMTP id 5mr25397523qto.237.1500248862354; Sun, 16 Jul 2017 16:47:42 -0700 (PDT) Received: from localhost.localdomain (75-171-229-159.hlrn.qwest.net. [75.171.229.159]) by smtp.gmail.com with ESMTPSA id i5sm12781635qtc.2.2017.07.16.16.47.40 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 16 Jul 2017 16:47:41 -0700 (PDT) To: Gcc Patch List From: Martin Sebor Subject: [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918) Message-ID: <8a0de82f-d68e-9c37-0406-f216e3f92884@gmail.com> Date: Sun, 16 Jul 2017 17:47:39 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0 MIME-Version: 1.0 X-IsSubscribed: yes Being implemented in the front end, the -Wrestrict warning detects only trivial instances of violations. The attached patch extends the implementation to the middle-end where data flow and alias analysis can be combined to detect even complex cases of overlap. This work is independent of but follows on the patch below (waiting for review): https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00036.html The changes rely on extending the tree-ssa-{alias,structalias} machinery in a simple way to answer the "must-alias" kind of question in addition to the current "may-alias." The rest of the changes are in gimple-fold.c, tree-ssa-strlen.c, and builtins.c. Even though this change makes -Wrestrict a lot more useful, it's not a complete implementation. Not all built-ins are handled yet (e.g., strncat), and support for user-defined functions is still subject to the limitations of the front end implementation. To complete the support, handlers for the missing string built-ins will need to be added to tree-ssa-strlen.c, and the remaining bits should be moved from the front end to somewhere in the middle-end (e.g., calls.c). Martin PR middle-end/78918 - missing -Wrestrict on memcpy copying over self gcc/ChangeLog: PR middle-end/78918 * builtins.c (check_sizes): Add argument and handle -Wrestrict. Rename function arguments for clarity. (check_memop_sizes): Adjust. (expand_builtin_memchr): Ditto. (expand_builtin_strcat): Ditto. (expand_builtin_strcpy): Ditto. (expand_builtin_stpcpy): Ditto. (expand_builtin_stpncpy): Ditto. (expand_builtin_strncpy): Ditto. (expand_builtin_memcmp): Ditto. (expand_builtin_memory_chk): Ditto. (check_strncat_sizes): Ditto. Rename locals for clarity. (expand_builtin_strncat): Ditto. (maybe_emit_chk_warning): Ditto. (maybe_emit_sprintf_chk_warning): Adjust. * cfgexpand.c (expand_call_stmt): Set TREE_NO_WARNING. * gimple-fold.c (gimple_fold_builtin_memory_op): Handle -Wrestrict. (gimple_fold_builtin_memory_chk): Ditto. (gimple_fold_builtin_stxcpy_chk): Ditto. * gimple.c (gimple_build_call_from_tree): Set call location. * tree-ssa-alias.c (ptr_deref_may_alias_decl_p): Add argument and rename... (ptr_deref_alias_decl_p): ...to this. (ptr_derefs_may_alias_p): Add argument and rename... (ptr_derefs_alias_p): ...to this. (ptr_deref_may_alias_ref_p_1): Adjust. (aliasing_component_refs_p): Change return type and add argument. (decl_refs_may_alias_p): Ditto. (indirect_ref_may_alias_decl_p): Ditto. (indirect_refs_may_alias_p): Ditto. Rename to... (indirect_refs_alias_p): ...this. (refs_alias_p_1): New static function. (refs_may_alias_p_1): Call it. (refs_must_alias_p_1): New extern function. * tree-ssa-alias.h (range_overlap): New inline function. (ranges_overlap_p): Call it. * tree-ssa-strlen.c (handle_builtin_strcpy): Handle -Wrestrict. (handle_builtin_strcat): Ditto. * tree-ssa-structalias.c (pt_solution_includes): Add argument. (pt_solutions_intersect_1): Ditto. (pt_solutions_intersect): Adjust. gcc/c-family/ChangeLog: PR middle-end/78918 * c-common.c (check_function_restrict): Suppress warning for built-in functions. * c.opt (-Wrestrict): Include in -Wall. gcc/testsuite/ChangeLog: PR middle-end/78918 * c-c++-common/Wrestrict.c: New test. * gcc.dg/Walloca-1.c: Suppress macro expansion tracking. * gcc.dg/pr69172.c: Prune -Wrestrict. diff --git a/gcc/builtins.c b/gcc/builtins.c index 2deef72..958ed51 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -3038,37 +3038,45 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp) /* Try to verify that the sizes and lengths of the arguments to a string manipulation function given by EXP are within valid bounds and that - the operation does not lead to buffer overflow. Arguments other than - EXP may be null. When non-null, the arguments have the following - meaning: - SIZE is the user-supplied size argument to the function (such as in - memcpy(d, s, SIZE) or strncpy(d, s, SIZE). It specifies the exact - number of bytes to write. - MAXLEN is the user-supplied bound on the length of the source sequence + the operation does not lead to buffer overflow or read past the end. + For copy operations the must not overlap also verify that the source + and destination do not overlap one another. + Arguments other than EXP may be null. When non-null, the arguments + have the following meaning: + DST is the destination of a copy call or null otherwise. + SRC is the source of a copy call or null otherwise. + DSTWRITE is the number of bytes into the destination obtained from + the user-supplied size argument to the function (such as in memcpy(DST, + SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE). + MAXREAD is the user-supplied bound on the length of the source sequence (such as in strncat(d, s, N). It specifies the upper limit on the number - of bytes to write. - SRC is the source string (such as in strcpy(d, s)) when the expression - EXP is a string function call (as opposed to a memory call like memcpy). - As an exception, SRC can also be an integer denoting the precomputed - size of the source string or object (for functions like memcpy). - OBJSIZE is the size of the destination object specified by the last + of bytes to write. If null it's taken to be the same as DSTWRITE. + SRCSTR is the source string (such as in strcpy(DST, SRC)) when the + expression EXP is a string function call (as opposed to a memory call + like memcpy). As an exception, SRCSTR can also be an integer denoting + the precomputed size of the source string or object (for functions like + memcpy). + DSTSIZE is the size of the destination object specified by the last argument to the _chk builtins, typically resulting from the expansion - of __builtin_object_size (such as in __builtin___strcpy_chk(d, s, - OBJSIZE). + of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC, + DSTSIZE). - When SIZE is null LEN is checked to verify that it doesn't exceed + When DSTWRITE is null LEN is checked to verify that it doesn't exceed SIZE_MAX. If the call is successfully verified as safe from buffer overflow - the function returns true, otherwise false.. */ + the function returns true, otherwise false. */ static bool -check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) +check_sizes (int opt, tree exp, tree dst, tree src, tree dstwrite, + tree maxread, tree srcstr, tree dstsize) { /* The size of the largest object is half the address space, or SSIZE_MAX. (This is way too permissive.) */ tree maxobjsize = TYPE_MAX_VALUE (ssizetype); + /* Either the length of the source string for string functions or + the size of the source object for raw memory functions. */ tree slen = NULL_TREE; tree range[2] = { NULL_TREE, NULL_TREE }; @@ -3077,28 +3085,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) function like strcpy is not known and the only thing that is known is that it must be at least one (for the terminating nul). */ bool at_least_one = false; - if (src) + if (srcstr) { - /* SRC is normally a pointer to string but as a special case + /* SRCSTR is normally a pointer to string but as a special case it can be an integer denoting the length of a string. */ - if (POINTER_TYPE_P (TREE_TYPE (src))) + if (POINTER_TYPE_P (TREE_TYPE (srcstr))) { /* Try to determine the range of lengths the source string refers to. If it can be determined and is less than - the upper bound given by MAXLEN add one to it for + the upper bound given by MAXREAD add one to it for the terminating nul. Otherwise, set it to one for - the same reason, or to MAXLEN as appropriate. */ - get_range_strlen (src, range); - if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST)) + the same reason, or to MAXREAD as appropriate. */ + get_range_strlen (srcstr, range); + if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST)) { - if (maxlen && tree_int_cst_le (maxlen, range[0])) - range[0] = range[1] = maxlen; + if (maxread && tree_int_cst_le (maxread, range[0])) + range[0] = range[1] = maxread; else range[0] = fold_build2 (PLUS_EXPR, size_type_node, range[0], size_one_node); - if (maxlen && tree_int_cst_le (maxlen, range[1])) - range[1] = maxlen; + if (maxread && tree_int_cst_le (maxread, range[1])) + range[1] = maxread; else if (!integer_all_onesp (range[1])) range[1] = fold_build2 (PLUS_EXPR, size_type_node, range[1], size_one_node); @@ -3112,10 +3120,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) } } else - slen = src; + slen = srcstr; } - if (!size && !maxlen) + if (!dstwrite && !maxread) { /* When the only available piece of data is the object size there is nothing to do. */ @@ -3123,20 +3131,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) return true; /* Otherwise, when the length of the source sequence is known - (as with with strlen), set SIZE to it. */ + (as with strlen), set DSTWRITE to it. */ if (!range[0]) - size = slen; + dstwrite = slen; } - if (!objsize) - objsize = maxobjsize; + if (!dstsize) + dstsize = maxobjsize; - /* The SIZE is exact if it's non-null, constant, and in range of - unsigned HOST_WIDE_INT. */ - bool exactsize = size && tree_fits_uhwi_p (size); + if (dstwrite) + get_size_range (dstwrite, range); - if (size) - get_size_range (size, range); + tree func = get_callee_fndecl (exp); /* First check the number of bytes to be written against the maximum object size. */ @@ -3149,30 +3155,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) warning_at (loc, opt, "%K%qD specified size %E " "exceeds maximum object size %E", - exp, get_callee_fndecl (exp), range[0], maxobjsize); + exp, func, range[0], maxobjsize); else warning_at (loc, opt, "%K%qD specified size between %E and %E " "exceeds maximum object size %E", - exp, get_callee_fndecl (exp), + exp, func, range[0], range[1], maxobjsize); return false; } + /* The number of bytes to write is "exact" if DSTWRITE is non-null, + constant, and in range of unsigned HOST_WIDE_INT. */ + bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite); + /* Next check the number of bytes to be written against the destination object size. */ - if (range[0] || !exactsize || integer_all_onesp (size)) + if (range[0] || !exactwrite || integer_all_onesp (dstwrite)) { if (range[0] - && ((tree_fits_uhwi_p (objsize) - && tree_int_cst_lt (objsize, range[0])) - || (tree_fits_uhwi_p (size) - && tree_int_cst_lt (size, range[0])))) + && ((tree_fits_uhwi_p (dstsize) + && tree_int_cst_lt (dstsize, range[0])) + || (tree_fits_uhwi_p (dstwrite) + && tree_int_cst_lt (dstwrite, range[0])))) { location_t loc = tree_nonartificial_location (exp); loc = expansion_point_location_if_in_system_header (loc); - if (size == slen && at_least_one) + if (dstwrite == slen && at_least_one) { /* This is a call to strcpy with a destination of 0 size and a source of unknown length. The call will write @@ -3180,7 +3190,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) warning_at (loc, opt, "%K%qD writing %E or more bytes into a region " "of size %E overflows the destination", - exp, get_callee_fndecl (exp), range[0], objsize); + exp, func, range[0], dstsize); } else if (tree_int_cst_equal (range[0], range[1])) warning_at (loc, opt, @@ -3189,21 +3199,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) "of size %E overflows the destination") : G_("%K%qD writing %E bytes into a region " "of size %E overflows the destination")), - exp, get_callee_fndecl (exp), range[0], objsize); + exp, func, range[0], dstsize); else if (tree_int_cst_sign_bit (range[1])) { /* Avoid printing the upper bound if it's invalid. */ warning_at (loc, opt, "%K%qD writing %E or more bytes into a region " "of size %E overflows the destination", - exp, get_callee_fndecl (exp), range[0], objsize); + exp, func, range[0], dstsize); } else warning_at (loc, opt, "%K%qD writing between %E and %E bytes into " "a region of size %E overflows the destination", - exp, get_callee_fndecl (exp), range[0], range[1], - objsize); + exp, func, range[0], range[1], + dstsize); /* Return error when an overflow has been detected. */ return false; @@ -3213,11 +3223,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) /* Check the maximum length of the source sequence against the size of the destination object if known, or against the maximum size of an object. */ - if (maxlen) + if (maxread) { - get_size_range (maxlen, range); + get_size_range (maxread, range); + + /* Use the lower end for MAXREAD from now on. */ + if (range[0]) + maxread = range[0]; - if (range[0] && objsize && tree_fits_uhwi_p (objsize)) + if (range[0] && dstsize && tree_fits_uhwi_p (dstsize)) { location_t loc = tree_nonartificial_location (exp); loc = expansion_point_location_if_in_system_header (loc); @@ -3231,40 +3245,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) warning_at (loc, opt, "%K%qD specified bound %E " "exceeds maximum object size %E", - exp, get_callee_fndecl (exp), + exp, func, range[0], maxobjsize); else warning_at (loc, opt, "%K%qD specified bound between %E and %E " "exceeds maximum object size %E", - exp, get_callee_fndecl (exp), + exp, func, range[0], range[1], maxobjsize); return false; } - if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0])) + if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0])) { if (tree_int_cst_equal (range[0], range[1])) warning_at (loc, opt, "%K%qD specified bound %E " "exceeds destination size %E", - exp, get_callee_fndecl (exp), - range[0], objsize); + exp, func, + range[0], dstsize); else warning_at (loc, opt, "%K%qD specified bound between %E and %E " "exceeds destination size %E", - exp, get_callee_fndecl (exp), - range[0], range[1], objsize); + exp, func, + range[0], range[1], dstsize); return false; } } } + /* Check for reading past the end of SRC. */ if (slen - && slen == src - && size && range[0] + && slen == srcstr + && dstwrite && range[0] && tree_int_cst_lt (slen, range[0])) { location_t loc = tree_nonartificial_location (exp); @@ -3274,23 +3289,98 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize) (tree_int_cst_equal (range[0], integer_one_node) ? G_("%K%qD reading %E byte from a region of size %E") : G_("%K%qD reading %E bytes from a region of size %E")), - exp, get_callee_fndecl (exp), range[0], slen); + exp, func, range[0], slen); else if (tree_int_cst_sign_bit (range[1])) { /* Avoid printing the upper bound if it's invalid. */ warning_at (loc, opt, "%K%qD reading %E or more bytes from a region " "of size %E", - exp, get_callee_fndecl (exp), range[0], slen); + exp, func, range[0], slen); } else warning_at (loc, opt, "%K%qD reading between %E and %E bytes from a region " "of size %E", - exp, get_callee_fndecl (exp), range[0], range[1], slen); + exp, func, range[0], range[1], slen); return false; } + /* Check for overlapping copies. Avoid warning if one has already + been issued (unlike -Wstringop-overflow, -Wrestrict is checked + in many places and TREE_NO_WARNING applies to it but not to + the former (there should be a bit for every kind of warning). */ + if (src && dst + && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE + && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK + && !TREE_NO_WARNING (exp)) + { + bool have_sizes = dstwrite && tree_fits_uhwi_p (dstwrite); + + if (maxread) + have_sizes &= tree_fits_uhwi_p (maxread); + else if (slen) + { + /* When DSTWRITE is known, set the maximum number of bytes + to access to DSTWRITE either when the length of the source + sequence is unknown, or when DSTWRITE is less than the length. */ + maxread = (have_sizes + && (at_least_one || tree_int_cst_lt (dstwrite, slen)) + ? dstwrite : slen); + } + else + { + maxread = dstwrite; + have_sizes = false; + } + + /* The exact number bytes copied is known only if the sizes + haven't adjusted earlier on. */ + have_sizes &= !at_least_one || (dstwrite && dstwrite != slen); + + /* Initialize the Alias Oracle references for each of the two + accesses. */ + ao_ref dstref, srcref; + ao_ref_init_from_ptr_and_size (&dstref, dst, dstwrite); + ao_ref_init_from_ptr_and_size (&srcref, src, maxread); + + /* This shouldn't happen. */ + if (dstref.size == -1) + { + dstref.size = BITS_PER_UNIT; + have_sizes = false; + } + + /* And neither should this. */ + if (srcref.size == -1) + { + srcref.size = BITS_PER_UNIT; + have_sizes = false; + } + + HOST_WIDE_INT offset; + if (unsigned HOST_WIDE_INT overlap + = refs_must_alias_p_1 (&dstref, &srcref, &offset)) + { + location_t loc = tree_nonartificial_location (exp); + + warning_at (loc, OPT_Wrestrict, + have_sizes + ? (overlap > 1 + ? G_("%K%qD source sequence overlaps %wu bytes " + "of destination at offset %wi") + : G_("%K%qD source sequence overlaps %wu byte " + "of destination at offset %wi")) + : (overlap > 1 + ? G_("%K%qD source sequence may overlap %wu bytes " + "of destination at offset %wi") + : G_("%K%qD source sequence may overlap %wu byte " + "of destination at offset %wi")), + exp, func, overlap, offset); + return false; + } + } + return true; } @@ -3330,8 +3420,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size) tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE; tree dstsize = compute_objsize (dest, 0); - return check_sizes (OPT_Wstringop_overflow_, exp, - size, /*maxlen=*/NULL_TREE, srcsize, dstsize); + return check_sizes (OPT_Wstringop_overflow_, exp, dest, src, + size, /*maxread=*/NULL_TREE, srcsize, dstsize); } /* Validate memchr arguments without performing any expansion. @@ -3353,8 +3443,8 @@ expand_builtin_memchr (tree exp, rtx) { tree size = compute_objsize (arg1, 0); check_sizes (OPT_Wstringop_overflow_, - exp, len, /*maxlen=*/NULL_TREE, - size, /*objsize=*/NULL_TREE); + exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, + /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE); } return NULL_RTX; @@ -3652,7 +3742,8 @@ expand_builtin_strcat (tree exp, rtx) tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); check_sizes (OPT_Wstringop_overflow_, - exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize); + exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src, + destsize); return NULL_RTX; } @@ -3671,11 +3762,15 @@ expand_builtin_strcpy (tree exp, rtx target) tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); - if (warn_stringop_overflow) + if (warn_stringop_overflow || warn_restrict) { - tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); + /* Use Object Size type-1 for -Wrestrict. */ + int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2; + + tree destsize = compute_objsize (dest, ost); check_sizes (OPT_Wstringop_overflow_, - exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize); + exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, + src, destsize); } return expand_builtin_strcpy_args (dest, src, target); @@ -3714,7 +3809,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode) { tree destsize = compute_objsize (dst, warn_stringop_overflow - 1); check_sizes (OPT_Wstringop_overflow_, - exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize); + exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src, + destsize); } /* If return value is ignored, transform stpcpy into strcpy. */ @@ -3799,7 +3895,7 @@ expand_builtin_stpncpy (tree exp, rtx) tree destsize = compute_objsize (dest, warn_stringop_overflow - 1); check_sizes (OPT_Wstringop_overflow_, - exp, len, /*maxlen=*/NULL_TREE, src, destsize); + exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize); return NULL_RTX; } @@ -3829,7 +3925,7 @@ check_strncat_sizes (tree exp, tree objsize) { tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); - tree maxlen = CALL_EXPR_ARG (exp, 2); + tree maxread = CALL_EXPR_ARG (exp, 2); /* Try to determine the range of lengths that the source expression refers to. */ @@ -3853,32 +3949,33 @@ check_strncat_sizes (tree exp, tree objsize) size_one_node) : NULL_TREE); - /* Strncat copies at most MAXLEN bytes and always appends the terminating + /* Strncat copies at most MAXREAD bytes and always appends the terminating nul so the specified upper bound should never be equal to (or greater than) the size of the destination. */ - if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize) - && tree_int_cst_equal (objsize, maxlen)) + if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize) + && tree_int_cst_equal (objsize, maxread)) { location_t loc = tree_nonartificial_location (exp); loc = expansion_point_location_if_in_system_header (loc); warning_at (loc, OPT_Wstringop_overflow_, "%K%qD specified bound %E equals destination size", - exp, get_callee_fndecl (exp), maxlen); + exp, get_callee_fndecl (exp), maxread); return false; } if (!srclen - || (maxlen && tree_fits_uhwi_p (maxlen) + || (maxread && tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (srclen) - && tree_int_cst_lt (maxlen, srclen))) - srclen = maxlen; + && tree_int_cst_lt (maxread, srclen))) + srclen = maxread; /* The number of bytes to write is LEN but check_sizes will also check SRCLEN if LEN's value isn't known. */ return check_sizes (OPT_Wstringop_overflow_, - exp, /*size=*/NULL_TREE, maxlen, srclen, objsize); + exp, dest, src, /*size=*/NULL_TREE, maxread, srclen, + objsize); } /* Similar to expand_builtin_strcat, do some very basic size validation @@ -3896,7 +3993,7 @@ expand_builtin_strncat (tree exp, rtx) tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); /* The upper bound on the number of bytes to write. */ - tree maxlen = CALL_EXPR_ARG (exp, 2); + tree maxread = CALL_EXPR_ARG (exp, 2); /* The length of the source sequence. */ tree slen = c_strlen (src, 1); @@ -3919,32 +4016,31 @@ expand_builtin_strncat (tree exp, rtx) size_one_node) : NULL_TREE); - /* Strncat copies at most MAXLEN bytes and always appends the terminating + /* Strncat copies at most MAXREAD bytes and always appends the terminating nul so the specified upper bound should never be equal to (or greater than) the size of the destination. */ - if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize) - && tree_int_cst_equal (destsize, maxlen)) + if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize) + && tree_int_cst_equal (destsize, maxread)) { location_t loc = tree_nonartificial_location (exp); loc = expansion_point_location_if_in_system_header (loc); warning_at (loc, OPT_Wstringop_overflow_, "%K%qD specified bound %E equals destination size", - exp, get_callee_fndecl (exp), maxlen); + exp, get_callee_fndecl (exp), maxread); return NULL_RTX; } if (!srclen - || (maxlen && tree_fits_uhwi_p (maxlen) + || (maxread && tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (srclen) - && tree_int_cst_lt (maxlen, srclen))) - srclen = maxlen; + && tree_int_cst_lt (maxread, srclen))) + srclen = maxread; - /* The number of bytes to write is LEN but check_sizes will also - check SRCLEN if LEN's value isn't known. */ + /* The number of bytes to write is SRCLEN. */ check_sizes (OPT_Wstringop_overflow_, - exp, /*size=*/NULL_TREE, maxlen, srclen, destsize); + exp, dest, src, NULL_TREE, maxread, srclen, destsize); return NULL_RTX; } @@ -3975,7 +4071,7 @@ expand_builtin_strncpy (tree exp, rtx target) /* The number of bytes to write is LEN but check_sizes will also check SLEN if LEN's value isn't known. */ check_sizes (OPT_Wstringop_overflow_, - exp, len, /*maxlen=*/NULL_TREE, src, destsize); + exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize); } /* We must be passed a constant len and src parameter. */ @@ -4319,13 +4415,13 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq) { tree size = compute_objsize (arg1, 0); if (check_sizes (OPT_Wstringop_overflow_, - exp, len, /*maxlen=*/NULL_TREE, - size, /*objsize=*/NULL_TREE)) + exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, + /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE)) { size = compute_objsize (arg2, 0); check_sizes (OPT_Wstringop_overflow_, - exp, len, /*maxlen=*/NULL_TREE, - size, /*objsize=*/NULL_TREE); + exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, + /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE); } } @@ -9708,8 +9804,6 @@ static rtx expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode, enum built_in_function fcode) { - tree dest, src, len, size; - if (!validate_arglist (exp, POINTER_TYPE, fcode == BUILT_IN_MEMSET_CHK @@ -9717,13 +9811,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) return NULL_RTX; - dest = CALL_EXPR_ARG (exp, 0); - src = CALL_EXPR_ARG (exp, 1); - len = CALL_EXPR_ARG (exp, 2); - size = CALL_EXPR_ARG (exp, 3); + tree dest = CALL_EXPR_ARG (exp, 0); + tree src = CALL_EXPR_ARG (exp, 1); + tree len = CALL_EXPR_ARG (exp, 2); + tree size = CALL_EXPR_ARG (exp, 3); bool sizes_ok = check_sizes (OPT_Wstringop_overflow_, - exp, len, /*maxlen=*/NULL_TREE, + exp, dest, src, len, /*maxread=*/NULL_TREE, /*str=*/NULL_TREE, size); if (!tree_fits_uhwi_p (size)) @@ -9833,7 +9927,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode) /* The maximum length of the source sequence in a bounded operation (such as __strncat_chk) or null if the operation isn't bounded (such as __strcat_chk). */ - tree maxlen = NULL_TREE; + tree maxread = NULL_TREE; switch (fcode) { @@ -9854,27 +9948,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode) case BUILT_IN_STRNCAT_CHK: catstr = CALL_EXPR_ARG (exp, 0); srcstr = CALL_EXPR_ARG (exp, 1); - maxlen = CALL_EXPR_ARG (exp, 2); + maxread = CALL_EXPR_ARG (exp, 2); objsize = CALL_EXPR_ARG (exp, 3); break; case BUILT_IN_STRNCPY_CHK: case BUILT_IN_STPNCPY_CHK: srcstr = CALL_EXPR_ARG (exp, 1); - maxlen = CALL_EXPR_ARG (exp, 2); + maxread = CALL_EXPR_ARG (exp, 2); objsize = CALL_EXPR_ARG (exp, 3); break; case BUILT_IN_SNPRINTF_CHK: case BUILT_IN_VSNPRINTF_CHK: - maxlen = CALL_EXPR_ARG (exp, 1); + maxread = CALL_EXPR_ARG (exp, 1); objsize = CALL_EXPR_ARG (exp, 3); break; default: gcc_unreachable (); } - if (catstr && maxlen) + if (catstr && maxread) { /* Check __strncat_chk. There is no way to determine the length of the string to which the source string is being appended so @@ -9883,8 +9977,11 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode) return; } - check_sizes (OPT_Wstringop_overflow_, exp, - /*size=*/NULL_TREE, maxlen, srcstr, objsize); + /* The destination argument is the first one for all built-ins above. */ + tree dst = CALL_EXPR_ARG (exp, 0); + + check_sizes (OPT_Wstringop_overflow_, exp, dst, srcstr, + /*size=*/NULL_TREE, maxread, srcstr, objsize); } /* Emit warning if a buffer overflow is detected at compile time @@ -9940,8 +10037,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode) /* Add one for the terminating nul. */ len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node); + check_sizes (OPT_Wstringop_overflow_, - exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size); + exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE, + /*maxread=*/NULL_TREE, len, size); } /* Emit warning if a free is called with address of a variable. */ diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index feb0904..c86a393 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -5261,14 +5261,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype, int nargs, tree *argarray) { int i; - tree parms; + tree parms = TYPE_ARG_TYPES (fntype); if (fndecl - && TREE_CODE (fndecl) == FUNCTION_DECL - && DECL_ARGUMENTS (fndecl)) - parms = DECL_ARGUMENTS (fndecl); - else - parms = TYPE_ARG_TYPES (fntype); + && TREE_CODE (fndecl) == FUNCTION_DECL) + { + /* Skip checking built-ins here. They are checked in more + detail elsewhere. */ + if (DECL_BUILT_IN (fndecl) + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + return; + + if (DECL_ARGUMENTS (fndecl)) + parms = DECL_ARGUMENTS (fndecl); + } for (i = 0; i < nargs; i++) TREE_VISITED (argarray[i]) = 0; diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index e0ad3ab..01506fd 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1162,7 +1162,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall) Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier. Wrestrict -C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++) +C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall) Warn when an argument passed to a restrict-qualified parameter aliases with another argument. diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 3e1d24d..76b9b15 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -2627,6 +2627,9 @@ expand_call_stmt (gcall *stmt) if (gimple_call_nothrow_p (stmt)) TREE_NOTHROW (exp) = 1; + if (gimple_no_warning_p (stmt)) + TREE_NO_WARNING (exp) = 1; + CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt); CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt); CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index b9d071b..14205f5 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -3852,6 +3852,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}. -Wparentheses @gol -Wpointer-sign @gol -Wreorder @gol +-Wrestrict @gol -Wreturn-type @gol -Wsequence-point @gol -Wsign-compare @r{(only in C++)} @gol @@ -6535,11 +6536,25 @@ reduce the padding and so make the structure smaller. Warn if anything is declared more than once in the same scope, even in cases where multiple declaration is valid and changes nothing. -@item -Wrestrict +@item -Wno-restrict @opindex Wrestrict @opindex Wno-restrict -Warn when an argument passed to a restrict-qualified parameter -aliases with another argument. +Warn when an object referenced by a @code{restrict}-qualified parameter +(or, in C++, @code{__restrict}-qualified parameter) is aliased by another +argument, or when copies between such objects overlap. For example, +the call to the @code{strcpy} unction below attempts to truncate the string +by replacing its initial characters with the last four. However, because +he call writes the terminating NUL into @code{a[4]}, the copies overlap and +the call is diagnosed. + +@smallexample +struct foo +@{ + char a[] = "abcd1234"; + strcpy (a, a + 4); +@}; +@end smallexample +The @option{-Wrestrict} is included in @option{-Wall}. @item -Wnested-externs @r{(C and Objective-C only)} @opindex Wnested-externs diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index d94dc9c..f55cffa 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -57,6 +57,8 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfg.h" #include "fold-const-call.h" #include "asan.h" +#include "intl.h" +#include "diagnostic-core.h" /* Return true when DECL can be referenced from current unit. FROM_DECL (if non-null) specify constructor of variable DECL was taken from. @@ -644,7 +646,13 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi, tree destvar, srcvar; location_t loc = gimple_location (stmt); - /* If the LEN parameter is zero, return DEST. */ + tree func = gimple_call_fndecl (stmt); + bool nowarn = gimple_no_warning_p (stmt); + bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE + && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK + && !nowarn); + + /* If the LEN parameter is zero, return DEST. */ if (integer_zerop (len)) { gimple *repl; @@ -666,6 +674,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi, DEST{,+LEN,+LEN-1}. */ if (operand_equal_p (src, dest, 0)) { + if (check_overlap) + warning_at (loc, OPT_Wrestrict, + "%qD source argument is the same as destination", + func); + unlink_stmt_vdef (stmt); if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME) release_ssa_name (gimple_vdef (stmt)); @@ -715,6 +728,40 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi, unsigned ilen = tree_to_uhwi (len); if (pow2p_hwi (ilen)) { + if (check_overlap) + { + ao_ref dstref, srcref; + ao_ref_init_from_ptr_and_size (&dstref, dest, len); + ao_ref_init_from_ptr_and_size (&srcref, src, len); + + if (refs_may_alias_p_1 (&dstref, &srcref, false)) + { + /* Defer folding until both arguments are in an SSA + form so that aliasing can be determined with greater + accuracy. */ + if (TREE_CODE (dest) != SSA_NAME + || TREE_CODE (src) != SSA_NAME) + return false; + + HOST_WIDE_INT offset; + if (unsigned HOST_WIDE_INT size + = refs_must_alias_p_1 (&dstref, &srcref, &offset)) + { + warning_at (loc, OPT_Wrestrict, + size > 1 + ? G_("%qD source sequence overlaps " + "%wu bytes of destination at offset " + "%wi") + : G_("%qD source sequence may overlap " + "%wu byte of destination at offset " + "%wi"), + func, size, offset); + gimple_set_no_warning (stmt, true); + return false; + } + } + } + tree type = lang_hooks.types.type_for_size (ilen * 8, 1); if (type && TYPE_MODE (type) != BLKmode @@ -1028,6 +1075,29 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi, } } + if (check_overlap) + { + ao_ref dstref, srcref; + ao_ref_init_from_ptr_and_size (&dstref, dest, len); + ao_ref_init_from_ptr_and_size (&srcref, src, len); + + HOST_WIDE_INT offset; + if (unsigned HOST_WIDE_INT overlap + = refs_must_alias_p_1 (&dstref, &srcref, &offset)) + { + warning_at (loc, OPT_Wrestrict, + overlap > 1 + ? G_("%qD source sequence overlaps %wu bytes of " + "destination at offset %wi") + : G_("%qD source sequence overlaps %wu byte of " + "destination at offset %wi"), + func, overlap, offset); + + gimple_set_no_warning (stmt, true); + return false; + } + } + gimple *new_stmt; if (is_gimple_reg_type (TREE_TYPE (srcvar))) { @@ -1484,12 +1554,19 @@ static bool gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi, tree dest, tree src) { - location_t loc = gimple_location (gsi_stmt (*gsi)); + gimple *stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); tree fn; /* If SRC and DEST are the same (and not volatile), return DEST. */ if (operand_equal_p (src, dest, 0)) { + tree func = gimple_call_fndecl (stmt); + + warning_at (loc, OPT_Wrestrict, + "%qD source argument is the same as destination", + func); + replace_call_with_value (gsi, dest); return true; } @@ -2272,6 +2349,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi, (resp. DEST+LEN for __mempcpy_chk). */ if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0)) { + if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK) + { + tree func = gimple_call_fndecl (stmt); + + warning_at (loc, OPT_Wrestrict, + "%qD source argument is the same as destination", + func); + } + if (fcode != BUILT_IN_MEMPCPY_CHK) { replace_call_with_value (gsi, dest); @@ -2373,6 +2459,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi, /* If SRC and DEST are the same (and not volatile), return DEST. */ if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0)) { + tree func = gimple_call_fndecl (stmt); + + warning_at (loc, OPT_Wrestrict, + "%qD source argument is the same as destination", + func); + replace_call_with_value (gsi, dest); return true; } diff --git a/gcc/gimple.c b/gcc/gimple.c index 488f8c8..f2dcece 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t) gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i)); gimple_set_block (call, TREE_BLOCK (t)); + gimple_set_location (call, EXPR_LOCATION (t)); /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL. */ gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t)); diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c new file mode 100644 index 0000000..729ced5 --- /dev/null +++ b/gcc/testsuite/c-c++-common/Wrestrict.c @@ -0,0 +1,424 @@ +/* PR 35503 - Warn about restricted pointers + { dg-do compile } + { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */ + +#if __cplusplus +# define restrict __restrict +extern "C" { +#endif + +typedef __SIZE_TYPE__ size_t; + +extern void* memcpy (void* restrict, const void* restrict, size_t); +extern void* mempcpy (void* restrict, const void* restrict, size_t); +extern void* memmove (void*, const void*, size_t); +extern char* strcat (char* restrict, const char* restrict); +extern char* strcpy (char* restrict, const char* restrict); +extern char* strncpy (char* restrict, const char* restrict, size_t); + +#if __cplusplus +} /* extern "C" */ +#endif + +int value (void); + +int range (int min, int max) +{ + int val = value (); + return val < min || max < val ? min : val; +} + +void sink (void*); + +/* Exercise memcpy with constant or known arguments. */ + +void test_memcpy_cst (void) +{ + { + char d[7]; + sink (d); + + const char *s = d; + memcpy (d, s, 0); + sink (d); + } + + { + char d[7]; + sink (d); + + const char *s = d; + memcpy (d, s, 1); /* { dg-warning "\\\[-Wrestrict" } */ + sink (d); + } + + { + char d[7]; + sink (d); + + const char *s = d + 1; + memcpy (d, s, 1); + sink (d); + } + + { + char d[7]; + sink (d); + + const char *s = d + 1; + memcpy (d, s, 2); /* { dg-warning "\\\[-Wrestrict" } */ + sink (d); + } + + { + char a[3][7]; + sink (a); + + void *d = a[0]; + const void *s = a[1]; + memcpy (d, s, sizeof a[0]); + sink (&a); + + d = a[0]; + s = a[1]; + memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */ + sink (&a); + + d = a[0] + 1; + s = a[1] + 1; + memcpy (d, s, sizeof a[0]); + sink (&a); + + d = a[0] + 1; + s = a[1] + 1; + memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */ + sink (&a); + } + + { + struct { + char a[7]; + char b[7]; + char c[7]; + } x; + sink (&x); + + void *d = x.a; + const void *s = x.b; + memcpy (d, s, sizeof x.a); + sink (&x); + + d = x.a; + s = x.a; + memcpy (d, s, sizeof x.a); /* { dg-warning "\\\[-Wrestrict" } */ + sink (&x); + + d = x.a + 4; + s = x.b; + memcpy (d, s, sizeof x.a); /* { dg-warning "\\\[-Wrestrict" } */ + sink (&x); + + d = x.a + 6; + s = x.b; + memcpy (d, s, 1); + sink (&x); + + d = x.a + 7; + s = x.b; + memcpy (d, s, 1); /* { dg-warning "\\\[-Wrestrict" } */ + sink (&x); + + d = x.a + 7; + s = x.b + 1; + memcpy (d, s, 1); + sink (&x); + + d = x.b; + s = x.a; + memcpy (d, s, 1); + sink (&x); + + d = x.b; + s = x.a; + memcpy (d, s, sizeof x.b); + sink (&x); + + d = x.b + 2; + s = x.a + 1; + memcpy (d, s, sizeof x.b); + sink (&x); + + d = x.b + 2; + s = x.a + 2; + memcpy (d, s, sizeof x.b); + sink (&x); + + d = x.b + 2; + s = x.a + 3; + memcpy (d, s, sizeof x.b); /* { dg-warning "\\\[-Wrestrict" } */ + sink (&x); + } +} + +/* Exercise memcpy with destination and source of unknown size. */ + +void test_memcpy_var (char *d, const char *s) +{ + int n = value (); + + memcpy (d, d, n); /* { dg-warning "\\\[-Wrestrict" } */ + sink (d); + + memcpy (d, &d[0], n); /* { dg-warning "\\\[-Wrestrict" } */ + sink (d); + + memcpy (&d[0], d, n); /* { dg-warning "\\\[-Wrestrict" } */ + sink (d); + + s = d; + memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" } */ + sink (d); + + /* The following overlaps if n is greater than 1. */ + s = d + 1; + memcpy (d, s, n); + sink (d); + + /* The following only overlaps if strlen (s + i) >= i so it's not + diagnosed. */ + s = d + n; + memcpy (d, s, n); + sink (d); + + s = d + 3; + n = 1; + memcpy (d, s, n); + sink (d); + + s = d + 3; + n = 2; + memcpy (d, s, n); + sink (d); + + s = d + 3; + n = 3; + memcpy (d, s, n); + sink (d); + + s = d + 3; + n = 4; + memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" } */ + sink (d); + + s = d + 5; + n = 7; + memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" } */ + + n = range (0, 1); + s = d; + memcpy (d, s, n); /* { dg-warning "\\\[-Wrestrict" } */ +} + +/* Exercise the absence of warnings with memmove. */ + +void test_memmove (void) +{ + { + char d[7]; + sink (d); + + const void *s = d; + memmove (d, s, 7); + sink (d); + + s = d + 1; + memmove (d, s, 6); + sink (d); + + s = d + 2; + memmove (d + 1, s, 5); + sink (d); + } +} + +/* Exercise strcat with constant or known arguments. */ + +void test_strcat_cst (const char *s) +{ +#undef T +#define T(init, dst, src) do { \ + char a[9] = init; \ + strcat ((dst), (src)); \ + sink (a); \ + } while (0) + + T ("123", a, a); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a, a + 1); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a, a + 2); /* { dg-warning "\\\[-Wrestrict" } */ + /* The nul copied from a[3] to a[3] overwrites itself so this is + diagnosed. */ + T ("123", a, a + 3); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a, a + 4); + T ("123", a, a + 5); + T ("123", a, a + 6); + T ("123", a, a + 7); + T ("123", a, a + 8); + + T ("123", a + 1, a); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a + 2, a); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a + 3, a); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a + 4, a); /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */ + T ("123", a + 5, a); /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */ + + /* Verify that the obviously benign cases below aren't diagnosed. */ + T ("123", a, "123"); + T ("123", a, s); + T ("12345678", a, s); +} + +/* Exercise strcat with destination and source of unknown length. */ + +void test_strcat_var (char *d, const char *s) +{ +#undef T +#define T(dst, src) do { \ + strcat ((dst), (src)); \ + sink ((dst)); \ + } while (0) + + T (d, d); /* { dg-warning "\\\[-Wrestrict" } */ + T (d, d + 1); /* { dg-warning "\\\[-Wrestrict" } */ + T (d, d + 2); /* { dg-warning "\\\[-Wrestrict" } */ + T (d, d + 3); + + int n = value (); + + /* Verify that the obviously benign cases below aren't diagnosed. */ + T (d, "123"); + T (d + 1, "1234"); + T (d + n, "12345"); + T (d, s); + T (d + 1, s); + T (d + n, s); + + /* This one isn't so obvious and might be worth diagnosing with "may + overlap," even more so than the analogous strcpy case. */ + T (d, d + n); +} + +/* Exercise strcpy with constant or known arguments. */ + +void test_strcpy_cst (void) +{ +#undef T +#define T(init, dst, src) do { \ + char a[8] = init; \ + strcpy ((dst), (src)); \ + sink (a); \ + } while (0) + + T ("123", a, a); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a, a + 1); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a, a + 2); + T ("123", a, a + 3); + + /* The terminating nul written to d[2] overwrites s[0]. */ + T ("1234", a, a + 2); /* { dg-warning "\\\[-Wrestrict" } */ + + /* The '5' copied from s[2] to d[2] overwrites s[0]. */ + T ("12345", a, a + 2); /* { dg-warning "\\\[-Wrestrict" } */ + + /* It's not 100% clear if this should trigger a warning. The non-const + string case doesn't. */ + T ("123", a, a + value ()); + + /* This happens to be safe in GCC but it's still wrong. */ + T ("123", a, a); /* { dg-warning "\\\[-Wrestrict" } */ + + T ("123", a + 1, a); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a + 2, a); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a + 3, a); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a + 4, a); +} + +/* Exercise strcpy with destination and source of unknown length. */ + +void test_strcpy_var (char *d, const char *s) +{ +#undef T +#define T(dst, src) do { \ + strcpy ((dst), (src)); \ + sink (dst); \ + } while (0) + + T (d, &d[0]); /* { dg-warning "\\\[-Wrestrict" } */ + T (&d[0], d); /* { dg-warning "\\\[-Wrestrict" } */ + + s = d; + T (d, s); /* { dg-warning "\\\[-Wrestrict" } */ + + /* The following overlaps if *s is not nul. It arguably should be + diagnosed. */ + T (d, d + 1); + + /* The following overlaps only if strlen (s + n) >= n so it's not + diagnosed. */ + s = d + value (); + T (d, s); +} + +/* Exercise strncpy with constant or known arguments. */ + +void test_strncpy_cst (void) +{ +#undef T +#define T(init, dst, src, size) do { \ + char a[7] = init; \ + strncpy (dst, src, size); \ + sink (a); \ + } while (0) + + T ("123", a, a, 0); + T ("123", a, a, 1); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a, a + 1, 1); + T ("123", a, a + 1, 2); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a, a + 1, 3); /* { dg-warning "\\\[-Wrestrict" } */ + T ("123", a, a + 2, 1); + T ("123", a, a + 2, 2); + /* The third written byte (nul) overwrites a[2]. */ + T ("123", a, a + 2, 3); /* { dg-warning "\\\[-Wrestrict" } */ + + T ("1234", a, a + 2, 1); + T ("1234", a, a + 2, 2); + /* The terminating nul written to a[2] overwrites s[0]. */ + T ("1234", a, a + 2, 3); /* { dg-warning "\\\[-Wrestrict" } */ + + T ("12345", a, a + 2, 1); + T ("12345", a, a + 2, 2); + /* The '5' copied from s[2] to d[2] overwrites s[0]. */ + T ("12345", a, a + 2, 4); /* { dg-warning "\\\[-Wrestrict" } */ +} + +/* Exercise strncpy with destination and source of unknown length. */ + +void test_strncpy_var (char *d, const char *s) +{ +#undef T +#define T(dst, src, size) do { \ + strncpy (dst, src, size); \ + sink ((dst)); \ + } while (0) + + int n = value (); + + T (d, d, 1); /* { dg-warning "\\\[-Wrestrict" } */ + T (d, d, n); /* { dg-warning "\\\[-Wrestrict" } */ + + T (d, s, 1); + T (d, s, n); + + T (d, d + 1, 1); + T (d, d + 1, 2); /* { dg-warning "\\\[-Wrestrict" } */ + T (d + 1, d, 1); + T (d + 1, d, 2); /* { dg-warning "\\\[-Wrestrict" } */ +} diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c index ad39373..85e9160 100644 --- a/gcc/testsuite/gcc.dg/Walloca-1.c +++ b/gcc/testsuite/gcc.dg/Walloca-1.c @@ -1,6 +1,6 @@ /* { dg-do compile } */ /* { dg-require-effective-target alloca } */ -/* { dg-options "-Walloca-larger-than=2000 -O2" } */ +/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */ #define alloca __builtin_alloca diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c index c0e7463..908d5a6 100644 --- a/gcc/testsuite/gcc.dg/pr69172.c +++ b/gcc/testsuite/gcc.dg/pr69172.c @@ -1,4 +1,5 @@ -/* PR tree-optimization/69172 */ +/* PR tree-optimization/69172 - ICE in make_ssa_name_fn, + at tree-ssanames.c:266 */ /* { dg-do compile } */ /* { dg-options "-O2" } */ @@ -43,3 +44,7 @@ f6 (int x) { return __builtin___mempcpy_chk (&a, &a, x, 0); } + +/* The calls above violate strict aliasing. Eliminate the -Wrestrict + warnings they trigger. + { dg-prune-output "\\\[-Wrestrict]" } */ diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index 74ee2b0..21f6c61 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -149,12 +149,13 @@ ptr_deref_may_alias_global_p (tree ptr) return pt_solution_includes_global (&pi->pt); } -/* Return true if dereferencing PTR may alias DECL. +/* Return true if dereferencing PTR may or alias DECL. When MUST_ALIAS + is true, return true only if PTR definitely aliases DECL. The caller is responsible for applying TBAA to see if PTR may access DECL at all. */ static bool -ptr_deref_may_alias_decl_p (tree ptr, tree decl) +ptr_deref_alias_decl_p (tree ptr, tree decl, bool must_alias = false) { struct ptr_info_def *pi; @@ -170,7 +171,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl) || (!VAR_P (decl) && TREE_CODE (decl) != PARM_DECL && TREE_CODE (decl) != RESULT_DECL)) - return true; + return !must_alias; /* Disregard pointer offsetting. */ if (TREE_CODE (ptr) == POINTER_PLUS_EXPR) @@ -180,7 +181,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl) ptr = TREE_OPERAND (ptr, 0); } while (TREE_CODE (ptr) == POINTER_PLUS_EXPR); - return ptr_deref_may_alias_decl_p (ptr, decl); + return ptr_deref_alias_decl_p (ptr, decl, must_alias); } /* ADDR_EXPR pointers either just offset another pointer or directly @@ -215,12 +216,13 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl) return pt_solution_includes (&pi->pt, decl); } -/* Return true if dereferenced PTR1 and PTR2 may alias. +/* Return true if dereferenced PTR1 and PTR2 may alias. When MUST_ALIAS + is true, return true only if PTR1 and PTR2 definitely alias. The caller is responsible for applying TBAA to see if accesses through PTR1 and PTR2 may conflict at all. */ -bool -ptr_derefs_may_alias_p (tree ptr1, tree ptr2) +static bool +ptr_derefs_alias_p (tree ptr1, tree ptr2, bool must_alias) { struct ptr_info_def *pi1, *pi2; @@ -237,7 +239,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2) ptr1 = TREE_OPERAND (ptr1, 0); } while (TREE_CODE (ptr1) == POINTER_PLUS_EXPR); - return ptr_derefs_may_alias_p (ptr1, ptr2); + return ptr_derefs_alias_p (ptr1, ptr2, must_alias); } if (TREE_CODE (ptr2) == POINTER_PLUS_EXPR) { @@ -246,7 +248,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2) ptr2 = TREE_OPERAND (ptr2, 0); } while (TREE_CODE (ptr2) == POINTER_PLUS_EXPR); - return ptr_derefs_may_alias_p (ptr1, ptr2); + return ptr_derefs_alias_p (ptr1, ptr2, must_alias); } /* ADDR_EXPR pointers either just offset another pointer or directly @@ -257,12 +259,12 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2) if (base && (TREE_CODE (base) == MEM_REF || TREE_CODE (base) == TARGET_MEM_REF)) - return ptr_derefs_may_alias_p (TREE_OPERAND (base, 0), ptr2); + return ptr_derefs_alias_p (TREE_OPERAND (base, 0), ptr2, must_alias); else if (base && DECL_P (base)) - return ptr_deref_may_alias_decl_p (ptr2, base); + return ptr_deref_alias_decl_p (ptr2, base, must_alias); else - return true; + return !must_alias; } if (TREE_CODE (ptr2) == ADDR_EXPR) { @@ -270,12 +272,12 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2) if (base && (TREE_CODE (base) == MEM_REF || TREE_CODE (base) == TARGET_MEM_REF)) - return ptr_derefs_may_alias_p (ptr1, TREE_OPERAND (base, 0)); + return ptr_derefs_alias_p (ptr1, TREE_OPERAND (base, 0), must_alias); else if (base && DECL_P (base)) - return ptr_deref_may_alias_decl_p (ptr1, base); + return ptr_deref_alias_decl_p (ptr1, base, must_alias); else - return true; + return !must_alias; } /* From here we require SSA name pointers. Anything else aliases. */ @@ -283,7 +285,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2) || TREE_CODE (ptr2) != SSA_NAME || !POINTER_TYPE_P (TREE_TYPE (ptr1)) || !POINTER_TYPE_P (TREE_TYPE (ptr2))) - return true; + return !must_alias; /* We may end up with two empty points-to solutions for two same pointers. In this case we still want to say both pointers alias, so shortcut @@ -296,11 +298,21 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2) pi1 = SSA_NAME_PTR_INFO (ptr1); pi2 = SSA_NAME_PTR_INFO (ptr2); if (!pi1 || !pi2) - return true; + return !must_alias; /* ??? This does not use TBAA to prune decls from the intersection that not both pointers may access. */ - return pt_solutions_intersect (&pi1->pt, &pi2->pt); + return pt_solutions_intersect (&pi1->pt, &pi2->pt, must_alias); +} + +/* Return true if dereferenced PTR1 and PTR2 may alias. + The caller is responsible for applying TBAA to see if accesses + through PTR1 and PTR2 may conflict at all. */ + +bool +ptr_derefs_may_alias_p (tree ptr1, tree ptr2) +{ + return ptr_derefs_alias_p (ptr1, ptr2, false); } /* Return true if dereferencing PTR may alias *REF. @@ -316,7 +328,7 @@ ptr_deref_may_alias_ref_p_1 (tree ptr, ao_ref *ref) || TREE_CODE (base) == TARGET_MEM_REF) return ptr_derefs_may_alias_p (ptr, TREE_OPERAND (base, 0)); else if (DECL_P (base)) - return ptr_deref_may_alias_decl_p (ptr, base); + return ptr_deref_alias_decl_p (ptr, base, false); return true; } @@ -773,9 +785,11 @@ same_type_for_tbaa (tree type1, tree type2) on an indirect reference may alias. REF2 is the only one that can be a decl in which case REF2_IS_DECL is true. REF1_ALIAS_SET, BASE1_ALIAS_SET, REF2_ALIAS_SET and BASE2_ALIAS_SET - are the respective alias sets. */ + are the respective alias sets. + The returned value is the size of the overlap if it can be determined, + otherwise it's HOST_WIDE_INT_M1U. */ -static bool +static unsigned HOST_WIDE_INT aliasing_component_refs_p (tree ref1, alias_set_type ref1_alias_set, alias_set_type base1_alias_set, @@ -784,7 +798,7 @@ aliasing_component_refs_p (tree ref1, alias_set_type ref2_alias_set, alias_set_type base2_alias_set, HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2, - bool ref2_is_decl) + bool ref2_is_decl, HOST_WIDE_INT *aloff) { /* If one reference is a component references through pointers try to find a common base and apply offset based disambiguation. This handles @@ -816,7 +830,7 @@ aliasing_component_refs_p (tree ref1, same_p = same_type_for_tbaa (TREE_TYPE (*refp), type1); /* If we couldn't compare types we have to bail out. */ if (same_p == -1) - return true; + return HOST_WIDE_INT_M1U; else if (same_p == 1) { HOST_WIDE_INT offadj, sztmp, msztmp; @@ -825,7 +839,7 @@ aliasing_component_refs_p (tree ref1, offset2 -= offadj; get_ref_base_and_extent (base1, &offadj, &sztmp, &msztmp, &reverse); offset1 -= offadj; - return ranges_overlap_p (offset1, max_size1, offset2, max_size2); + return range_overlap (offset1, max_size1, offset2, max_size2, aloff); } /* If we didn't find a common base, try the other way around. */ refp = &ref1; @@ -844,7 +858,7 @@ aliasing_component_refs_p (tree ref1, offset1 -= offadj; get_ref_base_and_extent (base2, &offadj, &sztmp, &msztmp, &reverse); offset2 -= offadj; - return ranges_overlap_p (offset1, max_size1, offset2, max_size2); + return range_overlap (offset1, max_size1, offset2, max_size2, aloff); } /* If we have two type access paths B1.path1 and B2.path2 they may @@ -855,12 +869,12 @@ aliasing_component_refs_p (tree ref1, tail of path2. */ if (base1_alias_set == ref2_alias_set || alias_set_subset_of (base1_alias_set, ref2_alias_set)) - return true; + return HOST_WIDE_INT_M1U; /* If this is ptr vs. decl then we know there is no ptr ... decl path. */ if (!ref2_is_decl) return (base2_alias_set == ref1_alias_set || alias_set_subset_of (base2_alias_set, ref1_alias_set)); - return false; + return 0; } /* Return true if we can determine that component references REF1 and REF2, @@ -1088,21 +1102,25 @@ nonoverlapping_component_refs_p (const_tree x, const_tree y) [OFFSET2, OFFSET2 + MAX_SIZE2) may alias. REF1 and REF2 if non-NULL are the complete memory reference trees. */ -static bool +static unsigned HOST_WIDE_INT decl_refs_may_alias_p (tree ref1, tree base1, HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1, tree ref2, tree base2, - HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2) + HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2, + HOST_WIDE_INT *aloff) { gcc_checking_assert (DECL_P (base1) && DECL_P (base2)); /* If both references are based on different variables, they cannot alias. */ if (compare_base_decls (base1, base2) == 0) - return false; + return 0; /* If both references are based on the same variable, they cannot alias if the accesses do not overlap. */ - if (!ranges_overlap_p (offset1, max_size1, offset2, max_size2)) + HOST_WIDE_INT overlap + = range_overlap (offset1, max_size1, offset2, max_size2, aloff); + + if (!overlap) return false; /* For components with variable position, the above test isn't sufficient, @@ -1110,9 +1128,9 @@ decl_refs_may_alias_p (tree ref1, tree base1, if (ref1 && ref2 && handled_component_p (ref1) && handled_component_p (ref2) && nonoverlapping_component_refs_of_decl_p (ref1, ref2)) - return false; + return 0; - return true; + return overlap; } /* Return true if an indirect reference based on *PTR1 constrained @@ -1122,16 +1140,17 @@ decl_refs_may_alias_p (tree ref1, tree base1, in which case they are computed on-demand. REF1 and REF2 if non-NULL are the complete memory reference trees. */ -static bool -indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, - HOST_WIDE_INT offset1, - HOST_WIDE_INT max_size1 ATTRIBUTE_UNUSED, - alias_set_type ref1_alias_set, - alias_set_type base1_alias_set, - tree ref2 ATTRIBUTE_UNUSED, tree base2, - HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2, - alias_set_type ref2_alias_set, - alias_set_type base2_alias_set, bool tbaa_p) +static unsigned HOST_WIDE_INT +indirect_ref_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, + HOST_WIDE_INT offset1, + HOST_WIDE_INT max_size1 ATTRIBUTE_UNUSED, + alias_set_type ref1_alias_set, + alias_set_type base1_alias_set, + tree ref2 ATTRIBUTE_UNUSED, tree base2, + HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2, + alias_set_type ref2_alias_set, + alias_set_type base2_alias_set, bool tbaa_p, + HOST_WIDE_INT *aloff) { tree ptr1; tree ptrtype1, dbase2; @@ -1161,20 +1180,20 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, so do not apply this optimization for TARGET_MEM_REFs. */ if (TREE_CODE (base1) != TARGET_MEM_REF && !ranges_overlap_p (MAX (0, offset1p), -1, offset2p, max_size2)) - return false; + return 0; /* They also cannot alias if the pointer may not point to the decl. */ - if (!ptr_deref_may_alias_decl_p (ptr1, base2)) - return false; + if (!ptr_deref_alias_decl_p (ptr1, base2, aloff != 0)) + return 0; /* Disambiguations that rely on strict aliasing rules follow. */ if (!flag_strict_aliasing || !tbaa_p) - return true; + return aloff ? 0 : HOST_WIDE_INT_M1U; ptrtype1 = TREE_TYPE (TREE_OPERAND (base1, 1)); /* If the alias set for a pointer access is zero all bets are off. */ if (base1_alias_set == 0) - return true; + return aloff ? 0 : HOST_WIDE_INT_M1U; /* When we are trying to disambiguate an access with a pointer dereference as base versus one with a decl as base we can use both the size @@ -1190,7 +1209,7 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, !alias_set_subset_of (base1_alias_set, base2_alias_set) instead. */ if (base1_alias_set != base2_alias_set && !alias_sets_conflict_p (base1_alias_set, base2_alias_set)) - return false; + return 0; /* If the size of the access relevant for TBAA through the pointer is bigger than the size of the decl we can't possibly access the decl via that pointer. */ @@ -1203,10 +1222,10 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, && TREE_CODE (TREE_TYPE (ptrtype1)) != UNION_TYPE && TREE_CODE (TREE_TYPE (ptrtype1)) != QUAL_UNION_TYPE && tree_int_cst_lt (DECL_SIZE (base2), TYPE_SIZE (TREE_TYPE (ptrtype1)))) - return false; + return 0; if (!ref2) - return true; + return aloff ? 0 : HOST_WIDE_INT_M1U; /* If the decl is accessed via a MEM_REF, reconstruct the base we can use for TBAA and an appropriately adjusted offset. */ @@ -1229,7 +1248,7 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, /* If either reference is view-converted, give up now. */ if (same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) != 1 || same_type_for_tbaa (TREE_TYPE (dbase2), TREE_TYPE (base2)) != 1) - return true; + return aloff ? 0 : HOST_WIDE_INT_M1U; /* If both references are through the same type, they do not alias if the accesses do not overlap. This does extra disambiguation @@ -1241,11 +1260,11 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, if ((TREE_CODE (base1) != TARGET_MEM_REF || (!TMR_INDEX (base1) && !TMR_INDEX2 (base1))) && same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (dbase2)) == 1) - return ranges_overlap_p (doffset1, max_size1, doffset2, max_size2); + return range_overlap (doffset1, max_size1, doffset2, max_size2, aloff); if (ref1 && ref2 && nonoverlapping_component_refs_p (ref1, ref2)) - return false; + return 0; /* Do access-path based disambiguation. */ if (ref1 && ref2 @@ -1255,27 +1274,32 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, offset1, max_size1, ref2, ref2_alias_set, base2_alias_set, - offset2, max_size2, true); + offset2, max_size2, true, aloff); - return true; + return aloff ? 0 : HOST_WIDE_INT_M1U; } -/* Return true if two indirect references based on *PTR1 - and *PTR2 constrained to [OFFSET1, OFFSET1 + MAX_SIZE1) and - [OFFSET2, OFFSET2 + MAX_SIZE2) may alias. *PTR1 and *PTR2 have - the alias sets BASE1_ALIAS_SET and BASE2_ALIAS_SET which can be -1 - in which case they are computed on-demand. REF1 and REF2 - if non-NULL are the complete memory reference trees. */ - -static bool -indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, - HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1, - alias_set_type ref1_alias_set, - alias_set_type base1_alias_set, - tree ref2 ATTRIBUTE_UNUSED, tree base2, - HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2, - alias_set_type ref2_alias_set, - alias_set_type base2_alias_set, bool tbaa_p) +/* Return non-zero if two indirect references based on *PTR1 and *PTR2 + constrained to [OFFSET1, OFFSET1 + MAX_SIZE1) and [OFFSET2, OFFSET2 + + MAX_SIZE2) may alias. *PTR1 and *PTR2 have the alias sets + BASE1_ALIAS_SET and BASE2_ALIAS_SET which can be -1 in which case + they are computed on-demand. If non-NULL, REF1 and REF2 are the + complete memory reference trees. + The returned value is the size of the overlap if it can be determined, + otherwise it's HOST_WIDE_INT_M1U. When ALOFF is non-null, the returned + value is non-zero if and only if REF1 and REF2 definitely overlap, and + *ALOFF is set to the offset of the overlap in REF1. */ + +static unsigned HOST_WIDE_INT +indirect_refs_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, + HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1, + alias_set_type ref1_alias_set, + alias_set_type base1_alias_set, + tree ref2 ATTRIBUTE_UNUSED, tree base2, + HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2, + alias_set_type ref2_alias_set, + alias_set_type base2_alias_set, bool tbaa_p, + HOST_WIDE_INT *aloff) { tree ptr1; tree ptr2; @@ -1328,14 +1352,14 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, offset1 += (-moff).to_short_addr (); else offset2 += moff.to_short_addr (); - return ranges_overlap_p (offset1, max_size1, offset2, max_size2); + return range_overlap (offset1, max_size1, offset2, max_size2, aloff); } - if (!ptr_derefs_may_alias_p (ptr1, ptr2)) - return false; + if (!ptr_derefs_alias_p (ptr1, ptr2, aloff != NULL)) + return 0; /* Disambiguations that rely on strict aliasing rules follow. */ if (!flag_strict_aliasing || !tbaa_p) - return true; + return HOST_WIDE_INT_M1U; ptrtype1 = TREE_TYPE (TREE_OPERAND (base1, 1)); ptrtype2 = TREE_TYPE (TREE_OPERAND (base2, 1)); @@ -1343,7 +1367,7 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, /* If the alias set for a pointer access is zero all bets are off. */ if (base1_alias_set == 0 || base2_alias_set == 0) - return true; + return aloff ? 0 : HOST_WIDE_INT_M1U; /* If both references are through the same type, they do not alias if the accesses do not overlap. This does extra disambiguation @@ -1359,21 +1383,21 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, /* But avoid treating arrays as "objects", instead assume they can overlap by an exact multiple of their element size. */ && TREE_CODE (TREE_TYPE (ptrtype1)) != ARRAY_TYPE) - return ranges_overlap_p (offset1, max_size1, offset2, max_size2); + return range_overlap (offset1, max_size1, offset2, max_size2, aloff); /* Do type-based disambiguation. */ if (base1_alias_set != base2_alias_set && !alias_sets_conflict_p (base1_alias_set, base2_alias_set)) - return false; + return 0; /* If either reference is view-converted, give up now. */ if (same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) != 1 || same_type_for_tbaa (TREE_TYPE (base2), TREE_TYPE (ptrtype2)) != 1) - return true; + return aloff ? 0 : HOST_WIDE_INT_M1U; if (ref1 && ref2 && nonoverlapping_component_refs_p (ref1, ref2)) - return false; + return 0; /* Do access-path based disambiguation. */ if (ref1 && ref2 @@ -1383,21 +1407,20 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1, offset1, max_size1, ref2, ref2_alias_set, base2_alias_set, - offset2, max_size2, false); + offset2, max_size2, false, aloff); - return true; + return HOST_WIDE_INT_M1U; } -/* Return true, if the two memory references REF1 and REF2 may alias. */ +/* Return non-zero if the two memory references REF1 and REF2 may alias. + The returned value is the size of the overlap if it can be determined, + otherwise it's HOST_WIDE_INT_M1U. When ALOFF is non-null, the returned + value is non-zero if and only if REF1 and REF2 definitely overlap, and + *ALOFF is set to the offset of the overlap in REF1. */ -bool -refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p) +static unsigned HOST_WIDE_INT +refs_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p, HOST_WIDE_INT *aloff) { - tree base1, base2; - HOST_WIDE_INT offset1 = 0, offset2 = 0; - HOST_WIDE_INT max_size1 = -1, max_size2 = -1; - bool var1_p, var2_p, ind1_p, ind2_p; - gcc_checking_assert ((!ref1->ref || TREE_CODE (ref1->ref) == SSA_NAME || DECL_P (ref1->ref) @@ -1414,12 +1437,30 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p) || TREE_CODE (ref2->ref) == TARGET_MEM_REF)); /* Decompose the references into their base objects and the access. */ - base1 = ao_ref_base (ref1); - offset1 = ref1->offset; - max_size1 = ref1->max_size; - base2 = ao_ref_base (ref2); - offset2 = ref2->offset; - max_size2 = ref2->max_size; + tree base1 = ao_ref_base (ref1); + tree base2 = ao_ref_base (ref2); + + HOST_WIDE_INT offset1 = ref1->offset; + HOST_WIDE_INT offset2 = ref2->offset; + + HOST_WIDE_INT max_size1 = ref1->max_size; + HOST_WIDE_INT max_size2 = ref2->max_size; + + bool must_alias = aloff != NULL; + if (must_alias) + { + /* ALOFF being non-null implies a request for a must-alias kind of + analysis. Consider the minimum size when ALOFF is non-zero. */ + max_size1 = ref1->size; + max_size2 = ref2->size; + } + else + { + /* ALOFF being null implies a request for a may-alias kind of + analysis. Consider the maximum size. */ + max_size1 = ref1->max_size; + max_size2 = ref2->max_size; + } /* We can end up with registers or constants as bases for example from *D.1663_44 = VIEW_CONVERT_EXPR(__tmp$B0F64_59); @@ -1434,7 +1475,7 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p) || TREE_CODE (base2) == CONSTRUCTOR || TREE_CODE (base2) == ADDR_EXPR || CONSTANT_CLASS_P (base2)) - return false; + return 0; /* We can end up referring to code via function and label decls. As we likely do not properly track code aliases conservatively @@ -1443,22 +1484,23 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p) || TREE_CODE (base1) == LABEL_DECL || TREE_CODE (base2) == FUNCTION_DECL || TREE_CODE (base2) == LABEL_DECL) - return true; + return must_alias ? 0 : HOST_WIDE_INT_M1U; - /* Two volatile accesses always conflict. */ + /* Two volatile accesses may always conflict. */ if (ref1->volatile_p && ref2->volatile_p) - return true; + return must_alias ? 0 : HOST_WIDE_INT_M1U; /* Defer to simple offset based disambiguation if we have references based on two decls. Do this before defering to TBAA to handle must-alias cases in conformance with the GCC extension of allowing type-punning through unions. */ - var1_p = DECL_P (base1); - var2_p = DECL_P (base2); + bool var1_p = DECL_P (base1); + bool var2_p = DECL_P (base2); if (var1_p && var2_p) return decl_refs_may_alias_p (ref1->ref, base1, offset1, max_size1, - ref2->ref, base2, offset2, max_size2); + ref2->ref, base2, offset2, max_size2, + aloff); /* Handle restrict based accesses. ??? ao_ref_base strips inner MEM_REF [&decl], recover from that @@ -1486,12 +1528,12 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p) && MR_DEPENDENCE_CLIQUE (base1) == MR_DEPENDENCE_CLIQUE (base2) /* But based on different pointers they do not alias. */ && MR_DEPENDENCE_BASE (base1) != MR_DEPENDENCE_BASE (base2)) - return false; + return 0; - ind1_p = (TREE_CODE (base1) == MEM_REF - || TREE_CODE (base1) == TARGET_MEM_REF); - ind2_p = (TREE_CODE (base2) == MEM_REF - || TREE_CODE (base2) == TARGET_MEM_REF); + bool ind1_p = (TREE_CODE (base1) == MEM_REF + || TREE_CODE (base1) == TARGET_MEM_REF); + bool ind2_p = (TREE_CODE (base2) == MEM_REF + || TREE_CODE (base2) == TARGET_MEM_REF); /* Canonicalize the pointer-vs-decl case. */ if (ind1_p && var2_p) @@ -1511,33 +1553,63 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p) && flag_strict_aliasing && !alias_sets_conflict_p (ao_ref_alias_set (ref1), ao_ref_alias_set (ref2))) - return false; + return 0; /* Dispatch to the pointer-vs-decl or pointer-vs-pointer disambiguators. */ if (var1_p && ind2_p) - return indirect_ref_may_alias_decl_p (ref2->ref, base2, - offset2, max_size2, - ao_ref_alias_set (ref2), - ao_ref_base_alias_set (ref2), - ref1->ref, base1, - offset1, max_size1, - ao_ref_alias_set (ref1), - ao_ref_base_alias_set (ref1), - tbaa_p); - else if (ind1_p && ind2_p) - return indirect_refs_may_alias_p (ref1->ref, base1, - offset1, max_size1, - ao_ref_alias_set (ref1), - ao_ref_base_alias_set (ref1), - ref2->ref, base2, + return indirect_ref_alias_decl_p (ref2->ref, base2, offset2, max_size2, ao_ref_alias_set (ref2), ao_ref_base_alias_set (ref2), - tbaa_p); + ref1->ref, base1, + offset1, max_size1, + ao_ref_alias_set (ref1), + ao_ref_base_alias_set (ref1), + tbaa_p, aloff); + else if (ind1_p && ind2_p) + return indirect_refs_alias_p (ref1->ref, base1, + offset1, max_size1, + ao_ref_alias_set (ref1), + ao_ref_base_alias_set (ref1), + ref2->ref, base2, + offset2, max_size2, + ao_ref_alias_set (ref2), + ao_ref_base_alias_set (ref2), + tbaa_p, aloff); gcc_unreachable (); } +bool +refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p) +{ + return refs_alias_p_1 (ref1, ref2, tbaa_p, NULL); +} + +/* Return the size of the overlap in bytes if REF1 and REF2 alias one + another, and set *ALOFF (alias offset) to the offset of the overlap + in REF1. Return HOST_WIDE_INT_M1U if the size of the overlap cannot + be determined. Otherwise, if REF1 and REF2 do not definitely overlap, + return zero. */ + +unsigned HOST_WIDE_INT +refs_must_alias_p_1 (ao_ref *ref1, ao_ref *ref2, HOST_WIDE_INT *aloff) +{ + /* ALOFF being non-zero is what distinguishes a must kind of analysis + from a may kind. Set it to &DUMMY when the caller doesn't care + about the offset. */ + HOST_WIDE_INT dummy; + if (!aloff) + aloff = &dummy; + + unsigned HOST_WIDE_INT alsz = refs_alias_p_1 (ref1, ref2, true, aloff); + + /* Convert the size and offset of the overlap from bits to bytes. */ + if (alsz) + *aloff /= BITS_PER_UNIT; + return alsz / BITS_PER_UNIT; +} + static bool refs_may_alias_p (tree ref1, ao_ref *ref2) { diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h index 8c89c69..ea4671b 100644 --- a/gcc/tree-ssa-alias.h +++ b/gcc/tree-ssa-alias.h @@ -105,11 +105,14 @@ extern alias_set_type ao_ref_alias_set (ao_ref *); extern alias_set_type ao_ref_base_alias_set (ao_ref *); extern bool ptr_deref_may_alias_global_p (tree); extern bool ptr_derefs_may_alias_p (tree, tree); +extern bool ptr_derefs_must_alias_p (tree, tree); extern bool ptrs_compare_unequal (tree, tree); extern bool ref_may_alias_global_p (tree); extern bool ref_may_alias_global_p (ao_ref *); extern bool refs_may_alias_p (tree, tree); extern bool refs_may_alias_p_1 (ao_ref *, ao_ref *, bool); +extern unsigned HOST_WIDE_INT refs_must_alias_p_1 (ao_ref *, ao_ref *, + HOST_WIDE_INT *); extern bool refs_anti_dependent_p (tree, tree); extern bool refs_output_dependent_p (tree, tree); extern bool ref_maybe_used_by_stmt_p (gimple *, tree); @@ -152,7 +155,8 @@ extern bool pt_solution_empty_p (struct pt_solution *); extern bool pt_solution_singleton_or_null_p (struct pt_solution *, unsigned *); extern bool pt_solution_includes_global (struct pt_solution *); extern bool pt_solution_includes (struct pt_solution *, const_tree); -extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *); +extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *, + bool = false); extern void pt_solution_reset (struct pt_solution *); extern void pt_solution_set (struct pt_solution *, bitmap, bool); extern void pt_solution_set_var (struct pt_solution *, tree); @@ -161,6 +165,47 @@ extern void dump_pta_stats (FILE *); extern GTY(()) struct pt_solution ipa_escaped_pt; +/* Return the size of the overlap if the two ranges [POS1, POS1 + SIZE1] + and [POS2, POS2 + SIZE2] overlap. Set *OFFSET to the offset of the + overlap in the first range. When SIZE1 or SIZE2 is -1 the range is + half-open. Otherwise return zero. */ + +static inline unsigned HOST_WIDE_INT +range_overlap (HOST_WIDE_INT pos1, + unsigned HOST_WIDE_INT size1, + HOST_WIDE_INT pos2, + unsigned HOST_WIDE_INT size2, + HOST_WIDE_INT *offset) +{ + if (offset) + *offset = pos2 - pos1; + + if (pos1 >= pos2) + { + if (size2 == HOST_WIDE_INT_M1U) + /* Overlap is certain but its size is unknown. */ + return HOST_WIDE_INT_M1U; + + unsigned HOST_WIDE_INT end2 = pos2 + (HOST_WIDE_INT) size2; + if ((unsigned HOST_WIDE_INT) pos1 < end2) + return end2 - pos1 + 1; + } + + if (pos2 >= pos1) + { + if (size1 == HOST_WIDE_INT_M1U) + /* Overlap is certain but its size is unknown. */ + return HOST_WIDE_INT_M1U; + + unsigned HOST_WIDE_INT end1 = pos1 + (HOST_WIDE_INT) size1; + if ((unsigned HOST_WIDE_INT) pos2 < end1) + return end1 - pos2 + 1; + } + + /* No overlap. */ + return 0; +} + /* Return true, if the two ranges [POS1, SIZE1] and [POS2, SIZE2] overlap. SIZE1 and/or SIZE2 can be (unsigned)-1 in which case the range is open-ended. Otherwise return false. */ @@ -171,18 +216,7 @@ ranges_overlap_p (HOST_WIDE_INT pos1, HOST_WIDE_INT pos2, unsigned HOST_WIDE_INT size2) { - if (pos1 >= pos2 - && (size2 == (unsigned HOST_WIDE_INT)-1 - || pos1 < (pos2 + (HOST_WIDE_INT) size2))) - return true; - if (pos2 >= pos1 - && (size1 == (unsigned HOST_WIDE_INT)-1 - || pos2 < (pos1 + (HOST_WIDE_INT) size1))) - return true; - - return false; + return range_overlap (pos1, size1, pos2, size2, NULL) != 0; } - - #endif /* TREE_SSA_ALIAS_H */ diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index b0563fe..e6b82d2 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -40,11 +40,15 @@ along with GCC; see the file COPYING3. If not see #include "expr.h" #include "tree-dfa.h" #include "domwalk.h" +#include "tree-ssa-alias.h" #include "tree-ssa-propagate.h" #include "params.h" #include "ipa-chkp.h" #include "tree-hash-traits.h" #include "builtins.h" +#include "intl.h" +#include "diagnostic.h" +#include "diagnostic-core.h" /* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value is an index into strinfo vector, negative value stands for @@ -1559,6 +1563,33 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1)); len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true, GSI_SAME_STMT); + + if (olddsi && si) + { + ao_ref dstref, srcref; + ao_ref_init_from_ptr_and_size (&dstref, olddsi->ptr, len); + ao_ref_init_from_ptr_and_size (&srcref, si->ptr, len); + + HOST_WIDE_INT offset; + if (unsigned HOST_WIDE_INT overlap + = refs_must_alias_p_1 (&dstref, &srcref, &offset)) + { + tree func = gimple_call_fndecl (stmt); + + if (warning_at (loc, OPT_Wrestrict, + overlap > 1 + ? G_("%qD source sequence overlaps %wu bytes of " + "destination at offset %wi") + : G_("%qD source sequence overlaps %wu byte of " + "destination at offset %wi"), + func, overlap, offset)) + gimple_set_no_warning (stmt, true); + + /* Avoid transforming overlapping strcpy to memcpy. */ + return; + } + } + if (dump_file && (dump_flags & TDF_DETAILS) != 0) { fprintf (dump_file, "Optimizing: "); @@ -1786,7 +1817,7 @@ static void handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) { int idx, didx; - tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr; + tree src, dst, srclen, dstlen, lhs, args, type, fn, objsz, endptr; bool success; gimple *stmt = gsi_stmt (*gsi); strinfo *si, *dsi; @@ -1804,10 +1835,65 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) dsi = NULL; if (didx > 0) dsi = get_strinfo (didx); + + srclen = NULL_TREE; + si = NULL; + idx = get_stridx (src); + if (idx < 0) + srclen = build_int_cst (size_type_node, ~idx); + else if (idx > 0) + { + si = get_strinfo (idx); + if (si != NULL) + srclen = get_string_length (si); + } + + loc = gimple_location (stmt); + if (dsi == NULL || get_string_length (dsi) == NULL_TREE) { + if (warn_restrict) + { + /* The concatenation always involves copying at least one byte + (the terminating nul), even if the source string is empty. + If the source is unknown assume it's one character long and + compute the size of the result. */ + tree ressize = srclen ? srclen : size_one_node; + tree type = TREE_TYPE (ressize); + ressize = fold_build2 (PLUS_EXPR, type, ressize, ressize); + ressize = fold_build2 (PLUS_EXPR, type, ressize, + build_int_cst (type, 1)); + + tree sptr = si && si->ptr ? si->ptr : src; + + /* Check for overlap between the concatentated string and + the source string. */ + ao_ref dstref, srcref; + ao_ref_init_from_ptr_and_size (&dstref, dst, ressize); + ao_ref_init_from_ptr_and_size (&srcref, sptr, ressize); + + HOST_WIDE_INT offset; + if (unsigned HOST_WIDE_INT size + = refs_must_alias_p_1 (&dstref, &srcref, &offset)) + { + tree func = gimple_call_fndecl (stmt); + + if (warning_at (loc, OPT_Wrestrict, + size > 1 + ? G_("%qD source sequence overlaps %wu bytes of " + "destination at offset %wi") + : G_("%qD source sequence overlaps %wu byte of " + "destination at offset %wi"), + func, size, offset)) + gimple_set_no_warning (stmt, true); + + /* Avoid transforming overlapping strcat. */ + return; + } + } + /* strcat (p, q) can be transformed into - tmp = p + strlen (p); endptr = strpcpy (tmp, q); + tmp = p + strlen (p); endptr = stpcpy (tmp, q); with length endptr - p if we need to compute the length later on. Don't do this transformation if we don't need it. */ @@ -1840,19 +1926,6 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) return; } - srclen = NULL_TREE; - si = NULL; - idx = get_stridx (src); - if (idx < 0) - srclen = build_int_cst (size_type_node, ~idx); - else if (idx > 0) - { - si = get_strinfo (idx); - if (si != NULL) - srclen = get_string_length (si); - } - - loc = gimple_location (stmt); dstlen = dsi->nonzero_chars; endptr = dsi->endptr; @@ -1914,7 +1987,44 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) if (fn == NULL_TREE) return; - len = NULL_TREE; + if (warn_restrict && dsi && dstlen) + { + tree slen = srclen ? srclen : size_zero_node; + /* Compute the size of the concatendated string, including + the terminating nul. */ + tree type = TREE_TYPE (dstlen); + tree ressize = fold_build2 (PLUS_EXPR, type, dstlen, slen); + ressize = fold_build2 (PLUS_EXPR, type, ressize, build_int_cst (type, 1)); + + tree sptr = si && si->ptr ? si->ptr : src; + + /* Check for overlap between the concatentated string and + the source string. */ + ao_ref dstref, srcref; + ao_ref_init_from_ptr_and_size (&dstref, dsi->ptr, ressize); + ao_ref_init_from_ptr_and_size (&srcref, sptr, slen); + + HOST_WIDE_INT offset; + if (unsigned HOST_WIDE_INT overlap + = refs_must_alias_p_1 (&dstref, &srcref, &offset)) + { + tree func = gimple_call_fndecl (stmt); + + if (warning_at (loc, OPT_Wrestrict, + overlap > 1 + ? G_("%qD source sequence overlaps %wu bytes of " + "destination at offset %wi") + : G_("%qD source sequence overlaps %wu byte of " + "destination at offset %wi"), + func, overlap, offset)) + gimple_set_no_warning (stmt, true); + + /* Avoid transforming overlapping strcat. */ + return; + } + } + + tree len = NULL_TREE; if (srclen != NULL_TREE) { args = TYPE_ARG_TYPES (TREE_TYPE (fn)); diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index e563e9d..1728260 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -6743,31 +6743,33 @@ pt_solution_includes (struct pt_solution *pt, const_tree decl) } /* Return true if both points-to solutions PT1 and PT2 have a non-empty - intersection. */ + intersection. When STRICT is true, interpret only definitive results + as positive, and possible results as negative. */ static bool -pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2) +pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2, + bool strict = false) { if (pt1->anything || pt2->anything) - return true; + return !strict; /* If either points to unknown global memory and the other points to - any global memory they alias. */ + any global memory they may (though need not) alias. */ if ((pt1->nonlocal && (pt2->nonlocal || pt2->vars_contains_nonlocal)) || (pt2->nonlocal && pt1->vars_contains_nonlocal)) - return true; + return !strict; /* If either points to all escaped memory and the other points to - any escaped memory they alias. */ + any escaped memory they may (though need not) alias. */ if ((pt1->escaped && (pt2->escaped || pt2->vars_contains_escaped)) || (pt2->escaped && pt1->vars_contains_escaped)) - return true; + return !strict; /* Check the escaped solution if required. ??? Do we need to check the local against the IPA escaped sets? */ @@ -6775,17 +6777,17 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2) && !pt_solution_empty_p (&ipa_escaped_pt)) { /* If both point to escaped memory and that solution - is not empty they alias. */ + is not empty they may alias. */ if (pt1->ipa_escaped && pt2->ipa_escaped) - return true; + return !strict; /* If either points to escaped memory see if the escaped solution intersects with the other. */ if ((pt1->ipa_escaped - && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2)) + && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2, strict)) || (pt2->ipa_escaped - && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1))) - return true; + && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1, strict))) + return !strict; } /* Now both pointers alias if their points-to solution intersects. */ @@ -6794,10 +6796,17 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2) && bitmap_intersect_p (pt1->vars, pt2->vars)); } +/* Return true if the solutions *PT1 and *PT2 intersect. When STRICT + is true, interpret only definitive results as positive, and possible + results as negative. The former effectively gives an answer to + the qestion of whether two pointers must refer to the same array, + while the latter answers whether they may refer to one. */ + bool -pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2) +pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2, + bool strict /* = false */) { - bool res = pt_solutions_intersect_1 (pt1, pt2); + bool res = pt_solutions_intersect_1 (pt1, pt2, strict); if (res) ++pta_stats.pt_solutions_intersect_may_alias; else @@ -6805,7 +6814,6 @@ pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2) return res; } - /* Dump points-to information to OUTFILE. */ static void