[PATCH] Allow memory and string builtins overflowing from one structure field into another one (PR rtl-optimization/23561)

Jakub Jelinek jakub@redhat.com
Fri Aug 26 16:51:00 GMT 2005


Hi!

The attached (convoluted) testcase fails on ppc{32,64}, because the
write into a.a3[0] is swapped during schedule with a.a2[4] write,
as nonoverlapping_memrefs_p says they don't overlap.
Whether this is allowed by ISO C or not is unclear, but both Richard
and Mark agreed in the bugzilla that memory/string builtins indeed should
allow that.
The following patch changes get_memory_rtx to strip off COMPONENT_REFs
(and adjust offset) in MEM_EXPR if it can't prove all memory accesses
derived from that get_memory_rtx will be contained within that field.
So, for:
struct A
{
  char a1[1];
  char a2[5];
  char a3[1];
  char a4[2048 - 7];
} a;
struct B
{
  char a[4];
  struct C
    {
      char d;
      char e[5];
      char h[2];
    } f;
  char g[4];
} b;

memcpy (a.a2, "HELLO", 5);
and
memcpy (b.f.e, "HELLO", 5);
this patch will make zero difference in MEM's attributes, while for
memcpy (a.a2, "HELLO", 6);
it will use a+1 instead of a.a2+0, for
memcpy (b.f.e, "HELLO", 6);
it will use b.f+1 instead of b.f.e+0, for
memcpy (b.f.e, "FOOBARBAZ", 8);
it will use b+5 instead of b.f.e+0.
Bootstrapped/regtested on i386-linux, tested with the testcase on
ppc{32,64}-linux.  Ok for HEAD/4.0?

2005-08-26  Jakub Jelinek  <jakub@redhat.com>

	PR rtl-optimization/23561
	* builtins.c (get_memory_rtx): Add LEN argument.  If MEM_EXPR is
	a COMPONENT_REF, remove all COMPONENT_REF from MEM_EXPR unless
	at most LEN bytes long memory fits into the field.
	(expand_builtin_memcpy, expand_builtin_mempcpy, expand_movstr,
	expand_builtin_strncpy, expand_builtin_memset, expand_builtin_memcmp,
	expand_builtin_strcmp, expand_builtin_strncmp): Adjust callers.

	* gcc.c-torture/execute/20050826-1.c: New test.

--- gcc/builtins.c.jj	2005-08-20 10:24:34.000000000 +0200
+++ gcc/builtins.c	2005-08-26 15:45:15.000000000 +0200
@@ -75,7 +75,7 @@ static int get_pointer_alignment (tree, 
 static const char *c_getstr (tree);
 static rtx c_readstr (const char *, enum machine_mode);
 static int target_char_cast (tree, char *);
-static rtx get_memory_rtx (tree);
+static rtx get_memory_rtx (tree, tree);
 static tree build_string_literal (int, const char *);
 static int apply_args_size (void);
 static int apply_result_size (void);
@@ -1013,10 +1013,12 @@ expand_builtin_prefetch (tree arglist)
 }
 
 /* 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, movmemsi, ..).  */
+   to be used in a string instruction (cmpstrsi, movmemsi, ..).  LEN is
+   the maximum length of the block of memory that might be accessed or
+   NULL if unknown.  */
 
 static rtx
-get_memory_rtx (tree exp)
+get_memory_rtx (tree exp, tree len)
 {
   rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
   rtx mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
@@ -1042,6 +1044,87 @@ get_memory_rtx (tree exp)
   if (exp)
     {
       set_mem_attributes (mem, exp, 0);
+
+      /* Allow the string and memory builtins to overflow from one
+	 field into another, see http://gcc.gnu.org/PR23561.
+	 Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole
+	 memory accessed by the string or memory builtin will fit
+	 within the field.  */
+      if (MEM_EXPR (mem) && TREE_CODE (MEM_EXPR (mem)) == COMPONENT_REF)
+	{
+	  tree mem_expr = MEM_EXPR (mem);
+	  HOST_WIDE_INT offset = -1, length = -1;
+	  tree inner = exp;
+
+	  while (TREE_CODE (inner) == ARRAY_REF
+		 || TREE_CODE (inner) == NOP_EXPR
+		 || TREE_CODE (inner) == CONVERT_EXPR
+		 || TREE_CODE (inner) == NON_LVALUE_EXPR
+		 || TREE_CODE (inner) == VIEW_CONVERT_EXPR
+		 || TREE_CODE (inner) == SAVE_EXPR)
+	    inner = TREE_OPERAND (inner, 0);
+
+	  gcc_assert (TREE_CODE (inner) == COMPONENT_REF);
+
+	  if (MEM_OFFSET (mem)
+	      && GET_CODE (MEM_OFFSET (mem)) == CONST_INT)
+	    offset = INTVAL (MEM_OFFSET (mem));
+
+	  if (offset >= 0 && len && host_integerp (len, 0))
+	    length = tree_low_cst (len, 0);
+
+	  while (TREE_CODE (inner) == COMPONENT_REF)
+	    {
+	      tree field = TREE_OPERAND (inner, 1);
+	      gcc_assert (! DECL_BIT_FIELD (field));
+	      gcc_assert (TREE_CODE (mem_expr) == COMPONENT_REF);
+	      gcc_assert (field == TREE_OPERAND (mem_expr, 1));
+
+	      if (length >= 0
+		  && TYPE_SIZE_UNIT (TREE_TYPE (inner))
+		  && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0))
+		{
+		  HOST_WIDE_INT size
+		    = tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0);
+		  /* If we can prove the memory starting at XEXP (mem, 0)
+		     and ending at XEXP (mem, 0) + LENGTH will fit into
+		     this field, we can keep that COMPONENT_REF in MEM_EXPR.  */
+		  if (offset <= size
+		      && length <= size
+		      && offset + length <= size)
+		    break;
+		}
+
+	      if (offset >= 0
+		  && host_integerp (DECL_FIELD_OFFSET (field), 0))
+		offset += tree_low_cst (DECL_FIELD_OFFSET (field), 0)
+			  + tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1)
+			    / BITS_PER_UNIT;
+	      else
+		{
+		  offset = -1;
+		  length = -1;
+		}
+
+	      mem_expr = TREE_OPERAND (mem_expr, 0);
+	      inner = TREE_OPERAND (inner, 0);
+
+	      while (TREE_CODE (inner) == NOP_EXPR
+		     || TREE_CODE (inner) == CONVERT_EXPR
+		     || TREE_CODE (inner) == NON_LVALUE_EXPR
+		     || TREE_CODE (inner) == VIEW_CONVERT_EXPR
+		     || TREE_CODE (inner) == SAVE_EXPR)
+		inner = TREE_OPERAND (inner, 0);
+	    }
+
+	  if (mem_expr == NULL)
+	    offset = -1;
+	  if (mem_expr != MEM_EXPR (mem))
+	    {
+	      set_mem_expr (mem, mem_expr);
+	      set_mem_offset (mem, offset >= 0 ? GEN_INT (offset) : NULL_RTX);
+	    }
+	}
       set_mem_alias_set (mem, 0);
       set_mem_size (mem, NULL_RTX);
     }
@@ -2808,7 +2891,7 @@ expand_builtin_memcpy (tree exp, rtx tar
       if (src_align == 0)
 	return 0;
 
-      dest_mem = get_memory_rtx (dest);
+      dest_mem = get_memory_rtx (dest, len);
       set_mem_align (dest_mem, dest_align);
       len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
       src_str = c_getstr (src);
@@ -2830,7 +2913,7 @@ expand_builtin_memcpy (tree exp, rtx tar
 	  return dest_mem;
 	}
 
-      src_mem = get_memory_rtx (src);
+      src_mem = get_memory_rtx (src, len);
       set_mem_align (src_mem, src_align);
 
       /* Copy word part most expediently.  */
@@ -2909,7 +2992,7 @@ expand_builtin_mempcpy (tree arglist, tr
 	  && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
 				  (void *) src_str, dest_align))
 	{
-	  dest_mem = get_memory_rtx (dest);
+	  dest_mem = get_memory_rtx (dest, len);
 	  set_mem_align (dest_mem, dest_align);
 	  dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
 				      builtin_memcpy_read_str,
@@ -2923,9 +3006,9 @@ expand_builtin_mempcpy (tree arglist, tr
 	  && can_move_by_pieces (INTVAL (len_rtx),
 				 MIN (dest_align, src_align)))
 	{
-	  dest_mem = get_memory_rtx (dest);
+	  dest_mem = get_memory_rtx (dest, len);
 	  set_mem_align (dest_mem, dest_align);
-	  src_mem = get_memory_rtx (src);
+	  src_mem = get_memory_rtx (src, len);
 	  set_mem_align (src_mem, src_align);
 	  dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx),
 				     MIN (dest_align, src_align), endp);
@@ -3053,8 +3136,8 @@ expand_movstr (tree dest, tree src, rtx 
   if (!HAVE_movstr)
     return 0;
 
-  dest_mem = get_memory_rtx (dest);
-  src_mem = get_memory_rtx (src);
+  dest_mem = get_memory_rtx (dest, NULL);
+  src_mem = get_memory_rtx (src, NULL);
   if (!endp)
     {
       target = force_reg (Pmode, XEXP (dest_mem, 0));
@@ -3260,7 +3343,7 @@ expand_builtin_strncpy (tree exp, rtx ta
 				       (void *) p, dest_align))
 	    return 0;
 
-	  dest_mem = get_memory_rtx (dest);
+	  dest_mem = get_memory_rtx (dest, len);
 	  store_by_pieces (dest_mem, tree_low_cst (len, 1),
 			   builtin_strncpy_read_str,
 			   (void *) p, dest_align, 0);
@@ -3351,7 +3434,7 @@ expand_builtin_memset (tree arglist, rtx
 	}
 
       len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
-      dest_mem = get_memory_rtx (dest);
+      dest_mem = get_memory_rtx (dest, len);
 
       if (TREE_CODE (val) != INTEGER_CST)
 	{
@@ -3502,8 +3585,8 @@ expand_builtin_memcmp (tree exp ATTRIBUT
 	   && REGNO (result) >= FIRST_PSEUDO_REGISTER))
       result = gen_reg_rtx (insn_mode);
 
-    arg1_rtx = get_memory_rtx (arg1);
-    arg2_rtx = get_memory_rtx (arg2);
+    arg1_rtx = get_memory_rtx (arg1, len);
+    arg2_rtx = get_memory_rtx (arg2, len);
     arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
 
     /* Set MEM_SIZE as appropriate.  */
@@ -3596,8 +3679,8 @@ expand_builtin_strcmp (tree exp, rtx tar
       arg1 = builtin_save_expr (arg1);
       arg2 = builtin_save_expr (arg2);
 
-      arg1_rtx = get_memory_rtx (arg1);
-      arg2_rtx = get_memory_rtx (arg2);
+      arg1_rtx = get_memory_rtx (arg1, NULL);
+      arg2_rtx = get_memory_rtx (arg2, NULL);
 
 #ifdef HAVE_cmpstrsi
       /* Try to call cmpstrsi.  */
@@ -3801,8 +3884,8 @@ expand_builtin_strncmp (tree exp, rtx ta
     arg2 = builtin_save_expr (arg2);
     len = builtin_save_expr (len);
 
-    arg1_rtx = get_memory_rtx (arg1);
-    arg2_rtx = get_memory_rtx (arg2);
+    arg1_rtx = get_memory_rtx (arg1, len);
+    arg2_rtx = get_memory_rtx (arg2, len);
     arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
     insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
 			  GEN_INT (MIN (arg1_align, arg2_align)));
--- gcc/testsuite/gcc.c-torture/execute/20050826-1.c.jj	2005-08-26 15:53:51.000000000 +0200
+++ gcc/testsuite/gcc.c-torture/execute/20050826-1.c	2005-08-26 15:53:30.000000000 +0200
@@ -0,0 +1,44 @@
+/* PR rtl-optimization/23561 */
+
+struct A
+{
+  char a1[1];
+  char a2[5];
+  char a3[1];
+  char a4[2048 - 7];
+} a;
+
+typedef __SIZE_TYPE__ size_t;
+extern void *memset (void *, int, size_t);
+extern void *memcpy (void *, const void *, size_t);
+extern int memcmp (const void *, const void *, size_t);
+extern void abort (void);
+
+void
+bar (struct A *x)
+{
+  size_t i;
+  if (memcmp (x, "\1HELLO\1", sizeof "\1HELLO\1"))
+    abort ();
+  for (i = 0; i < sizeof (x->a4); i++)
+    if (x->a4[i])
+      abort ();
+}
+
+int
+foo (void)
+{
+  memset (&a, 0, sizeof (a));
+  a.a1[0] = 1;
+  memcpy (a.a2, "HELLO", sizeof "HELLO");
+  a.a3[0] = 1;
+  bar (&a);
+  return 0;
+}
+
+int
+main (void)
+{
+  foo ();
+  return 0;
+}

	Jakub



More information about the Gcc-patches mailing list