[PATCH v5 08/18] RISC-V: Add LOGIC_LOGIC macro-fusion recognition

Jin Ma <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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
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.