[2/2,V3,RISC-V] support cm.push cm.pop cm.popret in zcmp

Message ID 20230602104247.26454-2-gaofei@eswincomputing.com
State Unresolved
Headers
Series [1/2,RISC-V] fix cfi issue in save-restore. |

Checks

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

Commit Message

Fei Gao June 2, 2023, 10:42 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.

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.

Signed-off-by: Fei Gao <gaofei@eswincomputing.com>

gcc/ChangeLog:

        * config/riscv/iterators.md (-8): slot offset in bytes
        (-16): likewise
        (-24): likewise
        (-32): likewise
        (-40): likewise
        (-48): likewise
        (-56): likewise
        (-64): likewise
        (-72): likewise
        (-80): likewise
        (-88): likewise
        (-96): likewise
        (-104): likewise
        * config/riscv/predicates.md
        (stack_push_up_to_ra_operand): predicates for stack adjust of pushing ra
        (stack_push_up_to_s0_operand): predicates for stack adjust of 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 for stack adjust of poping ra
        (stack_pop_up_to_s0_operand): predicates for stack adjust of 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_adjust_multi_push_cfi_prologue): adjust cfi for cm.push
        (riscv_gen_multi_push_pop_insn): gen function for multi push and pop
        (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 size is 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
        * 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_stack_alignment.c: New test.
---
 gcc/config/riscv/iterators.md                 |   15 +
 gcc/config/riscv/predicates.md                |   96 ++
 gcc/config/riscv/riscv-protos.h               |    1 +
 gcc/config/riscv/riscv.cc                     |  360 +++++-
 gcc/config/riscv/riscv.h                      |   23 +
 gcc/config/riscv/riscv.md                     |    2 +
 gcc/config/riscv/zc.md                        | 1042 +++++++++++++++++
 gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c   |  239 ++++
 gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c   |  239 ++++
 .../gcc.target/riscv/zcmp_stack_alignment.c   |   23 +
 10 files changed, 2000 insertions(+), 40 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_stack_alignment.c
  

Comments

Kito Cheng June 5, 2023, 8:31 a.m. UTC | #1
Only a few minor comments, otherwise LGTM :)

But I guess we need to wait until binutils merge zc stuff.

> Zcmp can share the same logic as save-restore in stack allocation: pre-allocation
> by cm.push, step 1 and step 2.
>
> 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.
>
> Signed-off-by: Fei Gao <gaofei@eswincomputing.com>
>
> gcc/ChangeLog:
>
>         * config/riscv/iterators.md (-8): slot offset in bytes
>         (-16): likewise
>         (-24): likewise
>         (-32): likewise
>         (-40): likewise
>         (-48): likewise
>         (-56): likewise
>         (-64): likewise
>         (-72): likewise
>         (-80): likewise
>         (-88): likewise
>         (-96): likewise
>         (-104): likewise

Use slot0_offset...slot12_offset.

> @@ -422,6 +430,16 @@ static const struct riscv_tune_info riscv_tune_info_table[] = {
>  #include "riscv-cores.def"
>  };
>
> +typedef enum
> +{
> +  PUSH_IDX = 0,
> +  POP_IDX,
> +  POPRET_IDX,
> +  ZCMP_OP_NUM
> +} op_idx;

op_idx -> riscv_zcmp_op_t
> @@ -5388,6 +5487,42 @@ 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 (SImode, regno);

Should be Pmode rather than SImode, and seems
riscv_adjust_libcall_cfi_prologue has same issue...could you send a
separate patch to fix that?

> +        mem = gen_frame_mem (SImode, plus_constant (Pmode,

Same here.

> +                                                    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)
>  {


> @@ -5493,6 +5697,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 (SImode, regno);

Pmode

> +        dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
> +      }
> +
> +  return dwarf;
> +}
> +
>  static rtx
>  riscv_adjust_libcall_cfi_epilogue ()
>  {

> diff --git a/gcc/config/riscv/zc.md b/gcc/config/riscv/zc.md
> new file mode 100644
> index 00000000000..f2f2198598c
> --- /dev/null
> +++ b/gcc/config/riscv/zc.md
> @@ -0,0 +1,1042 @@
> +;; Machine description for RISC-V Zc extention.
> +;; Copyright (C) 2011-2023 Free Software Foundation, Inc.

2023 rather than 2011-2023
  
Jiawei June 5, 2023, 8:41 a.m. UTC | #2
Sorry for the late,  I will send the binutils patch within this week.


----- Original Message -----
From: "Kito Cheng"
To: "Fei Gao"
Cc: gcc-patches@gcc.gnu.org, palmer@dabbelt.com, jeffreyalaw@gmail.com, sinan.lin@linux.alibaba.com, jiawei@iscas.ac.cn
Sent: Mon, 5 Jun 2023 16:31:29 +0800
Subject: Re: [PATCH 2/2] [V3] [RISC-V] support cm.push cm.pop cm.popret in zcmp

Only a few minor comments, otherwise LGTM :)

But I guess we need to wait until binutils merge zc stuff.

> Zcmp can share the same logic as save-restore in stack allocation: pre-allocation
> by cm.push, step 1 and step 2.
>
> 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.
>
> Signed-off-by: Fei Gao
>
> gcc/ChangeLog:
>
> * config/riscv/iterators.md (-8): slot offset in bytes
> (-16): likewise
> (-24): likewise
> (-32): likewise
> (-40): likewise
> (-48): likewise
> (-56): likewise
> (-64): likewise
> (-72): likewise
> (-80): likewise
> (-88): likewise
> (-96): likewise
> (-104): likewise

Use slot0_offset...slot12_offset.

> @@ -422,6 +430,16 @@ static const struct riscv_tune_info riscv_tune_info_table[] = {
> #include "riscv-cores.def"
> };
>
> +typedef enum
> +{
> + PUSH_IDX = 0,
> + POP_IDX,
> + POPRET_IDX,
> + ZCMP_OP_NUM
> +} op_idx;

op_idx -> riscv_zcmp_op_t
> @@ -5388,6 +5487,42 @@ 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 (SImode, regno);

Should be Pmode rather than SImode, and seems
riscv_adjust_libcall_cfi_prologue has same issue...could you send a
separate patch to fix that?

> + mem = gen_frame_mem (SImode, plus_constant (Pmode,

Same here.

> + 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)
> {


> @@ -5493,6 +5697,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 (SImode, regno);

Pmode

> + dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
> + }
> +
> + return dwarf;
> +}
> +
> static rtx
> riscv_adjust_libcall_cfi_epilogue ()
> {

> diff --git a/gcc/config/riscv/zc.md b/gcc/config/riscv/zc.md
> new file mode 100644
> index 00000000000..f2f2198598c
> --- /dev/null
> +++ b/gcc/config/riscv/zc.md
> @@ -0,0 +1,1042 @@
> +;; Machine description for RISC-V Zc extention.
> +;; Copyright (C) 2011-2023 Free Software Foundation, Inc.

2023 rather than 2011-2023
  
Fei Gao June 5, 2023, 8:44 a.m. UTC | #3
Thanks Kito. 
I will propose V4 and also make a separate patch to fix riscv_adjust_libcall_cfi_prologue. 

BR, 
Fei

On 2023-06-05 16:31  Kito Cheng <kito.cheng@gmail.com> wrote:
>
>Only a few minor comments, otherwise LGTM :)
>
>But I guess we need to wait until binutils merge zc stuff.
>
>> Zcmp can share the same logic as save-restore in stack allocation: pre-allocation
>> by cm.push, step 1 and step 2.
>>
>> 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.
>>
>> Signed-off-by: Fei Gao <gaofei@eswincomputing.com>
>>
>> gcc/ChangeLog:
>>
>>         * config/riscv/iterators.md (-8): slot offset in bytes
>>         (-16): likewise
>>         (-24): likewise
>>         (-32): likewise
>>         (-40): likewise
>>         (-48): likewise
>>         (-56): likewise
>>         (-64): likewise
>>         (-72): likewise
>>         (-80): likewise
>>         (-88): likewise
>>         (-96): likewise
>>         (-104): likewise
>
>Use slot0_offset...slot12_offset. 
>
>> @@ -422,6 +430,16 @@ static const struct riscv_tune_info riscv_tune_info_table[] = {
>>  #include "riscv-cores.def"
>>  };
>>
>> +typedef enum
>> +{
>> +  PUSH_IDX = 0,
>> +  POP_IDX,
>> +  POPRET_IDX,
>> +  ZCMP_OP_NUM
>> +} op_idx;
>
>op_idx -> riscv_zcmp_op_t 
>> @@ -5388,6 +5487,42 @@ 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 (SImode, regno);
>
>Should be Pmode rather than SImode, and seems
>riscv_adjust_libcall_cfi_prologue has same issue...could you send a
>separate patch to fix that? 
>
>> +        mem = gen_frame_mem (SImode, plus_constant (Pmode,
>
>Same here.
>
>> +                                                    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)
>>  {
>
>
>> @@ -5493,6 +5697,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 (SImode, regno);
>
>Pmode
>
>> +        dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
>> +      }
>> +
>> +  return dwarf;
>> +}
>> +
>>  static rtx
>>  riscv_adjust_libcall_cfi_epilogue ()
>>  {
>
>> diff --git a/gcc/config/riscv/zc.md b/gcc/config/riscv/zc.md
>> new file mode 100644
>> index 00000000000..f2f2198598c
>> --- /dev/null
>> +++ b/gcc/config/riscv/zc.md
>> @@ -0,0 +1,1042 @@
>> +;; Machine description for RISC-V Zc extention.
>> +;; Copyright (C) 2011-2023 Free Software Foundation, Inc.
>
>2023 rather than 2011-2023
  

Patch

diff --git a/gcc/config/riscv/iterators.md b/gcc/config/riscv/iterators.md
index d374a10810c..6ed4174f9cc 100644
--- a/gcc/config/riscv/iterators.md
+++ b/gcc/config/riscv/iterators.md
@@ -120,6 +120,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 1ed84850e35..f1bce6a9ca8 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -65,6 +65,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 d032f569a36..10284f6b8da 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -56,6 +56,7 @@  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);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 469af02bdf7..96a86bc2884 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -126,6 +126,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;
@@ -422,6 +430,16 @@  static const struct riscv_tune_info riscv_tune_info_table[] = {
 #include "riscv-cores.def"
 };
 
+typedef enum
+{
+  PUSH_IDX = 0,
+  POP_IDX,
+  POPRET_IDX,
+  ZCMP_OP_NUM
+} op_idx;
+
+typedef insn_code (* code_for_push_pop_t)(machine_mode);
+
 void riscv_frame_info::reset(void)
 {
   total_size = 0;
@@ -4876,6 +4894,37 @@  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
+      || (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
@@ -4913,6 +4962,51 @@  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.
@@ -4938,7 +5032,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)
@@ -4957,19 +5051,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)
 {
@@ -5017,8 +5098,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 ())
@@ -5030,6 +5112,15 @@  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);
+        }
     }
 
   /* At the bottom of the frame are any outgoing stack arguments. */
@@ -5044,7 +5135,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;
@@ -5388,6 +5487,42 @@  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 (SImode, regno);
+        mem = gen_frame_mem (SImode, 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)
 {
@@ -5397,6 +5532,45 @@  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 (op_idx 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);
+}
+
 /* Expand the "prologue" pattern.  */
 
 void
@@ -5405,7 +5579,8 @@  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;
+  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);
@@ -5413,8 +5588,35 @@  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;
+    }
   /* 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 ();
@@ -5430,13 +5632,15 @@  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);
     }
 
@@ -5493,6 +5697,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 (SImode, regno);
+        dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+      }
+
+  return dwarf;
+}
+
 static rtx
 riscv_adjust_libcall_cfi_epilogue ()
 {
@@ -5532,10 +5762,18 @@  riscv_expand_epilogue (int style)
   struct riscv_frame_info *frame = &cfun->machine->frame;
   unsigned mask = frame->mask;
   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;
 
@@ -5606,18 +5844,25 @@  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 save 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))
@@ -5652,7 +5897,7 @@  riscv_expand_epilogue (int style)
 					   adjust));
 	  rtx dwarf = NULL_RTX;
 	  rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-					     GEN_INT (step2 + libcall_size));
+					     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;
@@ -5667,15 +5912,15 @@  riscv_expand_epilogue (int style)
       epilogue_cfa_sp_offset = step2;
     }
 
-  if (use_restore_libcall)
+  if (use_restore_libcall || use_multi_pop)
     frame->mask = 0; /* Temporarily fib that we need not save GPRs.  */
 
   /* Restore the registers.  */
-  riscv_for_each_saved_reg (frame->total_size - step2 - libcall_size,
+  riscv_for_each_saved_reg (frame->total_size - step2 - libcall_size - multipop_size,
 			    riscv_restore_reg,
 			    true, style == EXCEPTION_RETURN);
 
-  if (use_restore_libcall)
+  if (use_restore_libcall || use_multi_pop)
       frame->mask = mask; /* Undo the above fib.  */
 
   if (need_barrier_p)
@@ -5689,14 +5934,30 @@  riscv_expand_epilogue (int style)
 
       rtx dwarf = NULL_RTX;
       rtx cfa_adjust_rtx = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-					 GEN_INT (libcall_size ));
+					 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)
+    {
+      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))));
@@ -6980,6 +7241,25 @@  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 4541255a8ae..2fa555dce2d 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
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index f545874edc1..38899c295e4 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -113,6 +113,7 @@ 
 
 (define_constants
   [(RETURN_ADDR_REGNUM		1)
+   (SP_REGNUM 			2)
    (GP_REGNUM 			3)
    (TP_REGNUM			4)
    (T0_REGNUM			5)
@@ -3162,3 +3163,4 @@ 
 (include "sifive-7.md")
 (include "thead.md")
 (include "vector.md")
+(include "zc.md")
diff --git a/gcc/config/riscv/zc.md b/gcc/config/riscv/zc.md
new file mode 100644
index 00000000000..f2f2198598c
--- /dev/null
+++ b/gcc/config/riscv/zc.md
@@ -0,0 +1,1042 @@ 
+;; Machine description for RISC-V Zc extention.
+;; Copyright (C) 2011-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..6dbe489da9b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rv32e_zcmp.c
@@ -0,0 +1,239 @@ 
+/* { dg-do compile } */
+/* { dg-options " -Os -march=rv32e_zca_zcmp -mabi=ilp32e -mcmodel=medlow" } */
+/* { 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..924197cb3c4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c
@@ -0,0 +1,239 @@ 
+/* { dg-do compile } */
+/* { dg-options " -Os -march=rv32imaf_zca_zcmp -mabi=ilp32f -mcmodel=medlow" } */
+/* { 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_stack_alignment.c b/gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c
new file mode 100644
index 00000000000..05602302a8f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zcmp_stack_alignment.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O0 -march=rv32e_zca_zcb_zcmp -mabi=ilp32e -mcmodel=medlow -fomit-frame-pointer" } */
+/* { 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	a5,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;
+}