This patch introduces a conservative loop unrolling heuristic for the
RISC-V backend, controlled by the new -munroll-only-small-loops option.
The option is disabled by default. When enabled together with
-funroll-loops, only small loops (those whose body does not exceed a
per-microarchitecture instruction threshold) are unrolled, so that tight
loops benefit from unrolling without exposing larger loops to its
code-size and instruction-cache costs. The size threshold and the
maximum unroll factor come from the -mtune tuning parameters.
gcc/ChangeLog:
* config/riscv/riscv.cc (struct riscv_tune_param): Add fields
unroll_small_loop_ninsns and unroll_small_loop_factor.
(xt_c9501_tune_info): Set them.
(riscv_loop_unroll_adjust): New function.
(TARGET_LOOP_UNROLL_ADJUST): Define.
* config/riscv/riscv.opt (munroll-only-small-loops): New option.
* config/riscv/riscv.opt.urls: Regenerate.
* doc/invoke.texi (RISC-V Options): Document
-munroll-only-small-loops.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/unroll-explicit.c: New test.
* gcc.target/riscv/unroll-large-loop.c: New test.
* gcc.target/riscv/unroll-small-loop.c: New test.
---
gcc/config/riscv/riscv.cc | 22 +++++++++++++++++++
gcc/config/riscv/riscv.opt | 4 ++++
gcc/config/riscv/riscv.opt.urls | 3 +++
gcc/doc/invoke.texi | 10 +++++++++
.../gcc.target/riscv/unroll-explicit.c | 21 ++++++++++++++++++
.../gcc.target/riscv/unroll-large-loop.c | 21 ++++++++++++++++++
.../gcc.target/riscv/unroll-small-loop.c | 17 ++++++++++++++
7 files changed, 98 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/unroll-explicit.c
create mode 100644 gcc/testsuite/gcc.target/riscv/unroll-large-loop.c
create mode 100644 gcc/testsuite/gcc.target/riscv/unroll-small-loop.c
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index cc1b8cd16a9..110abe5cad1 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -300,6 +300,8 @@ struct riscv_tune_param
const char *jump_align;
const char *loop_align;
bool prefer_agnostic;
+ unsigned int unroll_small_loop_ninsns = 4;
+ unsigned int unroll_small_loop_factor = 4;
};
@@ -734,6 +736,8 @@ static const struct riscv_tune_param xt_c9501_tune_info = {
"8", /* jump_align */
"16", /* loop_align */
true, /* prefer-agnostic. */
+ 4, /* unroll_small_loop_ninsns */
+ 8, /* unroll_small_loop_factor */
};
/* Costs to use when optimizing for Tenstorrent Ascalon 8 wide. */
@@ -5055,6 +5059,22 @@ riscv_insn_cost (rtx_insn *insn, bool speed)
return cost;
}
+/* This function adjusts the unroll factor based on
+ the hardware capabilities. */
+
+static unsigned
+riscv_loop_unroll_adjust (unsigned nunroll, class loop *loop)
+{
+ if (riscv_unroll_only_small_loops && !loop->unroll)
+ {
+ if (loop->ninsns <= tune_param->unroll_small_loop_ninsns)
+ return MIN (tune_param->unroll_small_loop_factor, nunroll);
+ else
+ return 1;
+ }
+ return nunroll;
+}
+
/* Implement TARGET_MAX_NOCE_IFCVT_SEQ_COST. Like the default implementation,
but we consider cost units of branch instructions equal to cost units of
other instructions. */
@@ -16364,6 +16384,8 @@ riscv_memtag_tag_bitsize ()
#define TARGET_RTX_COSTS riscv_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST riscv_address_cost
+#undef TARGET_LOOP_UNROLL_ADJUST
+#define TARGET_LOOP_UNROLL_ADJUST riscv_loop_unroll_adjust
#undef TARGET_INSN_COST
#define TARGET_INSN_COST riscv_insn_cost
diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
index c2670ad87b2..3226367bf8a 100644
--- a/gcc/config/riscv/riscv.opt
+++ b/gcc/config/riscv/riscv.opt
@@ -454,3 +454,7 @@ Enum(arcv_mpy_option) String(10c) Value(ARCV_MPY_OPTION_10C)
mmpy-option=
Target RejectNegative Joined Enum(arcv_mpy_option) Var(arcv_mpy_option)
Init(ARCV_MPY_OPTION_2C)
The type of MPY unit used by the RMX-100 core (to be used in combination with
-mtune=arc-v-rmx-100-series) (default: 2c).
+
+munroll-only-small-loops
+Target Var(riscv_unroll_only_small_loops) Init(0) Save
+Enable conservative small loop unrolling.
diff --git a/gcc/config/riscv/riscv.opt.urls b/gcc/config/riscv/riscv.opt.urls
index a11299b310a..15c1a8badc7 100644
--- a/gcc/config/riscv/riscv.opt.urls
+++ b/gcc/config/riscv/riscv.opt.urls
@@ -129,3 +129,6 @@ UrlSuffix(gcc/RISC-V-Options.html#index-mautovec-segment)
; skipping UrlSuffix for 'mmpy-option=' due to finding no URLs
+munroll-only-small-loops
+UrlSuffix(gcc/RISC-V-Options.html#index-munroll-only-small-loops)
+
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 8da5f03ccbd..1713501dde3 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1349,6 +1349,7 @@ See RS/6000 and PowerPC Options.
-mno-inline-atomics -mno-inline-strlen
-mno-inline-strcmp -mno-inline-strncmp
-mstringop-strategy=@var{strategy}
+-munroll-only-small-loops -mno-unroll-only-small-loops
-mtls-dialect=desc -mtls-dialect=trad
-mrvv-vector-bits=@var{value} -mrvv-max-lmul=@var{value}
-madjust-lmul-cost -mmax-vectorization -mno-autovec-segment}
@@ -31564,6 +31565,15 @@ of TLS variables.
Use traditional TLS as the thread-local storage mechanism for dynamic accesses
of TLS variables. This is the default.
+@opindex munroll-only-small-loops
+@opindex mno-unroll-only-small-loops
+@item -munroll-only-small-loops
+@itemx -mno-unroll-only-small-loops
+Enable or disable conservative loop unrolling limited to small loops.
+When enabled, only loops whose RTL instruction count does not exceed a
+target-specific threshold are unrolled. This option must be used
+together with @option{-funroll-loops} to take effect.
+
@opindex mrvv-vector-bits
@item -mrvv-vector-bits=@var{value}
Specify how the number of bits for an RVV vector register, as taken from
diff --git a/gcc/testsuite/gcc.target/riscv/unroll-explicit.c
b/gcc/testsuite/gcc.target/riscv/unroll-explicit.c
new file mode 100644
index 00000000000..5f7aaf443ad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/unroll-explicit.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funroll-loops -fdump-rtl-loop2_unroll-details" } */
+
+/* Verify that -funroll-loops without -munroll-only-small-loops
+ allows loops exceeding the small-loop instruction threshold
+ to be unrolled normally. */
+
+void
+large_loop_explicit (int *a, int *b, int *c, int *d, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ {
+ a[i] = b[i] + c[i];
+ d[i] = a[i] * b[i] - c[i];
+ b[i] = c[i] + d[i] + a[i];
+ c[i] = a[i] - d[i] + b[i];
+ }
+}
+
+/* { dg-final { scan-rtl-dump "Unrolled loop" "loop2_unroll" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/unroll-large-loop.c
b/gcc/testsuite/gcc.target/riscv/unroll-large-loop.c
new file mode 100644
index 00000000000..d8eb1d70a5c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/unroll-large-loop.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funroll-loops -munroll-only-small-loops
-fdump-rtl-loop2_unroll-details" } */
+
+/* Verify that a loop whose body exceeds the small-loop instruction
+ threshold is NOT unrolled when -munroll-only-small-loops is in
+ effect. */
+
+void
+large_loop (int *a, int *b, int *c, int *d, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ {
+ a[i] = b[i] + c[i];
+ d[i] = a[i] * b[i] - c[i];
+ b[i] = c[i] + d[i] + a[i];
+ c[i] = a[i] - d[i] + b[i];
+ }
+}
+
+/* { dg-final { scan-rtl-dump-not "Unrolled loop" "loop2_unroll" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/unroll-small-loop.c
b/gcc/testsuite/gcc.target/riscv/unroll-small-loop.c
new file mode 100644
index 00000000000..e9989d6dcbb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/unroll-small-loop.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funroll-loops -munroll-only-small-loops
-fdump-rtl-loop2_unroll-details" } */
+
+/* Verify -munroll-only-small-loops unrolls a loop whose body is within
+ the small-loop instruction threshold. */
+
+int
+small_loop (int n)
+{
+ int sum = 0;
+ do
+ sum += n;
+ while (--n);
+ return sum;
+}
+
+/* { dg-final { scan-rtl-dump "Unrolled loop" "loop2_unroll" } } */
--
2.39.5 (Apple Git-154)