diff --git a/gcc/config/loongarch/linux.h b/gcc/config/loongarch/linux.h
index 110d0fab93d..00039ac1871 100644
--- a/gcc/config/loongarch/linux.h
+++ b/gcc/config/loongarch/linux.h
@@ -48,3 +48,6 @@ along with GCC; see the file COPYING3.  If not see
 #define STACK_CHECK_PROTECT (TARGET_64BIT ? 16 * 1024 : 12 * 1024)
 
 #define TARGET_ASM_FILE_END file_end_indicate_exec_stack
+
+/* The stack pointer needs to be moved while checking the stack.  */
+#define STACK_CHECK_MOVING_SP 1
diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
index e9ba3374e35..755dec6a629 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -63,6 +63,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "context.h"
 #include "builtins.h"
 #include "rtl-iter.h"
+#include "opts.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -257,6 +258,10 @@ const char *const
 loongarch_fp_conditions[16]= {LARCH_FP_CONDITIONS (STRINGIFY)};
 #undef STRINGIFY
 
+/* Size of guard page.  */
+#define STACK_CLASH_PROTECTION_GUARD_SIZE \
+  (1 << param_stack_clash_protection_guard_size)
+
 /* Implement TARGET_FUNCTION_ARG_BOUNDARY.  Every parameter gets at
    least PARM_BOUNDARY bits of alignment, but will be given anything up
    to PREFERRED_STACK_BOUNDARY bits if the type requires it.  */
@@ -1069,11 +1074,20 @@ loongarch_restore_reg (rtx reg, rtx mem)
 static HOST_WIDE_INT
 loongarch_first_stack_step (struct loongarch_frame_info *frame)
 {
+  HOST_WIDE_INT min_first_step
+    = LARCH_STACK_ALIGN (frame->total_size - frame->fp_sp_offset);
+
+  /* When stack checking is required, if the sum of frame->total_size
+     and stack_check_protect is greater than stack clash protection guard
+     size, then return min_first_step.  */
+  if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK
+      || (flag_stack_clash_protection
+	  && frame->total_size > STACK_CLASH_PROTECTION_GUARD_SIZE))
+    return min_first_step;
+
   if (IMM12_OPERAND (frame->total_size))
     return frame->total_size;
 
-  HOST_WIDE_INT min_first_step
-    = LARCH_STACK_ALIGN (frame->total_size - frame->fp_sp_offset);
   HOST_WIDE_INT max_first_step = IMM_REACH / 2 - PREFERRED_STACK_BOUNDARY / 8;
   HOST_WIDE_INT min_second_step = frame->total_size - max_first_step;
   gcc_assert (min_first_step <= max_first_step);
@@ -1106,103 +1120,109 @@ loongarch_emit_stack_tie (void)
 static void
 loongarch_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size)
 {
-  /* See if we have a constant small number of probes to generate.  If so,
-     that's the easy case.  */
-  if ((TARGET_64BIT && (first + size <= 32768))
-      || (!TARGET_64BIT && (first + size <= 2048)))
-    {
-      HOST_WIDE_INT i;
+  HOST_WIDE_INT rounded_size;
+  HOST_WIDE_INT interval;
 
-      /* Probe at FIRST + N * PROBE_INTERVAL for values of N from 1 until
-	 it exceeds SIZE.  If only one probe is needed, this will not
-	 generate any code.  Then probe at FIRST + SIZE.  */
-      for (i = PROBE_INTERVAL; i < size; i += PROBE_INTERVAL)
-	emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
-					 -(first + i)));
+  if (flag_stack_clash_protection)
+    interval = STACK_CLASH_PROTECTION_GUARD_SIZE;
+  else
+    interval = PROBE_INTERVAL;
 
-      emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
-				       -(first + size)));
-    }
+  rtx r12 = LARCH_PROLOGUE_TEMP2 (Pmode);
+  rtx r14 = LARCH_PROLOGUE_TEMP3 (Pmode);
 
-  /* Otherwise, do the same as above, but in a loop.  Note that we must be
-     extra careful with variables wrapping around because we might be at
-     the very top (or the very bottom) of the address space and we have
-     to be able to handle this case properly; in particular, we use an
-     equality test for the loop condition.  */
-  else
-    {
-      HOST_WIDE_INT rounded_size;
-      rtx r13 = LARCH_PROLOGUE_TEMP (Pmode);
-      rtx r12 = LARCH_PROLOGUE_TEMP2 (Pmode);
-      rtx r14 = LARCH_PROLOGUE_TEMP3 (Pmode);
+  size = size + first;
 
-      /* Sanity check for the addressing mode we're going to use.  */
-      gcc_assert (first <= 16384);
+  /* Sanity check for the addressing mode we're going to use.  */
+  gcc_assert (first <= 16384);
 
+  /* Step 1: round SIZE to the previous multiple of the interval.  */
 
-      /* Step 1: round SIZE to the previous multiple of the interval.  */
+  rounded_size = ROUND_DOWN (size, interval);
 
-      rounded_size = ROUND_DOWN (size, PROBE_INTERVAL);
+  /* Step 2: compute initial and final value of the loop counter.  */
 
-      /* TEST_ADDR = SP + FIRST */
-      if (first != 0)
-	{
-	  emit_move_insn (r14, GEN_INT (first));
-	  emit_insn (gen_rtx_SET (r13, gen_rtx_MINUS (Pmode,
-						      stack_pointer_rtx,
-						      r14)));
-	}
-      else
-	emit_move_insn (r13, stack_pointer_rtx);
+  emit_move_insn (r14, GEN_INT (interval));
+
+  /* If rounded_size is zero, it means that the space requested by
+     the local variable is less than the interval, and there is no
+     need to display and detect the allocated space.  */
+  if (rounded_size != 0)
+    {
+      /* Step 3: the loop
+
+	 do
+	 {
+	 TEST_ADDR = TEST_ADDR + PROBE_INTERVAL
+	 probe at TEST_ADDR
+	 }
+	 while (TEST_ADDR != LAST_ADDR)
 
-      /* Step 2: compute initial and final value of the loop counter.  */
+	 probes at FIRST + N * PROBE_INTERVAL for values of N from 1
+	 until it is equal to ROUNDED_SIZE.  */
 
-      emit_move_insn (r14, GEN_INT (PROBE_INTERVAL));
-      /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE.  */
-      if (rounded_size == 0)
-	emit_move_insn (r12, r13);
+      if (rounded_size <= STACK_CLASH_MAX_UNROLL_PAGES * interval)
+	{
+	  for (HOST_WIDE_INT i = 0; i < rounded_size; i += interval)
+	    {
+	      emit_insn (gen_rtx_SET (stack_pointer_rtx,
+				      gen_rtx_MINUS (Pmode,
+						     stack_pointer_rtx,
+						     r14)));
+	      emit_move_insn (gen_rtx_MEM (Pmode,
+					   gen_rtx_PLUS (Pmode,
+							 stack_pointer_rtx,
+							 const0_rtx)),
+			      const0_rtx);
+	      emit_insn (gen_blockage ());
+	    }
+	  dump_stack_clash_frame_info (PROBE_INLINE, size != rounded_size);
+	}
       else
 	{
 	  emit_move_insn (r12, GEN_INT (rounded_size));
-	  emit_insn (gen_rtx_SET (r12, gen_rtx_MINUS (Pmode, r13, r12)));
-	  /* Step 3: the loop
-
-	     do
-	     {
-	     TEST_ADDR = TEST_ADDR + PROBE_INTERVAL
-	     probe at TEST_ADDR
-	     }
-	     while (TEST_ADDR != LAST_ADDR)
-
-	     probes at FIRST + N * PROBE_INTERVAL for values of N from 1
-	     until it is equal to ROUNDED_SIZE.  */
-
-	  emit_insn (gen_probe_stack_range (Pmode, r13, r13, r12, r14));
+	  emit_insn (gen_rtx_SET (r12,
+				  gen_rtx_MINUS (Pmode,
+						 stack_pointer_rtx,
+						 r12)));
+
+	  emit_insn (gen_probe_stack_range (Pmode, stack_pointer_rtx,
+					    stack_pointer_rtx, r12, r14));
+	  emit_insn (gen_blockage ());
+	  dump_stack_clash_frame_info (PROBE_LOOP, size != rounded_size);
 	}
+    }
+  else
+    dump_stack_clash_frame_info (NO_PROBE_SMALL_FRAME, true);
 
-      /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time
-	 that SIZE is equal to ROUNDED_SIZE.  */
 
-      if (size != rounded_size)
+  /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time
+     that SIZE is equal to ROUNDED_SIZE.  */
+
+  if (size != rounded_size)
+    {
+      if (size - rounded_size >= 2048)
 	{
-	  if (TARGET_64BIT)
-	    emit_stack_probe (plus_constant (Pmode, r12, rounded_size - size));
-	  else
-	    {
-	      HOST_WIDE_INT i;
-	      for (i = 2048; i < (size - rounded_size); i += 2048)
-		{
-		  emit_stack_probe (plus_constant (Pmode, r12, -i));
-		  emit_insn (gen_rtx_SET (r12,
-					  plus_constant (Pmode, r12, -2048)));
-		}
-	      rtx r1 = plus_constant (Pmode, r12,
-				      -(size - rounded_size - i + 2048));
-	      emit_stack_probe (r1);
-	    }
+	  emit_move_insn (r14, GEN_INT (size - rounded_size));
+	  emit_insn (gen_rtx_SET (stack_pointer_rtx,
+				  gen_rtx_MINUS (Pmode,
+						 stack_pointer_rtx,
+						 r14)));
 	}
+      else
+	emit_insn (gen_rtx_SET (stack_pointer_rtx,
+				gen_rtx_PLUS (Pmode,
+					      stack_pointer_rtx,
+					      GEN_INT (rounded_size - size))));
     }
 
+  if (first)
+    {
+      emit_move_insn (r12, GEN_INT (first));
+      emit_insn (gen_rtx_SET (stack_pointer_rtx,
+			      gen_rtx_PLUS (Pmode,
+					    stack_pointer_rtx, r12)));
+    }
   /* Make sure nothing is scheduled before we are done.  */
   emit_insn (gen_blockage ());
 }
@@ -1223,7 +1243,6 @@ loongarch_output_probe_stack_range (rtx reg1, rtx reg2, rtx reg3)
 
   /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL.  */
   xops[0] = reg1;
-  xops[1] = GEN_INT (-PROBE_INTERVAL);
   xops[2] = reg3;
   if (TARGET_64BIT)
     output_asm_insn ("sub.d\t%0,%0,%2", xops);
@@ -1249,28 +1268,11 @@ loongarch_expand_prologue (void)
 {
   struct loongarch_frame_info *frame = &cfun->machine->frame;
   HOST_WIDE_INT size = frame->total_size;
-  HOST_WIDE_INT tmp;
   rtx insn;
 
   if (flag_stack_usage_info)
     current_function_static_stack_size = size;
 
-  if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK
-      || flag_stack_clash_protection)
-    {
-      if (crtl->is_leaf && !cfun->calls_alloca)
-	{
-	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
-	    {
-	      tmp = size - get_stack_check_protect ();
-	      loongarch_emit_probe_stack_range (get_stack_check_protect (),
-						tmp);
-	    }
-	}
-      else if (size > 0)
-	loongarch_emit_probe_stack_range (get_stack_check_protect (), size);
-    }
-
   /* Save the registers.  */
   if ((frame->mask | frame->fmask) != 0)
     {
@@ -1283,7 +1285,6 @@ loongarch_expand_prologue (void)
       loongarch_for_each_saved_reg (size, loongarch_save_reg);
     }
 
-
   /* Set up the frame pointer, if we're using one.  */
   if (frame_pointer_needed)
     {
@@ -1294,7 +1295,45 @@ loongarch_expand_prologue (void)
       loongarch_emit_stack_tie ();
     }
 
-  /* Allocate the rest of the frame.  */
+  if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK
+       || flag_stack_clash_protection)
+    {
+      HOST_WIDE_INT first = get_stack_check_protect ();
+
+      if (frame->total_size == 0)
+	{
+	  /* do nothing.  */
+	  dump_stack_clash_frame_info (NO_PROBE_NO_FRAME, false);
+	  return;
+	}
+
+      if (crtl->is_leaf && !cfun->calls_alloca)
+	{
+	  HOST_WIDE_INT interval;
+
+	  if (flag_stack_clash_protection)
+	    interval = STACK_CLASH_PROTECTION_GUARD_SIZE;
+	  else
+	    interval = PROBE_INTERVAL;
+
+	  if (size > interval && size > first)
+	    loongarch_emit_probe_stack_range (first, size - first);
+	  else
+	    loongarch_emit_probe_stack_range (first, size);
+	}
+      else
+	loongarch_emit_probe_stack_range (first, size);
+
+      if (size > 0)
+	{
+	  /* Describe the effect of the previous instructions.  */
+	  insn = plus_constant (Pmode, stack_pointer_rtx, -size);
+	  insn = gen_rtx_SET (stack_pointer_rtx, insn);
+	  loongarch_set_frame_expr (insn);
+	}
+      return;
+    }
+
   if (size > 0)
     {
       if (IMM12_OPERAND (-size))
@@ -1305,7 +1344,8 @@ loongarch_expand_prologue (void)
 	}
       else
 	{
-	  loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode), GEN_INT (-size));
+	  loongarch_emit_move (LARCH_PROLOGUE_TEMP (Pmode),
+			       GEN_INT (-size));
 	  emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
 				    LARCH_PROLOGUE_TEMP (Pmode)));
 
@@ -6127,6 +6167,15 @@ loongarch_option_override_internal (struct gcc_options *opts)
 	gcc_unreachable ();
     }
 
+  /* Validate the guard size.  */
+  int guard_size = param_stack_clash_protection_guard_size;
+
+  /* Enforce that interval is the same size as size so the mid-end does the
+     right thing.  */
+  SET_OPTION_IF_UNSET (opts, &global_options_set,
+		       param_stack_clash_protection_probe_interval,
+		       guard_size);
+
   loongarch_init_print_operand_punct ();
 
   /* Set up array to map GCC register number to debug register number.
diff --git a/gcc/config/loongarch/loongarch.h b/gcc/config/loongarch/loongarch.h
index f4a9c329fef..a402d3ba35a 100644
--- a/gcc/config/loongarch/loongarch.h
+++ b/gcc/config/loongarch/loongarch.h
@@ -668,6 +668,10 @@ enum reg_class
 
 #define STACK_BOUNDARY (TARGET_ABI_LP64 ? 128 : 64)
 
+/* This value controls how many pages we manually unroll the loop for when
+   generating stack clash probes.  */
+#define STACK_CLASH_MAX_UNROLL_PAGES 4
+
 /* Symbolic macros for the registers used to return integer and floating
    point values.  */
 
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-1.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-1.c
new file mode 100644
index 00000000000..6ee589c4b3d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-1.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE y
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r\d{1,2},-8} 1 } } */
+/* { dg-final { scan-assembler-times {stx\.d\t\$r0,\$r3,\$r12} 1 } } */
+
+/* Dynamic alloca, expect loop, and 1 probes with top at sp.
+   1st probe is inside the loop for the full guard-size allocations, second
+   probe is for the case where residual is zero.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-2.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-2.c
new file mode 100644
index 00000000000..8deaa587305
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-2.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 0
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-not {stp*t*r*\.d\t\$r0,\$r3,4088} } } */
+
+/* Alloca of 0 should emit no probes, boundary condition.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-3.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-3.c
new file mode 100644
index 00000000000..e326ba9a092
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-3.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 100
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {st\.d\t\$r0,\$r3,104} 1 } } */
+
+/* Alloca is less than guard-size, 1 probe at the top of the new allocation.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-4.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-4.c
new file mode 100644
index 00000000000..b9f7572dedc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-4.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 64 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r\d{1,2},-8} 1 } } */
+
+/* Alloca is exactly one guard-size, 1 probe expected at top.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-5.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-5.c
new file mode 100644
index 00000000000..0ff6e493fec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-5.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 65 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r\d{1,2},-8} 1 } } */
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,1016} 1 } } */
+
+/* Alloca is more than one guard-page. 2 probes expected.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-6.c b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-6.c
new file mode 100644
index 00000000000..c5cf74fcb4f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca-6.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 127 * 64 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r\d{1,2},-8} 1 } } */
+
+/* Large alloca of a constant amount which is a multiple of a guard-size.
+   Loop expected with top probe.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-alloca.h b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca.h
new file mode 100644
index 00000000000..8c75f6c0f70
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-alloca.h
@@ -0,0 +1,15 @@
+
+/* Avoid inclusion of alloca.h, unavailable on some systems.  */
+#define alloca __builtin_alloca
+
+__attribute__((noinline, noipa))
+void g (char* ptr, int y)
+{
+  ptr[y] = '\0';
+}
+
+void f_caller (int y)
+{
+  char* pStr = alloca(SIZE);
+  g (pStr, y);
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
new file mode 100644
index 00000000000..f0c6877fc25
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16 -funwind-tables" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 128*1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 131088} 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/loongarch/stack-check-cfa-2.c b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
new file mode 100644
index 00000000000..c6e07bc561a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16 -funwind-tables" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 1280*1024 + 512
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 1311248} 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/loongarch/stack-check-prologue-1.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-1.c
new file mode 100644
index 00000000000..351bc1f61fd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-1.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 128
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 0 } } */
+
+/* SIZE is smaller than guard-size so no probe expected.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-2.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-2.c
new file mode 100644
index 00000000000..6bba659a36d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-2.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 63 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*.d\t\$r0,\$r3,0} 0 } } */
+
+/* SIZE is smaller than guard-size so no probe expected.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-3.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-3.c
new file mode 100644
index 00000000000..164956c3747
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-3.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 64 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 1 } } */
+
+/* SIZE is equal to guard-size, 1 probe expected, boundary condition.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-4.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-4.c
new file mode 100644
index 00000000000..f53da6b0de9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-4.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 65 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 1 } } */
+
+/* SIZE is more than guard-size 1 probe expected.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-5.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-5.c
new file mode 100644
index 00000000000..c092317ea94
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-5.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 127 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 1 } } */
+
+/* SIZE is more than 1x guard-size and remainder small than guard-size,
+   1 probe expected, unrolled, no loop.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-6.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-6.c
new file mode 100644
index 00000000000..70a2f53f6d7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-6.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 128 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*\.d\t\$r0,\$r3,0} 2 } } */
+
+/* SIZE is more than 2x guard-size and no remainder, unrolled, no loop.  */
diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-7.c b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-7.c
new file mode 100644
index 00000000000..e2df89acc77
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue-7.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-skip-if "" { *-*-* } { "-fstack-check" } { "" } } */
+
+#define SIZE 6 * 64 * 1024
+#include "stack-check-prologue.h"
+
+/* { dg-final { scan-assembler-times {stp*t*r*.d\t\$r0,\$r3,0} 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/loongarch/stack-check-prologue.h b/gcc/testsuite/gcc.target/loongarch/stack-check-prologue.h
new file mode 100644
index 00000000000..b7e06aedb81
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/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 fdd88e6a516..8ce879468e8 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -11377,7 +11377,8 @@ proc check_effective_target_supports_stack_clash_protection { } {
 
     if { [istarget x86_64-*-*] || [istarget i?86-*-*] 
 	  || [istarget powerpc*-*-*] || [istarget rs6000*-*-*]
-	  || [istarget aarch64*-**] || [istarget s390*-*-*] } {
+	  || [istarget aarch64*-**] || [istarget s390*-*-*]
+	  || [istarget loongarch64*-**] } {
 	return 1
     }
   return 0
@@ -11428,6 +11429,10 @@ proc check_effective_target_caller_implicit_probes { } {
 	return 1;
   }
 
+  if { [istarget loongarch64*-*-*] } {
+	return 1;
+  }
+
   return 0
 }
 
