[PATCH v2 2/4] RISC-V: Implement TARGET_SCHED_FUSION_PRIORITY for ARC-V RHX-100 series
Luis Silva <[email protected]> Wed, 5 Aug 2026 11:02:10 +0100
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <LO6P265MB622433B249E6313C7EAF7A45B4D32@LO6P265MB6224.GBRP265.PROD.OUTLOOK.COM> |
From: Luis Silva <[email protected]> To take better advantage of double load/store fusion, make use of the sched_fusion pass that assigns unique "fusion priorities" to load/store instructions and schedules operations on adjacent addresses together. This maximizes the probability that loads/stores are fused with each other rather than with other instructions. The ARC-V specific helpers live in a new arcv.cc; the generic wrapper riscv_sched_fusion_priority in riscv-fusion.cc dispatches to it and falls back to a default priority for other tunes. gcc/ChangeLog: * config.gcc: Build arcv.o. * config/riscv/riscv-fusion.cc (riscv_sched_fusion_priority): New. (riscv_adjacent_memops_p): Call arcv_pair_fusion_mode_allowed_p. * config/riscv/riscv-protos.h (riscv_sched_fusion_priority): Declare. (arcv_pair_fusion_mode_allowed_p): Declare. (arcv_sched_fusion_priority): Declare. * config/riscv/riscv.cc (TARGET_SCHED_FUSION_PRIORITY): Define. * config/riscv/t-riscv: Build arcv.o. * config/riscv/arcv.cc: New file. Co-authored-by: Artemiy Volkov <[email protected]> Co-authored-by: Michiel Derhaeg <[email protected]> Signed-off-by: Luis Silva <[email protected]> --- gcc/config.gcc | 2 +- gcc/config/riscv/arcv.cc | 162 +++++++++++++++++++++++++++++++ gcc/config/riscv/riscv-fusion.cc | 31 +++--- gcc/config/riscv/riscv-protos.h | 5 + gcc/config/riscv/riscv.cc | 3 + gcc/config/riscv/t-riscv | 6 ++ 6 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 gcc/config/riscv/arcv.cc diff --git a/gcc/config.gcc b/gcc/config.gcc index 0477bf22c8a..89e2ba3cee7 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -574,7 +574,7 @@ riscv*) extra_objs="riscv-builtins.o riscv-c.o riscv-sr.o riscv-shorten-memrefs.o riscv-selftests.o riscv-string.o" extra_objs="${extra_objs} riscv-v.o riscv-vsetvl.o riscv-vector-costs.o riscv-avlprop.o riscv-vect-permconst.o" extra_objs="${extra_objs} riscv-vector-builtins.o riscv-vector-builtins-shapes.o riscv-vector-builtins-bases.o sifive-vector-builtins-bases.o andes-vector-builtins-bases.o" - extra_objs="${extra_objs} thead.o riscv-target-attr.o riscv-zicfilp.o riscv-bclr-lowest-set-bit.o riscv-opt-popretz.o riscv-fusion.o" + extra_objs="${extra_objs} thead.o riscv-target-attr.o riscv-zicfilp.o riscv-bclr-lowest-set-bit.o riscv-opt-popretz.o riscv-fusion.o arcv.o" d_target_objs="riscv-d.o" extra_headers="riscv_vector.h riscv_crypto.h riscv_bitmanip.h riscv_th_vector.h sifive_vector.h andes_vector.h" target_gtfiles="$target_gtfiles \$(srcdir)/config/riscv/riscv-vector-builtins.cc" diff --git a/gcc/config/riscv/arcv.cc b/gcc/config/riscv/arcv.cc new file mode 100644 index 00000000000..e29712ca2be --- /dev/null +++ b/gcc/config/riscv/arcv.cc @@ -0,0 +1,162 @@ +/* Subroutines used for code generation for ARC-V processors. + Copyright (C) 2026 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#define IN_TARGET_CODE 1 + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "tree.h" +#include "memmodel.h" +#include "tm.h" +#include "optabs.h" +#include "regs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "stor-layout.h" +#include "alias.h" +#include "fold-const.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "explow.h" +#include "calls.h" +#include "varasm.h" +#include "expr.h" +#include "tm_p.h" +#include "df.h" +#include "reload.h" +#include "sched-int.h" +#include "tm-constrs.h" + +/* If INSN is a load or store of address in the form of [base+offset], + extract the two parts and set to BASE and OFFSET. IS_LOAD is set + to TRUE if it's a load. Return TRUE if INSN is such an instruction, + otherwise return FALSE. */ + +static bool +arcv_fusion_load_store (rtx_insn *insn, rtx *base, rtx *offset, + machine_mode *mode, bool *is_load) +{ + rtx x, dest, src; + + gcc_assert (INSN_P (insn)); + x = PATTERN (insn); + if (GET_CODE (x) != SET) + return false; + + src = SET_SRC (x); + dest = SET_DEST (x); + + if ((GET_CODE (src) == SIGN_EXTEND || GET_CODE (src) == ZERO_EXTEND) + && MEM_P (XEXP (src, 0))) + src = XEXP (src, 0); + + if (REG_P (src) && MEM_P (dest)) + { + *is_load = false; + if (extract_base_offset_in_addr (dest, base, offset)) + *mode = GET_MODE (dest); + } + else if (MEM_P (src) && REG_P (dest)) + { + *is_load = true; + if (extract_base_offset_in_addr (src, base, offset)) + *mode = GET_MODE (src); + } + else + return false; + + return (*base != NULL_RTX && *offset != NULL_RTX); +} + +/* Return TRUE if the target microarchitecture supports macro-op + fusion for two memory operations of mode MODE (the direction + of transfer is determined by the IS_LOAD parameter). */ + +bool +arcv_pair_fusion_mode_allowed_p (machine_mode mode, bool is_load) +{ + return ((is_load && (mode == SImode + || mode == HImode + || mode == QImode)) + || (!is_load && mode == SImode)); +} + +bool +arcv_sched_fusion_priority (rtx_insn *insn, int max_pri, int *fusion_pri, + int *pri) +{ + rtx base, offset; + machine_mode mode = SImode; + bool is_load; + + gcc_assert (INSN_P (insn)); + + /* Default priority for non-fusible instructions. */ + int default_pri = max_pri - 1; + + /* Check if this is a fusible load/store instruction. */ + if (!arcv_fusion_load_store (insn, &base, &offset, &mode, &is_load) + || !arcv_pair_fusion_mode_allowed_p (mode, is_load)) + return false; + + /* Start with half the default priority to distinguish fusible from + non-fusible instructions. */ + int priority = default_pri / 2; + + /* Scale priority by access width - narrower accesses get lower priority. + HImode: divide by 2, QImode: divide by 4. This encourages wider + accesses to be scheduled together. */ + if (mode == HImode) + priority /= 2; + else if (mode == QImode) + priority /= 4; + + /* Factor in base register: instructions with smaller register numbers + get higher priority. The shift by 20 bits ensures this is the most + significant component of the priority. */ + const int BASE_REG_SHIFT = 20; + const int BASE_REG_MASK = 0xff; + priority -= ((REGNO (base) & BASE_REG_MASK) << BASE_REG_SHIFT); + + /* Calculate fusion priority: group loads/stores with adjacent addresses + into the same scheduling group. We divide the offset by (mode_size * 2) + to group pairs of adjacent accesses, then shift left by 1 to make room + for the load/store bit. */ + int off_val = (int)(INTVAL (offset)); + int addr_group = off_val / (GET_MODE_SIZE (mode).to_constant () * 2); + *fusion_pri = priority - (addr_group << 1) + is_load; + + /* Factor in the actual offset value: instructions with smaller offsets + get higher priority. We use only the lower 20 bits to avoid overflow. */ + const int OFFSET_MASK = 0xfffff; + if (off_val >= 0) + priority -= (off_val & OFFSET_MASK); + else + priority += ((-off_val) & OFFSET_MASK); + + *pri = priority; + + return true; +} diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc index 98df3521029..a60d001d4d7 100644 --- a/gcc/config/riscv/riscv-fusion.cc +++ b/gcc/config/riscv/riscv-fusion.cc @@ -156,6 +156,19 @@ riscv_set_is_shNadduw_p (rtx set) && REG_P (SET_DEST (set))); } +void +riscv_sched_fusion_priority (rtx_insn *insn, int max_pri, int *fusion_pri, + int *pri) +{ + if (TARGET_ARCV_RHX100 + && arcv_sched_fusion_priority (insn, max_pri, fusion_pri, pri)) + return; + + /* Default priority. */ + *pri = max_pri - 1; + *fusion_pri = max_pri - 1; +} + /* Return TRUE if two memory operands can be fused based on their addresses. Checks if MEM0 and MEM1 have the same base register with adjacent offsets, making them suitable for fusion (e.g., adjacent load/store pairs). */ @@ -178,21 +191,9 @@ riscv_adjacent_memops_p (rtx mem0, rtx mem1, bool is_load) if (GET_MODE (mem0) != GET_MODE (mem1)) return false; - /* Check if the mode is allowed for ARC-V fusion restrictions. - Loads: allow SI, HI, and QI modes. - Stores: allow only SI mode. */ - if (TARGET_ARCV_RHX100) - { - machine_mode mode = GET_MODE (mem0); - bool mode_allowed = ((is_load - && (mode == SImode - || mode == HImode - || mode == QImode)) - || (!is_load && mode == SImode)); - - if (!mode_allowed) - return false; - } + if (TARGET_ARCV_RHX100 + && !arcv_pair_fusion_mode_allowed_p (GET_MODE (mem0), is_load)) + return false; rtx mem_addr0 = XEXP (mem0, 0); rtx mem_addr1 = XEXP (mem1, 0); diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h index a94c37fa052..4158af2fd1a 100644 --- a/gcc/config/riscv/riscv-protos.h +++ b/gcc/config/riscv/riscv-protos.h @@ -882,6 +882,7 @@ extern bool riscv_macro_fusion_p (void); extern bool riscv_macro_fusion_pair_p (rtx_insn *, rtx_insn *); extern bool riscv_fusion_enabled_p (enum riscv_fusion_pairs); extern unsigned int riscv_get_fusible_ops (void); +extern void riscv_sched_fusion_priority (rtx_insn *, int, int *, int *); /* Routines implemented in thead.cc. */ extern bool extract_base_offset_in_addr (rtx, rtx *, rtx *); @@ -913,6 +914,10 @@ 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 *); +/* Routines implemented in arcv.cc. */ +extern bool arcv_pair_fusion_mode_allowed_p (machine_mode, bool); +extern bool arcv_sched_fusion_priority (rtx_insn *, int, int *, int *); + extern bool strided_load_broadcast_p (void); extern bool riscv_prefer_agnostic_p (void); extern bool riscv_use_divmod_expander (void); diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index aabd12aa040..d08a4539536 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -16590,6 +16590,9 @@ riscv_memtag_tag_bitsize () #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/config/riscv/t-riscv b/gcc/config/riscv/t-riscv index 2a4a2ccab50..0712833e219 100644 --- a/gcc/config/riscv/t-riscv +++ b/gcc/config/riscv/t-riscv @@ -194,6 +194,12 @@ riscv-zicfilp.o: $(srcdir)/config/riscv/riscv-zicfilp.cc \ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ $(srcdir)/config/riscv/riscv-zicfilp.cc +arcv.o: $(srcdir)/config/riscv/arcv.cc \ + $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TARGET_H) $(BACKEND_H) $(RTL_H) \ + memmodel.h $(EMIT_RTL_H) $(TM_H) output.h + $(COMPILE) $< + $(POSTCOMPILE) + PASSES_EXTRA += $(srcdir)/config/riscv/riscv-passes.def $(common_out_file): $(srcdir)/config/riscv/riscv-cores.def \ -- 2.47.3