This patch fixes a regression introduced by g:708ee71808ea61758e73.
x86_64 allows addresses of the form:

  (zero_extend:DI (subreg:SI (symbol_ref:DI "foo") 0))

Before the previous patch, a lax SUBREG check meant that we would
treat the subreg as a base and reload it into a base register.
But that wasn't what the target was expecting.  Instead we should
treat "foo" as a constant displacement, to match:

        leal foo, <dest>

After the patch, we recognised that "foo" isn't a base register,
but ICEd on it rather than handling it as a displacement.

With or without the recent patches, if the address had instead been:

  (zero_extend:DI
    (subreg:SI (plus:DI (reg:DI R) (symbol_ref:DI "foo") 0)))

then we would have treated "foo" as the displacement and R as the base
or index, as expected.  The problem was that the code that does this was
rejecting all subregs of objects, rather than just subregs of variable
objects.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

Richard


gcc/
        PR middle-end/116516
        * rtlanal.cc (strip_address_mutations): Allow subregs around
        constant displacements.

gcc/testsuite/
        PR middle-end/116516
        * gcc.c-torture/compile/pr116516.c: New test.
---
 gcc/rtlanal.cc                                | 28 ++++++++++++++++---
 .../gcc.c-torture/compile/pr116516.c          | 10 +++++++
 2 files changed, 34 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.c-torture/compile/pr116516.c

diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
index 8afbb32f220..cb0c0c0d719 100644
--- a/gcc/rtlanal.cc
+++ b/gcc/rtlanal.cc
@@ -6467,10 +6467,30 @@ strip_address_mutations (rtx *loc, enum rtx_code 
*outer_code)
        /* (and ... (const_int -X)) is used to align to X bytes.  */
        loc = &XEXP (*loc, 0);
       else if (code == SUBREG
-               && !OBJECT_P (SUBREG_REG (*loc))
-               && subreg_lowpart_p (*loc))
-       /* (subreg (operator ...) ...) inside and is used for mode
-          conversion too.  */
+              && (!OBJECT_P (SUBREG_REG (*loc))
+                  || CONSTANT_P (SUBREG_REG (*loc)))
+              && subreg_lowpart_p (*loc))
+       /* (subreg (operator ...) ...) inside AND is used for mode
+          conversion too.  It is also used for load-address operations
+          in which an extension can be done for free, such as:
+
+            (zero_extend:DI
+              (subreg:SI (plus:DI (reg:DI R) (symbol_ref:DI "foo") 0)))
+
+          The latter usage also covers subregs of plain "displacements",
+          such as:
+
+            (zero_extend:DI (subreg:SI (symbol_ref:DI "foo") 0))
+
+          The inner address should then be the symbol_ref, not the subreg,
+          similarly to the plus case above.
+
+          In contrast, the subreg in:
+
+            (zero_extend:DI (subreg:SI (reg:DI R) 0))
+
+          should be treated as the base, since it should be replaced by
+          an SImode hard register during register allocation.  */
        loc = &SUBREG_REG (*loc);
       else
        return loc;
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr116516.c 
b/gcc/testsuite/gcc.c-torture/compile/pr116516.c
new file mode 100644
index 00000000000..c423ebfef5c
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr116516.c
@@ -0,0 +1,10 @@
+extern void my_func (int);
+typedef struct {
+  int var;
+} info_t;
+extern void *_data_offs;
+void test()
+{
+  info_t *info = (info_t *) ((void *)((void *)1) + ((unsigned 
int)&_data_offs));
+  my_func(info->var == 0);
+}
-- 
2.25.1

Reply via email to