aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir N. Makarov <vmakarov@redhat.com>2021-01-12 11:26:15 -0500
committerVladimir N. Makarov <vmakarov@redhat.com>2021-01-12 11:27:29 -0500
commitcf2ac1c30af0fa783c8d72e527904dda5d8cc330 (patch)
treea4bc0604739c4732d2e9770af00f7eabcde03d2a
parente0bec6ceac47752616dd9fe0801344ed45db2fd3 (diff)
downloadgcc-cf2ac1c30af0fa783c8d72e527904dda5d8cc330.zip
gcc-cf2ac1c30af0fa783c8d72e527904dda5d8cc330.tar.gz
gcc-cf2ac1c30af0fa783c8d72e527904dda5d8cc330.tar.bz2
[PR97969] LRA: Transform pattern `plus (plus (hard reg, const), pseudo)` after elimination
LRA can loop infinitely on targets without `reg + imm` insns. Register elimination on such targets can increase register pressure resulting in permanent stack size increase and changing elimination offset. To avoid such situation, a simple transformation can be done to avoid register pressure increase after generating reload insns containing eliminated hard regs. gcc/ChangeLog: PR target/97969 * lra-eliminations.c (eliminate_regs_in_insn): Add transformation of pattern 'plus (plus (hard reg, const), pseudo)'. gcc/testsuite/ChangeLog: PR target/97969 * gcc.target/arm/pr97969.c: New.
-rw-r--r--gcc/lra-eliminations.c28
-rw-r--r--gcc/testsuite/gcc.target/arm/pr97969.c54
2 files changed, 81 insertions, 1 deletions
diff --git a/gcc/lra-eliminations.c b/gcc/lra-eliminations.c
index b28f3c4..ebcadd1 100644
--- a/gcc/lra-eliminations.c
+++ b/gcc/lra-eliminations.c
@@ -885,7 +885,7 @@ eliminate_regs_in_insn (rtx_insn *insn, bool replace_p, bool first_p,
poly_int64 update_sp_offset)
{
int icode = recog_memoized (insn);
- rtx old_set = single_set (insn);
+ rtx set, old_set = single_set (insn);
bool validate_p;
int i;
rtx substed_operand[MAX_RECOG_OPERANDS];
@@ -1038,6 +1038,32 @@ eliminate_regs_in_insn (rtx_insn *insn, bool replace_p, bool first_p,
for (i = 0; i < static_id->n_dups; i++)
*id->dup_loc[i] = substed_operand[(int) static_id->dup_num[i]];
+ /* Transform plus (plus (hard reg, const), pseudo) to plus (plus (pseudo,
+ const), hard reg) in order to keep insn containing eliminated register
+ after all reloads calculating its offset. This permits to keep register
+ pressure under control and helps to avoid LRA cycling in patalogical
+ cases. */
+ if (! replace_p && (set = single_set (insn)) != NULL
+ && GET_CODE (SET_SRC (set)) == PLUS
+ && GET_CODE (XEXP (SET_SRC (set), 0)) == PLUS)
+ {
+ rtx reg1, reg2, op1, op2;
+
+ reg1 = op1 = XEXP (XEXP (SET_SRC (set), 0), 0);
+ reg2 = op2 = XEXP (SET_SRC (set), 1);
+ if (GET_CODE (reg1) == SUBREG)
+ reg1 = SUBREG_REG (reg1);
+ if (GET_CODE (reg2) == SUBREG)
+ reg2 = SUBREG_REG (reg2);
+ if (REG_P (reg1) && REG_P (reg2)
+ && REGNO (reg1) < FIRST_PSEUDO_REGISTER
+ && REGNO (reg2) >= FIRST_PSEUDO_REGISTER)
+ {
+ XEXP (XEXP (SET_SRC (set), 0), 0) = op2;
+ XEXP (SET_SRC (set), 1) = op1;
+ }
+ }
+
/* If we had a move insn but now we don't, re-recognize it.
This will cause spurious re-recognition if the old move had a
PARALLEL since the new one still will, but we can't call
diff --git a/gcc/testsuite/gcc.target/arm/pr97969.c b/gcc/testsuite/gcc.target/arm/pr97969.c
new file mode 100644
index 0000000..714a1d1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr97969.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c99 -fno-omit-frame-pointer -mthumb -w -Os" } */
+
+typedef a[23];
+enum { b };
+typedef struct {
+ int c;
+ char *e;
+ char f
+} d;
+typedef enum { g = 1 } h;
+typedef struct {
+ h i;
+ int j
+} k;
+typedef struct {
+ a l;
+ int a;
+ int m;
+ int n;
+ int o;
+ short p;
+ int q;
+ k r;
+ char e;
+ char *s;
+ d t;
+ d *u;
+ short v;
+ int w
+} aa;
+c(char x, int y, char z, int ab) {
+ aa ac;
+ ac.r.i = 0;
+ d ad;
+ ac.t = ad;
+ ac.u = 0;
+ ae(&ac.v, 0, 0);
+ ac.w = 0;
+ af(&ac, x + y, z, z + ab);
+ if (ag(0))
+ return 0;
+ if (x)
+ ac.s = z + ab;
+ else
+ ac.s = x + y;
+ ac.o |= g;
+ if (!setjmp()) {
+ ah(ac);
+ ai(b);
+ ac.e = z + ab;
+ aj(ac);
+ }
+}