Recognize ANDI followed by an ADD/ADDI-type instruction when the
result register forms the required dependency.  An ADD may use the ANDI
result as both source operands; no source-inequality constraint is
imposed.

Leave the fusion disabled by default.

gcc/ChangeLog:

        * config/riscv/riscv-fusion.cc (riscv_fuse_andi_add): New function.
        (riscv_fusion_table): Add RISCV_FUSE_ANDI_ADD.
        * config/riscv/riscv-protos.h (enum riscv_fusion_pairs): Add
        RISCV_FUSE_ANDI_ADD.

gcc/testsuite/ChangeLog:

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

Signed-off-by: Jin Ma <[email protected]>
---
 gcc/config/riscv/riscv-fusion.cc              | 36 ++++++++++
 gcc/config/riscv/riscv-protos.h               |  1 +
 .../gcc.target/riscv/fusion-andi-add-addi.c   | 36 ++++++++++
 .../gcc.target/riscv/fusion-andi-adduw-rtl.c  | 65 ++++++++++++++++++
 .../fusion-special-positive-rtl-andi-add.c    | 66 +++++++++++++++++++
 5 files changed, 204 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-andi-add-addi.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-andi-adduw-rtl.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-andi-add.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index 816c7dea452..3b09e1a4a57 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -1294,6 +1294,40 @@ riscv_fuse_add_andi (rtx_insn *prev, rtx_insn *curr)
   return false;
 }
 
+/* Check for RISCV_FUSE_ANDI_ADD fusion.
+   prev (one of the following):
+     (andi) == (set (reg rd1) (and (reg rs1) (const_int imm12_1)))
+     (andi) == (set (reg rd1) (zero_extend (reg rs1)))
+   curr (one of the following):
+     (add) == (set (reg rd2) (plus (reg rd1) (reg rs2)))
+     (addi) == (set (reg rd2) (plus (reg rd1) (const_int imm12_2)))
+     (addw) == (set (reg rd2)
+                   (sign_extend (plus (reg rd1) (reg rs2))))
+     (addiw) == (set (reg rd2)
+                    (sign_extend (plus (reg rd1) (const_int imm12_2))))
+     (add.uw) == (set (reg rd2) (plus (zero_extend (reg rd1))
+                                     (reg rs2)))
+     (mv) == (set (reg rd2) (reg rd1))
+     (addi) == (set (reg rd2) (lo_sum (reg rd1) symbol))
+
+   Constraints:
+     rd1 == rd2.  */
+
+static bool
+riscv_fuse_andi_add (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_andi_type_p (prev)
+      && riscv_insn_is_add_addi_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.  */
 
@@ -1349,6 +1383,8 @@ static const struct riscv_fusion_entry 
riscv_fusion_table[] =
     riscv_fuse_sub_seqz, "RISCV_FUSE_SUB_SEQZ" },
   { RISCV_FUSE_ADD_ANDI,
     riscv_fuse_add_andi, "RISCV_FUSE_ADD_ANDI" },
+  { RISCV_FUSE_ANDI_ADD,
+    riscv_fuse_andi_add, "RISCV_FUSE_ANDI_ADD" },
 };
 
 /* 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 0848bc0ec4f..f6863bc4c17 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -869,6 +869,7 @@ enum riscv_fusion_pairs
   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,
+  RISCV_FUSE_ANDI_ADD = HOST_WIDE_INT_1U << 16,
 };
 
 extern bool riscv_macro_fusion_p (void);
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-andi-add-addi.c 
b/gcc/testsuite/gcc.target/riscv/fusion-andi-add-addi.c
new file mode 100644
index 00000000000..746f865535d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-andi-add-addi.c
@@ -0,0 +1,36 @@
+/* { 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_ANDI_ADD" 4 "sched2" { xfail 
*-*-* } } } */
+
+typedef long int64_t;
+typedef int int32_t;
+
+/* andi + addi should fuse.  */
+int64_t
+test_andi_addi (int64_t a)
+{
+  return (a & 0x55) + 1;
+}
+
+/* andi + addiw should fuse.  */
+int64_t
+test_andi_addiw (int64_t a)
+{
+  return (int64_t) ((int32_t) (a & 0x55) + 5);
+}
+
+/* andi + add should fuse.  */
+int64_t
+test_andi_add (int64_t a, int64_t b)
+{
+  return (a & 0x55) + b;
+}
+
+/* andi + addw should fuse.  */
+int64_t
+test_andi_addw (int64_t a, int32_t b)
+{
+  return (int64_t) ((int32_t) (a & 0x55) + b);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-andi-adduw-rtl.c 
b/gcc/testsuite/gcc.target/riscv/fusion-andi-adduw-rtl.c
new file mode 100644
index 00000000000..996f4914f86
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-andi-adduw-rtl.c
@@ -0,0 +1,65 @@
+/* { 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_ANDI_ADD" 2 "sched2" { xfail 
*-*-* } } } */
+
+/* andi feeding add.uw's zero-extended operand should fuse.  */
+long __RTL (startwith ("sched2"))
+test_andi_adduw (void)
+{
+(function "test_andi_adduw"
+  (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)
+                    (and:DI (reg:DI a1)
+                            (const_int -3))))
+      (cinsn 4 (set (reg:DI a0)
+                    (plus:DI (zero_extend:DI (reg:SI a0))
+                             (reg:DI a2))))
+      (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_andi_adduw"
+}
+
+/* An add may use andi's result as both sources.  */
+long __RTL (startwith ("sched2"))
+test_andi_add_same_source (void)
+{
+(function "test_andi_add_same_source"
+  (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)
+                    (and:DI (reg:DI a1)
+                            (const_int 85))))
+      (cinsn 4 (set (reg:DI a0)
+                    (plus:DI (reg:DI a0)
+                             (reg:DI a0))))
+      (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_andi_add_same_source"
+}
diff --git 
a/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-andi-add.c 
b/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-andi-add.c
new file mode 100644
index 00000000000..4af0e90c798
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-andi-add.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_ANDI_ADD" 2 "sched2" { xfail 
*-*-* } } } */
+
+extern long fusion_low_symbol;
+
+/* andi expressed as zero_extend followed by add should fuse.  */
+long __RTL (startwith ("sched2"))
+test_zero_extend_andi_add (void)
+{
+(function "test_zero_extend_andi_add"
+  (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)
+                    (zero_extend:DI
+                      (subreg:QI (reg:DI a1) 0))))
+      (cinsn 4 (set (reg:DI a0)
+                    (plus:DI (reg:DI a0)
+                             (reg:DI a2))))
+      (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_zero_extend_andi_add"
+}
+/* andi followed by a lo_sum update should fuse.  */
+long __RTL (startwith ("sched2"))
+test_andi_lo_sum (void)
+{
+(function "test_andi_lo_sum"
+  (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)
+                    (and:DI (reg:DI a1)
+                            (const_int 85))))
+      (cinsn 4 (set (reg:DI a0)
+                    (lo_sum:DI
+                      (reg:DI a0)
+                      (symbol_ref:DI ("fusion_low_symbol")))))
+      (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_andi_lo_sum"
+}
-- 
2.52.0

Reply via email to