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

Reply via email to