]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Allow subregs around constant displacements [PR116516]
authorRichard Sandiford <richard.sandiford@arm.com>
Thu, 29 Aug 2024 13:00:23 +0000 (14:00 +0100)
committerRichard Sandiford <richard.sandiford@arm.com>
Thu, 29 Aug 2024 13:00:23 +0000 (14:00 +0100)
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.

gcc/rtlanal.cc
gcc/testsuite/gcc.c-torture/compile/pr116516.c [new file with mode: 0644]

index 8afbb32f2206096f094e92e9f7a615804a568b8e..cb0c0c0d71977ff833083ce3eee192990ef3f7ef 100644 (file)
@@ -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 (file)
index 0000000..c423ebf
--- /dev/null
@@ -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);
+}