diff mbox series

[3/5] RISC-V: Stack-clash protection implemention

Message ID 6f8bab519688aed305898320d1735409e4c46e10.1721681845.git.rzinsly@ventanamicro.com
State New
Headers show
Series RISC-V: Enable stack-clash protection | expand

Commit Message

Raphael Moreira Zinsly July 24, 2024, 6 p.m. UTC
This implements stack-clash protection for riscv, with
riscv_allocate_and_probe_stack_space being based of
aarch64_allocate_and_probe_stack_space from aarch64's implementation.
We enforce the probing interval and the guard size to always be equal, their
default value is 4Kb which is riscv page size.

We also probe up by 1024 bytes in the general case when a probe is required.

gcc/ChangeLog:
	* config/riscv/riscv.cc
	(riscv_option_override): Enforce that interval is the same size as
	guard size.
	(riscv_allocate_and_probe_stack_space): New function.
	(riscv_expand_prologue): Call riscv_allocate_and_probe_stack_space
	to the final allocation of the stack and add stack-clash dump
	information.
	* config/riscv/riscv.h: Define STACK_CLASH_CALLER_GUARD and
	STACK_CLASH_MAX_UNROLL_PAGES.

gcc/testsuite/ChangeLog:
	* gcc.dg/params/blocksort-part.c: Skip riscv for
	stack-clash protection intervals.
	* gcc.dg/pr82788.c: Skip riscv.
	* gcc.dg/stack-check-6.c: Skip residual check for riscv.
	* gcc.dg/stack-check-6a.c: Skip riscv.
	* gcc.target/riscv/stack-check-12.c: New test.
        * gcc.target/riscv/stack-check-13.c: New test.
        * gcc.target/riscv/stack-check-cfa-1.c: New test.
        * gcc.target/riscv/stack-check-cfa-2.c: New test.
        * gcc.target/riscv/stack-check-prologue-1.c: New test.
        * gcc.target/riscv/stack-check-prologue-10.c: New test.
        * gcc.target/riscv/stack-check-prologue-11.c: New test.
        * gcc.target/riscv/stack-check-prologue-12.c: New test.
        * gcc.target/riscv/stack-check-prologue-13.c: New test.
        * gcc.target/riscv/stack-check-prologue-14.c: New test.
        * gcc.target/riscv/stack-check-prologue-15.c: New test.
        * gcc.target/riscv/stack-check-prologue-2.c: New test.
        * gcc.target/riscv/stack-check-prologue-3.c: New test.
        * gcc.target/riscv/stack-check-prologue-4.c: New test.
        * gcc.target/riscv/stack-check-prologue-5.c: New test.
        * gcc.target/riscv/stack-check-prologue-6.c: New test.
        * gcc.target/riscv/stack-check-prologue-7.c: New test.
        * gcc.target/riscv/stack-check-prologue-8.c: New test.
        * gcc.target/riscv/stack-check-prologue-9.c: New test.
        * gcc.target/riscv/stack-check-prologue.h: New file.
	* lib/target-supports.exp
	(check_effective_target_supports_stack_clash_protection):
	Add riscv.
	(check_effective_target_caller_implicit_probes): Likewise.
---
 gcc/config/riscv/riscv.cc                     | 244 +++++++++++++++---
 gcc/config/riscv/riscv.h                      |   8 +
 gcc/testsuite/gcc.dg/params/blocksort-part.c  |   2 +-
 gcc/testsuite/gcc.dg/pr82788.c                |   2 +-
 gcc/testsuite/gcc.dg/stack-check-6.c          |   2 +-
 gcc/testsuite/gcc.dg/stack-check-6a.c         |   2 +-
 .../gcc.target/riscv/stack-check-12.c         |  23 ++
 .../gcc.target/riscv/stack-check-13.c         |  26 ++
 .../gcc.target/riscv/stack-check-cfa-1.c      |  12 +
 .../gcc.target/riscv/stack-check-cfa-2.c      |  13 +
 .../gcc.target/riscv/stack-check-prologue-1.c |   9 +
 .../riscv/stack-check-prologue-10.c           |  11 +
 .../riscv/stack-check-prologue-11.c           |  11 +
 .../riscv/stack-check-prologue-12.c           |  15 ++
 .../riscv/stack-check-prologue-13.c           |  20 ++
 .../riscv/stack-check-prologue-14.c           |  24 ++
 .../riscv/stack-check-prologue-15.c           |  23 ++
 .../gcc.target/riscv/stack-check-prologue-2.c |  10 +
 .../gcc.target/riscv/stack-check-prologue-3.c |  11 +
 .../gcc.target/riscv/stack-check-prologue-4.c |  11 +
 .../gcc.target/riscv/stack-check-prologue-5.c |  11 +
 .../gcc.target/riscv/stack-check-prologue-6.c |  11 +
 .../gcc.target/riscv/stack-check-prologue-7.c |  11 +
 .../gcc.target/riscv/stack-check-prologue-8.c |  10 +
 .../gcc.target/riscv/stack-check-prologue-9.c |  11 +
 .../gcc.target/riscv/stack-check-prologue.h   |   5 +
 gcc/testsuite/lib/target-supports.exp         |   6 +-
 27 files changed, 504 insertions(+), 40 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-12.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-13.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-cfa-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-cfa-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-10.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-11.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-12.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-13.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-14.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-15.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-5.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-6.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-7.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-8.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue-9.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-prologue.h

Comments

Jeff Law July 26, 2024, 9:37 p.m. UTC | #1
On 7/24/24 12:00 PM, Raphael Moreira Zinsly wrote:
> This implements stack-clash protection for riscv, with
> riscv_allocate_and_probe_stack_space being based of
> aarch64_allocate_and_probe_stack_space from aarch64's implementation.
> We enforce the probing interval and the guard size to always be equal, their
> default value is 4Kb which is riscv page size.
> 
> We also probe up by 1024 bytes in the general case when a probe is required.
> 
> gcc/ChangeLog:
> 	* config/riscv/riscv.cc
> 	(riscv_option_override): Enforce that interval is the same size as
> 	guard size.
> 	(riscv_allocate_and_probe_stack_space): New function.
> 	(riscv_expand_prologue): Call riscv_allocate_and_probe_stack_space
> 	to the final allocation of the stack and add stack-clash dump
> 	information.
> 	* config/riscv/riscv.h: Define STACK_CLASH_CALLER_GUARD and
> 	STACK_CLASH_MAX_UNROLL_PAGES.
> 
> gcc/testsuite/ChangeLog:
> 	* gcc.dg/params/blocksort-part.c: Skip riscv for
> 	stack-clash protection intervals.
> 	* gcc.dg/pr82788.c: Skip riscv.
> 	* gcc.dg/stack-check-6.c: Skip residual check for riscv.
> 	* gcc.dg/stack-check-6a.c: Skip riscv.
> 	* gcc.target/riscv/stack-check-12.c: New test.
>          * gcc.target/riscv/stack-check-13.c: New test.
>          * gcc.target/riscv/stack-check-cfa-1.c: New test.
>          * gcc.target/riscv/stack-check-cfa-2.c: New test.
>          * gcc.target/riscv/stack-check-prologue-1.c: New test.
>          * gcc.target/riscv/stack-check-prologue-10.c: New test.
>          * gcc.target/riscv/stack-check-prologue-11.c: New test.
>          * gcc.target/riscv/stack-check-prologue-12.c: New test.
>          * gcc.target/riscv/stack-check-prologue-13.c: New test.
>          * gcc.target/riscv/stack-check-prologue-14.c: New test.
>          * gcc.target/riscv/stack-check-prologue-15.c: New test.
>          * gcc.target/riscv/stack-check-prologue-2.c: New test.
>          * gcc.target/riscv/stack-check-prologue-3.c: New test.
>          * gcc.target/riscv/stack-check-prologue-4.c: New test.
>          * gcc.target/riscv/stack-check-prologue-5.c: New test.
>          * gcc.target/riscv/stack-check-prologue-6.c: New test.
>          * gcc.target/riscv/stack-check-prologue-7.c: New test.
>          * gcc.target/riscv/stack-check-prologue-8.c: New test.
>          * gcc.target/riscv/stack-check-prologue-9.c: New test.
>          * gcc.target/riscv/stack-check-prologue.h: New file.
> 	* lib/target-supports.exp
> 	(check_effective_target_supports_stack_clash_protection):
> 	Add riscv.
> 	(check_effective_target_caller_implicit_probes): Likewise.
Guessing you've got a mixture of tabs and spaces in the ChangeLog entry. 
  I suspect the pre-commit hooks will complain about them.

This all looks really good.  It follows the aarch64 implementation 
reasonably closely with the notable exception of the RTL probe loop 
rather than using probe_stack_range, but with the ability to have 
multiple blocks in the prologue (that we didn't have in 2017/2018), 
yours is the current preferred method.

I did reasonably closely check the bits you moved from 
riscv_expand_prologue given the difficulties we had with them recently. 
That all looked good as well.

I initially expected more changes to be necessary in 
target_supports.exp, but reviewing aarch64's handling in there, I think 
you got it right.  For aarch64 & riscv, we support stack clash 
protection and have limited implicit probes due to saving $ra.  The 
other properties don't apply to aarch64/riscv.

So again, overall it looks really good.

And to get on the record testing-wise.  Raphael and I had just started 
doing large scale testing of Fedora packages about a month ago.  We 
identified ~6k binary packages that looked potentially vulnerable given 
the scanner work from Red Hat & Samsung.  That narrowed down to around 
4k source packages that we'd need to test with before/after builds.

We were just starting the rebuild & rescan process and were seeing good 
results and our milkv pioneer system completely scrambled its disk (for 
the 2nd time :(  That naturally brought testing to a halt.  Just 
recovering data off the data drive is proving somewhat painful.  We're 
still committed to doing that testing as it proved quite valuable on x86 
years ago when I did that implementation.  I fully expect it'll find 
minor glitches either in the scanner or the compiler bits.

Raphael has done bootstraps and regression testing for rv32 and rv64 
with stack clash enabled (which was definitely useful in exposing 
additional issues).


Your call whether or not to include it now or wait for review on 4/5 and 
5/5.

Jeff
diff mbox series

Patch

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 89fc8966654..292d190f319 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -7950,6 +7950,191 @@  get_multi_push_fpr_mask (unsigned max_fprs_push)
   return mask_fprs_push;
 }
 
+/* Allocate SIZE bytes of stack space using TEMP1 as a scratch register.
+   If SIZE is not large enough to require a probe this function will only
+   adjust the stack.
+
+   We emit barriers after each stack adjustment to prevent optimizations from
+   breaking the invariant that we never drop the stack more than a page.  This
+   invariant is needed to make it easier to correctly handle asynchronous
+   events, e.g. if we were to allow the stack to be dropped by more than a page
+   and then have multiple probes up and we take a signal somewhere in between
+   then the signal handler doesn't know the state of the stack and can make no
+   assumptions about which pages have been probed.  */
+
+static void
+riscv_allocate_and_probe_stack_space (rtx temp1, HOST_WIDE_INT size)
+{
+  HOST_WIDE_INT guard_size
+    = 1 << param_stack_clash_protection_guard_size;
+  HOST_WIDE_INT guard_used_by_caller = STACK_CLASH_CALLER_GUARD;
+  HOST_WIDE_INT byte_sp_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
+  HOST_WIDE_INT min_probe_threshold = guard_size - guard_used_by_caller;
+  rtx insn;
+
+  /* We should always have a positive probe threshold.  */
+  gcc_assert (min_probe_threshold > 0);
+
+  /* If SIZE is not large enough to require probing, just adjust the stack and
+     exit.  */
+  if (known_lt (size, min_probe_threshold)
+      || !flag_stack_clash_protection)
+    {
+      if (flag_stack_clash_protection)
+	{
+	  if (known_eq (cfun->machine->frame.total_size, 0))
+	    dump_stack_clash_frame_info (NO_PROBE_NO_FRAME, false);
+	  else
+	    dump_stack_clash_frame_info (NO_PROBE_SMALL_FRAME, true);
+	}
+
+      if (SMALL_OPERAND (-size))
+	{
+	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-size));
+	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+	}
+      else if (SUM_OF_TWO_S12_ALGN (-size))
+	{
+	  HOST_WIDE_INT one, two;
+	  riscv_split_sum_of_two_s12 (-size, &one, &two);
+	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+				GEN_INT (one));
+	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+				GEN_INT (two));
+	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+	}
+      else
+	{
+	  temp1 = riscv_force_temporary (temp1, GEN_INT (-size));
+	  emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, temp1));
+	  insn = plus_constant (Pmode, stack_pointer_rtx, -size);
+	  insn = gen_rtx_SET (stack_pointer_rtx, insn);
+	  riscv_set_frame_expr (insn);
+	}
+
+      /* We must have allocated the remainder of the stack frame.
+	 Emit a stack tie if we have a frame pointer so that the
+	 allocation is ordered WRT fp setup and subsequent writes
+	 into the frame.  */
+      if (frame_pointer_needed)
+	riscv_emit_stack_tie (hard_frame_pointer_rtx);
+
+      return;
+    }
+
+  gcc_assert (multiple_p (size, byte_sp_alignment));
+
+  if (dump_file)
+    fprintf (dump_file,
+	     "Stack clash prologue: " HOST_WIDE_INT_PRINT_DEC
+	     " bytes, probing will be required.\n", size);
+
+  /* Round size to the nearest multiple of guard_size, and calculate the
+     residual as the difference between the original size and the rounded
+     size.  */
+  HOST_WIDE_INT rounded_size = ROUND_DOWN (size, guard_size);
+  HOST_WIDE_INT residual = size - rounded_size;
+
+  /* We can handle a small number of allocations/probes inline.  Otherwise
+     punt to a loop.  */
+  if (rounded_size <= STACK_CLASH_MAX_UNROLL_PAGES * guard_size)
+    {
+      temp1 = riscv_force_temporary (temp1, gen_int_mode (guard_size, Pmode));
+      for (HOST_WIDE_INT i = 0; i < rounded_size; i += guard_size)
+	{
+	  emit_insn (gen_sub3_insn (stack_pointer_rtx, stack_pointer_rtx, temp1));
+	  insn = plus_constant (Pmode, stack_pointer_rtx, -guard_size);
+	  insn = gen_rtx_SET (stack_pointer_rtx, insn);
+	  riscv_set_frame_expr (insn);
+	  emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+					   guard_used_by_caller));
+	  emit_insn (gen_blockage ());
+	}
+      dump_stack_clash_frame_info (PROBE_INLINE, size != rounded_size);
+    }
+  else
+    {
+      /* Compute the ending address.  */
+      temp1 = riscv_force_temporary (temp1, gen_int_mode (rounded_size, Pmode));
+      insn = emit_insn (gen_sub3_insn (temp1, stack_pointer_rtx, temp1));
+
+      if (!frame_pointer_needed)
+	{
+	  /* We want the CFA independent of the stack pointer for the
+	     duration of the loop.  */
+	  add_reg_note (insn, REG_CFA_DEF_CFA,
+			plus_constant (Pmode, temp1, rounded_size));
+	  RTX_FRAME_RELATED_P (insn) = 1;
+	}
+
+      /* Allocate and probe the stack.  */
+
+      rtx temp2 = gen_rtx_REG (Pmode, RISCV_PROLOGUE_TEMP2_REGNUM);
+      temp2 = riscv_force_temporary (temp2, gen_int_mode (guard_size, Pmode));
+
+      /* Loop.  */
+      rtx label = gen_label_rtx ();
+      emit_label (label);
+
+      emit_insn (gen_sub3_insn (stack_pointer_rtx, stack_pointer_rtx, temp2));
+      emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+			   guard_used_by_caller));
+      emit_insn (gen_blockage ());
+
+      /* Check if the stack pointer is at the ending address.  */
+      riscv_expand_conditional_branch (label, NE, stack_pointer_rtx, temp1);
+      JUMP_LABEL (get_last_insn ()) = label;
+
+      emit_insn (gen_blockage ());
+
+      /* Now reset the CFA register if needed.  */
+      if (!frame_pointer_needed)
+	{
+	  insn = get_last_insn ();
+	  add_reg_note (insn, REG_CFA_DEF_CFA,
+			plus_constant (Pmode, stack_pointer_rtx, rounded_size));
+	  RTX_FRAME_RELATED_P (insn) = 1;
+	}
+
+      dump_stack_clash_frame_info (PROBE_LOOP, size != rounded_size);
+    }
+
+  /* Handle any residuals.  Residuals of at least MIN_PROBE_THRESHOLD have to
+     be probed.  This maintains the requirement that each page is probed at
+     least once.  For initial probing we probe only if the allocation is
+     more than GUARD_SIZE - buffer, and below the saved registers we probe
+     if the amount is larger than buffer.  GUARD_SIZE - buffer + buffer ==
+     GUARD_SIZE.  This works that for any allocation that is large enough to
+     trigger a probe here, we'll have at least one, and if they're not large
+     enough for this code to emit anything for them, The page would have been
+     probed by the saving of FP/LR either by this function or any callees.  If
+     we don't have any callees then we won't have more stack adjustments and so
+     are still safe.  */
+  if (residual)
+    {
+      gcc_assert (guard_used_by_caller + byte_sp_alignment <= size);
+
+      temp1 = riscv_force_temporary (temp1, gen_int_mode (residual, Pmode));
+      emit_insn (gen_sub3_insn (stack_pointer_rtx, stack_pointer_rtx, temp1));
+      insn = plus_constant (Pmode, stack_pointer_rtx, -residual);
+      insn = gen_rtx_SET (stack_pointer_rtx, insn);
+      riscv_set_frame_expr (insn);
+      if (residual >= min_probe_threshold)
+	{
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Stack clash prologue residuals: "
+		     HOST_WIDE_INT_PRINT_DEC " bytes, probing will be required."
+		     "\n", residual);
+
+	  emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+					   guard_used_by_caller));
+	  emit_insn (gen_blockage ());
+	}
+    }
+}
+
 /* Expand the "prologue" pattern.  */
 
 void
@@ -8112,42 +8297,14 @@  riscv_expand_prologue (void)
 	  return;
 	}
 
-      if (SMALL_OPERAND (-constant_frame))
-	{
-	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
-				GEN_INT (-constant_frame));
-	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
-	}
-      else if (SUM_OF_TWO_S12_ALGN (-constant_frame))
-	{
-	  HOST_WIDE_INT one, two;
-	  riscv_split_sum_of_two_s12 (-constant_frame, &one, &two);
-	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
-				GEN_INT (one));
-	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
-	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
-				GEN_INT (two));
-	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
-	}
+      riscv_allocate_and_probe_stack_space (RISCV_PROLOGUE_TEMP (Pmode), constant_frame);
+    }
+  else if (flag_stack_clash_protection)
+    {
+      if (known_eq (frame->total_size, 0))
+	dump_stack_clash_frame_info (NO_PROBE_NO_FRAME, false);
       else
-	{
-	  riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), GEN_INT (-constant_frame));
-	  emit_insn (gen_add3_insn (stack_pointer_rtx,
-				    stack_pointer_rtx,
-				    RISCV_PROLOGUE_TEMP (Pmode)));
-
-	  /* Describe the effect of the previous instructions.  */
-	  insn = plus_constant (Pmode, stack_pointer_rtx, -constant_frame);
-	  insn = gen_rtx_SET (stack_pointer_rtx, insn);
-	  riscv_set_frame_expr (insn);
-	}
-
-      /* We must have allocated the remainder of the stack frame.
-	 Emit a stack tie if we have a frame pointer so that the
-	 allocation is ordered WRT fp setup and subsequent writes
-	 into the frame.  */
-      if (frame_pointer_needed)
-	riscv_emit_stack_tie (hard_frame_pointer_rtx);
+	dump_stack_clash_frame_info (NO_PROBE_SMALL_FRAME, true);
     }
 }
 
@@ -9894,6 +10051,23 @@  riscv_option_override (void)
       riscv_stack_protector_guard_offset = offs;
     }
 
+  int guard_size = param_stack_clash_protection_guard_size;
+
+  /* Enforce that interval is the same size as guard size so the mid-end does
+     the right thing.  */
+  SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+		       param_stack_clash_protection_probe_interval,
+		       guard_size);
+
+  /* The maybe_set calls won't update the value if the user has explicitly set
+     one.  Which means we need to validate that probing interval and guard size
+     are equal.  */
+  int probe_interval
+    = param_stack_clash_protection_probe_interval;
+  if (guard_size != probe_interval)
+    error ("stack clash guard size %<%d%> must be equal to probing interval "
+	   "%<%d%>", guard_size, probe_interval);
+
   SET_OPTION_IF_UNSET (&global_options, &global_options_set,
 		       param_sched_pressure_algorithm,
 		       SCHED_PRESSURE_MODEL);
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index 6f040011864..9670c7df8f7 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -1260,4 +1260,12 @@  extern void riscv_remove_unneeded_save_restore_calls (void);
 /* Check TLS Descriptors mechanism is selected.  */
 #define TARGET_TLSDESC (riscv_tls_dialect == TLS_DESCRIPTORS)
 
+/* This value is the amount of bytes a caller is allowed to drop the stack
+   before probing has to be done for stack clash protection.  */
+#define STACK_CLASH_CALLER_GUARD 1024
+
+/* This value controls how many pages we manually unroll the loop for when
+   generating stack clash probes.  */
+#define STACK_CLASH_MAX_UNROLL_PAGES 4
+
 #endif /* ! GCC_RISCV_H */
diff --git a/gcc/testsuite/gcc.dg/params/blocksort-part.c b/gcc/testsuite/gcc.dg/params/blocksort-part.c
index cc15223c0de..72cd5da322c 100644
--- a/gcc/testsuite/gcc.dg/params/blocksort-part.c
+++ b/gcc/testsuite/gcc.dg/params/blocksort-part.c
@@ -1,4 +1,4 @@ 
-/* { dg-skip-if "AArch64 does not support these bounds." { aarch64*-*-* } { "--param stack-clash-protection-*" } } */
+/* { dg-skip-if "RISC-V and AArch64 do not support these bounds." { riscv*-*-* aarch64*-*-* } { "--param stack-clash-protection-*" } } */
 /* { dg-skip-if "For 32-bit hosts such param is too much and even for 64-bit might require hundreds of GB of RAM" { *-*-* } { "--param min-nondebug-insn-uid=1073741824" } } */
 
 /*-------------------------------------------------------------*/
diff --git a/gcc/testsuite/gcc.dg/pr82788.c b/gcc/testsuite/gcc.dg/pr82788.c
index 41c442f61a6..f5cb333f619 100644
--- a/gcc/testsuite/gcc.dg/pr82788.c
+++ b/gcc/testsuite/gcc.dg/pr82788.c
@@ -1,5 +1,5 @@ 
 /* { dg-do run } */
 /* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-probe-interval=10 --param stack-clash-protection-guard-size=12" } */
 /* { dg-require-effective-target supports_stack_clash_protection } */
-/* { dg-skip-if "AArch64 does not support this interval." { aarch64*-*-* } } */
+/* { dg-skip-if "RISC-V and AArch64 do not support this interval." { riscv*-*-* aarch64*-*-* } } */
 int main() { int a[1442]; return 0;}
diff --git a/gcc/testsuite/gcc.dg/stack-check-6.c b/gcc/testsuite/gcc.dg/stack-check-6.c
index fe75612b737..50eb1924602 100644
--- a/gcc/testsuite/gcc.dg/stack-check-6.c
+++ b/gcc/testsuite/gcc.dg/stack-check-6.c
@@ -48,7 +48,7 @@  f7 (void)
 
 /* { dg-final { scan-rtl-dump-times "Stack clash inline probes" 2 "pro_and_epilogue" } } */
 /* { dg-final { scan-rtl-dump-times "Stack clash probe loop" 2 "pro_and_epilogue" } } */
-/* { dg-final { scan-rtl-dump-times "Stack clash residual allocation in prologue" 4 "pro_and_epilogue" } } */
+/* { dg-final { scan-rtl-dump-times "Stack clash residual allocation in prologue" 4 "pro_and_epilogue" { target { ! riscv*-*-* } } } } */
 /* { dg-final { scan-rtl-dump-times "Stack clash not noreturn" 4 "pro_and_epilogue" } } */
 
 /* { dg-final { scan-rtl-dump-times "Stack clash no frame pointer needed" 4 "pro_and_epilogue" { target { ! frame_pointer_for_non_leaf } } } } */
diff --git a/gcc/testsuite/gcc.dg/stack-check-6a.c b/gcc/testsuite/gcc.dg/stack-check-6a.c
index 68dd9bc48a0..8c6b5367afc 100644
--- a/gcc/testsuite/gcc.dg/stack-check-6a.c
+++ b/gcc/testsuite/gcc.dg/stack-check-6a.c
@@ -5,7 +5,7 @@ 
 /* { dg-options "-O2 -fstack-clash-protection -fdump-rtl-pro_and_epilogue -fno-optimize-sibling-calls --param stack-clash-protection-probe-interval=12 --param stack-clash-protection-guard-size=16" } */
 /* { dg-require-effective-target supports_stack_clash_protection  } */
 /* { dg-skip-if "" { *-*-* } { "-fstack-protector*" } { "" } } */
-/* { dg-skip-if "" { aarch64*-*-* } } */
+/* { dg-skip-if "" { riscv*-*-* aarch64*-*-* } } */
 
 
 #include "stack-check-6.c"
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-12.c b/gcc/testsuite/gcc.target/riscv/stack-check-12.c
new file mode 100644
index 00000000000..ceb9acc3c40
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-12.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector --param stack-clash-protection-guard-size=16" } */
+/* { dg-skip-if "" { *-*-* } { "-g"} } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+typedef unsigned __attribute__((mode(DI))) uint64_t;
+
+extern void arf (uint64_t *, uint64_t *);
+void
+frob ()
+{
+  uint64_t num[10000];
+  uint64_t den[10000];
+  arf (den, num);
+}
+
+/* This verifies that the scheduler did not break the dependencies
+   by adjusting the offsets within the probe and that the scheduler
+   did not reorder around the stack probes.  */
+/* { dg-final { scan-assembler-times "li\\tt0,65536" 1 } } */
+/* { dg-final { scan-assembler-times "sub\\tsp,sp,t0\\n\\tsd\\tzero,1024\\(sp\\)" 2 } } */
+/* There is some residual allocation, but we don't care about that. Only that it's not probed.  */
+/* { dg-final { scan-assembler-times "sd\\tzero," 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-13.c b/gcc/testsuite/gcc.target/riscv/stack-check-13.c
new file mode 100644
index 00000000000..abd8a32b712
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-13.c
@@ -0,0 +1,26 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection -fno-asynchronous-unwind-tables -fno-unwind-tables" } */
+/* { dg-skip-if "" { *-*-* } { "-g"} } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define ARG32(X) X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X
+#define ARG384(X) ARG32(X),ARG32(X),ARG32(X),ARG32(X),ARG32(X),ARG32(X), ARG32(X),ARG32(X),ARG32(X),ARG32(X),ARG32(X),ARG32(X)
+void out1(ARG384(__int128));
+int t1(int);
+
+int t3(int x)
+{
+  if (x < 1000)
+    return t1 (x) + 1;
+
+  out1 (ARG384(1));
+  return 0;
+}
+
+
+
+/* This test creates a large (> 1k) outgoing argument area that needs
+   to be probed.  We don't test the exact size of the space or the
+   exact offset to make the test a little less sensitive to trivial
+   output changes.  */
+/* { dg-final { scan-assembler-times "sub\\tsp,sp,t0\\n\\tsd\\tzero,1024\\(sp\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-cfa-1.c b/gcc/testsuite/gcc.target/riscv/stack-check-cfa-1.c
new file mode 100644
index 00000000000..60b01578692
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-cfa-1.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection -funwind-tables -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 128*1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa [0-9]+, 131072} 1 } } */
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_register 2} 1 } } */
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 } } */
+
+/* Checks that the CFA notes are correct for every sp adjustment.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-cfa-2.c b/gcc/testsuite/gcc.target/riscv/stack-check-cfa-2.c
new file mode 100644
index 00000000000..9d36a302222
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-cfa-2.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection -funwind-tables -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 80*1024 + 512
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa [0-9]+, 81920} 1 } } */
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_register 2} 1 } } */
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 82432} 1 } } */
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 } } */
+
+/* Checks that the CFA notes are correct for every sp adjustment.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-1.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-1.c
new file mode 100644
index 00000000000..9f2c527a5ed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-1.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 128
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-not "sd\tzero," } } */
+/* SIZE is smaller than guard-size - 1Kb so no probe expected.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-10.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-10.c
new file mode 100644
index 00000000000..fd171c30f89
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-10.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE (6 * 4 * 1024) + (1 * 3 * 1024) + 512
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 2 } } */
+
+/* SIZE is more than 4x guard-size and remainder larger than guard-size - 1Kb,
+   1 probe expected in a loop and 1 residual probe.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-11.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-11.c
new file mode 100644
index 00000000000..ebe3b139eb0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-11.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE (6 * 4 * 1024) + (1 * 2 * 1024)
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* SIZE is more than 4x guard-size and remainder larger than guard-size - 1Kb,
+   1 probe expected in a loop and 1 residual probe.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-12.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-12.c
new file mode 100644
index 00000000000..2a001ea8b1f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-12.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection --param stack-clash-protection-guard-size=16 -fomit-frame-pointer -momit-leaf-frame-pointer -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+void
+f (void)
+{
+  volatile int x[16384 + 1000];
+  x[0] = 0;
+}
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* SIZE is more than 1 guard-size, but only one 64KB page is used, expect only 1
+   probe.  Leaf function and omitting leaf pointers.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-13.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-13.c
new file mode 100644
index 00000000000..d97f69a943f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-13.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection --param stack-clash-protection-guard-size=16 -fomit-frame-pointer -momit-leaf-frame-pointer -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+void h (void) __attribute__ ((noreturn));
+
+void
+f (void)
+{
+  volatile int x[16384 + 1000];
+  x[30]=0;
+  h ();
+}
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+/* { dg-final { scan-assembler-times {sw\tzero,120\(sp\)} 1 } } */
+
+/* SIZE is more than 1 guard-size, but only one 64KB page is used, expect only 1
+   probe.  Leaf function and omitting leaf pointers, tail call to noreturn which
+   may only omit an epilogue and not a prologue.  Checking for LR saving.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-14.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-14.c
new file mode 100644
index 00000000000..bd263fbbd80
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-14.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection --param stack-clash-protection-guard-size=16 -fomit-frame-pointer -momit-leaf-frame-pointer -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+void h (void) __attribute__ ((noreturn));
+
+void
+f (void)
+{
+  volatile int x[16384 + 1000];
+  if (x[0])
+     h ();
+  x[345] = 1;
+  h ();
+}
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+/* { dg-final { scan-assembler-times {sd\tra,8\(sp\)} 1 } } */
+
+/* SIZE is more than 1 guard-size, two 64k pages used, expect only 1 explicit
+   probe at 1024 and one implicit probe due to LR being saved.  Leaf function
+   and omitting leaf pointers, tail call to noreturn which may only omit an
+   epilogue and not a prologue and control flow in between.  Checking for
+   LR saving.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-15.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-15.c
new file mode 100644
index 00000000000..f175e6f5b8f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-15.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection --param stack-clash-protection-guard-size=16 -fomit-frame-pointer -momit-leaf-frame-pointer -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+void g (volatile int *x) ;
+void h (void) __attribute__ ((noreturn));
+
+void
+f (void)
+{
+  volatile int x[16384 + 1000];
+  g (x);
+  h ();
+}
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+/* { dg-final { scan-assembler-times {sd\tra,8\(sp\)} 1 } } */
+
+/* SIZE is more than 1 guard-size, two 64k pages used, expect only 1 explicit
+   probe at 1024 and one implicit probe due to LR being saved.  Leaf function
+   and omitting leaf pointers, normal function call followed by a tail call to
+   noreturn which may only omit an epilogue and not a prologue and control flow
+   in between.  Checking for LR saving.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-2.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-2.c
new file mode 100644
index 00000000000..9c78b1ebaf1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-2.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 2 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-not "sd\tzero," } } */
+
+/* SIZE is smaller than guard-size - 1Kb so no probe expected.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-3.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-3.c
new file mode 100644
index 00000000000..2c7e55acae6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-3.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 3 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times "sd\tzero," 1 } } */
+
+/* SIZE is exactly guard-size - 1Kb, boundary condition so 1 probe expected.
+*/
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-4.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-4.c
new file mode 100644
index 00000000000..506ea7b19c8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-4.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 3 * 1024 + 512
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* SIZE is more than guard-size - 1Kb and remainder is less than 1kB,
+   1 probe expected.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-5.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-5.c
new file mode 100644
index 00000000000..4c50a2a47a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-5.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 4 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* SIZE is more than guard-size - 1Kb and remainder is zero,
+   1 probe expected, boundary condition.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-6.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-6.c
new file mode 100644
index 00000000000..db39ecdc39f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-6.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 5 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* SIZE is more than guard-size - 1Kb and remainder is equal to 1kB,
+   1 probe expected.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-7.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-7.c
new file mode 100644
index 00000000000..b394849136d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-7.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 7 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 2 } } */
+
+/* SIZE is more than 1x guard-size and remainder equal than guard-size - 1Kb,
+   2 probe expected, unrolled, no loop.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-8.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-8.c
new file mode 100644
index 00000000000..6366cacc520
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-8.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 8 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 2 } } */
+
+/* SIZE is more than 2x guard-size and no remainder, unrolled, no loop.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue-9.c b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-9.c
new file mode 100644
index 00000000000..5e65750b9e8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue-9.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection -fno-stack-protector" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+#define SIZE 6 * 4 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* SIZE is more than 4x guard-size and no remainder, 1 probe expected in a loop
+   and no residual probe.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-prologue.h b/gcc/testsuite/gcc.target/riscv/stack-check-prologue.h
new file mode 100644
index 00000000000..b7e06aedb81
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-prologue.h
@@ -0,0 +1,5 @@ 
+int f_test (int x)
+{
+  char arr[SIZE];
+  return arr[x];
+}
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index daa0c75d2bc..3d7a4691624 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -12718,7 +12718,7 @@  proc check_effective_target_supports_stack_clash_protection { } {
     if { [istarget x86_64-*-*] || [istarget i?86-*-*]
 	  || [istarget powerpc*-*-*] || [istarget rs6000*-*-*]
 	  || [istarget aarch64*-**] || [istarget s390*-*-*]
-	  || [istarget loongarch64*-**] } {
+	  || [istarget loongarch64*-**] || [istarget riscv64*-**] } {
 	return 1
     }
   return 0
@@ -12778,6 +12778,10 @@  proc check_effective_target_caller_implicit_probes { } {
 	return 1;
   }
 
+  if { [istarget riscv64*-*-*] } {
+	return 1;
+  }
+
   if { [istarget loongarch64*-*-*] } {
 	return 1;
   }