[PATCH v5 06/18] RISC-V: Add ADD_ANDI macro-fusion recognition

Jin Ma <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.