From: Loeka Rogge <[email protected]>
RPX-100 allows a mulh(s/u) followed by a mul operation to be bonded,
saving 3 cycles in the multiplier and allowing the mul's result to be
used 1 cycle earlier than expected.
- Added RISCV_FUSE_BONDED_MUL fusion
- riscv_sched_adjust_cost reflects the mul result being available 1
cycle sooner in this case
Regtested on rv32gc, rv64gc and with rhx100 config and mtune.
gcc/ChangeLog:
* config/riscv/riscv-cores.def (RISCV_TUNE): Make rpx100 a
separate tune instead of reusing rhx100 tune
* config/riscv/riscv-fusion.cc (riscv_set_is_multiply_high_pattern_p):
Check if a set is a mulh(s/u) pattern.
(riscv_get_mulh_operands): Get operands from a mulh(s/u)
pattern.
(riscv_fuse_bonded_mul): Check for bonded mul pairs to fuse.
(riscv_depends_on_lowpart_bonded_mul_p): check if dependency is
on low part of a bonded mul operation.
* config/riscv/riscv-protos.h (enum riscv_fusion_pairs): Add
RISCV_FUSE_BONDED_MUL fusion
(riscv_depends_on_lowpart_bonded_mul_p): Add function header
for use in riscv.cc .
* config/riscv/riscv.cc (riscv_sched_adjust_cost): Adjust cost
for rpx100 bonded mul.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/fusion-bonded-mul-rtl.c: New test.
Signed-off-by: Loeka Rogge <[email protected]>
---
Changes since v1:
- riscv_depends_on_lowpart_bonded_mul_p now checks SCHED_GROUP_P (prev_insn)
- Removed changes to RTL patterns
gcc/config/riscv/riscv-cores.def | 2 +-
gcc/config/riscv/riscv-fusion.cc | 143 +++++++++++++++
gcc/config/riscv/riscv-protos.h | 2 +
gcc/config/riscv/riscv.cc | 29 ++-
.../gcc.target/riscv/fusion-bonded-mul-rtl.c | 165 ++++++++++++++++++
5 files changed, 339 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-bonded-mul-rtl.c
diff --git a/gcc/config/riscv/riscv-cores.def b/gcc/config/riscv/riscv-cores.def
index f330c790de7..05994333ef6 100644
--- a/gcc/config/riscv/riscv-cores.def
+++ b/gcc/config/riscv/riscv-cores.def
@@ -58,7 +58,7 @@ RISCV_TUNE("spacemit-x60", spacemit_x60,
spacemit_x60_tune_info)
RISCV_TUNE("spacemit-x100", generic_ooo, spacemit_x100_tune_info)
RISCV_TUNE("spacemit-a100", spacemit_x60, spacemit_x60_tune_info)
RISCV_TUNE("arc-v-rhx-100-series", arcv_rhx100, arcv_rhx100_tune_info)
-RISCV_TUNE("arc-v-rpx-100-series", arcv_rpx100, arcv_rhx100_tune_info)
+RISCV_TUNE("arc-v-rpx-100-series", arcv_rpx100, arcv_rpx100_tune_info)
RISCV_TUNE("generic-ooo", generic_ooo, generic_ooo_tune_info)
RISCV_TUNE("size", generic, optimize_size_tune_info)
RISCV_TUNE("mips-p8700", mips_p8700, mips_p8700_tune_info)
diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index f04dfe52e20..7caae64c5b4 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -147,6 +147,38 @@ riscv_set_is_shNadduw_p (rtx set)
&& REG_P (SET_DEST (set)));
}
+/* Check if RTX matches multiply-high pattern, where X is the word mode:
+ (set (reg:X DEST)
+ (truncate:X (lshiftrt:2X (mult:2X (extend:2X (OP0))
+ (extend:2X (OP1)))
+ (const_int BITS_PER_WORD))) */
+
+static bool
+riscv_set_is_multiply_high_pattern_p (rtx src)
+{
+ if (GET_CODE (src) != SET)
+ return false;
+ rtx truncate = SET_SRC (src);
+
+ if (GET_CODE (truncate) != TRUNCATE)
+ return false;
+
+ rtx lshiftrt = XEXP (truncate, 0);
+ if (GET_CODE (lshiftrt) != LSHIFTRT)
+ return false;
+
+ rtx shift_amount = XEXP (lshiftrt, 1);
+ rtx mult = XEXP (lshiftrt, 0);
+
+ if (!CONST_INT_P (shift_amount) || INTVAL (shift_amount) != BITS_PER_WORD)
+ return false;
+
+ if (GET_CODE (mult) != MULT)
+ return false;
+
+ return true;
+}
+
/* Check the common RTL for ZEXTW, ZEXTWS and ZEXTH fusion. */
static bool
@@ -744,6 +776,115 @@ riscv_fuse_b_alui (rtx_insn *prev, rtx_insn *curr)
return false;
}
+/* Extract register operands and destination from multiply high-part pattern.
+ Operands are returned in the order of the assembly instruction. The usmul
+ pattern emits the operands in the reverse order.
+ Returns true if successful. */
+
+static bool
+riscv_get_mulh_operands (rtx set_rtx, rtx *op0, rtx *op1, rtx *dest)
+{
+ if (!riscv_set_is_multiply_high_pattern_p (set_rtx))
+ return false;
+
+ rtx mulh_pattern = SET_SRC (set_rtx);
+
+ rtx lshiftrt = XEXP (mulh_pattern, 0);
+ rtx mult = XEXP (lshiftrt, 0);
+
+ rtx operand0 = XEXP (mult, 0);
+ rtx operand1 = XEXP (mult, 1);
+
+ /* Swap operands for usmul pattern. */
+ if (GET_CODE (operand0) == ZERO_EXTEND
+ && GET_CODE (operand1) == SIGN_EXTEND)
+ std::swap (operand0, operand1);
+
+ /* Strip zero_extend/sign_extend. */
+ if (GET_CODE (operand0) == ZERO_EXTEND || GET_CODE (operand0) == SIGN_EXTEND)
+ operand0 = XEXP (operand0, 0);
+ if (GET_CODE (operand1) == ZERO_EXTEND || GET_CODE (operand1) == SIGN_EXTEND)
+ operand1 = XEXP (operand1, 0);
+
+ /* Operands must be low part of reg:TI. */
+ if (!subreg_lowpart_p (operand0) || !subreg_lowpart_p (operand1))
+ return false;
+
+ /* Strip subreg. */
+ if (GET_CODE (operand0) == SUBREG)
+ operand0 = SUBREG_REG (operand0);
+ if (GET_CODE (operand1) == SUBREG)
+ operand1 = SUBREG_REG (operand1);
+
+ if (!REG_P (operand0) || !REG_P (operand1))
+ return false;
+
+ rtx dest_reg = SET_DEST (set_rtx);
+ if (!REG_P (dest_reg))
+ return false;
+
+ *op0 = operand0;
+ *op1 = operand1;
+ *dest = dest_reg;
+ return true;
+}
+
+/* Check for RISCV_FUSE_BONDED_MUL fusion.
+ prev (mulh/mulhu/mulhsu) and curr (mul) of the same inputs, with the
+ high-part destination distinct from both inputs. */
+
+static bool
+riscv_fuse_bonded_mul (rtx_insn *prev, rtx_insn *curr)
+{
+ rtx prev_set = single_set (prev);
+ rtx curr_set = single_set (curr);
+
+ /* Bond mul hi part and mul low part pair:
+ prev: (set rd_hi (mulh(u/s) rs1 rs2))
+ curr: (set rd_lo (mul rs1 rs2))
+ where rd_hi != rs1 && rd_hi != rs2. */
+ if (prev_set && curr_set
+ && GET_CODE (SET_SRC (curr_set)) == MULT)
+ {
+ rtx prev_op0, prev_op1, prev_dest;
+ if (riscv_get_mulh_operands (prev_set, &prev_op0, &prev_op1, &prev_dest))
+ {
+ rtx curr_src = SET_SRC (curr_set);
+
+ rtx curr_op0 = XEXP (curr_src, 0);
+ rtx curr_op1 = XEXP (curr_src, 1);
+
+ /* Check that the operands are the same and in the same order
+ and check the prev destination is distinct from the operands. */
+ if (REG_P (prev_op0) && REG_P (prev_op1)
+ && REG_P (curr_op0) && REG_P (curr_op1)
+ && REGNO (prev_op0) == REGNO (curr_op0)
+ && REGNO (prev_op1) == REGNO (curr_op1)
+ && REGNO (prev_dest) != REGNO (prev_op0)
+ && REGNO (prev_dest) != REGNO (prev_op1))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+riscv_depends_on_lowpart_bonded_mul_p (int dep_type, rtx_insn *dep_insn)
+{
+ if (dep_type != REG_DEP_TRUE)
+ return false;
+
+ rtx_insn *prev_insn = prev_nonnote_nondebug_insn (dep_insn);
+ if (!prev_insn || !INSN_P (prev_insn) || SCHED_GROUP_P (prev_insn))
+ return false;
+
+ if (!riscv_fuse_bonded_mul (prev_insn, dep_insn))
+ return false;
+
+ return true;
+}
+
/* Type for a fusion checker function. Takes the two candidate insns
and returns true if they should be fused. */
@@ -793,6 +934,8 @@ static const struct riscv_fusion_entry riscv_fusion_table[]
=
riscv_fuse_bfext, "RISCV_FUSE_BFEXT" },
{ RISCV_FUSE_B_ALUI,
riscv_fuse_b_alui, "RISCV_FUSE_B_ALUI" },
+ { RISCV_FUSE_BONDED_MUL,
+ riscv_fuse_bonded_mul, "RISCV_FUSE_BONDED_MUL" },
};
/* Return the name of fusion operation OP. */
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 07a1da94605..648cc97e005 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_BFEXT = HOST_WIDE_INT_1U << 10,
RISCV_FUSE_EXPANDED_LD = HOST_WIDE_INT_1U << 11,
RISCV_FUSE_B_ALUI = HOST_WIDE_INT_1U << 12,
+ RISCV_FUSE_BONDED_MUL = HOST_WIDE_INT_1U << 13,
};
extern bool riscv_macro_fusion_p (void);
@@ -903,6 +904,7 @@ extern const char *th_output_move (rtx, rtx);
extern bool th_print_operand_address (FILE *, machine_mode, rtx);
#endif
+extern bool riscv_depends_on_lowpart_bonded_mul_p (int, rtx_insn *);
extern bool arcv_mpy_1c_bypass_p (rtx_insn *, rtx_insn *);
extern bool arcv_mpy_2c_bypass_p (rtx_insn *, rtx_insn *);
extern bool arcv_mpy_10c_bypass_p (rtx_insn *, rtx_insn *);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index d83e26351c9..c8e7a533312 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -1087,6 +1087,30 @@ static const struct riscv_tune_param
arcv_rhx100_tune_info = {
true, /* prefer-agnostic. */
};
+/* Same pipeline costs as RHX-100. Fuse bonded mulh/mul pairs. */
+static const struct riscv_tune_param arcv_rpx100_tune_info = {
+ {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_add */
+ {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_mul */
+ {COSTS_N_INSNS (20), COSTS_N_INSNS (20)}, /* fp_div */
+ {COSTS_N_INSNS (4), COSTS_N_INSNS (4)}, /* int_mul */
+ {COSTS_N_INSNS (27), COSTS_N_INSNS (43)}, /* int_div */
+ 4, /* issue_rate */
+ 9, /* branch_cost */
+ 2, /* memory_cost */
+ 8, /* fmv_cost */
+ false, /* slow_unaligned_access */
+ false, /* vector_unaligned_access */
+ true, /* use_divmod_expansion
*/
+ false, /* overlap_op_by_pieces */
+ true, /* use_zero_stride_load
*/
+ false, /* speculative_sched_vsetvl */
+ RISCV_FUSE_BONDED_MUL, /* fusible_ops */
+ NULL, /* vector cost */
+ NULL, /* function_align */
+ NULL, /* jump_align */
+ NULL, /* loop_align */
+ true, /* prefer-agnostic. */
+};
/* Costs to use when optimizing for Synopsys RMX-100. */
static const struct riscv_tune_param arcv_rmx100_tune_info = {
@@ -12088,9 +12112,12 @@ riscv_get_fusible_ops (void)
we currently only perform the adjustment when -madjust-lmul-cost is given.
*/
static int
-riscv_sched_adjust_cost (rtx_insn *, int, rtx_insn *insn, int cost,
+riscv_sched_adjust_cost (rtx_insn *, int dep_type, rtx_insn *insn, int cost,
unsigned int)
{
+ if (riscv_microarchitecture == arcv_rpx100
+ && riscv_depends_on_lowpart_bonded_mul_p (dep_type, insn))
+ return cost - 1;
/* Only do adjustments for the generic out-of-order and spacemit_x60
scheduling model. */
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-bonded-mul-rtl.c
b/gcc/testsuite/gcc.target/riscv/fusion-bonded-mul-rtl.c
new file mode 100644
index 00000000000..27de0cb60f1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-bonded-mul-rtl.c
@@ -0,0 +1,165 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target rv64 } */
+/* { dg-skip-if "" { *-*-* } { "-g" "-flto" "-O0" "-O1" "-Oz" "-Os" } } */
+/* { dg-options "-mtune=arc-v-rpx-100-series -march=rv64im_zbs -mabi=lp64
-fdump-rtl-sched2" } */
+
+__int128 __RTL (startwith ("sched2"))
+foo (void)
+{
+(function "foo"
+ (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 a4)
+ (truncate:DI
+ (lshiftrt:TI
+ (mult:TI (sign_extend:TI (reg:DI a0))
+ (sign_extend:TI (reg:DI a1)))
+ (const_int 64)))))
+ (cinsn 4 (set (reg:DI a5)
+ (mult:DI (reg:DI a0)
+ (reg:DI a1))))
+ (cinsn 5 (set (reg:DI a0)
+ (plus:DI (reg:DI a5)
+ (const_int 1))))
+ (cinsn 6 (set (reg:DI t0)
+ (ltu:DI (reg:DI a0)
+ (reg:DI a5))))
+ (cinsn 7 (set (reg:DI a1)
+ (plus:DI (reg:DI t0)
+ (reg:DI a4))))
+ (cinsn 8 (use (reg/i:TI a0)))
+ (cjump_insn 9 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 10)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx (reg/i:TI a0))
+ ) ;; crtl
+) ;; function "foo"
+}
+
+__int128 __RTL (startwith ("sched2"))
+bar (void)
+{
+(function "bar"
+ (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 a4)
+ (truncate:DI
+ (lshiftrt:TI
+ (mult:TI (zero_extend:TI (reg:DI a1))
+ (sign_extend:TI (reg:DI a0)))
+ (const_int 64)))))
+ (cinsn 4 (set (reg:DI a5)
+ (mult:DI (reg:DI a0)
+ (reg:DI a1))))
+ (cinsn 5 (set (reg:DI a0)
+ (plus:DI (reg:DI a5)
+ (const_int 1))))
+ (cinsn 6 (set (reg:DI t0)
+ (ltu:DI (reg:DI a0)
+ (reg:DI a5))))
+ (cinsn 7 (set (reg:DI a1)
+ (plus:DI (reg:DI t0)
+ (reg:DI a4))))
+ (cinsn 8 (use (reg/i:TI a0)))
+ (cjump_insn 9 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 10)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx (reg/i:TI a0))
+ ) ;; crtl
+) ;; function "bar"
+}
+
+__int128 __RTL (startwith ("sched2"))
+baz (void)
+{
+(function "baz"
+ (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 a4)
+ (truncate:DI
+ (lshiftrt:TI
+ (mult:TI (zero_extend:TI (reg:DI a0))
+ (zero_extend:TI (reg:DI a1)))
+ (const_int 64)))))
+ (cinsn 4 (set (reg:DI a5)
+ (mult:DI (reg:DI a0)
+ (reg:DI a1))))
+ (cinsn 5 (set (reg:DI a0)
+ (plus:DI (reg:DI a5)
+ (const_int 1))))
+ (cinsn 6 (set (reg:DI t0)
+ (ltu:DI (reg:DI a0)
+ (reg:DI a5))))
+ (cinsn 7 (set (reg:DI a1)
+ (plus:DI (reg:DI t0)
+ (reg:DI a4))))
+ (cinsn 8 (use (reg/i:TI a0)))
+ (cjump_insn 9 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 10)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx (reg/i:TI a0))
+ ) ;; crtl
+) ;; function "baz"
+}
+
+__int128 __RTL (startwith ("sched2"))
+quux (void)
+{
+(function "quux"
+ (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 a4)
+ (truncate:DI
+ (lshiftrt:TI
+ (mult:TI (zero_extend:TI (reg:DI a0))
+ (sign_extend:TI (reg:DI a1)))
+ (const_int 64)))))
+ (cinsn 4 (set (reg:DI a5)
+ (mult:DI (reg:DI a1)
+ (reg:DI a0))))
+ (cinsn 5 (set (reg:DI a0)
+ (plus:DI (reg:DI a5)
+ (const_int 1))))
+ (cinsn 6 (set (reg:DI t0)
+ (ltu:DI (reg:DI a0)
+ (reg:DI a5))))
+ (cinsn 7 (set (reg:DI a1)
+ (plus:DI (reg:DI t0)
+ (reg:DI a4))))
+ (cinsn 8 (use (reg/i:TI a0)))
+ (cjump_insn 9 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 10)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx (reg/i:TI a0))
+ ) ;; crtl
+) ;; function "quux"
+}
+
+/* { dg-final { scan-rtl-dump "(?n)0-->.*a4=trn\\(sxn\\(a0\\)\\*sxn\\(a1\\)
0>>0x40\\).*\n.*4-->.*a5=a0\\*a1.*\n.*9-->.*a0=a5\\+0x1.*" "sched2" } } */
+/* { dg-final { scan-rtl-dump "(?n)0-->.*a4=trn\\(zxn\\(a1\\)\\*sxn\\(a0\\)
0>>0x40\\).*\n.*4-->.*a5=a0\\*a1.*\n.*9-->.*a0=a5\\+0x1.*" "sched2" } } */
+/* { dg-final { scan-rtl-dump "(?n)0-->.*a4=trn\\(zxn\\(a0\\)\\*zxn\\(a1\\)
0>>0x40\\).*\n.*4-->.*a5=a0\\*a1.*\n.*9-->.*a0=a5\\+0x1.*" "sched2" } } */
+/* { dg-final { scan-rtl-dump "(?n)0-->.*a4=trn\\(zxn\\(a0\\)\\*sxn\\(a1\\)
0>>0x40\\).*\n.*4-->.*a5=a1\\*a0.*\n.*9-->.*a0=a5\\+0x1.*" "sched2" } } */
--
2.47.3