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)

Reply via email to