aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Sandiford <richard.sandiford@arm.com>2024-08-29 14:00:23 +0100
committerRichard Sandiford <richard.sandiford@arm.com>2024-08-29 14:00:23 +0100
commitac6d433b02ce26a646b2a7254b1d87fcc06b0beb (patch)
tree9398a53d9997c035ca33d2b1f80b197ca083c533
parent00ec6bd805924b2d7d72cf03b200b3b4b7831835 (diff)
downloadgcc-ac6d433b02ce26a646b2a7254b1d87fcc06b0beb.zip
gcc-ac6d433b02ce26a646b2a7254b1d87fcc06b0beb.tar.gz
gcc-ac6d433b02ce26a646b2a7254b1d87fcc06b0beb.tar.bz2
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.
-rw-r--r--gcc/rtlanal.cc28
-rw-r--r--gcc/testsuite/gcc.c-torture/compile/pr116516.c10
2 files changed, 34 insertions, 4 deletions
diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
index 8afbb32..cb0c0c0 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 0000000..c423ebf
--- /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);
+}