[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