diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b99a4c54788f4f0307cc5def826b6aa8ddc10bc5..a142fab00821f91979ca471382db0462f748cb76 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,13 @@
+2000-11-07  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
+
+	* builtins.c (expand_builtin_strpbrk): New function.
+	(expand_builtin): Handle BUILT_IN_STRPBRK.
+
+	* builtins.def (BUILT_IN_STRPBRK): New entry.
+
+	* c-common.c (c_common_nodes_and_builtins): Declare builtin
+	strpbrk.
+
 2000-11-07  David O'Brien  <obrien@dragon.nuxi.com>
 
 	* config/alpha/freebsd.h: New file -- FreeBSD/alpha architecture file.
diff --git a/gcc/builtins.c b/gcc/builtins.c
index c058ebaca2c401057f3fb6e8dd4a7ec95c8aa8fc..0021f76075792fa102b8a769a36cb8de9873190b 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -109,6 +109,8 @@ static rtx expand_builtin_bzero		PARAMS ((tree));
 static rtx expand_builtin_strlen	PARAMS ((tree, rtx));
 static rtx expand_builtin_strstr	PARAMS ((tree, rtx,
 						 enum machine_mode));
+static rtx expand_builtin_strpbrk	PARAMS ((tree, rtx,
+						 enum machine_mode));
 static rtx expand_builtin_alloca	PARAMS ((tree, rtx));
 static rtx expand_builtin_ffs		PARAMS ((tree, rtx, rtx));
 static rtx expand_builtin_frame_address	PARAMS ((tree));
@@ -1459,6 +1461,100 @@ expand_builtin_strstr (arglist, target, mode)
     }
 }
 
+/* Expand a call to the strpbrk builtin.  Return 0 if we failed the
+   caller should emit a normal call, otherwise try to get the result
+   in TARGET, if convenient (and in mode MODE if that's convenient).  */
+
+static rtx
+expand_builtin_strpbrk (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
+{
+  if (arglist == 0
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+      || TREE_CHAIN (arglist) == 0
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+    return 0;
+  else
+    {
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      tree len1 = c_strlen (s1), len2 = c_strlen (s2);
+      tree stripped_s1 = s1, stripped_s2 = s2;
+
+      STRIP_NOPS (stripped_s1);
+      if (stripped_s1 && TREE_CODE (stripped_s1) == ADDR_EXPR)
+	stripped_s1 = TREE_OPERAND (stripped_s1, 0);
+      STRIP_NOPS (stripped_s2);
+      if (stripped_s2 && TREE_CODE (stripped_s2) == ADDR_EXPR)
+	stripped_s2 = TREE_OPERAND (stripped_s2, 0);
+
+      /* If both arguments are constants, calculate the result now.  */
+      if (len1 && len2
+	  && TREE_CODE (stripped_s1) == STRING_CST
+	  && TREE_CODE (stripped_s2) == STRING_CST)
+        {
+	  const char *const result =
+	    strpbrk (TREE_STRING_POINTER (stripped_s1),
+		     TREE_STRING_POINTER (stripped_s2));
+
+	  if (result)
+	    {
+	      long offset = result - TREE_STRING_POINTER (stripped_s1);
+
+	      /* Return an offset into the constant string argument.  */
+	      return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+					       s1, ssize_int (offset))),
+				  target, mode, EXPAND_NORMAL);
+	    }
+	  else
+	    return const0_rtx;
+	}
+
+      /* We must have been able to figure out the second argument's
+         length to do anything else.  */
+      if (!len2)
+	return 0;
+
+      /* OK, handle some cases.  */
+      switch (compare_tree_int (len2, 1))
+        {
+	case -1: /* length is 0, return NULL.  */
+	  {
+	    /* Evaluate and ignore the arguments in case they had
+	       side-effects.  */
+	    expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL);
+	    expand_expr (s2, const0_rtx, VOIDmode, EXPAND_NORMAL);
+	    return const0_rtx;
+	  }
+	case 0: /* length is 1, return strchr(s1, s2[0]).  */
+	  {
+	    tree call_expr, fn = built_in_decls[BUILT_IN_STRCHR];
+
+	    if (!fn)
+	      return 0;
+
+	    /* New argument list transforming strpbrk(s1, s2) to
+	       strchr(s1, s2[0]).  */
+	    arglist =
+	      build_tree_list (NULL_TREE, build_int_2
+			       (TREE_STRING_POINTER (stripped_s2)[0], 0));
+	    arglist = tree_cons (NULL_TREE, s1, arglist);
+	    call_expr = build1 (ADDR_EXPR,
+				build_pointer_type (TREE_TYPE (fn)), fn);
+	    call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+			       call_expr, arglist, NULL_TREE);
+	    TREE_SIDE_EFFECTS (call_expr) = 1;
+	    return expand_expr (call_expr, target, mode, EXPAND_NORMAL);
+	  }
+	case 1: /* length is greater than 1, really call strpbrk.  */
+	  return 0;
+	default:
+	  abort();
+	}
+    }
+}
+
 /* Expand a call to the memcpy builtin, with arguments in ARGLIST.  */
 static rtx
 expand_builtin_memcpy (arglist)
@@ -2503,7 +2599,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
 	  || fcode == BUILT_IN_MEMCPY || fcode == BUILT_IN_MEMCMP
 	  || fcode == BUILT_IN_BCMP || fcode == BUILT_IN_BZERO
 	  || fcode == BUILT_IN_STRLEN || fcode == BUILT_IN_STRCPY
-	  || fcode == BUILT_IN_STRSTR
+	  || fcode == BUILT_IN_STRSTR || fcode == BUILT_IN_STRPBRK
 	  || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS
 	  || fcode == BUILT_IN_PUTCHAR || fcode == BUILT_IN_PUTS
 	  || fcode == BUILT_IN_PRINTF || fcode == BUILT_IN_FPUTC
@@ -2638,6 +2734,12 @@ expand_builtin (exp, target, subtarget, mode, ignore)
 	return target;
       break;
       
+    case BUILT_IN_STRPBRK:
+      target = expand_builtin_strpbrk (arglist, target, mode);
+      if (target)
+	return target;
+      break;
+      
     case BUILT_IN_MEMCPY:
       target = expand_builtin_memcpy (arglist);
       if (target)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 13e0f41839ec3c63cbbe56ff3ec6346ad853cf34..261c76a663cfa78f933c6cf03b2f037de8be2329 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -40,6 +40,7 @@ DEF_BUILTIN(BUILT_IN_STRCPY)
 DEF_BUILTIN(BUILT_IN_STRCMP)
 DEF_BUILTIN(BUILT_IN_STRLEN)
 DEF_BUILTIN(BUILT_IN_STRSTR)
+DEF_BUILTIN(BUILT_IN_STRPBRK)
 DEF_BUILTIN(BUILT_IN_STRCHR)
 DEF_BUILTIN(BUILT_IN_FSQRT)
 DEF_BUILTIN(BUILT_IN_SIN)
diff --git a/gcc/c-common.c b/gcc/c-common.c
index 1b7b028e92dc69f7c100bbf6b56bee85f49a14c1..ac0a10107074eb2b82df174005db78921aaf97a2 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -4935,7 +4935,7 @@ c_common_nodes_and_builtins ()
 						 const_string_type_node,
 						 endlink)));
 
-  /* Prototype for strstr, etc.  */
+  /* Prototype for strstr, strpbrk, etc.  */
   string_ftype_string_string
     = build_function_type (string_type_node,
 			   tree_cons (NULL_TREE, const_string_type_node,
@@ -5174,6 +5174,8 @@ c_common_nodes_and_builtins ()
 		    BUILT_IN_STRCMP, BUILT_IN_NORMAL, "strcmp");
   builtin_function ("__builtin_strstr", string_ftype_string_string,
 		    BUILT_IN_STRSTR, BUILT_IN_NORMAL, "strstr");
+  builtin_function ("__builtin_strpbrk", string_ftype_string_string,
+		    BUILT_IN_STRPBRK, BUILT_IN_NORMAL, "strpbrk");
   built_in_decls[BUILT_IN_STRCHR] =
     builtin_function ("__builtin_strchr", string_ftype_string_int,
 		    BUILT_IN_STRCHR, BUILT_IN_NORMAL, "strchr");
@@ -5247,6 +5249,8 @@ c_common_nodes_and_builtins ()
 			BUILT_IN_NORMAL, NULL_PTR);
       builtin_function ("strstr", string_ftype_string_string, BUILT_IN_STRSTR,
 			BUILT_IN_NORMAL, NULL_PTR);
+      builtin_function ("strpbrk", string_ftype_string_string, BUILT_IN_STRPBRK,
+			BUILT_IN_NORMAL, NULL_PTR);
       builtin_function ("strcpy", string_ftype_ptr_ptr, BUILT_IN_STRCPY,
 			BUILT_IN_NORMAL, NULL_PTR);
       builtin_function ("strlen", strlen_ftype, BUILT_IN_STRLEN,
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 61bc1c18beb816cf6ab2125263664c091c5d6cc1..33091b9c94503c22d203932082f5e8ca05a63c12 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2000-11-07  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
+
+	* gcc.c-torture/execute/string-opt-2.c: New test.
+
 2000-11-07  Nathan Sidwell  <nathan@codesourcery.com>
 
 	* g++.old-deja/g++.pt/crash60.C: New test.
diff --git a/gcc/testsuite/gcc.c-torture/execute/string-opt-2.c b/gcc/testsuite/gcc.c-torture/execute/string-opt-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..87144ded67cbc6a224f53f2d0080507dafb3b6d7
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/string-opt-2.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 2000  Free Software Foundation.
+
+   Ensure all expected transformations of builtin strpbrk occur and
+   perform correctly.
+
+   Written by Kaveh R. Ghazi, 11/6/2000.  */
+
+extern void abort(void);
+extern char *strpbrk (const char *, const char *);
+
+void fn (const char *foo, const char *const *bar)
+{
+  if (strcmp(strpbrk ("hello world", "lrooo"), "llo world") != 0)
+    abort();
+  if (strpbrk (foo, "") != 0)
+    abort();
+  if (strpbrk (foo + 4, "") != 0)
+    abort();
+  if (strpbrk (*bar--, "") != 0)
+    abort();
+  if (strpbrk (*bar, "h") != foo)
+    abort();
+  if (strpbrk (foo, "h") != foo)
+    abort();
+  if (strpbrk (foo, "w") != foo + 6)
+    abort();
+  if (strpbrk (foo + 6, "o") != foo + 7)
+    abort();
+}
+
+int main()
+{
+  const char *const foo[] = { "hello world", "bye bye world" };
+  fn (foo[0], foo + 1);
+  return 0;
+}
+
+
+#ifdef __OPTIMIZE__
+/* When optimizing, all the above cases should be transformed into
+   something else.  So any remaining calls to the original function
+   should abort.  */
+char *
+strpbrk(const char *s1, const char *s2)
+{
+  abort();
+}
+#endif