Recognize supported logical instruction pairs while enforcing their
destination, dependency, and source-count constraints.  Reuse the common
ANDI classifier for canonical ANDI and zero-extend RTL forms.

Leave the fusion disabled by default.

gcc/ChangeLog:

        * config/riscv/riscv-fusion.cc (riscv_insn_is_logical_type_p): New
        function.
        (riscv_fuse_logic_logic): Likewise.
        (riscv_fusion_table): Add RISCV_FUSE_LOGIC_LOGIC.
        * config/riscv/riscv-protos.h (enum riscv_fusion_pairs): Add
        RISCV_FUSE_LOGIC_LOGIC.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/fusion-logic-logic.c: New test.
        * gcc.target/riscv/fusion-logic-logic-zbkb.c: Likewise.
        * gcc.target/riscv/fusion-special-positive-rtl-logic-logic.c:
        Likewise.

Signed-off-by: Jin Ma <[email protected]>
---
 gcc/config/riscv/riscv-fusion.cc              | 131 ++++++++++++++++++
 gcc/config/riscv/riscv-protos.h               |   1 +
 .../riscv/fusion-logic-logic-zbkb.c           |  28 ++++
 .../gcc.target/riscv/fusion-logic-logic.c     |  70 ++++++++++
 .../fusion-special-positive-rtl-logic-logic.c |  36 +++++
 5 files changed, 266 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-logic-logic-zbkb.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-logic-logic.c
 create mode 100644 
gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-logic-logic.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index 3b09e1a4a57..a4f203347b7 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -753,6 +753,82 @@ riscv_insn_is_andi_type_p (rtx_insn *insn, rtx *src0 = 
NULL,
   return false;
 }
 
+/* Matches a logical instruction and stores its normalized operands in *SRC0
+   and *SRC1 when requested.  */
+
+static bool
+riscv_insn_is_logical_type_p (rtx_insn *insn,
+                             rtx *src0 = NULL,
+                             rtx *src1 = NULL)
+{
+  rtx set = single_set (insn);
+  if (!set)
+    return false;
+
+  if (riscv_insn_is_andi_type_p (insn, src0, src1))
+    return true;
+
+  enum attr_type type = get_attr_type (insn);
+  rtx src = SET_SRC (set);
+  rtx_code code = GET_CODE (src);
+
+  if (code == AND || code == IOR || code == XOR)
+    {
+      if (type == TYPE_LOGICAL
+         && REG_P (XEXP (src, 0))
+         && (REG_P (XEXP (src, 1))
+             || CONST_INT_P (XEXP (src, 1))))
+       {
+         if (src0)
+           *src0 = XEXP (src, 0);
+         if (src1)
+           *src1 = XEXP (src, 1);
+         return true;
+       }
+
+      if (type != TYPE_BITMANIP || !(TARGET_ZBB || TARGET_ZBKB))
+       return false;
+
+      rtx sub = XEXP (src, 0);
+      if (GET_CODE (sub) != NOT
+         || !REG_P (XEXP (src, 1))
+         || !REG_P (XEXP (sub, 0)))
+       return false;
+      if (src0)
+       *src0 = XEXP (src, 1);
+      if (src1)
+       *src1 = XEXP (sub, 0);
+      return true;
+    }
+
+  if (code != NOT)
+    return false;
+
+  rtx sub = XEXP (src, 0);
+  if (REG_P (sub))
+    {
+      if (type != TYPE_LOGICAL)
+       return false;
+      if (src0)
+       *src0 = sub;
+      return true;
+    }
+
+  if (SUBREG_P (sub)
+      || type != TYPE_BITMANIP
+      || !(TARGET_ZBB || TARGET_ZBKB)
+      || GET_CODE (sub) != XOR
+      || !REG_P (XEXP (sub, 0))
+      || !REG_P (XEXP (sub, 1)))
+    return false;
+
+  if (src0)
+    *src0 = XEXP (sub, 0);
+  if (src1)
+    *src1 = XEXP (sub, 1);
+  return true;
+}
+
 /* Fusion recognizers.  */
 
 /* Check for RISCV_FUSE_ZEXTW fusion.
@@ -1328,6 +1404,59 @@ riscv_fuse_andi_add (rtx_insn *prev, rtx_insn *curr)
   return false;
 }
 
+/* Check for RISCV_FUSE_LOGIC_LOGIC fusion.
+   prev (one of the following):
+     (logic) == (set (reg rd1) (op1 (reg rs1) (reg rs2)))
+     (logic) == (set (reg rd1) (op1 (reg rs1) (const_int imm12_1)))
+     (logic) == (set (reg rd1) (op1 (not (reg rs2)) (reg rs1)))
+     (xnor) == (set (reg rd1) (not (xor (reg rs1) (reg rs2))))
+     (not) == (set (reg rd1) (not (reg rs1)))
+     (andi) == (set (reg rd1) (zero_extend (reg rs1)))
+   curr (one of the following):
+     (logic) == (set (reg rd2) (op2 (reg rd1) (reg rs3)))
+     (logic) == (set (reg rd2) (op2 (reg rd1) (const_int imm12_2)))
+     (logic) == (set (reg rd2) (op2 (not (reg rs3)) (reg rd1)))
+     (xnor) == (set (reg rd2) (not (xor (reg rd1) (reg rs3))))
+     (not) == (set (reg rd2) (not (reg rd1)))
+     (andi) == (set (reg rd2) (zero_extend (reg rd1)))
+
+   Constraints:
+     rd1 == rd2
+     rd1 != rs3 when curr uses register source rs3
+     rs2 and rs3 cannot both be register sources
+     op1 and op2, when present, are and, ior, or xor
+     andn/orn/xnor require ZBB or ZBKB.  */
+
+static bool
+riscv_fuse_logic_logic (rtx_insn *prev, rtx_insn *curr)
+{
+  rtx prev_set, curr_set;
+  if (!riscv_fuse_sets_p (prev, curr, &prev_set, &curr_set))
+    return false;
+
+  rtx prev_dest = SET_DEST (prev_set);
+
+  rtx prev_src1 = NULL_RTX;
+  rtx curr_src0 = NULL_RTX, curr_src1 = NULL_RTX;
+  if (!riscv_insn_is_logical_type_p (prev, NULL, &prev_src1)
+      || !riscv_insn_is_logical_type_p (curr, &curr_src0,
+                                       &curr_src1))
+    return false;
+
+  if (riscv_fuse_same_dest_p (prev_set, curr_set)
+      && riscv_fuse_same_reg_p (prev_dest, curr_src0)
+      && ((curr_src1 == NULL_RTX)
+         || riscv_regno (curr_src1) == INVALID_REGNUM
+         || !riscv_fuse_same_reg_p (prev_dest, curr_src1))
+      && !(prev_src1 != NULL_RTX
+          && curr_src1 != NULL_RTX
+          && riscv_regno (prev_src1) != INVALID_REGNUM
+          && riscv_regno (curr_src1) != INVALID_REGNUM))
+    return true;
+
+  return false;
+}
+
 /* Type for a fusion checker function.  Takes the two candidate insns
    and returns true if they should be fused.  */
 
@@ -1385,6 +1514,8 @@ static const struct riscv_fusion_entry 
riscv_fusion_table[] =
     riscv_fuse_add_andi, "RISCV_FUSE_ADD_ANDI" },
   { RISCV_FUSE_ANDI_ADD,
     riscv_fuse_andi_add, "RISCV_FUSE_ANDI_ADD" },
+  { RISCV_FUSE_LOGIC_LOGIC,
+    riscv_fuse_logic_logic, "RISCV_FUSE_LOGIC_LOGIC" },
 };
 
 /* 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 f6863bc4c17..46f148a9311 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -870,6 +870,7 @@ enum riscv_fusion_pairs
   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,
+  RISCV_FUSE_LOGIC_LOGIC = HOST_WIDE_INT_1U << 17,
 };
 
 extern bool riscv_macro_fusion_p (void);
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-logic-logic-zbkb.c 
b/gcc/testsuite/gcc.target/riscv/fusion-logic-logic-zbkb.c
new file mode 100644
index 00000000000..e485ed65184
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-logic-logic-zbkb.c
@@ -0,0 +1,28 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc_zbkb -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_LOGIC_LOGIC" 3 "sched2" { 
xfail *-*-* } } } */
+
+typedef long int64_t;
+
+/* andn + andi should fuse under Zbkb.  */
+int64_t
+test_zbkb_andn_andi (int64_t a, int64_t b)
+{
+  return (~a & b) & 0x55;
+}
+
+/* orn + andi should fuse under Zbkb.  */
+int64_t
+test_zbkb_orn_andi (int64_t a, int64_t b)
+{
+  return (~a | b) & 0x55;
+}
+
+/* xnor + andi should fuse under Zbkb.  */
+int64_t
+test_zbkb_xnor_andi (int64_t a, int64_t b)
+{
+  return ~(a ^ b) & 0x55;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-logic-logic.c 
b/gcc/testsuite/gcc.target/riscv/fusion-logic-logic.c
new file mode 100644
index 00000000000..edb3e7d578f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-logic-logic.c
@@ -0,0 +1,70 @@
+/* { 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" } */
+/* No tune enables this fusion pair yet.  */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LOGIC_LOGIC" 6 "sched2" { 
xfail *-*-* } } } */
+
+typedef long int64_t;
+
+/* Register source followed by an immediate source.  */
+int64_t
+test_and_xori (int64_t a, int64_t b)
+{
+  return (a & b) ^ 0x1f;
+}
+
+/* Immediate source followed by a register source.  */
+int64_t
+test_andi_xor (int64_t a, int64_t b)
+{
+  return (a & 0x55) ^ b;
+}
+
+/* Complemented first operation followed by an immediate operation.  */
+int64_t
+test_andn_ori (int64_t a, int64_t b)
+{
+  return (~a & b) | 0x33;
+}
+
+/* Immediate first operation followed by a complemented operation.  */
+int64_t
+test_ori_xnor (int64_t a, int64_t b)
+{
+  return ~((a | 0x33) ^ b);
+}
+
+/* Unary first operation followed by an immediate operation.  Use RTL to
+   prevent combine from replacing the pair with andn.  */
+int64_t __RTL (startwith ("sched2"))
+test_not_andi (void)
+{
+(function "test_not_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)
+                    (not:DI (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))
+  ) ;; crtl
+) ;; function "test_not_andi"
+}
+
+/* Immediate first operation followed by a unary operation.  */
+int64_t
+test_andi_not (int64_t a)
+{
+  return ~(a & 0x55);
+}
diff --git 
a/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-logic-logic.c 
b/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-logic-logic.c
new file mode 100644
index 00000000000..2466f26ae0f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-special-positive-rtl-logic-logic.c
@@ -0,0 +1,36 @@
+/* { 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_LOGIC_LOGIC" 1 "sched2" { 
xfail *-*-* } } } */
+
+extern long fusion_low_symbol;
+
+/* andi expressed as zero_extend followed by ori should fuse.  */
+long __RTL (startwith ("sched2"))
+test_zero_extend_andi_ori (void)
+{
+(function "test_zero_extend_andi_ori"
+  (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)
+                    (ior:DI (reg:DI a0)
+                            (const_int 16))))
+      (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_ori"
+}
-- 
2.52.0

Reply via email to