https://gcc.gnu.org/g:7f6b6a5710258a720d2d35d14c30877b729b4833
commit 7f6b6a5710258a720d2d35d14c30877b729b4833 Author: Raphael Moreira Zinsly <rzin...@ventanamicro.com> Date: Mon Jul 22 11:23:20 2024 -0300 RISC-V: Stack-clash protection implemention 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. (cherry picked from commit b82d173dac33d9e2f7d31bf84eb0d9f0c21d0240) Diff: --- 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/testsuite/gcc.target/riscv/stack-check-12.c | 23 ++ gcc/testsuite/gcc.target/riscv/stack-check-13.c | 26 +++ gcc/testsuite/gcc.target/riscv/stack-check-cfa-1.c | 12 + gcc/testsuite/gcc.target/riscv/stack-check-cfa-2.c | 13 ++ .../gcc.target/riscv/stack-check-prologue-1.c | 9 + .../gcc.target/riscv/stack-check-prologue-10.c | 11 + .../gcc.target/riscv/stack-check-prologue-11.c | 11 + .../gcc.target/riscv/stack-check-prologue-12.c | 15 ++ .../gcc.target/riscv/stack-check-prologue-13.c | 20 ++ .../gcc.target/riscv/stack-check-prologue-14.c | 24 ++ .../gcc.target/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(-) diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index b1582e45aec..05dce3ccea4 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -7953,6 +7953,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 @@ -8115,42 +8300,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); } } @@ -9914,6 +10071,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 f6c5a11b2f9..92d379d1e8a 100644 --- a/gcc/config/riscv/riscv.h +++ b/gcc/config/riscv/riscv.h @@ -1262,4 +1262,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 5619f4602c6..3af79b544cd 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -12911,7 +12911,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 @@ -12971,6 +12971,10 @@ proc check_effective_target_caller_implicit_probes { } { return 1; } + if { [istarget riscv64*-*-*] } { + return 1; + } + if { [istarget loongarch64*-*-*] } { return 1; }