This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[PATCH] Builtin strcpy/strncpy/memcpy/strcmp/fputs fixes and optimizations


Hi!

This patch is a 3rd attempt at the memcpy(p, "abcd", 5) optimization.
So that strncpy can be handled by that code, I had to split it into a test
phase (can_store_by_pieces) which does not emit any instructions and actual
action function (store_by_pieces), because at the moment we want to return 0
and fallback to strncpy call if that fails.
This patch also fixes several bugs I found in the mean time. We'd die on
tree checking for calls like strncpy(p, "abcde" + (i++ & 3), 4) or
fputs ("foobar" + (i++ & 3), f) and miscompile
strcmp ("abcde" + (i++ & 3), "vwxyz" + (j++ & 3))
The issue in the first two cases is that c_strlen can return a tree other
than INTEGER_CST and in the last case that if both c_strlen returned trees
are having side-effects, we cannot simply take that expression with side
effects and copy it into another argument because then one of the side
effects happens twice. I know we could do something with SAVE_EXPR, but IMHO
it is not worth optimization as it is pretty uncommon.

To sum up, c_getstr guarantees the argument is constant string with constant
length without side effects if returning non-NULL, while c_strlen does not
(I think it just guarantees that if it returns an INTEGER_CST that it does
not have side effects).

Bootstrapped on i386-redhat-linux, no regressions.
Ok to commit?

2000-11-29  Jakub Jelinek  <jakub@redhat.com>

	* expr.h (store_by_pieces): Add prototype.
	(can_store_by_pieces): Likewise.
	* expr.c (struct store_by_pieces): Renamed from clear_by_pieces.
	(can_store_by_pieces): New.
	(store_by_pieces): New.
	(clear_by_pieces): New.
	(clear_by_pieces_1): New.
	(store_by_pieces_1): Renamed from clear_by_pieces, handle storing
	arbitrary compiler generated constants into memory block.
	(store_by_pieces_2): Renamed from clear_by_pieces_1, likewise.
	* builtins.c (c_readstr): New.
	(builtin_memcpy_read_str): New.
	(expand_builtin_memcpy): If src is string constant and
	emit_block_move would move it by pieces, compute integer constants
	from the string and store it into memory block instead.
	(builtin_strncpy_read_str): New.
	(expand_builtin_strncpy): I N is not constant zero and c_strlen does
	not return INTEGER_CST, don't optimize.
	If N is larger than strlen(src) + 1, try to copy the string
	including padding with store_by_pieces.
	(expand_builtin_strcmp): If both arguments have side effects, don't
	optimize.
	(expand_builtin_fputs): If STR has side effects, don't optimize.

	* gcc.c-torture/execute/string-opt-5.c: Add some strcmp and strncpy
	tests.
	* gcc.c-torture/execute/string-opt-6.c: New test.

--- gcc/expr.h.jj	Mon Nov  6 09:38:24 2000
+++ gcc/expr.h	Wed Nov 29 01:05:24 2000
@@ -1010,6 +1010,25 @@ extern void use_group_regs PARAMS ((rtx 
    alignment.  */
 extern rtx clear_storage PARAMS ((rtx, rtx, unsigned int));
 
+/* Return non-zero if it is desirable to store LEN bytes generated by
+   CONSTFUN with several move instructions by store_by_pieces
+   function.  CONSTFUNDATA is a pointer which will be passed as argument
+   in every CONSTFUN call.
+   ALIGN is maximum alignment we can assume.  */
+extern int can_store_by_pieces PARAMS ((unsigned HOST_WIDE_INT,
+					rtx (*) (PTR, HOST_WIDE_INT,
+						 enum machine_mode),
+					PTR, unsigned int));
+
+/* Generate several move instructions to store LEN bytes generated by
+   CONSTFUN to block TO.  (A MEM rtx with BLKmode).  CONSTFUNDATA is a
+   pointer which will be passed as argument in every CONSTFUN call.
+   ALIGN is maximum alignment we can assume.  */
+extern void store_by_pieces PARAMS ((rtx, unsigned HOST_WIDE_INT,
+				     rtx (*) (PTR, HOST_WIDE_INT,
+					      enum machine_mode),
+				     PTR, unsigned int));
+
 /* Emit insns to set X from Y.  */
 extern rtx emit_move_insn PARAMS ((rtx, rtx));
 
--- gcc/expr.c.jj	Fri Nov 24 10:47:59 2000
+++ gcc/expr.c	Wed Nov 29 01:05:24 2000
@@ -127,10 +127,10 @@ struct move_by_pieces
   int reverse;
 };
 
-/* This structure is used by clear_by_pieces to describe the clear to
+/* This structure is used by store_by_pieces to describe the clear to
    be performed.  */
 
-struct clear_by_pieces
+struct store_by_pieces
 {
   rtx to;
   rtx to_addr;
@@ -138,6 +138,8 @@ struct clear_by_pieces
   int explicit_inc_to;
   unsigned HOST_WIDE_INT len;
   HOST_WIDE_INT offset;
+  rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
+  PTR constfundata;
   int reverse;
 };
 
@@ -151,11 +153,15 @@ static unsigned HOST_WIDE_INT move_by_pi
 					 unsigned int));
 static void move_by_pieces_1	PARAMS ((rtx (*) (rtx, ...), enum machine_mode,
 					 struct move_by_pieces *));
+static rtx clear_by_pieces_1	PARAMS ((PTR, HOST_WIDE_INT,
+					 enum machine_mode));
 static void clear_by_pieces	PARAMS ((rtx, unsigned HOST_WIDE_INT,
 					 unsigned int));
-static void clear_by_pieces_1	PARAMS ((rtx (*) (rtx, ...),
+static void store_by_pieces_1	PARAMS ((struct store_by_pieces *,
+					 unsigned int));
+static void store_by_pieces_2	PARAMS ((rtx (*) (rtx, ...),
 					 enum machine_mode,
-					 struct clear_by_pieces *));
+					 struct store_by_pieces *));
 static rtx get_subtarget	PARAMS ((rtx));
 static int is_zeros_p		PARAMS ((tree));
 static int mostly_zeros_p	PARAMS ((tree));
@@ -2249,6 +2255,103 @@ use_group_regs (call_fusage, regs)
     }
 }
 
+
+int
+can_store_by_pieces (len, constfun, constfundata, align)
+     unsigned HOST_WIDE_INT len;
+     rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
+     PTR constfundata;
+     unsigned int align;
+{
+  unsigned HOST_WIDE_INT max_size = MOVE_MAX_PIECES + 1, l;
+  HOST_WIDE_INT offset = 0;
+  enum machine_mode mode, tmode;
+  enum insn_code icode;
+  int reverse;
+
+  if (! MOVE_BY_PIECES_P (len, align))
+    return 0;
+
+  if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
+      || align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
+    align = MOVE_MAX * BITS_PER_UNIT;
+
+  /* We would first store what we can in the largest integer mode, then go to
+     successively smaller modes.  */
+
+  for (reverse = 0;
+       reverse <= (HAVE_PRE_DECREMENT || HAVE_POST_DECREMENT);
+       reverse++)
+    {
+      l = len;
+      mode = VOIDmode;
+      while (max_size > 1)
+	{
+	  for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+	       tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
+	    if (GET_MODE_SIZE (tmode) < max_size)
+	      mode = tmode;
+
+	  if (mode == VOIDmode)
+	    break;
+
+	  icode = mov_optab->handlers[(int) mode].insn_code;
+	  if (icode != CODE_FOR_nothing
+	      && align >= GET_MODE_ALIGNMENT (mode))
+	    {
+	      unsigned int size = GET_MODE_SIZE (mode);
+
+	      while (l >= size)
+		{
+		  if (reverse)
+		    offset -= size;
+
+		  if (!(*constfun) (constfundata, offset, mode))
+		    return 0;
+
+		  if (!reverse)
+		    offset += size;
+
+		  l -= size;
+		}
+	    }
+
+	  max_size = GET_MODE_SIZE (mode);
+	}
+
+      /* The code above should have handled everything.  */
+      if (l != 0)
+	abort ();
+    }
+
+  return 1;
+}
+
+/* Generate several move instructions to store LEN bytes generated by
+   CONSTFUN to block TO.  (A MEM rtx with BLKmode).  CONSTFUNDATA is a
+   pointer which will be passed as argument in every CONSTFUN call.
+   ALIGN is maximum alignment we can assume.  */
+
+void
+store_by_pieces (to, len, constfun, constfundata, align)
+     rtx to;
+     unsigned HOST_WIDE_INT len;
+     rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
+     PTR constfundata;
+     unsigned int align;
+{
+  struct store_by_pieces data;
+
+  if (! MOVE_BY_PIECES_P (len, align))
+    abort ();
+  to = protect_from_queue (to, 1);
+  data.constfun = constfun;
+  data.constfundata = constfundata;
+  data.len = len;
+  data.to = to;
+  store_by_pieces_1 (&data, align);
+}
+
 /* Generate several move instructions to clear LEN bytes of block TO.  (A MEM
    rtx with BLKmode).  The caller must pass TO through protect_from_queue
    before calling. ALIGN is maximum alignment we can assume.  */
@@ -2259,31 +2362,59 @@ clear_by_pieces (to, len, align)
      unsigned HOST_WIDE_INT len;
      unsigned int align;
 {
-  struct clear_by_pieces data;
-  rtx to_addr = XEXP (to, 0);
+  struct store_by_pieces data;
+
+  data.constfun = clear_by_pieces_1;
+  data.constfundata = NULL_PTR;
+  data.len = len;
+  data.to = to;
+  store_by_pieces_1 (&data, align);
+}
+
+/* Callback routine for clear_by_pieces.
+   Return const0_rtx unconditionally.  */
+
+static rtx
+clear_by_pieces_1 (data, offset, mode)
+     PTR data ATTRIBUTE_UNUSED;
+     HOST_WIDE_INT offset ATTRIBUTE_UNUSED;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return const0_rtx;
+}
+
+/* Subroutine of clear_by_pieces and store_by_pieces.
+   Generate several move instructions to store LEN bytes of block TO.  (A MEM
+   rtx with BLKmode).  The caller must pass TO through protect_from_queue
+   before calling.  ALIGN is maximum alignment we can assume.  */
+
+static void
+store_by_pieces_1 (data, align)
+     struct store_by_pieces *data;
+     unsigned int align;
+{
+  rtx to_addr = XEXP (data->to, 0);
   unsigned HOST_WIDE_INT max_size = MOVE_MAX_PIECES + 1;
   enum machine_mode mode = VOIDmode, tmode;
   enum insn_code icode;
 
-  data.offset = 0;
-  data.to_addr = to_addr;
-  data.to = to;
-  data.autinc_to
+  data->offset = 0;
+  data->to_addr = to_addr;
+  data->autinc_to
     = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC
        || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC);
 
-  data.explicit_inc_to = 0;
-  data.reverse
+  data->explicit_inc_to = 0;
+  data->reverse
     = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC);
-  if (data.reverse)
-    data.offset = len;
-  data.len = len;
+  if (data->reverse)
+    data->offset = data->len;
 
-  /* If copying requires more than two move insns,
+  /* If storing requires more than two move insns,
      copy addresses to registers (to make displacements shorter)
      and use post-increment if available.  */
-  if (!data.autinc_to
-      && move_by_pieces_ninsns (len, align) > 2)
+  if (!data->autinc_to
+      && move_by_pieces_ninsns (data->len, align) > 2)
     {
       /* Determine the main mode we'll be using.  */
       for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
@@ -2291,30 +2422,30 @@ clear_by_pieces (to, len, align)
 	if (GET_MODE_SIZE (tmode) < max_size)
 	  mode = tmode;
 
-      if (USE_STORE_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_to)
+      if (USE_STORE_PRE_DECREMENT (mode) && data->reverse && ! data->autinc_to)
 	{
-	  data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len));
-	  data.autinc_to = 1;
-	  data.explicit_inc_to = -1;
+	  data->to_addr = copy_addr_to_reg (plus_constant (to_addr, data->len));
+	  data->autinc_to = 1;
+	  data->explicit_inc_to = -1;
 	}
 
-      if (USE_STORE_POST_INCREMENT (mode) && ! data.reverse
-	  && ! data.autinc_to)
+      if (USE_STORE_POST_INCREMENT (mode) && ! data->reverse
+	  && ! data->autinc_to)
 	{
-	  data.to_addr = copy_addr_to_reg (to_addr);
-	  data.autinc_to = 1;
-	  data.explicit_inc_to = 1;
+	  data->to_addr = copy_addr_to_reg (to_addr);
+	  data->autinc_to = 1;
+	  data->explicit_inc_to = 1;
 	}
 
-      if ( !data.autinc_to && CONSTANT_P (to_addr))
-	data.to_addr = copy_addr_to_reg (to_addr);
+      if ( !data->autinc_to && CONSTANT_P (to_addr))
+	data->to_addr = copy_addr_to_reg (to_addr);
     }
 
   if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
       || align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
     align = MOVE_MAX * BITS_PER_UNIT;
 
-  /* First move what we can in the largest integer mode, then go to
+  /* First store what we can in the largest integer mode, then go to
      successively smaller modes.  */
 
   while (max_size > 1)
@@ -2329,28 +2460,28 @@ clear_by_pieces (to, len, align)
 
       icode = mov_optab->handlers[(int) mode].insn_code;
       if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode))
-	clear_by_pieces_1 (GEN_FCN (icode), mode, &data);
+	store_by_pieces_2 (GEN_FCN (icode), mode, data);
 
       max_size = GET_MODE_SIZE (mode);
     }
 
   /* The code above should have handled everything.  */
-  if (data.len != 0)
+  if (data->len != 0)
     abort ();
 }
 
-/* Subroutine of clear_by_pieces.  Clear as many bytes as appropriate
+/* Subroutine of store_by_pieces_1.  Store as many bytes as appropriate
    with move instructions for mode MODE.  GENFUN is the gen_... function
    to make a move insn for that mode.  DATA has all the other info.  */
 
 static void
-clear_by_pieces_1 (genfun, mode, data)
+store_by_pieces_2 (genfun, mode, data)
      rtx (*genfun) PARAMS ((rtx, ...));
      enum machine_mode mode;
-     struct clear_by_pieces *data;
+     struct store_by_pieces *data;
 {
   unsigned int size = GET_MODE_SIZE (mode);
-  rtx to1;
+  rtx to1, cst;
 
   while (data->len >= size)
     {
@@ -2367,9 +2498,14 @@ clear_by_pieces_1 (genfun, mode, data)
 			      plus_constant (data->to_addr, data->offset));
 
       if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0)
-	emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size)));
+	emit_insn (gen_add2_insn (data->to_addr,
+				  GEN_INT (-(HOST_WIDE_INT) size)));
+
+      cst = (*data->constfun) (data->constfundata, data->offset, mode);
+      if (!cst)
+	abort ();
 
-      emit_insn ((*genfun) (to1, const0_rtx));
+      emit_insn ((*genfun) (to1, cst));
 
       if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0)
 	emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size)));
--- gcc/builtins.c.jj	Tue Nov 28 10:05:29 2000
+++ gcc/builtins.c	Wed Nov 29 01:05:24 2000
@@ -81,6 +81,8 @@ tree (*lang_type_promotes_to) PARAMS ((t
 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 rtx get_memory_rtx		PARAMS ((tree));
 static int apply_args_size		PARAMS ((void));
 static int apply_result_size		PARAMS ((void));
@@ -106,8 +108,12 @@ static rtx expand_builtin_strcmp	PARAMS 
 						 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_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 expand_builtin_memset	PARAMS ((tree));
@@ -308,6 +314,41 @@ c_getstr (src)
   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);
+}
+
 /* 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).  */
@@ -1685,6 +1726,26 @@ expand_builtin_strpbrk (arglist, target,
     }
 }
 
+/* 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;
+  rtx cst;
+
+  if (offset + GET_MODE_SIZE (mode) > strlen (str) + 1)
+    abort ();  /* Attempt to read past the end of constant string.  */
+
+  cst = c_readstr (str + offset, mode);
+  return LEGITIMATE_CONSTANT_P (cst) ? cst : NULL_RTX;
+}
+
 /* Expand a call to the memcpy builtin, with arguments in ARGLIST.  */
 static rtx
 expand_builtin_memcpy (arglist)
@@ -1706,6 +1767,7 @@ expand_builtin_memcpy (arglist)
       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);
@@ -1717,8 +1779,26 @@ expand_builtin_memcpy (arglist)
 	return 0;
 
       dest_mem = get_memory_rtx (dest);
-      src_mem = get_memory_rtx (src);
       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)
@@ -1774,6 +1854,27 @@ expand_builtin_strcpy (exp)
   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;
+  rtx cst;
+
+  if ((unsigned HOST_WIDE_INT) offset > strlen (str))
+    cst = const0_rtx;
+  else
+    cst = c_readstr (str + offset, mode);
+
+  return LEGITIMATE_CONSTANT_P (cst) ? cst : NULL_RTX;
+}
+
 /* Expand expression EXP, which is a call to the strncpy builtin.  Return 0
    if we failed the caller should emit a normal call.  */
 
@@ -1814,17 +1915,35 @@ expand_builtin_strncpy (arglist, target,
 	  return expand_expr (TREE_VALUE (arglist), target, mode,
 			      EXPAND_NORMAL);
 	}
-      
+
       /* Now, we must be passed a constant src ptr parameter.  */
-      if (slen == 0)
+      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, so in that case punt.  */
+         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))
-	return 0;
+	{
+	  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);
@@ -2081,7 +2200,8 @@ expand_builtin_strcmp (exp, target, mode
     /* 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
@@ -2091,7 +2211,7 @@ expand_builtin_strcmp (exp, target, mode
 
     if (!len || TREE_CODE (len) != INTEGER_CST)
       {
-	if (len2)
+	if (len2 && !TREE_SIDE_EFFECTS (len2))
 	  len = len2;
 	else if (len == 0)
 	  return 0;
@@ -2100,6 +2220,10 @@ expand_builtin_strcmp (exp, target, mode
 	     && 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)
@@ -2760,7 +2884,8 @@ expand_builtin_fputs (arglist, ignore)
 
   /* Get the length of the string passed to fputs.  If the length
      can't be determined, punt.  */
-  if (!(len = c_strlen (TREE_VALUE (arglist))))
+  if (!(len = c_strlen (TREE_VALUE (arglist)))
+      || TREE_CODE (len) != INTEGER_CST)
     return 0;
 
   switch (compare_tree_int (len, 1))
--- gcc/testsuite/gcc.c-torture/execute/string-opt-6.c.jj	Fri Nov 24 15:10:53 2000
+++ gcc/testsuite/gcc.c-torture/execute/string-opt-6.c	Wed Nov 29 01:05:24 2000
@@ -0,0 +1,46 @@
+/* Copyright (C) 2000  Free Software Foundation.
+
+   Ensure builtin memcpy and strcpy perform correctly.
+
+   Written by Jakub Jelinek, 11/24/2000.  */
+
+extern void abort (void);
+extern char *strcpy (char *, const char *);
+typedef __SIZE_TYPE__ size_t;
+extern void *memcpy (void *, const void *, size_t);
+extern int memcmp (const void *, const void *, size_t);
+
+char p[32] = "";
+
+int main()
+{
+  if (strcpy (p, "abcde") != p || memcmp (p, "abcde", 6))
+    abort ();
+  if (strcpy (p + 16, "vwxyz" + 1) != p + 16 || memcmp (p + 16, "wxyz", 5))
+    abort ();
+  if (strcpy (p + 1, "") != p + 1 || memcmp (p, "a\0cde", 6))
+    abort ();  
+  if (strcpy (p + 3, "fghij") != p + 3 || memcmp (p, "a\0cfghij", 9))
+    abort ();
+  if (memcpy (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6))
+    abort ();
+  if (memcpy (p + 16, "VWX" + 1, 2) != p + 16 || memcmp (p + 16, "WXyz", 5))
+    abort ();
+  if (memcpy (p + 1, "", 1) != p + 1 || memcmp (p, "A\0CDE", 6))
+    abort ();  
+  if (memcpy (p + 3, "FGHI", 4) != p + 3 || memcmp (p, "A\0CFGHIj", 9))
+    abort ();
+
+  return 0;
+}
+
+#ifdef __OPTIMIZE__
+/* When optimizing, all the above cases should be transformed into
+   something else.  So any remaining calls to the original function
+   should abort.  */
+static char *
+strcpy (char *d, const char *s)
+{
+  abort ();
+}
+#endif
--- gcc/testsuite/gcc.c-torture/execute/string-opt-5.c.jj	Tue Nov 28 23:32:16 2000
+++ gcc/testsuite/gcc.c-torture/execute/string-opt-5.c	Wed Nov 29 01:05:24 2000
@@ -1,6 +1,7 @@
 /* Copyright (C) 2000  Free Software Foundation.
 
-   Ensure builtin strlen, strcmp, strchr and strrchr perform correctly.
+   Ensure builtin strlen, strcmp, strchr, strrchr and strncpy
+   perform correctly.
 
    Written by Jakub Jelinek, 11/7/2000.  */
 
@@ -9,13 +10,18 @@ extern __SIZE_TYPE__ strlen (const char 
 extern int strcmp (const char *, const char *);
 extern char *strchr (const char *, int);
 extern char *strrchr (const char *, int);
+extern char *strncpy (char *, const char *, __SIZE_TYPE__);
+extern void *memset (void *, int, __SIZE_TYPE__);
+extern int memcmp (const void *, const void *, __SIZE_TYPE__);
 
 int x = 6;
+int y = 1;
 char *bar = "hi world";
 
 int main()
 {
   const char *const foo = "hello world";
+  char dst [64];
 
   if (strlen (bar) != 8)
     abort ();
@@ -53,6 +59,27 @@ int main()
     abort ();
   if (strrchr (bar, 'o') != bar + 4)
     abort ();
+  if (strcmp (foo + (x++ & 1), "ello world" + (--y & 1)))
+    abort ();
+  if (x != 6 || y != 0)
+    abort ();
+  dst[5] = ' ';
+  dst[6] = '\0';
+  x = 5;
+  y = 1;
+  if (strncpy (dst + 1, foo + (x++ & 3), 4) != dst + 1
+      || x != 6
+      || strcmp (dst + 1, "ello "))
+    abort ();
+  memset (dst, ' ', sizeof dst);
+  if (strncpy (dst + (++x & 1), (y++ & 3) + "foo", 10) != dst + 1
+      || x != 7
+      || y != 2
+      || memcmp (dst, " oo\0\0\0\0\0\0\0\0 ", 12))
+    abort ();
+  memset (dst, ' ', sizeof dst);
+  if (strncpy (dst, "hello", 8) != dst || memcmp (dst, "hello\0\0\0 ", 9))
+    abort();
 
   return 0;
 }

	Jakub

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]