From patchwork Wed Dec 7 17:30:37 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 703679 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 3tYlvL0X1qz9sdn for ; Thu, 8 Dec 2016 04:33:01 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="nKpkuYFo"; 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:date :from:to:cc:subject:message-id:reply-to:mime-version :content-type; q=dns; s=default; b=SuUlH5lsL7CJXe94zqRBWw0B2pzc1 Y1Soe1OQ5k7DO5KlcwuWZdawrxfwdW58qkRIUz6pNmVAlZbILn3C8WOsjsgfEYUf vz+G5QNsXBKCzyMIxxVBS4V1OgJ4pYDPkVBLvLn4Hnm0rQs0tH/pOC+sJ86c8K7n e1ttXX2I6UJI5c= 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:date :from:to:cc:subject:message-id:reply-to:mime-version :content-type; s=default; bh=NrFtP9VkNaouRrOZPIC46JeVvgw=; b=nKp kuYFovzbQc1N8avwwaro6fmA9XGcMTHLPX2+3srRGrlkV/6tQQABJTnJrGCZ6MG7 1XVEWN8zeI2a5hpZESjjo7nNJCkKLjunImpVL/3kEEawJqq1XpQOgXbt12BrSooL r/WGCjZnAZSaRfhdTIvIJrzFISixsdH9T5ej639Y= Received: (qmail 369 invoked by alias); 7 Dec 2016 17:31:31 -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 130894 invoked by uid 89); 7 Dec 2016 17:31:30 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-4.9 required=5.0 tests=BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=needle, gsi_stmt, fed X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 07 Dec 2016 17:30:43 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C3A8161963; Wed, 7 Dec 2016 17:30:41 +0000 (UTC) Received: from tucnak.zalov.cz (ovpn-204-100.brq.redhat.com [10.40.204.100]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id uB7HUeOp016565 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Wed, 7 Dec 2016 12:30:41 -0500 Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.15.2/8.15.2) with ESMTP id uB7HUcqH030412; Wed, 7 Dec 2016 18:30:38 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.15.2/8.15.2/Submit) id uB7HUbtj030155; Wed, 7 Dec 2016 18:30:37 +0100 Date: Wed, 7 Dec 2016 18:30:37 +0100 From: Jakub Jelinek To: Richard Biener Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] Move __builtin_strstr folding from builtins.c to fold-const-call.c + gimple-fold.c; plus a constexpr testcase for strstr Message-ID: <20161207173037.GQ3541@tucnak.redhat.com> Reply-To: Jakub Jelinek MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) X-IsSubscribed: yes Hi! As discussed on IRC, this patch adds a constexpr handling testcase for the strstr builtin and moves it over to the desired files (gimple-fold.c and fold-const-call.c). Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2016-12-07 Jakub Jelinek * builtins.c (fold_builtin_strstr): Removed. (fold_builtin_2): Don't call fold_builtin_strstr. * gimple-fold.c (gimple_fold_builtin_strchr): Check is_strrchr earlier in the strrchr (x, 0) -> strchr (x, 0) optimization. (gimple_fold_builtin_strstr): New function. (gimple_fold_builtin): Call it. * fold-const-call.c (fold_const_call): Handle CFN_BUILT_IN_STRSTR. Jakub --- gcc/builtins.c.jj 2016-11-18 20:04:26.000000000 +0100 +++ gcc/builtins.c 2016-12-07 11:45:05.466826715 +0100 @@ -163,7 +163,6 @@ static tree fold_builtin_3 (location_t, static tree fold_builtin_varargs (location_t, tree, tree*, int); static tree fold_builtin_strpbrk (location_t, tree, tree, tree); -static tree fold_builtin_strstr (location_t, tree, tree, tree); static tree fold_builtin_strspn (location_t, tree, tree); static tree fold_builtin_strcspn (location_t, tree, tree); @@ -8303,9 +8302,6 @@ fold_builtin_2 (location_t loc, tree fnd CASE_FLT_FN (BUILT_IN_MODF): return fold_builtin_modf (loc, arg0, arg1, type); - case BUILT_IN_STRSTR: - return fold_builtin_strstr (loc, arg0, arg1, type); - case BUILT_IN_STRSPN: return fold_builtin_strspn (loc, arg0, arg1); @@ -8729,72 +8725,6 @@ readonly_data_expr (tree exp) return false; } -/* Simplify a call to the strstr builtin. S1 and S2 are the arguments - to the call, and TYPE is its return type. - - Return NULL_TREE if no simplification was possible, otherwise return the - simplified form of the call as a tree. - - The simplified form may be a constant or other expression which - computes the same value, but in a more efficient manner (including - calls to other builtin functions). - - The call may contain arguments which need to be evaluated, but - which are not useful to determine the result of the call. In - this case we return a chain of COMPOUND_EXPRs. The LHS of each - COMPOUND_EXPR will be an argument which must be evaluated. - COMPOUND_EXPRs are chained through their RHS. The RHS of the last - COMPOUND_EXPR in the chain will contain the tree for the simplified - form of the builtin function call. */ - -static tree -fold_builtin_strstr (location_t loc, tree s1, tree s2, tree type) -{ - if (!validate_arg (s1, POINTER_TYPE) - || !validate_arg (s2, POINTER_TYPE)) - return NULL_TREE; - else - { - tree fn; - const char *p1, *p2; - - p2 = c_getstr (s2); - if (p2 == NULL) - return NULL_TREE; - - p1 = c_getstr (s1); - if (p1 != NULL) - { - const char *r = strstr (p1, p2); - tree tem; - - if (r == NULL) - return build_int_cst (TREE_TYPE (s1), 0); - - /* Return an offset into the constant string argument. */ - tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1); - return fold_convert_loc (loc, type, tem); - } - - /* The argument is const char *, and the result is char *, so we need - a type conversion here to avoid a warning. */ - if (p2[0] == '\0') - return fold_convert_loc (loc, type, s1); - - if (p2[1] != '\0') - return NULL_TREE; - - fn = builtin_decl_implicit (BUILT_IN_STRCHR); - if (!fn) - return NULL_TREE; - - /* New argument list transforming strstr(s1, s2) to - strchr(s1, s2[0]). */ - return build_call_expr_loc (loc, fn, 2, s1, - build_int_cst (integer_type_node, p2[0])); - } -} - /* Simplify a call to the strpbrk builtin. S1 and S2 are the arguments to the call, and TYPE is its return type. --- gcc/gimple-fold.c.jj 2016-11-25 18:11:05.000000000 +0100 +++ gcc/gimple-fold.c 2016-12-07 11:47:15.928167796 +0100 @@ -1506,11 +1506,11 @@ gimple_fold_builtin_strchr (gimple_stmt_ return false; /* Transform strrchr (s, 0) to strchr (s, 0) when optimizing for size. */ - if (optimize_function_for_size_p (cfun)) + if (is_strrchr && optimize_function_for_size_p (cfun)) { tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR); - if (is_strrchr && strchr_fn) + if (strchr_fn) { gimple *repl = gimple_build_call (strchr_fn, 2, str, c); replace_call_with_call_and_fold (gsi, repl); @@ -1549,6 +1549,68 @@ gimple_fold_builtin_strchr (gimple_stmt_ return true; } +/* Fold function call to builtin strstr. + If both arguments are constant, evaluate and fold the result, + additionally fold strstr (x, "") into x and strstr (x, "c") + into strchr (x, 'c'). */ +static bool +gimple_fold_builtin_strstr (gimple_stmt_iterator *gsi) +{ + gimple *stmt = gsi_stmt (*gsi); + tree haystack = gimple_call_arg (stmt, 0); + tree needle = gimple_call_arg (stmt, 1); + const char *p, *q; + + if (!gimple_call_lhs (stmt)) + return false; + + q = c_getstr (needle); + if (q == NULL) + return false; + + if ((p = c_getstr (haystack))) + { + const char *r = strstr (p, q); + + if (r == NULL) + { + replace_call_with_value (gsi, integer_zero_node); + return true; + } + + tree len = build_int_cst (size_type_node, r - p); + gimple_seq stmts = NULL; + gimple *new_stmt + = gimple_build_assign (gimple_call_lhs (stmt), POINTER_PLUS_EXPR, + haystack, len); + gimple_seq_add_stmt_without_update (&stmts, new_stmt); + gsi_replace_with_seq_vops (gsi, stmts); + return true; + } + + /* For strstr (x, "") return x. */ + if (q[0] == '\0') + { + replace_call_with_value (gsi, haystack); + return true; + } + + /* Transform strstr (x, "c") into strchr (x, 'c'). */ + if (q[1] == '\0') + { + tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR); + if (strchr_fn) + { + tree c = build_int_cst (integer_type_node, q[0]); + gimple *repl = gimple_build_call (strchr_fn, 2, haystack, c); + replace_call_with_call_and_fold (gsi, repl); + return true; + } + } + + return false; +} + /* Simplify a call to the strcat builtin. DST and SRC are the arguments to the call. @@ -3236,6 +3298,8 @@ gimple_fold_builtin (gimple_stmt_iterato case BUILT_IN_RINDEX: case BUILT_IN_STRRCHR: return gimple_fold_builtin_strchr (gsi, true); + case BUILT_IN_STRSTR: + return gimple_fold_builtin_strstr (gsi); case BUILT_IN_STRCMP: case BUILT_IN_STRCASECMP: case BUILT_IN_STRNCMP: --- gcc/fold-const-call.c.jj 2016-12-06 10:22:49.000000000 +0100 +++ gcc/fold-const-call.c 2016-12-07 11:11:38.378479382 +0100 @@ -1434,6 +1434,22 @@ fold_const_call (combined_fn fn, tree ty } return NULL_TREE; + case CFN_BUILT_IN_STRSTR: + if ((p1 = c_getstr (arg1))) + { + if ((p0 = c_getstr (arg0))) + { + const char *r = strstr (p0, p1); + if (r == NULL) + return build_int_cst (type, 0); + return fold_convert (type, + fold_build_pointer_plus_hwi (arg0, r - p0)); + } + if (*p1 == '\0') + return fold_convert (type, arg0); + } + return NULL_TREE; + default: return fold_const_call_1 (fn, type, arg0, arg1); } --- gcc/testsuite/gcc.dg/builtin-strstr-1.c.jj 2016-12-07 11:55:27.678914796 +0100 +++ gcc/testsuite/gcc.dg/builtin-strstr-1.c 2016-12-07 11:55:54.952567990 +0100 @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-not "link_error" "optimized" } } */ +/* { dg-final { scan-tree-dump-not "__builtin_strstr" "optimized" } } */ +/* { dg-final { scan-tree-dump-times "return p_\[0-9]*.D.;" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "__builtin_strchr" 1 "optimized" } } */ + +extern void link_error (void); + +void +foo (void) +{ + const char *p = "abcdef"; + const char *q = "def"; + p++; + q++; + if (__builtin_strstr (p, q) != p + 3) + link_error (); +} + +char * +bar (const char *p) +{ + return __builtin_strstr (p, ""); +} + +char * +baz (const char *p) +{ + return __builtin_strstr (p, "d"); +} --- gcc/testsuite/g++.dg/cpp0x/constexpr-strstr.C.jj 2016-12-07 11:05:05.737428518 +0100 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-strstr.C 2016-12-07 11:05:05.736428531 +0100 @@ -0,0 +1,12 @@ +// { dg-do compile { target c++11 } } + +constexpr const char *f1 (const char *p, const char *q) { return __builtin_strstr (p, q); } +constexpr const char a[] = "abcdefedcbaaaaab"; +constexpr const char b[] = "fed"; +constexpr const char c[] = "aaab"; +static_assert (f1 ("abcde", "ee") == nullptr, ""); +static_assert (f1 (a, b) == a + 5, ""); +static_assert (f1 (a, c) == a + 12, ""); +static_assert (f1 (a, "") == a, ""); +static_assert (f1 (a, "aaaaaab") == nullptr, ""); +static_assert (f1 (a, "aaa") == a + 10, "");