LRA: Always do elimination and only for hard register to check insn constraints
authorVladimir N. Makarov <vmakarov@redhat.com>
Tue, 24 Jan 2023 21:10:59 +0000 (16:10 -0500)
committerVladimir N. Makarov <vmakarov@redhat.com>
Tue, 24 Jan 2023 21:13:36 +0000 (16:13 -0500)
LRA does elimination but not always checks insn constraints in this case.
This results in LRA failure for PDP11 target whose addition is only 2-op insn.
The same might happen for other analogous targets.  The patch fixes this problem.

        PR rtl-optimization/108388

gcc/ChangeLog:

* lra-constraints.cc (get_hard_regno): Remove final_p arg.  Always
do elimination but only for hard register.
(operands_match_p, uses_hard_regs_p, process_alt_operands): Adjust
calls of get_hard_regno.

gcc/testsuite/ChangeLog:

* gcc.target/pdp11/pdp11.exp: New.
* gcc.target/pdp11/pr108388.c: New.

gcc/lra-constraints.cc
gcc/testsuite/gcc.target/pdp11/pdp11.exp [new file with mode: 0644]
gcc/testsuite/gcc.target/pdp11/pr108388.c [new file with mode: 0644]

index b0b3c5b..7bffbc0 100644 (file)
@@ -184,12 +184,12 @@ get_try_hard_regno (int regno)
   return ira_class_hard_regs[rclass][0];
 }
 
-/* Return the hard regno of X after removing its subreg.  If X is not
-   a register or a subreg of a register, return -1.  If X is a pseudo,
-   use its assignment.  If FINAL_P return the final hard regno which will
-   be after elimination.  */
+/* Return the hard regno of X after removing its subreg.  If X is not a
+   register or a subreg of a register, return -1.  If X is a pseudo, use its
+   assignment.  If X is a hard regno, return the final hard regno which will be
+   after elimination.  */
 static int
-get_hard_regno (rtx x, bool final_p)
+get_hard_regno (rtx x)
 {
   rtx reg;
   int hard_regno;
@@ -203,7 +203,7 @@ get_hard_regno (rtx x, bool final_p)
     hard_regno = lra_get_regno_hard_regno (hard_regno);
   if (hard_regno < 0)
     return -1;
-  if (final_p)
+  if (HARD_REGISTER_NUM_P (REGNO (reg)))
     hard_regno = lra_get_elimination_hard_regno (hard_regno);
   if (SUBREG_P (x))
     hard_regno += subreg_regno_offset (hard_regno, GET_MODE (reg),
@@ -782,7 +782,7 @@ operands_match_p (rtx x, rtx y, int y_hard_regno)
     {
       int j;
 
-      i = get_hard_regno (x, false);
+      i = get_hard_regno (x);
       if (i < 0)
        goto slow;
 
@@ -1920,7 +1920,7 @@ uses_hard_regs_p (rtx x, HARD_REG_SET set)
 
   if (REG_P (x) || SUBREG_P (x))
     {
-      x_hard_regno = get_hard_regno (x, true);
+      x_hard_regno = get_hard_regno (x);
       return (x_hard_regno >= 0
              && overlaps_hard_reg_set_p (set, mode, x_hard_regno));
     }
@@ -2078,7 +2078,7 @@ process_alt_operands (int only_alternative)
 
       op = no_subreg_reg_operand[nop] = *curr_id->operand_loc[nop];
       /* The real hard regno of the operand after the allocation.  */
-      hard_regno[nop] = get_hard_regno (op, true);
+      hard_regno[nop] = get_hard_regno (op);
 
       operand_reg[nop] = reg = op;
       biggest_mode[nop] = GET_MODE (op);
@@ -2258,7 +2258,7 @@ process_alt_operands (int only_alternative)
                        && curr_operand_mode[m] != curr_operand_mode[nop])
                      break;
                    
-                   m_hregno = get_hard_regno (*curr_id->operand_loc[m], false);
+                   m_hregno = get_hard_regno (*curr_id->operand_loc[m]);
                    /* We are supposed to match a previous operand.
                       If we do, we win if that one did.  If we do
                       not, count both of the operands as losers.
diff --git a/gcc/testsuite/gcc.target/pdp11/pdp11.exp b/gcc/testsuite/gcc.target/pdp11/pdp11.exp
new file mode 100644 (file)
index 0000000..89b1f25
--- /dev/null
@@ -0,0 +1,41 @@
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Exit immediately if this isn't an pdp11 target.
+if ![istarget pdp11*-*-*] then {
+  return
+}
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+    set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+       "" $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/gcc.target/pdp11/pr108388.c b/gcc/testsuite/gcc.target/pdp11/pr108388.c
new file mode 100644 (file)
index 0000000..0d54b91
--- /dev/null
@@ -0,0 +1,90 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mlra" } */
+
+typedef int SItype __attribute__ ((mode (SI)));
+typedef unsigned int USItype __attribute__ ((mode (SI)));
+typedef int DItype __attribute__ ((mode (DI)));
+typedef unsigned int UDItype __attribute__ ((mode (DI)));
+extern DItype __mulvdi3 (DItype, DItype);
+struct DWstruct {SItype high, low;};
+
+typedef union {
+  struct DWstruct s;
+  DItype ll;
+} DWunion;
+
+DItype __mulvdi3 (DItype u, DItype v) {
+  const DWunion uu = {.ll = u};
+  const DWunion vv = {.ll = v};
+  
+  if (__builtin_expect (uu.s.high == uu.s.low >> ((4 * 8) - 1), 1)) {
+    if (__builtin_expect (vv.s.high == vv.s.low >> ((4 * 8) - 1), 1)) {
+      return (DItype) uu.s.low * (DItype) vv.s.low;
+    } else {
+      DWunion w0 = {.ll = (UDItype) (USItype) uu.s.low * (UDItype) (USItype) vv.s.low};
+      DWunion w1 = {.ll = (UDItype) (USItype) uu.s.low * (UDItype) (USItype) vv.s.high};
+      
+      if (vv.s.high < 0)
+       w1.s.high -= uu.s.low;
+      if (uu.s.low < 0)
+       w1.ll -= vv.ll;
+      w1.ll += (USItype) w0.s.high;
+      if (__builtin_expect (w1.s.high == w1.s.low >> ((4 * 8) - 1), 1))        {
+       w0.s.high = w1.s.low;
+       return w0.ll;
+      }
+    }
+  } else {
+    if (__builtin_expect (vv.s.high == vv.s.low >> ((4 * 8) - 1), 1)) {
+      DWunion w0 = {.ll = (UDItype) (USItype) uu.s.low * (UDItype) (USItype) vv.s.low};
+      DWunion w1 = {.ll = (UDItype) (USItype) uu.s.high * (UDItype) (USItype) vv.s.low};
+
+      if (uu.s.high < 0)
+       w1.s.high -= vv.s.low;
+      if (vv.s.low < 0)
+       w1.ll -= uu.ll;
+      w1.ll += (USItype) w0.s.high;
+      if (__builtin_expect (w1.s.high == w1.s.low >> ((4 * 8) - 1), 1))        {
+       w0.s.high = w1.s.low;
+       return w0.ll;
+      }
+    } else {
+      if (uu.s.high >= 0) {
+       if (vv.s.high >= 0) {
+         if (uu.s.high == 0 && vv.s.high == 0) {
+           const DItype w = (UDItype) (USItype) uu.s.low * (UDItype) (USItype) vv.s.low;
+           if (__builtin_expect (w >= 0, 1))
+             return w;
+         }
+       } else {
+         if (uu.s.high == 0 && vv.s.high == (SItype) -1) {
+           DWunion ww = {.ll = (UDItype) (USItype) uu.s.low * (UDItype) (USItype) vv.s.low};
+           ww.s.high -= uu.s.low;
+           if (__builtin_expect (ww.s.high < 0, 1))
+             return ww.ll;
+         }
+       }
+      } else {
+       if (vv.s.high >= 0) {
+         if (uu.s.high == (SItype) -1 && vv.s.high == 0) {
+           DWunion ww = {.ll = (UDItype) (USItype) uu.s.low * (UDItype) (USItype) vv.s.low};
+           
+           ww.s.high -= vv.s.low;
+           if (__builtin_expect (ww.s.high < 0, 1))
+             return ww.ll;
+         }
+       } else {
+         if ((uu.s.high & vv.s.high) == (SItype) -1 && (uu.s.low | vv.s.low) != 0) {
+             DWunion ww = {.ll = (UDItype) (USItype) uu.s.low * (UDItype) (USItype) vv.s.low};
+             
+             ww.s.high -= uu.s.low;
+             ww.s.high -= vv.s.low;
+             if (__builtin_expect (ww.s.high >= 0, 1))
+               return ww.ll;
+         }
+       }
+      }
+    }
+  }
+  __builtin_trap ();
+}