The sched_fusion pass can bring independent memory accesses together
before sched2. Add a priority hook that groups scalar load/store-pair
candidates by register file, access kind, mode and base register, then
orders each group by offset. Prefer decreasing offsets when both
directions are available, matching frame save/restore memory order.
Enable scheduling fusion by default for load/store-pair tunes while
preserving an explicit -f[no-]schedule-fusion option. Keep the positive
test as XFAIL until the tuning patch enables the pairs.
Static sched2 macro-fusion counts for SPEC CPU 2017 Integer built with
-O3 -flto, measured independently of ready-list reordering against a
baseline with xt-c9501fdvt fusion pairs enabled:
Benchmark Baseline With patch Change
500.perlbench_r 15302 17077 +11.6%
502.gcc_r 72806 78135 +7.3%
505.mcf_r 63 85 +34.9%
520.omnetpp_r 20479 22084 +7.8%
523.xalancbmk_r 42442 44651 +5.2%
525.x264_r 6232 7033 +12.9%
531.deepsjeng_r 299 328 +9.7%
541.leela_r 794 851 +7.2%
548.exchange2_r 353 426 +20.7%
557.xz_r 1217 1321 +8.5%
SUM 159987 171991 +7.5%
gcc/ChangeLog:
* config/riscv/riscv-fusion.cc (riscv_sched_fusion_priority): New
function.
* config/riscv/riscv-protos.h (riscv_sched_fusion_priority):
Declare.
* config/riscv/riscv.cc (riscv_override_options_internal): Set the
scheduling-fusion default for the current tune.
(TARGET_SCHED_FUSION_PRIORITY): Define.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/sched-fusion-priority.c: New test.
Signed-off-by: Jin Ma <[email protected]>
---
gcc/config/riscv/riscv-fusion.cc | 68 +++++++++++++++++++
gcc/config/riscv/riscv-protos.h | 1 +
gcc/config/riscv/riscv.cc | 10 +++
.../gcc.target/riscv/sched-fusion-priority.c | 49 +++++++++++++
4 files changed, 128 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/sched-fusion-priority.c
diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index 332f64bd7cc..06cdab5694d 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -907,6 +907,74 @@ riscv_fuse_ldst_pair_p (rtx_insn *prev, rtx_insn *curr,
return diff == access_size;
}
+/* Implement TARGET_SCHED_FUSION_PRIORITY. Group load/store pair candidates
+ by register file, access kind, mode and base register, then by offset. */
+
+void
+riscv_sched_fusion_priority (rtx_insn *insn, int max_pri,
+ int *fusion_pri, int *pri)
+{
+ struct riscv_fusion_mem_info mem;
+ unsigned HOST_WIDE_INT fusible_ops;
+ enum riscv_fusion_pairs inc_op, dec_op;
+ HOST_WIDE_INT access_size;
+ unsigned int base_regno;
+ bool isload_p, fp_p, inc_p;
+ int fusion_type, tmp;
+
+ gcc_assert (INSN_P (insn));
+
+ tmp = max_pri - 1;
+ *fusion_pri = tmp;
+ *pri = tmp;
+
+ if (!riscv_fuse_mem_p (insn, &mem)
+ || mem.type == SCHED_FUSION_LD_ZERO_EXTEND
+ || mem.addr.type != ADDRESS_REG
+ || !CONST_INT_P (mem.addr.offset))
+ return;
+
+ base_regno = riscv_regno (mem.addr.reg);
+ if (base_regno >= FIRST_PSEUDO_REGISTER)
+ return;
+
+ isload_p = mem.type != SCHED_FUSION_ST;
+ fp_p = mem.fp_p;
+ access_size = GET_MODE_SIZE (mem.mode).to_constant ();
+ if (access_size != 4 && access_size != 8)
+ return;
+
+ inc_op = fp_p ? RISCV_FUSE_FLDFST_PAIR_INC : RISCV_FUSE_LDST_PAIR_INC;
+ dec_op = fp_p ? RISCV_FUSE_FLDFST_PAIR_DEC : RISCV_FUSE_LDST_PAIR_DEC;
+ fusible_ops = riscv_get_fusible_ops ();
+ if (!(fusible_ops & (inc_op | dec_op)))
+ return;
+
+ /* Prefer decreasing offsets to match frame save/restore order. */
+ inc_p = !(fusible_ops & dec_op);
+
+ /* Give each load/store class and base register a distinct priority below
+ that of unrelated instructions. */
+ fusion_type = (fp_p ? 4 : 0) + (isload_p ? 0 : 2);
+ fusion_type += access_size == 8;
+ fusion_type++;
+ *fusion_pri -= (fusion_type * FIRST_PSEUDO_REGISTER
+ + (int) base_regno);
+
+ tmp /= 2;
+ HOST_WIDE_INT off_val = INTVAL (mem.addr.offset);
+ unsigned HOST_WIDE_INT magnitude = off_val < 0
+ ? -(unsigned HOST_WIDE_INT) off_val
+ : off_val;
+ int offset_pri = magnitude & 0xfffff;
+
+ /* Order offsets in the preferred pair direction. */
+ if (inc_p == (off_val >= 0))
+ *pri = tmp - offset_pri;
+ else
+ *pri = tmp + offset_pri;
+}
+
/* Check the common RTL for ZEXTW, ZEXTWS and ZEXTH fusion. */
static bool
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index cf36efccf75..0921b3a7618 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -884,6 +884,7 @@ enum riscv_fusion_pairs
extern bool riscv_macro_fusion_p (void);
extern bool riscv_macro_fusion_pair_p (rtx_insn *, rtx_insn *);
+extern void riscv_sched_fusion_priority (rtx_insn *, int, int *, int *);
extern unsigned HOST_WIDE_INT riscv_get_fusible_ops (void);
/* Routines implemented in thead.cc. */
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index a2a51c019ec..9f0c1d704c2 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -12121,6 +12121,14 @@ riscv_override_options_internal (struct gcc_options
*opts)
? &optimize_size_tune_info
: cpu->tune_param;
+ /* Enable scheduling fusion for load/store-pair tunes. */
+ if (!OPTION_SET_P (flag_schedule_fusion))
+ opts->x_flag_schedule_fusion
+ = (riscv_get_fusible_ops ()
+ & (RISCV_FUSE_LDST_PAIR_INC | RISCV_FUSE_LDST_PAIR_DEC
+ | RISCV_FUSE_FLDFST_PAIR_INC | RISCV_FUSE_FLDFST_PAIR_DEC))
+ != RISCV_FUSE_NOTHING;
+
/* If not optimizing for size, set the default
alignment to what the target wants. */
if (!opts->x_optimize_size)
@@ -16617,6 +16625,8 @@ riscv_memtag_tag_bitsize ()
#define TARGET_SCHED_MACRO_FUSION_P riscv_macro_fusion_p
#undef TARGET_SCHED_MACRO_FUSION_PAIR_P
#define TARGET_SCHED_MACRO_FUSION_PAIR_P riscv_macro_fusion_pair_p
+#undef TARGET_SCHED_FUSION_PRIORITY
+#define TARGET_SCHED_FUSION_PRIORITY riscv_sched_fusion_priority
#undef TARGET_SCHED_INIT
#define TARGET_SCHED_INIT riscv_sched_init
diff --git a/gcc/testsuite/gcc.target/riscv/sched-fusion-priority.c
b/gcc/testsuite/gcc.target/riscv/sched-fusion-priority.c
new file mode 100644
index 00000000000..3d2c100a408
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/sched-fusion-priority.c
@@ -0,0 +1,49 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2" } */
+/* { dg-additional-options "-fno-schedule-insns2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Start before sched_fusion and disable sched2 to isolate the priority
+ hook. */
+
+/*
+**sched_fusion_load_pair: { xfail *-*-* }
+** ...
+** ld [a-z][0-9]+,8\(a0\)
+** ld [a-z][0-9]+,0\(a0\)
+** ...
+** ret
+*/
+long __RTL (startwith ("compgotos"))
+sched_fusion_load_pair (void)
+{
+(function "sched_fusion_load_pair"
+ (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 a1)
+ (mem:DI (reg:DI a0) [0 S8 A64])))
+ (cinsn 4 (set (reg:DI a3)
+ (plus:DI (reg:DI a4) (reg:DI a5))))
+ (cinsn 5 (set (reg:DI a2)
+ (mem:DI
+ (plus:DI (reg:DI a0)
+ (const_int 8)) [0 S8 A64])))
+ (cinsn 6 (use (reg:DI a1)))
+ (cinsn 7 (use (reg:DI a2)))
+ (cinsn 8 (use (reg:DI a3)))
+ (cjump_insn 9 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 10)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx
+ (reg/i:DI a0)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "sched_fusion_load_pair"
+}
--
2.52.0