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

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

Reply via email to