[committed,PR108388] LRA: Always do elimination and only for hard register to check insn constraints

Message ID 8f88686a-691f-4ec2-8249-3a181e256b8d@redhat.com
State Committed
Headers
Series [committed,PR108388] LRA: Always do elimination and only for hard register to check insn constraints |

Commit Message

Vladimir Makarov Jan. 24, 2023, 9:18 p.m. UTC
  The following patch solves

   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108388

The patch was successfully bootstrapped and tested on x86-64, aarch64, 
and ppc64le.
  

Patch

commit 265a749f290f7c6adc9a3aaa9c585b498a8a38ea
Author: Vladimir N. Makarov <vmakarov@redhat.com>
Date:   Tue Jan 24 16:10:59 2023 -0500

    LRA: Always do elimination and only for hard register to check insn constraints
    
    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.

diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc
index b0b3c5b01dc..7bffbc07ee2 100644
--- a/gcc/lra-constraints.cc
+++ b/gcc/lra-constraints.cc
@@ -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
index 00000000000..89b1f257329
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pdp11/pdp11.exp
@@ -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
index 00000000000..0d54b915c75
--- /dev/null
+++ b/gcc/testsuite/gcc.target/pdp11/pr108388.c
@@ -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 ();
+}