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