From: Kyrylo Tkachov <[email protected]>
cselib_lookup_mem looks for a value of the mode of X on the addr_list of
the value of the address of X. It accepts a value if one of its
locations is a MEM in the address space of X, but it does not check that
this MEM is at the address of X. A store in another address space can
put a value on the addr_list, and the value can also have a location at
another address in the address space of X. For example, on x86:
static const long tab[4] = { 1, 2, 3, 4 };
long
f (int i, long __seg_gs *p)
{
long *q = (long *) (__UINTPTR_TYPE__) p;
*p = tab[i];
return *q;
}
The %gs store puts the value of TAB[I] on the addr_list of P. The
generic load from Q then finds it through its location in TAB, and
postreload replaces the load with a register. With -O2 -fno-dse
(without -fno-dse, a similar bug in DSE removes the load earlier):
movslq %edi, %rdi
movq tab(,%rdi,8), %rax
movq %rax, %gs:(%rsi)
ret
Require the MEM location to be at the address of X too.
Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.
Ok for trunk?
Thanks,
Kyrill
Cc: Jeff Law <[email protected]>
gcc/ChangeLog:
* cselib.cc (cselib_has_mem_loc_p): New function.
(cselib_lookup_mem): Use it to also check the address of the
memory location.
gcc/testsuite/ChangeLog:
* gcc.target/i386/addr-space-9.c: New test.
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
New in v2. Patch 2/4 uses the same check for its new query. v1 of
that patch had the same problem.
gcc/cselib.cc | 30 ++++++++++++++------
gcc/testsuite/gcc.target/i386/addr-space-9.c | 19 +++++++++++++
2 files changed, 41 insertions(+), 8 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/addr-space-9.c
diff --git a/gcc/cselib.cc b/gcc/cselib.cc
index 3e48fe5dc..5567caa47 100644
--- a/gcc/cselib.cc
+++ b/gcc/cselib.cc
@@ -1765,6 +1765,24 @@ add_mem_for_addr (cselib_val *addr_elt, cselib_val
*mem_elt, rtx x)
}
}
+/* Return true if V has a memory location at address ADDR in address
+ space AS. */
+
+static bool
+cselib_has_mem_loc_p (cselib_val *v, cselib_val *addr, addr_space_t as)
+{
+ for (elt_loc_list *l = v->locs; l; l = l->next)
+ if (MEM_P (l->loc) && MEM_ADDR_SPACE (l->loc) == as)
+ {
+ /* add_mem_for_addr gives each memory location a VALUE address. */
+ rtx loc_addr = XEXP (l->loc, 0);
+ gcc_checking_assert (GET_CODE (loc_addr) == VALUE);
+ if (canonical_cselib_val (CSELIB_VAL_PTR (loc_addr)) == addr)
+ return true;
+ }
+ return false;
+}
+
/* Subroutine of cselib_lookup. Return a value for X, which is a MEM rtx.
If CREATE, make a new one if we haven't seen it before. */
@@ -1793,16 +1811,12 @@ cselib_lookup_mem (rtx x, int create)
addr = canonical_cselib_val (addr);
/* Find a value that describes a value of our mode at that address. */
- addr_space_t as = MEM_ADDR_SPACE (x);
for (elt_list *l = addr->addr_list; l; l = l->next)
- if (GET_MODE (l->elt->val_rtx) == mode)
+ if (GET_MODE (l->elt->val_rtx) == mode
+ && cselib_has_mem_loc_p (l->elt, addr, MEM_ADDR_SPACE (x)))
{
- for (elt_loc_list *l2 = l->elt->locs; l2; l2 = l2->next)
- if (MEM_P (l2->loc) && MEM_ADDR_SPACE (l2->loc) == as)
- {
- promote_debug_loc (l->elt->locs);
- return l->elt;
- }
+ promote_debug_loc (l->elt->locs);
+ return l->elt;
}
if (! create)
diff --git a/gcc/testsuite/gcc.target/i386/addr-space-9.c
b/gcc/testsuite/gcc.target/i386/addr-space-9.c
new file mode 100644
index 000000000..93e93f0b2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/addr-space-9.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-dse" } */
+/* { dg-additional-options "-fno-pic" { target ia32 } } */
+
+/* *P and *Q are different memory locations with the same address. The
+ stored value is also in TAB, a generic memory location. The load from
+ Q must not reuse that value. */
+
+static const long tab[4] = { 1, 2, 3, 4 };
+
+long
+f (int i, long __seg_gs *p)
+{
+ long *q = (long *) (__UINTPTR_TYPE__) p;
+ *p = tab[i];
+ return *q;
+}
+
+/* { dg-final { scan-assembler-times {\tmov[lq]\t\(%[a-z0-9]+\), %} 1 } } */
--
2.43.0