From patchwork Wed May 17 06:52:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Sandiford X-Patchwork-Id: 763351 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 3wSQ4b0rkqz9s85 for ; Wed, 17 May 2017 16:53:02 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="Ae67Cb9l"; 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:from :to:cc:subject:references:date:in-reply-to:message-id :mime-version:content-type; q=dns; s=default; b=HzFNSx4U4jpMNTPs jLk3+Mu7NqsAGswK1Tca7TzjNaqEtMdevp70QvunAMkHNOKwegfDnjzXKA3w7k3W 0pZeVrW+B0KWz7vTiMHK35k+U+4fE2OsxgkH+LBS8JU3zPrMQZFz1jsScq453J31 al+FTsJMwT0anr56h5Hga1PH1jk= 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:from :to:cc:subject:references:date:in-reply-to:message-id :mime-version:content-type; s=default; bh=6R+p0kKbW7dtv7pmdkfZLi eBHsI=; b=Ae67Cb9lKbIW0MkBHFx/+cBbqrtbWn6u/w766WW5N58ROHQo1vHO0e 4zeWoSsiMe6IANkcJT6B/75R49GVjiHDi58RO1rN3S3M/yqUpidqv5afHWRjUuPC OZ+lFo+Lax63LUgCtbJddZM0Lg1AqQhNOSJZvL9JGNpD146ygAV6w= Received: (qmail 51724 invoked by alias); 17 May 2017 06:52:45 -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 51694 invoked by uid 89); 17 May 2017 06:52:43 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-15.1 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=ham version=3.3.2 spammy=facto, ls, 1987 X-HELO: mail-wm0-f47.google.com Received: from mail-wm0-f47.google.com (HELO mail-wm0-f47.google.com) (74.125.82.47) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 17 May 2017 06:52:38 +0000 Received: by mail-wm0-f47.google.com with SMTP id d127so5106048wmf.0 for ; Tue, 16 May 2017 23:52:40 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:mail-followup-to:cc:subject:references :date:in-reply-to:message-id:user-agent:mime-version; bh=okWWjEmiXX4uEs9jvjVdcsrkutUrgJ4w1oZ5fwyOepo=; b=Ytiug7T7bU86fKe9XfqUZsv7NNyi/X/VJFRvBgGxOWtt64oQiLM8EDi5fSkzZbEYei bReut5YORb55dcOMKvqg8POeIt7f4Qp6fPBa2pKsy+8pnLPbBYVGnuhrGDi49y3TbiSR ygwhr6ty7TPWBWe3SlwhA8YrHDULaud/61VsXC+V8i+8BoouPgDiA930XEYjynzdNuQ+ 5llNU1sRukK7qVOzMGK+1NXU6WhggilfLbCWLNtgeRj3SvCdBa6Qg1T6x1EPsV2zrH/D IONWIVQ9IYfzPBXTsnv2JVl8v047fIQKUAmgr9CwPHsrkshGNajxzV6PYIJe46mSde6E gI2g== X-Gm-Message-State: AODbwcAGHqq0WEP3LdQ+N6FzpExFjWGsUAYYhounXxq7fHXFSebeQNcW wTRHG5dMsysy1MkpBhxDYQ== X-Received: by 10.28.0.13 with SMTP id 13mr1268657wma.19.1495003957340; Tue, 16 May 2017 23:52:37 -0700 (PDT) Received: from localhost (188.29.164.226.threembb.co.uk. [188.29.164.226]) by smtp.gmail.com with ESMTPSA id l27sm391009wrb.65.2017.05.16.23.52.35 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 16 May 2017 23:52:36 -0700 (PDT) From: Richard Sandiford To: Jakub Jelinek Mail-Followup-To: Jakub Jelinek , gcc-patches@gcc.gnu.org, richard.sandiford@linaro.org Cc: gcc-patches@gcc.gnu.org Subject: Re: Make tree-ssa-strlen.c handle partial unterminated strings References: <87efw3sebf.fsf@linaro.org> <20170505160153.GF1809@tucnak> <87mvapav6v.fsf@googlemail.com> <20170510140609.GX1809@tucnak> <87ziek8qmg.fsf@linaro.org> Date: Wed, 17 May 2017 07:52:38 +0100 In-Reply-To: <87ziek8qmg.fsf@linaro.org> (Richard Sandiford's message of "Wed, 10 May 2017 20:21:27 +0100") Message-ID: <87shk4asax.fsf@linaro.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.1 (gnu/linux) MIME-Version: 1.0 Richard Sandiford writes: > Jakub Jelinek writes: >> Hi! >> >> Note the intent of the pass is to handle the most common cases, it is fine >> if some cases that aren't common aren't handled, it is all about the extra >> complexity vs. how much it helps on real-world code. > > OK. > >> On Sun, May 07, 2017 at 10:10:48AM +0100, Richard Sandiford wrote: >>> I've got most of the way through a version that uses min_length instead. >>> But one thing that the terminated flag allows that a constant min_length >>> doesn't is: >>> >>> size_t >>> f1 (char *a1) >>> { >>> size_t x = strlen (a1); >>> char *a3 = a1 + x; >>> a3[0] = '1'; // a1 length x + 1, unterminated (min length x + 1) >>> a3[1] = '2'; // a1 length x + 2, unterminated (min length x + 2) >>> a3[2] = '3'; // a1 length x + 3, unterminated (min length x + 3) >>> a3[3] = 0; // a1 length x + 3, terminated >>> return strlen (a1); >>> } >>> >>> For the a3[3] = 0, we know a3's min_length is 3 and so it's obvious >>> that we can convert its min_length to a length. But even if we allow >>> a1's min_length to be nonconstant, it seems a bit risky to assume that >>> we can convert its min_length to a length as well. It would only work >>> if the min_lengths in related strinfos are kept in sync, whereas it >>> ought to be safe to say that the minimum length of something is 0. >> >> And we have code for that. If verify_related_strinfos returns non-NULL, >> we can adjust all the related strinfos that need adjusting. >> See e.g. zero_length_string on how it uses that. It is just that we should >> decide what is the acceptable complexity of the length/min_length >> expressions (whether INTEGER_CST or SSA_NAME is enough, then the above >> would not work, but is that really that important), or if we e.g. allow >> SSA_NAME + INTEGER_CST in addition to that, or sum of 2 SSA_NAMEs, etc.). >> I don't see how terminated vs. unterminated (which is misnamed anyway, it >> means that it isn't known to be terminated, it might be terminated or not) >> would help with that. > > The example above works with the flag because we already allow > SSA_NAME + INTEGER_CST for the length field, thanks to: > > tree adj = NULL_TREE; > if (oldlen == NULL_TREE) > ; > else if (integer_zerop (oldlen)) > adj = srclen; > else if (TREE_CODE (oldlen) == INTEGER_CST > || TREE_CODE (srclen) == INTEGER_CST) > adj = fold_build2_loc (loc, MINUS_EXPR, > TREE_TYPE (srclen), srclen, > fold_convert_loc (loc, TREE_TYPE (srclen), > oldlen)); > if (adj != NULL_TREE) > adjust_related_strinfos (loc, dsi, adj); > > etc. So with a constant min_length we lose out (compared to the flag) > by making min_length more restrictive. > > Like you say later, min_length is the number of characters that are > known to be nonzero, and length is the number of characters that are > known to be nonzero and followed by a zero, so even if we do relax the > rules for min_length to match length, I think in almost all useful cases > the length will be equal to the min_length or will be null (i.e. it'll > act almost like a de facto flag). How about the patch below? It renames "length" to "nonzero_chars" and adds a "full_string_p" flag to indicate that the nonzero characters are known to be followed by a zero. It keeps the current decisions about which kinds of length/nonzero_chars expression are valid and worth tracking. It also handles strings built up a character at a time (unlike the original). Tested on aarch64-linux-gnu and x86_64-linux-gnu. Thanks, Richard 2017-05-17 Richard Sandiford gcc/ * tree-ssa-strlen.c (strinfo): Rename the length field to nonzero_chars. Add a full_string_p field. (compare_nonzero_chars, zero_length_string_p): New functions. (get_addr_stridx): Add an offset_out parameter. Use compare_nonzero_chars. (get_stridx): Update accordingly. Use compare_nonzero_chars. (new_strinfo): Update after above changes to strinfo. (set_endptr_and_length): Set full_string_p. (get_string_length): Update after above changes to strinfo. (unshare_strinfo): Update call to new_strinfo. (maybe_invalidate): Likewise. (get_stridx_plus_constant): Change off to unsigned HOST_WIDE_INT. Use compare_nonzero_chars and zero_string_p. Treat nonzero_chars as a uhwi instead of an shwi. Update after above changes to strinfo and new_strinfo. (zero_length_string): Assert that chainsi contains full strings. Use zero_length_string_p. Update call to new_strinfo. (adjust_related_strinfos): Update after above changes to strinfo. Copy full_string_p from origsi. (adjust_last_stmt): Use zero_length_string_p. (handle_builtin_strlen): Update after above changes to strinfo and new_strinfo. Install the lhs as the string length if the previous entry didn't describe a full string. (handle_builtin_strchr): Update after above changes to strinfo and new_strinfo. (handle_builtin_strcpy): Likewise. (handle_builtin_strcat): Likewise. (handle_builtin_malloc): Likewise. (handle_pointer_plus): Likewise. (handle_builtin_memcpy): Likewise. Track nonzero characters that aren't necessarily followed by a nul terminator. (handle_char_store): Likewise. gcc/testsuite/ * gcc.dg/strlenopt-32.c: New testcase. * gcc.dg/strlenopt-33.c: Likewise. * gcc.dg/strlenopt-33g.c: Likewise. * gcc.dg/strlenopt-34.c: Likewise. * gcc.dg/strlenopt-35.c: Likewise. Index: gcc/tree-ssa-strlen.c =================================================================== --- gcc/tree-ssa-strlen.c 2017-05-17 07:33:01.091940890 +0100 +++ gcc/tree-ssa-strlen.c 2017-05-17 07:33:03.918870380 +0100 @@ -57,8 +57,13 @@ the Free Software Foundation; either ver /* String information record. */ struct strinfo { - /* String length of this string. */ - tree length; + /* Number of leading characters that are known to be nonzero. This is + also the length of the string if FULL_STRING_P. + + The values in a list of related string pointers must be consistent; + that is, if strinfo B comes X bytes after strinfo A, it must be + the case that A->nonzero_chars == X + B->nonzero_chars. */ + tree nonzero_chars; /* Any of the corresponding pointers for querying alias oracle. */ tree ptr; /* This is used for two things: @@ -105,6 +110,10 @@ struct strinfo /* A flag for the next maybe_invalidate that this strinfo shouldn't be invalidated. Always cleared by maybe_invalidate. */ bool dont_invalidate; + /* True if the string is known to be nul-terminated after NONZERO_CHARS + characters. False is useful when detecting strings that are built + up via successive memcpys. */ + bool full_string_p; }; /* Pool for allocating strinfo_struct entries. */ @@ -150,7 +159,34 @@ struct laststmt_struct int stridx; } laststmt; -static int get_stridx_plus_constant (strinfo *, HOST_WIDE_INT, tree); +static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree); + +/* Return: + + - 1 if SI is known to start with more than OFF nonzero characters. + + - 0 if SI is known to start with OFF nonzero characters, + but is not known to start with more. + + - -1 if SI might not start with OFF nonzero characters. */ + +static inline int +compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off) +{ + if (si->nonzero_chars + && TREE_CODE (si->nonzero_chars) == INTEGER_CST) + return compare_tree_int (si->nonzero_chars, off); + else + return -1; +} + +/* Return true if SI is known to be a zero-length string. */ + +static inline bool +zero_length_string_p (strinfo *si) +{ + return si->full_string_p && integer_zerop (si->nonzero_chars); +} /* Return strinfo vector entry IDX. */ @@ -175,10 +211,13 @@ get_next_strinfo (strinfo *si) return nextsi; } -/* Helper function for get_stridx. */ +/* Helper function for get_stridx. Return the strinfo index of the address + of EXP, which is available in PTR if nonnull. If OFFSET_OUT, it is + OK to return the index for some X <= &EXP and store &EXP - X in + *OFFSET_OUT. */ static int -get_addr_stridx (tree exp, tree ptr) +get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out) { HOST_WIDE_INT off; struct stridxlist *list, *last = NULL; @@ -198,7 +237,11 @@ get_addr_stridx (tree exp, tree ptr) do { if (list->offset == off) - return list->idx; + { + if (offset_out) + *offset_out = 0; + return list->idx; + } if (list->offset > off) return 0; last = list; @@ -206,14 +249,21 @@ get_addr_stridx (tree exp, tree ptr) } while (list); - if (ptr && last && last->idx > 0) + if ((offset_out || ptr) && last && last->idx > 0) { + unsigned HOST_WIDE_INT rel_off + = (unsigned HOST_WIDE_INT) off - last->offset; strinfo *si = get_strinfo (last->idx); - if (si - && si->length - && TREE_CODE (si->length) == INTEGER_CST - && compare_tree_int (si->length, off - last->offset) != -1) - return get_stridx_plus_constant (si, off - last->offset, ptr); + if (si && compare_nonzero_chars (si, rel_off) >= 0) + { + if (offset_out) + { + *offset_out = rel_off; + return last->idx; + } + else + return get_stridx_plus_constant (si, rel_off, ptr); + } } return 0; } @@ -253,10 +303,7 @@ get_stridx (tree exp) { strinfo *si = get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)]); - if (si - && si->length - && TREE_CODE (si->length) == INTEGER_CST - && compare_tree_int (si->length, off) != -1) + if (si && compare_nonzero_chars (si, off) >= 0) return get_stridx_plus_constant (si, off, exp); } e = rhs1; @@ -266,7 +313,7 @@ get_stridx (tree exp) if (TREE_CODE (exp) == ADDR_EXPR) { - int idx = get_addr_stridx (TREE_OPERAND (exp, 0), exp); + int idx = get_addr_stridx (TREE_OPERAND (exp, 0), exp, NULL); if (idx != 0) return idx; } @@ -419,10 +466,10 @@ new_addr_stridx (tree exp) /* Create a new strinfo. */ static strinfo * -new_strinfo (tree ptr, int idx, tree length) +new_strinfo (tree ptr, int idx, tree nonzero_chars, bool full_string_p) { strinfo *si = strinfo_pool.allocate (); - si->length = length; + si->nonzero_chars = nonzero_chars; si->ptr = ptr; si->stmt = NULL; si->endptr = NULL_TREE; @@ -433,6 +480,7 @@ new_strinfo (tree ptr, int idx, tree len si->next = 0; si->writable = false; si->dont_invalidate = false; + si->full_string_p = full_string_p; return si; } @@ -492,8 +540,9 @@ set_endptr_and_length (location_t loc, s si->stmt = NULL; tree start_as_size = fold_convert_loc (loc, size_type_node, si->ptr); tree end_as_size = fold_convert_loc (loc, size_type_node, endptr); - si->length = fold_build2_loc (loc, MINUS_EXPR, size_type_node, - end_as_size, start_as_size); + si->nonzero_chars = fold_build2_loc (loc, MINUS_EXPR, size_type_node, + end_as_size, start_as_size); + si->full_string_p = true; } /* Return string length, or NULL if it can't be computed. */ @@ -501,8 +550,8 @@ set_endptr_and_length (location_t loc, s static tree get_string_length (strinfo *si) { - if (si->length) - return si->length; + if (si->nonzero_chars) + return si->full_string_p ? si->nonzero_chars : NULL; if (si->stmt) { @@ -595,19 +644,19 @@ get_string_length (strinfo *si) for (strinfo *chainsi = verify_related_strinfos (si); chainsi != NULL; chainsi = get_next_strinfo (chainsi)) - if (chainsi->length == NULL) + if (chainsi->nonzero_chars == NULL) set_endptr_and_length (loc, chainsi, lhs); break; case BUILT_IN_MALLOC: break; - /* BUILT_IN_CALLOC always has si->length set. */ + /* BUILT_IN_CALLOC always has si->nonzero_chars set. */ default: gcc_unreachable (); break; } } - return si->length; + return si->nonzero_chars; } /* Invalidate string length information for strings whose length @@ -626,7 +675,7 @@ maybe_invalidate (gimple *stmt) if (!si->dont_invalidate) { ao_ref r; - /* Do not use si->length. */ + /* Do not use si->nonzero_chars. */ ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE); if (stmt_may_clobber_ref_p_1 (stmt, &r)) { @@ -653,7 +702,7 @@ unshare_strinfo (strinfo *si) if (si->refcount == 1 && !strinfo_shared ()) return si; - nsi = new_strinfo (si->ptr, si->idx, si->length); + nsi = new_strinfo (si->ptr, si->idx, si->nonzero_chars, si->full_string_p); nsi->stmt = si->stmt; nsi->endptr = si->endptr; nsi->first = si->first; @@ -670,39 +719,39 @@ unshare_strinfo (strinfo *si) been created. */ static int -get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr) +get_stridx_plus_constant (strinfo *basesi, unsigned HOST_WIDE_INT off, + tree ptr) { if (TREE_CODE (ptr) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr)) return 0; - if (basesi->length == NULL_TREE - || TREE_CODE (basesi->length) != INTEGER_CST - || compare_tree_int (basesi->length, off) == -1 - || !tree_fits_shwi_p (basesi->length)) + if (compare_nonzero_chars (basesi, off) < 0 + || !tree_fits_uhwi_p (basesi->nonzero_chars)) return 0; - HOST_WIDE_INT len = tree_to_shwi (basesi->length) - off; + unsigned HOST_WIDE_INT nonzero_chars + = tree_to_uhwi (basesi->nonzero_chars) - off; strinfo *si = basesi, *chainsi; if (si->first || si->prev || si->next) si = verify_related_strinfos (basesi); if (si == NULL - || si->length == NULL_TREE - || TREE_CODE (si->length) != INTEGER_CST) + || si->nonzero_chars == NULL_TREE + || TREE_CODE (si->nonzero_chars) != INTEGER_CST) return 0; if (TREE_CODE (ptr) == SSA_NAME && ssa_ver_to_stridx.length () <= SSA_NAME_VERSION (ptr)) ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names); - gcc_checking_assert (compare_tree_int (si->length, off) != -1); + gcc_checking_assert (compare_tree_int (si->nonzero_chars, off) != -1); for (chainsi = si; chainsi->next; chainsi = si) { si = get_next_strinfo (chainsi); if (si == NULL - || si->length == NULL_TREE - || TREE_CODE (si->length) != INTEGER_CST) + || si->nonzero_chars == NULL_TREE + || TREE_CODE (si->nonzero_chars) != INTEGER_CST) break; - int r = compare_tree_int (si->length, len); + int r = compare_tree_int (si->nonzero_chars, nonzero_chars); if (r != 1) { if (r == 0) @@ -724,7 +773,8 @@ get_stridx_plus_constant (strinfo *bases int idx = new_stridx (ptr); if (idx == 0) return 0; - si = new_strinfo (ptr, idx, build_int_cst (size_type_node, len)); + si = new_strinfo (ptr, idx, build_int_cst (size_type_node, nonzero_chars), + basesi->full_string_p); set_strinfo (idx, si); if (chainsi->next) { @@ -736,7 +786,7 @@ get_stridx_plus_constant (strinfo *bases if (chainsi->first == 0) chainsi->first = chainsi->idx; chainsi->next = idx; - if (chainsi->endptr == NULL_TREE && len == 0) + if (chainsi->endptr == NULL_TREE && zero_length_string_p (si)) chainsi->endptr = ptr; si->endptr = chainsi->endptr; si->prev = chainsi->idx; @@ -769,7 +819,7 @@ zero_length_string (tree ptr, strinfo *c do { /* We shouldn't mix delayed and non-delayed lengths. */ - gcc_assert (si->length); + gcc_assert (si->full_string_p); if (si->endptr == NULL_TREE) { si = unshare_strinfo (si); @@ -779,7 +829,7 @@ zero_length_string (tree ptr, strinfo *c si = get_next_strinfo (si); } while (si != NULL); - if (chainsi->length && integer_zerop (chainsi->length)) + if (zero_length_string_p (chainsi)) { if (chainsi->next) { @@ -793,7 +843,7 @@ zero_length_string (tree ptr, strinfo *c else { /* We shouldn't mix delayed and non-delayed lengths. */ - gcc_assert (chainsi->length); + gcc_assert (chainsi->full_string_p); if (chainsi->first || chainsi->prev || chainsi->next) { chainsi = unshare_strinfo (chainsi); @@ -806,7 +856,7 @@ zero_length_string (tree ptr, strinfo *c idx = new_stridx (ptr); if (idx == 0) return NULL; - si = new_strinfo (ptr, idx, build_int_cst (size_type_node, 0)); + si = new_strinfo (ptr, idx, build_int_cst (size_type_node, 0), true); set_strinfo (idx, si); si->endptr = ptr; if (chainsi != NULL) @@ -824,9 +874,11 @@ zero_length_string (tree ptr, strinfo *c return si; } -/* For strinfo ORIGSI whose length has been just updated - update also related strinfo lengths (add ADJ to each, - but don't adjust ORIGSI). */ +/* For strinfo ORIGSI whose length has been just updated, adjust other + related strinfos so that they match the new ORIGSI. This involves: + + - adding ADJ to the nonzero_chars fields + - copying full_string_p from the new ORIGSI. */ static void adjust_related_strinfos (location_t loc, strinfo *origsi, tree adj) @@ -848,10 +900,12 @@ adjust_related_strinfos (location_t loc, /* We shouldn't see delayed lengths here; the caller must have calculated the old length in order to calculate the adjustment. */ - gcc_assert (si->length); - tem = fold_convert_loc (loc, TREE_TYPE (si->length), adj); - si->length = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (si->length), - si->length, tem); + gcc_assert (si->nonzero_chars); + tem = fold_convert_loc (loc, TREE_TYPE (si->nonzero_chars), adj); + si->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR, + TREE_TYPE (si->nonzero_chars), + si->nonzero_chars, tem); + si->full_string_p = origsi->full_string_p; si->endptr = NULL_TREE; si->dont_invalidate = true; @@ -1020,11 +1074,8 @@ adjust_last_stmt (strinfo *si, gimple *s } } - if (!is_strcat) - { - if (si->length == NULL_TREE || !integer_zerop (si->length)) - return; - } + if (!is_strcat && !zero_length_string_p (si)) + return; if (is_gimple_assign (last.stmt)) { @@ -1138,12 +1189,13 @@ handle_builtin_strlen (gimple_stmt_itera print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); } if (si != NULL - && TREE_CODE (si->length) != SSA_NAME - && TREE_CODE (si->length) != INTEGER_CST + && TREE_CODE (si->nonzero_chars) != SSA_NAME + && TREE_CODE (si->nonzero_chars) != INTEGER_CST && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) { si = unshare_strinfo (si); - si->length = lhs; + si->nonzero_chars = lhs; + gcc_assert (si->full_string_p); } return; } @@ -1152,11 +1204,25 @@ handle_builtin_strlen (gimple_stmt_itera return; if (idx == 0) idx = new_stridx (src); - else if (get_strinfo (idx) != NULL) - return; + else + { + strinfo *si = get_strinfo (idx); + if (si != NULL) + { + if (!si->full_string_p && !si->stmt) + { + /* Until now we only had a lower bound on the string length. + Install LHS as the actual length. */ + si = unshare_strinfo (si); + si->nonzero_chars = lhs; + si->full_string_p = true; + } + return; + } + } if (idx) { - strinfo *si = new_strinfo (src, idx, lhs); + strinfo *si = new_strinfo (src, idx, lhs, true); set_strinfo (idx, si); find_equal_ptrs (src, idx); } @@ -1260,7 +1326,7 @@ handle_builtin_strchr (gimple_stmt_itera tree srcu = fold_convert_loc (loc, size_type_node, src); tree length = fold_build2_loc (loc, MINUS_EXPR, size_type_node, lhsu, srcu); - strinfo *si = new_strinfo (src, idx, length); + strinfo *si = new_strinfo (src, idx, length, true); si->endptr = lhs; set_strinfo (idx, si); find_equal_ptrs (src, idx); @@ -1349,9 +1415,10 @@ handle_builtin_strcpy (enum built_in_fun } if (olddsi != NULL) { - oldlen = olddsi->length; + oldlen = olddsi->nonzero_chars; dsi = unshare_strinfo (olddsi); - dsi->length = srclen; + dsi->nonzero_chars = srclen; + dsi->full_string_p = (srclen != NULL_TREE); /* Break the chain, so adjust_related_strinfo on later pointers in the chain won't adjust this one anymore. */ dsi->next = 0; @@ -1360,14 +1427,14 @@ handle_builtin_strcpy (enum built_in_fun } else { - dsi = new_strinfo (dst, didx, srclen); + dsi = new_strinfo (dst, didx, srclen, srclen != NULL_TREE); set_strinfo (didx, dsi); find_equal_ptrs (dst, didx); } dsi->writable = true; dsi->dont_invalidate = true; - if (dsi->length == NULL_TREE) + if (dsi->nonzero_chars == NULL_TREE) { strinfo *chainsi; @@ -1388,7 +1455,8 @@ handle_builtin_strcpy (enum built_in_fun invalidated. */ chainsi = unshare_strinfo (chainsi); chainsi->stmt = stmt; - chainsi->length = NULL_TREE; + chainsi->nonzero_chars = NULL_TREE; + chainsi->full_string_p = false; chainsi->endptr = NULL_TREE; chainsi->dont_invalidate = true; } @@ -1556,31 +1624,61 @@ handle_builtin_memcpy (enum built_in_fun && !integer_zerop (len)) adjust_last_stmt (olddsi, stmt, false); + bool full_string_p; if (idx > 0) { gimple *def_stmt; - /* Handle memcpy (x, y, l) where l is strlen (y) + 1. */ + /* Handle memcpy (x, y, l) where l's relationship with strlen (y) + is known. */ si = get_strinfo (idx); - if (si == NULL || si->length == NULL_TREE) - return; - if (TREE_CODE (len) != SSA_NAME) - return; - def_stmt = SSA_NAME_DEF_STMT (len); - if (!is_gimple_assign (def_stmt) - || gimple_assign_rhs_code (def_stmt) != PLUS_EXPR - || gimple_assign_rhs1 (def_stmt) != si->length - || !integer_onep (gimple_assign_rhs2 (def_stmt))) + if (si == NULL || si->nonzero_chars == NULL_TREE) return; + if (TREE_CODE (len) == INTEGER_CST + && TREE_CODE (si->nonzero_chars) == INTEGER_CST) + { + if (tree_int_cst_le (len, si->nonzero_chars)) + { + /* Copying LEN nonzero characters, where LEN is constant. */ + newlen = len; + full_string_p = false; + } + else + { + /* Copying the whole of the analyzed part of SI. */ + newlen = si->nonzero_chars; + full_string_p = si->full_string_p; + } + } + else + { + if (!si->full_string_p) + return; + if (TREE_CODE (len) != SSA_NAME) + return; + def_stmt = SSA_NAME_DEF_STMT (len); + if (!is_gimple_assign (def_stmt) + || gimple_assign_rhs_code (def_stmt) != PLUS_EXPR + || gimple_assign_rhs1 (def_stmt) != si->nonzero_chars + || !integer_onep (gimple_assign_rhs2 (def_stmt))) + return; + /* Copying variable-length string SI (and no more). */ + newlen = si->nonzero_chars; + full_string_p = true; + } } else { si = NULL; /* Handle memcpy (x, "abcd", 5) or memcpy (x, "abc\0uvw", 7). */ - if (!tree_fits_uhwi_p (len) - || tree_to_uhwi (len) <= (unsigned HOST_WIDE_INT) ~idx) + if (!tree_fits_uhwi_p (len)) return; + + unsigned HOST_WIDE_INT clen = tree_to_uhwi (len); + unsigned HOST_WIDE_INT nonzero_chars = ~idx; + newlen = build_int_cst (size_type_node, MIN (nonzero_chars, clen)); + full_string_p = clen > nonzero_chars; } if (olddsi != NULL && TREE_CODE (len) == SSA_NAME) @@ -1592,16 +1690,13 @@ handle_builtin_memcpy (enum built_in_fun if (didx == 0) return; } - if (si != NULL) - newlen = si->length; - else - newlen = build_int_cst (size_type_node, ~idx); oldlen = NULL_TREE; if (olddsi != NULL) { dsi = unshare_strinfo (olddsi); - oldlen = olddsi->length; - dsi->length = newlen; + oldlen = olddsi->nonzero_chars; + dsi->nonzero_chars = newlen; + dsi->full_string_p = full_string_p; /* Break the chain, so adjust_related_strinfo on later pointers in the chain won't adjust this one anymore. */ dsi->next = 0; @@ -1610,7 +1705,7 @@ handle_builtin_memcpy (enum built_in_fun } else { - dsi = new_strinfo (dst, didx, newlen); + dsi = new_strinfo (dst, didx, newlen, full_string_p); set_strinfo (didx, dsi); find_equal_ptrs (dst, didx); } @@ -1623,12 +1718,11 @@ handle_builtin_memcpy (enum built_in_fun if (oldlen == NULL_TREE) ; else if (integer_zerop (oldlen)) - adj = dsi->length; + adj = newlen; else if (TREE_CODE (oldlen) == INTEGER_CST - || TREE_CODE (dsi->length) == INTEGER_CST) - adj = fold_build2_loc (loc, MINUS_EXPR, - TREE_TYPE (dsi->length), dsi->length, - fold_convert_loc (loc, TREE_TYPE (dsi->length), + || TREE_CODE (newlen) == INTEGER_CST) + adj = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (newlen), newlen, + fold_convert_loc (loc, TREE_TYPE (newlen), oldlen)); if (adj != NULL_TREE) adjust_related_strinfos (loc, dsi, adj); @@ -1640,27 +1734,30 @@ handle_builtin_memcpy (enum built_in_fun if (si != NULL) si->dont_invalidate = true; - lhs = gimple_call_lhs (stmt); - switch (bcode) + if (full_string_p) { - case BUILT_IN_MEMCPY: - case BUILT_IN_MEMCPY_CHK: - case BUILT_IN_MEMCPY_CHKP: - case BUILT_IN_MEMCPY_CHK_CHKP: - /* Allow adjust_last_stmt to decrease this memcpy's size. */ - laststmt.stmt = stmt; - laststmt.len = dsi->length; - laststmt.stridx = dsi->idx; - if (lhs) - ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx; - break; - case BUILT_IN_MEMPCPY: - case BUILT_IN_MEMPCPY_CHK: - case BUILT_IN_MEMPCPY_CHKP: - case BUILT_IN_MEMPCPY_CHK_CHKP: - break; - default: - gcc_unreachable (); + lhs = gimple_call_lhs (stmt); + switch (bcode) + { + case BUILT_IN_MEMCPY: + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMCPY_CHKP: + case BUILT_IN_MEMCPY_CHK_CHKP: + /* Allow adjust_last_stmt to decrease this memcpy's size. */ + laststmt.stmt = stmt; + laststmt.len = dsi->nonzero_chars; + laststmt.stridx = dsi->idx; + if (lhs) + ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx; + break; + case BUILT_IN_MEMPCPY: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMPCPY_CHKP: + case BUILT_IN_MEMPCPY_CHK_CHKP: + break; + default: + gcc_unreachable (); + } } } @@ -1709,14 +1806,15 @@ handle_builtin_strcat (enum built_in_fun } if (dsi == NULL) { - dsi = new_strinfo (dst, didx, NULL_TREE); + dsi = new_strinfo (dst, didx, NULL_TREE, false); set_strinfo (didx, dsi); find_equal_ptrs (dst, didx); } else { dsi = unshare_strinfo (dsi); - dsi->length = NULL_TREE; + dsi->nonzero_chars = NULL_TREE; + dsi->full_string_p = false; dsi->next = 0; dsi->endptr = NULL_TREE; } @@ -1740,7 +1838,7 @@ handle_builtin_strcat (enum built_in_fun } loc = gimple_location (stmt); - dstlen = dsi->length; + dstlen = dsi->nonzero_chars; endptr = dsi->endptr; dsi = unshare_strinfo (dsi); @@ -1750,14 +1848,17 @@ handle_builtin_strcat (enum built_in_fun if (srclen != NULL_TREE) { - dsi->length = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (dsi->length), - dsi->length, srclen); + dsi->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR, + TREE_TYPE (dsi->nonzero_chars), + dsi->nonzero_chars, srclen); + gcc_assert (dsi->full_string_p); adjust_related_strinfos (loc, dsi, srclen); dsi->dont_invalidate = true; } else { - dsi->length = NULL; + dsi->nonzero_chars = NULL; + dsi->full_string_p = false; if (lhs == NULL_TREE && builtin_decl_implicit_p (BUILT_IN_STPCPY)) dsi->dont_invalidate = true; } @@ -1890,7 +1991,7 @@ handle_builtin_malloc (enum built_in_fun tree length = NULL_TREE; if (bcode == BUILT_IN_CALLOC) length = build_int_cst (size_type_node, 0); - strinfo *si = new_strinfo (lhs, idx, length); + strinfo *si = new_strinfo (lhs, idx, length, length != NULL_TREE); if (bcode == BUILT_IN_CALLOC) si->endptr = lhs; set_strinfo (idx, si); @@ -1932,7 +2033,8 @@ handle_builtin_memset (gimple_stmt_itera gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt1); update_gimple_call (&gsi1, builtin_decl_implicit (BUILT_IN_CALLOC), 2, size, build_one_cst (size_type_node)); - si1->length = build_int_cst (size_type_node, 0); + si1->nonzero_chars = build_int_cst (size_type_node, 0); + si1->full_string_p = true; si1->stmt = gsi_stmt (gsi1); } else @@ -2064,18 +2166,20 @@ handle_pointer_plus (gimple_stmt_iterato } si = get_strinfo (idx); - if (si == NULL || si->length == NULL_TREE) + if (si == NULL || si->nonzero_chars == NULL_TREE) return; off = gimple_assign_rhs2 (stmt); zsi = NULL; - if (operand_equal_p (si->length, off, 0)) + if (si->full_string_p && operand_equal_p (si->nonzero_chars, off, 0)) zsi = zero_length_string (lhs, si); else if (TREE_CODE (off) == SSA_NAME) { gimple *def_stmt = SSA_NAME_DEF_STMT (off); if (gimple_assign_single_p (def_stmt) - && operand_equal_p (si->length, gimple_assign_rhs1 (def_stmt), 0)) + && si->full_string_p + && operand_equal_p (si->nonzero_chars, + gimple_assign_rhs1 (def_stmt), 0)) zsi = zero_length_string (lhs, si); } if (zsi != NULL @@ -2101,63 +2205,63 @@ handle_char_store (gimple_stmt_iterator strinfo *si = NULL; gimple *stmt = gsi_stmt (*gsi); tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + unsigned HOST_WIDE_INT offset = 0; if (TREE_CODE (lhs) == MEM_REF && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME) { - if (integer_zerop (TREE_OPERAND (lhs, 1))) + tree mem_offset = TREE_OPERAND (lhs, 1); + if (tree_fits_uhwi_p (mem_offset)) { - ssaname = TREE_OPERAND (lhs, 0); - idx = get_stridx (ssaname); + /* Get the strinfo for the base, and use it if it starts with at + least OFFSET nonzero characters. This is trivially true if + OFFSET is zero. */ + offset = tree_to_uhwi (mem_offset); + idx = get_stridx (TREE_OPERAND (lhs, 0)); + if (idx > 0) + si = get_strinfo (idx); + if (offset == 0) + ssaname = TREE_OPERAND (lhs, 0); + else if (si == NULL || compare_nonzero_chars (si, offset) < 0) + return true; } } else - idx = get_addr_stridx (lhs, NULL_TREE); + { + idx = get_addr_stridx (lhs, NULL_TREE, &offset); + if (idx > 0) + si = get_strinfo (idx); + } - if (idx > 0) + bool storing_zero_p = initializer_zerop (rhs); + bool storing_nonzero_p = (!storing_zero_p + && TREE_CODE (rhs) == INTEGER_CST + && integer_nonzerop (rhs)); + + if (si != NULL) { - si = get_strinfo (idx); - if (si != NULL && si->length != NULL_TREE && integer_zerop (si->length)) - { - if (initializer_zerop (gimple_assign_rhs1 (stmt))) - { - /* When storing '\0', the store can be removed - if we know it has been stored in the current function. */ - if (!stmt_could_throw_p (stmt) && si->writable) - { - unlink_stmt_vdef (stmt); - release_defs (stmt); - gsi_remove (gsi, true); - return false; - } - else - { - si->writable = true; - gsi_next (gsi); - return false; - } + int cmp = compare_nonzero_chars (si, offset); + gcc_assert (offset == 0 || cmp >= 0); + if (storing_zero_p && cmp == 0 && si->full_string_p) + { + /* When overwriting a '\0' with a '\0', the store can be removed + if we know it has been stored in the current function. */ + if (!stmt_could_throw_p (stmt) && si->writable) + { + unlink_stmt_vdef (stmt); + release_defs (stmt); + gsi_remove (gsi, true); + return false; } else - /* Otherwise this statement overwrites the '\0' with - something, if the previous stmt was a memcpy, - its length may be decreased. */ - adjust_last_stmt (si, stmt, false); - } - else if (si != NULL && integer_zerop (gimple_assign_rhs1 (stmt))) - { - si = unshare_strinfo (si); - si->length = build_int_cst (size_type_node, 0); - si->endptr = NULL; - si->prev = 0; - si->next = 0; - si->stmt = NULL; - si->first = 0; - si->writable = true; - if (ssaname && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname)) - si->endptr = ssaname; - si->dont_invalidate = true; + { + si->writable = true; + gsi_next (gsi); + return false; + } } - /* If si->length is non-zero constant, we aren't overwriting '\0', + /* If si->nonzero_chars > OFFSET, we aren't overwriting '\0', and if we aren't storing '\0', we know that the length of the string and any other zero terminated string in memory remains the same. In that case we move to the next gimple statement and @@ -2177,35 +2281,74 @@ handle_char_store (gimple_stmt_iterator bar (len, len2, len3, len4); } */ - else if (si != NULL && si->length != NULL_TREE - && TREE_CODE (si->length) == INTEGER_CST - && integer_nonzerop (gimple_assign_rhs1 (stmt))) + else if (storing_nonzero_p && cmp > 0) { gsi_next (gsi); return false; } + else if (storing_zero_p || storing_nonzero_p || (offset != 0 && cmp > 0)) + { + /* When storing_nonzero_p, we know that the string now starts + with OFFSET + 1 nonzero characters, but don't know whether + there's a following nul terminator. + + When storing_zero_p, we know that the string is now OFFSET + characters long. + + Otherwise, we're storing an unknown value at offset OFFSET, + so need to clip the nonzero_chars to OFFSET. */ + location_t loc = gimple_location (stmt); + tree oldlen = si->nonzero_chars; + if (cmp == 0 && si->full_string_p) + /* We're overwriting the nul terminator with a nonzero or + unknown character. If the previous stmt was a memcpy, + its length may be decreased. */ + adjust_last_stmt (si, stmt, false); + si = unshare_strinfo (si); + if (storing_nonzero_p) + si->nonzero_chars = build_int_cst (size_type_node, offset + 1); + else + si->nonzero_chars = build_int_cst (size_type_node, offset); + si->full_string_p = storing_zero_p; + if (storing_zero_p + && ssaname + && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname)) + si->endptr = ssaname; + else + si->endptr = NULL; + si->next = 0; + si->stmt = NULL; + si->writable = true; + si->dont_invalidate = true; + if (oldlen) + { + tree adj = fold_build2_loc (loc, MINUS_EXPR, size_type_node, + si->nonzero_chars, oldlen); + adjust_related_strinfos (loc, si, adj); + } + else + si->prev = 0; + } } - else if (idx == 0 && initializer_zerop (gimple_assign_rhs1 (stmt))) + else if (idx == 0 && (storing_zero_p || storing_nonzero_p)) { if (ssaname) - { - si = zero_length_string (ssaname, NULL); - if (si != NULL) - si->dont_invalidate = true; - } + idx = new_stridx (ssaname); else + idx = new_addr_stridx (lhs); + if (idx != 0) { - int idx = new_addr_stridx (lhs); - if (idx != 0) - { - si = new_strinfo (build_fold_addr_expr (lhs), idx, - build_int_cst (size_type_node, 0)); - set_strinfo (idx, si); - si->dont_invalidate = true; - } + tree ptr = (ssaname ? ssaname : build_fold_addr_expr (lhs)); + tree len = storing_nonzero_p ? size_one_node : size_zero_node; + si = new_strinfo (ptr, idx, len, storing_zero_p); + set_strinfo (idx, si); + if (storing_zero_p + && ssaname + && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname)) + si->endptr = ssaname; + si->dont_invalidate = true; + si->writable = true; } - if (si != NULL) - si->writable = true; } else if (idx == 0 && TREE_CODE (gimple_assign_rhs1 (stmt)) == STRING_CST @@ -2220,14 +2363,14 @@ handle_char_store (gimple_stmt_iterator if (idx != 0) { si = new_strinfo (build_fold_addr_expr (lhs), idx, - build_int_cst (size_type_node, l)); + build_int_cst (size_type_node, l), true); set_strinfo (idx, si); si->dont_invalidate = true; } } } - if (si != NULL && initializer_zerop (gimple_assign_rhs1 (stmt))) + if (si != NULL && offset == 0 && storing_zero_p) { /* Allow adjust_last_stmt to remove it if the stored '\0' is immediately overwritten. */ Index: gcc/testsuite/gcc.dg/strlenopt-32.c =================================================================== --- /dev/null 2017-05-16 16:51:34.993412001 +0100 +++ gcc/testsuite/gcc.dg/strlenopt-32.c 2017-05-17 07:33:03.919877480 +0100 @@ -0,0 +1,193 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-strlen" } */ + +#include "strlenopt.h" + +char temp[30]; +volatile int v; + +size_t __attribute__ ((noinline, noclone)) +f1 (void) +{ + char a[30]; + v += 1; + memcpy (a, "1234567", 7); + memcpy (a + 7, "89abcdefg", 9); + memcpy (a + 16, "h", 2); + return strlen (a); // This strlen should be optimized into 17. +} + +size_t __attribute__ ((noinline, noclone)) +f2 (char *a) +{ + v += 2; + memcpy (a, "1234567", 7); + memcpy (a + 7, "89abcdefg", 9); + memcpy (a + 16, "h", 2); + return strlen (a); // This strlen should be optimized into 17. +} + +size_t __attribute__ ((noinline, noclone)) +f3 (void) +{ + char a[30]; + v += 3; + a[0] = '1'; + memcpy (a + 1, "2345678", 8); + return strlen (a); // This strlen should be optimized into 8. +} + +size_t __attribute__ ((noinline, noclone)) +f4 (char *a) +{ + v += 4; + a[0] = '1'; + memcpy (a + 1, "2345678", 8); + return strlen (a); // This strlen should be optimized into 8. +} + +size_t __attribute__ ((noinline, noclone)) +f5 (void) +{ + char a[30]; + v += 5; + a[0] = '1'; + a[1] = '2'; + a[2] = '3'; + memcpy (a + 3, "456", 3); + a[6] = '7'; + a[7] = 0; + return strlen (a); // This strlen should be optimized into 7. +} + +size_t __attribute__ ((noinline, noclone)) +f6 (char *a) +{ + v += 6; + a[0] = '1'; + a[1] = '2'; + a[2] = '3'; + memcpy (a + 3, "456", 3); + a[6] = '7'; + a[7] = 0; + return strlen (a); // This strlen should be optimized into 7. +} + +size_t __attribute__ ((noinline, noclone)) +f7 (void) +{ + char a[30]; + v += 7; + strcpy (a, "abcde"); + int len1 = strlen (a); + a[2] = '_'; + int len2 = strlen (a); + return len1 + len2; // This should be optimized into 10. +} + +size_t __attribute__ ((noinline, noclone)) +f8 (char *a) +{ + v += 8; + strcpy (a, "abcde"); + int len1 = strlen (a); + a[2] = '_'; + int len2 = strlen (a); + return len1 + len2; // This should be optimized into 10. +} + +size_t __attribute__ ((noinline, noclone)) +f9 (char b) +{ + char a[30]; + v += 9; + strcpy (a, "foo.bar"); + a[4] = b; + a[3] = 0; + return strlen (a); // This should be optimized into 3. +} + +size_t __attribute__ ((noinline, noclone)) +f10 (char *a, char b) +{ + v += 10; + strcpy (a, "foo.bar"); + a[4] = b; + a[3] = 0; + return strlen (a); // This should be optimized into 3. +} + +size_t __attribute__ ((noinline, noclone)) +f11 (void) +{ + char a[30]; + v += 11; + strcpy (temp, "123456"); + memcpy (a, temp, 7); + return strlen (a); // This should be optimized into 6. +} + +size_t __attribute__ ((noinline, noclone)) +f12 (char *a) +{ + v += 12; + strcpy (temp, "123456"); + memcpy (a, temp, 7); + return strlen (a); // This should be optimized into 6. +} + +size_t __attribute__ ((noinline, noclone)) +f13 (void) +{ + char a[30]; + v += 13; + strcpy (temp, "1234567"); + memcpy (a, temp, 7); + a[7] = 0; + return strlen (a); // This should be optimized into 7. +} + +size_t __attribute__ ((noinline, noclone)) +f14 (char *a) +{ + v += 14; + strcpy (temp, "1234567"); + memcpy (a, temp, 7); + a[7] = 0; + return strlen (a); // This should be optimized into 7. +} + +size_t __attribute__ ((noinline, noclone)) +f15 (void) +{ + char a[30]; + v += 15; + strcpy (temp, "12345679"); + memcpy (a, temp, 7); + a[7] = 0; + return strlen (a); // This should be optimized into 7. +} + +size_t __attribute__ ((noinline, noclone)) +f16 (char *a) +{ + v += 16; + strcpy (temp, "123456789"); + memcpy (a, temp, 7); + a[7] = 0; + return strlen (a); // This should be optimized into 7. +} + +int +main () +{ + char a[30]; + if (f1 () != 17 || f2 (a) != 17 || f3 () != 8 || f4 (a) != 8 + || f5 () != 7 || f6 (a) != 7 || f7 () != 10 || f8 (a) != 10 + || f9 ('_') != 3 || f10 (a, '_') != 3 || f11 () != 6 || f12 (a) != 6 + || f13 () != 7 || f14 (a) != 7 || f15 () != 7 || f16 (a) != 7) + abort (); + return 0; +} + +/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-33.c =================================================================== --- /dev/null 2017-05-16 16:51:34.993412001 +0100 +++ gcc/testsuite/gcc.dg/strlenopt-33.c 2017-05-17 07:33:03.919877480 +0100 @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-strlen" } */ + +#include "strlenopt.h" + +volatile int v; + +size_t __attribute__ ((noinline, noclone)) +f1 (char *b) +{ + char a[30]; + v += 1; + strcpy (a, b); + // This needs to stay. + int len1 = strlen (a); + a[0] = '_'; + a[1] = 0; + return len1 + strlen (a); +} + +size_t __attribute__ ((noinline, noclone)) +f2 (char *a, char *b) +{ + v += 2; + strcpy (a, b); + // This needs to stay. + int len1 = strlen (a); + a[0] = '_'; + a[1] = 0; + return len1 + strlen (a); +} + +int +main () +{ + char a[30]; + if (f1 ("foo") != 4 || f2 (a, "foobar") != 7) + abort (); + return 0; +} + +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-33g.c =================================================================== --- /dev/null 2017-05-16 16:51:34.993412001 +0100 +++ gcc/testsuite/gcc.dg/strlenopt-33g.c 2017-05-17 07:33:03.919877480 +0100 @@ -0,0 +1,45 @@ +/* { dg-do run { target *-*-linux* *-*-gnu* } } */ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-strlen" } */ + +#define USE_GNU +#include "strlenopt.h" + +volatile int v; + +size_t __attribute__ ((noinline, noclone)) +f1 (char *b) +{ + char a[30]; + v += 1; + // Should be converted to stpcpy. + strcpy (a, b); + int len1 = strlen (a); + a[0] = '_'; + a[1] = 0; + return len1 + strlen (a); +} + +size_t __attribute__ ((noinline, noclone)) +f2 (char *a, char *b) +{ + v += 2; + // Should be converted to stpcpy. + strcpy (a, b); + int len1 = strlen (a); + a[0] = '_'; + a[1] = 0; + return len1 + strlen (a); +} + +int +main () +{ + char a[30]; + if (f1 ("foo") != 4 || f2 (a, "foobar") != 7) + abort (); + return 0; +} + +/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ +/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-34.c =================================================================== --- /dev/null 2017-05-16 16:51:34.993412001 +0100 +++ gcc/testsuite/gcc.dg/strlenopt-34.c 2017-05-17 07:33:14.155034020 +0100 @@ -0,0 +1,38 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-strlen" } */ + +#include "strlenopt.h" + +volatile int v; + +size_t __attribute__ ((noinline, noclone)) +f1 (char b) +{ + char a[30]; + v += 1; + strcpy (a, "foo.bar"); + a[3] = b; + a[4] = 0; + return strlen (a); +} + +size_t __attribute__ ((noinline, noclone)) +f2 (char *a, char b) +{ + v += 2; + strcpy (a, "foo.bar"); + a[3] = b; + a[4] = 0; + return strlen (a); +} + +int +main () +{ + char a[30]; + if (f1 ('_') != 4 || f1 (0) != 3 || f2 (a, '_') != 4 || f2 (a, 0) != 3) + abort (); + return 0; +} + +/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ Index: gcc/testsuite/gcc.dg/strlenopt-35.c =================================================================== --- /dev/null 2017-05-16 16:51:34.993412001 +0100 +++ gcc/testsuite/gcc.dg/strlenopt-35.c 2017-05-17 07:33:03.919877480 +0100 @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-strlen" } */ + +#include "strlenopt.h" + +volatile int v; + +size_t +f1 (char *a1) +{ + v += 1; + size_t x = strlen (a1); + char *a2 = a1 + x; + a2[0] = '1'; + a2[1] = '2'; + a2[2] = '3'; + a2[3] = 0; + return strlen (a1); +} + +int +main () +{ + char a[30]; + strcpy (a, "abcd"); + if (f1 (a) != 7) + abort (); + return 0; +} + +/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */