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;
   }
