[v5,2/4] RISC-V: Add Zicfilp ISA extension.

Message ID 20250116081334.95540-2-monk.chiang@sifive.com
State Committed
Commit 805a052d0083e8e76c720fd7ac52cb3ccc36c310
Headers
Series [v5,1/4] RISC-V: Add Zicfiss ISA extension. |

Checks

Context Check Description
rivoscibot/toolchain-ci-rivos-apply-patch success Patch applied
rivoscibot/toolchain-ci-rivos-lint warning Lint failed
rivoscibot/toolchain-ci-rivos-build--newlib-rv64gcv-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gc_zba_zbb_zbc_zbs-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gcv-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-test success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed

Commit Message

Monk Chiang Jan. 16, 2025, 8:13 a.m. UTC
  This patch only support landing pad value is 0.
The next version will implement function signature based labeling
scheme.

RISC-V CFI SPEC: https://github.com/riscv/riscv-cfi

gcc/ChangeLog:
	* gcc/common/config/riscv/riscv-common.cc: Add ZICFILP ISA
	  string.
	* gcc/config.gcc: Add riscv-zicfilp.o
	* gcc/config/riscv/riscv-passes.def (INSERT_PASS_BEFORE):
	  Insert landing pad instructions.
	* gcc/config/riscv/riscv-protos.h (make_pass_insert_landing_pad):
	  Declare.
	* gcc/config/riscv/riscv-zicfilp.cc: New file.
	* gcc/config/riscv/riscv.cc
	  (riscv_trampoline_init): Add landing pad instructions.
	  (riscv_legitimize_call_address): Likewise.
	  (riscv_output_mi_thunk): Likewise.
	* gcc/config/riscv/riscv.h: Update.
	* gcc/config/riscv/riscv.md: Add landing pad patterns.
	* gcc/config/riscv/riscv.opt (TARGET_ZICFILP): Define.
	* gcc/config/riscv/t-riscv: Add build rule for
	  riscv-zicfilp.o

gcc/testsuite/ChangeLog:
	* gcc/testsuite/gcc.target/riscv/interrupt-no-lpad.c: New test.
	* gcc/testsuite/gcc.target/riscv/zicfilp-call.c: New test.

Co-Developed-by: Greg McGary <gkm@rivosinc.com>
                 Kito Cheng  <kito.cheng@gmail.com>
---
 gcc/common/config/riscv/riscv-common.cc       |   3 +
 gcc/config.gcc                                |   2 +-
 gcc/config/riscv/riscv-passes.def             |   1 +
 gcc/config/riscv/riscv-protos.h               |   1 +
 gcc/config/riscv/riscv-zicfilp.cc             | 169 ++++++++++++++++++
 gcc/config/riscv/riscv.cc                     | 145 ++++++++++++---
 gcc/config/riscv/riscv.h                      |  13 +-
 gcc/config/riscv/riscv.md                     |  73 +++++++-
 gcc/config/riscv/riscv.opt                    |   2 +
 gcc/config/riscv/t-riscv                      |   9 +
 .../gcc.target/riscv/interrupt-no-lpad.c      |   7 +
 gcc/testsuite/gcc.target/riscv/zicfilp-call.c |  14 ++
 12 files changed, 402 insertions(+), 37 deletions(-)
 create mode 100644 gcc/config/riscv/riscv-zicfilp.cc
 create mode 100644 gcc/testsuite/gcc.target/riscv/interrupt-no-lpad.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zicfilp-call.c
  

Comments

Jin Ma March 12, 2025, 9:43 a.m. UTC | #1
Hi, Monk Chiang

I noticed that at -O0, static functions are emitting lpad instructions, whereas
they do not at -O2. I'm not sure if this is expected behavior.

Upon further investigation, I found that c_node->only_called_directly_p() returns
false, which is caused by force_output being set to 1. Tracing back, I encountered
the following patch[1] and PR25961[2], which set force_output to 1 for static
functions at -O0, while it is 0 at -O2.

Do you have any comments on this?


Example code:

int cc = 333;
extern int aa;

__attribute__((noinline))
static void
foo(void)
{
  cc = aa;
}
int main(void)

{
  foo();
  return 0;
}

[1] https://gcc.gnu.org/legacy-ml/gcc-patches/2006-05/msg00315.html
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=24561
  
Monk Chiang March 14, 2025, 5:30 a.m. UTC | #2
Hi Jin Ma,
  This situation is the same on x86. When using -O0, the lpad instruction
is merely a redundant instruction and does not affect the execution result.
  This is the ASM result for x86, and there is also an endbr64 in foo().
     https://godbolt.org/z/M1fTendE3

On Wed, Mar 12, 2025 at 5:44 PM Jin Ma <jinma@linux.alibaba.com> wrote:

> Hi, Monk Chiang
>
> I noticed that at -O0, static functions are emitting lpad instructions,
> whereas
> they do not at -O2. I'm not sure if this is expected behavior.
>
> Upon further investigation, I found that c_node->only_called_directly_p()
> returns
> false, which is caused by force_output being set to 1. Tracing back, I
> encountered
> the following patch[1] and PR25961[2], which set force_output to 1 for
> static
> functions at -O0, while it is 0 at -O2.
>
> Do you have any comments on this?
>
>
> Example code:
>
> int cc = 333;
> extern int aa;
>
> __attribute__((noinline))
> static void
> foo(void)
> {
>   cc = aa;
> }
> int main(void)
>
> {
>   foo();
>   return 0;
> }
>
> [1] https://gcc.gnu.org/legacy-ml/gcc-patches/2006-05/msg00315.html
> [2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=24561
>
  
Jin Ma March 14, 2025, 5:52 a.m. UTC | #3
On Fri, 14 Mar 2025 13:30:39 +0800, Monk Chiang wrote:
> Hi Jin Ma,
>   This situation is the same on x86. When using -O0, the lpad instruction
> is merely a redundant instruction and does not affect the execution result.
>   This is the ASM result for x86, and there is also an endbr64 in foo().
>      https://godbolt.org/z/M1fTendE3

As I mentioned earlier, this is architecture-agnostic (regarding the handling of
static functions at -O0). There is a minor concern about whether it is reasonable
to insert instructions into static functions. Currently, LLVM does not encounter
the same issue as GCC (LLVM does not handle static functions in the same way).

Regardless, I understand that this will not affect the execution results. However,
I believe it would be best to maintain logical consistency with LLVM (whether it's
about lpad instructions or static functions in -O0). I'm not entirely certain, but I wanted
to bring this up to hear everyone's thoughts on the matter :)

Best regards,
Jin Ma


> On Wed, Mar 12, 2025 at 5:44=E2=80=AFPM Jin Ma <jinma@linux.alibaba.com> wrote:
> 
> > Hi, Monk Chiang
> >
> > I noticed that at -O0, static functions are emitting lpad instructions,
> > whereas
> > they do not at -O2. I'm not sure if this is expected behavior.
> >
> > Upon further investigation, I found that c_node->only_called_directly_p()
> > returns
> > false, which is caused by force_output being set to 1. Tracing back, I
> > encountered
> > the following patch[1] and PR25961[2], which set force_output to 1 for
> > static
> > functions at -O0, while it is 0 at -O2.
> >
> > Do you have any comments on this?
> >
> >
> > Example code:
> >
> > int cc = 333;
> > extern int aa;
> >
> > __attribute__((noinline))
> > static void
> > foo(void)
> > {
> >   cc = aa;
> > }
> > int main(void)
> >
> > {
> >   foo();
> >   return 0;
> > }
> >
> > [1] https://gcc.gnu.org/legacy-ml/gcc-patches/2006-05/msg00315.html
> > [2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=24561
> >
>
  

Patch

diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index 8e8b6107a6d..5038f0eb959 100644
--- a/gcc/common/config/riscv/riscv-common.cc
+++ b/gcc/common/config/riscv/riscv-common.cc
@@ -113,6 +113,7 @@  static const riscv_implied_info_t riscv_implied_info[] =
 
   {"zicfiss", "zicsr"},
   {"zicfiss", "zimop"},
+  {"zicfilp", "zicsr"},
 
   {"zk", "zkn"},
   {"zk", "zkr"},
@@ -329,6 +330,7 @@  static const struct riscv_ext_version riscv_ext_version_table[] =
   {"ziccrse",  ISA_SPEC_CLASS_NONE, 1, 0},
 
   {"zicfiss", ISA_SPEC_CLASS_NONE, 1, 0},
+  {"zicfilp", ISA_SPEC_CLASS_NONE, 1, 0},
 
   {"zimop", ISA_SPEC_CLASS_NONE, 1, 0},
   {"zcmop", ISA_SPEC_CLASS_NONE, 1, 0},
@@ -1653,6 +1655,7 @@  static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
   RISCV_EXT_FLAG_ENTRY ("zic64b", x_riscv_zicmo_subext, MASK_ZIC64B),
 
   RISCV_EXT_FLAG_ENTRY ("zicfiss", x_riscv_zi_subext, MASK_ZICFISS),
+  RISCV_EXT_FLAG_ENTRY ("zicfilp", x_riscv_zi_subext, MASK_ZICFILP),
 
   RISCV_EXT_FLAG_ENTRY ("zimop", x_riscv_mop_subext, MASK_ZIMOP),
   RISCV_EXT_FLAG_ENTRY ("zcmop", x_riscv_mop_subext, MASK_ZCMOP),
diff --git a/gcc/config.gcc b/gcc/config.gcc
index 55e37146ee0..87fed823118 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -553,7 +553,7 @@  riscv*)
 	extra_objs="riscv-builtins.o riscv-c.o riscv-sr.o riscv-shorten-memrefs.o riscv-selftests.o riscv-string.o"
 	extra_objs="${extra_objs} riscv-v.o riscv-vsetvl.o riscv-vector-costs.o riscv-avlprop.o"
 	extra_objs="${extra_objs} riscv-vector-builtins.o riscv-vector-builtins-shapes.o riscv-vector-builtins-bases.o sifive-vector-builtins-bases.o"
-	extra_objs="${extra_objs} thead.o riscv-target-attr.o"
+	extra_objs="${extra_objs} thead.o riscv-target-attr.o riscv-zicfilp.o"
 	d_target_objs="riscv-d.o"
 	extra_headers="riscv_vector.h riscv_crypto.h riscv_bitmanip.h riscv_th_vector.h riscv_cmo.h"
 	target_gtfiles="$target_gtfiles \$(srcdir)/config/riscv/riscv-vector-builtins.cc"
diff --git a/gcc/config/riscv/riscv-passes.def b/gcc/config/riscv/riscv-passes.def
index cbea23c8b44..7e6a2a0e53d 100644
--- a/gcc/config/riscv/riscv-passes.def
+++ b/gcc/config/riscv/riscv-passes.def
@@ -20,3 +20,4 @@ 
 INSERT_PASS_AFTER (pass_rtl_store_motion, 1, pass_shorten_memrefs);
 INSERT_PASS_AFTER (pass_split_all_insns, 1, pass_avlprop);
 INSERT_PASS_BEFORE (pass_fast_rtl_dce, 1, pass_vsetvl);
+INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_landing_pad);
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index dd3b36d47a6..6362380ec6c 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -200,6 +200,7 @@  extern bool riscv_hard_regno_rename_ok (unsigned, unsigned);
 rtl_opt_pass * make_pass_shorten_memrefs (gcc::context *ctxt);
 rtl_opt_pass * make_pass_avlprop (gcc::context *ctxt);
 rtl_opt_pass * make_pass_vsetvl (gcc::context *ctxt);
+rtl_opt_pass * make_pass_insert_landing_pad (gcc::context *ctxt);
 
 /* Routines implemented in riscv-string.c.  */
 extern bool riscv_expand_block_compare (rtx, rtx, rtx, rtx);
diff --git a/gcc/config/riscv/riscv-zicfilp.cc b/gcc/config/riscv/riscv-zicfilp.cc
new file mode 100644
index 00000000000..42b129920b3
--- /dev/null
+++ b/gcc/config/riscv/riscv-zicfilp.cc
@@ -0,0 +1,169 @@ 
+/* Branch Target Identification for RISCV architecture.
+   Copyright (C) 2019-2025 Free Software Foundation, Inc.
+   Based on ARM target.
+
+   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 IN_TARGET_CODE 1
+
+#include "config.h"
+#define INCLUDE_STRING
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "memmodel.h"
+#include "gimple.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "expr.h"
+#include "emit-rtl.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "dumpfile.h"
+#include "rtl-iter.h"
+#include "cfgrtl.h"
+#include "tree-pass.h"
+#include "cgraph.h"
+#include "output.h"
+
+/* This pass implements forward-CFI landing pad checks for RISCV. This is
+   a security feature similar to BTI (branch target identification) in
+   AArch64 and IBT (indirect branch tracking)in X86. A LPAD (landing-pad
+   check) instruction is used to guard against the execution of
+   instructions which are not the intended target of an indirect branch.
+
+   When forward-CFI is disabled or unimplemented in the CPU, the
+   landing-pad check label instructions behave as NOP. When implemented in
+   the CPU, and enabled, the destination of an indirect branch must be
+   LPAD insn. Otherwise, the CPU reaises an exception.
+
+   In order to enable this mechanism, this pass iterates through the
+   control flow of the code and adds appropriate LPAD instructions at the
+   beginning of any function that can be called indirectly, and for targets
+   of indirect jumps, i.e., jump table targets, non-local goto targets, and
+   labels that might be referenced by variables, constant pools, etc
+   (NOTE_INSN_DELETED_LABEL). */
+
+namespace {
+
+const pass_data pass_data_insert_landing_pad =
+{
+  RTL_PASS, /* type. */
+  "zisslpcfi", /* name. */
+  OPTGROUP_NONE, /* optinfo_flags. */
+  TV_MACH_DEP, /* tv_id. */
+  0, /* properties_required. */
+  0, /* properties_provided. */
+  0, /* properties_destroyed. */
+  0, /* todo_flags_start. */
+  0, /* todo_flags_finish. */
+};
+
+static bool
+is_interrupt_handler_p (tree type)
+{
+  return lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+/* Insert landing-pad check instructions.  This is a late RTL pass that runs
+   before branch shortening. */
+static unsigned int
+rest_of_insert_landing_pad (void)
+{
+  timevar_push (TV_MACH_DEP);
+
+  struct cgraph_node *c_node;
+  rtx lpad_insn;
+  rtx_insn *insn;
+  basic_block bb;
+
+  bb = 0;
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
+	   insn = NEXT_INSN (insn))
+	{
+	  /* If a label is marked to be preserved or can be a non-local goto
+	     target, it must be protected with a lpad instruction.  */
+	  if (LABEL_P (insn)
+	       && (LABEL_PRESERVE_P (insn)
+		   || bb->flags & BB_NON_LOCAL_GOTO_TARGET))
+	    {
+	      emit_insn_before (gen_lpad_align (), insn);
+	      emit_insn_after (gen_lpad (const0_rtx), insn);
+	      continue;
+	    }
+
+	  if (INSN_P (insn) && INSN_CODE (insn) == CODE_FOR_gpr_save)
+	    {
+	      emit_move_insn (RISCV_CALL_ADDRESS_LPAD (Pmode), const0_rtx);
+	      emit_insn_before (gen_lpad_align (), insn);
+	      emit_insn_after (gen_lpad (const0_rtx), insn);
+	      continue;
+	    }
+
+	  if (INSN_P (insn) && INSN_CODE (insn) == CODE_FOR_gpr_restore)
+	    emit_move_insn (RISCV_CALL_ADDRESS_LPAD (Pmode), const0_rtx);
+
+	}
+    }
+
+  c_node = cgraph_node::get (cfun->decl);
+  if (!c_node->only_called_directly_p ()
+      && !is_interrupt_handler_p (TREE_TYPE (cfun->decl)))
+    {
+      bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
+      insn = BB_HEAD (bb);
+      lpad_insn = gen_lpad (const0_rtx);
+      emit_insn_before (lpad_insn, insn);
+    }
+
+  timevar_pop (TV_MACH_DEP);
+  return 0;
+}
+
+class pass_insert_landing_pad : public rtl_opt_pass
+{
+public:
+  pass_insert_landing_pad (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_insert_landing_pad, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+    {
+      return TARGET_ZICFILP;
+    }
+
+  virtual unsigned int execute (function *)
+    {
+      return rest_of_insert_landing_pad ();
+    }
+
+}; // class pass_insert_landing_pad
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_insert_landing_pad (gcc::context *ctxt)
+{
+  return new pass_insert_landing_pad (ctxt);
+}
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index cd37b492183..4afb0b95839 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -6681,8 +6681,20 @@  riscv_legitimize_call_address (rtx addr)
     {
       rtx reg = RISCV_CALL_ADDRESS_TEMP (Pmode);
       riscv_emit_move (reg, addr);
+
+      if (TARGET_ZICFILP)
+	{
+	  rtx sw_guarded = RISCV_CALL_ADDRESS_LPAD (Pmode);
+	  emit_insn (gen_set_guarded (Pmode, reg));
+	  return sw_guarded;
+	}
+
       return reg;
     }
+
+  if (TARGET_ZICFILP && REG_P (addr))
+    emit_insn (gen_set_lpl (Pmode, const0_rtx));
+
   return addr;
 }
 
@@ -10342,6 +10354,9 @@  riscv_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   /* Mark the end of the (empty) prologue.  */
   emit_note (NOTE_INSN_PROLOGUE_END);
 
+  if (TARGET_ZICFILP)
+    emit_insn(gen_lpad (const0_rtx));
+
   /* Determine if we can use a sibcall to call FUNCTION directly.  */
   fnaddr = gen_rtx_MEM (FUNCTION_MODE, XEXP (DECL_RTL (function), 0));
 
@@ -10853,12 +10868,17 @@  riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
 {
   rtx addr, end_addr, mem;
   uint32_t trampoline[4];
+  uint32_t trampoline_cfi[6];
   unsigned int i;
   HOST_WIDE_INT static_chain_offset, target_function_offset;
+  HOST_WIDE_INT lp_value = 0;
 
   /* Work out the offsets of the pointers from the start of the
      trampoline code.  */
-  gcc_assert (ARRAY_SIZE (trampoline) * 4 == TRAMPOLINE_CODE_SIZE);
+  if (!TARGET_ZICFILP)
+    gcc_assert (ARRAY_SIZE (trampoline) * 4 == TRAMPOLINE_CODE_SIZE);
+  else
+    gcc_assert (ARRAY_SIZE (trampoline_cfi) * 4 == TRAMPOLINE_CODE_SIZE);
 
   /* Get pointers to the beginning and end of the code block.  */
   addr = force_reg (Pmode, XEXP (m_tramp, 0));
@@ -10880,6 +10900,17 @@  riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
       unsigned HOST_WIDE_INT lo_chain_code, lo_func_code;
 
       rtx uimm_mask = force_reg (SImode, gen_int_mode (-IMM_REACH, SImode));
+      unsigned insn_count = 0;
+
+      /* Insert lpad, if zicfilp is enabled.  */
+      if (TARGET_ZICFILP)
+	{
+	  unsigned HOST_WIDE_INT lpad_code;
+	  lpad_code = OPCODE_AUIPC | (0 << SHIFT_RD) | (lp_value << IMM_BITS);
+	  mem = adjust_address (m_tramp, SImode, 0);
+	  riscv_emit_move (mem, gen_int_mode (lpad_code, SImode));
+	  insn_count++;
+	}
 
       /* 0xfff.  */
       rtx imm12_mask = gen_reg_rtx (SImode);
@@ -10893,11 +10924,14 @@  riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
       hi_chain = riscv_force_binary (SImode, AND, hi_chain,
 				     uimm_mask);
       lui_hi_chain_code = OPCODE_LUI | (STATIC_CHAIN_REGNUM << SHIFT_RD);
+      rtx lui_hi_chain_value = force_reg (SImode, gen_int_mode (lui_hi_chain_code,
+								SImode));
       rtx lui_hi_chain = riscv_force_binary (SImode, IOR, hi_chain,
-					     gen_int_mode (lui_hi_chain_code, SImode));
-
-      mem = adjust_address (m_tramp, SImode, 0);
+					     lui_hi_chain_value);
+      mem = adjust_address (m_tramp, SImode,
+			    insn_count * GET_MODE_SIZE (SImode));
       riscv_emit_move (mem, riscv_swap_instruction (lui_hi_chain));
+      insn_count++;
 
       /* Gen lui t0, hi(func).  */
       rtx hi_func = riscv_force_binary (SImode, PLUS, target_function,
@@ -10908,8 +10942,10 @@  riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
       rtx lui_hi_func = riscv_force_binary (SImode, IOR, hi_func,
 					    gen_int_mode (lui_hi_func_code, SImode));
 
-      mem = adjust_address (m_tramp, SImode, 1 * GET_MODE_SIZE (SImode));
+      mem = adjust_address (m_tramp, SImode,
+			    insn_count * GET_MODE_SIZE (SImode));
       riscv_emit_move (mem, riscv_swap_instruction (lui_hi_func));
+      insn_count++;
 
       /* Gen addi t2, t2, lo(chain).  */
       rtx lo_chain = riscv_force_binary (SImode, AND, chain_value,
@@ -10923,8 +10959,23 @@  riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
       rtx addi_lo_chain = riscv_force_binary (SImode, IOR, lo_chain,
 					      force_reg (SImode, GEN_INT (lo_chain_code)));
 
-      mem = adjust_address (m_tramp, SImode, 2 * GET_MODE_SIZE (SImode));
+      mem = adjust_address (m_tramp, SImode,
+			    insn_count * GET_MODE_SIZE (SImode));
       riscv_emit_move (mem, riscv_swap_instruction (addi_lo_chain));
+      insn_count++;
+
+      /* For zicfilp only, insert lui t2, 1, because use jr t0.  */
+      if (TARGET_ZICFILP)
+	{
+	  unsigned HOST_WIDE_INT set_lpl_code;
+	  set_lpl_code  = OPCODE_LUI
+			  | (RISCV_CALL_ADDRESS_LPAD_REGNUM << SHIFT_RD)
+			  | (lp_value << IMM_BITS);
+	  mem = adjust_address (m_tramp, SImode,
+				insn_count * GET_MODE_SIZE (SImode));
+	  riscv_emit_move (mem, gen_int_mode (set_lpl_code, SImode));
+	  insn_count++;
+	}
 
       /* Gen jr t0, lo(func).  */
       rtx lo_func = riscv_force_binary (SImode, AND, target_function,
@@ -10936,7 +10987,7 @@  riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
       rtx jr_lo_func = riscv_force_binary (SImode, IOR, lo_func,
 					   force_reg (SImode, GEN_INT (lo_func_code)));
 
-      mem = adjust_address (m_tramp, SImode, 3 * GET_MODE_SIZE (SImode));
+      mem = adjust_address (m_tramp, SImode, insn_count * GET_MODE_SIZE (SImode));
       riscv_emit_move (mem, riscv_swap_instruction (jr_lo_func));
     }
   else
@@ -10944,29 +10995,65 @@  riscv_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
       static_chain_offset = TRAMPOLINE_CODE_SIZE;
       target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode);
 
-      /* auipc   t2, 0
-	 l[wd]   t0, target_function_offset(t2)
-	 l[wd]   t2, static_chain_offset(t2)
-	 jr      t0
-      */
-      trampoline[0] = OPCODE_AUIPC | (STATIC_CHAIN_REGNUM << SHIFT_RD);
-      trampoline[1] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW)
-		      | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RD)
-		      | (STATIC_CHAIN_REGNUM << SHIFT_RS1)
-		      | (target_function_offset << SHIFT_IMM);
-      trampoline[2] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW)
-		      | (STATIC_CHAIN_REGNUM << SHIFT_RD)
-		      | (STATIC_CHAIN_REGNUM << SHIFT_RS1)
-		      | (static_chain_offset << SHIFT_IMM);
-      trampoline[3] = OPCODE_JALR | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RS1);
-
-      /* Copy the trampoline code.  */
-      for (i = 0; i < ARRAY_SIZE (trampoline); i++)
+      if (!TARGET_ZICFILP)
 	{
-	  if (BYTES_BIG_ENDIAN)
-	    trampoline[i] = __builtin_bswap32(trampoline[i]);
-	  mem = adjust_address (m_tramp, SImode, i * GET_MODE_SIZE (SImode));
-	  riscv_emit_move (mem, gen_int_mode (trampoline[i], SImode));
+	  /* auipc   t2, 0
+	     l[wd]   t0, (target_function_offset)(t2)
+	     l[wd]   t2, (static_chain_offset)(t2)
+	     jr      t0
+	  */
+	  trampoline[0] = OPCODE_AUIPC | (STATIC_CHAIN_REGNUM << SHIFT_RD);
+	  trampoline[1] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW)
+			  | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RD)
+			  | (STATIC_CHAIN_REGNUM << SHIFT_RS1)
+			  | (target_function_offset << SHIFT_IMM);
+	  trampoline[2] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW)
+			  | (STATIC_CHAIN_REGNUM << SHIFT_RD)
+			  | (STATIC_CHAIN_REGNUM << SHIFT_RS1)
+			  | (static_chain_offset << SHIFT_IMM);
+	  trampoline[3] = OPCODE_JALR | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RS1);
+
+	  /* Copy the trampoline code.  */
+	  for (i = 0; i < ARRAY_SIZE (trampoline); i++)
+	    {
+	      if (BYTES_BIG_ENDIAN)
+		trampoline[i] = __builtin_bswap32 (trampoline[i]);
+	      mem = adjust_address (m_tramp, SImode, i * GET_MODE_SIZE (SImode));
+	      riscv_emit_move (mem, gen_int_mode (trampoline[i], SImode));
+	    }
+	}
+      else
+	{
+	  /* lpad    1
+	     auipc   t3, 0
+	     l[wd]   t0, (target_function_offset - 4)(t3)
+	     l[wd]   t3, (static_chain_offset - 4)(t3)
+	     lui     t2, 1
+	     jr      t0
+	  */
+	  trampoline_cfi[0] = OPCODE_AUIPC | (0 << SHIFT_RD) | (lp_value << IMM_BITS);
+	  trampoline_cfi[1] = OPCODE_AUIPC | (STATIC_CHAIN_REGNUM << SHIFT_RD);
+	  trampoline_cfi[2] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW)
+			      | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RD)
+			      | (STATIC_CHAIN_REGNUM << SHIFT_RS1)
+			      | ((target_function_offset - 4) << SHIFT_IMM);
+	  trampoline_cfi[3] = (Pmode == DImode ? OPCODE_LD : OPCODE_LW)
+			      | (STATIC_CHAIN_REGNUM << SHIFT_RD)
+			      | (STATIC_CHAIN_REGNUM << SHIFT_RS1)
+			      | ((static_chain_offset - 4) << SHIFT_IMM);
+	  trampoline_cfi[4] = OPCODE_LUI
+			      | (RISCV_CALL_ADDRESS_LPAD_REGNUM << SHIFT_RD)
+			      | (lp_value << IMM_BITS);
+	  trampoline_cfi[5] = OPCODE_JALR | (RISCV_PROLOGUE_TEMP_REGNUM << SHIFT_RS1);
+
+	  /* Copy the trampoline code.  */
+	  for (i = 0; i < ARRAY_SIZE (trampoline_cfi); i++)
+	    {
+	      if (BYTES_BIG_ENDIAN)
+		trampoline_cfi[i] = __builtin_bswap32 (trampoline_cfi[i]);
+	      mem = adjust_address (m_tramp, SImode, i * GET_MODE_SIZE (SImode));
+	      riscv_emit_move (mem, gen_int_mode (trampoline_cfi[i], SImode));
+	    }
 	}
 
       /* Set up the static chain pointer field.  */
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index 93e88fe885d..073b9e4f2bd 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -190,7 +190,8 @@  ASM_MISA_SPEC
 #define PARM_BOUNDARY BITS_PER_WORD
 
 /* Allocation boundary (in *bits*) for the code of a function.  */
-#define FUNCTION_BOUNDARY ((TARGET_RVC || TARGET_ZCA) ? 16 : 32)
+#define FUNCTION_BOUNDARY \
+  (((TARGET_RVC || TARGET_ZCA) && !TARGET_ZICFILP) ? 16 : 32)
 
 /* The smallest supported stack boundary the calling convention supports.  */
 #define STACK_BOUNDARY \
@@ -413,7 +414,8 @@  ASM_MISA_SPEC
 #define RISCV_DWARF_VLENB (4096 + 0xc22)
 
 /* Register in which static-chain is passed to a function.  */
-#define STATIC_CHAIN_REGNUM (GP_TEMP_FIRST + 2)
+#define STATIC_CHAIN_REGNUM \
+  ((TARGET_ZICFILP) ? (GP_TEMP_FIRST + 23) : (GP_TEMP_FIRST + 2))
 
 /* Registers used as temporaries in prologue/epilogue code.
 
@@ -436,6 +438,10 @@  ASM_MISA_SPEC
 #define RISCV_CALL_ADDRESS_TEMP(MODE) \
   gen_rtx_REG (MODE, RISCV_CALL_ADDRESS_TEMP_REGNUM)
 
+#define RISCV_CALL_ADDRESS_LPAD_REGNUM (GP_TEMP_FIRST + 2)
+#define RISCV_CALL_ADDRESS_LPAD(MODE) \
+  gen_rtx_REG (MODE, RISCV_CALL_ADDRESS_LPAD_REGNUM)
+
 #define RETURN_ADDR_MASK (1 << RETURN_ADDR_REGNUM)
 #define S0_MASK (1 << S0_REGNUM)
 #define S1_MASK (1 << S1_REGNUM)
@@ -821,7 +827,8 @@  extern enum riscv_cc get_riscv_cc (const rtx use);
 
 /* Trampolines are a block of code followed by two pointers.  */
 
-#define TRAMPOLINE_CODE_SIZE 16
+#define TRAMPOLINE_CODE_SIZE ((TARGET_ZICFILP) ? 24 : 16)
+
 #define TRAMPOLINE_SIZE		\
   ((Pmode == SImode)		\
    ? TRAMPOLINE_CODE_SIZE	\
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 480e6c8856d..4748c0a9a1f 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -143,6 +143,12 @@ 
   UNSPECV_SSRDP
   UNSPECV_SSP
 
+  ;; ZICFILP
+  UNSPECV_LPAD
+  UNSPECV_SETLPL
+  UNSPECV_LPAD_ALIGN
+  UNSPECV_SET_GUARDED
+
   ;; XTheadInt unspec
   UNSPECV_XTHEADINT_PUSH
   UNSPECV_XTHEADINT_POP
@@ -155,6 +161,7 @@ 
    (TP_REGNUM			4)
    (T0_REGNUM			5)
    (T1_REGNUM			6)
+   (T2_REGNUM			7)
    (S0_REGNUM			8)
    (S1_REGNUM			9)
    (A0_REGNUM			10)
@@ -3709,11 +3716,18 @@ 
   [(set (pc) (match_operand 0 "register_operand"))]
   ""
 {
+  if (TARGET_ZICFILP)
+    emit_insn (gen_set_lpl (Pmode, const0_rtx));
+
   operands[0] = force_reg (Pmode, operands[0]);
+  if (TARGET_ZICFILP)
+    emit_use (gen_rtx_REG (Pmode, T2_REGNUM));
+
   if (Pmode == SImode)
     emit_jump_insn (gen_indirect_jumpsi (operands[0]));
   else
     emit_jump_insn (gen_indirect_jumpdi (operands[0]));
+
   DONE;
 })
 
@@ -3734,21 +3748,42 @@ 
 					 gen_rtx_LABEL_REF (Pmode, operands[1]),
 					 NULL_RTX, 0, OPTAB_DIRECT);
 
-  if (CASE_VECTOR_PC_RELATIVE && Pmode == DImode)
-    emit_jump_insn (gen_tablejumpdi (operands[0], operands[1]));
+  if (TARGET_ZICFILP)
+    {
+      rtx t2 = RISCV_CALL_ADDRESS_LPAD (GET_MODE (operands[0]));
+      emit_move_insn (t2, operands[0]);
+
+      if (CASE_VECTOR_PC_RELATIVE && Pmode == DImode)
+	emit_jump_insn (gen_tablejump_cfidi (operands[1]));
+      else
+	emit_jump_insn (gen_tablejump_cfisi (operands[1]));
+    }
   else
-    emit_jump_insn (gen_tablejumpsi (operands[0], operands[1]));
+    {
+      if (CASE_VECTOR_PC_RELATIVE && Pmode == DImode)
+	emit_jump_insn (gen_tablejumpdi (operands[0], operands[1]));
+      else
+	emit_jump_insn (gen_tablejumpsi (operands[0], operands[1]));
+    }
   DONE;
 })
 
 (define_insn "tablejump<mode>"
   [(set (pc) (match_operand:GPR 0 "register_operand" "l"))
    (use (label_ref (match_operand 1 "" "")))]
-  ""
+  "!TARGET_ZICFILP"
   "jr\t%0"
   [(set_attr "type" "jalr")
    (set_attr "mode" "none")])
 
+(define_insn "tablejump_cfi<mode>"
+  [(set (pc) (reg:GPR T2_REGNUM))
+   (use (label_ref (match_operand 0 "")))]
+  "TARGET_ZICFILP"
+  "jr\tt2"
+  [(set_attr "type" "jalr")
+   (set_attr "mode" "none")])
+
 ;;
 ;;  ....................
 ;;
@@ -4718,6 +4753,36 @@ 
   [(set_attr "type" "arith")
    (set_attr "mode" "<MODE>")])
 
+;; Lading pad.
+
+(define_insn "lpad"
+  [(unspec_volatile [(match_operand 0 "immediate_operand" "i")] UNSPECV_LPAD)]
+  "TARGET_ZICFILP"
+  "lpad\t%0"
+  [(set_attr "type" "auipc")])
+
+(define_insn "@set_lpl<mode>"
+  [(set (reg:GPR T2_REGNUM)
+	(unspec_volatile [(match_operand:GPR 0 "immediate_operand" "i")] UNSPECV_SETLPL))]
+   "TARGET_ZICFILP"
+   "lui\tt2,%0"
+  [(set_attr "type" "const")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "lpad_align"
+  [(unspec_volatile [(const_int 0)] UNSPECV_LPAD_ALIGN)]
+  "TARGET_ZICFILP"
+  ".align 2"
+  [(set_attr "type" "nop")])
+
+(define_insn "@set_guarded<mode>"
+  [(set (reg:GPR T2_REGNUM)
+	(unspec_volatile [(match_operand:GPR 0 "register_operand" "r")] UNSPECV_SET_GUARDED))]
+  "TARGET_ZICFILP"
+  "mv\tt2,%0"
+  [(set_attr "type" "move")
+   (set_attr "mode" "<MODE>")])
+
 (include "bitmanip.md")
 (include "crypto.md")
 (include "sync.md")
diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
index 4a43bfd7bc6..f51f8fd1cdf 100644
--- a/gcc/config/riscv/riscv.opt
+++ b/gcc/config/riscv/riscv.opt
@@ -255,6 +255,8 @@  Mask(ZICCRSE)     Var(riscv_zi_subext)
 
 Mask(ZICFISS)     Var(riscv_zi_subext)
 
+Mask(ZICFILP)     Var(riscv_zi_subext)
+
 TargetVariable
 int riscv_za_subext
 
diff --git a/gcc/config/riscv/t-riscv b/gcc/config/riscv/t-riscv
index bc67d8719fa..6493087fe51 100644
--- a/gcc/config/riscv/t-riscv
+++ b/gcc/config/riscv/t-riscv
@@ -145,6 +145,15 @@  thead.o: $(srcdir)/config/riscv/thead.cc \
 	$(COMPILE) $<
 	$(POSTCOMPILE)
 
+riscv-zicfilp.o: $(srcdir)/config/riscv/riscv-zicfilp.cc \
+    $(CONFIG_H) $(SYSTEM_H) $(TM_H) $(REGS_H) insn-config.h $(RTL_BASE_H) \
+    dominance.h cfg.h cfganal.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(RECOG_H) \
+    output.h hash-map.h $(DF_H) $(OBSTACK_H) $(TARGET_H) $(RTL_H) \
+    $(CONTEXT_H) $(TREE_PASS_H) regrename.h \
+    $(srcdir)/config/riscv/riscv-protos.h
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+		$(srcdir)/config/riscv/riscv-zicfilp.cc
+
 PASSES_EXTRA += $(srcdir)/config/riscv/riscv-passes.def
 
 $(common_out_file): $(srcdir)/config/riscv/riscv-cores.def \
diff --git a/gcc/testsuite/gcc.target/riscv/interrupt-no-lpad.c b/gcc/testsuite/gcc.target/riscv/interrupt-no-lpad.c
new file mode 100644
index 00000000000..ff512b98c61
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/interrupt-no-lpad.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile { target { riscv64*-*-* } } } */
+/* { dg-options "-march=rv64gc_zicfilp -mabi=lp64d" } */
+void __attribute__ ((interrupt))
+foo (void)
+{
+}
+/* { dg-final { scan-assembler-not "lpad\t" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zicfilp-call.c b/gcc/testsuite/gcc.target/riscv/zicfilp-call.c
new file mode 100644
index 00000000000..75c8b32faa3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zicfilp-call.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target { riscv64*-*-* } } } */
+/* { dg-options "-O2 -fPIE -march=rv64gc_zicfilp -mabi=lp64d" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+extern void _dl_find_object_init (void);
+
+void
+_dl_non_dynamic_init (void)
+{
+  extern __typeof__ (_dl_find_object_init) _dl_find_object_init __attribute__ ((weak));
+  (_dl_find_object_init != ((void *) 0) ? _dl_find_object_init () : (void)0);
+}
+
+/* { dg-final { scan-assembler-times "mv\tt2" 1 } } */