From: Stefan Schulze Frielinghaus <[email protected]>
Treat a hard register constraint the same way as a constraint associated
a single register class for process_single_reg_class_operands().
---
This patch makes gcc.dg/asm-hard-reg-6.c fail on s390 where we have for
function test_reg_reg:
(insn 10 4 11 2 (set (reg:DI 62 [ x+-4 ])
(reg:DI 2 %r2 [ x+-4 ])) "asm-hard-reg-6.c":54:1 1909 {*movdi_64}
(expr_list:REG_DEAD (reg:DI 2 %r2 [ x+-4 ])
(nil)))
(insn 11 10 7 2 (set (reg/f:DI 63 [ y ])
(reg:DI 3 %r3 [ y ])) "asm-hard-reg-6.c":54:1 1909 {*movdi_64}
(expr_list:REG_DEAD (reg:DI 3 %r3 [ y ])
(nil)))
(insn 7 11 12 2 (asm_operands/v ("foo %0,%1") ("") 0 [
(subreg/s/u:SI (reg:DI 62 [ x+-4 ]) 4)
(reg/f:DI 63 [ y ])
]
[
(asm_input:SI ("{r0}m,{r1}") asm-hard-reg-6.c:55)
(asm_input:DI ("{r2},m") asm-hard-reg-6.c:55)
]
[] asm-hard-reg-6.c:55) "asm-hard-reg-6.c":55:3 -1
(expr_list:REG_DEAD (reg/f:DI 63 [ y ])
(expr_list:REG_DEAD (reg:DI 62 [ x+-4 ])
(nil))))
Previously, during IRA we assigned to pseudo r62 hard register r2 and to pseudo
r63 hard register r3 which we derived from the hard register assignments as
preferred registers. During LRA we then decide to go for alternative 0 due to
costing:
Considering alt=0 of insn 7: (0) {r0}m (1) {r2}
0 Operand reload: losers++
1 Operand reload: losers++
overall=12,losers=2,rld_nregs=2
Considering alt=1 of insn 7: (0) {r1} (1) m
0 Operand reload: losers++
1 Operand reload: losers++
1 Spill pseudo into memory: reject+=3
Using memory insn operand 1: reject+=3
overall=18,losers=2 -- refuse
Choosing alt 0 in insn 7: (0) {r0}m (1) {r2}
Note, in both alternatives we require two reloads. This changes with this
patch where we assign during IRA to pseudo r62 hard register r1 and to pseudo
as before hard register r3. We chose for pseudo r62 hard register r1 since
during process_single_reg_class_operands we have preferred alternative 0 only
which in turn means that we assume for pseudo r63 a single reg class operand,
now, which in turn renders r2 a conflicting hard register for pseudo r62:
;; a0(r62,l0) conflicts: a1(r63,l0)
;; total conflict hard regs: 2-3 14
;; conflict hard regs: 2-3 14
;; a1(r63,l0) conflicts: a0(r62,l0)
;; total conflict hard regs: 14
;; conflict hard regs: 14
Previously we had r2 non-conflicting for r62:
;; a0(r62,l0) conflicts: a1(r63,l0)
;; total conflict hard regs: 3 14
;; conflict hard regs: 3 14
;; a1(r63,l0) conflicts: a0(r62,l0)
;; total conflict hard regs: 14
;; conflict hard regs: 14
So now we are lucky (or unlucky depending on how you want to approach this) and
choose r1 for r62 which is the correct hard register for alternative 1 and thus
we only need one reload for this alternative which means in the end both
alternatives have the same cost:
Considering alt=0 of insn 7: (0) {r0}m (1) {r2}
0 Operand reload: losers++
1 Operand reload: losers++
overall=12,losers=2,rld_nregs=2
Considering alt=1 of insn 7: (0) {r1} (1) m
1 Operand reload: losers++
1 Spill pseudo into memory: reject+=3
Using memory insn operand 1: reject+=3
overall=12,losers=1,rld_nregs=0
Choosing alt 1 in insn 7: (0) {r1} (1) m
which is why we choose a different alternative, now, rendering the test
failing. I'm inclined to restore the previous behaviour where we had two
reloads in both cases by changing GPR3 to r4 for the test.
Independent of the test case engineering part I think this patch moves hard
register constraints to the right direction and aligning with single register
class constraints is correct.
Bootstrapped and regtested on
- aarch64-unknown-linux-gnu
- powerpc64le-unknown-linux-gnu
- s390x-ibm-linux-gnu
- x86_64-pc-linux-gnu
Ok for mainline?
gcc/ira-lives.cc | 77 +++++++++++--------
gcc/testsuite/gcc.dg/asm-hard-reg-6.c | 4 +-
.../gcc.target/s390/asm-hard-reg-1.c | 4 +-
3 files changed, 50 insertions(+), 35 deletions(-)
diff --git a/gcc/ira-lives.cc b/gcc/ira-lives.cc
index 9103b3e4ff2..fb1ddf23be6 100644
--- a/gcc/ira-lives.cc
+++ b/gcc/ira-lives.cc
@@ -846,16 +846,16 @@ mark_hard_reg_early_clobbers (rtx_insn *insn, bool live_p)
}
/* Checks that CONSTRAINTS permits to use only one hard register. If
- it is so, the function returns the class of the hard register.
- Otherwise it returns NO_REGS. */
-static enum reg_class
+ it is so, the function returns the register. Otherwise it returns -1. */
+static int
single_reg_class (const char *constraints, rtx op, rtx equiv_const)
{
int c;
- enum reg_class cl, next_cl;
+ int regno, next_regno;
+ enum reg_class next_cl;
enum constraint_num cn;
- cl = NO_REGS;
+ regno = -1;
alternative_mask preferred = preferred_alternatives;
while ((c = *constraints))
{
@@ -867,7 +867,14 @@ single_reg_class (const char *constraints, rtx op, rtx
equiv_const)
switch (c)
{
case 'g':
- return NO_REGS;
+ return -1;
+
+ case '{':
+ next_regno = decode_hard_reg_constraint (constraints);
+ if (regno >= 0 && regno != next_regno)
+ return -1;
+ regno = next_regno;
+ break;
default:
/* ??? Is this the best way to handle memory constraints? */
@@ -876,21 +883,21 @@ single_reg_class (const char *constraints, rtx op, rtx
equiv_const)
|| insn_extra_special_memory_constraint (cn)
|| insn_extra_relaxed_memory_constraint (cn)
|| insn_extra_address_constraint (cn))
- return NO_REGS;
+ return -1;
if (constraint_satisfied_p (op, cn)
|| (equiv_const != NULL_RTX
&& CONSTANT_P (equiv_const)
&& constraint_satisfied_p (equiv_const, cn)))
- return NO_REGS;
+ return -1;
next_cl = reg_class_for_constraint (cn);
if (next_cl == NO_REGS)
break;
- if (cl == NO_REGS
- ? ira_class_singleton[next_cl][GET_MODE (op)] < 0
- : (ira_class_singleton[cl][GET_MODE (op)]
- != ira_class_singleton[next_cl][GET_MODE (op)]))
- return NO_REGS;
- cl = next_cl;
+ next_regno = ira_class_singleton[next_cl][GET_MODE (op)];
+ if (regno < 0
+ ? next_regno < 0
+ : regno != next_regno)
+ return -1;
+ regno = next_regno;
break;
case '0': case '1': case '2': case '3': case '4':
@@ -899,31 +906,30 @@ single_reg_class (const char *constraints, rtx op, rtx
equiv_const)
char *end;
unsigned long dup = strtoul (constraints, &end, 10);
constraints = end;
- next_cl
+ next_regno
= single_reg_class (recog_data.constraints[dup],
recog_data.operand[dup], NULL_RTX);
- if (cl == NO_REGS
- ? ira_class_singleton[next_cl][GET_MODE (op)] < 0
- : (ira_class_singleton[cl][GET_MODE (op)]
- != ira_class_singleton[next_cl][GET_MODE (op)]))
- return NO_REGS;
- cl = next_cl;
+ if (regno < 0
+ ? next_regno < 0
+ : regno != next_regno)
+ return -1;
+ regno = next_regno;
continue;
}
}
constraints += CONSTRAINT_LEN (c, constraints);
}
- return cl;
+ return regno;
}
/* The function checks that operand OP_NUM of the current insn can use
only one hard register. If it is so, the function returns the
- class of the hard register. Otherwise it returns NO_REGS. */
-static enum reg_class
+ hard register. Otherwise it returns -1. */
+static int
single_reg_operand_class (int op_num)
{
if (op_num < 0 || recog_data.n_alternatives == 0)
- return NO_REGS;
+ return -1;
return single_reg_class (recog_data.constraints[op_num],
recog_data.operand[op_num], NULL_RTX);
}
@@ -991,7 +997,7 @@ ira_implicitly_set_insn_hard_regs (HARD_REG_SET *set,
static void
process_single_reg_class_operands (bool in_p, int freq)
{
- int i, regno;
+ int i, regno, hard_regno;
unsigned int px;
enum reg_class cl;
rtx operand;
@@ -1006,8 +1012,8 @@ process_single_reg_class_operands (bool in_p, int freq)
if (! in_p && recog_data.operand_type[i] != OP_OUT
&& recog_data.operand_type[i] != OP_INOUT)
continue;
- cl = single_reg_operand_class (i);
- if (cl == NO_REGS)
+ hard_regno = single_reg_operand_class (i);
+ if (hard_regno < 0)
continue;
operand_a = NULL;
@@ -1020,6 +1026,11 @@ process_single_reg_class_operands (bool in_p, int freq)
{
enum reg_class aclass;
+ cl = NO_REGS;
+ int nregs = hard_regno_nregs (hard_regno, recog_data.operand_mode[i]);
+ for (int j = 0; j < nregs; ++j)
+ cl = reg_class_superunion[cl][REGNO_REG_CLASS (hard_regno + j)];
+
operand_a = ira_curr_regno_allocno_map[regno];
aclass = ALLOCNO_CLASS (operand_a);
if (ira_class_subset_p[cl][aclass])
@@ -1036,7 +1047,7 @@ process_single_reg_class_operands (bool in_p, int freq)
poly_int64 offset;
xmode = recog_data.operand_mode[i];
- xregno = ira_class_singleton[cl][xmode];
+ xregno = hard_regno;
gcc_assert (xregno >= 0);
ymode = ALLOCNO_MODE (operand_a);
offset = subreg_lowpart_offset (ymode, xmode);
@@ -1059,6 +1070,10 @@ process_single_reg_class_operands (bool in_p, int freq)
}
}
+ HARD_REG_SET hard_regs;
+ CLEAR_HARD_REG_SET (hard_regs);
+ add_to_hard_reg_set (&hard_regs, recog_data.operand_mode[i], hard_regno);
+
EXECUTE_IF_SET_IN_SPARSESET (objects_live, px)
{
ira_object_t obj = ira_object_id_map[px];
@@ -1068,8 +1083,8 @@ process_single_reg_class_operands (bool in_p, int freq)
/* We could increase costs of A instead of making it
conflicting with the hard register. But it works worse
because it will be spilled in reload in anyway. */
- OBJECT_CONFLICT_HARD_REGS (obj) |= reg_class_contents[cl];
- OBJECT_TOTAL_CONFLICT_HARD_REGS (obj) |= reg_class_contents[cl];
+ OBJECT_CONFLICT_HARD_REGS (obj) |= hard_regs;
+ OBJECT_TOTAL_CONFLICT_HARD_REGS (obj) |= hard_regs;
}
}
}
diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-6.c
b/gcc/testsuite/gcc.dg/asm-hard-reg-6.c
index c87a8116a86..b42b4d2c198 100644
--- a/gcc/testsuite/gcc.dg/asm-hard-reg-6.c
+++ b/gcc/testsuite/gcc.dg/asm-hard-reg-6.c
@@ -36,8 +36,8 @@
#elif defined (__s390__)
# define GPR1 "{r0}"
# define GPR2 "{r1}"
-# define GPR3 "{r2}"
-/* { dg-final { scan-assembler-times "foo\t%r0,%r2" 1 { target { s390*-*-* } }
} } */
+# define GPR3 "{r4}"
+/* { dg-final { scan-assembler-times "foo\t%r0,%r4" 1 { target { s390*-*-* } }
} } */
/* { dg-final { scan-assembler-times "bar\t%r1,0\\(%r3\\)" 1 { target {
s390*-*-* } } } } */
#elif defined (__x86_64__)
# define GPR1 "{eax}"
diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c
b/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c
index 671c0ede6ef..53ac46eb40c 100644
--- a/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c
+++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c
@@ -75,8 +75,8 @@ test_out_1 (void)
** test_out_2:
** lgr (%r[0-9]+),%r2
** foo %r2
-** ark (%r[0-9]+),\1,%r2
-** lgfr %r2,\2
+** ar %r2,\1
+** lgfr %r2,%r2
** br %r14
*/
--
2.55.0