Recognize ADD/ADDI-type instructions followed by ANDI when the result
register forms the required dependency.  Parameterize the ANDI
classifier so the new recognizer and existing B_ALUI logic share RTL
parsing.

Leave the fusion disabled by default.

gcc/ChangeLog:

        * config/riscv/riscv-fusion.cc (riscv_insn_is_add_addi_p): New
        function.
        (riscv_insn_is_andi_type_p): Likewise.
        (riscv_fuse_b_alui): Reuse the ANDI matcher.
        (riscv_fuse_add_andi): New function.
        (riscv_fusion_table): Add RISCV_FUSE_ADD_ANDI.
        * config/riscv/riscv-protos.h (enum riscv_fusion_pairs): Add
        RISCV_FUSE_ADD_ANDI.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/fusion-add-addi-andi.c: New test.
        * gcc.target/riscv/fusion-mv-li-andi-rtl.c: Likewise.
        * gcc.target/riscv/fusion-special-positive-rtl-add-andi.c: Likewise.

Signed-off-by: Jin Ma <[email protected]>
---
 gcc/config/riscv/riscv-fusion.cc              | 95 ++++++++++++++++++-
 gcc/config/riscv/riscv-protos.h               |  1 +
 .../gcc.target/riscv/fusion-add-addi-andi.c   | 45 +++++++++
 .../gcc.target/riscv/fusion-mv-li-andi-rtl.c  | 63 ++++++++++++
 .../fusion-special-positive-rtl-add-andi.c    | 66 +++++++++++++
 5 files changed, 267 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-add-addi-andi.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-mv-li-andi-rtl.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-add-andi.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index bb4076e9ffc..816c7dea452 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -702,6 +702,57 @@ riscv_insn_is_sub_type_p (rtx_insn *insn)
          && REG_P (XEXP (src, 1)));
 }
 
+/* Matches an add- or addi-type instruction.  */
+
+static bool
+riscv_insn_is_add_addi_p (rtx_insn *insn)
+{
+  return (riscv_insn_is_add_type_p (insn)
+         || riscv_insn_is_addi_type_p (insn));
+}
+
+/* Matches an andi:
+     (set (reg rd) (and (reg rs1) (const_int imm12)))
+   or an equivalent zero-extend RTL form.  Store the normalized operands in
+   *SRC0 and *SRC1 when requested.  */
+
+static bool
+riscv_insn_is_andi_type_p (rtx_insn *insn, rtx *src0 = NULL,
+                          rtx *src1 = NULL)
+{
+  rtx set = single_set (insn);
+  if (!set)
+    return false;
+
+  rtx src = SET_SRC (set);
+  if (get_attr_type (insn) == TYPE_LOGICAL
+      && GET_CODE (src) == AND
+      && REG_P (XEXP (src, 0))
+      && CONST_INT_P (XEXP (src, 1))
+      && riscv_regno (SET_DEST (set)) != INVALID_REGNUM)
+    {
+      if (src0)
+       *src0 = XEXP (src, 0);
+      if (src1)
+       *src1 = XEXP (src, 1);
+      return true;
+    }
+
+  if (get_attr_move_type (insn) == MOVE_TYPE_ANDI
+      && GET_CODE (src) == ZERO_EXTEND
+      && riscv_regno (XEXP (src, 0)) != INVALID_REGNUM
+      && riscv_regno (SET_DEST (set)) != INVALID_REGNUM)
+    {
+      if (src0)
+       *src0 = XEXP (src, 0);
+      if (src1)
+       *src1 = GEN_INT (0xff);
+      return true;
+    }
+
+  return false;
+}
+
 /* Fusion recognizers.  */
 
 /* Check for RISCV_FUSE_ZEXTW fusion.
@@ -1150,10 +1201,11 @@ riscv_fuse_b_alui (rtx_insn *prev, rtx_insn *curr)
     return true;
 
   /* ctz + andi.  */
+  rtx andi_src1;
   if (GET_CODE (SET_SRC (prev_set)) == CTZ
-      && GET_CODE (SET_SRC (curr_set)) == AND
-      && CONST_INT_P (XEXP (SET_SRC (curr_set), 1))
-      && INTVAL (XEXP (SET_SRC (curr_set), 1)) == 63)
+      && riscv_insn_is_andi_type_p (curr, NULL, &andi_src1)
+      && CONST_INT_P (andi_src1)
+      && INTVAL (andi_src1) == 63)
     return true;
 
   /* sub + smax (abs pattern).  */
@@ -1207,6 +1259,41 @@ riscv_fuse_sub_seqz (rtx_insn *prev, rtx_insn *curr)
   return false;
 }
 
+/* Check for RISCV_FUSE_ADD_ANDI fusion.
+   prev (one of the following):
+     (add) == (set (reg rd1) (plus (reg rs1) (reg rs2)))
+     (addi) == (set (reg rd1) (plus (reg rs1) (const_int imm12_1)))
+     (addw) == (set (reg rd1)
+                   (sign_extend (plus (reg rs1) (reg rs2))))
+     (addiw) == (set (reg rd1)
+                    (sign_extend (plus (reg rs1) (const_int imm12_1))))
+     (add.uw) == (set (reg rd1) (plus (zero_extend (reg rs1))
+                                     (reg rs2)))
+     (mv) == (set (reg rd1) (reg rs1))
+     (li) == (set (reg rd1) (const_int imm12_1))
+     (addi) == (set (reg rd1) (lo_sum (reg rs1) symbol))
+   curr (one of the following):
+     (andi) == (set (reg rd2) (and (reg rd1) (const_int imm12_2)))
+     (andi) == (set (reg rd2) (zero_extend (reg rd1)))
+
+   Constraints:
+     rd1 == rd2.  */
+
+static bool
+riscv_fuse_add_andi (rtx_insn *prev, rtx_insn *curr)
+{
+  rtx prev_set, curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set))
+    return false;
+
+  if (riscv_insn_is_add_addi_p (prev)
+      && riscv_insn_is_andi_type_p (curr)
+      && riscv_fuse_same_dest_p (prev_set, curr_set, true))
+    return true;
+
+  return false;
+}
+
 /* Type for a fusion checker function.  Takes the two candidate insns
    and returns true if they should be fused.  */
 
@@ -1260,6 +1347,8 @@ static const struct riscv_fusion_entry 
riscv_fusion_table[] =
     riscv_fuse_b_alui, "RISCV_FUSE_B_ALUI" },
   { RISCV_FUSE_SUB_SEQZ,
     riscv_fuse_sub_seqz, "RISCV_FUSE_SUB_SEQZ" },
+  { RISCV_FUSE_ADD_ANDI,
+    riscv_fuse_add_andi, "RISCV_FUSE_ADD_ANDI" },
 };
 
 /* Implement TARGET_SCHED_MACRO_FUSION_PAIR_P.  Return true if PREV and CURR
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 0bd26523a98..0848bc0ec4f 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -868,6 +868,7 @@ enum riscv_fusion_pairs
   RISCV_FUSE_B_ALUI = HOST_WIDE_INT_1U << 12,
   RISCV_FUSE_SUB_SEQZ = HOST_WIDE_INT_1U << 13,
   RISCV_FUSE_ADD_ST = HOST_WIDE_INT_1U << 14,
+  RISCV_FUSE_ADD_ANDI = HOST_WIDE_INT_1U << 15,
 };
 
 extern bool riscv_macro_fusion_p (void);
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-add-addi-andi.c 
b/gcc/testsuite/gcc.target/riscv/fusion-add-addi-andi.c
new file mode 100644
index 00000000000..983d40c8f6b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-add-addi-andi.c
@@ -0,0 +1,45 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc_zba -mabi=lp64d -mtune=xt-c9501fdvt -O2 
-fdump-rtl-sched2-details" } */
+/* No tune enables these fusion pairs yet.  */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_ADD_ANDI" 5 "sched2" { xfail 
*-*-* } } } */
+
+typedef long int64_t;
+typedef unsigned long uint64_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+
+/* addi + andi should fuse.  */
+int64_t
+test_addi_andi (int64_t a)
+{
+  return (a + 16) & 0x55;
+}
+
+/* addiw + andi should fuse.  */
+int64_t
+test_addiw_andi (int32_t a)
+{
+  return (int64_t) (a + 5) & 0x55;
+}
+
+/* add + andi should fuse.  */
+int64_t
+test_add_andi (int64_t a, int64_t b)
+{
+  return (a + b) & 0x55;
+}
+
+/* addw + andi should fuse.  */
+int64_t
+test_addw_andi (int32_t a, int32_t b)
+{
+  return (int64_t) (a + b) & 0x33;
+}
+
+/* add.uw + andi should fuse.  */
+uint64_t
+test_adduw_andi (uint64_t a, uint64_t b)
+{
+  return ((uint64_t) (uint32_t) a + b) & -2UL;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-mv-li-andi-rtl.c 
b/gcc/testsuite/gcc.target/riscv/fusion-mv-li-andi-rtl.c
new file mode 100644
index 00000000000..507efd0c2c7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-mv-li-andi-rtl.c
@@ -0,0 +1,63 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2 
-fdump-rtl-sched2-details" } */
+/* No tune enables these fusion pairs yet.  */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_ADD_ANDI" 2 "sched2" { xfail 
*-*-* } } } */
+
+/* mv followed by andi should fuse.  */
+long __RTL (startwith ("sched2"))
+test_mv_andi (void)
+{
+(function "test_mv_andi"
+  (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)
+                    (reg:DI a1)))
+      (cinsn 4 (set (reg:DI a0)
+                    (and:DI (reg:DI a0)
+                            (const_int 85))))
+      (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_mv_andi"
+}
+
+/* li followed by andi should fuse.  */
+long __RTL (startwith ("sched2"))
+test_li_andi (void)
+{
+(function "test_li_andi"
+  (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 31)))
+      (cinsn 4 (set (reg:DI a0)
+                    (and:DI (reg:DI a0)
+                            (const_int 85))))
+      (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_li_andi"
+}
diff --git 
a/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-add-andi.c 
b/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-add-andi.c
new file mode 100644
index 00000000000..40a554e1cbd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-add-andi.c
@@ -0,0 +1,66 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc_zbb -mabi=lp64d -mtune=xt-c9501fdvt -O2 
-fdump-rtl-sched2-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_ADD_ANDI" 2 "sched2" { xfail 
*-*-* } } } */
+
+extern long fusion_low_symbol;
+
+/* add followed by andi expressed as zero_extend should fuse.  */
+long __RTL (startwith ("sched2"))
+test_add_zero_extend_andi (void)
+{
+(function "test_add_zero_extend_andi"
+  (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)
+                    (plus:DI (reg:DI a1)
+                             (reg:DI a2))))
+      (cinsn 4 (set (reg:DI a0)
+                    (zero_extend:DI
+                      (subreg:QI (reg:DI a0) 0))))
+      (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_add_zero_extend_andi"
+}
+/* lo_sum update followed by andi should fuse.  */
+long __RTL (startwith ("sched2"))
+test_lo_sum_andi (void)
+{
+(function "test_lo_sum_andi"
+  (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)
+                    (lo_sum:DI
+                      (reg:DI a0)
+                      (symbol_ref:DI ("fusion_low_symbol")))))
+      (cinsn 4 (set (reg:DI a0)
+                    (and:DI (reg:DI a0)
+                            (const_int 85))))
+      (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_lo_sum_andi"
+}
-- 
2.52.0

Reply via email to