This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] Fix the one entry mem{{,p}cpy,move,set} optimization aliasing issues (PR middle-end/29272)
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Eric Botcazou <ebotcazou at adacore dot com>
- Cc: gcc-patches at gcc dot gnu dot org, Mark Mitchell <mark at codesourcery dot com>, "Joseph S. Myers" <joseph at codesourcery dot com>, Ian Lance Taylor <ian at airs dot com>
- Date: Mon, 9 Oct 2006 11:32:37 -0400
- Subject: Re: [PATCH] Fix the one entry mem{{,p}cpy,move,set} optimization aliasing issues (PR middle-end/29272)
- References: <20060929075035.GQ20982@devserv.devel.redhat.com> <4529E262.2050505@codesourcery.com> <20061009073625.GO20982@devserv.devel.redhat.com> <200610091301.28519.ebotcazou@adacore.com>
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
On Mon, Oct 09, 2006 at 01:01:28PM +0200, Eric Botcazou wrote:
> > but I don't see what e.g. prevents the optimization
> > to be done when we have
> > p = &q->t;
> > ...
> > p0 = (ref_all) p; /* These two stmts come from __builtin_memcpy folding.
> > p0->s = x;
> > and later on some other pass notices that p is &q->t and optimizes
> > that into q->t.s = x;
>
> Where's the problem here? You need a latent aliasing violation in the code.
I was afraid that for something like:
extern void abort (void);
struct S { struct S *s; } s;
struct T;
struct TI { struct T *t; };
struct T { struct TI ti; } t;
static inline void
foo (void *s)
{
struct T *p = s;
struct TI *q = &p->ti;
__builtin_memcpy (&q->t, &t.ti.t, sizeof (t.ti.t));
}
void *
__attribute__((noinline))
bar (void *p, struct S *q)
{
q->s = &s;
foo (p);
return q->s;
}
int
main (void)
{
t.ti.t = &t;
if (bar (&s, &s) != (void *) &t)
abort ();
return 0;
}
where fold_builtin_memory_op only sees &q->t and not &p->ti.t
the ref_all cast would be removed in some folding, but I haven't
been able to reproduce something like that, apparently on
((struct TI * {ref-all}) &((struct T *) p)->ti)->t = t.ti.t;
the ref-all cast wasn't folded out. So, if you think this is
100% safe, then something like the following (as fold_builtin
is called even before gimplification, I think we can't modify
the INDIRECT_REF's operand in-place) should work (certainly
works on the testcase):
2006-10-09 Jakub Jelinek <jakub@redhat.com>
Eric Botcazou <ebotcazou@adacore.com>
PR middle-end/29272
* builtins.c (adjust_memop_var_decl): New function.
(fold_builtin_memset, fold_builtin_memory_op): Use it.
* gcc.c-torture/execute/20060930-2.c: New test.
--- gcc/builtins.c.jj 2006-10-09 15:45:53.000000000 +0200
+++ gcc/builtins.c 2006-10-09 17:04:55.000000000 +0200
@@ -7893,6 +7893,43 @@ fold_builtin_exponent (tree fndecl, tree
return 0;
}
+/* If VAR is a VAR_DECL or component thereof, just return VAR.
+ If VAR is an INDIRECT_REF, return a copy of VAR's tree in which
+ the INDIRECT_REF is through a TYPE_REF_CAN_ALIAS_ALL pointer.
+ Otherwise return 0. */
+
+static tree
+adjust_memop_var_decl (tree var)
+{
+ tree inner = var;
+ while (handled_component_p (inner))
+ inner = TREE_OPERAND (inner, 0);
+ if (TREE_CODE (inner) == INDIRECT_REF)
+ {
+ tree ref_all, *ptr;
+
+ if (TYPE_REF_CAN_ALIAS_ALL (TREE_TYPE (TREE_OPERAND (inner, 0))))
+ return var;
+
+ ref_all
+ = build_pointer_type_for_mode (TREE_TYPE (inner), ptr_mode, true);
+ ptr = &var;
+ while (handled_component_p (*ptr))
+ {
+ *ptr = copy_node (*ptr);
+ ptr = &TREE_OPERAND (*ptr, 0);
+ }
+ *ptr = copy_node (*ptr);
+ TREE_OPERAND (*ptr, 0)
+ = fold_convert (ref_all, TREE_OPERAND (inner, 0));
+ return var;
+ }
+
+ if (SSA_VAR_P (inner))
+ return var;
+ return 0;
+}
+
/* Fold function call to builtin memset. Return
NULL_TREE if no simplification can be made. */
@@ -7933,6 +7970,10 @@ fold_builtin_memset (tree arglist, tree
&& !POINTER_TYPE_P (TREE_TYPE (var)))
return 0;
+ var = adjust_memop_var_decl (var);
+ if (! var)
+ return 0;
+
length = tree_low_cst (len, 1);
if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (var))) != length
|| get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT
@@ -8044,6 +8085,10 @@ fold_builtin_memory_op (tree arglist, tr
&& !SCALAR_FLOAT_TYPE_P (TREE_TYPE (destvar)))
return 0;
+ destvar = adjust_memop_var_decl (destvar);
+ if (! destvar)
+ return 0;
+
srcvar = src;
STRIP_NOPS (srcvar);
if (TREE_CODE (srcvar) != ADDR_EXPR)
@@ -8058,6 +8103,10 @@ fold_builtin_memory_op (tree arglist, tr
&& !SCALAR_FLOAT_TYPE_P (TREE_TYPE (srcvar)))
return 0;
+ srcvar = adjust_memop_var_decl (srcvar);
+ if (! srcvar)
+ return 0;
+
length = tree_low_cst (len, 1);
if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (destvar))) != length
|| get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT
--- gcc/testsuite/gcc.c-torture/execute/20060930-2.c.jj 2006-10-09 15:54:31.000000000 +0200
+++ gcc/testsuite/gcc.c-torture/execute/20060930-2.c 2006-10-09 15:54:40.000000000 +0200
@@ -0,0 +1,31 @@
+/* PR middle-end/29272 */
+
+extern void abort (void);
+
+struct S { struct S *s; } s;
+struct T { struct T *t; } t;
+
+static inline void
+foo (void *s)
+{
+ struct T *p = s;
+ __builtin_memcpy (&p->t, &t.t, sizeof (t.t));
+}
+
+void *
+__attribute__((noinline))
+bar (void *p, struct S *q)
+{
+ q->s = &s;
+ foo (p);
+ return q->s;
+}
+
+int
+main (void)
+{
+ t.t = &t;
+ if (bar (&s, &s) != (void *) &t)
+ abort ();
+ return 0;
+}
Jakub