[1/3,V6,RISC-V] support cm.push cm.pop cm.popret in zcmp

Message ID 20230829083746.1458-2-gaofei@eswincomputing.com
State Unresolved
Headers
Series support zcmp extension |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Fei Gao Aug. 29, 2023, 8:37 a.m. UTC
  Zcmp can share the same logic as save-restore in stack allocation: pre-allocation
by cm.push, step 1 and step 2.

Pre-allocation not only saves callee saved GPRs, but also saves callee saved FPRs and
local variables if any.

Please be noted cm.push pushes ra, s0-s11 in reverse order than what save-restore does.
So adaption has been done in .cfi directives in my patch.

gcc/ChangeLog:

        * config/riscv/iterators.md
        slot0_offset: slot 0 offset in stack GPRs area in bytes
        slot1_offset: slot 1 offset in stack GPRs area in bytes
        slot2_offset: likewise
        slot3_offset: likewise
        slot4_offset: likewise
        slot5_offset: likewise
        slot6_offset: likewise
        slot7_offset: likewise
        slot8_offset: likewise
        slot9_offset: likewise
        slot10_offset: likewise
        slot11_offset: likewise
        slot12_offset: likewise
        * config/riscv/predicates.md
        (stack_push_up_to_ra_operand): predicates of stack adjust pushing ra
        (stack_push_up_to_s0_operand): predicates of stack adjust pushing ra, s0
        (stack_push_up_to_s1_operand): likewise
        (stack_push_up_to_s2_operand): likewise
        (stack_push_up_to_s3_operand): likewise
        (stack_push_up_to_s4_operand): likewise
        (stack_push_up_to_s5_operand): likewise
        (stack_push_up_to_s6_operand): likewise
        (stack_push_up_to_s7_operand): likewise
        (stack_push_up_to_s8_operand): likewise
        (stack_push_up_to_s9_operand): likewise
        (stack_push_up_to_s11_operand): likewise
        (stack_pop_up_to_ra_operand): predicates of stack adjust poping ra
        (stack_pop_up_to_s0_operand): predicates of stack adjust poping ra, s0
        (stack_pop_up_to_s1_operand): likewise
        (stack_pop_up_to_s2_operand): likewise
        (stack_pop_up_to_s3_operand): likewise
        (stack_pop_up_to_s4_operand): likewise
        (stack_pop_up_to_s5_operand): likewise
        (stack_pop_up_to_s6_operand): likewise
        (stack_pop_up_to_s7_operand): likewise
        (stack_pop_up_to_s8_operand): likewise
        (stack_pop_up_to_s9_operand): likewise
        (stack_pop_up_to_s11_operand): likewise
        * config/riscv/riscv-protos.h
        (riscv_zcmp_valid_stack_adj_bytes_p):declaration
        * config/riscv/riscv.cc (struct riscv_frame_info): comment change
        (riscv_avoid_multi_push): helper function of riscv_use_multi_push
        (riscv_use_multi_push): true if multi push is used
        (riscv_multi_push_sregs_count): num of sregs in multi-push
        (riscv_multi_push_regs_count): num of regs in multi-push
        (riscv_16bytes_align): align to 16 bytes
        (riscv_stack_align): moved to a better place
        (riscv_save_libcall_count): no functional change
        (riscv_compute_frame_info): add zcmp frame info
        (riscv_for_each_saved_reg): save or restore fprs in specified slot for zcmp
        (riscv_adjust_multi_push_cfi_prologue): adjust cfi for cm.push
        (riscv_gen_multi_push_pop_insn): gen function for multi push and pop
        (get_multi_push_fpr_mask): get mask for the fprs pushed by cm.push
        (riscv_expand_prologue): allocate stack by cm.push
        (riscv_adjust_multi_pop_cfi_epilogue): adjust cfi for cm.pop[ret]
        (riscv_expand_epilogue): allocate stack by cm.pop[ret]
        (zcmp_base_adj): calculate stack adjustment base size
        (zcmp_additional_adj): calculate stack adjustment additional size
        (riscv_zcmp_valid_stack_adj_bytes_p): check if stack adjustment valid
        * config/riscv/riscv.h (RETURN_ADDR_MASK): mask of ra
        (S0_MASK): likewise
        (S1_MASK): likewise
        (S2_MASK): likewise
        (S3_MASK): likewise
        (S4_MASK): likewise
        (S5_MASK): likewise
        (S6_MASK): likewise
        (S7_MASK): likewise
        (S8_MASK): likewise
        (S9_MASK): likewise
        (S10_MASK): likewise
        (S11_MASK): likewise
        (MULTI_PUSH_GPR_MASK): GPR_MASK that cm.push can cover at most
        (ZCMP_MAX_SPIMM): max spimm value
        (ZCMP_SP_INC_STEP): zcmp sp increment step
        (ZCMP_INVALID_S0S10_SREGS_COUNTS): num of s0-s10
        (ZCMP_S0S11_SREGS_COUNTS): num of s0-s11
        (ZCMP_MAX_GRP_SLOTS): max slots of pushing and poping in zcmp
        (CALLEE_SAVED_FREG_NUMBER): get x of fsx(fs0 ~ fs11)
        * config/riscv/riscv.md: include zc.md
        * config/riscv/zc.md: New file. machine description for zcmp

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/rv32e_zcmp.c: New test.
        * gcc.target/riscv/rv32i_zcmp.c: New test.
        * gcc.target/riscv/zcmp_push_fpr.c: New test.
        * gcc.target/riscv/zcmp_stack_alignment.c: New test.
---
 gcc/config/riscv/iterators.md                 |   15 +
 gcc/config/riscv/predicates.md                |   96 ++
 gcc/config/riscv/riscv-protos.h               |    2 +
 gcc/config/riscv/riscv.cc                     |  435 ++++++-
 gcc/config/riscv/riscv.h                      |   25 +
 gcc/config/riscv/riscv.md                     |    2 +
 gcc/config/riscv/zc.md                        | 1042 +++++++++++++++++
 gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c   |  256 ++++
 gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c   |  256 ++++
 .../gcc.target/riscv/zcmp_push_fpr.c          |   34 +
 .../gcc.target/riscv/zcmp_stack_alignment.c   |   24 +
 11 files changed, 2136 insertions(+), 51 deletions(-)
 create mode 100644 gcc/config/riscv/zc.md
 create mode 100644 gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zcmp_push_fpr.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c
  

Comments

Patrick O'Neill Oct. 27, 2023, 8:31 p.m. UTC | #1
Hi Fei,

A recent change to GCC [1] updated the  the registers in the cm.push and 
cm.pop insns for these testcases:

|FAIL: gcc.target/riscv/rv32i_zcmp.c -Os check-function-bodies test1 
FAIL: gcc.target/riscv/rv32i_zcmp.c -Os check-function-bodies 
test2_step1_0_size FAIL: gcc.target/riscv/rv32i_zcmp.c -Os 
check-function-bodies test3|

Debug log:

Executing on host: /github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/build/build-gcc-linux-stage2/gcc/xgcc -B/github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/build/build-gcc-linux-stage2/gcc/  /github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/gcc/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c  -march=rv64gcv -mabi=lp64d -mcmodel=medlow   -fdiagnostics-plain-output    -Os   -Os -march=rv32imaf_zca_zcmp -mabi=ilp32f -mcmodel=medlow -S   -o rv32i_zcmp.s    (timeout = 600)
spawn -ignore SIGHUP /github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/build/build-gcc-linux-stage2/gcc/xgcc -B/github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/build/build-gcc-linux-stage2/gcc/ /github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/gcc/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c -march=rv64gcv -mabi=lp64d -mcmodel=medlow -fdiagnostics-plain-output -Os -Os -march=rv32imaf_zca_zcmp -mabi=ilp32f -mcmodel=medlow -S -o rv32i_zcmp.s
PASS: gcc.target/riscv/rv32i_zcmp.c   -Os  (test for excess errors)
body: .*\tcm.push	{ra, s0-s4}, -80
.*\tcm.popret	{ra, s0-s4}, 80
.*
against: 	lui	a5,%hi(.LC0)
	li	t0,-16384
	cm.push	{ra, s0-s6}, -80
	addi	t0,t0,816
	fsw	fs0,44(sp)
	lw	s2,%lo(.LC0)(a5)
	lw	s3,%lo(.LC0+4)(a5)
	fmv.s.x	fs0,zero
	li	a5,4096
	add	sp,sp,t0
	addi	a5,a5,-784
	li	s1,4096
	li	s0,0
	addi	s5,sp,-784
	add	s4,sp,a5
	addi	s1,s1,-976
	call	my_getchar
	add	s6,s5,s0
	sb	a0,784(s6)
	call	my_getchar
	call	__floatsidf
	mv	a2,s2
	mv	a3,s3
	call	__muldf3
	call	__truncdfsf2
	slli	a5,s0,2
	add	a5,s4,a5
	fsw	fa0,-192(a5)
	addi	s0,s0,1
	lbu	a4,784(s6)
	fcvt.s.w	fa5,a4
	flw	fa4,-192(a5)
	fadd.s	fa5,fa5,fa4
	fadd.s	fs0,fs0,fa5
	bne	s0,s1,.L2
	li	t0,16384
	addi	t0,t0,-816
	add	sp,sp,t0
	fcvt.w.s a0,fs0,rtz
	flw	fs0,44(sp)
	cm.popret	{ra, s0-s6}, 80

FAIL: gcc.target/riscv/rv32i_zcmp.c   -Os   check-function-bodies test1

Would  it be OK if we made the regex accept any s[0-9] register (or 
would it be better if it was [1-9])?
Proposed change:

diff --git a/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
index ea562b7a233..5f65a1c7c0b 100644
--- a/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
+++ b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
@@ -26,9 +26,9 @@ f2 (void);
  /*
  **test1:
  **     ...
-**     cm.push {ra, s0-s4}, -80
+**     cm.push {ra, s0-s[0-9]}, -80
  **     ...
-**     cm.popret       {ra, s0-s4}, 80
+**     cm.popret       {ra, s0-s[0-9]}, 80
  **     ...
  */
  int
@@ -50,9 +50,9 @@ test1 ()
  /*
  **test2_step1_0_size:
  **     ...
-**     cm.push {ra, s0-s1}, -64
+**     cm.push {ra, s0-s[0-9]}, -64
  **     ...
-**     cm.popret       {ra, s0-s1}, 64
+**     cm.popret       {ra, s0-s[0-9]}, 64
  **     ...
  */
  int
@@ -70,9 +70,9 @@ test2_step1_0_size ()
  /*
  **test3:
  **     ...
-**     cm.push {ra, s0-s4}, -80
+**     cm.push {ra, s0-s[0-9]}, -80
  **     ...
-**     cm.popret       {ra, s0-s4}, 80
+**     cm.popret       {ra, s0-s[0-9]}, 80
  **     ...
  */
  float

Thanks,
Patrick

[1] It was one of these commits:
https://github.com/gcc-mirror/gcc/compare/a4ca8691333344cecc595d1af8b21e51f588e2f2...4d49685d671e4e604b2b873ada65aaac89348794

On 8/29/23 01:37, Fei Gao wrote:
> Zcmp can share the same logic as save-restore in stack allocation: pre-allocation
> by cm.push, step 1 and step 2.
>
> Pre-allocation not only saves callee saved GPRs, but also saves callee saved FPRs and
> local variables if any.
>
> Please be noted cm.push pushes ra, s0-s11 in reverse order than what save-restore does.
> So adaption has been done in .cfi directives in my patch.
>
> gcc/ChangeLog:
>
>          * config/riscv/iterators.md
>          slot0_offset: slot 0 offset in stack GPRs area in bytes
>          slot1_offset: slot 1 offset in stack GPRs area in bytes
>          slot2_offset: likewise
>          slot3_offset: likewise
>          slot4_offset: likewise
>          slot5_offset: likewise
>          slot6_offset: likewise
>          slot7_offset: likewise
>          slot8_offset: likewise
>          slot9_offset: likewise
>          slot10_offset: likewise
>          slot11_offset: likewise
>          slot12_offset: likewise
>          * config/riscv/predicates.md
>          (stack_push_up_to_ra_operand): predicates of stack adjust pushing ra
>          (stack_push_up_to_s0_operand): predicates of stack adjust pushing ra, s0
>          (stack_push_up_to_s1_operand): likewise
>          (stack_push_up_to_s2_operand): likewise
>          (stack_push_up_to_s3_operand): likewise
>          (stack_push_up_to_s4_operand): likewise
>          (stack_push_up_to_s5_operand): likewise
>          (stack_push_up_to_s6_operand): likewise
>          (stack_push_up_to_s7_operand): likewise
>          (stack_push_up_to_s8_operand): likewise
>          (stack_push_up_to_s9_operand): likewise
>          (stack_push_up_to_s11_operand): likewise
>          (stack_pop_up_to_ra_operand): predicates of stack adjust poping ra
>          (stack_pop_up_to_s0_operand): predicates of stack adjust poping ra, s0
>          (stack_pop_up_to_s1_operand): likewise
>          (stack_pop_up_to_s2_operand): likewise
>          (stack_pop_up_to_s3_operand): likewise
>          (stack_pop_up_to_s4_operand): likewise
>          (stack_pop_up_to_s5_operand): likewise
>          (stack_pop_up_to_s6_operand): likewise
>          (stack_pop_up_to_s7_operand): likewise
>          (stack_pop_up_to_s8_operand): likewise
>          (stack_pop_up_to_s9_operand): likewise
>          (stack_pop_up_to_s11_operand): likewise
>          * config/riscv/riscv-protos.h
>          (riscv_zcmp_valid_stack_adj_bytes_p):declaration
>          * config/riscv/riscv.cc (struct riscv_frame_info): comment change
>          (riscv_avoid_multi_push): helper function of riscv_use_multi_push
>          (riscv_use_multi_push): true if multi push is used
>          (riscv_multi_push_sregs_count): num of sregs in multi-push
>          (riscv_multi_push_regs_count): num of regs in multi-push
>          (riscv_16bytes_align): align to 16 bytes
>          (riscv_stack_align): moved to a better place
>          (riscv_save_libcall_count): no functional change
>          (riscv_compute_frame_info): add zcmp frame info
>          (riscv_for_each_saved_reg): save or restore fprs in specified slot for zcmp
>          (riscv_adjust_multi_push_cfi_prologue): adjust cfi for cm.push
>          (riscv_gen_multi_push_pop_insn): gen function for multi push and pop
>          (get_multi_push_fpr_mask): get mask for the fprs pushed by cm.push
>          (riscv_expand_prologue): allocate stack by cm.push
>          (riscv_adjust_multi_pop_cfi_epilogue): adjust cfi for cm.pop[ret]
>          (riscv_expand_epilogue): allocate stack by cm.pop[ret]
>          (zcmp_base_adj): calculate stack adjustment base size
>          (zcmp_additional_adj): calculate stack adjustment additional size
>          (riscv_zcmp_valid_stack_adj_bytes_p): check if stack adjustment valid
>          * config/riscv/riscv.h (RETURN_ADDR_MASK): mask of ra
>          (S0_MASK): likewise
>          (S1_MASK): likewise
>          (S2_MASK): likewise
>          (S3_MASK): likewise
>          (S4_MASK): likewise
>          (S5_MASK): likewise
>          (S6_MASK): likewise
>          (S7_MASK): likewise
>          (S8_MASK): likewise
>          (S9_MASK): likewise
>          (S10_MASK): likewise
>          (S11_MASK): likewise
>          (MULTI_PUSH_GPR_MASK): GPR_MASK that cm.push can cover at most
>          (ZCMP_MAX_SPIMM): max spimm value
>          (ZCMP_SP_INC_STEP): zcmp sp increment step
>          (ZCMP_INVALID_S0S10_SREGS_COUNTS): num of s0-s10
>          (ZCMP_S0S11_SREGS_COUNTS): num of s0-s11
>          (ZCMP_MAX_GRP_SLOTS): max slots of pushing and poping in zcmp
>          (CALLEE_SAVED_FREG_NUMBER): get x of fsx(fs0 ~ fs11)
>          * config/riscv/riscv.md: include zc.md
>          * config/riscv/zc.md: New file. machine description for zcmp
>
> gcc/testsuite/ChangeLog:
>
>          * gcc.target/riscv/rv32e_zcmp.c: New test.
>          * gcc.target/riscv/rv32i_zcmp.c: New test.
>          * gcc.target/riscv/zcmp_push_fpr.c: New test.
>          * gcc.target/riscv/zcmp_stack_alignment.c: New test.
> ---
>   gcc/config/riscv/iterators.md                 |   15 +
>   gcc/config/riscv/predicates.md                |   96 ++
>   gcc/config/riscv/riscv-protos.h               |    2 +
>   gcc/config/riscv/riscv.cc                     |  435 ++++++-
>   gcc/config/riscv/riscv.h                      |   25 +
>   gcc/config/riscv/riscv.md                     |    2 +
>   gcc/config/riscv/zc.md                        | 1042 +++++++++++++++++
>   gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c   |  256 ++++
>   gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c   |  256 ++++
>   .../gcc.target/riscv/zcmp_push_fpr.c          |   34 +
>   .../gcc.target/riscv/zcmp_stack_alignment.c   |   24 +
>   11 files changed, 2136 insertions(+), 51 deletions(-)
>   create mode 100644 gcc/config/riscv/zc.md
>   create mode 100644 gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zcmp_push_fpr.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c
>
> diff --git a/gcc/config/riscv/iterators.md b/gcc/config/riscv/iterators.md
> index a4070de1510..ecf033f2fa7 100644
> --- a/gcc/config/riscv/iterators.md
> +++ b/gcc/config/riscv/iterators.md
> @@ -125,6 +125,21 @@
>   (define_mode_attr shiftm1 [(SI "const_si_mask_operand") (DI "const_di_mask_operand")])
>   (define_mode_attr shiftm1p [(SI "DsS") (DI "DsD")])
>   
> +; zcmp mode attribute
> +(define_mode_attr slot0_offset  [(SI "-4")  (DI "-8")])
> +(define_mode_attr slot1_offset  [(SI "-8")  (DI "-16")])
> +(define_mode_attr slot2_offset  [(SI "-12") (DI "-24")])
> +(define_mode_attr slot3_offset  [(SI "-16") (DI "-32")])
> +(define_mode_attr slot4_offset  [(SI "-20") (DI "-40")])
> +(define_mode_attr slot5_offset  [(SI "-24") (DI "-48")])
> +(define_mode_attr slot6_offset  [(SI "-28") (DI "-56")])
> +(define_mode_attr slot7_offset  [(SI "-32") (DI "-64")])
> +(define_mode_attr slot8_offset  [(SI "-36") (DI "-72")])
> +(define_mode_attr slot9_offset  [(SI "-40") (DI "-80")])
> +(define_mode_attr slot10_offset [(SI "-44") (DI "-88")])
> +(define_mode_attr slot11_offset [(SI "-48") (DI "-96")])
> +(define_mode_attr slot12_offset [(SI "-52") (DI "-104")])
> +
>   ;; -------------------------------------------------------------------
>   ;; Code Iterators
>   ;; -------------------------------------------------------------------
> diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
> index 618ad607047..3ef09996a85 100644
> --- a/gcc/config/riscv/predicates.md
> +++ b/gcc/config/riscv/predicates.md
> @@ -69,6 +69,102 @@
>     (ior (match_operand 0 "const_0_operand")
>          (match_operand 0 "register_operand")))
>   
> +(define_predicate "stack_push_up_to_ra_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 1)")))
> +
> +(define_predicate "stack_push_up_to_s0_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 2)")))
> +
> +(define_predicate "stack_push_up_to_s1_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 3)")))
> +
> +(define_predicate "stack_push_up_to_s2_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 4)")))
> +
> +(define_predicate "stack_push_up_to_s3_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 5)")))
> +
> +(define_predicate "stack_push_up_to_s4_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 6)")))
> +
> +(define_predicate "stack_push_up_to_s5_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 7)")))
> +
> +(define_predicate "stack_push_up_to_s6_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 8)")))
> +
> +(define_predicate "stack_push_up_to_s7_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 9)")))
> +
> +(define_predicate "stack_push_up_to_s8_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 10)")))
> +
> +(define_predicate "stack_push_up_to_s9_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 11)")))
> +
> +(define_predicate "stack_push_up_to_s11_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 13)")))
> +
> +(define_predicate "stack_pop_up_to_ra_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 1)")))
> +
> +(define_predicate "stack_pop_up_to_s0_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 2)")))
> +
> +(define_predicate "stack_pop_up_to_s1_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 3)")))
> +
> +(define_predicate "stack_pop_up_to_s2_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 4)")))
> +
> +(define_predicate "stack_pop_up_to_s3_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 5)")))
> +
> +(define_predicate "stack_pop_up_to_s4_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 6)")))
> +
> +(define_predicate "stack_pop_up_to_s5_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 7)")))
> +
> +(define_predicate "stack_pop_up_to_s6_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 8)")))
> +
> +(define_predicate "stack_pop_up_to_s7_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 9)")))
> +
> +(define_predicate "stack_pop_up_to_s8_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 10)")))
> +
> +(define_predicate "stack_pop_up_to_s9_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 11)")))
> +
> +(define_predicate "stack_pop_up_to_s11_operand"
> +  (and (match_code "const_int")
> +       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 13)")))
> +
>   ;; Only use branch-on-bit sequences when the mask is not an ANDI immediate.
>   (define_predicate "branch_on_bit_operand"
>     (and (match_code "const_int")
> diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
> index 0e0470280f8..04885bbebd2 100644
> --- a/gcc/config/riscv/riscv-protos.h
> +++ b/gcc/config/riscv/riscv-protos.h
> @@ -102,6 +102,8 @@ extern bool riscv_split_64bit_move_p (rtx, rtx);
>   extern void riscv_split_doubleword_move (rtx, rtx);
>   extern const char *riscv_output_move (rtx, rtx);
>   extern const char *riscv_output_return ();
> +extern bool
> +riscv_zcmp_valid_stack_adj_bytes_p (HOST_WIDE_INT, int);
>   
>   #ifdef RTX_CODE
>   extern void riscv_expand_int_scc (rtx, enum rtx_code, rtx, rtx, bool *invert_ptr = 0);
> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index 1d6e278ea90..ed4d28b2eb0 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -112,6 +112,14 @@ struct GTY(())  riscv_frame_info {
>     /* How much the GPR save/restore routines adjust sp (or 0 if unused).  */
>     unsigned save_libcall_adjustment;
>   
> +  /* the minimum number of bytes, in multiples of 16-byte address increments,
> +     required to cover the registers in a multi push & pop.  */
> +  unsigned multi_push_adj_base;
> +
> +  /* the number of additional 16-byte address increments allocated for the stack
> +     frame in a multi push & pop.  */
> +  unsigned multi_push_adj_addi;
> +
>     /* Offsets of fixed-point and floating-point save areas from frame bottom */
>     poly_int64 gp_sp_offset;
>     poly_int64 fp_sp_offset;
> @@ -409,6 +417,16 @@ static const struct riscv_tune_info riscv_tune_info_table[] = {
>      function.  */
>   static bool riscv_save_frame_pointer;
>   
> +typedef enum
> +{
> +  PUSH_IDX = 0,
> +  POP_IDX,
> +  POPRET_IDX,
> +  ZCMP_OP_NUM
> +} riscv_zcmp_op_t;
> +
> +typedef insn_code (*code_for_push_pop_t) (machine_mode);
> +
>   void riscv_frame_info::reset(void)
>   {
>     total_size = 0;
> @@ -5539,6 +5557,34 @@ riscv_save_reg_p (unsigned int regno)
>     return false;
>   }
>   
> +/* Return TRUE if Zcmp push and pop insns should be
> +   avoided. FALSE otherwise.
> +   Only use multi push & pop if all GPRs masked can be covered,
> +   and stack access is SP based,
> +   and GPRs are at top of the stack frame,
> +   and no conflicts in stack allocation with other features  */
> +static bool
> +riscv_avoid_multi_push (const struct riscv_frame_info *frame)
> +{
> +  if (!TARGET_ZCMP || crtl->calls_eh_return || frame_pointer_needed
> +      || cfun->machine->interrupt_handler_p || cfun->machine->varargs_size != 0
> +      || crtl->args.pretend_args_size != 0 || flag_shrink_wrap_separate
> +      || (frame->mask & ~MULTI_PUSH_GPR_MASK))
> +    return true;
> +
> +  return false;
> +}
> +
> +/* Determine whether to use multi push insn.  */
> +static bool
> +riscv_use_multi_push (const struct riscv_frame_info *frame)
> +{
> +  if (riscv_avoid_multi_push (frame))
> +    return false;
> +
> +  return (frame->multi_push_adj_base != 0);
> +}
> +
>   /* Return TRUE if a libcall to save/restore GPRs should be
>      avoided.  FALSE otherwise.  */
>   static bool
> @@ -5576,6 +5622,50 @@ riscv_save_libcall_count (unsigned mask)
>     abort ();
>   }
>   
> +/* calculate number of s regs in multi push and pop.
> +   Note that {s0-s10} is not valid in Zcmp, use {s0-s11} instead.  */
> +static unsigned
> +riscv_multi_push_sregs_count (unsigned mask)
> +{
> +  unsigned num = riscv_save_libcall_count (mask);
> +  return (num == ZCMP_INVALID_S0S10_SREGS_COUNTS) ? ZCMP_S0S11_SREGS_COUNTS
> +						  : num;
> +}
> +
> +/* calculate number of regs(ra, s0-sx) in multi push and pop.  */
> +static unsigned
> +riscv_multi_push_regs_count (unsigned mask)
> +{
> +  /* 1 is for ra  */
> +  return riscv_multi_push_sregs_count (mask) + 1;
> +}
> +
> +/* Handle 16 bytes align for poly_int.  */
> +static poly_int64
> +riscv_16bytes_align (poly_int64 value)
> +{
> +  return aligned_upper_bound (value, 16);
> +}
> +
> +static HOST_WIDE_INT
> +riscv_16bytes_align (HOST_WIDE_INT value)
> +{
> +  return ROUND_UP (value, 16);
> +}
> +
> +/* Handle stack align for poly_int.  */
> +static poly_int64
> +riscv_stack_align (poly_int64 value)
> +{
> +  return aligned_upper_bound (value, PREFERRED_STACK_BOUNDARY / 8);
> +}
> +
> +static HOST_WIDE_INT
> +riscv_stack_align (HOST_WIDE_INT value)
> +{
> +  return RISCV_STACK_ALIGN (value);
> +}
> +
>   /* Populate the current function's riscv_frame_info structure.
>   
>      RISC-V stack frames grown downward.  High addresses are at the top.
> @@ -5601,7 +5691,7 @@ riscv_save_libcall_count (unsigned mask)
>   	|  GPR save area                |       + UNITS_PER_WORD
>   	|                               |
>   	+-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset
> -	|                               |       + UNITS_PER_HWVALUE
> +	|                               |       + UNITS_PER_FP_REG
>   	|  FPR save area                |
>   	|                               |
>   	+-------------------------------+ <-- frame_pointer_rtx (virtual)
> @@ -5620,19 +5710,6 @@ riscv_save_libcall_count (unsigned mask)
>   
>   static HOST_WIDE_INT riscv_first_stack_step (struct riscv_frame_info *frame, poly_int64 remaining_size);
>   
> -/* Handle stack align for poly_int.  */
> -static poly_int64
> -riscv_stack_align (poly_int64 value)
> -{
> -  return aligned_upper_bound (value, PREFERRED_STACK_BOUNDARY / 8);
> -}
> -
> -static HOST_WIDE_INT
> -riscv_stack_align (HOST_WIDE_INT value)
> -{
> -  return RISCV_STACK_ALIGN (value);
> -}
> -
>   static void
>   riscv_compute_frame_info (void)
>   {
> @@ -5683,8 +5760,9 @@ riscv_compute_frame_info (void)
>     if (frame->mask)
>       {
>         x_save_size = riscv_stack_align (num_x_saved * UNITS_PER_WORD);
> -      unsigned num_save_restore = 1 + riscv_save_libcall_count (frame->mask);
>   
> +      /* 1 is for ra  */
> +      unsigned num_save_restore = 1 + riscv_save_libcall_count (frame->mask);
>         /* Only use save/restore routines if they don't alter the stack size.  */
>         if (riscv_stack_align (num_save_restore * UNITS_PER_WORD) == x_save_size
>             && !riscv_avoid_save_libcall ())
> @@ -5696,6 +5774,14 @@ riscv_compute_frame_info (void)
>   
>   	  frame->save_libcall_adjustment = x_save_size;
>   	}
> +
> +      if (!riscv_avoid_multi_push (frame))
> +	{
> +	  /* num(ra, s0-sx)  */
> +	  unsigned num_multi_push = riscv_multi_push_regs_count (frame->mask);
> +	  x_save_size = riscv_stack_align (num_multi_push * UNITS_PER_WORD);
> +	  frame->multi_push_adj_base = riscv_16bytes_align (x_save_size);
> +	}
>       }
>   
>     /* In an interrupt function, we need extra space for the initial saves of CSRs.  */
> @@ -5721,7 +5807,15 @@ riscv_compute_frame_info (void)
>     frame->fp_sp_offset = offset - UNITS_PER_FP_REG;
>     /* Next are the callee-saved GPRs. */
>     if (frame->mask)
> -    offset += x_save_size;
> +    {
> +      offset += x_save_size;
> +      /* align to 16 bytes and add paddings to GPR part to honor
> +	 both stack alignment and zcmp pus/pop size alignment. */
> +      if (riscv_use_multi_push (frame)
> +	  && known_lt (offset, frame->multi_push_adj_base
> +				 + ZCMP_SP_INC_STEP * ZCMP_MAX_SPIMM))
> +	offset = riscv_16bytes_align (offset);
> +    }
>     frame->gp_sp_offset = offset - UNITS_PER_WORD;
>     /* The hard frame pointer points above the callee-saved GPRs. */
>     frame->hard_frame_pointer_offset = offset;
> @@ -5900,8 +5994,8 @@ static void
>   riscv_for_each_saved_reg (poly_int64 sp_offset, riscv_save_restore_fn fn,
>   			  bool epilogue, bool maybe_eh_return)
>   {
> -  HOST_WIDE_INT offset;
> -  unsigned int regno;
> +  HOST_WIDE_INT offset, first_fp_offset;
> +  unsigned int regno, num_masked_fp = 0;
>     unsigned int start = GP_REG_FIRST;
>     unsigned int limit = GP_REG_LAST;
>   
> @@ -5987,16 +6081,20 @@ riscv_for_each_saved_reg (poly_int64 sp_offset, riscv_save_restore_fn fn,
>   
>     /* This loop must iterate over the same space as its companion in
>        riscv_compute_frame_info.  */
> -  offset = (cfun->machine->frame.fp_sp_offset - sp_offset).to_constant ();
> +  first_fp_offset
> +    = (cfun->machine->frame.fp_sp_offset - sp_offset).to_constant ();
>     for (unsigned int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
>       if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
>         {
>   	bool handle_reg = !cfun->machine->reg_is_wrapped_separately[regno];
>   	machine_mode mode = TARGET_DOUBLE_FLOAT ? DFmode : SFmode;
> -
> +	unsigned int slot = (riscv_use_multi_push (&cfun->machine->frame))
> +			      ? CALLEE_SAVED_FREG_NUMBER (regno)
> +			      : num_masked_fp;
> +	offset = first_fp_offset - slot * GET_MODE_SIZE (mode).to_constant ();
>   	if (handle_reg)
>   	  riscv_save_restore_reg (mode, regno, offset, fn);
> -	offset -= GET_MODE_SIZE (mode).to_constant ();
> +	num_masked_fp++;
>         }
>   }
>   
> @@ -6093,6 +6191,41 @@ riscv_adjust_libcall_cfi_prologue ()
>     return dwarf;
>   }
>   
> +static rtx
> +riscv_adjust_multi_push_cfi_prologue (int saved_size)
> +{
> +  rtx dwarf = NULL_RTX;
> +  rtx adjust_sp_rtx, reg, mem, insn;
> +  unsigned int mask = cfun->machine->frame.mask;
> +  int offset;
> +  int saved_cnt = 0;
> +
> +  if (mask & S10_MASK)
> +    mask |= S11_MASK;
> +
> +  for (int regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
> +    if (BITSET_P (mask & MULTI_PUSH_GPR_MASK, regno - GP_REG_FIRST))
> +      {
> +	/* The save order is s11-s0, ra
> +	   from high to low addr.  */
> +	offset = saved_size - UNITS_PER_WORD * (++saved_cnt);
> +
> +	reg = gen_rtx_REG (Pmode, regno);
> +	mem = gen_frame_mem (Pmode,
> +			     plus_constant (Pmode, stack_pointer_rtx, offset));
> +
> +	insn = gen_rtx_SET (mem, reg);
> +	dwarf = alloc_reg_note (REG_CFA_OFFSET, insn, dwarf);
> +      }
> +
> +  /* Debug info for adjust sp.  */
> +  adjust_sp_rtx
> +    = gen_rtx_SET (stack_pointer_rtx,
> +		   plus_constant (Pmode, stack_pointer_rtx, -saved_size));
> +  dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, dwarf);
> +  return dwarf;
> +}
> +
>   static void
>   riscv_emit_stack_tie (void)
>   {
> @@ -6102,6 +6235,56 @@ riscv_emit_stack_tie (void)
>       emit_insn (gen_stack_tiedi (stack_pointer_rtx, hard_frame_pointer_rtx));
>   }
>   
> +/*zcmp multi push and pop code_for_push_pop function ptr array  */
> +const code_for_push_pop_t code_for_push_pop[ZCMP_MAX_GRP_SLOTS][ZCMP_OP_NUM]
> +  = {{code_for_gpr_multi_push_up_to_ra, code_for_gpr_multi_pop_up_to_ra,
> +      code_for_gpr_multi_popret_up_to_ra},
> +     {code_for_gpr_multi_push_up_to_s0, code_for_gpr_multi_pop_up_to_s0,
> +      code_for_gpr_multi_popret_up_to_s0},
> +     {code_for_gpr_multi_push_up_to_s1, code_for_gpr_multi_pop_up_to_s1,
> +      code_for_gpr_multi_popret_up_to_s1},
> +     {code_for_gpr_multi_push_up_to_s2, code_for_gpr_multi_pop_up_to_s2,
> +      code_for_gpr_multi_popret_up_to_s2},
> +     {code_for_gpr_multi_push_up_to_s3, code_for_gpr_multi_pop_up_to_s3,
> +      code_for_gpr_multi_popret_up_to_s3},
> +     {code_for_gpr_multi_push_up_to_s4, code_for_gpr_multi_pop_up_to_s4,
> +      code_for_gpr_multi_popret_up_to_s4},
> +     {code_for_gpr_multi_push_up_to_s5, code_for_gpr_multi_pop_up_to_s5,
> +      code_for_gpr_multi_popret_up_to_s5},
> +     {code_for_gpr_multi_push_up_to_s6, code_for_gpr_multi_pop_up_to_s6,
> +      code_for_gpr_multi_popret_up_to_s6},
> +     {code_for_gpr_multi_push_up_to_s7, code_for_gpr_multi_pop_up_to_s7,
> +      code_for_gpr_multi_popret_up_to_s7},
> +     {code_for_gpr_multi_push_up_to_s8, code_for_gpr_multi_pop_up_to_s8,
> +      code_for_gpr_multi_popret_up_to_s8},
> +     {code_for_gpr_multi_push_up_to_s9, code_for_gpr_multi_pop_up_to_s9,
> +      code_for_gpr_multi_popret_up_to_s9},
> +     {nullptr, nullptr, nullptr},
> +     {code_for_gpr_multi_push_up_to_s11, code_for_gpr_multi_pop_up_to_s11,
> +      code_for_gpr_multi_popret_up_to_s11}};
> +
> +static rtx
> +riscv_gen_multi_push_pop_insn (riscv_zcmp_op_t op, HOST_WIDE_INT adj_size,
> +			       unsigned int regs_num)
> +{
> +  gcc_assert (op < ZCMP_OP_NUM);
> +  gcc_assert (regs_num <= ZCMP_MAX_GRP_SLOTS
> +	      && regs_num != ZCMP_INVALID_S0S10_SREGS_COUNTS + 1); /* 1 for ra*/
> +  rtx stack_adj = GEN_INT (adj_size);
> +  return GEN_FCN (code_for_push_pop[regs_num - 1][op](Pmode)) (stack_adj);
> +}
> +
> +static unsigned
> +get_multi_push_fpr_mask (unsigned max_fprs_push)
> +{
> +  unsigned mask_fprs_push = 0, num_f_pushed = 0;
> +  for (unsigned regno = FP_REG_FIRST;
> +       regno <= FP_REG_LAST && num_f_pushed < max_fprs_push; regno++)
> +    if (riscv_save_reg_p (regno))
> +      mask_fprs_push |= 1 << (regno - FP_REG_FIRST), num_f_pushed++;
> +  return mask_fprs_push;
> +}
> +
>   /* Expand the "prologue" pattern.  */
>   
>   void
> @@ -6110,7 +6293,9 @@ riscv_expand_prologue (void)
>     struct riscv_frame_info *frame = &cfun->machine->frame;
>     poly_int64 remaining_size = frame->total_size;
>     unsigned mask = frame->mask;
> -  rtx insn;
> +  unsigned fmask = frame->fmask;
> +  int spimm, multi_push_additional, stack_adj;
> +  rtx insn, dwarf = NULL_RTX;
>   
>     if (flag_stack_usage_info)
>       current_function_static_stack_size = constant_lower_bound (remaining_size);
> @@ -6118,8 +6303,46 @@ riscv_expand_prologue (void)
>     if (cfun->machine->naked_p)
>       return;
>   
> +  /* prefer muti-push to save-restore libcall.  */
> +  if (riscv_use_multi_push (frame))
> +    {
> +      remaining_size -= frame->multi_push_adj_base;
> +      if (known_gt (remaining_size, 2 * ZCMP_SP_INC_STEP))
> +	spimm = 3;
> +      else if (known_gt (remaining_size, ZCMP_SP_INC_STEP))
> +	spimm = 2;
> +      else if (known_gt (remaining_size, 0))
> +	spimm = 1;
> +      else
> +	spimm = 0;
> +      multi_push_additional = spimm * ZCMP_SP_INC_STEP;
> +      frame->multi_push_adj_addi = multi_push_additional;
> +      remaining_size -= multi_push_additional;
> +
> +      /* emit multi push insn & dwarf along with it.  */
> +      stack_adj = frame->multi_push_adj_base + multi_push_additional;
> +      insn = emit_insn (riscv_gen_multi_push_pop_insn (
> +	PUSH_IDX, -stack_adj, riscv_multi_push_regs_count (frame->mask)));
> +      dwarf = riscv_adjust_multi_push_cfi_prologue (stack_adj);
> +      RTX_FRAME_RELATED_P (insn) = 1;
> +      REG_NOTES (insn) = dwarf;
> +
> +      /* Temporarily fib that we need not save GPRs.  */
> +      frame->mask = 0;
> +
> +      /* push FPRs into the addtional reserved space by cm.push. */
> +      if (fmask)
> +	{
> +	  unsigned mask_fprs_push
> +	    = get_multi_push_fpr_mask (multi_push_additional / UNITS_PER_WORD);
> +	  frame->fmask &= mask_fprs_push;
> +	  riscv_for_each_saved_reg (remaining_size, riscv_save_reg, false,
> +				    false);
> +	  frame->fmask = fmask & ~mask_fprs_push; /* mask for the rest FPRs.  */
> +	}
> +    }
>     /* When optimizing for size, call a subroutine to save the registers.  */
> -  if (riscv_use_save_libcall (frame))
> +  else if (riscv_use_save_libcall (frame))
>       {
>         rtx dwarf = NULL_RTX;
>         dwarf = riscv_adjust_libcall_cfi_prologue ();
> @@ -6135,17 +6358,20 @@ riscv_expand_prologue (void)
>     /* Save the registers.  */
>     if ((frame->mask | frame->fmask) != 0)
>       {
> -      HOST_WIDE_INT step1 = riscv_first_stack_step (frame, remaining_size);
> -
> -      insn = gen_add3_insn (stack_pointer_rtx,
> -			    stack_pointer_rtx,
> -			    GEN_INT (-step1));
> -      RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
> -      remaining_size -= step1;
> +      if (known_gt (remaining_size, frame->frame_pointer_offset))
> +	{
> +	  HOST_WIDE_INT step1 = riscv_first_stack_step (frame, remaining_size);
> +	  remaining_size -= step1;
> +	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
> +				GEN_INT (-step1));
> +	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
> +	}
>         riscv_for_each_saved_reg (remaining_size, riscv_save_reg, false, false);
>       }
>   
> -  frame->mask = mask; /* Undo the above fib.  */
> +  /* Undo the above fib.  */
> +  frame->mask = mask;
> +  frame->fmask = fmask;
>   
>     /* Set up the frame pointer, if we're using one.  */
>     if (frame_pointer_needed)
> @@ -6198,6 +6424,32 @@ riscv_expand_prologue (void)
>       }
>   }
>   
> +static rtx
> +riscv_adjust_multi_pop_cfi_epilogue (int saved_size)
> +{
> +  rtx dwarf = NULL_RTX;
> +  rtx adjust_sp_rtx, reg;
> +  unsigned int mask = cfun->machine->frame.mask;
> +
> +  if (mask & S10_MASK)
> +    mask |= S11_MASK;
> +
> +  /* Debug info for adjust sp.  */
> +  adjust_sp_rtx
> +    = gen_rtx_SET (stack_pointer_rtx,
> +		   plus_constant (Pmode, stack_pointer_rtx, saved_size));
> +  dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, dwarf);
> +
> +  for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
> +    if (BITSET_P (mask, regno - GP_REG_FIRST))
> +      {
> +	reg = gen_rtx_REG (Pmode, regno);
> +	dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
> +      }
> +
> +  return dwarf;
> +}
> +
>   static rtx
>   riscv_adjust_libcall_cfi_epilogue ()
>   {
> @@ -6236,11 +6488,24 @@ riscv_expand_epilogue (int style)
>        Start off by assuming that no registers need to be restored.  */
>     struct riscv_frame_info *frame = &cfun->machine->frame;
>     unsigned mask = frame->mask;
> +  unsigned fmask = frame->fmask;
> +  unsigned mask_fprs_push = 0;
>     HOST_WIDE_INT step2 = 0;
> -  bool use_restore_libcall = ((style == NORMAL_RETURN)
> -			      && riscv_use_save_libcall (frame));
> -  unsigned libcall_size = (use_restore_libcall
> -			   ? frame->save_libcall_adjustment : 0);
> +  bool use_multi_pop_normal
> +    = ((style == NORMAL_RETURN) && riscv_use_multi_push (frame));
> +  bool use_multi_pop_sibcall
> +    = ((style == SIBCALL_RETURN) && riscv_use_multi_push (frame));
> +  bool use_multi_pop = use_multi_pop_normal || use_multi_pop_sibcall;
> +
> +  bool use_restore_libcall
> +    = !use_multi_pop
> +      && ((style == NORMAL_RETURN) && riscv_use_save_libcall (frame));
> +  unsigned libcall_size = use_restore_libcall && !use_multi_pop
> +			    ? frame->save_libcall_adjustment
> +			    : 0;
> +  unsigned multipop_size
> +    = use_multi_pop ? frame->multi_push_adj_base + frame->multi_push_adj_addi
> +		    : 0;
>     rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
>     rtx insn;
>   
> @@ -6311,18 +6576,26 @@ riscv_expand_epilogue (int style)
>         REG_NOTES (insn) = dwarf;
>       }
>   
> -  if (use_restore_libcall)
> -    frame->mask = 0; /* Temporarily fib for GPRs.  */
> +  if (use_restore_libcall || use_multi_pop)
> +    frame->mask = 0; /* Temporarily fib that we need not restore GPRs.  */
>   
>     /* If we need to restore registers, deallocate as much stack as
>        possible in the second step without going out of range.  */
> -  if ((frame->mask | frame->fmask) != 0)
> +  if (use_multi_pop)
> +    {
> +      if (frame->fmask
> +	  && known_gt (frame->total_size - multipop_size,
> +		       frame->frame_pointer_offset))
> +	step2
> +	  = riscv_first_stack_step (frame, frame->total_size - multipop_size);
> +    }
> +  else if ((frame->mask | frame->fmask) != 0)
>       step2 = riscv_first_stack_step (frame, frame->total_size - libcall_size);
>   
> -  if (use_restore_libcall)
> +  if (use_restore_libcall || use_multi_pop)
>       frame->mask = mask; /* Undo the above fib.  */
>   
> -  poly_int64 step1 = frame->total_size - step2 - libcall_size;
> +  poly_int64 step1 = frame->total_size - step2 - libcall_size - multipop_size;
>   
>     /* Set TARGET to BASE + STEP1.  */
>     if (known_gt (step1, 0))
> @@ -6356,8 +6629,9 @@ riscv_expand_epilogue (int style)
>   					   stack_pointer_rtx,
>   					   adjust));
>   	  rtx dwarf = NULL_RTX;
> -	  rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
> -					     GEN_INT (step2 + libcall_size));
> +	  rtx cfa_adjust_rtx
> +	    = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
> +			    GEN_INT (step2 + libcall_size + multipop_size));
>   
>   	  dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf);
>   	  RTX_FRAME_RELATED_P (insn) = 1;
> @@ -6372,16 +6646,26 @@ riscv_expand_epilogue (int style)
>         epilogue_cfa_sp_offset = step2;
>       }
>   
> -  if (use_restore_libcall)
> -    frame->mask = 0; /* Temporarily fib that we need not save GPRs.  */
> +  if (use_multi_pop)
> +    {
> +      frame->mask = 0; /* Temporarily fib that we need not restore GPRs.  */
> +      if (fmask)
> +	{
> +	  mask_fprs_push = get_multi_push_fpr_mask (frame->multi_push_adj_addi
> +						    / UNITS_PER_WORD);
> +	  frame->fmask &= ~mask_fprs_push; /* FPRs not saved by cm.push  */
> +	}
> +    }
> +  else if (use_restore_libcall)
> +    frame->mask = 0; /* Temporarily fib that we need not restore GPRs.  */
>   
>     /* Restore the registers.  */
> -  riscv_for_each_saved_reg (frame->total_size - step2 - libcall_size,
> -			    riscv_restore_reg,
> -			    true, style == EXCEPTION_RETURN);
> +  riscv_for_each_saved_reg (frame->total_size - step2 - libcall_size
> +			      - multipop_size,
> +			    riscv_restore_reg, true, style == EXCEPTION_RETURN);
>   
>     if (use_restore_libcall)
> -      frame->mask = mask; /* Undo the above fib.  */
> +    frame->mask = mask; /* Undo the above fib.  */
>   
>     if (need_barrier_p)
>       riscv_emit_stack_tie ();
> @@ -6393,15 +6677,43 @@ riscv_expand_epilogue (int style)
>   				       GEN_INT (step2)));
>   
>         rtx dwarf = NULL_RTX;
> -      rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
> -					 GEN_INT (libcall_size));
> +      rtx cfa_adjust_rtx
> +	= gen_rtx_PLUS (Pmode, stack_pointer_rtx,
> +			GEN_INT (libcall_size + multipop_size));
>         dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf);
>         RTX_FRAME_RELATED_P (insn) = 1;
>   
>         REG_NOTES (insn) = dwarf;
>       }
>   
> -  if (use_restore_libcall)
> +  if (use_multi_pop)
> +    {
> +      /* restore FPRs pushed by cm.push. */
> +      frame->fmask = fmask & mask_fprs_push;
> +      if (frame->fmask)
> +	riscv_for_each_saved_reg (frame->total_size - libcall_size
> +				    - multipop_size,
> +				  riscv_restore_reg, true,
> +				  style == EXCEPTION_RETURN);
> +      /* Undo the above fib.  */
> +      frame->mask = mask;
> +      frame->fmask = fmask;
> +      unsigned regs_count = riscv_multi_push_regs_count (frame->mask);
> +      if (use_multi_pop_normal)
> +	insn = emit_jump_insn (riscv_gen_multi_push_pop_insn (POPRET_IDX,
> +							      multipop_size,
> +							      regs_count));
> +      else
> +	insn = emit_insn (
> +	  riscv_gen_multi_push_pop_insn (POP_IDX, multipop_size, regs_count));
> +
> +      rtx dwarf = riscv_adjust_multi_pop_cfi_epilogue (multipop_size);
> +      RTX_FRAME_RELATED_P (insn) = 1;
> +      REG_NOTES (insn) = dwarf;
> +      if (use_multi_pop_normal)
> +	return;
> +    }
> +  else if (use_restore_libcall)
>       {
>         rtx dwarf = riscv_adjust_libcall_cfi_epilogue ();
>         insn = emit_insn (gen_gpr_restore (GEN_INT (riscv_save_libcall_count (mask))));
> @@ -7744,6 +8056,27 @@ riscv_gen_gpr_save_insn (struct riscv_frame_info *frame)
>     return gen_rtx_PARALLEL (VOIDmode, vec);
>   }
>   
> +static HOST_WIDE_INT
> +zcmp_base_adj (int regs_num)
> +{
> +  return riscv_16bytes_align ((regs_num) *GET_MODE_SIZE (word_mode));
> +}
> +
> +static HOST_WIDE_INT
> +zcmp_additional_adj (HOST_WIDE_INT total, int regs_num)
> +{
> +  return total - zcmp_base_adj (regs_num);
> +}
> +
> +bool
> +riscv_zcmp_valid_stack_adj_bytes_p (HOST_WIDE_INT total, int regs_num)
> +{
> +  HOST_WIDE_INT additioanl_bytes = zcmp_additional_adj (total, regs_num);
> +  return additioanl_bytes == 0 || additioanl_bytes == 1 * ZCMP_SP_INC_STEP
> +	 || additioanl_bytes == 2 * ZCMP_SP_INC_STEP
> +	 || additioanl_bytes == ZCMP_MAX_SPIMM * ZCMP_SP_INC_STEP;
> +}
> +
>   /* Return true if it's valid gpr_save pattern.  */
>   
>   bool
> diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
> index e18a0081297..42b6eb784d4 100644
> --- a/gcc/config/riscv/riscv.h
> +++ b/gcc/config/riscv/riscv.h
> @@ -420,6 +420,29 @@ ASM_MISA_SPEC
>   #define RISCV_CALL_ADDRESS_TEMP(MODE) \
>     gen_rtx_REG (MODE, RISCV_CALL_ADDRESS_TEMP_REGNUM)
>   
> +#define RETURN_ADDR_MASK (1 << RETURN_ADDR_REGNUM)
> +#define S0_MASK (1 << S0_REGNUM)
> +#define S1_MASK (1 << S1_REGNUM)
> +#define S2_MASK (1 << S2_REGNUM)
> +#define S3_MASK (1 << S3_REGNUM)
> +#define S4_MASK (1 << S4_REGNUM)
> +#define S5_MASK (1 << S5_REGNUM)
> +#define S6_MASK (1 << S6_REGNUM)
> +#define S7_MASK (1 << S7_REGNUM)
> +#define S8_MASK (1 << S8_REGNUM)
> +#define S9_MASK (1 << S9_REGNUM)
> +#define S10_MASK (1 << S10_REGNUM)
> +#define S11_MASK (1 << S11_REGNUM)
> +
> +#define MULTI_PUSH_GPR_MASK                                                    \
> +  (RETURN_ADDR_MASK | S0_MASK | S1_MASK | S2_MASK | S3_MASK | S4_MASK          \
> +   | S5_MASK | S6_MASK | S7_MASK | S8_MASK | S9_MASK | S10_MASK | S11_MASK)
> +#define ZCMP_MAX_SPIMM 3
> +#define ZCMP_SP_INC_STEP 16
> +#define ZCMP_INVALID_S0S10_SREGS_COUNTS 11
> +#define ZCMP_S0S11_SREGS_COUNTS 12
> +#define ZCMP_MAX_GRP_SLOTS 13
> +
>   #define MCOUNT_NAME "_mcount"
>   
>   #define NO_PROFILE_COUNTERS 1
> @@ -655,6 +678,8 @@ enum reg_class
>     ((REGNO) >= 8 && (REGNO) <= 9 ? (REGNO) - 8 :		\
>      (REGNO) >= 18 && (REGNO) <= 27 ? (REGNO) - 16 : -1)
>   
> +#define CALLEE_SAVED_FREG_NUMBER(REGNO) CALLEE_SAVED_REG_NUMBER (REGNO - 32)
> +
>   #define LIBCALL_VALUE(MODE) \
>     riscv_function_value (NULL_TREE, NULL_TREE, MODE)
>   
> diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
> index 47d14d99903..f489646cec3 100644
> --- a/gcc/config/riscv/riscv.md
> +++ b/gcc/config/riscv/riscv.md
> @@ -124,6 +124,7 @@
>   
>   (define_constants
>     [(RETURN_ADDR_REGNUM		1)
> +   (SP_REGNUM 			2)
>      (GP_REGNUM 			3)
>      (TP_REGNUM			4)
>      (T0_REGNUM			5)
> @@ -3431,3 +3432,4 @@
>   (include "thead.md")
>   (include "vector.md")
>   (include "zicond.md")
> +(include "zc.md")
> diff --git a/gcc/config/riscv/zc.md b/gcc/config/riscv/zc.md
> new file mode 100644
> index 00000000000..5c1bf031b8d
> --- /dev/null
> +++ b/gcc/config/riscv/zc.md
> @@ -0,0 +1,1042 @@
> +;; Machine description for RISC-V Zc extention.
> +;; Copyright (C) 2023 Free Software Foundation, Inc.
> +;; Contributed by Fei Gao (gaofei@eswincomputing.com).
> +
> +;; This file is part of GCC.
> +
> +;; GCC is free software; you can redistribute it and/or modify
> +;; it under the terms of the GNU General Public License as published by
> +;; the Free Software Foundation; either version 3, or (at your option)
> +;; any later version.
> +
> +;; GCC is distributed in the hope that it will be useful,
> +;; but WITHOUT ANY WARRANTY; without even the implied warranty of
> +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +;; GNU General Public License for more details.
> +
> +;; You should have received a copy of the GNU General Public License
> +;; along with GCC; see the file COPYING3.  If not see
> +;;<http://www.gnu.org/licenses/>.
> +
> +(define_insn "@gpr_multi_pop_up_to_ra_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_ra_operand" "I")))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s0_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s0_operand" "I")))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s1_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s1_operand" "I")))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s1}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s2_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s2_operand" "I")))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s2}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s3_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s3_operand" "I")))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s3}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s4_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s4_operand" "I")))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s4}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s5_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s5_operand" "I")))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s5}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s6_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s6_operand" "I")))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s6}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s7_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s7_operand" "I")))
> +   (set (reg:X S7_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                      (const_int <slot8_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s7}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s8_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s8_operand" "I")))
> +   (set (reg:X S8_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S7_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot8_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s8}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s9_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s9_operand" "I")))
> +   (set (reg:X S9_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S8_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S7_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                      (const_int <slot8_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot10_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s9}, %0"
> +)
> +
> +(define_insn "@gpr_multi_pop_up_to_s11_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s11_operand" "I")))
> +   (set (reg:X S11_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S10_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S9_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S8_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S7_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                      (const_int <slot8_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot10_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot11_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot12_offset>))))]
> +  "TARGET_ZCMP"
> +  "cm.pop	{ra, s0-s11}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_ra_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_ra_operand" "I")))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s0_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s0_operand" "I")))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s1_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s1_operand" "I")))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s1}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s2_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s2_operand" "I")))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s2}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s3_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s3_operand" "I")))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s3}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s4_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s4_operand" "I")))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s4}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s5_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s5_operand" "I")))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s5}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s6_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s6_operand" "I")))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s6}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s7_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s7_operand" "I")))
> +   (set (reg:X S7_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot8_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s7}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s8_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s8_operand" "I")))
> +   (set (reg:X S8_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S7_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot8_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s8}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s9_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s9_operand" "I")))
> +   (set (reg:X S9_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S8_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S7_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot8_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot10_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s9}, %0"
> +)
> +
> +(define_insn "@gpr_multi_popret_up_to_s11_<mode>"
> +  [(set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_pop_up_to_s11_operand" "I")))
> +   (set (reg:X S11_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>))))
> +   (set (reg:X S10_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>))))
> +   (set (reg:X S9_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>))))
> +   (set (reg:X S8_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>))))
> +   (set (reg:X S7_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>))))
> +   (set (reg:X S6_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>))))
> +   (set (reg:X S5_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>))))
> +   (set (reg:X S4_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>))))
> +   (set (reg:X S3_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                      (const_int <slot8_offset>))))
> +   (set (reg:X S2_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>))))
> +   (set (reg:X S1_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot10_offset>))))
> +   (set (reg:X S0_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot11_offset>))))
> +   (set (reg:X RETURN_ADDR_REGNUM)
> +        (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot12_offset>))))
> +   (return)
> +   (use (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_ZCMP"
> +  "cm.popret	{ra, s0-s11}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_ra_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_ra_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s0_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s0_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s1_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s1_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s1}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s2_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s2_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s2}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s3_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S3_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s3_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s3}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s4_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S4_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S3_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s4_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s4}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s5_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S5_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S4_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S3_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s5_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s5}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s6_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S6_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S5_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S4_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X S3_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s6_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s6}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s7_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S7_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S6_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S5_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X S4_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>)))
> +        (reg:X S3_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                      (const_int <slot8_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s7_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s7}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s8_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S8_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S7_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S6_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X S5_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>)))
> +        (reg:X S4_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>)))
> +        (reg:X S3_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot8_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s8_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s8}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s9_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S9_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S8_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S7_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X S6_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>)))
> +        (reg:X S5_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>)))
> +        (reg:X S4_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>)))
> +        (reg:X S3_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot8_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot10_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s9_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s9}, %0"
> +)
> +
> +(define_insn "@gpr_multi_push_up_to_s11_<mode>"
> +  [(set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot0_offset>)))
> +        (reg:X S11_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot1_offset>)))
> +        (reg:X S10_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot2_offset>)))
> +        (reg:X S9_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot3_offset>)))
> +        (reg:X S8_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot4_offset>)))
> +        (reg:X S7_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot5_offset>)))
> +        (reg:X S6_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot6_offset>)))
> +        (reg:X S5_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot7_offset>)))
> +        (reg:X S4_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot8_offset>)))
> +        (reg:X S3_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot9_offset>)))
> +        (reg:X S2_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot10_offset>)))
> +        (reg:X S1_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot11_offset>)))
> +        (reg:X S0_REGNUM))
> +   (set (mem:X (plus:X (reg:X SP_REGNUM)
> +                       (const_int <slot12_offset>)))
> +        (reg:X RETURN_ADDR_REGNUM))
> +   (set (reg:X SP_REGNUM)
> +        (plus:X (reg:X SP_REGNUM)
> +                 (match_operand 0 "stack_push_up_to_s11_operand" "I")))]
> +  "TARGET_ZCMP"
> +  "cm.push	{ra, s0-s11}, %0"
> +)
> diff --git a/gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c b/gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c
> new file mode 100644
> index 00000000000..57c83249741
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c
> @@ -0,0 +1,256 @@
> +/* { dg-do compile } */
> +/* { dg-options " -Os -march=rv32e_zca_zcmp -mabi=ilp32e -mcmodel=medlow -fno-shrink-wrap-separate" } */
> +/* { dg-skip-if "" { *-*-* } {"-O0" "-O1" "-O2" "-Og" "-O3" "-Oz" "-flto"} } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +char
> +my_getchar ();
> +float
> +getf ();
> +int __attribute__ ((noinline))
> +incoming_stack_args (int arg0, int arg1, int arg2, int arg3, int arg4, int arg5,
> +		     int arg6, int arg7, int arg8);
> +int
> +getint ();
> +void
> +PrintInts (int n, ...);						 // varargs
> +void __attribute__ ((noinline)) PrintIntsNoVaStart (int n, ...); // varargs
> +void
> +PrintInts2 (int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int n,
> +	    ...);
> +extern void
> +f1 (void);
> +extern void
> +f2 (void);
> +
> +/*
> +**test1:
> +**	...
> +**	cm.push	{ra, s0-s1}, -64
> +**	...
> +**	cm.popret	{ra, s0-s1}, 64
> +**	...
> +*/
> +int
> +test1 ()
> +{
> +  char volatile array[3120];
> +  float volatile farray[3120];
> +
> +  float sum = 0;
> +  for (int i = 0; i < 3120; i++)
> +    {
> +      array[i] = my_getchar ();
> +      farray[i] = my_getchar () * 1.2;
> +      sum += array[i] + farray[i];
> +    }
> +  return sum;
> +}
> +
> +/*
> +**test2_step1_0_size:
> +**	...
> +**	cm.push	{ra, s0}, -64
> +**	...
> +**	cm.popret	{ra, s0}, 64
> +**	...
> +*/
> +int
> +test2_step1_0_size ()
> +{
> +  int volatile iarray[3120 + 1824 / 4 - 8];
> +
> +  for (int i = 0; i < 3120 + 1824 / 4 - 8; i++)
> +    {
> +      iarray[i] = my_getchar () * 2;
> +    }
> +  return iarray[0] + iarray[1];
> +}
> +
> +/*
> +**test3:
> +**	...
> +**	cm.push	{ra, s0-s1}, -64
> +**	...
> +**	cm.popret	{ra, s0-s1}, 64
> +**	...
> +*/
> +float
> +test3 ()
> +{
> +  char volatile array[3120];
> +  float volatile farray[3120];
> +
> +  float sum = 0, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0;
> +
> +  for (int i = 0; i < 3120; i++)
> +    {
> +      f1 = getf ();
> +      f2 = getf ();
> +      f3 = getf ();
> +      f4 = getf ();
> +      array[i] = my_getchar ();
> +      farray[i] = my_getchar () * 1.2;
> +      sum += array[i] + farray[i] + f1 + f2 + f3 + f4;
> +    }
> +  return sum;
> +}
> +
> +/*
> +**outgoing_stack_args:
> +**	...
> +**	cm.push	{ra, s0}, -32
> +**	...
> +**	cm.popret	{ra, s0}, 32
> +**	...
> +*/
> +int
> +outgoing_stack_args ()
> +{
> +  int local = getint ();
> +  return local + incoming_stack_args (0, 1, 2, 3, 4, 5, 6, 7, 8);
> +}
> +
> +/*
> +**callPrintInts:
> +**	...
> +**	cm.push	{ra}, -32
> +**	...
> +**	cm.popret	{ra}, 32
> +**	...
> +*/
> +float
> +callPrintInts ()
> +{
> +  volatile float f = getf (); // f in local
> +  PrintInts (9, 1, 2, 3, 4, 5, 6, 7, 8, 9);
> +  return f;
> +}
> +
> +/*
> +**callPrint:
> +**	...
> +**	cm.push	{ra}, -32
> +**	...
> +**	cm.popret	{ra}, 32
> +**	...
> +*/
> +float
> +callPrint ()
> +{
> +  volatile float f = getf (); // f in local
> +  PrintIntsNoVaStart (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
> +  return f;
> +}
> +
> +/*
> +**callPrint_S:
> +**	...
> +**	cm.push	{ra, s0}, -32
> +**	...
> +**	cm.popret	{ra, s0}, 32
> +**	...
> +*/
> +float
> +callPrint_S ()
> +{
> +  float f = getf ();
> +  PrintIntsNoVaStart (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
> +  return f;
> +}
> +
> +/*
> +**callPrint_2:
> +**	...
> +**	cm.push	{ra, s0}, -32
> +**	...
> +**	cm.popret	{ra, s0}, 32
> +**	...
> +*/
> +float
> +callPrint_2 ()
> +{
> +  float f = getf ();
> +  PrintInts2 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
> +  return f;
> +}
> +
> +/*
> +**test_step1_0bytes_save_restore:
> +**	...
> +**	cm.push	{ra}, -16
> +**	...
> +**	cm.popret	{ra}, 16
> +**	...
> +*/
> +int
> +test_step1_0bytes_save_restore ()
> +{
> +  int a = 9;
> +  int b = my_getchar ();
> +  return a + b;
> +}
> +
> +/*
> +**test_s0:
> +**	...
> +**	cm.push	{ra, s0}, -16
> +**	...
> +**	cm.popret	{ra, s0}, 16
> +**	...
> +*/
> +int
> +test_s0 ()
> +{
> +  int a = my_getchar ();
> +  int b = my_getchar ();
> +  return a + b;
> +}
> +
> +/*
> +**test_s1:
> +**	...
> +**	cm.push	{ra, s0-s1}, -16
> +**	...
> +**	cm.popret	{ra, s0-s1}, 16
> +**	...
> +*/
> +int
> +test_s1 ()
> +{
> +  int s0 = my_getchar ();
> +  int s1 = my_getchar ();
> +  int b = my_getchar ();
> +  return s1 + s0 + b;
> +}
> +
> +/*
> +**test_f0:
> +**	...
> +**	cm.push	{ra, s0-s1}, -16
> +**	...
> +**	cm.popret	{ra, s0-s1}, 16
> +**	...
> +*/
> +int
> +test_f0 ()
> +{
> +  int s0 = my_getchar ();
> +  float f0 = getf ();
> +  int b = my_getchar ();
> +  return f0 + s0 + b;
> +}
> +
> +/*
> +**foo:
> +**	cm.push	{ra}, -16
> +**	call	f1
> +**	cm.pop	{ra}, 16
> +**	tail	f2
> +*/
> +void
> +foo (void)
> +{
> +  f1 ();
> +  f2 ();
> +}
> diff --git a/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
> new file mode 100644
> index 00000000000..f24e0a8bab9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
> @@ -0,0 +1,256 @@
> +/* { dg-do compile } */
> +/* { dg-options " -Os -march=rv32imaf_zca_zcmp -mabi=ilp32f -mcmodel=medlow -fno-shrink-wrap-separate" }*/
> +/* { dg-skip-if "" { *-*-* } {"-O0" "-O1" "-O2" "-Og" "-O3" "-Oz" "-flto"} } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +char
> +my_getchar ();
> +float
> +getf ();
> +int __attribute__ ((noinline))
> +incoming_stack_args (int arg0, int arg1, int arg2, int arg3, int arg4, int arg5,
> +		     int arg6, int arg7, int arg8);
> +int
> +getint ();
> +void
> +PrintInts (int n, ...);						 // varargs
> +void __attribute__ ((noinline)) PrintIntsNoVaStart (int n, ...); // varargs
> +void
> +PrintInts2 (int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int n,
> +	    ...);
> +extern void
> +f1 (void);
> +extern void
> +f2 (void);
> +
> +/*
> +**test1:
> +**	...
> +**	cm.push	{ra, s0-s4}, -80
> +**	...
> +**	cm.popret	{ra, s0-s4}, 80
> +**	...
> +*/
> +int
> +test1 ()
> +{
> +  char volatile array[3120];
> +  float volatile farray[3120];
> +
> +  float sum = 0;
> +  for (int i = 0; i < 3120; i++)
> +    {
> +      array[i] = my_getchar ();
> +      farray[i] = my_getchar () * 1.2;
> +      sum += array[i] + farray[i];
> +    }
> +  return sum;
> +}
> +
> +/*
> +**test2_step1_0_size:
> +**	...
> +**	cm.push	{ra, s0-s1}, -64
> +**	...
> +**	cm.popret	{ra, s0-s1}, 64
> +**	...
> +*/
> +int
> +test2_step1_0_size ()
> +{
> +  int volatile iarray[3120 + 1824 / 4 - 8];
> +
> +  for (int i = 0; i < 3120 + 1824 / 4 - 8; i++)
> +    {
> +      iarray[i] = my_getchar () * 2;
> +    }
> +  return iarray[0] + iarray[1];
> +}
> +
> +/*
> +**test3:
> +**	...
> +**	cm.push	{ra, s0-s4}, -80
> +**	...
> +**	cm.popret	{ra, s0-s4}, 80
> +**	...
> +*/
> +float
> +test3 ()
> +{
> +  char volatile array[3120];
> +  float volatile farray[3120];
> +
> +  float sum = 0, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0;
> +
> +  for (int i = 0; i < 3120; i++)
> +    {
> +      f1 = getf ();
> +      f2 = getf ();
> +      f3 = getf ();
> +      f4 = getf ();
> +      array[i] = my_getchar ();
> +      farray[i] = my_getchar () * 1.2;
> +      sum += array[i] + farray[i] + f1 + f2 + f3 + f4;
> +    }
> +  return sum;
> +}
> +
> +/*
> +**outgoing_stack_args:
> +**	...
> +**	cm.push	{ra, s0}, -32
> +**	...
> +**	cm.popret	{ra, s0}, 32
> +**	...
> +*/
> +int
> +outgoing_stack_args ()
> +{
> +  int local = getint ();
> +  return local + incoming_stack_args (0, 1, 2, 3, 4, 5, 6, 7, 8);
> +}
> +
> +/*
> +**callPrintInts:
> +**	...
> +**	cm.push	{ra}, -48
> +**	...
> +**	cm.popret	{ra}, 48
> +**	...
> +*/
> +float
> +callPrintInts ()
> +{
> +  volatile float f = getf (); // f in local
> +  PrintInts (9, 1, 2, 3, 4, 5, 6, 7, 8, 9);
> +  return f;
> +}
> +
> +/*
> +**callPrint:
> +**	...
> +**	cm.push	{ra}, -48
> +**	...
> +**	cm.popret	{ra}, 48
> +**	...
> +*/
> +float
> +callPrint ()
> +{
> +  volatile float f = getf (); // f in local
> +  PrintIntsNoVaStart (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
> +  return f;
> +}
> +
> +/*
> +**callPrint_S:
> +**	...
> +**	cm.push	{ra}, -48
> +**	...
> +**	cm.popret	{ra}, 48
> +**	...
> +*/
> +float
> +callPrint_S ()
> +{
> +  float f = getf ();
> +  PrintIntsNoVaStart (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
> +  return f;
> +}
> +
> +/*
> +**callPrint_2:
> +**	...
> +**	cm.push	{ra}, -48
> +**	...
> +**	cm.popret	{ra}, 48
> +**	...
> +*/
> +float
> +callPrint_2 ()
> +{
> +  float f = getf ();
> +  PrintInts2 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
> +  return f;
> +}
> +
> +/*
> +**test_step1_0bytes_save_restore:
> +**	...
> +**	cm.push	{ra}, -16
> +**	...
> +**	cm.popret	{ra}, 16
> +**	...
> +*/
> +int
> +test_step1_0bytes_save_restore ()
> +{
> +  int a = 9;
> +  int b = my_getchar ();
> +  return a + b;
> +}
> +
> +/*
> +**test_s0:
> +**	...
> +**	cm.push	{ra, s0}, -16
> +**	...
> +**	cm.popret	{ra, s0}, 16
> +**	...
> +*/
> +int
> +test_s0 ()
> +{
> +  int a = my_getchar ();
> +  int b = my_getchar ();
> +  return a + b;
> +}
> +
> +/*
> +**test_s1:
> +**	...
> +**	cm.push	{ra, s0-s1}, -16
> +**	...
> +**	cm.popret	{ra, s0-s1}, 16
> +**	...
> +*/
> +int
> +test_s1 ()
> +{
> +  int s0 = my_getchar ();
> +  int s1 = my_getchar ();
> +  int b = my_getchar ();
> +  return s1 + s0 + b;
> +}
> +
> +/*
> +**test_f0:
> +**	...
> +**	cm.push	{ra, s0}, -32
> +**	...
> +**	cm.popret	{ra, s0}, 32
> +**	...
> +*/
> +int
> +test_f0 ()
> +{
> +  int s0 = my_getchar ();
> +  float f0 = getf ();
> +  int b = my_getchar ();
> +  return f0 + s0 + b;
> +}
> +
> +/*
> +**foo:
> +**	cm.push	{ra}, -16
> +**	call	f1
> +**	cm.pop	{ra}, 16
> +**	tail	f2
> +*/
> +void
> +foo (void)
> +{
> +  f1 ();
> +  f2 ();
> +}
> diff --git a/gcc/testsuite/gcc.target/riscv/zcmp_push_fpr.c b/gcc/testsuite/gcc.target/riscv/zcmp_push_fpr.c
> new file mode 100644
> index 00000000000..530b35b53dd
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zcmp_push_fpr.c
> @@ -0,0 +1,34 @@
> +/* { dg-do compile } */
> +/* { dg-options "-march=rv64imafd_zicsr_zifencei_zca_zcmp -mabi=lp64d -Os -fno-shrink-wrap-separate" } */
> +/* { dg-skip-if "" { *-*-* } {"-O0" "-O1" "-O2" "-Og" "-O3" "-Oz" "-flto"} } */
> +
> +typedef struct
> +{
> +  struct
> +  {
> +    struct
> +    {
> +      struct
> +      {
> +	long a;
> +      };
> +    } a[129];
> +  };
> +} b;
> +
> +struct c
> +{
> +  void *a[129];
> +};
> +
> +extern void
> +f (struct c, b *);
> +
> +struct c
> +d ()
> +{
> +  struct c a;
> +  __builtin_unwind_init ();
> +  b e;
> +  f (a, &e);
> +}
> diff --git a/gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c b/gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c
> new file mode 100644
> index 00000000000..2f2fa55baac
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c
> @@ -0,0 +1,24 @@
> +/* { dg-do compile } */
> +/* { dg-options " -O0 -march=rv32e_zca_zcb_zcmp -mabi=ilp32e -mcmodel=medlow -fomit-frame-pointer -fno-shrink-wrap-separate" } */
> +/* { dg-skip-if "" { *-*-* } {"-O2" "-O1" "-Os" "-Og" "-O3" "-Oz" "-flto"} } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +void
> +bar ();
> +
> +/*
> +**fool_rv32e:
> +**	cm.push	{ra}, -32
> +**	...
> +**	call	bar
> +**	...
> +**	lw	a[0-5],32\(sp\)
> +**	...
> +**	cm.popret	{ra}, 32
> +*/
> +int
> +fool_rv32e (int a0, int a1, int a2, int a3, int a4, int a5, int incoming0)
> +{
> +  bar ();
> +  return a0 + a1 + a2 + a3 + a4 + a5 + incoming0;
> +}
  
Jeff Law Oct. 28, 2023, 2:32 a.m. UTC | #2
On 10/27/23 14:31, Patrick O'Neill wrote:
> Hi Fei,
> 
> A recent change to GCC [1] updated the  the registers in the cm.push and 
> cm.pop insns for these testcases:
> 
> |FAIL: gcc.target/riscv/rv32i_zcmp.c -Os check-function-bodies test1 
> FAIL: gcc.target/riscv/rv32i_zcmp.c -Os check-function-bodies 
> test2_step1_0_size FAIL: gcc.target/riscv/rv32i_zcmp.c -Os 
> check-function-bodies test3|
> 
> Debug log:
> 
> Executing on host: /github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/build/build-gcc-linux-stage2/gcc/xgcc -B/github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/build/build-gcc-linux-stage2/gcc/  /github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/gcc/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c  -march=rv64gcv -mabi=lp64d -mcmodel=medlow   -fdiagnostics-plain-output    -Os   -Os -march=rv32imaf_zca_zcmp -mabi=ilp32f -mcmodel=medlow -S   -o rv32i_zcmp.s    (timeout = 600)
> spawn -ignore SIGHUP /github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/build/build-gcc-linux-stage2/gcc/xgcc -B/github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/build/build-gcc-linux-stage2/gcc/ /github/patrick-postcommit-runner-1/_work/gcc-postcommit-ci/gcc-postcommit-ci/riscv-gnu-toolchain/gcc/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c -march=rv64gcv -mabi=lp64d -mcmodel=medlow -fdiagnostics-plain-output -Os -Os -march=rv32imaf_zca_zcmp -mabi=ilp32f -mcmodel=medlow -S -o rv32i_zcmp.s
> PASS: gcc.target/riscv/rv32i_zcmp.c   -Os  (test for excess errors)
> body: .*\tcm.push	{ra, s0-s4}, -80
> .*\tcm.popret	{ra, s0-s4}, 80
> .*
> against: 	lui	a5,%hi(.LC0)
> 	li	t0,-16384
> 	cm.push	{ra, s0-s6}, -80
> 	addi	t0,t0,816
> 	fsw	fs0,44(sp)
> 	lw	s2,%lo(.LC0)(a5)
> 	lw	s3,%lo(.LC0+4)(a5)
> 	fmv.s.x	fs0,zero
> 	li	a5,4096
> 	add	sp,sp,t0
> 	addi	a5,a5,-784
> 	li	s1,4096
> 	li	s0,0
> 	addi	s5,sp,-784
> 	add	s4,sp,a5
> 	addi	s1,s1,-976
> 	call	my_getchar
> 	add	s6,s5,s0
> 	sb	a0,784(s6)
> 	call	my_getchar
> 	call	__floatsidf
> 	mv	a2,s2
> 	mv	a3,s3
> 	call	__muldf3
> 	call	__truncdfsf2
> 	slli	a5,s0,2
> 	add	a5,s4,a5
> 	fsw	fa0,-192(a5)
> 	addi	s0,s0,1
> 	lbu	a4,784(s6)
> 	fcvt.s.w	fa5,a4
> 	flw	fa4,-192(a5)
> 	fadd.s	fa5,fa5,fa4
> 	fadd.s	fs0,fs0,fa5
> 	bne	s0,s1,.L2
> 	li	t0,16384
> 	addi	t0,t0,-816
> 	add	sp,sp,t0
> 	fcvt.w.s a0,fs0,rtz
> 	flw	fs0,44(sp)
> 	cm.popret	{ra, s0-s6}, 80
> 
> FAIL: gcc.target/riscv/rv32i_zcmp.c   -Os   check-function-bodies test1
> 
> Would  it be OK if we made the regex accept any s[0-9] register (or 
> would it be better if it was [1-9])?
> Proposed change:
I'd think your proposed change would be fine.


> 
> [1] It was one of these commits:
> https://github.com/gcc-mirror/gcc/compare/a4ca8691333344cecc595d1af8b21e51f588e2f2...4d49685d671e4e604b2b873ada65aaac89348794
Almost certainly the regsiter allocator change.

Jeff
  
Jeff Law Oct. 28, 2023, 2:35 a.m. UTC | #3
On 10/27/23 14:31, Patrick O'Neill wrote:
> Hi Fei,
> 
> A recent change to GCC [1] updated the  the registers in the cm.push and 
> cm.pop insns for these testcases:
> 
> |FAIL: gcc.target/riscv/rv32i_zcmp.c -Os check-function-bodies test1 
> FAIL: gcc.target/riscv/rv32i_zcmp.c -Os check-function-bodies 
> test2_step1_0_size FAIL: gcc.target/riscv/rv32i_zcmp.c -Os 
> check-function-bodies test3|
[ ... ]
Actually [1-9] looks better upon further review.

jeff
  
Fei Gao Oct. 30, 2023, 7:46 a.m. UTC | #4
On 2023-10-28 10:35  Jeff Law <jeffreyalaw@gmail.com> wrote:
>
>
>
>On 10/27/23 14:31, Patrick O'Neill wrote:
>> Hi Fei,
>>
>> A recent change to GCC [1] updated the  the registers in the cm.push and
>> cm.pop insns for these testcases:
>>
>> |FAIL: gcc.target/riscv/rv32i_zcmp.c -Os check-function-bodies test1
>> FAIL: gcc.target/riscv/rv32i_zcmp.c -Os check-function-bodies
>> test2_step1_0_size FAIL: gcc.target/riscv/rv32i_zcmp.c -Os
>> check-function-bodies test3|
>[ ... ]
>Actually [1-9] looks better upon further review. 

hi Patrick

Thanks for adapting the TCs.
I follow Jeff's advice {ra, s0-s[1-9]} for case {ra, s0-sx}.

BR
Fei
>
>jeff
  

Patch

diff --git a/gcc/config/riscv/iterators.md b/gcc/config/riscv/iterators.md
index a4070de1510..ecf033f2fa7 100644
--- a/gcc/config/riscv/iterators.md
+++ b/gcc/config/riscv/iterators.md
@@ -125,6 +125,21 @@ 
 (define_mode_attr shiftm1 [(SI "const_si_mask_operand") (DI "const_di_mask_operand")])
 (define_mode_attr shiftm1p [(SI "DsS") (DI "DsD")])
 
+; zcmp mode attribute
+(define_mode_attr slot0_offset  [(SI "-4")  (DI "-8")])
+(define_mode_attr slot1_offset  [(SI "-8")  (DI "-16")])
+(define_mode_attr slot2_offset  [(SI "-12") (DI "-24")])
+(define_mode_attr slot3_offset  [(SI "-16") (DI "-32")])
+(define_mode_attr slot4_offset  [(SI "-20") (DI "-40")])
+(define_mode_attr slot5_offset  [(SI "-24") (DI "-48")])
+(define_mode_attr slot6_offset  [(SI "-28") (DI "-56")])
+(define_mode_attr slot7_offset  [(SI "-32") (DI "-64")])
+(define_mode_attr slot8_offset  [(SI "-36") (DI "-72")])
+(define_mode_attr slot9_offset  [(SI "-40") (DI "-80")])
+(define_mode_attr slot10_offset [(SI "-44") (DI "-88")])
+(define_mode_attr slot11_offset [(SI "-48") (DI "-96")])
+(define_mode_attr slot12_offset [(SI "-52") (DI "-104")])
+
 ;; -------------------------------------------------------------------
 ;; Code Iterators
 ;; -------------------------------------------------------------------
diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
index 618ad607047..3ef09996a85 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -69,6 +69,102 @@ 
   (ior (match_operand 0 "const_0_operand")
        (match_operand 0 "register_operand")))
 
+(define_predicate "stack_push_up_to_ra_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 1)")))
+
+(define_predicate "stack_push_up_to_s0_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 2)")))
+
+(define_predicate "stack_push_up_to_s1_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 3)")))
+
+(define_predicate "stack_push_up_to_s2_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 4)")))
+
+(define_predicate "stack_push_up_to_s3_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 5)")))
+
+(define_predicate "stack_push_up_to_s4_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 6)")))
+
+(define_predicate "stack_push_up_to_s5_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 7)")))
+
+(define_predicate "stack_push_up_to_s6_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 8)")))
+
+(define_predicate "stack_push_up_to_s7_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 9)")))
+
+(define_predicate "stack_push_up_to_s8_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 10)")))
+
+(define_predicate "stack_push_up_to_s9_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 11)")))
+
+(define_predicate "stack_push_up_to_s11_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op) * -1, 13)")))
+
+(define_predicate "stack_pop_up_to_ra_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 1)")))
+
+(define_predicate "stack_pop_up_to_s0_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 2)")))
+
+(define_predicate "stack_pop_up_to_s1_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 3)")))
+
+(define_predicate "stack_pop_up_to_s2_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 4)")))
+
+(define_predicate "stack_pop_up_to_s3_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 5)")))
+
+(define_predicate "stack_pop_up_to_s4_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 6)")))
+
+(define_predicate "stack_pop_up_to_s5_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 7)")))
+
+(define_predicate "stack_pop_up_to_s6_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 8)")))
+
+(define_predicate "stack_pop_up_to_s7_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 9)")))
+
+(define_predicate "stack_pop_up_to_s8_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 10)")))
+
+(define_predicate "stack_pop_up_to_s9_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 11)")))
+
+(define_predicate "stack_pop_up_to_s11_operand"
+  (and (match_code "const_int")
+       (match_test "riscv_zcmp_valid_stack_adj_bytes_p (INTVAL (op), 13)")))
+
 ;; Only use branch-on-bit sequences when the mask is not an ANDI immediate.
 (define_predicate "branch_on_bit_operand"
   (and (match_code "const_int")
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 0e0470280f8..04885bbebd2 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -102,6 +102,8 @@  extern bool riscv_split_64bit_move_p (rtx, rtx);
 extern void riscv_split_doubleword_move (rtx, rtx);
 extern const char *riscv_output_move (rtx, rtx);
 extern const char *riscv_output_return ();
+extern bool
+riscv_zcmp_valid_stack_adj_bytes_p (HOST_WIDE_INT, int);
 
 #ifdef RTX_CODE
 extern void riscv_expand_int_scc (rtx, enum rtx_code, rtx, rtx, bool *invert_ptr = 0);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 1d6e278ea90..ed4d28b2eb0 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -112,6 +112,14 @@  struct GTY(())  riscv_frame_info {
   /* How much the GPR save/restore routines adjust sp (or 0 if unused).  */
   unsigned save_libcall_adjustment;
 
+  /* the minimum number of bytes, in multiples of 16-byte address increments,
+     required to cover the registers in a multi push & pop.  */
+  unsigned multi_push_adj_base;
+
+  /* the number of additional 16-byte address increments allocated for the stack
+     frame in a multi push & pop.  */
+  unsigned multi_push_adj_addi;
+
   /* Offsets of fixed-point and floating-point save areas from frame bottom */
   poly_int64 gp_sp_offset;
   poly_int64 fp_sp_offset;
@@ -409,6 +417,16 @@  static const struct riscv_tune_info riscv_tune_info_table[] = {
    function.  */
 static bool riscv_save_frame_pointer;
 
+typedef enum
+{
+  PUSH_IDX = 0,
+  POP_IDX,
+  POPRET_IDX,
+  ZCMP_OP_NUM
+} riscv_zcmp_op_t;
+
+typedef insn_code (*code_for_push_pop_t) (machine_mode);
+
 void riscv_frame_info::reset(void)
 {
   total_size = 0;
@@ -5539,6 +5557,34 @@  riscv_save_reg_p (unsigned int regno)
   return false;
 }
 
+/* Return TRUE if Zcmp push and pop insns should be
+   avoided. FALSE otherwise.
+   Only use multi push & pop if all GPRs masked can be covered,
+   and stack access is SP based,
+   and GPRs are at top of the stack frame,
+   and no conflicts in stack allocation with other features  */
+static bool
+riscv_avoid_multi_push (const struct riscv_frame_info *frame)
+{
+  if (!TARGET_ZCMP || crtl->calls_eh_return || frame_pointer_needed
+      || cfun->machine->interrupt_handler_p || cfun->machine->varargs_size != 0
+      || crtl->args.pretend_args_size != 0 || flag_shrink_wrap_separate
+      || (frame->mask & ~MULTI_PUSH_GPR_MASK))
+    return true;
+
+  return false;
+}
+
+/* Determine whether to use multi push insn.  */
+static bool
+riscv_use_multi_push (const struct riscv_frame_info *frame)
+{
+  if (riscv_avoid_multi_push (frame))
+    return false;
+
+  return (frame->multi_push_adj_base != 0);
+}
+
 /* Return TRUE if a libcall to save/restore GPRs should be
    avoided.  FALSE otherwise.  */
 static bool
@@ -5576,6 +5622,50 @@  riscv_save_libcall_count (unsigned mask)
   abort ();
 }
 
+/* calculate number of s regs in multi push and pop.
+   Note that {s0-s10} is not valid in Zcmp, use {s0-s11} instead.  */
+static unsigned
+riscv_multi_push_sregs_count (unsigned mask)
+{
+  unsigned num = riscv_save_libcall_count (mask);
+  return (num == ZCMP_INVALID_S0S10_SREGS_COUNTS) ? ZCMP_S0S11_SREGS_COUNTS
+						  : num;
+}
+
+/* calculate number of regs(ra, s0-sx) in multi push and pop.  */
+static unsigned
+riscv_multi_push_regs_count (unsigned mask)
+{
+  /* 1 is for ra  */
+  return riscv_multi_push_sregs_count (mask) + 1;
+}
+
+/* Handle 16 bytes align for poly_int.  */
+static poly_int64
+riscv_16bytes_align (poly_int64 value)
+{
+  return aligned_upper_bound (value, 16);
+}
+
+static HOST_WIDE_INT
+riscv_16bytes_align (HOST_WIDE_INT value)
+{
+  return ROUND_UP (value, 16);
+}
+
+/* Handle stack align for poly_int.  */
+static poly_int64
+riscv_stack_align (poly_int64 value)
+{
+  return aligned_upper_bound (value, PREFERRED_STACK_BOUNDARY / 8);
+}
+
+static HOST_WIDE_INT
+riscv_stack_align (HOST_WIDE_INT value)
+{
+  return RISCV_STACK_ALIGN (value);
+}
+
 /* Populate the current function's riscv_frame_info structure.
 
    RISC-V stack frames grown downward.  High addresses are at the top.
@@ -5601,7 +5691,7 @@  riscv_save_libcall_count (unsigned mask)
 	|  GPR save area                |       + UNITS_PER_WORD
 	|                               |
 	+-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset
-	|                               |       + UNITS_PER_HWVALUE
+	|                               |       + UNITS_PER_FP_REG
 	|  FPR save area                |
 	|                               |
 	+-------------------------------+ <-- frame_pointer_rtx (virtual)
@@ -5620,19 +5710,6 @@  riscv_save_libcall_count (unsigned mask)
 
 static HOST_WIDE_INT riscv_first_stack_step (struct riscv_frame_info *frame, poly_int64 remaining_size);
 
-/* Handle stack align for poly_int.  */
-static poly_int64
-riscv_stack_align (poly_int64 value)
-{
-  return aligned_upper_bound (value, PREFERRED_STACK_BOUNDARY / 8);
-}
-
-static HOST_WIDE_INT
-riscv_stack_align (HOST_WIDE_INT value)
-{
-  return RISCV_STACK_ALIGN (value);
-}
-
 static void
 riscv_compute_frame_info (void)
 {
@@ -5683,8 +5760,9 @@  riscv_compute_frame_info (void)
   if (frame->mask)
     {
       x_save_size = riscv_stack_align (num_x_saved * UNITS_PER_WORD);
-      unsigned num_save_restore = 1 + riscv_save_libcall_count (frame->mask);
 
+      /* 1 is for ra  */
+      unsigned num_save_restore = 1 + riscv_save_libcall_count (frame->mask);
       /* Only use save/restore routines if they don't alter the stack size.  */
       if (riscv_stack_align (num_save_restore * UNITS_PER_WORD) == x_save_size
           && !riscv_avoid_save_libcall ())
@@ -5696,6 +5774,14 @@  riscv_compute_frame_info (void)
 
 	  frame->save_libcall_adjustment = x_save_size;
 	}
+
+      if (!riscv_avoid_multi_push (frame))
+	{
+	  /* num(ra, s0-sx)  */
+	  unsigned num_multi_push = riscv_multi_push_regs_count (frame->mask);
+	  x_save_size = riscv_stack_align (num_multi_push * UNITS_PER_WORD);
+	  frame->multi_push_adj_base = riscv_16bytes_align (x_save_size);
+	}
     }
 
   /* In an interrupt function, we need extra space for the initial saves of CSRs.  */
@@ -5721,7 +5807,15 @@  riscv_compute_frame_info (void)
   frame->fp_sp_offset = offset - UNITS_PER_FP_REG;
   /* Next are the callee-saved GPRs. */
   if (frame->mask)
-    offset += x_save_size;
+    {
+      offset += x_save_size;
+      /* align to 16 bytes and add paddings to GPR part to honor
+	 both stack alignment and zcmp pus/pop size alignment. */
+      if (riscv_use_multi_push (frame)
+	  && known_lt (offset, frame->multi_push_adj_base
+				 + ZCMP_SP_INC_STEP * ZCMP_MAX_SPIMM))
+	offset = riscv_16bytes_align (offset);
+    }
   frame->gp_sp_offset = offset - UNITS_PER_WORD;
   /* The hard frame pointer points above the callee-saved GPRs. */
   frame->hard_frame_pointer_offset = offset;
@@ -5900,8 +5994,8 @@  static void
 riscv_for_each_saved_reg (poly_int64 sp_offset, riscv_save_restore_fn fn,
 			  bool epilogue, bool maybe_eh_return)
 {
-  HOST_WIDE_INT offset;
-  unsigned int regno;
+  HOST_WIDE_INT offset, first_fp_offset;
+  unsigned int regno, num_masked_fp = 0;
   unsigned int start = GP_REG_FIRST;
   unsigned int limit = GP_REG_LAST;
 
@@ -5987,16 +6081,20 @@  riscv_for_each_saved_reg (poly_int64 sp_offset, riscv_save_restore_fn fn,
 
   /* This loop must iterate over the same space as its companion in
      riscv_compute_frame_info.  */
-  offset = (cfun->machine->frame.fp_sp_offset - sp_offset).to_constant ();
+  first_fp_offset
+    = (cfun->machine->frame.fp_sp_offset - sp_offset).to_constant ();
   for (unsigned int regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
     if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
       {
 	bool handle_reg = !cfun->machine->reg_is_wrapped_separately[regno];
 	machine_mode mode = TARGET_DOUBLE_FLOAT ? DFmode : SFmode;
-
+	unsigned int slot = (riscv_use_multi_push (&cfun->machine->frame))
+			      ? CALLEE_SAVED_FREG_NUMBER (regno)
+			      : num_masked_fp;
+	offset = first_fp_offset - slot * GET_MODE_SIZE (mode).to_constant ();
 	if (handle_reg)
 	  riscv_save_restore_reg (mode, regno, offset, fn);
-	offset -= GET_MODE_SIZE (mode).to_constant ();
+	num_masked_fp++;
       }
 }
 
@@ -6093,6 +6191,41 @@  riscv_adjust_libcall_cfi_prologue ()
   return dwarf;
 }
 
+static rtx
+riscv_adjust_multi_push_cfi_prologue (int saved_size)
+{
+  rtx dwarf = NULL_RTX;
+  rtx adjust_sp_rtx, reg, mem, insn;
+  unsigned int mask = cfun->machine->frame.mask;
+  int offset;
+  int saved_cnt = 0;
+
+  if (mask & S10_MASK)
+    mask |= S11_MASK;
+
+  for (int regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+    if (BITSET_P (mask & MULTI_PUSH_GPR_MASK, regno - GP_REG_FIRST))
+      {
+	/* The save order is s11-s0, ra
+	   from high to low addr.  */
+	offset = saved_size - UNITS_PER_WORD * (++saved_cnt);
+
+	reg = gen_rtx_REG (Pmode, regno);
+	mem = gen_frame_mem (Pmode,
+			     plus_constant (Pmode, stack_pointer_rtx, offset));
+
+	insn = gen_rtx_SET (mem, reg);
+	dwarf = alloc_reg_note (REG_CFA_OFFSET, insn, dwarf);
+      }
+
+  /* Debug info for adjust sp.  */
+  adjust_sp_rtx
+    = gen_rtx_SET (stack_pointer_rtx,
+		   plus_constant (Pmode, stack_pointer_rtx, -saved_size));
+  dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, dwarf);
+  return dwarf;
+}
+
 static void
 riscv_emit_stack_tie (void)
 {
@@ -6102,6 +6235,56 @@  riscv_emit_stack_tie (void)
     emit_insn (gen_stack_tiedi (stack_pointer_rtx, hard_frame_pointer_rtx));
 }
 
+/*zcmp multi push and pop code_for_push_pop function ptr array  */
+const code_for_push_pop_t code_for_push_pop[ZCMP_MAX_GRP_SLOTS][ZCMP_OP_NUM]
+  = {{code_for_gpr_multi_push_up_to_ra, code_for_gpr_multi_pop_up_to_ra,
+      code_for_gpr_multi_popret_up_to_ra},
+     {code_for_gpr_multi_push_up_to_s0, code_for_gpr_multi_pop_up_to_s0,
+      code_for_gpr_multi_popret_up_to_s0},
+     {code_for_gpr_multi_push_up_to_s1, code_for_gpr_multi_pop_up_to_s1,
+      code_for_gpr_multi_popret_up_to_s1},
+     {code_for_gpr_multi_push_up_to_s2, code_for_gpr_multi_pop_up_to_s2,
+      code_for_gpr_multi_popret_up_to_s2},
+     {code_for_gpr_multi_push_up_to_s3, code_for_gpr_multi_pop_up_to_s3,
+      code_for_gpr_multi_popret_up_to_s3},
+     {code_for_gpr_multi_push_up_to_s4, code_for_gpr_multi_pop_up_to_s4,
+      code_for_gpr_multi_popret_up_to_s4},
+     {code_for_gpr_multi_push_up_to_s5, code_for_gpr_multi_pop_up_to_s5,
+      code_for_gpr_multi_popret_up_to_s5},
+     {code_for_gpr_multi_push_up_to_s6, code_for_gpr_multi_pop_up_to_s6,
+      code_for_gpr_multi_popret_up_to_s6},
+     {code_for_gpr_multi_push_up_to_s7, code_for_gpr_multi_pop_up_to_s7,
+      code_for_gpr_multi_popret_up_to_s7},
+     {code_for_gpr_multi_push_up_to_s8, code_for_gpr_multi_pop_up_to_s8,
+      code_for_gpr_multi_popret_up_to_s8},
+     {code_for_gpr_multi_push_up_to_s9, code_for_gpr_multi_pop_up_to_s9,
+      code_for_gpr_multi_popret_up_to_s9},
+     {nullptr, nullptr, nullptr},
+     {code_for_gpr_multi_push_up_to_s11, code_for_gpr_multi_pop_up_to_s11,
+      code_for_gpr_multi_popret_up_to_s11}};
+
+static rtx
+riscv_gen_multi_push_pop_insn (riscv_zcmp_op_t op, HOST_WIDE_INT adj_size,
+			       unsigned int regs_num)
+{
+  gcc_assert (op < ZCMP_OP_NUM);
+  gcc_assert (regs_num <= ZCMP_MAX_GRP_SLOTS
+	      && regs_num != ZCMP_INVALID_S0S10_SREGS_COUNTS + 1); /* 1 for ra*/
+  rtx stack_adj = GEN_INT (adj_size);
+  return GEN_FCN (code_for_push_pop[regs_num - 1][op](Pmode)) (stack_adj);
+}
+
+static unsigned
+get_multi_push_fpr_mask (unsigned max_fprs_push)
+{
+  unsigned mask_fprs_push = 0, num_f_pushed = 0;
+  for (unsigned regno = FP_REG_FIRST;
+       regno <= FP_REG_LAST && num_f_pushed < max_fprs_push; regno++)
+    if (riscv_save_reg_p (regno))
+      mask_fprs_push |= 1 << (regno - FP_REG_FIRST), num_f_pushed++;
+  return mask_fprs_push;
+}
+
 /* Expand the "prologue" pattern.  */
 
 void
@@ -6110,7 +6293,9 @@  riscv_expand_prologue (void)
   struct riscv_frame_info *frame = &cfun->machine->frame;
   poly_int64 remaining_size = frame->total_size;
   unsigned mask = frame->mask;
-  rtx insn;
+  unsigned fmask = frame->fmask;
+  int spimm, multi_push_additional, stack_adj;
+  rtx insn, dwarf = NULL_RTX;
 
   if (flag_stack_usage_info)
     current_function_static_stack_size = constant_lower_bound (remaining_size);
@@ -6118,8 +6303,46 @@  riscv_expand_prologue (void)
   if (cfun->machine->naked_p)
     return;
 
+  /* prefer muti-push to save-restore libcall.  */
+  if (riscv_use_multi_push (frame))
+    {
+      remaining_size -= frame->multi_push_adj_base;
+      if (known_gt (remaining_size, 2 * ZCMP_SP_INC_STEP))
+	spimm = 3;
+      else if (known_gt (remaining_size, ZCMP_SP_INC_STEP))
+	spimm = 2;
+      else if (known_gt (remaining_size, 0))
+	spimm = 1;
+      else
+	spimm = 0;
+      multi_push_additional = spimm * ZCMP_SP_INC_STEP;
+      frame->multi_push_adj_addi = multi_push_additional;
+      remaining_size -= multi_push_additional;
+
+      /* emit multi push insn & dwarf along with it.  */
+      stack_adj = frame->multi_push_adj_base + multi_push_additional;
+      insn = emit_insn (riscv_gen_multi_push_pop_insn (
+	PUSH_IDX, -stack_adj, riscv_multi_push_regs_count (frame->mask)));
+      dwarf = riscv_adjust_multi_push_cfi_prologue (stack_adj);
+      RTX_FRAME_RELATED_P (insn) = 1;
+      REG_NOTES (insn) = dwarf;
+
+      /* Temporarily fib that we need not save GPRs.  */
+      frame->mask = 0;
+
+      /* push FPRs into the addtional reserved space by cm.push. */
+      if (fmask)
+	{
+	  unsigned mask_fprs_push
+	    = get_multi_push_fpr_mask (multi_push_additional / UNITS_PER_WORD);
+	  frame->fmask &= mask_fprs_push;
+	  riscv_for_each_saved_reg (remaining_size, riscv_save_reg, false,
+				    false);
+	  frame->fmask = fmask & ~mask_fprs_push; /* mask for the rest FPRs.  */
+	}
+    }
   /* When optimizing for size, call a subroutine to save the registers.  */
-  if (riscv_use_save_libcall (frame))
+  else if (riscv_use_save_libcall (frame))
     {
       rtx dwarf = NULL_RTX;
       dwarf = riscv_adjust_libcall_cfi_prologue ();
@@ -6135,17 +6358,20 @@  riscv_expand_prologue (void)
   /* Save the registers.  */
   if ((frame->mask | frame->fmask) != 0)
     {
-      HOST_WIDE_INT step1 = riscv_first_stack_step (frame, remaining_size);
-
-      insn = gen_add3_insn (stack_pointer_rtx,
-			    stack_pointer_rtx,
-			    GEN_INT (-step1));
-      RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
-      remaining_size -= step1;
+      if (known_gt (remaining_size, frame->frame_pointer_offset))
+	{
+	  HOST_WIDE_INT step1 = riscv_first_stack_step (frame, remaining_size);
+	  remaining_size -= step1;
+	  insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+				GEN_INT (-step1));
+	  RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+	}
       riscv_for_each_saved_reg (remaining_size, riscv_save_reg, false, false);
     }
 
-  frame->mask = mask; /* Undo the above fib.  */
+  /* Undo the above fib.  */
+  frame->mask = mask;
+  frame->fmask = fmask;
 
   /* Set up the frame pointer, if we're using one.  */
   if (frame_pointer_needed)
@@ -6198,6 +6424,32 @@  riscv_expand_prologue (void)
     }
 }
 
+static rtx
+riscv_adjust_multi_pop_cfi_epilogue (int saved_size)
+{
+  rtx dwarf = NULL_RTX;
+  rtx adjust_sp_rtx, reg;
+  unsigned int mask = cfun->machine->frame.mask;
+
+  if (mask & S10_MASK)
+    mask |= S11_MASK;
+
+  /* Debug info for adjust sp.  */
+  adjust_sp_rtx
+    = gen_rtx_SET (stack_pointer_rtx,
+		   plus_constant (Pmode, stack_pointer_rtx, saved_size));
+  dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, dwarf);
+
+  for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+    if (BITSET_P (mask, regno - GP_REG_FIRST))
+      {
+	reg = gen_rtx_REG (Pmode, regno);
+	dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+      }
+
+  return dwarf;
+}
+
 static rtx
 riscv_adjust_libcall_cfi_epilogue ()
 {
@@ -6236,11 +6488,24 @@  riscv_expand_epilogue (int style)
      Start off by assuming that no registers need to be restored.  */
   struct riscv_frame_info *frame = &cfun->machine->frame;
   unsigned mask = frame->mask;
+  unsigned fmask = frame->fmask;
+  unsigned mask_fprs_push = 0;
   HOST_WIDE_INT step2 = 0;
-  bool use_restore_libcall = ((style == NORMAL_RETURN)
-			      && riscv_use_save_libcall (frame));
-  unsigned libcall_size = (use_restore_libcall
-			   ? frame->save_libcall_adjustment : 0);
+  bool use_multi_pop_normal
+    = ((style == NORMAL_RETURN) && riscv_use_multi_push (frame));
+  bool use_multi_pop_sibcall
+    = ((style == SIBCALL_RETURN) && riscv_use_multi_push (frame));
+  bool use_multi_pop = use_multi_pop_normal || use_multi_pop_sibcall;
+
+  bool use_restore_libcall
+    = !use_multi_pop
+      && ((style == NORMAL_RETURN) && riscv_use_save_libcall (frame));
+  unsigned libcall_size = use_restore_libcall && !use_multi_pop
+			    ? frame->save_libcall_adjustment
+			    : 0;
+  unsigned multipop_size
+    = use_multi_pop ? frame->multi_push_adj_base + frame->multi_push_adj_addi
+		    : 0;
   rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
   rtx insn;
 
@@ -6311,18 +6576,26 @@  riscv_expand_epilogue (int style)
       REG_NOTES (insn) = dwarf;
     }
 
-  if (use_restore_libcall)
-    frame->mask = 0; /* Temporarily fib for GPRs.  */
+  if (use_restore_libcall || use_multi_pop)
+    frame->mask = 0; /* Temporarily fib that we need not restore GPRs.  */
 
   /* If we need to restore registers, deallocate as much stack as
      possible in the second step without going out of range.  */
-  if ((frame->mask | frame->fmask) != 0)
+  if (use_multi_pop)
+    {
+      if (frame->fmask
+	  && known_gt (frame->total_size - multipop_size,
+		       frame->frame_pointer_offset))
+	step2
+	  = riscv_first_stack_step (frame, frame->total_size - multipop_size);
+    }
+  else if ((frame->mask | frame->fmask) != 0)
     step2 = riscv_first_stack_step (frame, frame->total_size - libcall_size);
 
-  if (use_restore_libcall)
+  if (use_restore_libcall || use_multi_pop)
     frame->mask = mask; /* Undo the above fib.  */
 
-  poly_int64 step1 = frame->total_size - step2 - libcall_size;
+  poly_int64 step1 = frame->total_size - step2 - libcall_size - multipop_size;
 
   /* Set TARGET to BASE + STEP1.  */
   if (known_gt (step1, 0))
@@ -6356,8 +6629,9 @@  riscv_expand_epilogue (int style)
 					   stack_pointer_rtx,
 					   adjust));
 	  rtx dwarf = NULL_RTX;
-	  rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-					     GEN_INT (step2 + libcall_size));
+	  rtx cfa_adjust_rtx
+	    = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+			    GEN_INT (step2 + libcall_size + multipop_size));
 
 	  dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf);
 	  RTX_FRAME_RELATED_P (insn) = 1;
@@ -6372,16 +6646,26 @@  riscv_expand_epilogue (int style)
       epilogue_cfa_sp_offset = step2;
     }
 
-  if (use_restore_libcall)
-    frame->mask = 0; /* Temporarily fib that we need not save GPRs.  */
+  if (use_multi_pop)
+    {
+      frame->mask = 0; /* Temporarily fib that we need not restore GPRs.  */
+      if (fmask)
+	{
+	  mask_fprs_push = get_multi_push_fpr_mask (frame->multi_push_adj_addi
+						    / UNITS_PER_WORD);
+	  frame->fmask &= ~mask_fprs_push; /* FPRs not saved by cm.push  */
+	}
+    }
+  else if (use_restore_libcall)
+    frame->mask = 0; /* Temporarily fib that we need not restore GPRs.  */
 
   /* Restore the registers.  */
-  riscv_for_each_saved_reg (frame->total_size - step2 - libcall_size,
-			    riscv_restore_reg,
-			    true, style == EXCEPTION_RETURN);
+  riscv_for_each_saved_reg (frame->total_size - step2 - libcall_size
+			      - multipop_size,
+			    riscv_restore_reg, true, style == EXCEPTION_RETURN);
 
   if (use_restore_libcall)
-      frame->mask = mask; /* Undo the above fib.  */
+    frame->mask = mask; /* Undo the above fib.  */
 
   if (need_barrier_p)
     riscv_emit_stack_tie ();
@@ -6393,15 +6677,43 @@  riscv_expand_epilogue (int style)
 				       GEN_INT (step2)));
 
       rtx dwarf = NULL_RTX;
-      rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-					 GEN_INT (libcall_size));
+      rtx cfa_adjust_rtx
+	= gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+			GEN_INT (libcall_size + multipop_size));
       dwarf = alloc_reg_note (REG_CFA_DEF_CFA, cfa_adjust_rtx, dwarf);
       RTX_FRAME_RELATED_P (insn) = 1;
 
       REG_NOTES (insn) = dwarf;
     }
 
-  if (use_restore_libcall)
+  if (use_multi_pop)
+    {
+      /* restore FPRs pushed by cm.push. */
+      frame->fmask = fmask & mask_fprs_push;
+      if (frame->fmask)
+	riscv_for_each_saved_reg (frame->total_size - libcall_size
+				    - multipop_size,
+				  riscv_restore_reg, true,
+				  style == EXCEPTION_RETURN);
+      /* Undo the above fib.  */
+      frame->mask = mask;
+      frame->fmask = fmask;
+      unsigned regs_count = riscv_multi_push_regs_count (frame->mask);
+      if (use_multi_pop_normal)
+	insn = emit_jump_insn (riscv_gen_multi_push_pop_insn (POPRET_IDX,
+							      multipop_size,
+							      regs_count));
+      else
+	insn = emit_insn (
+	  riscv_gen_multi_push_pop_insn (POP_IDX, multipop_size, regs_count));
+
+      rtx dwarf = riscv_adjust_multi_pop_cfi_epilogue (multipop_size);
+      RTX_FRAME_RELATED_P (insn) = 1;
+      REG_NOTES (insn) = dwarf;
+      if (use_multi_pop_normal)
+	return;
+    }
+  else if (use_restore_libcall)
     {
       rtx dwarf = riscv_adjust_libcall_cfi_epilogue ();
       insn = emit_insn (gen_gpr_restore (GEN_INT (riscv_save_libcall_count (mask))));
@@ -7744,6 +8056,27 @@  riscv_gen_gpr_save_insn (struct riscv_frame_info *frame)
   return gen_rtx_PARALLEL (VOIDmode, vec);
 }
 
+static HOST_WIDE_INT
+zcmp_base_adj (int regs_num)
+{
+  return riscv_16bytes_align ((regs_num) *GET_MODE_SIZE (word_mode));
+}
+
+static HOST_WIDE_INT
+zcmp_additional_adj (HOST_WIDE_INT total, int regs_num)
+{
+  return total - zcmp_base_adj (regs_num);
+}
+
+bool
+riscv_zcmp_valid_stack_adj_bytes_p (HOST_WIDE_INT total, int regs_num)
+{
+  HOST_WIDE_INT additioanl_bytes = zcmp_additional_adj (total, regs_num);
+  return additioanl_bytes == 0 || additioanl_bytes == 1 * ZCMP_SP_INC_STEP
+	 || additioanl_bytes == 2 * ZCMP_SP_INC_STEP
+	 || additioanl_bytes == ZCMP_MAX_SPIMM * ZCMP_SP_INC_STEP;
+}
+
 /* Return true if it's valid gpr_save pattern.  */
 
 bool
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index e18a0081297..42b6eb784d4 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -420,6 +420,29 @@  ASM_MISA_SPEC
 #define RISCV_CALL_ADDRESS_TEMP(MODE) \
   gen_rtx_REG (MODE, RISCV_CALL_ADDRESS_TEMP_REGNUM)
 
+#define RETURN_ADDR_MASK (1 << RETURN_ADDR_REGNUM)
+#define S0_MASK (1 << S0_REGNUM)
+#define S1_MASK (1 << S1_REGNUM)
+#define S2_MASK (1 << S2_REGNUM)
+#define S3_MASK (1 << S3_REGNUM)
+#define S4_MASK (1 << S4_REGNUM)
+#define S5_MASK (1 << S5_REGNUM)
+#define S6_MASK (1 << S6_REGNUM)
+#define S7_MASK (1 << S7_REGNUM)
+#define S8_MASK (1 << S8_REGNUM)
+#define S9_MASK (1 << S9_REGNUM)
+#define S10_MASK (1 << S10_REGNUM)
+#define S11_MASK (1 << S11_REGNUM)
+
+#define MULTI_PUSH_GPR_MASK                                                    \
+  (RETURN_ADDR_MASK | S0_MASK | S1_MASK | S2_MASK | S3_MASK | S4_MASK          \
+   | S5_MASK | S6_MASK | S7_MASK | S8_MASK | S9_MASK | S10_MASK | S11_MASK)
+#define ZCMP_MAX_SPIMM 3
+#define ZCMP_SP_INC_STEP 16
+#define ZCMP_INVALID_S0S10_SREGS_COUNTS 11
+#define ZCMP_S0S11_SREGS_COUNTS 12
+#define ZCMP_MAX_GRP_SLOTS 13
+
 #define MCOUNT_NAME "_mcount"
 
 #define NO_PROFILE_COUNTERS 1
@@ -655,6 +678,8 @@  enum reg_class
   ((REGNO) >= 8 && (REGNO) <= 9 ? (REGNO) - 8 :		\
    (REGNO) >= 18 && (REGNO) <= 27 ? (REGNO) - 16 : -1)
 
+#define CALLEE_SAVED_FREG_NUMBER(REGNO) CALLEE_SAVED_REG_NUMBER (REGNO - 32)
+
 #define LIBCALL_VALUE(MODE) \
   riscv_function_value (NULL_TREE, NULL_TREE, MODE)
 
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 47d14d99903..f489646cec3 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -124,6 +124,7 @@ 
 
 (define_constants
   [(RETURN_ADDR_REGNUM		1)
+   (SP_REGNUM 			2)
    (GP_REGNUM 			3)
    (TP_REGNUM			4)
    (T0_REGNUM			5)
@@ -3431,3 +3432,4 @@ 
 (include "thead.md")
 (include "vector.md")
 (include "zicond.md")
+(include "zc.md")
diff --git a/gcc/config/riscv/zc.md b/gcc/config/riscv/zc.md
new file mode 100644
index 00000000000..5c1bf031b8d
--- /dev/null
+++ b/gcc/config/riscv/zc.md
@@ -0,0 +1,1042 @@ 
+;; Machine description for RISC-V Zc extention.
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+;; Contributed by Fei Gao (gaofei@eswincomputing.com).
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; GCC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+(define_insn "@gpr_multi_pop_up_to_ra_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_ra_operand" "I")))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s0_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s0_operand" "I")))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s1_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s1_operand" "I")))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s1}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s2_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s2_operand" "I")))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s2}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s3_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s3_operand" "I")))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s3}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s4_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s4_operand" "I")))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s4}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s5_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s5_operand" "I")))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s5}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s6_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s6_operand" "I")))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s6}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s7_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s7_operand" "I")))
+   (set (reg:X S7_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                      (const_int <slot8_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s7}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s8_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s8_operand" "I")))
+   (set (reg:X S8_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S7_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot8_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s8}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s9_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s9_operand" "I")))
+   (set (reg:X S9_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S8_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S7_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                      (const_int <slot8_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot10_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s9}, %0"
+)
+
+(define_insn "@gpr_multi_pop_up_to_s11_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s11_operand" "I")))
+   (set (reg:X S11_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S10_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S9_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S8_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S7_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                      (const_int <slot8_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot10_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot11_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot12_offset>))))]
+  "TARGET_ZCMP"
+  "cm.pop	{ra, s0-s11}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_ra_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_ra_operand" "I")))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s0_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s0_operand" "I")))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s1_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s1_operand" "I")))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s1}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s2_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s2_operand" "I")))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s2}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s3_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s3_operand" "I")))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s3}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s4_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s4_operand" "I")))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s4}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s5_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s5_operand" "I")))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s5}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s6_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s6_operand" "I")))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s6}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s7_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s7_operand" "I")))
+   (set (reg:X S7_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot8_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s7}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s8_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s8_operand" "I")))
+   (set (reg:X S8_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S7_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot8_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s8}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s9_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s9_operand" "I")))
+   (set (reg:X S9_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S8_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S7_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot8_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot10_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s9}, %0"
+)
+
+(define_insn "@gpr_multi_popret_up_to_s11_<mode>"
+  [(set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_pop_up_to_s11_operand" "I")))
+   (set (reg:X S11_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>))))
+   (set (reg:X S10_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>))))
+   (set (reg:X S9_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>))))
+   (set (reg:X S8_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>))))
+   (set (reg:X S7_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>))))
+   (set (reg:X S6_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>))))
+   (set (reg:X S5_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>))))
+   (set (reg:X S4_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>))))
+   (set (reg:X S3_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                      (const_int <slot8_offset>))))
+   (set (reg:X S2_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>))))
+   (set (reg:X S1_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot10_offset>))))
+   (set (reg:X S0_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot11_offset>))))
+   (set (reg:X RETURN_ADDR_REGNUM)
+        (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot12_offset>))))
+   (return)
+   (use (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_ZCMP"
+  "cm.popret	{ra, s0-s11}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_ra_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_ra_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s0_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s0_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s1_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s1_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s1}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s2_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s2_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s2}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s3_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S3_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s3_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s3}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s4_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S4_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S3_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s4_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s4}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s5_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S5_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S4_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S3_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s5_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s5}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s6_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S6_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S5_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S4_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X S3_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s6_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s6}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s7_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S7_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S6_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S5_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X S4_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>)))
+        (reg:X S3_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                      (const_int <slot8_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s7_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s7}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s8_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S8_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S7_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S6_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X S5_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>)))
+        (reg:X S4_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>)))
+        (reg:X S3_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot8_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s8_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s8}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s9_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S9_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S8_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S7_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X S6_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>)))
+        (reg:X S5_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>)))
+        (reg:X S4_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>)))
+        (reg:X S3_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot8_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot10_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s9_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s9}, %0"
+)
+
+(define_insn "@gpr_multi_push_up_to_s11_<mode>"
+  [(set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot0_offset>)))
+        (reg:X S11_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot1_offset>)))
+        (reg:X S10_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot2_offset>)))
+        (reg:X S9_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot3_offset>)))
+        (reg:X S8_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot4_offset>)))
+        (reg:X S7_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot5_offset>)))
+        (reg:X S6_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot6_offset>)))
+        (reg:X S5_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot7_offset>)))
+        (reg:X S4_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot8_offset>)))
+        (reg:X S3_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot9_offset>)))
+        (reg:X S2_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot10_offset>)))
+        (reg:X S1_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot11_offset>)))
+        (reg:X S0_REGNUM))
+   (set (mem:X (plus:X (reg:X SP_REGNUM)
+                       (const_int <slot12_offset>)))
+        (reg:X RETURN_ADDR_REGNUM))
+   (set (reg:X SP_REGNUM)
+        (plus:X (reg:X SP_REGNUM)
+                 (match_operand 0 "stack_push_up_to_s11_operand" "I")))]
+  "TARGET_ZCMP"
+  "cm.push	{ra, s0-s11}, %0"
+)
diff --git a/gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c b/gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c
new file mode 100644
index 00000000000..57c83249741
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c
@@ -0,0 +1,256 @@ 
+/* { dg-do compile } */
+/* { dg-options " -Os -march=rv32e_zca_zcmp -mabi=ilp32e -mcmodel=medlow -fno-shrink-wrap-separate" } */
+/* { dg-skip-if "" { *-*-* } {"-O0" "-O1" "-O2" "-Og" "-O3" "-Oz" "-flto"} } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+char
+my_getchar ();
+float
+getf ();
+int __attribute__ ((noinline))
+incoming_stack_args (int arg0, int arg1, int arg2, int arg3, int arg4, int arg5,
+		     int arg6, int arg7, int arg8);
+int
+getint ();
+void
+PrintInts (int n, ...);						 // varargs
+void __attribute__ ((noinline)) PrintIntsNoVaStart (int n, ...); // varargs
+void
+PrintInts2 (int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int n,
+	    ...);
+extern void
+f1 (void);
+extern void
+f2 (void);
+
+/*
+**test1:
+**	...
+**	cm.push	{ra, s0-s1}, -64
+**	...
+**	cm.popret	{ra, s0-s1}, 64
+**	...
+*/
+int
+test1 ()
+{
+  char volatile array[3120];
+  float volatile farray[3120];
+
+  float sum = 0;
+  for (int i = 0; i < 3120; i++)
+    {
+      array[i] = my_getchar ();
+      farray[i] = my_getchar () * 1.2;
+      sum += array[i] + farray[i];
+    }
+  return sum;
+}
+
+/*
+**test2_step1_0_size:
+**	...
+**	cm.push	{ra, s0}, -64
+**	...
+**	cm.popret	{ra, s0}, 64
+**	...
+*/
+int
+test2_step1_0_size ()
+{
+  int volatile iarray[3120 + 1824 / 4 - 8];
+
+  for (int i = 0; i < 3120 + 1824 / 4 - 8; i++)
+    {
+      iarray[i] = my_getchar () * 2;
+    }
+  return iarray[0] + iarray[1];
+}
+
+/*
+**test3:
+**	...
+**	cm.push	{ra, s0-s1}, -64
+**	...
+**	cm.popret	{ra, s0-s1}, 64
+**	...
+*/
+float
+test3 ()
+{
+  char volatile array[3120];
+  float volatile farray[3120];
+
+  float sum = 0, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0;
+
+  for (int i = 0; i < 3120; i++)
+    {
+      f1 = getf ();
+      f2 = getf ();
+      f3 = getf ();
+      f4 = getf ();
+      array[i] = my_getchar ();
+      farray[i] = my_getchar () * 1.2;
+      sum += array[i] + farray[i] + f1 + f2 + f3 + f4;
+    }
+  return sum;
+}
+
+/*
+**outgoing_stack_args:
+**	...
+**	cm.push	{ra, s0}, -32
+**	...
+**	cm.popret	{ra, s0}, 32
+**	...
+*/
+int
+outgoing_stack_args ()
+{
+  int local = getint ();
+  return local + incoming_stack_args (0, 1, 2, 3, 4, 5, 6, 7, 8);
+}
+
+/*
+**callPrintInts:
+**	...
+**	cm.push	{ra}, -32
+**	...
+**	cm.popret	{ra}, 32
+**	...
+*/
+float
+callPrintInts ()
+{
+  volatile float f = getf (); // f in local
+  PrintInts (9, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+  return f;
+}
+
+/*
+**callPrint:
+**	...
+**	cm.push	{ra}, -32
+**	...
+**	cm.popret	{ra}, 32
+**	...
+*/
+float
+callPrint ()
+{
+  volatile float f = getf (); // f in local
+  PrintIntsNoVaStart (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+  return f;
+}
+
+/*
+**callPrint_S:
+**	...
+**	cm.push	{ra, s0}, -32
+**	...
+**	cm.popret	{ra, s0}, 32
+**	...
+*/
+float
+callPrint_S ()
+{
+  float f = getf ();
+  PrintIntsNoVaStart (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+  return f;
+}
+
+/*
+**callPrint_2:
+**	...
+**	cm.push	{ra, s0}, -32
+**	...
+**	cm.popret	{ra, s0}, 32
+**	...
+*/
+float
+callPrint_2 ()
+{
+  float f = getf ();
+  PrintInts2 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+  return f;
+}
+
+/*
+**test_step1_0bytes_save_restore:
+**	...
+**	cm.push	{ra}, -16
+**	...
+**	cm.popret	{ra}, 16
+**	...
+*/
+int
+test_step1_0bytes_save_restore ()
+{
+  int a = 9;
+  int b = my_getchar ();
+  return a + b;
+}
+
+/*
+**test_s0:
+**	...
+**	cm.push	{ra, s0}, -16
+**	...
+**	cm.popret	{ra, s0}, 16
+**	...
+*/
+int
+test_s0 ()
+{
+  int a = my_getchar ();
+  int b = my_getchar ();
+  return a + b;
+}
+
+/*
+**test_s1:
+**	...
+**	cm.push	{ra, s0-s1}, -16
+**	...
+**	cm.popret	{ra, s0-s1}, 16
+**	...
+*/
+int
+test_s1 ()
+{
+  int s0 = my_getchar ();
+  int s1 = my_getchar ();
+  int b = my_getchar ();
+  return s1 + s0 + b;
+}
+
+/*
+**test_f0:
+**	...
+**	cm.push	{ra, s0-s1}, -16
+**	...
+**	cm.popret	{ra, s0-s1}, 16
+**	...
+*/
+int
+test_f0 ()
+{
+  int s0 = my_getchar ();
+  float f0 = getf ();
+  int b = my_getchar ();
+  return f0 + s0 + b;
+}
+
+/*
+**foo:
+**	cm.push	{ra}, -16
+**	call	f1
+**	cm.pop	{ra}, 16
+**	tail	f2
+*/
+void
+foo (void)
+{
+  f1 ();
+  f2 ();
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
new file mode 100644
index 00000000000..f24e0a8bab9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
@@ -0,0 +1,256 @@ 
+/* { dg-do compile } */
+/* { dg-options " -Os -march=rv32imaf_zca_zcmp -mabi=ilp32f -mcmodel=medlow -fno-shrink-wrap-separate" }*/
+/* { dg-skip-if "" { *-*-* } {"-O0" "-O1" "-O2" "-Og" "-O3" "-Oz" "-flto"} } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+char
+my_getchar ();
+float
+getf ();
+int __attribute__ ((noinline))
+incoming_stack_args (int arg0, int arg1, int arg2, int arg3, int arg4, int arg5,
+		     int arg6, int arg7, int arg8);
+int
+getint ();
+void
+PrintInts (int n, ...);						 // varargs
+void __attribute__ ((noinline)) PrintIntsNoVaStart (int n, ...); // varargs
+void
+PrintInts2 (int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int n,
+	    ...);
+extern void
+f1 (void);
+extern void
+f2 (void);
+
+/*
+**test1:
+**	...
+**	cm.push	{ra, s0-s4}, -80
+**	...
+**	cm.popret	{ra, s0-s4}, 80
+**	...
+*/
+int
+test1 ()
+{
+  char volatile array[3120];
+  float volatile farray[3120];
+
+  float sum = 0;
+  for (int i = 0; i < 3120; i++)
+    {
+      array[i] = my_getchar ();
+      farray[i] = my_getchar () * 1.2;
+      sum += array[i] + farray[i];
+    }
+  return sum;
+}
+
+/*
+**test2_step1_0_size:
+**	...
+**	cm.push	{ra, s0-s1}, -64
+**	...
+**	cm.popret	{ra, s0-s1}, 64
+**	...
+*/
+int
+test2_step1_0_size ()
+{
+  int volatile iarray[3120 + 1824 / 4 - 8];
+
+  for (int i = 0; i < 3120 + 1824 / 4 - 8; i++)
+    {
+      iarray[i] = my_getchar () * 2;
+    }
+  return iarray[0] + iarray[1];
+}
+
+/*
+**test3:
+**	...
+**	cm.push	{ra, s0-s4}, -80
+**	...
+**	cm.popret	{ra, s0-s4}, 80
+**	...
+*/
+float
+test3 ()
+{
+  char volatile array[3120];
+  float volatile farray[3120];
+
+  float sum = 0, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0;
+
+  for (int i = 0; i < 3120; i++)
+    {
+      f1 = getf ();
+      f2 = getf ();
+      f3 = getf ();
+      f4 = getf ();
+      array[i] = my_getchar ();
+      farray[i] = my_getchar () * 1.2;
+      sum += array[i] + farray[i] + f1 + f2 + f3 + f4;
+    }
+  return sum;
+}
+
+/*
+**outgoing_stack_args:
+**	...
+**	cm.push	{ra, s0}, -32
+**	...
+**	cm.popret	{ra, s0}, 32
+**	...
+*/
+int
+outgoing_stack_args ()
+{
+  int local = getint ();
+  return local + incoming_stack_args (0, 1, 2, 3, 4, 5, 6, 7, 8);
+}
+
+/*
+**callPrintInts:
+**	...
+**	cm.push	{ra}, -48
+**	...
+**	cm.popret	{ra}, 48
+**	...
+*/
+float
+callPrintInts ()
+{
+  volatile float f = getf (); // f in local
+  PrintInts (9, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+  return f;
+}
+
+/*
+**callPrint:
+**	...
+**	cm.push	{ra}, -48
+**	...
+**	cm.popret	{ra}, 48
+**	...
+*/
+float
+callPrint ()
+{
+  volatile float f = getf (); // f in local
+  PrintIntsNoVaStart (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+  return f;
+}
+
+/*
+**callPrint_S:
+**	...
+**	cm.push	{ra}, -48
+**	...
+**	cm.popret	{ra}, 48
+**	...
+*/
+float
+callPrint_S ()
+{
+  float f = getf ();
+  PrintIntsNoVaStart (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+  return f;
+}
+
+/*
+**callPrint_2:
+**	...
+**	cm.push	{ra}, -48
+**	...
+**	cm.popret	{ra}, 48
+**	...
+*/
+float
+callPrint_2 ()
+{
+  float f = getf ();
+  PrintInts2 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+  return f;
+}
+
+/*
+**test_step1_0bytes_save_restore:
+**	...
+**	cm.push	{ra}, -16
+**	...
+**	cm.popret	{ra}, 16
+**	...
+*/
+int
+test_step1_0bytes_save_restore ()
+{
+  int a = 9;
+  int b = my_getchar ();
+  return a + b;
+}
+
+/*
+**test_s0:
+**	...
+**	cm.push	{ra, s0}, -16
+**	...
+**	cm.popret	{ra, s0}, 16
+**	...
+*/
+int
+test_s0 ()
+{
+  int a = my_getchar ();
+  int b = my_getchar ();
+  return a + b;
+}
+
+/*
+**test_s1:
+**	...
+**	cm.push	{ra, s0-s1}, -16
+**	...
+**	cm.popret	{ra, s0-s1}, 16
+**	...
+*/
+int
+test_s1 ()
+{
+  int s0 = my_getchar ();
+  int s1 = my_getchar ();
+  int b = my_getchar ();
+  return s1 + s0 + b;
+}
+
+/*
+**test_f0:
+**	...
+**	cm.push	{ra, s0}, -32
+**	...
+**	cm.popret	{ra, s0}, 32
+**	...
+*/
+int
+test_f0 ()
+{
+  int s0 = my_getchar ();
+  float f0 = getf ();
+  int b = my_getchar ();
+  return f0 + s0 + b;
+}
+
+/*
+**foo:
+**	cm.push	{ra}, -16
+**	call	f1
+**	cm.pop	{ra}, 16
+**	tail	f2
+*/
+void
+foo (void)
+{
+  f1 ();
+  f2 ();
+}
diff --git a/gcc/testsuite/gcc.target/riscv/zcmp_push_fpr.c b/gcc/testsuite/gcc.target/riscv/zcmp_push_fpr.c
new file mode 100644
index 00000000000..530b35b53dd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zcmp_push_fpr.c
@@ -0,0 +1,34 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv64imafd_zicsr_zifencei_zca_zcmp -mabi=lp64d -Os -fno-shrink-wrap-separate" } */
+/* { dg-skip-if "" { *-*-* } {"-O0" "-O1" "-O2" "-Og" "-O3" "-Oz" "-flto"} } */
+
+typedef struct
+{
+  struct
+  {
+    struct
+    {
+      struct
+      {
+	long a;
+      };
+    } a[129];
+  };
+} b;
+
+struct c
+{
+  void *a[129];
+};
+
+extern void
+f (struct c, b *);
+
+struct c
+d ()
+{
+  struct c a;
+  __builtin_unwind_init ();
+  b e;
+  f (a, &e);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c b/gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c
new file mode 100644
index 00000000000..2f2fa55baac
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O0 -march=rv32e_zca_zcb_zcmp -mabi=ilp32e -mcmodel=medlow -fomit-frame-pointer -fno-shrink-wrap-separate" } */
+/* { dg-skip-if "" { *-*-* } {"-O2" "-O1" "-Os" "-Og" "-O3" "-Oz" "-flto"} } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+void
+bar ();
+
+/*
+**fool_rv32e:
+**	cm.push	{ra}, -32
+**	...
+**	call	bar
+**	...
+**	lw	a[0-5],32\(sp\)
+**	...
+**	cm.popret	{ra}, 32
+*/
+int
+fool_rv32e (int a0, int a1, int a2, int a3, int a4, int a5, int incoming0)
+{
+  bar ();
+  return a0 + a1 + a2 + a3 + a4 + a5 + incoming0;
+}