https://gcc.gnu.org/g:ac6d433b02ce26a646b2a7254b1d87fcc06b0beb

commit r15-3288-gac6d433b02ce26a646b2a7254b1d87fcc06b0beb
Author: Richard Sandiford <richard.sandif...@arm.com>
Date:   Thu Aug 29 14:00:23 2024 +0100

    Allow subregs around constant displacements [PR116516]
    
    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.
    
    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.

Diff:
---
 gcc/rtlanal.cc                                 | 28 ++++++++++++++++++++++----
 gcc/testsuite/gcc.c-torture/compile/pr116516.c | 10 +++++++++
 2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
index 8afbb32f2206..cb0c0c0d7197 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 000000000000..c423ebfef5c0
--- /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);
+}

Reply via email to