[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