diff mbox

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
State New
Headers show

Commit Message

Jakub Jelinek Dec. 7, 2016, 5:30 p.m. UTC
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  <jakub@redhat.com>

	* 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

Comments

Jeff Law Dec. 7, 2016, 7:43 p.m. UTC | #1
On 12/07/2016 10:30 AM, Jakub Jelinek wrote:
> 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  <jakub@redhat.com>
>
> 	* 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.
OK.
jeff
diff mbox

Patch

--- 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, "");