Factor common RTL matching out of the existing fusion recognizers and
use it to validate register dependencies consistently.  Restrict ADDI
matching to immediates encodable by the instruction.

gcc/ChangeLog:

        * config/riscv/riscv-fusion.cc (riscv_set_extract_word_add_p):
        New function.
        (riscv_regno): Likewise.
        (riscv_fusion_same_dest_p): Handle hard-register SUBREGs and
        optionally check the first-source dependency.
        (riscv_set_is_addi_p): Check the immediate range.
        (riscv_fuse_ldpreincrement): Check the update dependency.
        (riscv_fuse_lui_addi): Reuse the ADDI matcher, check the source
        dependency, and reject x0 and zero-valued LUI patterns.
        (riscv_fuse_auipc_addi): Check the source dependency and reject x0.
        (riscv_fuse_auipc_ld): Check the address base dependency.
        (riscv_fuse_bfext): Use riscv_regno to validate the source register.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/fusion-lui-addi-boundary.c: New test.
        * gcc.target/riscv/fusion-lui-addi-dependency-rtl.c: New test.
---
 gcc/config/riscv/riscv-fusion.cc              | 154 +++++++++++++++---
 .../riscv/fusion-lui-addi-boundary.c          |  61 +++++++
 .../riscv/fusion-lui-addi-dependency-rtl.c    | 122 ++++++++++++++
 3 files changed, 316 insertions(+), 21 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-lui-addi-boundary.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/fusion-lui-addi-dependency-rtl.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index f276ab42e18..dd58661df30 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -51,25 +51,128 @@ riscv_fusion_enabled_p (enum riscv_fusion_pairs op)
   return riscv_get_fusible_ops () & op;
 }
 
-/* Return true if PREV_SET and CURR_SET satisfy the same-dest constraint
-   required by most fusion rules: when we are past register allocation
-   (i.e. can_create_pseudo_p () is false), the destination registers of
-   the two sets must be the same physical register.  */
+/* Matches a word add:
+    (set (reg:DI rd)
+        (sign_extend:DI (plus:SI (reg:SI rs1) (reg:SI rs2))))
+   or
+    (set (reg:DI rd)
+         (sign_extend:DI (truncate:SI (plus:DI (reg:DI rs1) (reg:DI rs2)))))
+   or
+    (set (reg:DI rd)
+         (sign_extend:DI (subreg:SI (plus:DI (reg:DI rs1) (reg:DI rs2)) 0)))
+   Store the PLUS in *PLUS_SRC.  */
 
 static bool
-riscv_fusion_same_dest_p (rtx prev_set, rtx curr_set)
+riscv_set_extract_word_add_p (rtx set, rtx *plus_src)
 {
-  if (can_create_pseudo_p ())
-    return true;
+  rtx src = SET_SRC (set);
+  *plus_src = NULL_RTX;
+
+  if (!TARGET_64BIT
+      || GET_CODE (src) != SIGN_EXTEND
+      || GET_MODE (src) != DImode)
+    return false;
+
+  src = XEXP (src, 0);
+  if (GET_CODE (src) == PLUS && GET_MODE (src) == SImode)
+    {
+      *plus_src = src;
+      return true;
+    }
+
+  if (GET_MODE (src) != SImode)
+    return false;
+
+  if (GET_CODE (src) == TRUNCATE)
+    src = XEXP (src, 0);
+  else if (SUBREG_P (src) && subreg_lowpart_p (src))
+    src = SUBREG_REG (src);
+  else
+    return false;
+
+  if (GET_CODE (src) == PLUS && GET_MODE (src) == DImode)
+    {
+      *plus_src = src;
+      return true;
+    }
+
+  return false;
+}
 
-  unsigned int prev_dest_regno = (REG_P (SET_DEST (prev_set))
-                                 ? REGNO (SET_DEST (prev_set))
-                                 : FIRST_PSEUDO_REGISTER);
-  unsigned int curr_dest_regno = (REG_P (SET_DEST (curr_set))
-                                 ? REGNO (SET_DEST (curr_set))
-                                 : FIRST_PSEUDO_REGISTER);
+/* Return a comparable register number for X, accounting for hard-register
+   SUBREG offsets, or INVALID_REGNUM.  */
 
-  return prev_dest_regno == curr_dest_regno;
+static unsigned int
+riscv_regno (rtx x)
+{
+  int regno = true_regnum (x);
+  if (regno >= 0)
+    return regno;
+
+  /* An unassigned pseudo has no hard-register mapping yet.  Its lowpart can
+     use the pseudo's identity, but other parts cannot.  */
+  if (SUBREG_P (x)
+      && REG_P (SUBREG_REG (x))
+      && !HARD_REGISTER_P (SUBREG_REG (x))
+      && subreg_lowpart_p (x))
+    return REGNO (SUBREG_REG (x));
+
+  return INVALID_REGNUM;
+}
+
+/* Return true if PREV_SET and CURR_SET satisfy the same-destination
+   constraint.  Require fixed hard-register destinations to match; otherwise
+   defer matching until after allocation.  If USED_IN_SRC_P, also require
+   CURR_SET to use PREV_SET's destination as its first source operand.  */
+
+static bool
+riscv_fusion_same_dest_p (rtx prev_set, rtx curr_set,
+                         bool used_in_src_p = false)
+{
+  rtx prev_dest = SET_DEST (prev_set);
+  rtx curr_dest = SET_DEST (curr_set);
+  if (!REG_P (prev_dest) || !REG_P (curr_dest))
+    return false;
+
+  unsigned int prev_dest_regno = riscv_regno (prev_dest);
+  unsigned int curr_dest_regno = riscv_regno (curr_dest);
+  if (prev_dest_regno == INVALID_REGNUM
+      || curr_dest_regno == INVALID_REGNUM)
+    return false;
+
+  if (used_in_src_p)
+    {
+      rtx src = SET_SRC (curr_set);
+      rtx word_add_src;
+      if (riscv_set_extract_word_add_p (curr_set, &word_add_src))
+       src = XEXP (word_add_src, 0);
+      else
+       {
+         while (GET_CODE (src) == SIGN_EXTEND
+                || GET_CODE (src) == ZERO_EXTEND)
+           src = XEXP (src, 0);
+
+         if (GET_CODE (src) == NOT
+             || BINARY_P (src)
+             || GET_CODE (src) == LO_SUM
+             || GET_CODE (src) == ZERO_EXTRACT)
+           src = XEXP (src, 0);
+
+         while (GET_CODE (src) == SIGN_EXTEND
+                || GET_CODE (src) == ZERO_EXTEND)
+           src = XEXP (src, 0);
+       }
+
+      if (riscv_regno (src) != prev_dest_regno)
+       return false;
+    }
+
+  /* Hard-register destinations cannot be changed by allocation.  */
+  if (!can_create_pseudo_p ()
+      || (HARD_REGISTER_P (prev_dest) && HARD_REGISTER_P (curr_dest)))
+    return prev_dest_regno == curr_dest_regno;
+
+  return true;
 }
 
 /* Matches an add:
@@ -93,6 +196,7 @@ riscv_set_is_addi_p (rtx set)
   return (GET_CODE (SET_SRC (set)) == PLUS
          && REG_P (XEXP (SET_SRC (set), 0))
          && CONST_INT_P (XEXP (SET_SRC (set), 1))
+         && SMALL_OPERAND (INTVAL (XEXP (SET_SRC (set), 1)))
          && REG_P (SET_DEST (set)));
 }
 
@@ -402,6 +506,8 @@ riscv_fuse_ldpreincrement (rtx_insn *prev, rtx_insn *curr)
       && REGNO (XEXP (SET_SRC (curr_set), 0)) == REGNO (SET_DEST (prev_set))
       && GET_CODE (SET_SRC (prev_set)) == PLUS
       && REG_P (XEXP (SET_SRC (prev_set), 0))
+      && (REGNO (XEXP (SET_SRC (prev_set), 0))
+         == REGNO (SET_DEST (prev_set)))
       && CONST_INT_P (XEXP (SET_SRC (prev_set), 1)))
     return true;
 
@@ -431,15 +537,16 @@ riscv_fuse_lui_addi (rtx_insn *prev, rtx_insn *curr)
   if (!prev_set || !curr_set || any_condjump_p (curr))
     return false;
 
-  if (!riscv_fusion_same_dest_p (prev_set, curr_set))
+  if (!riscv_fusion_same_dest_p (prev_set, curr_set, true)
+      || REGNO (SET_DEST (prev_set)) == GP_REG_FIRST)
     return false;
 
   if ((GET_CODE (SET_SRC (curr_set)) == LO_SUM
-       || (GET_CODE (SET_SRC (curr_set)) == PLUS
-          && CONST_INT_P (XEXP (SET_SRC (curr_set), 1))
-          && SMALL_OPERAND (INTVAL (XEXP (SET_SRC (curr_set), 1)))))
+       || riscv_set_is_addi_p (curr_set))
       && (GET_CODE (SET_SRC (prev_set)) == HIGH
          || (CONST_INT_P (SET_SRC (prev_set))
+             && (SET_SRC (prev_set)
+                 != CONST0_RTX (GET_MODE (SET_DEST (prev_set))))
              && LUI_OPERAND (INTVAL (SET_SRC (prev_set))))))
     return true;
 
@@ -466,7 +573,8 @@ riscv_fuse_auipc_addi (rtx_insn *prev, rtx_insn *curr)
   if (!prev_set || !curr_set || any_condjump_p (curr))
     return false;
 
-  if (!riscv_fusion_same_dest_p (prev_set, curr_set))
+  if (!riscv_fusion_same_dest_p (prev_set, curr_set, true)
+      || REGNO (SET_DEST (prev_set)) == GP_REG_FIRST)
     return false;
 
   if (GET_CODE (SET_SRC (prev_set)) == UNSPEC
@@ -565,7 +673,10 @@ riscv_fuse_auipc_ld (rtx_insn *prev, rtx_insn *curr)
       && XINT (SET_SRC (prev_set), 1) == UNSPEC_AUIPC
       && MEM_P (SET_SRC (curr_set))
       && SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set)))
-      && GET_CODE (XEXP (SET_SRC (curr_set), 0)) == PLUS)
+      && GET_CODE (XEXP (SET_SRC (curr_set), 0)) == PLUS
+      && REG_P (XEXP (XEXP (SET_SRC (curr_set), 0), 0))
+      && (REGNO (XEXP (XEXP (SET_SRC (curr_set), 0), 0))
+         == REGNO (SET_DEST (prev_set))))
     return true;
 
   return false;
@@ -724,7 +835,8 @@ riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr)
          || GET_CODE (SET_SRC (curr_set)) == ASHIFTRT)
       && REG_P (SET_DEST (prev_set))
       && REG_P (SET_DEST (curr_set))
-      && REGNO (XEXP (SET_SRC (curr_set), 0)) == REGNO (SET_DEST (prev_set))
+      && (riscv_regno (XEXP (SET_SRC (curr_set), 0))
+         == REGNO (SET_DEST (prev_set)))
       && CONST_INT_P (XEXP (SET_SRC (prev_set), 1))
       && CONST_INT_P (XEXP (SET_SRC (curr_set), 1)))
     return true;
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-boundary.c 
b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-boundary.c
new file mode 100644
index 00000000000..872748dae71
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-boundary.c
@@ -0,0 +1,61 @@
+/* Check encodable ADDI boundary values through LUI/ADDI fusion.  */
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -O2 -fno-dce 
-mtune=sifive-p600-series -fdump-rtl-sched1-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LUI_ADDI" 2 "sched1" } } */
+
+long __RTL (startwith ("sched1"))
+test_addi_min (void)
+{
+(function "test_addi_min"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 3 (set (reg:DI a0)
+                    (const_int 305418240)))
+      (cinsn 4 (set (reg:DI a0)
+                    (plus:DI (reg:DI a0)
+                             (const_int -2048))))
+      (cinsn 5 (use (reg/i:DI a0)))
+      (cjump_insn 6 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 7)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_addi_min"
+}
+
+long __RTL (startwith ("sched1"))
+test_addi_max (void)
+{
+(function "test_addi_max"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 3 (set (reg:DI a0)
+                    (const_int 305418240)))
+      (cinsn 4 (set (reg:DI a0)
+                    (plus:DI (reg:DI a0)
+                             (const_int 2047))))
+      (cinsn 5 (use (reg/i:DI a0)))
+      (cjump_insn 6 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 7)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_addi_max"
+}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-dependency-rtl.c 
b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-dependency-rtl.c
new file mode 100644
index 00000000000..b407dc6a813
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-dependency-rtl.c
@@ -0,0 +1,122 @@
+/* Check that LUI/ADDI fusion requires a true data dependency.  */
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -O2 -fno-dce -mcmodel=medany 
-mexplicit-relocs -mtune=sifive-p600-series -fdump-rtl-sched1-details" } */
+/* { dg-final { scan-rtl-dump-not "RISCV_FUSE_LUI_ADDI" "sched1" } } */
+/* { dg-final { scan-rtl-dump-not "RISCV_FUSE_AUIPC_ADDI" "sched1" } } */
+
+long __RTL (startwith ("sched1"))
+test_lui_addi_source_mismatch (void)
+{
+(function "test_lui_addi_source_mismatch"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 3 (set (reg:DI a0)
+                    (const_int 305418240)))
+      (cinsn 4 (set (reg:DI a0)
+                    (plus:DI (reg:DI a1)
+                             (const_int 1656))))
+      (cinsn 5 (use (reg:DI a0)))
+      (cjump_insn 6 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 7)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_lui_addi_source_mismatch"
+}
+
+long __RTL (startwith ("sched1"))
+test_auipc_addi_destination_mismatch (void)
+{
+(function "test_auipc_addi_destination_mismatch"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 3 (set (reg:DI a2)
+                    (unspec:DI [
+                      (symbol_ref:DI ("foo_destination"))
+                      (const_int 1)
+                      (pc)
+                    ] UNSPEC_AUIPC)))
+      (cinsn 4 (set (reg:DI a0)
+                    (lo_sum:DI (reg:DI a2)
+                               (symbol_ref:DI ("foo_destination")))))
+      (cinsn 5 (use (reg:DI a0)))
+      (cinsn 6 (use (reg:DI a2)))
+      (cjump_insn 7 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 8)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_auipc_addi_destination_mismatch"
+}
+
+long __RTL (startwith ("sched1"))
+test_lui_addi_zero_destination (void)
+{
+(function "test_lui_addi_zero_destination"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 3 (set (reg:DI zero)
+                    (const_int 305418240)))
+      (cinsn 4 (set (reg:DI zero)
+                    (plus:DI (reg:DI zero)
+                             (const_int 1656))))
+      (cjump_insn 5 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 6)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_lui_addi_zero_destination"
+}
+
+long __RTL (startwith ("sched1"))
+test_lui_addi_zero_constant (void)
+{
+(function "test_lui_addi_zero_constant"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 3 (set (reg:DI a0)
+                    (const_int 0)))
+      (cinsn 4 (set (reg:DI a0)
+                    (plus:DI (reg:DI a0)
+                             (const_int 1))))
+      (cinsn 5 (use (reg:DI a0)))
+      (cjump_insn 6 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 7)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx
+      (reg/i:DI a0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "test_lui_addi_zero_constant"
+}
-- 
2.52.0

Reply via email to