From patchwork Thu Dec 14 19:45:21 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Qing Zhao X-Patchwork-Id: 848755 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-469259-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="jjd0gX33"; dkim-atps=neutral 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 3yyPJg1Sdpz9s5L for ; Fri, 15 Dec 2017 06:49:06 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :mime-version:subject:message-id:date:cc:to:content-type :content-transfer-encoding; q=dns; s=default; b=rStZ4xTnzrOma+VL zGt8qiCHStYJpVyC4AuIZRCjJijByDk/zywBHi4MFCjWmgrDtL4IpT8tUYBBngnX lMEvGVNCpNVC6Y3OR8C0OB3SmZXd/4K6qCxF7BfzOoOgCrO8w1akF6hzjeqPcmB2 qd4f9L5VAeeup9CBcDk2C8SbNco= 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 :mime-version:subject:message-id:date:cc:to:content-type :content-transfer-encoding; s=default; bh=Jriyrb5ItkZ2InVBigfFIY uPRYg=; b=jjd0gX33wm/hLoxnlO9USFnrhtyF9zH5INwVxxTOpuj1s9lh+fi4aq Gilm6ErjNYRMryniVr+dnmTOd1z8MkC2En69hVhDbOHT5dJCLOm8e4bOI2meGu4e IBsCL6eBJH6DlDF4SuXJYLcXQDc15bYMY4JmFV/NHPG3yi77+mo7g= Received: (qmail 31657 invoked by alias); 14 Dec 2017 19:48:57 -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 31642 invoked by uid 89); 14 Dec 2017 19:48:56 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-21.4 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, HTML_MESSAGE, KAM_NUMSUBJECT, KAM_SHORT, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.2 spammy=b2, B2, sk:built_i, sk:BUILT_I X-HELO: userp2120.oracle.com Received: from userp2120.oracle.com (HELO userp2120.oracle.com) (156.151.31.85) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 14 Dec 2017 19:48:53 +0000 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.21/8.16.0.21) with SMTP id vBEJkiFI193980; Thu, 14 Dec 2017 19:48:50 GMT Received: from userv0022.oracle.com (userv0022.oracle.com [156.151.31.74]) by userp2120.oracle.com with ESMTP id 2euyr501n0-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 14 Dec 2017 19:48:46 +0000 Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by userv0022.oracle.com (8.14.4/8.14.4) with ESMTP id vBEJjPo7009256 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 14 Dec 2017 19:45:25 GMT Received: from abhmp0013.oracle.com (abhmp0013.oracle.com [141.146.116.19]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id vBEJjNFc027823; Thu, 14 Dec 2017 19:45:25 GMT Received: from dhcp-10-154-188-31.vpn.oracle.com (/10.154.188.31) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Thu, 14 Dec 2017 11:45:22 -0800 From: Qing Zhao Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Subject: [PATCH][Middle-end]2nd patch of PR78809 and PR83026 Message-Id: Date: Thu, 14 Dec 2017 13:45:21 -0600 Cc: jeff Law , richard Biener To: gcc-patches X-Proofpoint-Virus-Version: vendor=nai engine=5900 definitions=8745 signatures=668648 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=3 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1711220000 definitions=main-1712140271 X-IsSubscribed: yes Hi, I am not sure whether it’s proper to send this patch during this late stage. however, since the patch itself is quite straightforward, I decided to send it now. ================= 2nd Patch for PR78009 Patch for PR83026 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78809 Inline strcmp with small constant strings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83026 missing strlen optimization for strcmp of unequal strings The design doc for PR78809 is at: https://www.mail-archive.com/gcc@gcc.gnu.org/msg83822.html this patch is for the second part of change of PR78809 and PR83026: B. for strncmp (s1, s2, n) (!)= 0 or strcmp (s1, s2) (!)= 0 B.1. (PR83026) When both arguments are constant strings and it's a strcmp: * if the length of the strings are NOT equal, we can safely fold the call to a non-zero value. * otherwise, do nothing now. B.2. (PR78809) When one of the arguments is a constant string, try to replace the call with a __builtin_memcmp_eq call where possible, i.e: strncmp (s, STR, C) (!)= 0 in which, s is a pointer to a string, STR is a constant string, C is a constant. if (C <= strlen(STR) && sizeof_array(s) > C) { replace this call with memcmp (s, STR, C) (!)= 0 } if (C > strlen(STR) { it can be safely treated as a call to strcmp (s, STR) (!)= 0 can handled by the following strcmp. } strcmp (s, STR) (!)= 0 in which, s is a pointer to a string, STR is a constant string. if (sizeof_array(s) > strlen(STR)) { replace this call with memcmp (s, STR, strlen(STR)+1) (!)= 0 } (NOTE, currently, memcmp(s1, s2, N) (!)=0 has been optimized to a simple sequence to access all bytes and accumulate the overall result in GCC by https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52171 as a result, such str(n)cmp call would like to be replaced by simple sequences to access all types and accumulate the overall results. adding test case strcmpopt_2.c into gcc.dg for part B of PR78809 adding test case strcmpopt_3.c into gcc.dg for PR83026 bootstraped and tested on both X86 and Aarch64. no regression. Okay for trunk? thanks. Qing ================ gcc/ChangeLog: 2017-12-11 Qing Zhao > PR middle-end/78809 PR middle-end/83026 * tree-ssa-strlen.c (compute_string_length): New function. (handle_builtin_string_cmp): New function to handle calls to string compare functions. (strlen_optimize_stmt): Add handling to builtin string compare calls. gcc/testsuite/ChangeLog: 2017-12-11 Qing Zhao > PR middle-end/78809 * gcc.dg/strcmpopt_2.c: New testcase. PR middle-end/83026 * gcc.dg/strcmpopt_3.c: New testcase. ============ --- gcc/testsuite/gcc.dg/strcmpopt_2.c | 67 +++++++++++++ gcc/testsuite/gcc.dg/strcmpopt_3.c | 27 +++++ gcc/tree-ssa-strlen.c | 197 +++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/strcmpopt_2.c create mode 100644 gcc/testsuite/gcc.dg/strcmpopt_3.c diff --git a/gcc/testsuite/gcc.dg/strcmpopt_2.c b/gcc/testsuite/gcc.dg/strcmpopt_2.c new file mode 100644 index 0000000..ac8ff39 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strcmpopt_2.c @@ -0,0 +1,67 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-strlen" } */ + +char s[100] = {'a','b','c','d'}; +typedef struct { int x; char s[8]; } S; + +__attribute__ ((noinline)) int +f1 (S *s) +{ + return __builtin_strcmp (s->s, "abc") != 0; +} + +__attribute__ ((noinline)) int +f2 (void) +{ + return __builtin_strcmp (s, "abc") != 0; +} + +__attribute__ ((noinline)) int +f3 (S *s) +{ + return __builtin_strcmp ("abc", s->s) != 0; +} + +__attribute__ ((noinline)) int +f4 (void) +{ + return __builtin_strcmp ("abc", s) != 0; +} + +__attribute__ ((noinline)) int +f5 (S *s) +{ + return __builtin_strncmp (s->s, "abc", 3) != 0; +} + +__attribute__ ((noinline)) int +f6 (void) +{ + return __builtin_strncmp (s, "abc", 2) != 0; +} + +__attribute__ ((noinline)) int +f7 (S *s) +{ + return __builtin_strncmp ("abc", s->s, 3) != 0; +} + +__attribute__ ((noinline)) int +f8 (void) +{ + return __builtin_strncmp ("abc", s, 2) != 0; +} + +int main (void) +{ + S ss = {2, {'a','b','c'}}; + + if (f1 (&ss) != 0 || f2 () != 1 || f3 (&ss) != 0 || + f4 () != 1 || f5 (&ss) != 0 || f6 () != 0 || + f7 (&ss) != 0 || f8 () != 0) + __builtin_abort (); + + return 0; +} + +/* { dg-final { scan-tree-dump-times "memcmp_eq \\(" 8 "strlen" } } */ diff --git a/gcc/testsuite/gcc.dg/strcmpopt_3.c b/gcc/testsuite/gcc.dg/strcmpopt_3.c new file mode 100644 index 0000000..a3be431 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strcmpopt_3.c @@ -0,0 +1,27 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-strlen" } */ + +__attribute__ ((noinline)) int +f1 (void) +{ + char *s= "abcd"; + return __builtin_strcmp(s, "abc") != 0; +} + +__attribute__ ((noinline)) int +f2 (void) +{ + char *s = "ab"; + return __builtin_strcmp("abc", s) != 0; +} + +int main (void) +{ + if (f1 () != 1 + || f2 () != 1) + __builtin_abort (); + + return 0; +} + +/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen" } } */ diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 48b9241..aafa574 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -2541,6 +2541,198 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi) return false; } +/* Given an index to the strinfo vector, compute the string length for the + corresponding string. Return -1 when unknown. */ + +static HOST_WIDE_INT +compute_string_length (int idx) +{ + HOST_WIDE_INT string_leni = -1; + gcc_assert (idx != 0); + + if (idx < 0) + string_leni = ~idx; + else + { + strinfo *si = get_strinfo (idx); + if (si) + { + tree const_string_len = get_string_length (si); + string_leni + = (const_string_len && tree_fits_uhwi_p (const_string_len) + ? tree_to_uhwi(const_string_len) : -1); + } + } + return string_leni; +} + +/* Handle a call to strcmp or strncmp. When the result is ONLY used to do + equality test against zero: + + A. When both arguments are constant strings and it's a strcmp: + * if the length of the strings are NOT equal, we can safely fold the call + to a non-zero value. + * otherwise, do nothing now. + + B. When one of the arguments is constant string, try to replace the call with + a __builtin_memcmp_eq call where possible, i.e: + + strncmp (s, STR, C) (!)= 0 in which, s is a pointer to a string, STR is a + constant string, C is a constant. + if (C <= strlen(STR) && sizeof_array(s) > C) + { + replace this call with + memcmp (s, STR, C) (!)= 0 + } + if (C > strlen(STR) + { + it can be safely treated as a call to strcmp (s, STR) (!)= 0 + can handled by the following strcmp. + } + + strcmp (s, STR) (!)= 0 in which, s is a pointer to a string, STR is a + constant string. + if (sizeof_array(s) > strlen(STR)) + { + replace this call with + memcmp (s, STR, strlen(STR)+1) (!)= 0 + } + */ + +static bool +handle_builtin_string_cmp (gimple_stmt_iterator *gsi) +{ + gcall *stmt = as_a (gsi_stmt (*gsi)); + tree res = gimple_call_lhs (stmt); + use_operand_p use_p; + imm_use_iterator iter; + tree arg1 = gimple_call_arg (stmt, 0); + tree arg2 = gimple_call_arg (stmt, 1); + int idx1 = get_stridx (arg1); + int idx2 = get_stridx (arg2); + HOST_WIDE_INT length = -1; + bool is_ncmp = false; + + if (!res) + return true; + + /* When both arguments are unknown, do nothing. */ + if (idx1 == 0 && idx2 == 0) + return true; + + /* Handle strncmp function. */ + if (gimple_call_num_args (stmt) == 3) + { + tree len = gimple_call_arg (stmt, 2); + if (tree_fits_uhwi_p (len)) + length = tree_to_uhwi (len); + + is_ncmp = true; + } + + /* For strncmp, if the length argument is NOT known, do nothing. */ + if (is_ncmp && length < 0) + return true; + + /* When the result is ONLY used to do equality test against zero. */ + FOR_EACH_IMM_USE_FAST (use_p, iter, res) + { + gimple *ustmt = USE_STMT (use_p); + + if (is_gimple_debug (ustmt)) + continue; + if (gimple_code (ustmt) == GIMPLE_ASSIGN) + { + gassign *asgn = as_a (ustmt); + tree_code code = gimple_assign_rhs_code (asgn); + if ((code != EQ_EXPR && code != NE_EXPR) + || !integer_zerop (gimple_assign_rhs2 (asgn))) + return true; + } + else if (gimple_code (ustmt) == GIMPLE_COND) + { + tree_code code = gimple_cond_code (ustmt); + if ((code != EQ_EXPR && code != NE_EXPR) + || !integer_zerop (gimple_cond_rhs (ustmt))) + return true; + } + else + return true; + } + + /* When both arguments are known, and their strlens are unequal, we can + safely fold the call to a non-zero value for strcmp; + othewise, do nothing now. */ + if (idx1 != 0 && idx2 != 0) + { + HOST_WIDE_INT const_string_leni1 = -1; + HOST_WIDE_INT const_string_leni2 = -1; + const_string_leni1 = compute_string_length (idx1); + const_string_leni2 = compute_string_length (idx2); + if (!is_ncmp + && const_string_leni1 != -1 + && const_string_leni2 != -1 + && const_string_leni1 != const_string_leni2) + { + replace_call_with_value (gsi, integer_one_node); + return false; + } + return true; + } + + /* When one of args is constant string. */ + tree var_string; + HOST_WIDE_INT const_string_leni = -1; + + if (idx1) + { + const_string_leni = compute_string_length (idx1); + var_string = arg2; + } + else if (idx2) + { + const_string_leni = compute_string_length (idx2); + var_string = arg1; + } + + if (const_string_leni < 0) + return true; + + tree size[2]; + unsigned HOST_WIDE_INT var_sizei = 0; + + /* Try to get the min and max string length for var_string, the max length is + the size of the array - 1, recorded in size[1]. */ + get_range_strlen (var_string, size); + if (size[1] && tree_fits_uhwi_p (size[1])) + var_sizei = tree_to_uhwi (size[1]) + 1; + + if (var_sizei == 0) + return true; + + /* For strncmp, if length > const_string_leni , this call can be safely + transformed to a strcmp. */ + if (is_ncmp && length > const_string_leni) + is_ncmp = false; + + unsigned HOST_WIDE_INT final_length + = is_ncmp ? length : (const_string_leni + 1); + + /* Replace strcmp or strncmp with the corresponding memcmp. */ + if (var_sizei > final_length) + { + tree fn = builtin_decl_implicit (BUILT_IN_MEMCMP_EQ); + if (!fn) + return true; + tree const_string_len = build_int_cst (size_type_node, final_length); + update_gimple_call (gsi, fn, 3, arg1, arg2, const_string_len); + } + else + return true; + + return false; +} + /* Handle a POINTER_PLUS_EXPR statement. For p = "abcd" + 2; compute associated length, or if p = q + off is pointing to a '\0' character of a string, call @@ -2940,6 +3132,11 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi) if (!handle_builtin_memcmp (gsi)) return false; break; + case BUILT_IN_STRCMP: + case BUILT_IN_STRNCMP: + if (!handle_builtin_string_cmp (gsi)) + return false; + break; default: break; }