OSDN Git Service

* config/m68hc11/m68hc11.c (m68hc11_gen_highpart): Don't use
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index 7bfe10d..8a44a7c 100644 (file)
@@ -1,5 +1,6 @@
 /* Expand builtin functions.
-   Copyright (C) 1988, 92-98, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+   1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -29,16 +30,15 @@ Boston, MA 02111-1307, USA.  */
 #include "hard-reg-set.h"
 #include "except.h"
 #include "function.h"
-#include "insn-flags.h"
-#include "insn-codes.h"
 #include "insn-config.h"
 #include "expr.h"
 #include "recog.h"
 #include "output.h"
 #include "typeclass.h"
-#include "defaults.h"
 #include "toplev.h"
+#include "predict.h"
 #include "tm_p.h"
+#include "target.h"
 
 #define CALLED_AS_BUILT_IN(NODE) \
    (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
@@ -51,16 +51,40 @@ Boston, MA 02111-1307, USA.  */
 #define OUTGOING_REGNO(IN) (IN)
 #endif
 
+#ifndef PAD_VARARGS_DOWN
+#define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN
+#endif
+
+/* Define the names of the builtin function types and codes.  */
+const char *const built_in_class_names[4]
+  = {"NOT_BUILT_IN", "BUILT_IN_FRONTEND", "BUILT_IN_MD", "BUILT_IN_NORMAL"};
+
+#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA) STRINGX(X),
+const char *const built_in_names[(int) END_BUILTINS] =
+{
+#include "builtins.def"
+};
+#undef DEF_BUILTIN
+
+/* Setup an array of _DECL trees, make sure each element is
+   initialized to NULL_TREE.  */
+tree built_in_decls[(int) END_BUILTINS];
+
 tree (*lang_type_promotes_to) PARAMS ((tree));
 
 static int get_pointer_alignment       PARAMS ((tree, unsigned));
 static tree c_strlen                   PARAMS ((tree));
+static const char *c_getstr            PARAMS ((tree));
+static rtx c_readstr                   PARAMS ((const char *,
+                                                enum machine_mode));
+static int target_char_cast            PARAMS ((tree, char *)); 
 static rtx get_memory_rtx              PARAMS ((tree));
 static int apply_args_size             PARAMS ((void));
 static int apply_result_size           PARAMS ((void));
 #if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
 static rtx result_vector               PARAMS ((int, rtx));
 #endif
+static rtx expand_builtin_setjmp       PARAMS ((tree, rtx));
 static rtx expand_builtin_apply_args   PARAMS ((void));
 static rtx expand_builtin_apply_args_1 PARAMS ((void));
 static rtx expand_builtin_apply                PARAMS ((rtx, rtx, rtx));
@@ -75,16 +99,49 @@ static rtx expand_builtin_va_end    PARAMS ((tree));
 static rtx expand_builtin_va_copy      PARAMS ((tree));
 #ifdef HAVE_cmpstrsi
 static rtx expand_builtin_memcmp       PARAMS ((tree, tree, rtx));
-static rtx expand_builtin_strcmp       PARAMS ((tree, rtx));
 #endif
+static rtx expand_builtin_strcmp       PARAMS ((tree, rtx,
+                                                enum machine_mode));
+static rtx expand_builtin_strncmp      PARAMS ((tree, rtx,
+                                                enum machine_mode));
+static rtx builtin_memcpy_read_str     PARAMS ((PTR, HOST_WIDE_INT,
+                                                enum machine_mode));
+static rtx expand_builtin_strcat       PARAMS ((tree, rtx,
+                                                enum machine_mode));
+static rtx expand_builtin_strncat      PARAMS ((tree, rtx,
+                                                enum machine_mode));
+static rtx expand_builtin_strspn       PARAMS ((tree, rtx,
+                                                enum machine_mode));
+static rtx expand_builtin_strcspn      PARAMS ((tree, rtx,
+                                                enum machine_mode));
 static rtx expand_builtin_memcpy       PARAMS ((tree));
 static rtx expand_builtin_strcpy       PARAMS ((tree));
+static rtx builtin_strncpy_read_str    PARAMS ((PTR, HOST_WIDE_INT,
+                                                enum machine_mode));
+static rtx expand_builtin_strncpy      PARAMS ((tree, rtx,
+                                                enum machine_mode));
+static rtx builtin_memset_read_str     PARAMS ((PTR, HOST_WIDE_INT,
+                                                enum machine_mode));
 static rtx expand_builtin_memset       PARAMS ((tree));
-static rtx expand_builtin_strlen       PARAMS ((tree, rtx, enum machine_mode));
+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_strchr       PARAMS ((tree, rtx,
+                                                enum machine_mode));
+static rtx expand_builtin_strrchr      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));
+static rtx expand_builtin_fputs                PARAMS ((tree, int));
 static tree stabilize_va_list          PARAMS ((tree, int));
+static rtx expand_builtin_expect       PARAMS ((tree, rtx));
+static tree fold_builtin_constant_p    PARAMS ((tree));
+static tree build_function_call_expr   PARAMS ((tree, tree));
+static int validate_arglist            PARAMS ((tree, ...));
 
 /* Return the alignment in bits of EXP, a pointer valued expression.
    But don't return more than MAX_ALIGN no matter what.
@@ -117,6 +174,7 @@ get_pointer_alignment (exp, max_align)
          exp = TREE_OPERAND (exp, 0);
          if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE)
            return align;
+
          inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp)));
          align = MIN (inner, max_align);
          break;
@@ -125,10 +183,10 @@ get_pointer_alignment (exp, max_align)
          /* If sum of pointer + int, restrict our maximum alignment to that
             imposed by the integer.  If not, we can't do any better than
             ALIGN.  */
-         if (TREE_CODE (TREE_OPERAND (exp, 1)) != INTEGER_CST)
+         if (! host_integerp (TREE_OPERAND (exp, 1), 1))
            return align;
 
-         while (((TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) * BITS_PER_UNIT)
+         while (((tree_low_cst (TREE_OPERAND (exp, 1), 1) * BITS_PER_UNIT)
                  & (max_align - 1))
                 != 0)
            max_align >>= 1;
@@ -141,7 +199,7 @@ get_pointer_alignment (exp, max_align)
          exp = TREE_OPERAND (exp, 0);
          if (TREE_CODE (exp) == FUNCTION_DECL)
            align = FUNCTION_BOUNDARY;
-         else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'd')
+         else if (DECL_P (exp))
            align = DECL_ALIGN (exp);
 #ifdef CONSTANT_ALIGNMENT
          else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c')
@@ -159,6 +217,8 @@ get_pointer_alignment (exp, max_align)
    way, because it could contain a zero byte in the middle.
    TREE_STRING_LENGTH is the size of the character array, not the string.
 
+   The value returned is of type `ssizetype'.
+
    Unfortunately, string_constant can't access the values of const char
    arrays with initializers, so neither can we do so here.  */
 
@@ -168,30 +228,34 @@ c_strlen (src)
 {
   tree offset_node;
   int offset, max;
-  char *ptr;
+  const char *ptr;
 
   src = string_constant (src, &offset_node);
   if (src == 0)
     return 0;
-  max = TREE_STRING_LENGTH (src);
+
+  max = TREE_STRING_LENGTH (src) - 1;
   ptr = TREE_STRING_POINTER (src);
+
   if (offset_node && TREE_CODE (offset_node) != INTEGER_CST)
     {
       /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't
         compute the offset to the following null if we don't know where to
         start searching for it.  */
       int i;
+
       for (i = 0; i < max; i++)
        if (ptr[i] == 0)
          return 0;
+
       /* We don't know the starting offset, but we do know that the string
         has no internal zero bytes.  We can assume that the offset falls
         within the bounds of the string; otherwise, the programmer deserves
         what he gets.  Subtract the offset from the length of the string,
-        and return that.  */
-      /* This would perhaps not be valid if we were dealing with named
-         arrays in addition to literal string constants.  */
-      return size_binop (MINUS_EXPR, size_int (max), offset_node);
+        and return that.  This would perhaps not be valid if we were dealing
+        with named arrays in addition to literal string constants.  */
+
+      return size_diffop (size_int (max), offset_node);
     }
 
   /* We have a known offset into the string.  Start searching there for
@@ -205,6 +269,7 @@ c_strlen (src)
        return 0;
       offset = TREE_INT_CST_LOW (offset_node);
     }
+
   /* If the offset is known to be out of bounds, warn, and call strlen at
      runtime.  */
   if (offset < 0 || offset > max)
@@ -212,18 +277,121 @@ c_strlen (src)
       warning ("offset outside bounds of constant string");
       return 0;
     }
+
   /* Use strlen to search for the first zero byte.  Since any strings
      constructed with build_string will have nulls appended, we win even
      if we get handed something like (char[4])"abcd".
 
      Since OFFSET is our starting index into the string, no further
      calculation is needed.  */
-  return size_int (strlen (ptr + offset));
+  return ssize_int (strlen (ptr + offset));
+}
+
+/* Return a char pointer for a C string if it is a string constant
+   or sum of string constant and integer constant.  */
+
+static const char *
+c_getstr (src)
+     tree src;
+{
+  tree offset_node;
+  int offset, max;
+  const char *ptr;
+
+  src = string_constant (src, &offset_node);
+  if (src == 0)
+    return 0;
+
+  max = TREE_STRING_LENGTH (src) - 1;
+  ptr = TREE_STRING_POINTER (src);
+
+  if (!offset_node)
+    offset = 0;
+  else if (TREE_CODE (offset_node) != INTEGER_CST)
+    return 0;
+  else
+    {
+      /* Did we get a long long offset?  If so, punt.  */
+      if (TREE_INT_CST_HIGH (offset_node) != 0)
+       return 0;
+      offset = TREE_INT_CST_LOW (offset_node);
+      if (offset < 0 || offset > max)
+       return 0;
+    }
+
+  return ptr + offset;
+}
+
+/* Return a CONST_INT or CONST_DOUBLE corresponding to target
+   reading GET_MODE_BITSIZE (MODE) bits from string constant
+   STR.  */
+
+static rtx
+c_readstr (str, mode)
+     const char *str;
+     enum machine_mode mode;
+{
+  HOST_WIDE_INT c[2];
+  HOST_WIDE_INT ch;
+  unsigned int i, j;
+
+  if (GET_MODE_CLASS (mode) != MODE_INT)
+    abort ();
+  c[0] = 0;
+  c[1] = 0;
+  ch = 1;
+  for (i = 0; i < GET_MODE_SIZE (mode); i++)
+    {
+      j = i;
+      if (WORDS_BIG_ENDIAN)
+       j = GET_MODE_SIZE (mode) - i - 1;
+      if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN
+         && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+       j = j + UNITS_PER_WORD - 2 * (j % UNITS_PER_WORD) - 1;
+      j *= BITS_PER_UNIT;
+      if (j > 2 * HOST_BITS_PER_WIDE_INT)
+       abort ();
+      if (ch)
+       ch = (unsigned char) str[i];
+      c[j / HOST_BITS_PER_WIDE_INT] |= ch << (j % HOST_BITS_PER_WIDE_INT);
+    }
+  return immed_double_const (c[0], c[1], mode);
+}
+
+/* Cast a target constant CST to target CHAR and if that value fits into
+   host char type, return zero and put that value into variable pointed by
+   P.  */
+
+static int
+target_char_cast (cst, p)
+     tree cst;
+     char *p;
+{
+  unsigned HOST_WIDE_INT val, hostval;
+
+  if (TREE_CODE (cst) != INTEGER_CST
+      || CHAR_TYPE_SIZE > HOST_BITS_PER_WIDE_INT)
+    return 1;
+
+  val = TREE_INT_CST_LOW (cst);
+  if (CHAR_TYPE_SIZE < HOST_BITS_PER_WIDE_INT)
+    val &= (((unsigned HOST_WIDE_INT) 1) << CHAR_TYPE_SIZE) - 1;
+
+  hostval = val;
+  if (HOST_BITS_PER_CHAR < HOST_BITS_PER_WIDE_INT)
+    hostval &= (((unsigned HOST_WIDE_INT) 1) << HOST_BITS_PER_CHAR) - 1;
+
+  if (val != hostval)
+    return 1;
+
+  *p = hostval;
+  return 0;
 }
 
 /* Given TEM, a pointer to a stack frame, follow the dynamic chain COUNT
    times to get the address of either a higher stack frame, or a return
    address located within it (depending on FNDECL_CODE).  */
+
 rtx
 expand_builtin_return_addr (fndecl_code, count, tem)
      enum built_in_function fndecl_code;
@@ -258,7 +426,9 @@ expand_builtin_return_addr (fndecl_code, count, tem)
       tem = DYNAMIC_CHAIN_ADDRESS (tem);
 #endif
       tem = memory_address (Pmode, tem);
-      tem = copy_to_reg (gen_rtx_MEM (Pmode, tem));
+      tem = gen_rtx_MEM (Pmode, tem);
+      set_mem_alias_set (tem, get_frame_alias_set ());
+      tem = copy_to_reg (tem);
     }
 
   /* For __builtin_frame_address, return what we've got.  */
@@ -273,32 +443,29 @@ expand_builtin_return_addr (fndecl_code, count, tem)
   tem = memory_address (Pmode,
                        plus_constant (tem, GET_MODE_SIZE (Pmode)));
   tem = gen_rtx_MEM (Pmode, tem);
+  set_mem_alias_set (tem, get_frame_alias_set ());
 #endif
   return tem;
 }
 
-/* __builtin_setjmp is passed a pointer to an array of five words (not
-   all will be used on all machines).  It operates similarly to the C
-   library function of the same name, but is more efficient.  Much of
-   the code below (and for longjmp) is copied from the handling of
-   non-local gotos.
+/* Alias set used for setjmp buffer.  */
+static HOST_WIDE_INT setjmp_alias_set = -1;
 
-   NOTE: This is intended for use by GNAT and the exception handling
-   scheme in the compiler and will only work in the method used by
-   them.  */
+/* Construct the leading half of a __builtin_setjmp call.  Control will
+   return to RECEIVER_LABEL.  This is used directly by sjlj exception
+   handling code.  */
 
-rtx
-expand_builtin_setjmp (buf_addr, target, first_label, next_label)
+void
+expand_builtin_setjmp_setup (buf_addr, receiver_label)
      rtx buf_addr;
-     rtx target;
-     rtx first_label, next_label;
+     rtx receiver_label;
 {
-  rtx lab1 = gen_label_rtx ();
   enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
-  enum machine_mode value_mode;
   rtx stack_save;
+  rtx mem;
 
-  value_mode = TYPE_MODE (integer_type_node);
+  if (setjmp_alias_set == -1)
+    setjmp_alias_set = new_alias_set ();
 
 #ifdef POINTERS_EXTEND_UNSIGNED
   buf_addr = convert_memory_address (Pmode, buf_addr);
@@ -306,31 +473,30 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label)
 
   buf_addr = force_reg (Pmode, force_operand (buf_addr, NULL_RTX));
 
-  if (target == 0 || GET_CODE (target) != REG
-      || REGNO (target) < FIRST_PSEUDO_REGISTER)
-    target = gen_reg_rtx (value_mode);
-
   emit_queue ();
 
-  /* We store the frame pointer and the address of lab1 in the buffer
-     and use the rest of it for the stack save area, which is
-     machine-dependent.  */
+  /* We store the frame pointer and the address of receiver_label in
+     the buffer and use the rest of it for the stack save area, which
+     is machine-dependent.  */
 
 #ifndef BUILTIN_SETJMP_FRAME_VALUE
 #define BUILTIN_SETJMP_FRAME_VALUE virtual_stack_vars_rtx
 #endif
 
-  emit_move_insn (gen_rtx_MEM (Pmode, buf_addr),
-                 BUILTIN_SETJMP_FRAME_VALUE);
-  emit_move_insn (validize_mem
-                 (gen_rtx_MEM (Pmode,
-                               plus_constant (buf_addr,
-                                              GET_MODE_SIZE (Pmode)))),
-                 force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, lab1)));
+  mem = gen_rtx_MEM (Pmode, buf_addr);
+  set_mem_alias_set (mem, setjmp_alias_set);
+  emit_move_insn (mem, BUILTIN_SETJMP_FRAME_VALUE);
+
+  mem = gen_rtx_MEM (Pmode, plus_constant (buf_addr, GET_MODE_SIZE (Pmode))),
+  set_mem_alias_set (mem, setjmp_alias_set);
+
+  emit_move_insn (validize_mem (mem),
+                 force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label)));
 
   stack_save = gen_rtx_MEM (sa_mode,
                            plus_constant (buf_addr,
                                           2 * GET_MODE_SIZE (Pmode)));
+  set_mem_alias_set (stack_save, setjmp_alias_set);
   emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX);
 
   /* If there is further processing to do, do it.  */
@@ -339,20 +505,22 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label)
     emit_insn (gen_builtin_setjmp_setup (buf_addr));
 #endif
 
-  /* Set TARGET to zero and branch to the first-time-through label.  */
-  emit_move_insn (target, const0_rtx);
-  emit_jump_insn (gen_jump (first_label));
-  emit_barrier ();
-  emit_label (lab1);
-
-  /* Tell flow about the strange goings on.  Putting `lab1' on
-     `nonlocal_goto_handler_labels' to indicates that function
-     calls may traverse the arc back to this label.  */
+  /* Tell optimize_save_area_alloca that extra work is going to
+     need to go on during alloca.  */
+  current_function_calls_setjmp = 1;
 
+  /* Set this so all the registers get saved in our frame; we need to be
+     able to copy the saved values for any registers from frames we unwind. */
   current_function_has_nonlocal_label = 1;
-  nonlocal_goto_handler_labels =
-    gen_rtx_EXPR_LIST (VOIDmode, lab1, nonlocal_goto_handler_labels);
+}
+
+/* Construct the trailing part of a __builtin_setjmp call.
+   This is used directly by sjlj exception handling code.  */
 
+void
+expand_builtin_setjmp_receiver (receiver_label)
+      rtx receiver_label ATTRIBUTE_UNUSED;
+{
   /* Clobber the FP when we get here, so we have to make sure it's
      marked as used by this function.  */
   emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
@@ -376,12 +544,12 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label)
       size_t i;
       static struct elims {int from, to;} elim_regs[] = ELIMINABLE_REGS;
 
-      for (i = 0; i < sizeof elim_regs / sizeof elim_regs[0]; i++)
+      for (i = 0; i < ARRAY_SIZE (elim_regs); i++)
        if (elim_regs[i].from == ARG_POINTER_REGNUM
            && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
          break;
 
-      if (i == sizeof elim_regs / sizeof elim_regs [0])
+      if (i == ARRAY_SIZE (elim_regs))
 #endif
        {
          /* Now restore our arg pointer from the address at which it
@@ -399,7 +567,7 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label)
 
 #ifdef HAVE_builtin_setjmp_receiver
   if (HAVE_builtin_setjmp_receiver)
-    emit_insn (gen_builtin_setjmp_receiver (lab1));
+    emit_insn (gen_builtin_setjmp_receiver (receiver_label));
   else
 #endif
 #ifdef HAVE_nonlocal_goto_receiver
@@ -407,14 +575,66 @@ expand_builtin_setjmp (buf_addr, target, first_label, next_label)
       emit_insn (gen_nonlocal_goto_receiver ());
     else
 #endif
-      {
-       ; /* Nothing */
-      }
+      { /* Nothing */ }
+
+  /* @@@ This is a kludge.  Not all machine descriptions define a blockage
+     insn, but we must not allow the code we just generated to be reordered
+     by scheduling.  Specifically, the update of the frame pointer must
+     happen immediately, not later.  So emit an ASM_INPUT to act as blockage
+     insn.  */
+  emit_insn (gen_rtx_ASM_INPUT (VOIDmode, ""));
+}
 
-  /* Set TARGET, and branch to the next-time-through label.  */
-  emit_move_insn (target, const1_rtx);
-  emit_jump_insn (gen_jump (next_label));
+/* __builtin_setjmp is passed a pointer to an array of five words (not
+   all will be used on all machines).  It operates similarly to the C
+   library function of the same name, but is more efficient.  Much of
+   the code below (and for longjmp) is copied from the handling of
+   non-local gotos.
+
+   NOTE: This is intended for use by GNAT and the exception handling
+   scheme in the compiler and will only work in the method used by
+   them.  */
+
+static rtx
+expand_builtin_setjmp (arglist, target)
+     tree arglist;
+     rtx target;
+{
+  rtx buf_addr, next_lab, cont_lab;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  if (target == 0 || GET_CODE (target) != REG
+      || REGNO (target) < FIRST_PSEUDO_REGISTER)
+    target = gen_reg_rtx (TYPE_MODE (integer_type_node));
+
+  buf_addr = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+
+  next_lab = gen_label_rtx ();
+  cont_lab = gen_label_rtx ();
+
+  expand_builtin_setjmp_setup (buf_addr, next_lab);
+
+  /* Set TARGET to zero and branch to the continue label.  */
+  emit_move_insn (target, const0_rtx);
+  emit_jump_insn (gen_jump (cont_lab));
   emit_barrier ();
+  emit_label (next_lab);
+
+  expand_builtin_setjmp_receiver (next_lab);
+
+  /* Set TARGET to one.  */
+  emit_move_insn (target, const1_rtx);
+  emit_label (cont_lab);
+
+  /* Tell flow about the strange goings on.  Putting `next_lab' on
+     `nonlocal_goto_handler_labels' to indicates that function
+     calls may traverse the arc back to this label.  */
+
+  current_function_has_nonlocal_label = 1;
+  nonlocal_goto_handler_labels
+    = gen_rtx_EXPR_LIST (VOIDmode, next_lab, nonlocal_goto_handler_labels);
 
   return target;
 }
@@ -432,9 +652,12 @@ void
 expand_builtin_longjmp (buf_addr, value)
      rtx buf_addr, value;
 {
-  rtx fp, lab, stack;
+  rtx fp, lab, stack, insn;
   enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
 
+  if (setjmp_alias_set == -1)
+    setjmp_alias_set = new_alias_set ();
+
 #ifdef POINTERS_EXTEND_UNSIGNED
   buf_addr = convert_memory_address (Pmode, buf_addr);
 #endif
@@ -448,6 +671,8 @@ expand_builtin_longjmp (buf_addr, value)
   if (value != const1_rtx)
     abort ();
 
+  current_function_calls_longjmp = 1;
+
 #ifdef HAVE_builtin_longjmp
   if (HAVE_builtin_longjmp)
     emit_insn (gen_builtin_longjmp (buf_addr));
@@ -460,6 +685,9 @@ expand_builtin_longjmp (buf_addr, value)
 
       stack = gen_rtx_MEM (sa_mode, plus_constant (buf_addr,
                                                   2 * GET_MODE_SIZE (Pmode)));
+      set_mem_alias_set (fp, setjmp_alias_set);
+      set_mem_alias_set (lab, setjmp_alias_set);
+      set_mem_alias_set (stack, setjmp_alias_set);
 
       /* Pick up FP, label, and SP from the block and jump.  This code is
         from expand_goto in stmt.c; see there for detailed comments.  */
@@ -482,55 +710,55 @@ expand_builtin_longjmp (buf_addr, value)
          emit_indirect_jump (lab);
        }
     }
-}
-
-/* Get a MEM rtx for expression EXP which can be used in a string instruction
-   (cmpstrsi, movstrsi, ..).  */
-static rtx
-get_memory_rtx (exp)
-     tree exp;
-{
-  rtx mem;
-  int is_aggregate;
 
-  mem = gen_rtx_MEM (BLKmode,
-                    memory_address (BLKmode,
-                                    expand_expr (exp, NULL_RTX,
-                                                 ptr_mode, EXPAND_SUM)));
-
-  RTX_UNCHANGING_P (mem) = TREE_READONLY (exp);
-
-  /* Figure out the type of the object pointed to.  Set MEM_IN_STRUCT_P
-     if the value is the address of a structure or if the expression is
-     cast to a pointer to structure type.  */
-  is_aggregate = 0;
-
-  while (TREE_CODE (exp) == NOP_EXPR)
+  /* Search backwards and mark the jump insn as a non-local goto.
+     Note that this precludes the use of __builtin_longjmp to a
+     __builtin_setjmp target in the same function.  However, we've
+     already cautioned the user that these functions are for
+     internal exception handling use only.  */
+  for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
     {
-      tree cast_type = TREE_TYPE (exp);
-      if (TREE_CODE (cast_type) == POINTER_TYPE
-         && AGGREGATE_TYPE_P (TREE_TYPE (cast_type)))
+      if (GET_CODE (insn) == JUMP_INSN)
        {
-         is_aggregate = 1;
+         REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO, const0_rtx,
+                                             REG_NOTES (insn));
          break;
        }
-      exp = TREE_OPERAND (exp, 0);
+      else if (GET_CODE (insn) == CALL_INSN)
+        break;
     }
+}
 
-  if (is_aggregate == 0)
-    {
-      tree type;
+/* Get a MEM rtx for expression EXP which is the address of an operand
+   to be used to be used in a string instruction (cmpstrsi, movstrsi, ..).  */
 
-      if (TREE_CODE (exp) == ADDR_EXPR)
-       /* If this is the address of an object, check whether the
-          object is an array.  */
-       type = TREE_TYPE (TREE_OPERAND (exp, 0));
-      else
-       type = TREE_TYPE (TREE_TYPE (exp));
-      is_aggregate = AGGREGATE_TYPE_P (type);
-    }
+static rtx
+get_memory_rtx (exp)
+     tree exp;
+{
+  rtx mem = gen_rtx_MEM (BLKmode,
+                        memory_address (BLKmode,
+                                        expand_expr (exp, NULL_RTX,
+                                                     ptr_mode, EXPAND_SUM)));
+
+  /* Get an expression we can use to find the attributes to assign to MEM.
+     If it is an ADDR_EXPR, use the operand.  Otherwise, dereference it if
+     we can.  First remove any nops.  */
+  while ((TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR
+        || TREE_CODE (exp) == NON_LVALUE_EXPR)
+        && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0))))
+    exp = TREE_OPERAND (exp, 0);
+
+  if (TREE_CODE (exp) == ADDR_EXPR)
+    exp = TREE_OPERAND (exp, 0);
+  else if (POINTER_TYPE_P (TREE_TYPE (exp)))
+    exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
+  else
+    return mem;
 
-  MEM_SET_IN_STRUCT_P (mem, is_aggregate);
+  set_mem_attributes (mem, exp, 0);
+  /* memcpy, memset and other builtin stringops can alias with anything. */
+  set_mem_alias_set (mem, 0);
   return mem;
 }
 \f
@@ -720,8 +948,7 @@ result_vector (savep, result)
        if (size % align != 0)
          size = CEIL (size, align) * align;
        reg = gen_rtx_REG (mode, savep ? regno : INCOMING_REGNO (regno));
-       mem = change_address (result, mode,
-                             plus_constant (XEXP (result, 0), size));
+       mem = adjust_address (result, mode, size);
        savevec[nelts++] = (savep
                            ? gen_rtx_SET (VOIDmode, mem, reg)
                            : gen_rtx_SET (VOIDmode, reg, mem));
@@ -762,15 +989,12 @@ expand_builtin_apply_args_1 ()
 
        tem = gen_rtx_REG (mode, INCOMING_REGNO (regno));
 
-       emit_move_insn (change_address (registers, mode,
-                                       plus_constant (XEXP (registers, 0),
-                                                      size)),
-                       tem);
+       emit_move_insn (adjust_address (registers, mode, size), tem);
        size += GET_MODE_SIZE (mode);
       }
 
   /* Save the arg pointer to the block.  */
-  emit_move_insn (change_address (registers, Pmode, XEXP (registers, 0)),
+  emit_move_insn (adjust_address (registers, Pmode, 0),
                  copy_to_reg (virtual_incoming_args_rtx));
   size = GET_MODE_SIZE (Pmode);
 
@@ -778,9 +1002,7 @@ expand_builtin_apply_args_1 ()
      "invisible" first argument.  */
   if (struct_value_incoming_rtx)
     {
-      emit_move_insn (change_address (registers, Pmode,
-                                     plus_constant (XEXP (registers, 0),
-                                                    size)),
+      emit_move_insn (adjust_address (registers, Pmode, size),
                      copy_to_reg (struct_value_incoming_rtx));
       size += GET_MODE_SIZE (Pmode);
     }
@@ -843,8 +1065,6 @@ expand_builtin_apply (function, arguments, argsize)
   /* Create a block where the return registers can be saved.  */
   result = assign_stack_local (BLKmode, apply_result_size (), -1);
 
-  /* ??? The argsize value should be adjusted here.  */
-
   /* Fetch the arg pointer from the ARGUMENTS block.  */
   incoming_args = gen_reg_rtx (Pmode);
   emit_move_insn (incoming_args,
@@ -857,8 +1077,11 @@ expand_builtin_apply (function, arguments, argsize)
   /* Perform postincrements before actually calling the function.  */
   emit_queue ();
 
-  /* Push a new argument block and copy the arguments.  */
+  /* Push a new argument block and copy the arguments.  Do not allow
+     the (potential) memcpy call below to interfere with our stack
+     manipulations.  */
   do_pending_stack_adjust ();
+  NO_DEFER_POP;
 
   /* Save the stack with nonlocal if available */
 #ifdef HAVE_save_stack_nonlocal
@@ -873,11 +1096,10 @@ expand_builtin_apply (function, arguments, argsize)
      haven't figured out how the calling convention macros effect this,
      but it's likely that the source and/or destination addresses in
      the block copy will need updating in machine specific ways.  */
-  dest = allocate_dynamic_stack_space (argsize, 0, 0);
+  dest = allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT);
   emit_block_move (gen_rtx_MEM (BLKmode, dest),
                   gen_rtx_MEM (BLKmode, incoming_args),
-                  argsize,
-                  PARM_BOUNDARY / BITS_PER_UNIT);
+                  argsize, PARM_BOUNDARY);
 
   /* Refer to the argument block.  */
   apply_args_size ();
@@ -897,11 +1119,7 @@ expand_builtin_apply (function, arguments, argsize)
        if (size % align != 0)
          size = CEIL (size, align) * align;
        reg = gen_rtx_REG (mode, regno);
-       emit_move_insn (reg,
-                       change_address (arguments, mode,
-                                       plus_constant (XEXP (arguments, 0),
-                                                      size)));
-
+       emit_move_insn (reg, adjust_address (arguments, mode, size));
        use_reg (&call_fusage, reg);
        size += GET_MODE_SIZE (mode);
       }
@@ -912,10 +1130,7 @@ expand_builtin_apply (function, arguments, argsize)
   if (struct_value_rtx)
     {
       rtx value = gen_reg_rtx (Pmode);
-      emit_move_insn (value,
-                     change_address (arguments, Pmode,
-                                     plus_constant (XEXP (arguments, 0),
-                                                    size)));
+      emit_move_insn (value, adjust_address (arguments, Pmode, size));
       emit_move_insn (struct_value_rtx, value);
       if (GET_CODE (struct_value_rtx) == REG)
          use_reg (&call_fusage, struct_value_rtx);
@@ -923,7 +1138,7 @@ expand_builtin_apply (function, arguments, argsize)
     }
 
   /* All arguments and registers used for the call are set up by now!  */
-  function = prepare_call_address (function, NULL_TREE, &call_fusage, 0);
+  function = prepare_call_address (function, NULL_TREE, &call_fusage, 0, 0);
 
   /* Ensure address is valid.  SYMBOL_REF is already valid, so no need,
      and we don't want to load it into a register as an optimization,
@@ -955,13 +1170,11 @@ expand_builtin_apply (function, arguments, argsize)
            valreg = gen_rtx_REG (mode, regno);
          }
 
-      emit_call_insn (gen_call_value (valreg,
+      emit_call_insn (GEN_CALL_VALUE (valreg,
                                      gen_rtx_MEM (FUNCTION_MODE, function),
                                      const0_rtx, NULL_RTX, const0_rtx));
 
-      emit_move_insn (change_address (result, GET_MODE (valreg),
-                                     XEXP (result, 0)),
-                     valreg);
+      emit_move_insn (adjust_address (result, GET_MODE (valreg), 0), valreg);
     }
   else
 #endif
@@ -999,6 +1212,8 @@ expand_builtin_apply (function, arguments, argsize)
 #endif
     emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
 
+  OK_DEFER_POP;
+
   /* Return the address of the result block.  */
   return copy_addr_to_reg (XEXP (result, 0));
 }
@@ -1035,10 +1250,7 @@ expand_builtin_return (result)
        if (size % align != 0)
          size = CEIL (size, align) * align;
        reg = gen_rtx_REG (mode, INCOMING_REGNO (regno));
-       emit_move_insn (reg,
-                       change_address (result, mode,
-                                       plus_constant (XEXP (result, 0),
-                                                      size)));
+       emit_move_insn (reg, adjust_address (result, mode, size));
 
        push_to_sequence (call_fusage);
        emit_insn (gen_rtx_USE (VOIDmode, reg));
@@ -1117,44 +1329,19 @@ expand_builtin_constant_p (exp)
 {
   tree arglist = TREE_OPERAND (exp, 1);
   enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp));
+  rtx tmp;
 
   if (arglist == 0)
     return const0_rtx;
-  else
-    {
-      tree arg = TREE_VALUE (arglist);
-      rtx tmp;
-
-      /* We return 1 for a numeric type that's known to be a constant
-        value at compile-time or for an aggregate type that's a
-        literal constant.  */
-      STRIP_NOPS (arg);
-
-      /* If we know this is a constant, emit the constant of one.  */
-      if (TREE_CODE_CLASS (TREE_CODE (arg)) == 'c'
-         || (TREE_CODE (arg) == CONSTRUCTOR
-             && TREE_CONSTANT (arg))
-         || (TREE_CODE (arg) == ADDR_EXPR
-             && TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST))
-       return const1_rtx;
-
-      /* If we aren't going to be running CSE or this expression
-        has side effects, show we don't know it to be a constant.
-        Likewise if it's a pointer or aggregate type since in those
-        case we only want literals, since those are only optimized
-        when generating RTL, not later.  */
-      if (TREE_SIDE_EFFECTS (arg) || cse_not_expected
-         || AGGREGATE_TYPE_P (TREE_TYPE (arg))
-         || POINTER_TYPE_P (TREE_TYPE (arg)))
-       return const0_rtx;
+  arglist = TREE_VALUE (arglist);
 
-      /* Otherwise, emit (constant_p_rtx (ARG)) and let CSE get a
-        chance to see if it can deduce whether ARG is constant.  */
+  /* We have taken care of the easy cases during constant folding.  This
+     case is not obvious, so emit (constant_p_rtx (ARGLIST)) and let CSE get a
+     chance to see if it can deduce whether ARGLIST is constant.  */
 
-      tmp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
-      tmp = gen_rtx_CONSTANT_P_RTX (value_mode, tmp);
-      return tmp;
-    }
+  tmp = expand_expr (arglist, NULL_RTX, VOIDmode, 0);
+  tmp = gen_rtx_CONSTANT_P_RTX (value_mode, tmp);
+  return tmp;
 }
 
 /* Expand a call to one of the builtin math functions (sin, cos, or sqrt).
@@ -1172,9 +1359,7 @@ expand_builtin_mathfn (exp, target, subtarget)
   tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
   tree arglist = TREE_OPERAND (exp, 1);
 
-  if (arglist == 0
-      /* Arg could be wrong type if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE)
+  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
     return 0;
 
   /* Stabilize and compute the argument.  */
@@ -1203,11 +1388,17 @@ expand_builtin_mathfn (exp, target, subtarget)
 
   switch (DECL_FUNCTION_CODE (fndecl))
     {
-     case BUILT_IN_SIN:
+    case BUILT_IN_SIN:
+    case BUILT_IN_SINF:
+    case BUILT_IN_SINL:
       builtin_optab = sin_optab; break;
-     case BUILT_IN_COS:
+    case BUILT_IN_COS:
+    case BUILT_IN_COSF:
+    case BUILT_IN_COSL:
       builtin_optab = cos_optab; break;
-     case BUILT_IN_FSQRT:
+    case BUILT_IN_FSQRT:
+    case BUILT_IN_SQRTF:
+    case BUILT_IN_SQRTL:
       builtin_optab = sqrt_optab; break;
      default:
       abort ();
@@ -1227,10 +1418,10 @@ expand_builtin_mathfn (exp, target, subtarget)
       return 0;
     }
 
-  /* Check the results by default.  But if flag_fast_math is turned on,
-     then assume sqrt will always be called with valid arguments.  */
+  /* If errno must be maintained and if we are not allowing unsafe
+     math optimizations, check the result.  */
 
-  if (flag_errno_math && ! flag_fast_math)
+  if (flag_errno_math && ! flag_unsafe_math_optimizations)
     {
       rtx lab1;
 
@@ -1278,43 +1469,35 @@ expand_builtin_mathfn (exp, target, subtarget)
 
 /* Expand expression EXP which is a call to the strlen 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).  */
+   try to get the result in TARGET, if convenient.  */
+
 static rtx
-expand_builtin_strlen (exp, target, mode)
+expand_builtin_strlen (exp, target)
      tree exp;
      rtx target;
-     enum machine_mode mode;
 {
   tree arglist = TREE_OPERAND (exp, 1);
   enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp));
 
-  if (arglist == 0
-      /* Arg could be non-pointer if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+  if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
     return 0;
   else
     {
+      rtx pat;
       tree src = TREE_VALUE (arglist);
-      tree len = c_strlen (src);
 
       int align
        = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
 
-      rtx result, src_rtx, char_rtx;
+      rtx result, src_reg, char_rtx, before_strlen;
       enum machine_mode insn_mode = value_mode, char_mode;
       enum insn_code icode = CODE_FOR_nothing;
 
-      /* If the length is known, just return it.  */
-      if (len != 0)
-       return expand_expr (len, target, mode, EXPAND_MEMORY_USE_BAD);
-
       /* If SRC is not a pointer type, don't do this operation inline.  */
       if (align == 0)
        return 0;
 
-      /* Call a function if we can't compute strlen in the right mode.  */
-
+      /* Bail out if we can't compute strlen in the right mode.  */
       while (insn_mode != VOIDmode)
        {
          icode = strlen_optab->handlers[(int) insn_mode].insn_code;
@@ -1334,194 +1517,556 @@ expand_builtin_strlen (exp, target, mode)
             && REGNO (result) >= FIRST_PSEUDO_REGISTER))
        result = gen_reg_rtx (insn_mode);
 
-      /* Make sure the operands are acceptable to the predicates.  */
-
-      if (! (*insn_data[(int)icode].operand[0].predicate) (result, insn_mode))
-       result = gen_reg_rtx (insn_mode);
-      src_rtx = memory_address (BLKmode,
-                               expand_expr (src, NULL_RTX, ptr_mode,
-                                            EXPAND_NORMAL));
+      /* Make a place to hold the source address.  We will not expand
+        the actual source until we are sure that the expansion will
+        not fail -- there are trees that cannot be expanded twice.  */
+      src_reg = gen_reg_rtx (Pmode);
 
-      if (! (*insn_data[(int)icode].operand[1].predicate) (src_rtx, Pmode))
-       src_rtx = copy_to_mode_reg (Pmode, src_rtx);
+      /* Mark the beginning of the strlen sequence so we can emit the
+        source operand later.  */
+      before_strlen = get_last_insn();
 
       /* Check the string is readable and has an end.  */
       if (current_function_check_memory_usage)
-       emit_library_call (chkr_check_str_libfunc, 1, VOIDmode, 2,
-                          src_rtx, Pmode,
+       emit_library_call (chkr_check_str_libfunc, LCT_CONST_MAKE_BLOCK,
+                          VOIDmode, 2, src_reg, Pmode,
                           GEN_INT (MEMORY_USE_RO),
                           TYPE_MODE (integer_type_node));
 
       char_rtx = const0_rtx;
-      char_mode = insn_data[(int)icode].operand[2].mode;
-      if (! (*insn_data[(int)icode].operand[2].predicate) (char_rtx, char_mode))
+      char_mode = insn_data[(int) icode].operand[2].mode;
+      if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx,
+                                                           char_mode))
        char_rtx = copy_to_mode_reg (char_mode, char_rtx);
 
-      emit_insn (GEN_FCN (icode) (result,
-                                 gen_rtx_MEM (BLKmode, src_rtx),
-                                 char_rtx, GEN_INT (align)));
+      pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
+                            char_rtx, GEN_INT (align));
+      if (! pat)
+       return 0;
+      emit_insn (pat);
+
+      /* Now that we are assured of success, expand the source.  */
+      start_sequence ();
+      pat = memory_address (BLKmode, 
+                           expand_expr (src, src_reg, ptr_mode, EXPAND_SUM));
+      if (pat != src_reg)
+       emit_move_insn (src_reg, pat);
+      pat = gen_sequence ();
+      end_sequence ();
+
+      if (before_strlen)
+       emit_insn_after (pat, before_strlen);
+      else
+       emit_insn_before (pat, get_insns ());
 
       /* Return the value in the proper mode for this function.  */
       if (GET_MODE (result) == value_mode)
-       return result;
+       target = result;
       else if (target != 0)
-       {
-         convert_move (target, result, 0);
-         return target;
-       }
+       convert_move (target, result, 0);
       else
-       return convert_to_mode (value_mode, result, 0);
+       target = convert_to_mode (value_mode, result, 0);
+
+      return target;
     }
 }
 
-/* Expand a call to the memcpy builtin, with arguments in ARGLIST.  */
+/* Expand a call to the strstr 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_memcpy (arglist)
+expand_builtin_strstr (arglist, target, mode)
      tree arglist;
+     rtx target;
+     enum machine_mode mode;
 {
-  if (arglist == 0
-      /* Arg could be non-pointer if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
-      || TREE_CHAIN (arglist) == 0
-      || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist))))
-         != POINTER_TYPE)
-      || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
-      || (TREE_CODE (TREE_TYPE (TREE_VALUE
-                               (TREE_CHAIN (TREE_CHAIN (arglist)))))
-         != INTEGER_TYPE))
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+      || current_function_check_memory_usage)
     return 0;
   else
     {
-      tree dest = TREE_VALUE (arglist);
-      tree src = TREE_VALUE (TREE_CHAIN (arglist));
-      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
-
-      int src_align
-       = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
-      int dest_align
-       = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
-      rtx dest_mem, src_mem, dest_addr, len_rtx;
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      tree fn;
+      const char *p1, *p2;
 
-      /* If either SRC or DEST is not a pointer type, don't do
-        this operation in-line.  */
-      if (src_align == 0 || dest_align == 0)
+      p2 = c_getstr (s2);
+      if (p2 == NULL)
        return 0;
 
-      dest_mem = get_memory_rtx (dest);
-      src_mem = get_memory_rtx (src);
-      len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+      p1 = c_getstr (s1);
+      if (p1 != NULL)
+       {
+         const char *r = strstr (p1, p2);
 
-      /* Just copy the rights of SRC to the rights of DEST.  */
-      if (current_function_check_memory_usage)
-       emit_library_call (chkr_copy_bitmap_libfunc, 1, VOIDmode, 3,
-                          XEXP (dest_mem, 0), Pmode,
-                          XEXP (src_mem, 0), Pmode,
-                          len_rtx, TYPE_MODE (sizetype));
+         if (r == NULL)
+           return const0_rtx;
 
-      /* Copy word part most expediently.  */
-      dest_addr
-       = emit_block_move (dest_mem, src_mem, len_rtx,
-                          MIN (src_align, dest_align));
+         /* Return an offset into the constant string argument.  */
+         return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+                                          s1, ssize_int (r - p1))),
+                             target, mode, EXPAND_NORMAL);
+       }
 
-      if (dest_addr == 0)
-       dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+      if (p2[0] == '\0')
+       return expand_expr (s1, target, mode, EXPAND_NORMAL);
 
-      return dest_addr;
+      if (p2[1] != '\0')
+       return 0;
+
+      fn = built_in_decls[BUILT_IN_STRCHR];
+      if (!fn)
+       return 0;
+
+      /* New argument list transforming strstr(s1, s2) to
+        strchr(s1, s2[0]).  */
+      arglist =
+       build_tree_list (NULL_TREE, build_int_2 (p2[0], 0));
+      arglist = tree_cons (NULL_TREE, s1, arglist);
+      return expand_expr (build_function_call_expr (fn, arglist),
+                         target, mode, EXPAND_NORMAL);
     }
 }
 
-/* Expand expression EXP, which is a call to the strcpy builtin.  Return 0
-   if we failed the caller should emit a normal call.  */
+/* Expand a call to the strchr 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_strcpy (exp)
-     tree exp;
+expand_builtin_strchr (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
 {
-  tree arglist = TREE_OPERAND (exp, 1);
-  rtx result;
-
-  if (arglist == 0
-      /* Arg could be non-pointer if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
-      || TREE_CHAIN (arglist) == 0
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+      || current_function_check_memory_usage)
     return 0;
   else
     {
-      tree len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      const char *p1;
 
-      if (len == 0)
+      if (TREE_CODE (s2) != INTEGER_CST)
        return 0;
 
-      len = size_binop (PLUS_EXPR, len, integer_one_node);
+      p1 = c_getstr (s1);
+      if (p1 != NULL)
+       {
+         char c;
+         const char *r;
 
-      chainon (arglist, build_tree_list (NULL_TREE, len));
+         if (target_char_cast (s2, &c))
+           return 0;
+
+         r = strchr (p1, c);
+
+         if (r == NULL)
+           return const0_rtx;
+
+         /* Return an offset into the constant string argument.  */
+         return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+                                          s1, ssize_int (r - p1))),
+                             target, mode, EXPAND_NORMAL);
+       }
+
+      /* FIXME: Should use here strchrM optab so that ports can optimize
+        this.  */
+      return 0;
     }
-  result = expand_builtin_memcpy (arglist);
-  if (! result)
-    TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
-  return result;
 }
 
-/* Expand expression EXP, which is a call to the memset builtin.  Return 0
-   if we failed the caller should emit a normal call.  */
+/* Expand a call to the strrchr 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_memset (exp)
-     tree exp;
+expand_builtin_strrchr (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
 {
-  tree arglist = TREE_OPERAND (exp, 1);
-
-  if (arglist == 0
-      /* Arg could be non-pointer if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
-      || TREE_CHAIN (arglist) == 0
-      || (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist))))
-         != INTEGER_TYPE)
-      || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
-      || (INTEGER_TYPE
-         != (TREE_CODE (TREE_TYPE
-                        (TREE_VALUE
-                         (TREE_CHAIN (TREE_CHAIN (arglist))))))))
+  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+      || current_function_check_memory_usage)
     return 0;
   else
     {
-      tree dest = TREE_VALUE (arglist);
-      tree val = TREE_VALUE (TREE_CHAIN (arglist));
-      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
-
-      int dest_align
-       = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
-      rtx dest_mem, dest_addr, len_rtx;
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      tree fn;
+      const char *p1;
 
-      /* If DEST is not a pointer type, don't do this 
-        operation in-line.  */
-      if (dest_align == 0)
+      if (TREE_CODE (s2) != INTEGER_CST)
        return 0;
 
-      /* If the arguments have side-effects, then we can only evaluate
-        them at most once.  The following code evaluates them twice if
-        they are not constants because we break out to expand_call
-        in that case.  They can't be constants if they have side-effects
-        so we can check for that first.  Alternatively, we could call
-        save_expr to make multiple evaluation safe.  */
-      if (TREE_SIDE_EFFECTS (val) || TREE_SIDE_EFFECTS (len))
-       return 0;
+      p1 = c_getstr (s1);
+      if (p1 != NULL)
+       {
+         char c;
+         const char *r;
 
-      /* If VAL is not 0, don't do this operation in-line. */
-      if (expand_expr (val, NULL_RTX, VOIDmode, 0) != const0_rtx)
+         if (target_char_cast (s2, &c))
+           return 0;
+
+         r = strrchr (p1, c);
+
+         if (r == NULL)
+           return const0_rtx;
+
+         /* Return an offset into the constant string argument.  */
+         return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+                                          s1, ssize_int (r - p1))),
+                             target, mode, EXPAND_NORMAL);
+       }
+
+      if (! integer_zerop (s2))
        return 0;
 
-      /* If LEN does not expand to a constant, don't do this
-        operation in-line.  */
-      len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
-      if (GET_CODE (len_rtx) != CONST_INT)
+      fn = built_in_decls[BUILT_IN_STRCHR];
+      if (!fn)
        return 0;
 
-      dest_mem = get_memory_rtx (dest);
+      /* Transform strrchr(s1, '\0') to strchr(s1, '\0').  */
+      return expand_expr (build_function_call_expr (fn, arglist),
+                         target, mode, EXPAND_NORMAL);
+    }
+}
+
+/* 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 (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+      || current_function_check_memory_usage)
+    return 0;
+  else
+    {
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      tree fn;
+      const char *p1, *p2;
+
+      p2 = c_getstr (s2);
+      if (p2 == NULL)
+       return 0;
+
+      p1 = c_getstr (s1);
+      if (p1 != NULL)
+       {
+         const char *r = strpbrk (p1, p2);
+
+         if (r == NULL)
+           return const0_rtx;
+
+         /* Return an offset into the constant string argument.  */
+         return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
+                                          s1, ssize_int (r - p1))),
+                             target, mode, EXPAND_NORMAL);
+       }
+
+      if (p2[0] == '\0')
+       {
+         /* strpbrk(x, "") == NULL.
+            Evaluate and ignore the arguments in case they had
+            side-effects.  */
+         expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return const0_rtx;
+       }
+
+      if (p2[1] != '\0')
+       return 0;  /* Really call strpbrk.  */
+
+      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 (p2[0], 0));
+      arglist = tree_cons (NULL_TREE, s1, arglist);
+      return expand_expr (build_function_call_expr (fn, arglist),
+                         target, mode, EXPAND_NORMAL);
+    }
+}
+
+/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
+   bytes from constant string DATA + OFFSET and return it as target
+   constant.  */
+
+static rtx
+builtin_memcpy_read_str (data, offset, mode)
+     PTR data;
+     HOST_WIDE_INT offset;
+     enum machine_mode mode;
+{
+  const char *str = (const char *) data;
+
+  if (offset + GET_MODE_SIZE (mode) > strlen (str) + 1)
+    abort ();  /* Attempt to read past the end of constant string.  */
+
+  return c_readstr (str + offset, mode);
+}
+
+/* Expand a call to the memcpy builtin, with arguments in ARGLIST.  */
+static rtx
+expand_builtin_memcpy (arglist)
+     tree arglist;
+{
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+  else
+    {
+      tree dest = TREE_VALUE (arglist);
+      tree src = TREE_VALUE (TREE_CHAIN (arglist));
+      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+      const char *src_str;
+
+      int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
+      int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+      rtx dest_mem, src_mem, dest_addr, len_rtx;
+
+      /* If either SRC or DEST is not a pointer type, don't do
+        this operation in-line.  */
+      if (src_align == 0 || dest_align == 0)
+       return 0;
+
+      dest_mem = get_memory_rtx (dest);
+      len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+      src_str = c_getstr (src);
+
+      /* If SRC is a string constant and block move would be done
+        by pieces, we can avoid loading the string from memory
+        and only stored the computed constants.  */
+      if (src_str
+         && !current_function_check_memory_usage
+         && GET_CODE (len_rtx) == CONST_INT
+         && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
+         && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
+                                 (PTR) src_str, dest_align))
+       {
+         store_by_pieces (dest_mem, INTVAL (len_rtx),
+                          builtin_memcpy_read_str,
+                          (PTR) src_str, dest_align);
+         return force_operand (XEXP (dest_mem, 0), NULL_RTX);
+       }
+
+      src_mem = get_memory_rtx (src);
+
+      /* Just copy the rights of SRC to the rights of DEST.  */
+      if (current_function_check_memory_usage)
+       emit_library_call (chkr_copy_bitmap_libfunc, LCT_CONST_MAKE_BLOCK,
+                          VOIDmode, 3, XEXP (dest_mem, 0), Pmode,
+                          XEXP (src_mem, 0), Pmode,
+                          len_rtx, TYPE_MODE (sizetype));
+
+      /* Copy word part most expediently.  */
+      dest_addr
+       = emit_block_move (dest_mem, src_mem, len_rtx,
+                          MIN (src_align, dest_align));
+
+      if (dest_addr == 0)
+       dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+
+      return dest_addr;
+    }
+}
+
+/* Expand expression EXP, which is a call to the strcpy builtin.  Return 0
+   if we failed the caller should emit a normal call.  */
+
+static rtx
+expand_builtin_strcpy (exp)
+     tree exp;
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  rtx result;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return 0;
+  else
+    {
+      tree len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
+
+      if (len == 0)
+       return 0;
+
+      len = size_binop (PLUS_EXPR, len, ssize_int (1));
+      chainon (arglist, build_tree_list (NULL_TREE, len));
+    }
+
+  result = expand_builtin_memcpy (arglist);
+
+  if (! result)
+    TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
+  return result;
+}
+
+/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
+   bytes from constant string DATA + OFFSET and return it as target
+   constant.  */
+
+static rtx
+builtin_strncpy_read_str (data, offset, mode)
+     PTR data;
+     HOST_WIDE_INT offset;
+     enum machine_mode mode;
+{
+  const char *str = (const char *) data;
+
+  if ((unsigned HOST_WIDE_INT) offset > strlen (str))
+    return const0_rtx;
+
+  return c_readstr (str + offset, mode);
+}
+
+/* Expand expression EXP, which is a call to the strncpy builtin.  Return 0
+   if we failed the caller should emit a normal call.  */
+
+static rtx
+expand_builtin_strncpy (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
+{
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+  else
+    {
+      tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
+      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+      /* We must be passed a constant len parameter.  */
+      if (TREE_CODE (len) != INTEGER_CST)
+       return 0;
+
+      /* If the len parameter is zero, return the dst parameter.  */
+      if (compare_tree_int (len, 0) == 0)
+        {
+       /* Evaluate and ignore the src argument in case it has
+           side-effects.  */
+         expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), const0_rtx,
+                      VOIDmode, EXPAND_NORMAL);
+         /* Return the dst parameter.  */                     
+         return expand_expr (TREE_VALUE (arglist), target, mode,
+                             EXPAND_NORMAL);
+       }
+
+      /* Now, we must be passed a constant src ptr parameter.  */
+      if (slen == 0 || TREE_CODE (slen) != INTEGER_CST)
+       return 0;
+
+      slen = size_binop (PLUS_EXPR, slen, ssize_int (1));
+
+      /* We're required to pad with trailing zeros if the requested
+         len is greater than strlen(s2)+1.  In that case try to
+        use store_by_pieces, if it fails, punt.  */
+      if (tree_int_cst_lt (slen, len))
+       {
+         tree dest = TREE_VALUE (arglist);
+         int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+         const char *p = c_getstr (TREE_VALUE (TREE_CHAIN (arglist)));
+         rtx dest_mem;
+
+         if (!p || !dest_align || TREE_INT_CST_HIGH (len)
+             || !can_store_by_pieces (TREE_INT_CST_LOW (len),
+                                      builtin_strncpy_read_str,
+                                      (PTR) p, dest_align))
+           return 0;
+
+         dest_mem = get_memory_rtx (dest);
+         store_by_pieces (dest_mem, TREE_INT_CST_LOW (len),
+                          builtin_strncpy_read_str,
+                          (PTR) p, dest_align);
+         return force_operand (XEXP (dest_mem, 0), NULL_RTX);
+       }
+      
+      /* OK transform into builtin memcpy.  */
+      return expand_builtin_memcpy (arglist);
+    }
+}
+
+/* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
+   bytes from constant string DATA + OFFSET and return it as target
+   constant.  */
+
+static rtx
+builtin_memset_read_str (data, offset, mode)
+     PTR data;
+     HOST_WIDE_INT offset ATTRIBUTE_UNUSED;
+     enum machine_mode mode;
+{
+  const char *c = (const char *) data;
+  char *p = alloca (GET_MODE_SIZE (mode));
+
+  memset (p, *c, GET_MODE_SIZE (mode));
+
+  return c_readstr (p, mode);
+}
+
+/* Expand expression EXP, which is a call to the memset builtin.  Return 0
+   if we failed the caller should emit a normal call.  */
+
+static rtx
+expand_builtin_memset (exp)
+     tree exp;
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+  else
+    {
+      tree dest = TREE_VALUE (arglist);
+      tree val = TREE_VALUE (TREE_CHAIN (arglist));
+      tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+      char c;
+
+      int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+      rtx dest_mem, dest_addr, len_rtx;
+
+      /* If DEST is not a pointer type, don't do this 
+        operation in-line.  */
+      if (dest_align == 0)
+       return 0;
+
+      if (TREE_CODE (val) != INTEGER_CST)
+       return 0;
+
+      if (target_char_cast (val, &c))
+       return 0;
+
+      if (c)
+       {
+         if (TREE_CODE (len) != INTEGER_CST || TREE_INT_CST_HIGH (len))
+           return 0;
+         if (current_function_check_memory_usage
+             || !can_store_by_pieces (TREE_INT_CST_LOW (len),
+                                      builtin_memset_read_str,
+                                      (PTR) &c, dest_align))
+           return 0;
+
+         dest_mem = get_memory_rtx (dest);
+         store_by_pieces (dest_mem, TREE_INT_CST_LOW (len),
+                          builtin_memset_read_str,
+                          (PTR) &c, dest_align);
+         return force_operand (XEXP (dest_mem, 0), NULL_RTX);
+       }
+
+      len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+
+      dest_mem = get_memory_rtx (dest);
           
       /* Just check DST is writable and mark it as readable.  */
       if (current_function_check_memory_usage)
-       emit_library_call (chkr_check_addr_libfunc, 1, VOIDmode, 3,
-                          XEXP (dest_mem, 0), Pmode,
+       emit_library_call (chkr_check_addr_libfunc, LCT_CONST_MAKE_BLOCK,
+                          VOIDmode, 3, XEXP (dest_mem, 0), Pmode,
                           len_rtx, TYPE_MODE (sizetype),
                           GEN_INT (MEMORY_USE_WO),
                           TYPE_MODE (integer_type_node));
@@ -1536,6 +2081,38 @@ expand_builtin_memset (exp)
     }
 }
 
+/* Expand expression EXP, which is a call to the bzero builtin.  Return 0
+   if we failed the caller should emit a normal call.  */
+static rtx
+expand_builtin_bzero (exp)
+     tree exp;
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree dest, size, newarglist;
+  rtx result;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  dest = TREE_VALUE (arglist);
+  size = TREE_VALUE (TREE_CHAIN (arglist));
+  
+  /* New argument list transforming bzero(ptr x, int y) to
+     memset(ptr x, int 0, size_t y).  */
+  
+  newarglist = build_tree_list (NULL_TREE, convert (sizetype, size));
+  newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist);
+  newarglist = tree_cons (NULL_TREE, dest, newarglist);
+
+  TREE_OPERAND (exp, 1) = newarglist;
+  result = expand_builtin_memset(exp);
+      
+  /* Always restore the original arguments.  */
+  TREE_OPERAND (exp, 1) = arglist;
+
+  return result;
+}
+
 #ifdef HAVE_cmpstrsi
 /* Expand expression EXP, which is a call to the memcmp or the strcmp builtin.
    ARGLIST is the argument list for this call.  Return 0 if we failed and the
@@ -1551,15 +2128,8 @@ expand_builtin_memcmp (exp, arglist, target)
   if (current_function_check_memory_usage)
     return 0;
 
-  if (arglist == 0
-      /* Arg could be non-pointer if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
-      || TREE_CHAIN (arglist) == 0
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
-      || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE)
-    return 0;
-  else if (!HAVE_cmpstrsi)
+  if (!validate_arglist (arglist,
+                     POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return 0;
 
   {
@@ -1567,7 +2137,9 @@ expand_builtin_memcmp (exp, arglist, target)
     tree arg1 = TREE_VALUE (arglist);
     tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
     tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+    rtx arg1_rtx, arg2_rtx, arg3_rtx;
     rtx result;
+    rtx insn;
 
     int arg1_align
       = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
@@ -1587,10 +2159,25 @@ expand_builtin_memcmp (exp, arglist, target)
           && REGNO (result) >= FIRST_PSEUDO_REGISTER))
       result = gen_reg_rtx (insn_mode);
 
-    emit_insn (gen_cmpstrsi (result, get_memory_rtx (arg1),
-                            get_memory_rtx (arg2),
-                            expand_expr (len, NULL_RTX, VOIDmode, 0),
-                            GEN_INT (MIN (arg1_align, arg2_align))));
+    arg1_rtx = get_memory_rtx (arg1);
+    arg2_rtx = get_memory_rtx (arg2);
+    arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+    if (!HAVE_cmpstrsi)
+      insn = NULL_RTX;
+    else
+      insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
+                          GEN_INT (MIN (arg1_align, arg2_align)));
+
+    if (insn)
+      emit_insn (insn);
+    else
+      emit_library_call_value (memcmp_libfunc, result, LCT_PURE_MAKE_BLOCK,
+                              TYPE_MODE (integer_type_node), 3,
+                              XEXP (arg1_rtx, 0), Pmode,
+                              XEXP (arg2_rtx, 0), Pmode,
+                              convert_to_mode (TYPE_MODE (sizetype), arg3_rtx,
+                                               TREE_UNSIGNED (sizetype)),
+                              TYPE_MODE (sizetype));
 
     /* Return the value in the proper mode for this function.  */
     mode = TYPE_MODE (TREE_TYPE (exp));
@@ -1605,75 +2192,402 @@ expand_builtin_memcmp (exp, arglist, target)
       return convert_to_mode (mode, result, 0);
   }
 }
+#endif
 
 /* Expand expression EXP, which is a call to the strcmp builtin.  Return 0
    if we failed the caller should emit a normal call, otherwise try to get
    the result in TARGET, if convenient.  */
+
 static rtx
-expand_builtin_strcmp (exp, target)
+expand_builtin_strcmp (exp, target, mode)
      tree exp;
      rtx target;
+     enum machine_mode mode;
 {
   tree arglist = TREE_OPERAND (exp, 1);
+  tree arg1, arg2;
+  const char *p1, *p2;
 
   /* If we need to check memory accesses, call the library function.  */
   if (current_function_check_memory_usage)
     return 0;
 
-  if (arglist == 0
-      /* Arg could be non-pointer if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
-      || TREE_CHAIN (arglist) == 0
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
     return 0;
-  else if (!HAVE_cmpstrsi)
+
+  arg1 = TREE_VALUE (arglist);
+  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+
+  p1 = c_getstr (arg1);
+  p2 = c_getstr (arg2);
+
+  if (p1 && p2)
+    {
+      const int i = strcmp (p1, p2);
+      return (i < 0 ? constm1_rtx : (i > 0 ? const1_rtx : const0_rtx));
+    }
+
+  /* If either arg is "", return an expression corresponding to
+     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
+  if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
+    {
+      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+      tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+      tree ind1 =
+       fold (build1 (CONVERT_EXPR, integer_type_node,
+                     build1 (INDIRECT_REF, cst_uchar_node,
+                             build1 (NOP_EXPR, cst_uchar_ptr_node, arg1))));
+      tree ind2 =
+       fold (build1 (CONVERT_EXPR, integer_type_node,
+                     build1 (INDIRECT_REF, cst_uchar_node,
+                             build1 (NOP_EXPR, cst_uchar_ptr_node, arg2))));
+      tree result = fold (build (MINUS_EXPR, integer_type_node, ind1, ind2));
+      return expand_expr (result, target, mode, EXPAND_NORMAL);
+    }
+  
+#ifdef HAVE_cmpstrsi
+  if (! HAVE_cmpstrsi)
     return 0;
+
   {
-    tree arg1 = TREE_VALUE (arglist);
-    tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
-    tree len, len2;
+    tree len = c_strlen (arg1);
+    tree len2 = c_strlen (arg2);
     rtx result;
-    len = c_strlen (arg1);
+
     if (len)
-      len = size_binop (PLUS_EXPR, integer_one_node, len);
-    len2 = c_strlen (arg2);
+      len = size_binop (PLUS_EXPR, ssize_int (1), len);
+
     if (len2)
-      len2 = size_binop (PLUS_EXPR, integer_one_node, len2);
+      len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
 
     /* If we don't have a constant length for the first, use the length
        of the second, if we know it.  We don't require a constant for
        this case; some cost analysis could be done if both are available
-       but neither is constant.  For now, assume they're equally cheap.
+       but neither is constant.  For now, assume they're equally cheap
+       unless one has side effects.
 
        If both strings have constant lengths, use the smaller.  This
        could arise if optimization results in strcpy being called with
        two fixed strings, or if the code was machine-generated.  We should
        add some code to the `memcmp' handler below to deal with such
        situations, someday.  */
+
     if (!len || TREE_CODE (len) != INTEGER_CST)
       {
-       if (len2)
+       if (len2 && !TREE_SIDE_EFFECTS (len2))
          len = len2;
        else if (len == 0)
          return 0;
       }
-    else if (len2 && TREE_CODE (len2) == INTEGER_CST)
-      {
-       if (tree_int_cst_lt (len2, len))
-         len = len2;
-      }
+    else if (len2 && TREE_CODE (len2) == INTEGER_CST
+            && tree_int_cst_lt (len2, len))
+      len = len2;
+
+    /* If both arguments have side effects, we cannot optimize.  */
+    if (TREE_SIDE_EFFECTS (len))
+      return 0;
+
+    chainon (arglist, build_tree_list (NULL_TREE, len));
+    result = expand_builtin_memcmp (exp, arglist, target);
+    if (! result)
+      TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
+
+    return result;
+  }
+#else
+  return 0;
+#endif
+}
+
+/* Expand expression EXP, which is a call to the strncmp builtin.  Return 0
+   if we failed the caller should emit a normal call, otherwise try to get
+   the result in TARGET, if convenient.  */
+static rtx
+expand_builtin_strncmp (exp, target, mode)
+     tree exp;
+     rtx target;
+     enum machine_mode mode;
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg1, arg2, arg3;
+  const char *p1, *p2;
+
+  /* If we need to check memory accesses, call the library function.  */
+  if (current_function_check_memory_usage)
+    return 0;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+
+  arg1 = TREE_VALUE (arglist);
+  arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+  arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  /* If the len parameter is zero, return zero.  */
+  if (host_integerp (arg3, 1) && tree_low_cst (arg3, 1) == 0)
+  {
+    /* Evaluate and ignore arg1 and arg2 in case they have
+       side-effects.  */
+    expand_expr (arg1, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    expand_expr (arg2, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    return const0_rtx;
+  }
+
+  p1 = c_getstr (arg1);
+  p2 = c_getstr (arg2);
+
+  /* If all arguments are constant, evaluate at compile-time.  */
+  if (host_integerp (arg3, 1) && p1 && p2)
+  {
+    const int r = strncmp (p1, p2, tree_low_cst (arg3, 1));
+    return (r < 0 ? constm1_rtx : (r > 0 ? const1_rtx : const0_rtx));
+  }
+
+  /* If len == 1 or (either string parameter is "" and (len >= 1)),
+      return (*(const u_char*)arg1 - *(const u_char*)arg2).  */
+  if (host_integerp (arg3, 1)
+      && (tree_low_cst (arg3, 1) == 1
+         || (tree_low_cst (arg3, 1) > 1
+             && ((p1 && *p1 == '\0') || (p2 && *p2 == '\0')))))
+    {
+      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+      tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+      tree ind1 =
+       fold (build1 (CONVERT_EXPR, integer_type_node,
+                     build1 (INDIRECT_REF, cst_uchar_node,
+                             build1 (NOP_EXPR, cst_uchar_ptr_node, arg1))));
+      tree ind2 =
+       fold (build1 (CONVERT_EXPR, integer_type_node,
+                     build1 (INDIRECT_REF, cst_uchar_node,
+                             build1 (NOP_EXPR, cst_uchar_ptr_node, arg2))));
+      tree result = fold (build (MINUS_EXPR, integer_type_node, ind1, ind2));
+      return expand_expr (result, target, mode, EXPAND_NORMAL);
+    }
+
+#ifdef HAVE_cmpstrsi
+  /* If c_strlen can determine an expression for one of the string
+     lengths, and it doesn't have side effects, then call
+     expand_builtin_memcmp() using length MIN(strlen(string)+1, arg3).  */
+  if (HAVE_cmpstrsi)
+    { 
+      tree newarglist, len = 0;
+
+      /* Perhaps one of the strings is really constant, if so prefer
+         that constant length over the other string's length.  */
+      if (p1)
+       len = c_strlen (arg1);
+      else if (p2)
+       len = c_strlen (arg2);
+
+      /* If we still don't have a len, try either string arg as long
+        as they don't have side effects.  */
+      if (!len && !TREE_SIDE_EFFECTS (arg1))
+       len = c_strlen (arg1);
+      if (!len && !TREE_SIDE_EFFECTS (arg2))
+       len = c_strlen (arg2);
+      /* If we still don't have a length, punt.  */
+      if (!len)
+       return 0;
+       
+      /* Add one to the string length.  */
+      len = fold (size_binop (PLUS_EXPR, len, ssize_int (1)));
+        
+      /* The actual new length parameter is MIN(len,arg3).  */
+      len = fold (build (MIN_EXPR, TREE_TYPE (len), len, arg3));
+
+      newarglist = build_tree_list (NULL_TREE, len);
+      newarglist = tree_cons (NULL_TREE, arg2, newarglist);
+      newarglist = tree_cons (NULL_TREE, arg1, newarglist);
+      return expand_builtin_memcmp (exp, newarglist, target);
+    }
+#endif
+  
+  return 0;
+}
+
+/* Expand expression EXP, which is a call to the strcat builtin.
+   Return 0 if we failed the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient.  */
+static rtx
+expand_builtin_strcat (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
+{
+  /* If we need to check memory accesses, call the library function.  */
+  if (current_function_check_memory_usage)
+    return 0;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return 0;
+  else
+    {
+      tree dst = TREE_VALUE (arglist),
+       src = TREE_VALUE (TREE_CHAIN (arglist));
+      const char *p = c_getstr (src);
+
+      /* If the string length is zero, return the dst parameter.  */
+      if (p && *p == '\0')
+       return expand_expr (dst, target, mode, EXPAND_NORMAL);
+
+      return 0;
+    }
+}
+
+/* Expand expression EXP, which is a call to the strncat builtin.
+   Return 0 if we failed the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient.  */
+static rtx
+expand_builtin_strncat (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
+{
+  /* If we need to check memory accesses, call the library function.  */
+  if (current_function_check_memory_usage)
+    return 0;
+
+  if (!validate_arglist (arglist,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return 0;
+  else
+    {
+      tree dst = TREE_VALUE (arglist),
+       src = TREE_VALUE (TREE_CHAIN (arglist)),
+       len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+      const char *p = c_getstr (src);
+
+      /* If the requested length is zero, or the src parameter string
+          length is zero, return the dst parameter.  */
+      if ((TREE_CODE (len) == INTEGER_CST && compare_tree_int (len, 0) == 0)
+         || (p && *p == '\0'))
+        {
+         /* Evaluate and ignore the src and len parameters in case
+            they have side-effects.  */
+         expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return expand_expr (dst, target, mode, EXPAND_NORMAL);
+       }
+
+      /* If the requested len is greater than or equal to the string
+         length, call strcat.  */
+      if (TREE_CODE (len) == INTEGER_CST && p
+         && compare_tree_int (len, strlen (p)) >= 0)
+        {
+         tree newarglist =
+           tree_cons (NULL_TREE, dst, build_tree_list (NULL_TREE, src)),
+           fn = built_in_decls[BUILT_IN_STRCAT];
+         
+         /* If the replacement _DECL isn't initialized, don't do the
+            transformation. */
+         if (!fn)
+           return 0;
+
+         return expand_expr (build_function_call_expr (fn, newarglist),
+                             target, mode, EXPAND_NORMAL);
+       }
+      return 0;
+    }
+}
+
+/* Expand expression EXP, which is a call to the strspn builtin.
+   Return 0 if we failed the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient.  */
+static rtx
+expand_builtin_strspn (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
+{
+  /* If we need to check memory accesses, call the library function.  */
+  if (current_function_check_memory_usage)
+    return 0;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return 0;
+  else
+    {
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
+      
+      /* If both arguments are constants, evaluate at compile-time.  */
+      if (p1 && p2)
+        {
+         const size_t r = strspn (p1, p2);
+         return expand_expr (size_int (r), target, mode, EXPAND_NORMAL);
+       }
+      
+      /* If either argument is "", return 0.  */
+      if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
+        {
+         /* Evaluate and ignore both arguments in case either one has
+            side-effects.  */
+         expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         expand_expr (s2, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return const0_rtx;
+       }
+      return 0;
+    }
+}
+
+/* Expand expression EXP, which is a call to the strcspn builtin.
+   Return 0 if we failed the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient.  */
+static rtx
+expand_builtin_strcspn (arglist, target, mode)
+     tree arglist;
+     rtx target;
+     enum machine_mode mode;
+{
+  /* If we need to check memory accesses, call the library function.  */
+  if (current_function_check_memory_usage)
+    return 0;
+
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return 0;
+  else
+    {
+      tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
+      const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
+      
+      /* If both arguments are constants, evaluate at compile-time.  */
+      if (p1 && p2)
+        {
+         const size_t r = strcspn (p1, p2);
+         return expand_expr (size_int (r), target, mode, EXPAND_NORMAL);
+       }
+      
+      /* If the first argument is "", return 0.  */
+      if (p1 && *p1 == '\0')
+        {
+         /* Evaluate and ignore argument s2 in case it has
+            side-effects.  */
+         expand_expr (s2, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         return const0_rtx;
+       }
 
-    chainon (arglist, build_tree_list (NULL_TREE, len));
-    result = expand_builtin_memcmp (exp, arglist, target);
-    if (! result)
-      TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
-    return result;
-  }
+      /* If the second argument is "", return __builtin_strlen(s1).  */
+      if (p2 && *p2 == '\0')
+        {
+         tree newarglist = build_tree_list (NULL_TREE, s1),
+           fn = built_in_decls[BUILT_IN_STRLEN];
+         
+         /* If the replacement _DECL isn't initialized, don't do the
+            transformation. */
+         if (!fn)
+           return 0;
+
+         return expand_expr (build_function_call_expr (fn, newarglist),
+                             target, mode, EXPAND_NORMAL);
+       }
+      return 0;
+    }
 }
-#endif
 
 /* Expand a call to __builtin_saveregs, generating the result in TARGET,
    if that's convenient.  */
+
 rtx
 expand_builtin_saveregs ()
 {
@@ -1724,6 +2638,7 @@ expand_builtin_saveregs ()
 /* __builtin_args_info (N) returns word N of the arg space info
    for the current function.  The number and meanings of words
    is controlled by the definition of CUMULATIVE_ARGS.  */
+
 static rtx
 expand_builtin_args_info (exp)
      tree exp;
@@ -1823,66 +2738,44 @@ expand_builtin_next_arg (arglist)
    from multiple evaluations.  */
 
 static tree
-stabilize_va_list (valist, was_ptr)
+stabilize_va_list (valist, needs_lvalue)
      tree valist;
-     int was_ptr;
+     int needs_lvalue;
 {
   if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
     {
-      /* If stdarg.h took the address of an array-type valist that was passed
-         as a parameter, we'll have taken the address of the parameter itself
-         rather than the array as we'd intended.  Undo this mistake.  */
+      if (TREE_SIDE_EFFECTS (valist))
+       valist = save_expr (valist);
 
-      if (was_ptr)
+      /* For this case, the backends will be expecting a pointer to
+        TREE_TYPE (va_list_type_node), but it's possible we've
+        actually been given an array (an actual va_list_type_node).
+        So fix it.  */
+      if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
        {
-         STRIP_NOPS (valist);
-
-         /* Two cases: either &array, which decomposed to 
-               <ptr <array <record> valist>>
-            or &ptr, which turned into
-               <ptr <ptr <record>>>
-            In the first case we'll need to put the ADDR_EXPR back
-            after frobbing the types as if &array[0].  */
-
-         if (TREE_CODE (valist) != ADDR_EXPR)
-           abort ();
-         valist = TREE_OPERAND (valist, 0);
-       }
+         tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node));
+         tree p2 = build_pointer_type (va_list_type_node);
 
-      if (TYPE_MAIN_VARIANT (TREE_TYPE (valist))
-         == TYPE_MAIN_VARIANT (va_list_type_node))
-       {
-         tree pt = build_pointer_type (TREE_TYPE (va_list_type_node));
-         valist = build1 (ADDR_EXPR, pt, valist);
-         TREE_SIDE_EFFECTS (valist)
-           = TREE_SIDE_EFFECTS (TREE_OPERAND (valist, 0));
-       }
-      else
-       {
-         if (! POINTER_TYPE_P (TREE_TYPE (valist))
-             || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (valist)))
-                 != TYPE_MAIN_VARIANT (TREE_TYPE (va_list_type_node))))
-           abort ();
+         valist = build1 (ADDR_EXPR, p2, valist);
+         valist = fold (build1 (NOP_EXPR, p1, valist));
        }
-
-      if (TREE_SIDE_EFFECTS (valist))
-       valist = save_expr (valist);
     }
   else
     {
-      if (! was_ptr)
-       {
-         tree pt;
+      tree pt;
 
+      if (! needs_lvalue)
+       {
          if (! TREE_SIDE_EFFECTS (valist))
            return valist;
-
+         
          pt = build_pointer_type (va_list_type_node);
-          valist = fold (build1 (ADDR_EXPR, pt, valist));
+         valist = fold (build1 (ADDR_EXPR, pt, valist));
          TREE_SIDE_EFFECTS (valist) = 1;
        }
+
       if (TREE_SIDE_EFFECTS (valist))
-        valist = save_expr (valist);
+       valist = save_expr (valist);
       valist = fold (build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)),
                             valist));
     }
@@ -1894,14 +2787,23 @@ stabilize_va_list (valist, was_ptr)
    the variable.  */
 void
 std_expand_builtin_va_start (stdarg_p, valist, nextarg)
-     int stdarg_p ATTRIBUTE_UNUSED;
+     int stdarg_p;
      tree valist;
      rtx nextarg;
 {
   tree t;
 
-  if (!stdarg_p)
-    nextarg = plus_constant (nextarg, -UNITS_PER_WORD);
+  if (! stdarg_p)
+    {
+      /* The dummy named parameter is declared as a 'word' sized
+        object, but if a 'word' is smaller than an 'int', it would
+        have been promoted to int when it was added to the arglist.  */
+      int align = PARM_BOUNDARY / BITS_PER_UNIT;
+      int size = MAX (UNITS_PER_WORD,
+                     GET_MODE_SIZE (TYPE_MODE (integer_type_node)));
+      int offset = ((size + align - 1) / align) * align;
+      nextarg = plus_constant (nextarg, -offset);
+    }
 
   t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
             make_tree (ptr_type_node, nextarg));
@@ -1939,19 +2841,9 @@ expand_builtin_va_start (stdarg_p, arglist)
   return const0_rtx;
 }
 
-/* Allocate an alias set for use in storing and reading from the varargs
-   spill area.  */
-int
-get_varargs_alias_set ()
-{
-  static int set = -1;
-  if (set == -1)
-    set = new_alias_set ();
-  return set;
-}
-
 /* The "standard" implementation of va_arg: read the value from the
    current (padded) address and increment by the (padded) size.  */
+
 rtx
 std_expand_builtin_va_arg (valist, type)
      tree valist, type;
@@ -1967,7 +2859,7 @@ std_expand_builtin_va_arg (valist, type)
 
   /* Get AP.  */
   addr_tree = valist;
-  if (BYTES_BIG_ENDIAN)
+  if (PAD_VARARGS_DOWN)
     {
       /* Small args are padded downward.  */
 
@@ -1995,6 +2887,7 @@ std_expand_builtin_va_arg (valist, type)
 
 /* Expand __builtin_va_arg, which is not really a builtin function, but
    a very special sort of operator.  */
+
 rtx
 expand_builtin_va_arg (valist, type)
      tree valist, type;
@@ -2072,12 +2965,13 @@ expand_builtin_va_arg (valist, type)
     }
 
   result = gen_rtx_MEM (TYPE_MODE (type), addr);
-  MEM_ALIAS_SET (result) = get_varargs_alias_set ();
+  set_mem_alias_set (result, get_varargs_alias_set ());
 
   return result;
 }
 
 /* Expand ARGLIST, from a call to __builtin_va_end.  */
+
 static rtx
 expand_builtin_va_end (arglist)
      tree arglist;
@@ -2100,6 +2994,7 @@ expand_builtin_va_end (arglist)
 /* Expand ARGLIST, from a call to __builtin_va_copy.  We do this as a 
    builtin rather than just as an assignment in stdarg.h because of the
    nastiness of array-type va_list types.  */
+
 static rtx
 expand_builtin_va_copy (arglist)
      tree arglist;
@@ -2130,13 +3025,12 @@ expand_builtin_va_copy (arglist)
 
       /* "Dereference" to BLKmode memories.  */
       dstb = gen_rtx_MEM (BLKmode, dstb);
-      MEM_ALIAS_SET (dstb) = get_alias_set (TREE_TYPE (TREE_TYPE (dst)));
+      set_mem_alias_set (dstb, get_alias_set (TREE_TYPE (TREE_TYPE (dst))));
       srcb = gen_rtx_MEM (BLKmode, srcb);
-      MEM_ALIAS_SET (srcb) = get_alias_set (TREE_TYPE (TREE_TYPE (src)));
+      set_mem_alias_set (srcb, get_alias_set (TREE_TYPE (TREE_TYPE (src))));
 
       /* Copy.  */
-      emit_block_move (dstb, srcb, size, 
-                      TYPE_ALIGN (va_list_type_node) / BITS_PER_UNIT);
+      emit_block_move (dstb, srcb, size, TYPE_ALIGN (va_list_type_node));
     }
 
   return const0_rtx;
@@ -2196,38 +3090,43 @@ expand_builtin_frame_address (exp)
 /* Expand a call to the alloca builtin, with arguments ARGLIST.  Return 0 if
    we failed and the caller should emit a normal call, otherwise try to get
    the result in TARGET, if convenient.  */
+
 static rtx
 expand_builtin_alloca (arglist, target)
      tree arglist;
      rtx target;
 {
   rtx op0;
+  rtx result;
 
-  if (arglist == 0
-      /* Arg could be non-integer if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
+  if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
     return 0;
 
   /* Compute the argument.  */
   op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
 
   /* Allocate the desired space.  */
-  return allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
+  result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+  result = convert_memory_address (ptr_mode, result);
+#endif
+
+  return result;
 }
 
 /* Expand a call to the ffs builtin.  The arguments are in ARGLIST.
    Return 0 if a normal call should be emitted rather than expanding the
    function in-line.  If convenient, the result should be placed in TARGET.
    SUBTARGET may be used as the target for computing one of EXP's operands.  */
+
 static rtx
 expand_builtin_ffs (arglist, target, subtarget)
      tree arglist;
      rtx target, subtarget;
 {
   rtx op0;
-  if (arglist == 0
-      /* Arg could be non-integer if user redeclared this fcn wrong.  */
-      || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
+  if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
     return 0;
 
   /* Compute the argument.  */
@@ -2240,6 +3139,250 @@ expand_builtin_ffs (arglist, target, subtarget)
     abort ();
   return target;
 }
+
+/* If the string passed to fputs is a constant and is one character
+   long, we attempt to transform this call into __builtin_fputc(). */
+
+static rtx
+expand_builtin_fputs (arglist, ignore)
+     tree arglist;
+     int ignore;
+{
+  tree len, fn, fn_fputc = built_in_decls[BUILT_IN_FPUTC],
+    fn_fwrite = built_in_decls[BUILT_IN_FWRITE];
+
+  /* If the return value is used, or the replacement _DECL isn't
+     initialized, don't do the transformation. */
+  if (!ignore || !fn_fputc || !fn_fwrite)
+    return 0;
+
+  /* Verify the arguments in the original call. */
+  if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+      || current_function_check_memory_usage)
+    return 0;
+
+  /* Get the length of the string passed to fputs.  If the length
+     can't be determined, punt.  */
+  if (!(len = c_strlen (TREE_VALUE (arglist)))
+      || TREE_CODE (len) != INTEGER_CST)
+    return 0;
+
+  switch (compare_tree_int (len, 1))
+    {
+    case -1: /* length is 0, delete the call entirely .  */
+      {
+       /* Evaluate and ignore the argument in case it has
+           side-effects.  */
+       expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), const0_rtx,
+                    VOIDmode, EXPAND_NORMAL);
+       return const0_rtx;
+      }
+    case 0: /* length is 1, call fputc.  */
+      {
+       const char *p = c_getstr (TREE_VALUE (arglist));
+
+       if (p != NULL)
+         {      
+           /* New argument list transforming fputs(string, stream) to
+              fputc(string[0], stream).  */
+           arglist =
+             build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)));
+           arglist =
+             tree_cons (NULL_TREE, build_int_2 (p[0], 0), arglist);
+           fn = fn_fputc;
+           break;
+         }
+      }
+      /* FALLTHROUGH */
+    case 1: /* length is greater than 1, call fwrite.  */
+      {
+       tree string_arg = TREE_VALUE (arglist);
+      
+       /* New argument list transforming fputs(string, stream) to
+          fwrite(string, 1, len, stream).  */
+       arglist = build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)));
+       arglist = tree_cons (NULL_TREE, len, arglist);
+       arglist = tree_cons (NULL_TREE, size_one_node, arglist);
+       arglist = tree_cons (NULL_TREE, string_arg, arglist);
+       fn = fn_fwrite;
+       break;
+      }
+    default:
+      abort();
+    }
+  
+  return expand_expr (build_function_call_expr (fn, arglist),
+                     (ignore ? const0_rtx : NULL_RTX),
+                     VOIDmode, EXPAND_NORMAL);
+}
+
+/* Expand a call to __builtin_expect.  We return our argument and emit a
+   NOTE_INSN_EXPECTED_VALUE note.  This is the expansion of __builtin_expect in
+   a non-jump context.  */
+
+static rtx
+expand_builtin_expect (arglist, target)
+     tree arglist;
+     rtx target;
+{
+  tree exp, c;
+  rtx note, rtx_c;
+
+  if (arglist == NULL_TREE
+      || TREE_CHAIN (arglist) == NULL_TREE)
+    return const0_rtx;
+  exp = TREE_VALUE (arglist);
+  c = TREE_VALUE (TREE_CHAIN (arglist));
+
+  if (TREE_CODE (c) != INTEGER_CST)
+    {
+      error ("second arg to `__builtin_expect' must be a constant");
+      c = integer_zero_node;
+    }
+
+  target = expand_expr (exp, target, VOIDmode, EXPAND_NORMAL);
+
+  /* Don't bother with expected value notes for integral constants.  */
+  if (GET_CODE (target) != CONST_INT)
+    {
+      /* We do need to force this into a register so that we can be
+        moderately sure to be able to correctly interpret the branch
+        condition later.  */
+      target = force_reg (GET_MODE (target), target);
+  
+      rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL);
+
+      note = emit_note (NULL, NOTE_INSN_EXPECTED_VALUE);
+      NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c);
+    }
+
+  return target;
+}
+
+/* Like expand_builtin_expect, except do this in a jump context.  This is
+   called from do_jump if the conditional is a __builtin_expect.  Return either
+   a SEQUENCE of insns to emit the jump or NULL if we cannot optimize
+   __builtin_expect.  We need to optimize this at jump time so that machines
+   like the PowerPC don't turn the test into a SCC operation, and then jump
+   based on the test being 0/1.  */
+
+rtx
+expand_builtin_expect_jump (exp, if_false_label, if_true_label)
+     tree exp;
+     rtx if_false_label;
+     rtx if_true_label;
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg0 = TREE_VALUE (arglist);
+  tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+  rtx ret = NULL_RTX;
+
+  /* Only handle __builtin_expect (test, 0) and
+     __builtin_expect (test, 1).  */
+  if (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE
+      && TREE_CODE (arg1) == INTEGER_CST
+      && (TREE_INT_CST_LOW (arg1) == 0 || TREE_INT_CST_LOW (arg1) == 1)
+      && TREE_INT_CST_HIGH (arg1) == 0)
+    {
+      int j;
+      int num_jumps = 0;
+
+      /* If we fail to locate an appropriate conditional jump, we'll
+        fall back to normal evaluation.  Ensure that the expression
+        can be re-evaluated.  */
+      switch (unsafe_for_reeval (arg0))
+       {
+       case 0: /* Safe.  */
+         break;
+
+       case 1: /* Mildly unsafe.  */
+         arg0 = unsave_expr (arg0);
+         break;
+
+       case 2: /* Wildly unsafe.  */
+         return NULL_RTX;
+       }
+
+      /* Expand the jump insns.  */
+      start_sequence ();
+      do_jump (arg0, if_false_label, if_true_label);
+      ret = gen_sequence ();
+      end_sequence ();
+
+      /* Now that the __builtin_expect has been validated, go through and add
+        the expect's to each of the conditional jumps.  If we run into an
+        error, just give up and generate the 'safe' code of doing a SCC
+        operation and then doing a branch on that.  */
+      for (j = 0; j < XVECLEN (ret, 0); j++)
+       {
+         rtx insn = XVECEXP (ret, 0, j);
+         rtx pattern;
+
+         if (GET_CODE (insn) == JUMP_INSN && any_condjump_p (insn)
+             && (pattern = pc_set (insn)) != NULL_RTX)
+           {
+             rtx ifelse = SET_SRC (pattern);
+             rtx label;
+             int taken;
+
+             if (GET_CODE (ifelse) != IF_THEN_ELSE)
+               continue;
+
+             if (GET_CODE (XEXP (ifelse, 1)) == LABEL_REF)
+               {
+                 taken = 1;
+                 label = XEXP (XEXP (ifelse, 1), 0);
+               }
+             /* An inverted jump reverses the probabilities.  */
+             else if (GET_CODE (XEXP (ifelse, 2)) == LABEL_REF)
+               {
+                 taken = 0;
+                 label = XEXP (XEXP (ifelse, 2), 0);
+               }
+             /* We shouldn't have to worry about conditional returns during
+                the expansion stage, but handle it gracefully anyway.  */
+             else if (GET_CODE (XEXP (ifelse, 1)) == RETURN)
+               {
+                 taken = 1;
+                 label = NULL_RTX;
+               }
+             /* An inverted return reverses the probabilities.  */
+             else if (GET_CODE (XEXP (ifelse, 2)) == RETURN)
+               {
+                 taken = 0;
+                 label = NULL_RTX;
+               }
+             else
+               continue;
+
+             /* If the test is expected to fail, reverse the
+                probabilities.  */
+             if (TREE_INT_CST_LOW (arg1) == 0)
+               taken = 1 - taken;
+
+             /* If we are jumping to the false label, reverse the
+                probabilities.  */
+             if (label == NULL_RTX)
+               ;               /* conditional return */
+             else if (label == if_false_label)
+               taken = 1 - taken;
+             else if (label != if_true_label)
+               continue;
+
+             num_jumps++;
+             predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken);
+           }
+       }
+
+      /* If no jumps were modified, fail and do __builtin_expect the normal
+        way.  */
+      if (num_jumps == 0)
+       ret = NULL_RTX;
+    }
+
+  return ret;
+}
+
 \f
 /* Expand an expression EXP that calls a built-in function,
    with result going to TARGET if that's convenient
@@ -2259,35 +3402,68 @@ expand_builtin (exp, target, subtarget, mode, ignore)
   tree arglist = TREE_OPERAND (exp, 1);
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
 
-#ifdef MD_EXPAND_BUILTIN
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
-    return MD_EXPAND_BUILTIN (exp, target, subtarget, mode, ignore);
-#endif
+    return (*targetm.expand_builtin) (exp, target, subtarget, mode, ignore);
   
   /* When not optimizing, generate calls to library functions for a certain
      set of builtins.  */
   if (! optimize && ! CALLED_AS_BUILT_IN (fndecl)
       && (fcode == BUILT_IN_SIN || fcode == BUILT_IN_COS
-         || fcode == BUILT_IN_FSQRT || fcode == BUILT_IN_MEMSET
+         || fcode == BUILT_IN_FSQRT || fcode == BUILT_IN_SQRTF
+         || fcode == BUILT_IN_SQRTL || fcode == BUILT_IN_MEMSET
          || fcode == BUILT_IN_MEMCPY || fcode == BUILT_IN_MEMCMP
+         || fcode == BUILT_IN_BCMP || fcode == BUILT_IN_BZERO
+         || fcode == BUILT_IN_INDEX || fcode == BUILT_IN_RINDEX
+         || fcode == BUILT_IN_STRCHR || fcode == BUILT_IN_STRRCHR
          || fcode == BUILT_IN_STRLEN || fcode == BUILT_IN_STRCPY
-         || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS))
+         || fcode == BUILT_IN_STRNCPY || fcode == BUILT_IN_STRNCMP
+         || fcode == BUILT_IN_STRSTR || fcode == BUILT_IN_STRPBRK
+         || fcode == BUILT_IN_STRCAT || fcode == BUILT_IN_STRNCAT
+         || fcode == BUILT_IN_STRSPN || fcode == BUILT_IN_STRCSPN
+         || fcode == BUILT_IN_STRCMP || fcode == BUILT_IN_FFS
+         || fcode == BUILT_IN_PUTCHAR || fcode == BUILT_IN_PUTS
+         || fcode == BUILT_IN_PRINTF || fcode == BUILT_IN_FPUTC
+         || fcode == BUILT_IN_FPUTS || fcode == BUILT_IN_FWRITE))
     return expand_call (exp, target, ignore);
 
   switch (fcode)
     {
     case BUILT_IN_ABS:
     case BUILT_IN_LABS:
+    case BUILT_IN_LLABS:
+    case BUILT_IN_IMAXABS:
     case BUILT_IN_FABS:
+    case BUILT_IN_FABSF:
+    case BUILT_IN_FABSL:
       /* build_function_call changes these into ABS_EXPR.  */
       abort ();
 
+    case BUILT_IN_CONJ:
+    case BUILT_IN_CONJF:
+    case BUILT_IN_CONJL:
+    case BUILT_IN_CREAL:
+    case BUILT_IN_CREALF:
+    case BUILT_IN_CREALL:
+    case BUILT_IN_CIMAG:
+    case BUILT_IN_CIMAGF:
+    case BUILT_IN_CIMAGL:
+      /* expand_tree_builtin changes these into CONJ_EXPR, REALPART_EXPR
+        and IMAGPART_EXPR.  */
+      abort ();
+
     case BUILT_IN_SIN:
+    case BUILT_IN_SINF:
+    case BUILT_IN_SINL:
     case BUILT_IN_COS:
-      /* Treat these like sqrt, but only if the user asks for them.  */
-      if (! flag_fast_math)
+    case BUILT_IN_COSF:
+    case BUILT_IN_COSL:
+      /* Treat these like sqrt only if unsafe math optimizations are allowed,
+        because of possible accuracy problems.  */
+      if (! flag_unsafe_math_optimizations)
        break;
     case BUILT_IN_FSQRT:
+    case BUILT_IN_SQRTF:
+    case BUILT_IN_SQRTL:
       target = expand_builtin_mathfn (exp, target, subtarget);
       if (target)
        return target;
@@ -2310,13 +3486,10 @@ expand_builtin (exp, target, subtarget, mode, ignore)
         computed?  We'll also need a safe worst case value for varargs
         functions.  */
     case BUILT_IN_APPLY:
-      if (arglist == 0
-         /* Arg could be non-pointer if user redeclared this fcn wrong.  */
-         || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
-         || TREE_CHAIN (arglist) == 0
-         || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
-         || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
-         || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE)
+      if (!validate_arglist (arglist, POINTER_TYPE,
+                            POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+         && !validate_arglist (arglist, REFERENCE_TYPE,
+                               POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
        return const0_rtx;
       else
        {
@@ -2334,9 +3507,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
         value described by RESULT.  RESULT is address of the block of
         memory returned by __builtin_apply.  */
     case BUILT_IN_RETURN:
-      if (arglist
-         /* Arg could be non-pointer if user redeclared this fcn wrong.  */
-         && TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) == POINTER_TYPE)
+      if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
        expand_builtin_return (expand_expr (TREE_VALUE (arglist),
                                            NULL_RTX, VOIDmode, 0));
       return const0_rtx;
@@ -2384,7 +3555,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
       break;
 
     case BUILT_IN_STRLEN:
-      target = expand_builtin_strlen (exp, target, mode);
+      target = expand_builtin_strlen (exp, target);
       if (target)
        return target;
       break;
@@ -2395,6 +3566,62 @@ expand_builtin (exp, target, subtarget, mode, ignore)
        return target;
       break;
       
+    case BUILT_IN_STRNCPY:
+      target = expand_builtin_strncpy (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+      
+    case BUILT_IN_STRCAT:
+      target = expand_builtin_strcat (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+      
+    case BUILT_IN_STRNCAT:
+      target = expand_builtin_strncat (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+      
+    case BUILT_IN_STRSPN:
+      target = expand_builtin_strspn (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+      
+    case BUILT_IN_STRCSPN:
+      target = expand_builtin_strcspn (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+      
+    case BUILT_IN_STRSTR:
+      target = expand_builtin_strstr (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+      
+    case BUILT_IN_STRPBRK:
+      target = expand_builtin_strpbrk (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+      
+    case BUILT_IN_INDEX:
+    case BUILT_IN_STRCHR:
+      target = expand_builtin_strchr (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_RINDEX:
+    case BUILT_IN_STRRCHR:
+      target = expand_builtin_strrchr (arglist, target, mode);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_MEMCPY:
       target = expand_builtin_memcpy (arglist);
       if (target)
@@ -2407,47 +3634,51 @@ expand_builtin (exp, target, subtarget, mode, ignore)
        return target;
       break;
 
-/* These comparison functions need an instruction that returns an actual
-   index.  An ordinary compare that just sets the condition codes
-   is not enough.  */
-#ifdef HAVE_cmpstrsi
+    case BUILT_IN_BZERO:
+      target = expand_builtin_bzero (exp);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_STRCMP:
-      target = expand_builtin_strcmp (exp, target);
+      target = expand_builtin_strcmp (exp, target, mode);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_STRNCMP:
+      target = expand_builtin_strncmp (exp, target, mode);
       if (target)
        return target;
       break;
 
+/* These comparison functions need an instruction that returns an actual
+   index.  An ordinary compare that just sets the condition codes
+   is not enough.  */
+#ifdef HAVE_cmpstrsi
+    case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       target = expand_builtin_memcmp (exp, arglist, target);
       if (target)
        return target;
       break;
 #else
-    case BUILT_IN_STRCMP:
+    case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       break;
 #endif
 
     case BUILT_IN_SETJMP:
-      if (arglist == 0
-         || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
-       break;
-      else
-       {
-         rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget,
-                                     VOIDmode, 0);
-         rtx lab = gen_label_rtx ();
-         rtx ret = expand_builtin_setjmp (buf_addr, target, lab, lab);
-         emit_label (lab);
-         return ret;
-       }
+      target = expand_builtin_setjmp (arglist, target);
+      if (target)
+       return target;
+      break;
 
       /* __builtin_longjmp is passed a pointer to an array of five words.
         It's similar to the C library longjmp function but works with
         __builtin_setjmp above.  */
     case BUILT_IN_LONGJMP:
-      if (arglist == 0 || TREE_CHAIN (arglist) == 0
-         || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+      if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
        break;
       else
        {
@@ -2476,6 +3707,17 @@ expand_builtin (exp, target, subtarget, mode, ignore)
       emit_barrier ();
       return const0_rtx;
 
+    case BUILT_IN_PUTCHAR:
+    case BUILT_IN_PUTS:
+    case BUILT_IN_FPUTC:
+    case BUILT_IN_FWRITE:
+      break;
+    case BUILT_IN_FPUTS:
+      target = expand_builtin_fputs (arglist, ignore);
+      if (target)
+       return target;
+      break;
+      
       /* Various hooks for the DWARF 2 __throw routine.  */
     case BUILT_IN_UNWIND_INIT:
       expand_builtin_unwind_init ();
@@ -2495,9 +3737,12 @@ expand_builtin (exp, target, subtarget, mode, ignore)
       return expand_builtin_extract_return_addr (TREE_VALUE (arglist));
     case BUILT_IN_EH_RETURN:
       expand_builtin_eh_return (TREE_VALUE (arglist),
-                               TREE_VALUE (TREE_CHAIN (arglist)),
-                               TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))));
+                               TREE_VALUE (TREE_CHAIN (arglist)));
       return const0_rtx;
+#ifdef EH_RETURN_DATA_REGNO
+    case BUILT_IN_EH_RETURN_DATA_REGNO:
+      return expand_builtin_eh_return_data_regno (arglist);
+#endif
     case BUILT_IN_VARARGS_START:
       return expand_builtin_va_start (0, arglist);
     case BUILT_IN_STDARG_START:
@@ -2506,6 +3751,8 @@ expand_builtin (exp, target, subtarget, mode, ignore)
       return expand_builtin_va_end (arglist);
     case BUILT_IN_VA_COPY:
       return expand_builtin_va_copy (arglist);
+    case BUILT_IN_EXPECT:
+      return expand_builtin_expect (arglist, target);
 
     default:                   /* just do library call, if unknown builtin */
       error ("built-in function `%s' not currently supported",
@@ -2516,3 +3763,156 @@ expand_builtin (exp, target, subtarget, mode, ignore)
      to be called normally.  */
   return expand_call (exp, target, ignore);
 }
+
+/* Fold a call to __builtin_constant_p, if we know it will evaluate to a
+   constant.  ARGLIST is the argument list of the call.  */
+
+static tree
+fold_builtin_constant_p (arglist)
+     tree arglist;
+{
+  if (arglist == 0)
+    return 0;
+
+  arglist = TREE_VALUE (arglist);
+
+  /* We return 1 for a numeric type that's known to be a constant
+     value at compile-time or for an aggregate type that's a
+     literal constant.  */
+  STRIP_NOPS (arglist);
+
+  /* If we know this is a constant, emit the constant of one.  */
+  if (TREE_CODE_CLASS (TREE_CODE (arglist)) == 'c'
+      || (TREE_CODE (arglist) == CONSTRUCTOR
+         && TREE_CONSTANT (arglist))
+      || (TREE_CODE (arglist) == ADDR_EXPR
+         && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST))
+    return integer_one_node;
+
+  /* If we aren't going to be running CSE or this expression
+     has side effects, show we don't know it to be a constant.
+     Likewise if it's a pointer or aggregate type since in those
+     case we only want literals, since those are only optimized
+     when generating RTL, not later.  */
+  if (TREE_SIDE_EFFECTS (arglist) || cse_not_expected
+      || AGGREGATE_TYPE_P (TREE_TYPE (arglist))
+      || POINTER_TYPE_P (TREE_TYPE (arglist)))
+    return integer_zero_node;
+
+  return 0;
+}
+
+/* Used by constant folding to eliminate some builtin calls early.  EXP is
+   the CALL_EXPR of a call to a builtin function.  */
+
+tree
+fold_builtin (exp)
+     tree exp;
+{
+  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  tree arglist = TREE_OPERAND (exp, 1);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+    return 0;
+
+  switch (fcode)
+    {
+    case BUILT_IN_CONSTANT_P:
+      return fold_builtin_constant_p (arglist);
+
+    case BUILT_IN_STRLEN:
+      if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
+       {
+         tree len = c_strlen (TREE_VALUE (arglist));
+         if (len != 0)
+           return len;
+       }
+      break;
+
+    default:
+      break;
+    }
+
+  return 0;
+}
+
+static tree
+build_function_call_expr (fn, arglist)
+     tree fn, arglist;
+{
+  tree call_expr;
+
+  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);
+  TREE_SIDE_EFFECTS (call_expr) = 1;
+  return fold (call_expr);
+}
+
+/* This function validates the types of a function call argument list
+   represented as a tree chain of parameters against a specified list
+   of tree_codes.  If the last specifier is a 0, that represents an
+   ellipses, otherwise the last specifier must be a VOID_TYPE.  */
+static int
+validate_arglist VPARAMS ((tree arglist, ...))
+{
+#ifndef ANSI_PROTOTYPES
+  tree arglist;
+#endif
+  enum tree_code code;
+  va_list ap;
+
+  VA_START (ap, arglist);
+
+#ifndef ANSI_PROTOTYPES
+  arglist = va_arg (ap, tree);
+#endif
+
+  do {
+    code = va_arg (ap, enum tree_code);
+    switch (code)
+    {
+    case 0:
+      /* This signifies an ellipses, any further arguments are all ok.  */
+      va_end (ap);
+      return 1;
+    case VOID_TYPE:
+      /* This signifies an endlink, if no arguments remain, return
+         true, otherwise return false.  */
+      va_end (ap);
+      return arglist == 0;
+    default:
+      /* If no parameters remain or the parameter's code does not
+         match the specified code, return false.  Otherwise continue
+         checking any remaining arguments.  */
+      if (arglist == 0 || code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))))
+        {
+         va_end (ap);
+         return 0;
+       }
+      break;
+    }
+    arglist = TREE_CHAIN (arglist);
+  } while (1);
+}
+
+/* Default version of target-specific builtin setup that does nothing.  */
+
+void
+default_init_builtins ()
+{
+}
+
+/* Default target-specific builtin expander that does nothing.  */
+
+rtx
+default_expand_builtin (exp, target, subtarget, mode, ignore)
+     tree exp ATTRIBUTE_UNUSED;
+     rtx target ATTRIBUTE_UNUSED;
+     rtx subtarget ATTRIBUTE_UNUSED;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     int ignore ATTRIBUTE_UNUSED;
+{
+  return NULL_RTX;
+}