[clang] [llvm] [RISCV][Zicfilp] Enable Zicfilp CFI compiler behaviors by looking at module flags (PR #152121)

Ming-Yi Lai via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 27 02:00:13 PDT 2025


https://github.com/mylai-mtk updated https://github.com/llvm/llvm-project/pull/152121

>From 614f475c4067f15df01186f782688d6ab8607d5a Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Tue, 5 Aug 2025 17:31:54 +0800
Subject: [PATCH 01/16] [RISCV][Zicfilp] Codegen LPAD insns by looking at
 module flags

Expected Behavior:

Stop codegening LPAD insns by testing if the target has the Zicfilp extension
and instead, codegen LPAD insns if the LLVM module has all of these flags:

+ `cf-protection-branch`: Needs to be a non-zero integer (which means `true`)
+ `cf-branch-label-scheme`: Needs to be `unlabeled`

Context:

In clang, Zicfilp-based control flow integrity (the `unlabeled` scheme) can now
be enabled by giving the `-fcf-protection=branch
-mcf-branch-label-scheme=unlabeled` options. With these options, the clang
frontend adds the above-mentioned flags to LLVM modules. Here we want to align
LPAD insn codegen to be enabled by the semantics of those LLVM module flags,
instead of relying on the inaccurate indicator of whether the Zicfilp extension
is available, so the toolchain's behavior is more streamlined and expected.

Also, since LPAD insns can be executed regardless of whether Zicfilp is
available in target or not (due to LPAD insn being encoded as a standard hint
insn), clang accepts the above-mentioned CLI options even if Zicfilp is not
enabled and expects backend to still generate LPAD insns. This patch enables
LPAD insn generation in such cases.
---
 llvm/include/llvm/Support/RISCVISAUtils.h     | 19 +++++++++++++++++
 .../RISCV/RISCVIndirectBranchTracking.cpp     | 21 ++++++++++++++++++-
 llvm/test/CodeGen/RISCV/lpad.ll               |  5 +++++
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Support/RISCVISAUtils.h b/llvm/include/llvm/Support/RISCVISAUtils.h
index 165bb08d66431..e2dd833de74fd 100644
--- a/llvm/include/llvm/Support/RISCVISAUtils.h
+++ b/llvm/include/llvm/Support/RISCVISAUtils.h
@@ -14,7 +14,9 @@
 #define LLVM_SUPPORT_RISCVISAUTILS_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/Compiler.h"
+#include <cassert>
 #include <map>
 #include <string>
 
@@ -43,6 +45,23 @@ struct ExtensionComparator {
 typedef std::map<std::string, ExtensionVersion, ExtensionComparator>
     OrderedExtensionMap;
 
+enum class ZicfilpLabelSchemeKind {
+  Invalid,
+  Unlabeled,
+  FuncSig,
+};
+
+// See clang::getCFBranchLabelSchemeFlagVal() for possible CFBranchLabelScheme
+inline ZicfilpLabelSchemeKind
+getZicfilpLabelScheme(const StringRef CFBranchLabelScheme) {
+  const auto Ret = StringSwitch<ZicfilpLabelSchemeKind>(CFBranchLabelScheme)
+                       .Case("unlabeled", ZicfilpLabelSchemeKind::Unlabeled)
+                       .Case("func-sig", ZicfilpLabelSchemeKind::FuncSig)
+                       .Default(ZicfilpLabelSchemeKind::Invalid);
+  assert(Ret != ZicfilpLabelSchemeKind::Invalid);
+  return Ret;
+}
+
 } // namespace RISCVISAUtils
 
 } // namespace llvm
diff --git a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
index 43621b8f0f33d..554f7d5494731 100644
--- a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
+++ b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
@@ -19,11 +19,14 @@
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/RISCVISAUtils.h"
 
 #define DEBUG_TYPE "riscv-indrect-branch-tracking"
 #define PASS_NAME "RISC-V Indirect Branch Tracking"
 
 using namespace llvm;
+using namespace llvm::RISCVISAUtils;
 
 cl::opt<uint32_t> PreferredLandingPadLabel(
     "riscv-landing-pad-label", cl::ReallyHidden,
@@ -64,9 +67,25 @@ static void emitLpad(MachineBasicBlock &MBB, const RISCVInstrInfo *TII,
 bool RISCVIndirectBranchTracking::runOnMachineFunction(MachineFunction &MF) {
   const auto &Subtarget = MF.getSubtarget<RISCVSubtarget>();
   const RISCVInstrInfo *TII = Subtarget.getInstrInfo();
-  if (!Subtarget.hasStdExtZicfilp())
+
+  const Module *const M = MF.getFunction().getParent();
+  if (!M)
+    return false;
+  if (const Metadata *const Flag = M->getModuleFlag("cf-protection-branch");
+      !Flag || mdconst::extract<ConstantInt>(Flag)->isZero())
     return false;
 
+  StringRef CFBranchLabelScheme;
+  if (const Metadata *const MD = M->getModuleFlag("cf-branch-label-scheme"))
+    CFBranchLabelScheme = cast<MDString>(MD)->getString();
+  else
+    report_fatal_error("missing cf-branch-label-scheme module flag");
+
+  const ZicfilpLabelSchemeKind Scheme =
+      getZicfilpLabelScheme(CFBranchLabelScheme);
+  if (Scheme != ZicfilpLabelSchemeKind::Unlabeled)
+    report_fatal_error("unsupported cf-branch-label-scheme module flag");
+
   uint32_t FixedLabel = 0;
   if (PreferredLandingPadLabel.getNumOccurrences() > 0) {
     if (!isUInt<20>(PreferredLandingPadLabel))
diff --git a/llvm/test/CodeGen/RISCV/lpad.ll b/llvm/test/CodeGen/RISCV/lpad.ll
index 93eda6f10eedb..f69b4dad068b5 100644
--- a/llvm/test/CodeGen/RISCV/lpad.ll
+++ b/llvm/test/CodeGen/RISCV/lpad.ll
@@ -289,3 +289,8 @@ define void @interrupt() "interrupt"="machine" {
 ; FIXED-ONE-NEXT:    mret
   ret void
 }
+
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 8, !"cf-protection-branch", i32 1}
+!1 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}

>From 1f761cd55196764f8e7c35afa445e19c18ba4d84 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Wed, 6 Aug 2025 17:43:20 +0800
Subject: [PATCH 02/16] fixup: Change SelectionDAG patterns to depend on
 Zicfilp CFI feature adoption instead of Zicfilp extension availability

---
 llvm/include/llvm/Support/RISCVISAUtils.h     | 19 ----------
 llvm/lib/Target/RISCV/RISCVFeatures.td        |  4 +-
 llvm/lib/Target/RISCV/RISCVInstrInfo.td       | 16 ++++----
 llvm/lib/Target/RISCV/RISCVSubtarget.cpp      |  2 +
 llvm/lib/Target/RISCV/RISCVSubtarget.h        | 20 +++++++++-
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp  | 38 +++++++++++++++++--
 .../Target/RISCV/RISCVInstrInfoTest.cpp       |  3 +-
 7 files changed, 68 insertions(+), 34 deletions(-)

diff --git a/llvm/include/llvm/Support/RISCVISAUtils.h b/llvm/include/llvm/Support/RISCVISAUtils.h
index e2dd833de74fd..165bb08d66431 100644
--- a/llvm/include/llvm/Support/RISCVISAUtils.h
+++ b/llvm/include/llvm/Support/RISCVISAUtils.h
@@ -14,9 +14,7 @@
 #define LLVM_SUPPORT_RISCVISAUTILS_H
 
 #include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/Compiler.h"
-#include <cassert>
 #include <map>
 #include <string>
 
@@ -45,23 +43,6 @@ struct ExtensionComparator {
 typedef std::map<std::string, ExtensionVersion, ExtensionComparator>
     OrderedExtensionMap;
 
-enum class ZicfilpLabelSchemeKind {
-  Invalid,
-  Unlabeled,
-  FuncSig,
-};
-
-// See clang::getCFBranchLabelSchemeFlagVal() for possible CFBranchLabelScheme
-inline ZicfilpLabelSchemeKind
-getZicfilpLabelScheme(const StringRef CFBranchLabelScheme) {
-  const auto Ret = StringSwitch<ZicfilpLabelSchemeKind>(CFBranchLabelScheme)
-                       .Case("unlabeled", ZicfilpLabelSchemeKind::Unlabeled)
-                       .Case("func-sig", ZicfilpLabelSchemeKind::FuncSig)
-                       .Default(ZicfilpLabelSchemeKind::Invalid);
-  assert(Ret != ZicfilpLabelSchemeKind::Invalid);
-  return Ret;
-}
-
 } // namespace RISCVISAUtils
 
 } // namespace llvm
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index 171940e149815..eb0749163cc0e 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -170,8 +170,8 @@ def FeatureStdExtZicfilp
 def HasStdExtZicfilp : Predicate<"Subtarget->hasStdExtZicfilp()">,
                        AssemblerPredicate<(all_of FeatureStdExtZicfilp),
                                           "'Zicfilp' (Landing pad)">;
-def NoStdExtZicfilp : Predicate<"!Subtarget->hasStdExtZicfilp()">,
-                      AssemblerPredicate<(all_of (not FeatureStdExtZicfilp))>;
+def HasZicfilpCFI : Predicate<"Subtarget->hasZicfilpCFI()">;
+def HasNoZicfilpCFI : Predicate<"!Subtarget->hasZicfilpCFI()">;
 
 def FeatureStdExtZicfiss
     : RISCVExperimentalExtension<1, 0, "Shadow stack",
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 8bd383033f11c..610edf126f0c5 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -1746,12 +1746,12 @@ let isBarrier = 1, isBranch = 1, isTerminator = 1 in
 def PseudoBR : Pseudo<(outs), (ins simm21_lsb0_jal:$imm20), [(br bb:$imm20)]>,
                PseudoInstExpansion<(JAL X0, simm21_lsb0_jal:$imm20)>;
 
-let Predicates = [NoStdExtZicfilp],
+let Predicates = [HasNoZicfilpCFI],
     isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in
 def PseudoBRIND : Pseudo<(outs), (ins GPRJALR:$rs1, simm12:$imm12), []>,
                   PseudoInstExpansion<(JALR X0, GPR:$rs1, simm12:$imm12)>;
 
-let Predicates = [HasStdExtZicfilp],
+let Predicates = [HasZicfilpCFI],
     isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in {
 def PseudoBRINDNonX7 : Pseudo<(outs), (ins GPRJALRNonX7:$rs1, simm12:$imm12), []>,
                        PseudoInstExpansion<(JALR X0, GPR:$rs1, simm12:$imm12)>;
@@ -1761,7 +1761,7 @@ def PseudoBRINDX7 : Pseudo<(outs), (ins GPRX7:$rs1, simm12:$imm12), []>,
 
 // For Zicfilp, need to avoid using X7/T2 for indirect branches which need
 // landing pad.
-let Predicates = [HasStdExtZicfilp] in {
+let Predicates = [HasZicfilpCFI] in {
 def : Pat<(brind GPRJALRNonX7:$rs1), (PseudoBRINDNonX7 GPRJALRNonX7:$rs1, 0)>;
 def : Pat<(brind (add GPRJALRNonX7:$rs1, simm12:$imm12)),
           (PseudoBRINDNonX7 GPRJALRNonX7:$rs1, simm12:$imm12)>;
@@ -1771,7 +1771,7 @@ def : Pat<(riscv_sw_guarded_brind (add GPRX7:$rs1, simm12:$imm12)),
           (PseudoBRINDX7 GPRX7:$rs1, simm12:$imm12)>;
 }
 
-let Predicates = [NoStdExtZicfilp] in {
+let Predicates = [HasNoZicfilpCFI] in {
 def : Pat<(brind GPRJALR:$rs1), (PseudoBRIND GPRJALR:$rs1, 0)>;
 def : Pat<(brind (add GPRJALR:$rs1, simm12:$imm12)),
           (PseudoBRIND GPRJALR:$rs1, simm12:$imm12)>;
@@ -1808,11 +1808,11 @@ let Predicates = [HasStdExtSmrnmi] in
 def : Pat<(riscv_mnret_glue), (MNRET)>;
 
 let isCall = 1, Defs = [X1] in {
-let Predicates = [NoStdExtZicfilp] in
+let Predicates = [HasNoZicfilpCFI] in
 def PseudoCALLIndirect : Pseudo<(outs), (ins GPRJALR:$rs1),
                                 [(riscv_call GPRJALR:$rs1)]>,
                          PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>;
-let Predicates = [HasStdExtZicfilp] in {
+let Predicates = [HasZicfilpCFI] in {
 def PseudoCALLIndirectNonX7 : Pseudo<(outs), (ins GPRJALRNonX7:$rs1),
                                      [(riscv_call GPRJALRNonX7:$rs1)]>,
                               PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>;
@@ -1837,11 +1837,11 @@ def PseudoTAIL : Pseudo<(outs), (ins call_symbol:$dst), [],
                  Sched<[WriteIALU, WriteJalr, ReadJalr]>;
 
 let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [X2] in {
-let Predicates = [NoStdExtZicfilp] in
+let Predicates = [HasNoZicfilpCFI] in
 def PseudoTAILIndirect : Pseudo<(outs), (ins GPRTC:$rs1),
                                 [(riscv_tail GPRTC:$rs1)]>,
                          PseudoInstExpansion<(JALR X0, GPR:$rs1, 0)>;
-let Predicates = [HasStdExtZicfilp] in {
+let Predicates = [HasZicfilpCFI] in {
 def PseudoTAILIndirectNonX7 : Pseudo<(outs), (ins GPRTCNonX7:$rs1),
                                      [(riscv_tail GPRTCNonX7:$rs1)]>,
                               PseudoInstExpansion<(JALR X0, GPR:$rs1, 0)>;
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
index e35ffaf2b3935..6098f3c10296f 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
@@ -99,9 +99,11 @@ RISCVSubtarget::RISCVSubtarget(const Triple &TT, StringRef CPU,
                                StringRef TuneCPU, StringRef FS,
                                StringRef ABIName, unsigned RVVVectorBitsMin,
                                unsigned RVVVectorBitsMax,
+                               RISCVSubtarget::ZicfilpLabelSchemeKind Zicfilp,
                                const TargetMachine &TM)
     : RISCVGenSubtargetInfo(TT, CPU, TuneCPU, FS),
       RVVVectorBitsMin(RVVVectorBitsMin), RVVVectorBitsMax(RVVVectorBitsMax),
+      ZicfilpLabelScheme(Zicfilp),
       FrameLowering(
           initializeSubtargetDependencies(TT, CPU, TuneCPU, FS, ABIName)),
       InstrInfo(*this), RegInfo(getHwMode()), TLInfo(TM, *this) {
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index fd57e02c25d05..424087b8ca83b 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -91,6 +91,14 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
     NLog2N,
   };
   // clang-format on
+
+  enum class ZicfilpLabelSchemeKind {
+    Disabled,
+    Unknown,
+    Unlabeled,
+    FuncSig,
+  };
+
 private:
   virtual void anchor();
 
@@ -107,6 +115,7 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
   unsigned RVVVectorBitsMax;
   uint8_t MaxInterleaveFactor = 2;
   RISCVABI::ABI TargetABI = RISCVABI::ABI_Unknown;
+  const ZicfilpLabelSchemeKind ZicfilpLabelScheme;
   std::bitset<RISCV::NUM_TARGET_REGS> UserReservedRegister;
   const RISCVTuneInfoTable::RISCVTuneInfo *TuneInfo;
 
@@ -127,7 +136,9 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
   // Initializes the data members to match that of the specified triple.
   RISCVSubtarget(const Triple &TT, StringRef CPU, StringRef TuneCPU,
                  StringRef FS, StringRef ABIName, unsigned RVVVectorBitsMin,
-                 unsigned RVVVectorLMULMax, const TargetMachine &TM);
+                 unsigned RVVVectorLMULMax,
+                 ZicfilpLabelSchemeKind ZicfilpLabelScheme,
+                 const TargetMachine &TM);
 
   ~RISCVSubtarget() override;
 
@@ -186,6 +197,13 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
     return HasStdExtZfhmin || HasStdExtZfbfmin;
   }
 
+  ZicfilpLabelSchemeKind getZicfilpLabelScheme() const {
+    return ZicfilpLabelScheme;
+  }
+  bool hasZicfilpCFI() const {
+    return getZicfilpLabelScheme() != ZicfilpLabelSchemeKind::Disabled;
+  }
+
   bool hasConditionalMoveFusion() const {
     // Do we support fusing a branch+mv or branch+c.mv as a conditional move.
     return (hasConditionalCompressedMoveFusion() && hasStdExtZca()) ||
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 3f2a83f8ce98c..fee8a2598be5b 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -17,6 +17,9 @@
 #include "RISCVTargetObjectFile.h"
 #include "RISCVTargetTransformInfo.h"
 #include "TargetInfo/RISCVTargetInfo.h"
+#include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/CodeGen/GlobalISel/CSEInfo.h"
 #include "llvm/CodeGen/GlobalISel/IRTranslator.h"
@@ -31,6 +34,9 @@
 #include "llvm/CodeGen/RegAllocRegistry.h"
 #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
 #include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
 #include "llvm/InitializePasses.h"
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Passes/PassBuilder.h"
@@ -40,6 +46,7 @@
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/Vectorize/EVLIndVarSimplify.h"
 #include "llvm/Transforms/Vectorize/LoopIdiomVectorize.h"
+#include <cassert>
 #include <optional>
 using namespace llvm;
 
@@ -246,9 +253,33 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
   RVVBitsMax =
       llvm::bit_floor((RVVBitsMax < 64 || RVVBitsMax > 65536) ? 0 : RVVBitsMax);
 
+  using ZicfilpLabelSchemeKind = RISCVSubtarget::ZicfilpLabelSchemeKind;
+  ZicfilpLabelSchemeKind ZicfilpLabelScheme = ZicfilpLabelSchemeKind::Disabled;
+  if (const Module *const M = F.getParent()) {
+    if (const Metadata *const Flag = M->getModuleFlag("cf-protection-branch");
+        Flag && !mdconst::extract<ConstantInt>(Flag)->isZero()) {
+      StringRef CFBranchLabelScheme;
+      if (const Metadata *const MD = M->getModuleFlag("cf-branch-label-scheme"))
+        CFBranchLabelScheme = cast<MDString>(MD)->getString();
+      else
+        report_fatal_error("missing cf-branch-label-scheme module flag");
+
+      // See clang::getCFBranchLabelSchemeFlagVal() for possible
+      // CFBranchLabelScheme
+      ZicfilpLabelScheme =
+          StringSwitch<ZicfilpLabelSchemeKind>(CFBranchLabelScheme)
+              .Case("unlabeled", ZicfilpLabelSchemeKind::Unlabeled)
+              .Case("func-sig", ZicfilpLabelSchemeKind::FuncSig)
+              .Default(ZicfilpLabelSchemeKind::Unknown);
+      assert(ZicfilpLabelScheme != ZicfilpLabelSchemeKind::Unknown);
+    }
+  }
+
   SmallString<512> Key;
   raw_svector_ostream(Key) << "RVVMin" << RVVBitsMin << "RVVMax" << RVVBitsMax
-                           << CPU << TuneCPU << FS;
+                           << "ZicfilpLabelSchemeKind"
+                           << to_underlying(ZicfilpLabelScheme) << CPU
+                           << TuneCPU << FS;
   auto &I = SubtargetMap[Key];
   if (!I) {
     // This needs to be done before we create a new subtarget since any
@@ -265,8 +296,9 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
       }
       ABIName = ModuleTargetABI->getString();
     }
-    I = std::make_unique<RISCVSubtarget>(
-        TargetTriple, CPU, TuneCPU, FS, ABIName, RVVBitsMin, RVVBitsMax, *this);
+    I = std::make_unique<RISCVSubtarget>(TargetTriple, CPU, TuneCPU, FS,
+                                         ABIName, RVVBitsMin, RVVBitsMax,
+                                         ZicfilpLabelScheme, *this);
   }
   return I.get();
 }
diff --git a/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp b/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp
index 2744dbc0907bf..3b6a7a143f351 100644
--- a/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp
+++ b/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp
@@ -61,7 +61,8 @@ class RISCVInstrInfoTest : public testing::TestWithParam<const char *> {
     ST = std::make_unique<RISCVSubtarget>(
         TM->getTargetTriple(), TM->getTargetCPU(), TM->getTargetCPU(),
         TM->getTargetFeatureString(),
-        TM->getTargetTriple().isArch64Bit() ? "lp64" : "ilp32", 0, 0, *TM);
+        TM->getTargetTriple().isArch64Bit() ? "lp64" : "ilp32", 0, 0,
+        RISCVSubtarget::ZicfilpLabelSchemeKind::Disabled, *TM);
 
     MF = std::make_unique<MachineFunction>(*F, *TM, *ST, MMI->getContext(), 42);
   }

>From 968e25057229f1c6f1f4c9b7a68945e4bfa8133e Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Wed, 6 Aug 2025 16:41:18 +0800
Subject: [PATCH 03/16] fixup: Codegen software-guarded long calls based on
 Zicfilp CFI feature adoption instead of Zicfilp extension availability

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   |   9 +-
 llvm/test/CodeGen/RISCV/calls.ll              | 186 --------
 llvm/test/CodeGen/RISCV/tail-calls.ll         | 270 -----------
 .../CodeGen/RISCV/zicfilp-unlabeled-calls.ll  | 253 ++++++++++
 .../RISCV/zicfilp-unlabeled-tail-calls.ll     | 434 ++++++++++++++++++
 5 files changed, 691 insertions(+), 461 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/zicfilp-unlabeled-calls.ll
 create mode 100644 llvm/test/CodeGen/RISCV/zicfilp-unlabeled-tail-calls.ll

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index e09e6fb5b26ee..86d51b8d7d172 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -22953,11 +22953,10 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
   // Use software guarded branch for large code model non-indirect calls
   // Tail call to external symbol will have a null CLI.CB and we need another
   // way to determine the callsite type
-  bool NeedSWGuarded = false;
-  if (getTargetMachine().getCodeModel() == CodeModel::Large &&
-      Subtarget.hasStdExtZicfilp() &&
-      ((CLI.CB && !CLI.CB->isIndirectCall()) || CalleeIsLargeExternalSymbol))
-    NeedSWGuarded = true;
+  const bool NeedSWGuarded =
+      getTargetMachine().getCodeModel() == CodeModel::Large &&
+      Subtarget.hasZicfilpCFI() &&
+      ((CLI.CB && !CLI.CB->isIndirectCall()) || CalleeIsLargeExternalSymbol);
 
   if (IsTailCall) {
     MF.getFrameInfo().setHasTailCall();
diff --git a/llvm/test/CodeGen/RISCV/calls.ll b/llvm/test/CodeGen/RISCV/calls.ll
index f30c453d7f6bc..1a3e77b8b8ea1 100644
--- a/llvm/test/CodeGen/RISCV/calls.ll
+++ b/llvm/test/CodeGen/RISCV/calls.ll
@@ -11,8 +11,6 @@
 ; RUN:   | FileCheck -check-prefix=RV64I-MEDIUM %s
 ; RUN: llc -code-model=large -mtriple=riscv64 -verify-machineinstrs < %s \
 ; RUN:   | FileCheck -check-prefix=RV64I-LARGE %s
-; RUN: llc -code-model=large -mtriple=riscv64 -mattr=experimental-zicfilp -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV64I-LARGE-ZICFILP %s
 
 declare i32 @external_function(i32)
 
@@ -64,19 +62,6 @@ define i32 @test_call_external(i32 %a) nounwind {
 ; RV64I-LARGE-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
 ; RV64I-LARGE-NEXT:    addi sp, sp, 16
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: test_call_external:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:  .Lpcrel_hi0:
-; RV64I-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI0_0)
-; RV64I-LARGE-ZICFILP-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi0)(a1)
-; RV64I-LARGE-ZICFILP-NEXT:    jalr t2
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = call i32 @external_function(i32 %a)
   ret i32 %1
 }
@@ -131,19 +116,6 @@ define i32 @test_call_dso_local(i32 %a) nounwind {
 ; RV64I-LARGE-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
 ; RV64I-LARGE-NEXT:    addi sp, sp, 16
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: test_call_dso_local:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:  .Lpcrel_hi1:
-; RV64I-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI1_0)
-; RV64I-LARGE-ZICFILP-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi1)(a1)
-; RV64I-LARGE-ZICFILP-NEXT:    jalr t2
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = call i32 @dso_local_function(i32 %a)
   ret i32 %1
 }
@@ -173,12 +145,6 @@ define i32 @defined_function(i32 %a) nounwind {
 ; RV64I-LARGE:       # %bb.0:
 ; RV64I-LARGE-NEXT:    addiw a0, a0, 1
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: defined_function:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addiw a0, a0, 1
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = add i32 %a, 1
   ret i32 %1
 }
@@ -231,19 +197,6 @@ define i32 @test_call_defined(i32 %a) nounwind {
 ; RV64I-LARGE-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
 ; RV64I-LARGE-NEXT:    addi sp, sp, 16
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: test_call_defined:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:  .Lpcrel_hi2:
-; RV64I-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI3_0)
-; RV64I-LARGE-ZICFILP-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi2)(a1)
-; RV64I-LARGE-ZICFILP-NEXT:    jalr t2
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = call i32 @defined_function(i32 %a)
   ret i32 %1
 }
@@ -303,18 +256,6 @@ define i32 @test_call_indirect(ptr %a, i32 %b) nounwind {
 ; RV64I-LARGE-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
 ; RV64I-LARGE-NEXT:    addi sp, sp, 16
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: test_call_indirect:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:    mv a2, a0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a0, a1
-; RV64I-LARGE-ZICFILP-NEXT:    jalr a2
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = call i32 %a(i32 %b)
   ret i32 %1
 }
@@ -406,24 +347,6 @@ define i32 @test_call_indirect_no_t0(ptr %a, i32 %b, i32 %c, i32 %d, i32 %e, i32
 ; RV64I-LARGE-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
 ; RV64I-LARGE-NEXT:    addi sp, sp, 16
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: test_call_indirect_no_t0:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:    mv t1, a0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a0, a1
-; RV64I-LARGE-ZICFILP-NEXT:    mv a1, a2
-; RV64I-LARGE-ZICFILP-NEXT:    mv a2, a3
-; RV64I-LARGE-ZICFILP-NEXT:    mv a3, a4
-; RV64I-LARGE-ZICFILP-NEXT:    mv a4, a5
-; RV64I-LARGE-ZICFILP-NEXT:    mv a5, a6
-; RV64I-LARGE-ZICFILP-NEXT:    mv a6, a7
-; RV64I-LARGE-ZICFILP-NEXT:    jalr t1
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = call i32 %a(i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h)
   ret i32 %1
 }
@@ -456,12 +379,6 @@ define fastcc i32 @fastcc_function(i32 %a, i32 %b) nounwind {
 ; RV64I-LARGE:       # %bb.0:
 ; RV64I-LARGE-NEXT:    addw a0, a0, a1
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: fastcc_function:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addw a0, a0, a1
-; RV64I-LARGE-ZICFILP-NEXT:    ret
  %1 = add i32 %a, %b
  ret i32 %1
 }
@@ -535,24 +452,6 @@ define i32 @test_call_fastcc(i32 %a, i32 %b) nounwind {
 ; RV64I-LARGE-NEXT:    ld s0, 0(sp) # 8-byte Folded Reload
 ; RV64I-LARGE-NEXT:    addi sp, sp, 16
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: test_call_fastcc:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:    sd s0, 0(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:    mv s0, a0
-; RV64I-LARGE-ZICFILP-NEXT:  .Lpcrel_hi3:
-; RV64I-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI7_0)
-; RV64I-LARGE-ZICFILP-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi3)(a0)
-; RV64I-LARGE-ZICFILP-NEXT:    mv a0, s0
-; RV64I-LARGE-ZICFILP-NEXT:    jalr t2
-; RV64I-LARGE-ZICFILP-NEXT:    mv a0, s0
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    ld s0, 0(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = call fastcc i32 @fastcc_function(i32 %a, i32 %b)
   ret i32 %a
 }
@@ -673,33 +572,6 @@ define i32 @test_call_external_many_args(i32 %a) nounwind {
 ; RV64I-LARGE-NEXT:    ld s0, 16(sp) # 8-byte Folded Reload
 ; RV64I-LARGE-NEXT:    addi sp, sp, 32
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: test_call_external_many_args:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -32
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:    sd s0, 16(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:    mv s0, a0
-; RV64I-LARGE-ZICFILP-NEXT:  .Lpcrel_hi4:
-; RV64I-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI8_0)
-; RV64I-LARGE-ZICFILP-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi4)(a0)
-; RV64I-LARGE-ZICFILP-NEXT:    sd s0, 0(sp)
-; RV64I-LARGE-ZICFILP-NEXT:    sd s0, 8(sp)
-; RV64I-LARGE-ZICFILP-NEXT:    mv a0, s0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a1, s0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a2, s0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a3, s0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a4, s0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a5, s0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a6, s0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a7, s0
-; RV64I-LARGE-ZICFILP-NEXT:    jalr t2
-; RV64I-LARGE-ZICFILP-NEXT:    mv a0, s0
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    ld s0, 16(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 32
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = call i32 @external_many_args(i32 %a, i32 %a, i32 %a, i32 %a, i32 %a,
                                     i32 %a, i32 %a, i32 %a, i32 %a, i32 %a)
   ret i32 %a
@@ -735,13 +607,6 @@ define i32 @defined_many_args(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 %
 ; RV64I-LARGE-NEXT:    lw a0, 8(sp)
 ; RV64I-LARGE-NEXT:    addiw a0, a0, 1
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: defined_many_args:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    lw a0, 8(sp)
-; RV64I-LARGE-ZICFILP-NEXT:    addiw a0, a0, 1
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %added = add i32 %j, 1
   ret i32 %added
 }
@@ -839,28 +704,6 @@ define i32 @test_call_defined_many_args(i32 %a) nounwind {
 ; RV64I-LARGE-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
 ; RV64I-LARGE-NEXT:    addi sp, sp, 32
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: test_call_defined_many_args:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -32
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:  .Lpcrel_hi5:
-; RV64I-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI10_0)
-; RV64I-LARGE-ZICFILP-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi5)(a1)
-; RV64I-LARGE-ZICFILP-NEXT:    sd a0, 0(sp)
-; RV64I-LARGE-ZICFILP-NEXT:    sd a0, 8(sp)
-; RV64I-LARGE-ZICFILP-NEXT:    mv a1, a0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a2, a0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a3, a0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a4, a0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a5, a0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a6, a0
-; RV64I-LARGE-ZICFILP-NEXT:    mv a7, a0
-; RV64I-LARGE-ZICFILP-NEXT:    jalr t2
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 32
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   %1 = call i32 @defined_many_args(i32 %a, i32 %a, i32 %a, i32 %a, i32 %a,
                                    i32 %a, i32 %a, i32 %a, i32 %a, i32 %a)
   ret i32 %1
@@ -994,35 +837,6 @@ define fastcc void @fastcc_call_nonfastcc(){
 ; RV64I-LARGE-NEXT:    addi sp, sp, 32
 ; RV64I-LARGE-NEXT:    .cfi_def_cfa_offset 0
 ; RV64I-LARGE-NEXT:    ret
-;
-; RV64I-LARGE-ZICFILP-LABEL: fastcc_call_nonfastcc:
-; RV64I-LARGE-ZICFILP:       # %bb.0:
-; RV64I-LARGE-ZICFILP-NEXT:    lpad 0
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, -32
-; RV64I-LARGE-ZICFILP-NEXT:    .cfi_def_cfa_offset 32
-; RV64I-LARGE-ZICFILP-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
-; RV64I-LARGE-ZICFILP-NEXT:    .cfi_offset ra, -8
-; RV64I-LARGE-ZICFILP-NEXT:    li t0, 10
-; RV64I-LARGE-ZICFILP-NEXT:    li t1, 9
-; RV64I-LARGE-ZICFILP-NEXT:  .Lpcrel_hi6:
-; RV64I-LARGE-ZICFILP-NEXT:    auipc a5, %pcrel_hi(.LCPI11_0)
-; RV64I-LARGE-ZICFILP-NEXT:    li a0, 1
-; RV64I-LARGE-ZICFILP-NEXT:    li a1, 2
-; RV64I-LARGE-ZICFILP-NEXT:    li a2, 3
-; RV64I-LARGE-ZICFILP-NEXT:    li a3, 4
-; RV64I-LARGE-ZICFILP-NEXT:    li a4, 5
-; RV64I-LARGE-ZICFILP-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi6)(a5)
-; RV64I-LARGE-ZICFILP-NEXT:    li a5, 6
-; RV64I-LARGE-ZICFILP-NEXT:    li a6, 7
-; RV64I-LARGE-ZICFILP-NEXT:    li a7, 8
-; RV64I-LARGE-ZICFILP-NEXT:    sd t1, 0(sp)
-; RV64I-LARGE-ZICFILP-NEXT:    sd t0, 8(sp)
-; RV64I-LARGE-ZICFILP-NEXT:    jalr t2
-; RV64I-LARGE-ZICFILP-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
-; RV64I-LARGE-ZICFILP-NEXT:    .cfi_restore ra
-; RV64I-LARGE-ZICFILP-NEXT:    addi sp, sp, 32
-; RV64I-LARGE-ZICFILP-NEXT:    .cfi_def_cfa_offset 0
-; RV64I-LARGE-ZICFILP-NEXT:    ret
   call void @external_many_args(i32 1, i32 2,i32 3,i32 4,i32 5,i32 6,i32 7,i32 8,i32 9,i32 10)
   ret void
 }
diff --git a/llvm/test/CodeGen/RISCV/tail-calls.ll b/llvm/test/CodeGen/RISCV/tail-calls.ll
index 366b37ac5d472..4d164c84e9d3f 100644
--- a/llvm/test/CodeGen/RISCV/tail-calls.ll
+++ b/llvm/test/CodeGen/RISCV/tail-calls.ll
@@ -1,8 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc -mtriple riscv32-unknown-linux-gnu -o - %s | FileCheck %s
-; RUN: llc -mtriple riscv32-unknown-linux-gnu -mattr=experimental-zicfilp \
-; RUN:   -code-model=large -o - %s \
-; RUN:   | FileCheck %s -check-prefix=CHECK-LARGE-ZICFILP
 ; RUN: llc -mtriple riscv32-unknown-elf       -o - %s | FileCheck %s
 
 ; Perform tail call optimization for global address.
@@ -11,14 +8,6 @@ define i32 @caller_tail(i32 %i) nounwind {
 ; CHECK-LABEL: caller_tail:
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    tail callee_tail
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_tail:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi0:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI0_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi0)(a1)
-; CHECK-LARGE-ZICFILP-NEXT:    jr t2
 entry:
   %r = tail call i32 @callee_tail(i32 %i)
   ret i32 %r
@@ -37,21 +26,6 @@ define void @caller_extern(ptr %src) optsize {
 ; CHECK-NEXT:    mv a0, a1
 ; CHECK-NEXT:    mv a1, a3
 ; CHECK-NEXT:    tail memcpy
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_extern:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi1:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI1_0)
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi2:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a2, %pcrel_hi(.LCPI1_1)
-; CHECK-LARGE-ZICFILP-NEXT:    lw a1, %pcrel_lo(.Lpcrel_hi1)(a1)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi2)(a2)
-; CHECK-LARGE-ZICFILP-NEXT:    li a2, 7
-; CHECK-LARGE-ZICFILP-NEXT:    mv a3, a0
-; CHECK-LARGE-ZICFILP-NEXT:    mv a0, a1
-; CHECK-LARGE-ZICFILP-NEXT:    mv a1, a3
-; CHECK-LARGE-ZICFILP-NEXT:    jr t2
 entry:
   tail call void @llvm.memcpy.p0.p0.i32(ptr @dest, ptr %src, i32 7, i1 false)
   ret void
@@ -69,21 +43,6 @@ define void @caller_extern_pgso(ptr %src) !prof !14 {
 ; CHECK-NEXT:    mv a0, a1
 ; CHECK-NEXT:    mv a1, a3
 ; CHECK-NEXT:    tail memcpy
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_extern_pgso:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi3:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI2_0)
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi4:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a2, %pcrel_hi(.LCPI2_1)
-; CHECK-LARGE-ZICFILP-NEXT:    lw a1, %pcrel_lo(.Lpcrel_hi3)(a1)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi4)(a2)
-; CHECK-LARGE-ZICFILP-NEXT:    li a2, 7
-; CHECK-LARGE-ZICFILP-NEXT:    mv a3, a0
-; CHECK-LARGE-ZICFILP-NEXT:    mv a0, a1
-; CHECK-LARGE-ZICFILP-NEXT:    mv a1, a3
-; CHECK-LARGE-ZICFILP-NEXT:    jr t2
 entry:
   tail call void @llvm.memcpy.p0.p0.i32(ptr @dest_pgso, ptr %src, i32 7, i1 false)
   ret void
@@ -104,21 +63,6 @@ define void @caller_indirect_tail(i32 %a) nounwind {
 ; CHECK-NEXT:    lui t1, %hi(callee_indirect1)
 ; CHECK-NEXT:    addi t1, t1, %lo(callee_indirect1)
 ; CHECK-NEXT:    jr t1
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_indirect_tail:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    beqz a0, .LBB3_2
-; CHECK-LARGE-ZICFILP-NEXT:  # %bb.1: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi6:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI3_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t1, %pcrel_lo(.Lpcrel_hi6)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jr t1
-; CHECK-LARGE-ZICFILP-NEXT:  .LBB3_2:
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi5:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI3_1)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t1, %pcrel_lo(.Lpcrel_hi5)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jr t1
 entry:
   %tobool = icmp eq i32 %a, 0
   %callee = select i1 %tobool, ptr @callee_indirect1, ptr @callee_indirect2
@@ -140,19 +84,6 @@ define i32 @caller_indirect_no_t0(ptr %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5
 ; CHECK-NEXT:    mv a5, a6
 ; CHECK-NEXT:    mv a6, a7
 ; CHECK-NEXT:    jr t1
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_indirect_no_t0:
-; CHECK-LARGE-ZICFILP:       # %bb.0:
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    mv t1, a0
-; CHECK-LARGE-ZICFILP-NEXT:    mv a0, a1
-; CHECK-LARGE-ZICFILP-NEXT:    mv a1, a2
-; CHECK-LARGE-ZICFILP-NEXT:    mv a2, a3
-; CHECK-LARGE-ZICFILP-NEXT:    mv a3, a4
-; CHECK-LARGE-ZICFILP-NEXT:    mv a4, a5
-; CHECK-LARGE-ZICFILP-NEXT:    mv a5, a6
-; CHECK-LARGE-ZICFILP-NEXT:    mv a6, a7
-; CHECK-LARGE-ZICFILP-NEXT:    jr t1
   %9 = tail call i32 %0(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7)
   ret i32 %9
 }
@@ -175,26 +106,6 @@ define void @caller_varargs(i32 %a, i32 %b) nounwind {
 ; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 16
 ; CHECK-NEXT:    ret
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_varargs:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; CHECK-LARGE-ZICFILP-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi7:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a2, %pcrel_hi(.LCPI5_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi7)(a2)
-; CHECK-LARGE-ZICFILP-NEXT:    sw a0, 0(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    mv a2, a1
-; CHECK-LARGE-ZICFILP-NEXT:    mv a3, a0
-; CHECK-LARGE-ZICFILP-NEXT:    mv a4, a0
-; CHECK-LARGE-ZICFILP-NEXT:    mv a5, a1
-; CHECK-LARGE-ZICFILP-NEXT:    mv a6, a1
-; CHECK-LARGE-ZICFILP-NEXT:    mv a7, a0
-; CHECK-LARGE-ZICFILP-NEXT:    jalr t2
-; CHECK-LARGE-ZICFILP-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; CHECK-LARGE-ZICFILP-NEXT:    ret
 entry:
   %call = tail call i32 (i32, ...) @callee_varargs(i32 %a, i32 %b, i32 %b, i32 %a, i32 %a, i32 %b, i32 %b, i32 %a, i32 %a)
   ret void
@@ -223,31 +134,6 @@ define i32 @caller_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g
 ; CHECK-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 32
 ; CHECK-NEXT:    ret
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_args:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, -32
-; CHECK-LARGE-ZICFILP-NEXT:    sw ra, 28(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    lw t0, 32(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t1, 36(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t3, 40(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t4, 44(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, 48(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t5, 52(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw t2, 16(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw t5, 20(sp)
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi8:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc t2, %pcrel_hi(.LCPI6_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi8)(t2)
-; CHECK-LARGE-ZICFILP-NEXT:    sw t0, 0(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw t1, 4(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw t3, 8(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw t4, 12(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    jalr t2
-; CHECK-LARGE-ZICFILP-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, 32
-; CHECK-LARGE-ZICFILP-NEXT:    ret
 entry:
   %r = tail call i32 @callee_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n)
   ret i32 %r
@@ -270,25 +156,6 @@ define void @caller_indirect_args() nounwind {
 ; CHECK-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 32
 ; CHECK-NEXT:    ret
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_indirect_args:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, -32
-; CHECK-LARGE-ZICFILP-NEXT:    sw ra, 28(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    lui a1, 262128
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi9:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI7_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi9)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    mv a0, sp
-; CHECK-LARGE-ZICFILP-NEXT:    sw zero, 0(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw zero, 4(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw zero, 8(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw a1, 12(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    jalr t2
-; CHECK-LARGE-ZICFILP-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, 32
-; CHECK-LARGE-ZICFILP-NEXT:    ret
 entry:
   %call = tail call i32 @callee_indirect_args(fp128 0xL00000000000000003FFF000000000000)
   ret void
@@ -300,14 +167,6 @@ define void @caller_weak() nounwind {
 ; CHECK-LABEL: caller_weak:
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    tail callee_weak
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_weak:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi10:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI8_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi10)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jr t2
 entry:
   tail call void @callee_weak()
   ret void
@@ -356,48 +215,6 @@ define void @caller_irq() nounwind "interrupt"="machine" {
 ; CHECK-NEXT:    lw t6, 0(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 64
 ; CHECK-NEXT:    mret
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_irq:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, -64
-; CHECK-LARGE-ZICFILP-NEXT:    sw ra, 60(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw t0, 56(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw t1, 52(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw t2, 48(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw a0, 44(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw a1, 40(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw a2, 36(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw a3, 32(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw a4, 28(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw a5, 24(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw a6, 20(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw a7, 16(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw t3, 12(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw t4, 8(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw t5, 4(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    sw t6, 0(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi11:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI9_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi11)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jalr t2
-; CHECK-LARGE-ZICFILP-NEXT:    lw ra, 60(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw t0, 56(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw t1, 52(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, 48(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw a0, 44(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw a1, 40(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw a2, 36(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw a3, 32(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw a4, 28(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw a5, 24(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw a6, 20(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw a7, 16(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw t3, 12(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw t4, 8(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw t5, 4(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    lw t6, 0(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, 64
-; CHECK-LARGE-ZICFILP-NEXT:    mret
 entry:
   tail call void @callee_irq()
   ret void
@@ -419,22 +236,6 @@ define i32 @caller_byval() nounwind {
 ; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 16
 ; CHECK-NEXT:    ret
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_byval:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; CHECK-LARGE-ZICFILP-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:    lw a0, 8(sp)
-; CHECK-LARGE-ZICFILP-NEXT:    sw a0, 4(sp)
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi12:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI10_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi12)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    addi a0, sp, 4
-; CHECK-LARGE-ZICFILP-NEXT:    jalr t2
-; CHECK-LARGE-ZICFILP-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; CHECK-LARGE-ZICFILP-NEXT:    ret
 entry:
   %a = alloca ptr
   %r = tail call i32 @callee_byval(ptr byval(ptr) %a)
@@ -457,22 +258,6 @@ define void @caller_nostruct() nounwind {
 ; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 16
 ; CHECK-NEXT:    ret
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_nostruct:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; CHECK-LARGE-ZICFILP-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi13:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI11_0)
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi14:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI11_1)
-; CHECK-LARGE-ZICFILP-NEXT:    lw a0, %pcrel_lo(.Lpcrel_hi13)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi14)(a1)
-; CHECK-LARGE-ZICFILP-NEXT:    jalr t2
-; CHECK-LARGE-ZICFILP-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; CHECK-LARGE-ZICFILP-NEXT:    ret
 entry:
   tail call void @callee_struct(ptr sret(%struct.A) @a)
   ret void
@@ -489,19 +274,6 @@ define void @caller_struct(ptr sret(%struct.A) %a) nounwind {
 ; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 16
 ; CHECK-NEXT:    ret
-;
-; CHECK-LARGE-ZICFILP-LABEL: caller_struct:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; CHECK-LARGE-ZICFILP-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi15:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI12_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi15)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jalr t2
-; CHECK-LARGE-ZICFILP-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; CHECK-LARGE-ZICFILP-NEXT:    ret
 entry:
   tail call void @callee_nostruct()
   ret void
@@ -517,19 +289,6 @@ define i32 @disable_tail_calls(i32 %i) nounwind "disable-tail-calls"="true" {
 ; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 16
 ; CHECK-NEXT:    ret
-;
-; CHECK-LARGE-ZICFILP-LABEL: disable_tail_calls:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, -16
-; CHECK-LARGE-ZICFILP-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi16:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a1, %pcrel_hi(.LCPI13_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi16)(a1)
-; CHECK-LARGE-ZICFILP-NEXT:    jalr t2
-; CHECK-LARGE-ZICFILP-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
-; CHECK-LARGE-ZICFILP-NEXT:    addi sp, sp, 16
-; CHECK-LARGE-ZICFILP-NEXT:    ret
 entry:
   %rv = tail call i32 @callee_tail(i32 %i)
   ret i32 %rv
@@ -556,35 +315,6 @@ define i32 @duplicate_returns(i32 %a, i32 %b) nounwind {
 ; CHECK-NEXT:    tail test1
 ; CHECK-NEXT:  .LBB14_6: # %if.else8
 ; CHECK-NEXT:    tail test3
-;
-; CHECK-LARGE-ZICFILP-LABEL: duplicate_returns:
-; CHECK-LARGE-ZICFILP:       # %bb.0: # %entry
-; CHECK-LARGE-ZICFILP-NEXT:    lpad 0
-; CHECK-LARGE-ZICFILP-NEXT:    beqz a0, .LBB14_4
-; CHECK-LARGE-ZICFILP-NEXT:  # %bb.1: # %if.else
-; CHECK-LARGE-ZICFILP-NEXT:    beqz a1, .LBB14_5
-; CHECK-LARGE-ZICFILP-NEXT:  # %bb.2: # %if.else4
-; CHECK-LARGE-ZICFILP-NEXT:    bge a1, a0, .LBB14_6
-; CHECK-LARGE-ZICFILP-NEXT:  # %bb.3: # %if.then6
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi19:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI14_1)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi19)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jr t2
-; CHECK-LARGE-ZICFILP-NEXT:  .LBB14_4: # %if.then
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi17:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI14_3)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi17)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jr t2
-; CHECK-LARGE-ZICFILP-NEXT:  .LBB14_5: # %if.then2
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi18:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI14_2)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi18)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jr t2
-; CHECK-LARGE-ZICFILP-NEXT:  .LBB14_6: # %if.else8
-; CHECK-LARGE-ZICFILP-NEXT:  .Lpcrel_hi20:
-; CHECK-LARGE-ZICFILP-NEXT:    auipc a0, %pcrel_hi(.LCPI14_0)
-; CHECK-LARGE-ZICFILP-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi20)(a0)
-; CHECK-LARGE-ZICFILP-NEXT:    jr t2
 entry:
   %cmp = icmp eq i32 %a, 0
   br i1 %cmp, label %if.then, label %if.else
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-calls.ll b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-calls.ll
new file mode 100644
index 0000000000000..0cd8dff8cd02b
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-calls.ll
@@ -0,0 +1,253 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -code-model=large -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s
+
+declare i32 @external_function(i32)
+
+define i32 @test_call_external(i32 %a) nounwind {
+; CHECK-LABEL: test_call_external:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi0:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI0_0)
+; CHECK-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi0)(a1)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+  %1 = call i32 @external_function(i32 %a)
+  ret i32 %1
+}
+
+declare dso_local i32 @dso_local_function(i32)
+
+define i32 @test_call_dso_local(i32 %a) nounwind {
+; CHECK-LABEL: test_call_dso_local:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi1:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI1_0)
+; CHECK-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi1)(a1)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+  %1 = call i32 @dso_local_function(i32 %a)
+  ret i32 %1
+}
+
+define i32 @defined_function(i32 %a) nounwind {
+; CHECK-LABEL: defined_function:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addiw a0, a0, 1
+; CHECK-NEXT:    ret
+  %1 = add i32 %a, 1
+  ret i32 %1
+}
+
+define i32 @test_call_defined(i32 %a) nounwind {
+; CHECK-LABEL: test_call_defined:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi2:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI3_0)
+; CHECK-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi2)(a1)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+  %1 = call i32 @defined_function(i32 %a)
+  ret i32 %1
+}
+
+define i32 @test_call_indirect(ptr %a, i32 %b) nounwind {
+; CHECK-LABEL: test_call_indirect:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    mv a2, a0
+; CHECK-NEXT:    mv a0, a1
+; CHECK-NEXT:    jalr a2
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+  %1 = call i32 %a(i32 %b)
+  ret i32 %1
+}
+
+; Make sure we don't use t0 as the source for jalr as that is a hint to pop the
+; return address stack on some microarchitectures.
+define i32 @test_call_indirect_no_t0(ptr %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) nounwind {
+; CHECK-LABEL: test_call_indirect_no_t0:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    mv t1, a0
+; CHECK-NEXT:    mv a0, a1
+; CHECK-NEXT:    mv a1, a2
+; CHECK-NEXT:    mv a2, a3
+; CHECK-NEXT:    mv a3, a4
+; CHECK-NEXT:    mv a4, a5
+; CHECK-NEXT:    mv a5, a6
+; CHECK-NEXT:    mv a6, a7
+; CHECK-NEXT:    jalr t1
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+  %1 = call i32 %a(i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h)
+  ret i32 %1
+}
+
+; Ensure that calls to fastcc functions aren't rejected. Such calls may be
+; introduced when compiling with optimisation.
+
+define fastcc i32 @fastcc_function(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: fastcc_function:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addw a0, a0, a1
+; CHECK-NEXT:    ret
+ %1 = add i32 %a, %b
+ ret i32 %1
+}
+
+define i32 @test_call_fastcc(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: test_call_fastcc:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    sd s0, 0(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    mv s0, a0
+; CHECK-NEXT:  .Lpcrel_hi3:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI7_0)
+; CHECK-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi3)(a0)
+; CHECK-NEXT:    mv a0, s0
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    mv a0, s0
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    ld s0, 0(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+  %1 = call fastcc i32 @fastcc_function(i32 %a, i32 %b)
+  ret i32 %a
+}
+
+declare i32 @external_many_args(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) nounwind
+
+define i32 @test_call_external_many_args(i32 %a) nounwind {
+; CHECK-LABEL: test_call_external_many_args:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -32
+; CHECK-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    sd s0, 16(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    mv s0, a0
+; CHECK-NEXT:  .Lpcrel_hi4:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI8_0)
+; CHECK-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi4)(a0)
+; CHECK-NEXT:    sd s0, 0(sp)
+; CHECK-NEXT:    sd s0, 8(sp)
+; CHECK-NEXT:    mv a0, s0
+; CHECK-NEXT:    mv a1, s0
+; CHECK-NEXT:    mv a2, s0
+; CHECK-NEXT:    mv a3, s0
+; CHECK-NEXT:    mv a4, s0
+; CHECK-NEXT:    mv a5, s0
+; CHECK-NEXT:    mv a6, s0
+; CHECK-NEXT:    mv a7, s0
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    mv a0, s0
+; CHECK-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    ld s0, 16(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 32
+; CHECK-NEXT:    ret
+  %1 = call i32 @external_many_args(i32 %a, i32 %a, i32 %a, i32 %a, i32 %a,
+                                    i32 %a, i32 %a, i32 %a, i32 %a, i32 %a)
+  ret i32 %a
+}
+
+define i32 @defined_many_args(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 %j) nounwind {
+; CHECK-LABEL: defined_many_args:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    lw a0, 8(sp)
+; CHECK-NEXT:    addiw a0, a0, 1
+; CHECK-NEXT:    ret
+  %added = add i32 %j, 1
+  ret i32 %added
+}
+
+define i32 @test_call_defined_many_args(i32 %a) nounwind {
+; CHECK-LABEL: test_call_defined_many_args:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -32
+; CHECK-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi5:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI10_0)
+; CHECK-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi5)(a1)
+; CHECK-NEXT:    sd a0, 0(sp)
+; CHECK-NEXT:    sd a0, 8(sp)
+; CHECK-NEXT:    mv a1, a0
+; CHECK-NEXT:    mv a2, a0
+; CHECK-NEXT:    mv a3, a0
+; CHECK-NEXT:    mv a4, a0
+; CHECK-NEXT:    mv a5, a0
+; CHECK-NEXT:    mv a6, a0
+; CHECK-NEXT:    mv a7, a0
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 32
+; CHECK-NEXT:    ret
+  %1 = call i32 @defined_many_args(i32 %a, i32 %a, i32 %a, i32 %a, i32 %a,
+                                   i32 %a, i32 %a, i32 %a, i32 %a, i32 %a)
+  ret i32 %1
+}
+
+define fastcc void @fastcc_call_nonfastcc(){
+; CHECK-LABEL: fastcc_call_nonfastcc:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -32
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_offset ra, -8
+; CHECK-NEXT:    li t0, 10
+; CHECK-NEXT:    li t1, 9
+; CHECK-NEXT:  .Lpcrel_hi6:
+; CHECK-NEXT:    auipc a5, %pcrel_hi(.LCPI11_0)
+; CHECK-NEXT:    li a0, 1
+; CHECK-NEXT:    li a1, 2
+; CHECK-NEXT:    li a2, 3
+; CHECK-NEXT:    li a3, 4
+; CHECK-NEXT:    li a4, 5
+; CHECK-NEXT:    ld t2, %pcrel_lo(.Lpcrel_hi6)(a5)
+; CHECK-NEXT:    li a5, 6
+; CHECK-NEXT:    li a6, 7
+; CHECK-NEXT:    li a7, 8
+; CHECK-NEXT:    sd t1, 0(sp)
+; CHECK-NEXT:    sd t0, 8(sp)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    .cfi_restore ra
+; CHECK-NEXT:    addi sp, sp, 32
+; CHECK-NEXT:    .cfi_def_cfa_offset 0
+; CHECK-NEXT:    ret
+  call void @external_many_args(i32 1, i32 2,i32 3,i32 4,i32 5,i32 6,i32 7,i32 8,i32 9,i32 10)
+  ret void
+}
+
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 8, !"cf-protection-branch", i32 1}
+!1 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-tail-calls.ll b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-tail-calls.ll
new file mode 100644
index 0000000000000..8cfc83bb11a4c
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-tail-calls.ll
@@ -0,0 +1,434 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple riscv32-unknown-linux-gnu -code-model=large -o - %s | FileCheck %s
+
+; Perform tail call optimization for global address.
+declare i32 @callee_tail(i32 %i)
+define i32 @caller_tail(i32 %i) nounwind {
+; CHECK-LABEL: caller_tail:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:  .Lpcrel_hi0:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI0_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi0)(a1)
+; CHECK-NEXT:    jr t2
+entry:
+  %r = tail call i32 @callee_tail(i32 %i)
+  ret i32 %r
+}
+
+; Perform tail call optimization for external symbol.
+ at dest = global [2 x i8] zeroinitializer
+declare void @llvm.memcpy.p0.p0.i32(ptr, ptr, i32, i1)
+define void @caller_extern(ptr %src) optsize {
+; CHECK-LABEL: caller_extern:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:  .Lpcrel_hi1:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI1_0)
+; CHECK-NEXT:  .Lpcrel_hi2:
+; CHECK-NEXT:    auipc a2, %pcrel_hi(.LCPI1_1)
+; CHECK-NEXT:    lw a1, %pcrel_lo(.Lpcrel_hi1)(a1)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi2)(a2)
+; CHECK-NEXT:    li a2, 7
+; CHECK-NEXT:    mv a3, a0
+; CHECK-NEXT:    mv a0, a1
+; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    jr t2
+entry:
+  tail call void @llvm.memcpy.p0.p0.i32(ptr @dest, ptr %src, i32 7, i1 false)
+  ret void
+}
+
+; Perform tail call optimization for external symbol.
+ at dest_pgso = global [2 x i8] zeroinitializer
+define void @caller_extern_pgso(ptr %src) !prof !14 {
+; CHECK-LABEL: caller_extern_pgso:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:  .Lpcrel_hi3:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI2_0)
+; CHECK-NEXT:  .Lpcrel_hi4:
+; CHECK-NEXT:    auipc a2, %pcrel_hi(.LCPI2_1)
+; CHECK-NEXT:    lw a1, %pcrel_lo(.Lpcrel_hi3)(a1)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi4)(a2)
+; CHECK-NEXT:    li a2, 7
+; CHECK-NEXT:    mv a3, a0
+; CHECK-NEXT:    mv a0, a1
+; CHECK-NEXT:    mv a1, a3
+; CHECK-NEXT:    jr t2
+entry:
+  tail call void @llvm.memcpy.p0.p0.i32(ptr @dest_pgso, ptr %src, i32 7, i1 false)
+  ret void
+}
+
+; Perform indirect tail call optimization (for function pointer call).
+declare void @callee_indirect1()
+declare void @callee_indirect2()
+define void @caller_indirect_tail(i32 %a) nounwind {
+; CHECK-LABEL: caller_indirect_tail:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    beqz a0, .LBB3_2
+; CHECK-NEXT:  # %bb.1: # %entry
+; CHECK-NEXT:  .Lpcrel_hi6:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI3_0)
+; CHECK-NEXT:    lw t1, %pcrel_lo(.Lpcrel_hi6)(a0)
+; CHECK-NEXT:    jr t1
+; CHECK-NEXT:  .LBB3_2:
+; CHECK-NEXT:  .Lpcrel_hi5:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI3_1)
+; CHECK-NEXT:    lw t1, %pcrel_lo(.Lpcrel_hi5)(a0)
+; CHECK-NEXT:    jr t1
+entry:
+  %tobool = icmp eq i32 %a, 0
+  %callee = select i1 %tobool, ptr @callee_indirect1, ptr @callee_indirect2
+  tail call void %callee()
+  ret void
+}
+
+; Make sure we don't use t0 as the source for jr as that is a hint to pop the
+; return address stack on some microarchitectures.
+define i32 @caller_indirect_no_t0(ptr %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7) {
+; CHECK-LABEL: caller_indirect_no_t0:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    mv t1, a0
+; CHECK-NEXT:    mv a0, a1
+; CHECK-NEXT:    mv a1, a2
+; CHECK-NEXT:    mv a2, a3
+; CHECK-NEXT:    mv a3, a4
+; CHECK-NEXT:    mv a4, a5
+; CHECK-NEXT:    mv a5, a6
+; CHECK-NEXT:    mv a6, a7
+; CHECK-NEXT:    jr t1
+  %9 = tail call i32 %0(i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6, i32 %7)
+  ret i32 %9
+}
+
+; Do not tail call optimize functions with varargs passed by stack.
+declare i32 @callee_varargs(i32, ...)
+define void @caller_varargs(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: caller_varargs:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi7:
+; CHECK-NEXT:    auipc a2, %pcrel_hi(.LCPI5_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi7)(a2)
+; CHECK-NEXT:    sw a0, 0(sp)
+; CHECK-NEXT:    mv a2, a1
+; CHECK-NEXT:    mv a3, a0
+; CHECK-NEXT:    mv a4, a0
+; CHECK-NEXT:    mv a5, a1
+; CHECK-NEXT:    mv a6, a1
+; CHECK-NEXT:    mv a7, a0
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+entry:
+  %call = tail call i32 (i32, ...) @callee_varargs(i32 %a, i32 %b, i32 %b, i32 %a, i32 %a, i32 %b, i32 %b, i32 %a, i32 %a)
+  ret void
+}
+
+; Do not tail call optimize if stack is used to pass parameters.
+declare i32 @callee_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n)
+define i32 @caller_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n) nounwind {
+; CHECK-LABEL: caller_args:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -32
+; CHECK-NEXT:    sw ra, 28(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    lw t0, 32(sp)
+; CHECK-NEXT:    lw t1, 36(sp)
+; CHECK-NEXT:    lw t3, 40(sp)
+; CHECK-NEXT:    lw t4, 44(sp)
+; CHECK-NEXT:    lw t2, 48(sp)
+; CHECK-NEXT:    lw t5, 52(sp)
+; CHECK-NEXT:    sw t2, 16(sp)
+; CHECK-NEXT:    sw t5, 20(sp)
+; CHECK-NEXT:  .Lpcrel_hi8:
+; CHECK-NEXT:    auipc t2, %pcrel_hi(.LCPI6_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi8)(t2)
+; CHECK-NEXT:    sw t0, 0(sp)
+; CHECK-NEXT:    sw t1, 4(sp)
+; CHECK-NEXT:    sw t3, 8(sp)
+; CHECK-NEXT:    sw t4, 12(sp)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 32
+; CHECK-NEXT:    ret
+entry:
+  %r = tail call i32 @callee_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n)
+  ret i32 %r
+}
+
+; Do not tail call optimize if parameters need to be passed indirectly.
+declare i32 @callee_indirect_args(fp128 %a)
+define void @caller_indirect_args() nounwind {
+; CHECK-LABEL: caller_indirect_args:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -32
+; CHECK-NEXT:    sw ra, 28(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    lui a1, 262128
+; CHECK-NEXT:  .Lpcrel_hi9:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI7_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi9)(a0)
+; CHECK-NEXT:    mv a0, sp
+; CHECK-NEXT:    sw zero, 0(sp)
+; CHECK-NEXT:    sw zero, 4(sp)
+; CHECK-NEXT:    sw zero, 8(sp)
+; CHECK-NEXT:    sw a1, 12(sp)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 32
+; CHECK-NEXT:    ret
+entry:
+  %call = tail call i32 @callee_indirect_args(fp128 0xL00000000000000003FFF000000000000)
+  ret void
+}
+
+; Perform tail call optimization for external weak symbol.
+declare extern_weak void @callee_weak()
+define void @caller_weak() nounwind {
+; CHECK-LABEL: caller_weak:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:  .Lpcrel_hi10:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI8_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi10)(a0)
+; CHECK-NEXT:    jr t2
+entry:
+  tail call void @callee_weak()
+  ret void
+}
+
+; Exception-handling functions need a special set of instructions to indicate a
+; return to the hardware. Tail-calling another function would probably break
+; this.
+declare void @callee_irq()
+define void @caller_irq() nounwind "interrupt"="machine" {
+; CHECK-LABEL: caller_irq:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi sp, sp, -64
+; CHECK-NEXT:    sw ra, 60(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw t0, 56(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw t1, 52(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw t2, 48(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw a0, 44(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw a1, 40(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw a2, 36(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw a3, 32(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw a4, 28(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw a5, 24(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw a6, 20(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw a7, 16(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw t3, 12(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw t4, 8(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw t5, 4(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    sw t6, 0(sp) # 4-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi11:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI9_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi11)(a0)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    lw ra, 60(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw t0, 56(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw t1, 52(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw t2, 48(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw a0, 44(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw a1, 40(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw a2, 36(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw a3, 32(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw a4, 28(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw a5, 24(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw a6, 20(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw a7, 16(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw t3, 12(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw t4, 8(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw t5, 4(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    lw t6, 0(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 64
+; CHECK-NEXT:    mret
+entry:
+  tail call void @callee_irq()
+  ret void
+}
+
+; Byval parameters hand the function a pointer directly into the stack area
+; we want to reuse during a tail call. Do not tail call optimize functions with
+; byval parameters.
+declare i32 @callee_byval(ptr byval(ptr) %a)
+define i32 @caller_byval() nounwind {
+; CHECK-LABEL: caller_byval:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    lw a0, 8(sp)
+; CHECK-NEXT:    sw a0, 4(sp)
+; CHECK-NEXT:  .Lpcrel_hi12:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI10_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi12)(a0)
+; CHECK-NEXT:    addi a0, sp, 4
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+entry:
+  %a = alloca ptr
+  %r = tail call i32 @callee_byval(ptr byval(ptr) %a)
+  ret i32 %r
+}
+
+; Do not tail call optimize if callee uses structret semantics.
+%struct.A = type { i32 }
+ at a = global %struct.A zeroinitializer
+
+declare void @callee_struct(ptr sret(%struct.A) %a)
+define void @caller_nostruct() nounwind {
+; CHECK-LABEL: caller_nostruct:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi13:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI11_0)
+; CHECK-NEXT:  .Lpcrel_hi14:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI11_1)
+; CHECK-NEXT:    lw a0, %pcrel_lo(.Lpcrel_hi13)(a0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi14)(a1)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+entry:
+  tail call void @callee_struct(ptr sret(%struct.A) @a)
+  ret void
+}
+
+; Do not tail call optimize if caller uses structret semantics.
+declare void @callee_nostruct()
+define void @caller_struct(ptr sret(%struct.A) %a) nounwind {
+; CHECK-LABEL: caller_struct:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi15:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI12_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi15)(a0)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+entry:
+  tail call void @callee_nostruct()
+  ret void
+}
+
+; Do not tail call optimize if disabled.
+define i32 @disable_tail_calls(i32 %i) nounwind "disable-tail-calls"="true" {
+; CHECK-LABEL: disable_tail_calls:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; CHECK-NEXT:  .Lpcrel_hi16:
+; CHECK-NEXT:    auipc a1, %pcrel_hi(.LCPI13_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi16)(a1)
+; CHECK-NEXT:    jalr t2
+; CHECK-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
+; CHECK-NEXT:    ret
+entry:
+  %rv = tail call i32 @callee_tail(i32 %i)
+  ret i32 %rv
+}
+
+; Duplicate returns to enable tail call optimizations.
+declare i32 @test()
+declare i32 @test1()
+declare i32 @test2()
+declare i32 @test3()
+define i32 @duplicate_returns(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: duplicate_returns:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    auipc zero, 0
+; CHECK-NEXT:    beqz a0, .LBB14_4
+; CHECK-NEXT:  # %bb.1: # %if.else
+; CHECK-NEXT:    beqz a1, .LBB14_5
+; CHECK-NEXT:  # %bb.2: # %if.else4
+; CHECK-NEXT:    bge a1, a0, .LBB14_6
+; CHECK-NEXT:  # %bb.3: # %if.then6
+; CHECK-NEXT:  .Lpcrel_hi19:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI14_1)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi19)(a0)
+; CHECK-NEXT:    jr t2
+; CHECK-NEXT:  .LBB14_4: # %if.then
+; CHECK-NEXT:  .Lpcrel_hi17:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI14_3)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi17)(a0)
+; CHECK-NEXT:    jr t2
+; CHECK-NEXT:  .LBB14_5: # %if.then2
+; CHECK-NEXT:  .Lpcrel_hi18:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI14_2)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi18)(a0)
+; CHECK-NEXT:    jr t2
+; CHECK-NEXT:  .LBB14_6: # %if.else8
+; CHECK-NEXT:  .Lpcrel_hi20:
+; CHECK-NEXT:    auipc a0, %pcrel_hi(.LCPI14_0)
+; CHECK-NEXT:    lw t2, %pcrel_lo(.Lpcrel_hi20)(a0)
+; CHECK-NEXT:    jr t2
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %call = tail call i32 @test()
+  br label %return
+
+if.else:                                          ; preds = %entry
+  %cmp1 = icmp eq i32 %b, 0
+  br i1 %cmp1, label %if.then2, label %if.else4
+
+if.then2:                                         ; preds = %if.else
+  %call3 = tail call i32 @test1()
+  br label %return
+
+if.else4:                                         ; preds = %if.else
+  %cmp5 = icmp sgt i32 %a, %b
+  br i1 %cmp5, label %if.then6, label %if.else8
+
+if.then6:                                         ; preds = %if.else4
+  %call7 = tail call i32 @test2()
+  br label %return
+
+if.else8:                                         ; preds = %if.else4
+  %call9 = tail call i32 @test3()
+  br label %return
+
+return:                                           ; preds = %if.else8, %if.then6, %if.then2, %if.then
+  %retval = phi i32 [ %call, %if.then ], [ %call3, %if.then2 ], [ %call7, %if.then6 ], [ %call9, %if.else8 ]
+  ret i32 %retval
+}
+
+!llvm.module.flags = !{!0, !15, !16}
+!0 = !{i32 1, !"ProfileSummary", !1}
+!1 = !{!2, !3, !4, !5, !6, !7, !8, !9}
+!2 = !{!"ProfileFormat", !"InstrProf"}
+!3 = !{!"TotalCount", i64 10000}
+!4 = !{!"MaxCount", i64 10}
+!5 = !{!"MaxInternalCount", i64 1}
+!6 = !{!"MaxFunctionCount", i64 1000}
+!7 = !{!"NumCounts", i64 3}
+!8 = !{!"NumFunctions", i64 3}
+!9 = !{!"DetailedSummary", !10}
+!10 = !{!11, !12, !13}
+!11 = !{i32 10000, i64 100, i32 1}
+!12 = !{i32 999000, i64 100, i32 1}
+!13 = !{i32 999999, i64 1, i32 2}
+!14 = !{!"function_entry_count", i64 0}
+!15 = !{i32 8, !"cf-protection-branch", i32 1}
+!16 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}

>From 8f601dd4799d9ce5c7636a82450981d995b90a9c Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Wed, 6 Aug 2025 17:59:04 +0800
Subject: [PATCH 04/16] fixup: Codegen software-guarded jumps for jump tables
 based on Zicfilp CFI feature adoption instead of Zicfilp extension
 availability

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   |  4 +-
 .../test/CodeGen/RISCV/jumptable-swguarded.ll | 48 ++++---------------
 2 files changed, 10 insertions(+), 42 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 86d51b8d7d172..0f490cb1890a4 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -24545,8 +24545,8 @@ SDValue RISCVTargetLowering::expandIndirectJTBranch(const SDLoc &dl,
                                                     SDValue Value, SDValue Addr,
                                                     int JTI,
                                                     SelectionDAG &DAG) const {
-  if (Subtarget.hasStdExtZicfilp()) {
-    // When Zicfilp enabled, we need to use software guarded branch for jump
+  if (Subtarget.hasZicfilpCFI()) {
+    // When Zicfilp CFI is used, we need to use software guarded branch for jump
     // table branch.
     SDValue Chain = Value;
     // Jump table debug info is only needed if CodeView is enabled.
diff --git a/llvm/test/CodeGen/RISCV/jumptable-swguarded.ll b/llvm/test/CodeGen/RISCV/jumptable-swguarded.ll
index b4ab0585c0cc9..32e3af6af5ad3 100644
--- a/llvm/test/CodeGen/RISCV/jumptable-swguarded.ll
+++ b/llvm/test/CodeGen/RISCV/jumptable-swguarded.ll
@@ -1,14 +1,11 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple riscv32 -mattr=+experimental-zicfilp < %s | FileCheck %s
-; RUN: llc -mtriple riscv64 -mattr=+experimental-zicfilp < %s | FileCheck %s
-; RUN: llc -mtriple riscv32 < %s | FileCheck %s --check-prefix=NO-ZICFILP
-; RUN: llc -mtriple riscv64 < %s | FileCheck %s --check-prefix=NO-ZICFILP
+; RUN: llc -mtriple riscv32 < %s | FileCheck %s
+; RUN: llc -mtriple riscv64 < %s | FileCheck %s
 
 ; Test using t2 to jump table branch.
 define void @above_threshold(i32 signext %in, ptr %out) nounwind {
 ; CHECK-LABEL: above_threshold:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    lpad 0
+; CHECK-NEXT:    auipc zero, 0
 ; CHECK-NEXT:    addi a0, a0, -1
 ; CHECK-NEXT:    li a2, 5
 ; CHECK-NEXT:    bltu a2, a0, .LBB0_9
@@ -40,40 +37,6 @@ define void @above_threshold(i32 signext %in, ptr %out) nounwind {
 ; CHECK-NEXT:    sw a0, 0(a1)
 ; CHECK-NEXT:  .LBB0_9: # %exit
 ; CHECK-NEXT:    ret
-;
-; NO-ZICFILP-LABEL: above_threshold:
-; NO-ZICFILP:       # %bb.0: # %entry
-; NO-ZICFILP-NEXT:    addi a0, a0, -1
-; NO-ZICFILP-NEXT:    li a2, 5
-; NO-ZICFILP-NEXT:    bltu a2, a0, .LBB0_9
-; NO-ZICFILP-NEXT:  # %bb.1: # %entry
-; NO-ZICFILP-NEXT:    slli a0, a0, 2
-; NO-ZICFILP-NEXT:    lui a2, %hi(.LJTI0_0)
-; NO-ZICFILP-NEXT:    addi a2, a2, %lo(.LJTI0_0)
-; NO-ZICFILP-NEXT:    add a0, a2, a0
-; NO-ZICFILP-NEXT:    lw a0, 0(a0)
-; NO-ZICFILP-NEXT:    jr a0
-; NO-ZICFILP-NEXT:  .LBB0_2: # %bb1
-; NO-ZICFILP-NEXT:    li a0, 4
-; NO-ZICFILP-NEXT:    j .LBB0_8
-; NO-ZICFILP-NEXT:  .LBB0_3: # %bb5
-; NO-ZICFILP-NEXT:    li a0, 100
-; NO-ZICFILP-NEXT:    j .LBB0_8
-; NO-ZICFILP-NEXT:  .LBB0_4: # %bb3
-; NO-ZICFILP-NEXT:    li a0, 2
-; NO-ZICFILP-NEXT:    j .LBB0_8
-; NO-ZICFILP-NEXT:  .LBB0_5: # %bb4
-; NO-ZICFILP-NEXT:    li a0, 1
-; NO-ZICFILP-NEXT:    j .LBB0_8
-; NO-ZICFILP-NEXT:  .LBB0_6: # %bb2
-; NO-ZICFILP-NEXT:    li a0, 3
-; NO-ZICFILP-NEXT:    j .LBB0_8
-; NO-ZICFILP-NEXT:  .LBB0_7: # %bb6
-; NO-ZICFILP-NEXT:    li a0, 200
-; NO-ZICFILP-NEXT:  .LBB0_8: # %exit
-; NO-ZICFILP-NEXT:    sw a0, 0(a1)
-; NO-ZICFILP-NEXT:  .LBB0_9: # %exit
-; NO-ZICFILP-NEXT:    ret
 entry:
   switch i32 %in, label %exit [
     i32 1, label %bb1
@@ -104,3 +67,8 @@ bb6:
 exit:
   ret void
 }
+
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 8, !"cf-protection-branch", i32 1}
+!1 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}

>From 876c7cae06b33df4d86ce8a5020c46ad7c3054eb Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Thu, 7 Aug 2025 14:49:38 +0800
Subject: [PATCH 05/16] fixup: Fix codegen of nested functions

---
 llvm/lib/Target/RISCV/RISCVCallingConv.cpp    |  4 +-
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   |  5 +--
 llvm/test/CodeGen/RISCV/nest-register.ll      | 26 -------------
 .../test/CodeGen/RISCV/rv64-trampoline-cfi.ll | 13 +++----
 .../RISCV/zicfilp-unlabeled-nest-register.ll  | 38 +++++++++++++++++++
 5 files changed, 46 insertions(+), 40 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/zicfilp-unlabeled-nest-register.ll

diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
index cb6117eb0917b..908b976d6e4bb 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
@@ -337,9 +337,7 @@ bool llvm::CC_RISCV(unsigned ValNo, MVT ValVT, MVT LocVT,
     // Static chain parameter must not be passed in normal argument registers,
     // so we assign t2/t3 for it as done in GCC's
     // __builtin_call_with_static_chain
-    bool HasCFBranch =
-        Subtarget.hasStdExtZicfilp() &&
-        MF.getFunction().getParent()->getModuleFlag("cf-protection-branch");
+    bool HasCFBranch = Subtarget.hasZicfilpCFI();
 
     // Normal: t2, Branch control flow protection: t3
     const auto StaticChainReg = HasCFBranch ? RISCV::X28 : RISCV::X7;
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 0f490cb1890a4..d7d602b102de5 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -8528,10 +8528,7 @@ SDValue RISCVTargetLowering::lowerINIT_TRAMPOLINE(SDValue Op,
   //     28: <FunctionAddressOffset>
   //     36:
 
-  const bool HasCFBranch =
-      Subtarget.hasStdExtZicfilp() &&
-      DAG.getMachineFunction().getFunction().getParent()->getModuleFlag(
-          "cf-protection-branch");
+  const bool HasCFBranch = Subtarget.hasZicfilpCFI();
   const unsigned StaticChainIdx = HasCFBranch ? 5 : 4;
   const unsigned StaticChainOffset = StaticChainIdx * 4;
   const unsigned FunctionAddressOffset = StaticChainOffset + 8;
diff --git a/llvm/test/CodeGen/RISCV/nest-register.ll b/llvm/test/CodeGen/RISCV/nest-register.ll
index 6e892e05c4297..e222beee45783 100644
--- a/llvm/test/CodeGen/RISCV/nest-register.ll
+++ b/llvm/test/CodeGen/RISCV/nest-register.ll
@@ -3,10 +3,6 @@
 ; RUN:   | FileCheck -check-prefix=RV32I %s
 ; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
 ; RUN:   | FileCheck -check-prefix=RV64I %s
-; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicfilp -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV64I-ZICFILP %s
-; RUN: not llc -mtriple=riscv64 -target-abi=lp64e -mattr=+experimental-zicfilp \
-; RUN:   -verify-machineinstrs < %s 2>&1 | FileCheck -check-prefix=LP64E-ZICFILP %s
 
 ; Tests that the 'nest' parameter attribute causes the relevant parameter to be
 ; passed in the right register.
@@ -21,12 +17,6 @@ define ptr @nest_receiver(ptr nest %arg) nounwind {
 ; RV64I:       # %bb.0:
 ; RV64I-NEXT:    mv a0, t2
 ; RV64I-NEXT:    ret
-;
-; RV64I-ZICFILP-LABEL: nest_receiver:
-; RV64I-ZICFILP:       # %bb.0:
-; RV64I-ZICFILP-NEXT:    lpad 0
-; RV64I-ZICFILP-NEXT:    mv a0, t3
-; RV64I-ZICFILP-NEXT:    ret
   ret ptr %arg
 }
 
@@ -50,22 +40,6 @@ define ptr @nest_caller(ptr %arg) nounwind {
 ; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
 ; RV64I-NEXT:    addi sp, sp, 16
 ; RV64I-NEXT:    ret
-;
-; RV64I-ZICFILP-LABEL: nest_caller:
-; RV64I-ZICFILP:       # %bb.0:
-; RV64I-ZICFILP-NEXT:    lpad 0
-; RV64I-ZICFILP-NEXT:    addi sp, sp, -16
-; RV64I-ZICFILP-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
-; RV64I-ZICFILP-NEXT:    mv t3, a0
-; RV64I-ZICFILP-NEXT:    call nest_receiver
-; RV64I-ZICFILP-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
-; RV64I-ZICFILP-NEXT:    addi sp, sp, 16
-; RV64I-ZICFILP-NEXT:    ret
   %result = call ptr @nest_receiver(ptr nest %arg)
   ret ptr %result
 }
-
-; LP64E-ZICFILP: LLVM ERROR: Nested functions with control flow protection are not usable with ILP32E or LP64E ABI.
-!llvm.module.flags = !{!0}
-
-!0 = !{i32 8, !"cf-protection-branch", i32 1}
diff --git a/llvm/test/CodeGen/RISCV/rv64-trampoline-cfi.ll b/llvm/test/CodeGen/RISCV/rv64-trampoline-cfi.ll
index 8a338a855c863..a50fa1924718b 100644
--- a/llvm/test/CodeGen/RISCV/rv64-trampoline-cfi.ll
+++ b/llvm/test/CodeGen/RISCV/rv64-trampoline-cfi.ll
@@ -1,8 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc -O0 -mtriple=riscv64 -mattr=+experimental-zicfilp -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV64 %s
-; RUN: llc -O0 -mtriple=riscv64-unknown-linux-gnu -mattr=+experimental-zicfilp -verify-machineinstrs < %s \
-; RUN:   | FileCheck -check-prefix=RV64-LINUX %s
+; RUN: llc -O0 -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck -check-prefix=RV64 %s
+; RUN: llc -O0 -mtriple=riscv64-unknown-linux-gnu -verify-machineinstrs < %s | FileCheck -check-prefix=RV64-LINUX %s
 
 declare void @llvm.init.trampoline(ptr, ptr, ptr)
 declare ptr @llvm.adjust.trampoline(ptr)
@@ -11,7 +9,7 @@ declare i64 @f(ptr nest, i64)
 define i64 @test0(i64 %n, ptr %p) nounwind {
 ; RV64-LABEL: test0:
 ; RV64:       # %bb.0:
-; RV64-NEXT:    lpad 0
+; RV64-NEXT:    auipc zero, 0
 ; RV64-NEXT:    addi sp, sp, -64
 ; RV64-NEXT:    sd ra, 56(sp) # 8-byte Folded Spill
 ; RV64-NEXT:    sd a0, 0(sp) # 8-byte Folded Spill
@@ -50,7 +48,7 @@ define i64 @test0(i64 %n, ptr %p) nounwind {
 ;
 ; RV64-LINUX-LABEL: test0:
 ; RV64-LINUX:       # %bb.0:
-; RV64-LINUX-NEXT:    lpad 0
+; RV64-LINUX-NEXT:    auipc zero, 0
 ; RV64-LINUX-NEXT:    addi sp, sp, -64
 ; RV64-LINUX-NEXT:    sd ra, 56(sp) # 8-byte Folded Spill
 ; RV64-LINUX-NEXT:    sd a0, 0(sp) # 8-byte Folded Spill
@@ -94,6 +92,7 @@ define i64 @test0(i64 %n, ptr %p) nounwind {
   ret i64 %ret
 }
 
-!llvm.module.flags = !{!0}
+!llvm.module.flags = !{!0, !1}
 
 !0 = !{i32 8, !"cf-protection-branch", i32 1}
+!1 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-nest-register.ll b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-nest-register.ll
new file mode 100644
index 0000000000000..89454e8f830b1
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-nest-register.ll
@@ -0,0 +1,38 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | \
+; RUN:    FileCheck -check-prefix=RV64I %s
+; RUN: not llc -mtriple=riscv64 -target-abi=lp64e -verify-machineinstrs < %s \
+; RUN:    2>&1 | FileCheck -check-prefix=LP64E %s
+
+; Tests that the 'nest' parameter attribute causes the relevant parameter to be
+; passed in the right register.
+
+define ptr @nest_receiver(ptr nest %arg) nounwind {
+; RV64I-LABEL: nest_receiver:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    auipc zero, 0
+; RV64I-NEXT:    mv a0, t3
+; RV64I-NEXT:    ret
+  ret ptr %arg
+}
+
+define ptr @nest_caller(ptr %arg) nounwind {
+; RV64I-LABEL: nest_caller:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    auipc zero, 0
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    mv t3, a0
+; RV64I-NEXT:    call nest_receiver
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %result = call ptr @nest_receiver(ptr nest %arg)
+  ret ptr %result
+}
+
+; LP64E: LLVM ERROR: Nested functions with control flow protection are not usable with ILP32E or LP64E ABI.
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 8, !"cf-protection-branch", i32 1}
+!1 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}

>From 32ee08d9bd872e69b3f3c02b2db1fff90760b869 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Wed, 6 Aug 2025 16:20:29 +0800
Subject: [PATCH 06/16] fixup: Use RISCVSubtarget::ZicfilpLabelScheme in
 RISCVIndirectBranchTracking pass

---
 .../RISCV/RISCVIndirectBranchTracking.cpp     | 26 +++++--------------
 1 file changed, 7 insertions(+), 19 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
index 554f7d5494731..f795031a866f1 100644
--- a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
+++ b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
@@ -19,14 +19,12 @@
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Support/RISCVISAUtils.h"
+#include "llvm/Support/Error.h"
 
 #define DEBUG_TYPE "riscv-indrect-branch-tracking"
 #define PASS_NAME "RISC-V Indirect Branch Tracking"
 
 using namespace llvm;
-using namespace llvm::RISCVISAUtils;
 
 cl::opt<uint32_t> PreferredLandingPadLabel(
     "riscv-landing-pad-label", cl::ReallyHidden,
@@ -66,25 +64,15 @@ static void emitLpad(MachineBasicBlock &MBB, const RISCVInstrInfo *TII,
 
 bool RISCVIndirectBranchTracking::runOnMachineFunction(MachineFunction &MF) {
   const auto &Subtarget = MF.getSubtarget<RISCVSubtarget>();
-  const RISCVInstrInfo *TII = Subtarget.getInstrInfo();
-
-  const Module *const M = MF.getFunction().getParent();
-  if (!M)
-    return false;
-  if (const Metadata *const Flag = M->getModuleFlag("cf-protection-branch");
-      !Flag || mdconst::extract<ConstantInt>(Flag)->isZero())
+  if (!Subtarget.hasZicfilpCFI())
     return false;
 
-  StringRef CFBranchLabelScheme;
-  if (const Metadata *const MD = M->getModuleFlag("cf-branch-label-scheme"))
-    CFBranchLabelScheme = cast<MDString>(MD)->getString();
-  else
-    report_fatal_error("missing cf-branch-label-scheme module flag");
+  const RISCVInstrInfo *TII = Subtarget.getInstrInfo();
 
-  const ZicfilpLabelSchemeKind Scheme =
-      getZicfilpLabelScheme(CFBranchLabelScheme);
-  if (Scheme != ZicfilpLabelSchemeKind::Unlabeled)
-    report_fatal_error("unsupported cf-branch-label-scheme module flag");
+  if (Subtarget.getZicfilpLabelScheme() !=
+      RISCVSubtarget::ZicfilpLabelSchemeKind::Unlabeled)
+    reportFatalUsageError(
+        "only cf-branch-label-scheme=unlabeled is supported for now");
 
   uint32_t FixedLabel = 0;
   if (PreferredLandingPadLabel.getNumOccurrences() > 0) {

>From fc0bfdfc6bc098087e9619054d18237f20f6981c Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Thu, 7 Aug 2025 15:02:48 +0800
Subject: [PATCH 07/16] fixup: Codegen landing pad setup insns based on cf
 flags

---
 .../lib/Target/RISCV/RISCVLandingPadSetup.cpp |  2 +-
 llvm/test/CodeGen/RISCV/lpad.ll               | 54 +++++++++----------
 2 files changed, 27 insertions(+), 29 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLandingPadSetup.cpp b/llvm/lib/Target/RISCV/RISCVLandingPadSetup.cpp
index 072694e123084..9b5cd1f5d0b80 100644
--- a/llvm/lib/Target/RISCV/RISCVLandingPadSetup.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLandingPadSetup.cpp
@@ -48,7 +48,7 @@ bool RISCVLandingPadSetup::runOnMachineFunction(MachineFunction &MF) {
   const auto &STI = MF.getSubtarget<RISCVSubtarget>();
   const RISCVInstrInfo &TII = *STI.getInstrInfo();
 
-  if (!STI.hasStdExtZicfilp())
+  if (!STI.hasZicfilpCFI())
     return false;
 
   uint32_t Label = 0;
diff --git a/llvm/test/CodeGen/RISCV/lpad.ll b/llvm/test/CodeGen/RISCV/lpad.ll
index f69b4dad068b5..4676d06a29725 100644
--- a/llvm/test/CodeGen/RISCV/lpad.ll
+++ b/llvm/test/CodeGen/RISCV/lpad.ll
@@ -1,17 +1,15 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple riscv32 -mattr=+experimental-zicfilp < %s | FileCheck %s --check-prefixes=CHECK,RV32
-; RUN: llc -mtriple riscv64 -mattr=+experimental-zicfilp < %s | FileCheck %s --check-prefixes=CHECK,RV64
-; RUN: llc -mtriple riscv32 -mattr=+experimental-zicfilp \
-; RUN: -riscv-landing-pad-label=1 < %s | FileCheck %s --check-prefixes=FIXED-ONE,FIXED-ONE-RV32
-; RUN: llc -mtriple riscv64 -mattr=+experimental-zicfilp \
-; RUN: -riscv-landing-pad-label=1 < %s | FileCheck %s --check-prefixes=FIXED-ONE,FIXED-ONE-RV64
+; RUN: llc -mtriple riscv32 < %s | FileCheck %s --check-prefixes=CHECK,RV32
+; RUN: llc -mtriple riscv64 < %s | FileCheck %s --check-prefixes=CHECK,RV64
+; RUN: llc -mtriple riscv32 -riscv-landing-pad-label=1 < %s | FileCheck %s --check-prefixes=FIXED-ONE,FIXED-ONE-RV32
+; RUN: llc -mtriple riscv64 -riscv-landing-pad-label=1 < %s | FileCheck %s --check-prefixes=FIXED-ONE,FIXED-ONE-RV64
 
 ; Check indirectbr.
 @__const.indirctbr.addr = private unnamed_addr constant [2 x ptr] [ptr blockaddress(@indirctbr, %labelA), ptr blockaddress(@indirctbr, %labelB)], align 8
 define void @indirctbr(i32 %i, ptr %p) {
 ; RV32-LABEL: indirctbr:
 ; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    lpad 0
+; RV32-NEXT:    auipc zero, 0
 ; RV32-NEXT:    slli a0, a0, 2
 ; RV32-NEXT:    lui a2, %hi(.L__const.indirctbr.addr)
 ; RV32-NEXT:    addi a2, a2, %lo(.L__const.indirctbr.addr)
@@ -21,20 +19,20 @@ define void @indirctbr(i32 %i, ptr %p) {
 ; RV32-NEXT:    .p2align 2
 ; RV32-NEXT:  .Ltmp3: # Block address taken
 ; RV32-NEXT:  .LBB0_1: # %labelA
-; RV32-NEXT:    lpad 0
+; RV32-NEXT:    auipc zero, 0
 ; RV32-NEXT:    li a0, 1
 ; RV32-NEXT:    sw a0, 0(a1)
 ; RV32-NEXT:    .p2align 2
 ; RV32-NEXT:  .Ltmp4: # Block address taken
 ; RV32-NEXT:  .LBB0_2: # %labelB
-; RV32-NEXT:    lpad 0
+; RV32-NEXT:    auipc zero, 0
 ; RV32-NEXT:    li a0, 2
 ; RV32-NEXT:    sw a0, 0(a1)
 ; RV32-NEXT:    ret
 ;
 ; RV64-LABEL: indirctbr:
 ; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    lpad 0
+; RV64-NEXT:    auipc zero, 0
 ; RV64-NEXT:    sext.w a0, a0
 ; RV64-NEXT:    slli a0, a0, 3
 ; RV64-NEXT:    lui a2, %hi(.L__const.indirctbr.addr)
@@ -45,20 +43,20 @@ define void @indirctbr(i32 %i, ptr %p) {
 ; RV64-NEXT:    .p2align 2
 ; RV64-NEXT:  .Ltmp3: # Block address taken
 ; RV64-NEXT:  .LBB0_1: # %labelA
-; RV64-NEXT:    lpad 0
+; RV64-NEXT:    auipc zero, 0
 ; RV64-NEXT:    li a0, 1
 ; RV64-NEXT:    sw a0, 0(a1)
 ; RV64-NEXT:    .p2align 2
 ; RV64-NEXT:  .Ltmp4: # Block address taken
 ; RV64-NEXT:  .LBB0_2: # %labelB
-; RV64-NEXT:    lpad 0
+; RV64-NEXT:    auipc zero, 0
 ; RV64-NEXT:    li a0, 2
 ; RV64-NEXT:    sw a0, 0(a1)
 ; RV64-NEXT:    ret
 ;
 ; FIXED-ONE-RV32-LABEL: indirctbr:
 ; FIXED-ONE-RV32:       # %bb.0: # %entry
-; FIXED-ONE-RV32-NEXT:    lpad 1
+; FIXED-ONE-RV32-NEXT:    auipc zero, 1
 ; FIXED-ONE-RV32-NEXT:    slli a0, a0, 2
 ; FIXED-ONE-RV32-NEXT:    lui a2, %hi(.L__const.indirctbr.addr)
 ; FIXED-ONE-RV32-NEXT:    addi a2, a2, %lo(.L__const.indirctbr.addr)
@@ -69,20 +67,20 @@ define void @indirctbr(i32 %i, ptr %p) {
 ; FIXED-ONE-RV32-NEXT:    .p2align 2
 ; FIXED-ONE-RV32-NEXT:  .Ltmp3: # Block address taken
 ; FIXED-ONE-RV32-NEXT:  .LBB0_1: # %labelA
-; FIXED-ONE-RV32-NEXT:    lpad 1
+; FIXED-ONE-RV32-NEXT:    auipc zero, 1
 ; FIXED-ONE-RV32-NEXT:    li a0, 1
 ; FIXED-ONE-RV32-NEXT:    sw a0, 0(a1)
 ; FIXED-ONE-RV32-NEXT:    .p2align 2
 ; FIXED-ONE-RV32-NEXT:  .Ltmp4: # Block address taken
 ; FIXED-ONE-RV32-NEXT:  .LBB0_2: # %labelB
-; FIXED-ONE-RV32-NEXT:    lpad 1
+; FIXED-ONE-RV32-NEXT:    auipc zero, 1
 ; FIXED-ONE-RV32-NEXT:    li a0, 2
 ; FIXED-ONE-RV32-NEXT:    sw a0, 0(a1)
 ; FIXED-ONE-RV32-NEXT:    ret
 ;
 ; FIXED-ONE-RV64-LABEL: indirctbr:
 ; FIXED-ONE-RV64:       # %bb.0: # %entry
-; FIXED-ONE-RV64-NEXT:    lpad 1
+; FIXED-ONE-RV64-NEXT:    auipc zero, 1
 ; FIXED-ONE-RV64-NEXT:    sext.w a0, a0
 ; FIXED-ONE-RV64-NEXT:    slli a0, a0, 3
 ; FIXED-ONE-RV64-NEXT:    lui a2, %hi(.L__const.indirctbr.addr)
@@ -94,13 +92,13 @@ define void @indirctbr(i32 %i, ptr %p) {
 ; FIXED-ONE-RV64-NEXT:    .p2align 2
 ; FIXED-ONE-RV64-NEXT:  .Ltmp3: # Block address taken
 ; FIXED-ONE-RV64-NEXT:  .LBB0_1: # %labelA
-; FIXED-ONE-RV64-NEXT:    lpad 1
+; FIXED-ONE-RV64-NEXT:    auipc zero, 1
 ; FIXED-ONE-RV64-NEXT:    li a0, 1
 ; FIXED-ONE-RV64-NEXT:    sw a0, 0(a1)
 ; FIXED-ONE-RV64-NEXT:    .p2align 2
 ; FIXED-ONE-RV64-NEXT:  .Ltmp4: # Block address taken
 ; FIXED-ONE-RV64-NEXT:  .LBB0_2: # %labelB
-; FIXED-ONE-RV64-NEXT:    lpad 1
+; FIXED-ONE-RV64-NEXT:    auipc zero, 1
 ; FIXED-ONE-RV64-NEXT:    li a0, 2
 ; FIXED-ONE-RV64-NEXT:    sw a0, 0(a1)
 ; FIXED-ONE-RV64-NEXT:    ret
@@ -122,12 +120,12 @@ labelB:                                           ; preds = %labelA, %entry
 define void @call(ptr %0) {
 ; CHECK-LABEL: call:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    lpad 0
+; CHECK-NEXT:    auipc zero, 0
 ; CHECK-NEXT:    jr a0
 ;
 ; FIXED-ONE-LABEL: call:
 ; FIXED-ONE:       # %bb.0:
-; FIXED-ONE-NEXT:    lpad 1
+; FIXED-ONE-NEXT:    auipc zero, 1
 ; FIXED-ONE-NEXT:    lui t2, 1
 ; FIXED-ONE-NEXT:    jr a0
   tail call void %0()
@@ -139,7 +137,7 @@ declare dso_local i32 @__gxx_personality_v0(...)
 define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; RV32-LABEL: invoke:
 ; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    lpad 0
+; RV32-NEXT:    auipc zero, 0
 ; RV32-NEXT:    addi sp, sp, -16
 ; RV32-NEXT:    .cfi_def_cfa_offset 16
 ; RV32-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
@@ -161,7 +159,7 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ;
 ; RV64-LABEL: invoke:
 ; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    lpad 0
+; RV64-NEXT:    auipc zero, 0
 ; RV64-NEXT:    addi sp, sp, -16
 ; RV64-NEXT:    .cfi_def_cfa_offset 16
 ; RV64-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
@@ -183,7 +181,7 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ;
 ; FIXED-ONE-RV32-LABEL: invoke:
 ; FIXED-ONE-RV32:       # %bb.0: # %entry
-; FIXED-ONE-RV32-NEXT:    lpad 1
+; FIXED-ONE-RV32-NEXT:    auipc zero, 1
 ; FIXED-ONE-RV32-NEXT:    addi sp, sp, -16
 ; FIXED-ONE-RV32-NEXT:    .cfi_def_cfa_offset 16
 ; FIXED-ONE-RV32-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
@@ -206,7 +204,7 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ;
 ; FIXED-ONE-RV64-LABEL: invoke:
 ; FIXED-ONE-RV64:       # %bb.0: # %entry
-; FIXED-ONE-RV64-NEXT:    lpad 1
+; FIXED-ONE-RV64-NEXT:    auipc zero, 1
 ; FIXED-ONE-RV64-NEXT:    addi sp, sp, -16
 ; FIXED-ONE-RV64-NEXT:    .cfi_def_cfa_offset 16
 ; FIXED-ONE-RV64-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
@@ -241,12 +239,12 @@ try.cont:
 define void @external() {
 ; CHECK-LABEL: external:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    lpad 0
+; CHECK-NEXT:    auipc zero, 0
 ; CHECK-NEXT:    ret
 ;
 ; FIXED-ONE-LABEL: external:
 ; FIXED-ONE:       # %bb.0:
-; FIXED-ONE-NEXT:    lpad 1
+; FIXED-ONE-NEXT:    auipc zero, 1
 ; FIXED-ONE-NEXT:    ret
   ret void
 }
@@ -268,12 +266,12 @@ define internal void @internal() {
 define internal void @internal2() {
 ; CHECK-LABEL: internal2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    lpad 0
+; CHECK-NEXT:    auipc zero, 0
 ; CHECK-NEXT:    ret
 ;
 ; FIXED-ONE-LABEL: internal2:
 ; FIXED-ONE:       # %bb.0:
-; FIXED-ONE-NEXT:    lpad 1
+; FIXED-ONE-NEXT:    auipc zero, 1
 ; FIXED-ONE-NEXT:    ret
   ret void
 }

>From c8a374d5a4a482740eba1b825ae771ec12dd56ca Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Thu, 7 Aug 2025 15:01:02 +0800
Subject: [PATCH 08/16] fixup: Fix misc Zicfilp-based tests to base on cf flags

---
 .../RISCV/zicfilp-disabled-indirect-branch.ll | 41 +++++++++++++++++++
 .../CodeGen/RISCV/zicfilp-indirect-branch.ll  | 18 ++++----
 2 files changed, 49 insertions(+), 10 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/zicfilp-disabled-indirect-branch.ll

diff --git a/llvm/test/CodeGen/RISCV/zicfilp-disabled-indirect-branch.ll b/llvm/test/CodeGen/RISCV/zicfilp-disabled-indirect-branch.ll
new file mode 100644
index 0000000000000..897260908075c
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/zicfilp-disabled-indirect-branch.ll
@@ -0,0 +1,41 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
+; RUN: llc -mtriple=riscv64 -stop-after=finalize-isel < %s | FileCheck %s
+
+ at brind.arr = internal unnamed_addr constant [2 x ptr] [ptr blockaddress(@brind, %5), ptr blockaddress(@brind, %8)], align 8
+ at x = dso_local global i32 0, align 4
+
+define void @brind(i32 noundef signext %0) {
+  ; CHECK-LABEL: name: brind
+  ; CHECK:   PseudoBRIND killed [[VAR:%.*]], 0
+  %2 = sext i32 %0 to i64
+  %3 = getelementptr inbounds [2 x ptr], ptr @brind.arr, i64 0, i64 %2
+  %4 = load ptr, ptr %3, align 8
+  indirectbr ptr %4, [label %5, label %8]
+
+5:                                                ; preds = %1
+  %6 = load i32, ptr @x, align 4
+  %7 = add nsw i32 %6, 2
+  store i32 %7, ptr @x, align 4
+  br label %8
+
+8:                                                ; preds = %5, %1
+  %9 = load i32, ptr @x, align 4
+  %10 = add nsw i32 %9, 1
+  store i32 %10, ptr @x, align 4
+  ret void
+}
+
+define i32 @indirect_call(ptr %0) {
+  ; CHECK-LABEL: name: indirect_call
+  ; CHECK: PseudoCALLIndirect
+  call void %0()
+  ret i32 0
+}
+
+
+define void @indirect_tail(ptr %0) {
+  ; CHECK-LABEL: name: indirect_tail
+  ; CHECK: PseudoTAILIndirect
+  tail call void %0()
+  ret void
+}
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll b/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
index bccd28ee7e2b3..277f0d67b1d93 100644
--- a/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
+++ b/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
@@ -1,15 +1,12 @@
 ; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
 ; RUN: llc -mtriple=riscv64 -stop-after=finalize-isel < %s | FileCheck %s
-; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicfilp -stop-after=finalize-isel < %s | FileCheck -check-prefixes=ZICFILP %s
 
 @brind.arr = internal unnamed_addr constant [2 x ptr] [ptr blockaddress(@brind, %5), ptr blockaddress(@brind, %8)], align 8
 @x = dso_local global i32 0, align 4
 
 define void @brind(i32 noundef signext %0) {
   ; CHECK-LABEL: name: brind
-  ; CHECK:   PseudoBRIND killed [[VAR:%.*]], 0
-  ; ZICFILP-LABEL: name: brind
-  ; ZICFILP:   PseudoBRINDNonX7 killed [[VAR:%.*]], 0
+  ; CHECK:   PseudoBRINDNonX7 killed [[VAR:%.*]], 0
   %2 = sext i32 %0 to i64
   %3 = getelementptr inbounds [2 x ptr], ptr @brind.arr, i64 0, i64 %2
   %4 = load ptr, ptr %3, align 8
@@ -30,9 +27,7 @@ define void @brind(i32 noundef signext %0) {
 
 define i32 @indirect_call(ptr %0) {
   ; CHECK-LABEL: name: indirect_call
-  ; CHECK: PseudoCALLIndirect
-  ; ZICFILP-LABEL: name: indirect_call
-  ; ZICFILP: PseudoCALLIndirectNonX7
+  ; CHECK: PseudoCALLIndirectNonX7
   call void %0()
   ret i32 0
 }
@@ -40,9 +35,12 @@ define i32 @indirect_call(ptr %0) {
 
 define void @indirect_tail(ptr %0) {
   ; CHECK-LABEL: name: indirect_tail
-  ; CHECK: PseudoTAILIndirect
-  ; ZICFILP-LABEL: name: indirect_tail
-  ; ZICFILP: PseudoTAILIndirectNonX7
+  ; CHECK: PseudoTAILIndirectNonX7
   tail call void %0()
   ret void
 }
+
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 8, !"cf-protection-branch", i32 1}
+!1 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}

>From 3b9222337fab2939304f4d891e856389daf451d4 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Fri, 8 Aug 2025 11:06:57 +0800
Subject: [PATCH 09/16] fixup: Misc improvements

---
 llvm/lib/Target/RISCV/RISCVSubtarget.h       |  2 +-
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 15 +++++++++------
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index 424087b8ca83b..bf545ae594e4a 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -94,7 +94,7 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
 
   enum class ZicfilpLabelSchemeKind {
     Disabled,
-    Unknown,
+    EnabledUnknown,
     Unlabeled,
     FuncSig,
   };
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index fee8a2598be5b..ab4f523205af1 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -270,16 +270,19 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
           StringSwitch<ZicfilpLabelSchemeKind>(CFBranchLabelScheme)
               .Case("unlabeled", ZicfilpLabelSchemeKind::Unlabeled)
               .Case("func-sig", ZicfilpLabelSchemeKind::FuncSig)
-              .Default(ZicfilpLabelSchemeKind::Unknown);
-      assert(ZicfilpLabelScheme != ZicfilpLabelSchemeKind::Unknown);
+              .Default(ZicfilpLabelSchemeKind::EnabledUnknown);
+      assert(ZicfilpLabelScheme != ZicfilpLabelSchemeKind::EnabledUnknown);
     }
   }
 
   SmallString<512> Key;
-  raw_svector_ostream(Key) << "RVVMin" << RVVBitsMin << "RVVMax" << RVVBitsMax
-                           << "ZicfilpLabelSchemeKind"
-                           << to_underlying(ZicfilpLabelScheme) << CPU
-                           << TuneCPU << FS;
+  {
+    raw_svector_ostream OS(Key);
+    OS << "RVVMin" << RVVBitsMin << "RVVMax" << RVVBitsMax;
+    if (ZicfilpLabelScheme != ZicfilpLabelSchemeKind::Disabled)
+      OS << "ZicfilpLabelSchemeKind" << to_underlying(ZicfilpLabelScheme);
+    OS << CPU << TuneCPU << FS;
+  }
   auto &I = SubtargetMap[Key];
   if (!I) {
     // This needs to be done before we create a new subtarget since any

>From cb9940d684b1d1fc96b8455d7f6c8f3f72eed9b3 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Fri, 8 Aug 2025 17:56:34 +0800
Subject: [PATCH 10/16] fixup: Add LTO tests

---
 .../branch-cfi/Inputs/rv32-foo-disabled.ll    | 18 ++++++++
 .../Inputs/rv32-foo-unknown-scheme.ll         | 20 +++++++++
 .../branch-cfi/Inputs/rv32-foo-unlabeled.ll   | 20 +++++++++
 .../branch-cfi/Inputs/rv64-foo-disabled.ll    | 18 ++++++++
 .../Inputs/rv64-foo-unknown-scheme.ll         | 20 +++++++++
 .../branch-cfi/Inputs/rv64-foo-unlabeled.ll   | 20 +++++++++
 .../LTO/RISCV/branch-cfi/rv32-unlabeled.ll    | 43 +++++++++++++++++++
 .../LTO/RISCV/branch-cfi/rv64-unlabeled.ll    | 43 +++++++++++++++++++
 8 files changed, 202 insertions(+)
 create mode 100644 llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-disabled.ll
 create mode 100644 llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-unknown-scheme.ll
 create mode 100644 llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-unlabeled.ll
 create mode 100644 llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-disabled.ll
 create mode 100644 llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-unknown-scheme.ll
 create mode 100644 llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-unlabeled.ll
 create mode 100644 llvm/test/LTO/RISCV/branch-cfi/rv32-unlabeled.ll
 create mode 100644 llvm/test/LTO/RISCV/branch-cfi/rv64-unlabeled.ll

diff --git a/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-disabled.ll b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-disabled.ll
new file mode 100644
index 0000000000000..45090922eafd0
--- /dev/null
+++ b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-disabled.ll
@@ -0,0 +1,18 @@
+target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
+target triple = "riscv32-unknown-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone
+define dso_local i32 @foo() #0 {
+entry:
+  ret i32 0
+}
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv32" "target-features"="+32bit" }
+
+!llvm.module.flags = !{!0, !1, !2, !4}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"ilp32"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv32i2p1"}
+!4 = !{i32 8, !"SmallDataLimit", i32 0}
diff --git a/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-unknown-scheme.ll b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-unknown-scheme.ll
new file mode 100644
index 0000000000000..c390edddb0c0a
--- /dev/null
+++ b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-unknown-scheme.ll
@@ -0,0 +1,20 @@
+target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
+target triple = "riscv32-unknown-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone
+define dso_local i32 @foo() #0 {
+entry:
+  ret i32 0
+}
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv32" "target-features"="+32bit" }
+
+!llvm.module.flags = !{!0, !1, !2, !4, !5, !6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"ilp32"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv32i2p1"}
+!4 = !{i32 8, !"cf-protection-branch", i32 1}
+!5 = !{i32 1, !"cf-branch-label-scheme", !"unknown-scheme"}
+!6 = !{i32 8, !"SmallDataLimit", i32 0}
diff --git a/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-unlabeled.ll b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-unlabeled.ll
new file mode 100644
index 0000000000000..7c6f56422683e
--- /dev/null
+++ b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv32-foo-unlabeled.ll
@@ -0,0 +1,20 @@
+target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
+target triple = "riscv32-unknown-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone
+define dso_local i32 @foo() #0 {
+entry:
+  ret i32 0
+}
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv32" "target-features"="+32bit" }
+
+!llvm.module.flags = !{!0, !1, !2, !4, !5, !6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"ilp32"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv32i2p1"}
+!4 = !{i32 8, !"cf-protection-branch", i32 1}
+!5 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
+!6 = !{i32 8, !"SmallDataLimit", i32 0}
diff --git a/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-disabled.ll b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-disabled.ll
new file mode 100644
index 0000000000000..e59f8ec41c1a6
--- /dev/null
+++ b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-disabled.ll
@@ -0,0 +1,18 @@
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+target triple = "riscv64-unknown-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone
+define dso_local signext i32 @foo() #0 {
+entry:
+  ret i32 0
+}
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit" }
+
+!llvm.module.flags = !{!0, !1, !2, !4}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"lp64"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv64i2p1"}
+!4 = !{i32 8, !"SmallDataLimit", i32 0}
diff --git a/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-unknown-scheme.ll b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-unknown-scheme.ll
new file mode 100644
index 0000000000000..f308f10dd718d
--- /dev/null
+++ b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-unknown-scheme.ll
@@ -0,0 +1,20 @@
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+target triple = "riscv64-unknown-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone
+define dso_local signext i32 @foo() #0 {
+entry:
+  ret i32 0
+}
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit" }
+
+!llvm.module.flags = !{!0, !1, !2, !4, !5, !6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"lp64"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv64i2p1"}
+!4 = !{i32 8, !"cf-protection-branch", i32 1}
+!5 = !{i32 1, !"cf-branch-label-scheme", !"unknown-scheme"}
+!6 = !{i32 8, !"SmallDataLimit", i32 0}
diff --git a/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-unlabeled.ll b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-unlabeled.ll
new file mode 100644
index 0000000000000..0aa7f112ba942
--- /dev/null
+++ b/llvm/test/LTO/RISCV/branch-cfi/Inputs/rv64-foo-unlabeled.ll
@@ -0,0 +1,20 @@
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+target triple = "riscv64-unknown-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone
+define dso_local signext i32 @foo() #0 {
+entry:
+  ret i32 0
+}
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit" }
+
+!llvm.module.flags = !{!0, !1, !2, !4, !5, !6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"lp64"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv64i2p1"}
+!4 = !{i32 8, !"cf-protection-branch", i32 1}
+!5 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
+!6 = !{i32 8, !"SmallDataLimit", i32 0}
diff --git a/llvm/test/LTO/RISCV/branch-cfi/rv32-unlabeled.ll b/llvm/test/LTO/RISCV/branch-cfi/rv32-unlabeled.ll
new file mode 100644
index 0000000000000..cb29af67b777e
--- /dev/null
+++ b/llvm/test/LTO/RISCV/branch-cfi/rv32-unlabeled.ll
@@ -0,0 +1,43 @@
+; RUN: llvm-as %s -o %t.main.bc
+; RUN: llvm-as %p/Inputs/rv32-foo-unlabeled.ll -o %t.foo.unlabeled.bc
+; RUN: llvm-link %t.main.bc %t.foo.unlabeled.bc -S | FileCheck --check-prefix=UNLABELED %s
+
+; RUN: llvm-as %p/Inputs/rv32-foo-disabled.ll -o %t.foo.disabled.bc
+; RUN: llvm-link %t.main.bc %t.foo.disabled.bc -S | FileCheck --check-prefix=DISABLED %s
+
+; RUN: llvm-as %p/Inputs/rv32-foo-unknown-scheme.ll -o %t.foo.unknown.scheme.bc
+; RUN: not llvm-link %t.main.bc %t.foo.unknown.scheme.bc 2>&1 | FileCheck --check-prefix=SCHEME-CONFLICT %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
+target triple = "riscv32-unknown-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone
+define dso_local i32 @main() #0 {
+entry:
+  %retval = alloca i32, align 4
+  store i32 0, ptr %retval, align 4
+  %call = call i32 @foo()
+  ret i32 %call
+}
+
+declare dso_local i32 @foo() #1
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv32" "target-features"="+32bit" }
+attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv32" "target-features"="+32bit" }
+
+!llvm.module.flags = !{!0, !1, !2, !4, !5, !6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"ilp32"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv32i2p1"}
+; UNLABELED-DAG: [[P_FLAG:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 1}
+; DISABLED-DAG: [[P_FLAG:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 0}
+!4 = !{i32 8, !"cf-protection-branch", i32 1}
+; UNLABELED-DAG: [[S_FLAG:![0-9]+]] = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
+!5 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
+!6 = !{i32 8, !"SmallDataLimit", i32 0}
+; UNLABELED-DAG: !llvm.module.flags = !{{[{].*}}[[P_FLAG]]{{, .*}}[[S_FLAG]]{{[,}]}}
+; DISABLED-DAG: !llvm.module.flags = !{{[{].*}}[[P_FLAG]]{{[,}]}}
+
+; SCHEME-CONFLICT: error: linking module flags 'cf-branch-label-scheme': IDs have conflicting values: '!"unknown-scheme"' from {{.*}}, and '!"unlabeled"' from llvm-link
diff --git a/llvm/test/LTO/RISCV/branch-cfi/rv64-unlabeled.ll b/llvm/test/LTO/RISCV/branch-cfi/rv64-unlabeled.ll
new file mode 100644
index 0000000000000..b0897a8462319
--- /dev/null
+++ b/llvm/test/LTO/RISCV/branch-cfi/rv64-unlabeled.ll
@@ -0,0 +1,43 @@
+; RUN: llvm-as %s -o %t.main.bc
+; RUN: llvm-as %p/Inputs/rv64-foo-unlabeled.ll -o %t.foo.unlabeled.bc
+; RUN: llvm-link %t.main.bc %t.foo.unlabeled.bc -S | FileCheck --check-prefix=UNLABELED %s
+
+; RUN: llvm-as %p/Inputs/rv64-foo-disabled.ll -o %t.foo.disabled.bc
+; RUN: llvm-link %t.main.bc %t.foo.disabled.bc -S | FileCheck --check-prefix=DISABLED %s
+
+; RUN: llvm-as %p/Inputs/rv64-foo-unknown-scheme.ll -o %t.foo.unknown.scheme.bc
+; RUN: not llvm-link %t.main.bc %t.foo.unknown.scheme.bc 2>&1 | FileCheck --check-prefix=SCHEME-CONFLICT %s
+
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
+target triple = "riscv64-unknown-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone
+define dso_local signext i32 @main() #0 {
+entry:
+  %retval = alloca i32, align 4
+  store i32 0, ptr %retval, align 4
+  %call = call signext i32 @foo()
+  ret i32 %call
+}
+
+declare dso_local signext i32 @foo() #1
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit" }
+attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit" }
+
+!llvm.module.flags = !{!0, !1, !2, !4, !5, !6}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"target-abi", !"lp64"}
+!2 = !{i32 6, !"riscv-isa", !3}
+!3 = !{!"rv64i2p1"}
+; UNLABELED-DAG: [[P_FLAG:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 1}
+; DISABLED-DAG: [[P_FLAG:![0-9]+]] = !{i32 8, !"cf-protection-branch", i32 0}
+!4 = !{i32 8, !"cf-protection-branch", i32 1}
+; UNLABELED-DAG: [[S_FLAG:![0-9]+]] = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
+!5 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}
+!6 = !{i32 8, !"SmallDataLimit", i32 0}
+; UNLABELED-DAG: !llvm.module.flags = !{{[{].*}}[[P_FLAG]]{{, .*}}[[S_FLAG]]{{[,}]}}
+; DISABLED-DAG: !llvm.module.flags = !{{[{].*}}[[P_FLAG]]{{[,}]}}
+
+; SCHEME-CONFLICT: error: linking module flags 'cf-branch-label-scheme': IDs have conflicting values: '!"unknown-scheme"' from {{.*}}, and '!"unlabeled"' from llvm-link

>From f9f6bbaef1385876aae1852bc331701c4eb7af05 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Thu, 21 Aug 2025 16:40:59 +0800
Subject: [PATCH 11/16] fixup: Model ZICFILP-func-sig and ZICFILP-unlabeled as
 real features

---
 clang/lib/Driver/ToolChains/Arch/RISCV.cpp    | 29 ++++++++
 clang/test/Driver/riscv-features.c            | 18 +++++
 .../RISCV/MCTargetDesc/RISCVBaseInfo.cpp      |  4 ++
 llvm/lib/Target/RISCV/RISCVFeatures.td        | 14 +++-
 .../RISCV/RISCVIndirectBranchTracking.cpp     |  2 +-
 llvm/lib/Target/RISCV/RISCVSubtarget.cpp      |  2 -
 llvm/lib/Target/RISCV/RISCVSubtarget.h        | 18 ++---
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp  | 66 +++++++++----------
 llvm/test/CodeGen/RISCV/features-info.ll      |  2 +
 .../test/CodeGen/RISCV/jumptable-swguarded.ll |  4 +-
 llvm/test/CodeGen/RISCV/lpad.ll               |  8 +--
 .../test/CodeGen/RISCV/rv64-trampoline-cfi.ll |  4 +-
 .../CodeGen/RISCV/zicfilp-indirect-branch.ll  |  2 +-
 .../CodeGen/RISCV/zicfilp-unlabeled-calls.ll  |  2 +-
 .../RISCV/zicfilp-unlabeled-nest-register.ll  |  4 +-
 .../RISCV/zicfilp-unlabeled-tail-calls.ll     |  2 +-
 .../Target/RISCV/RISCVInstrInfoTest.cpp       |  3 +-
 17 files changed, 120 insertions(+), 64 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp
index baa2c8c0bcfb2..64cc43957e01f 100644
--- a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp
@@ -176,6 +176,35 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
     Features.push_back("+unaligned-vector-mem");
   }
 
+  if (const Arg *A = Args.getLastArg(options::OPT_fcf_protection_EQ)) {
+    const StringRef CFProtection{A->getValue()};
+    const bool CFProtectionBranch =
+        CFProtection == "full" || CFProtection == "branch";
+    if (CFProtectionBranch) {
+      bool FuncSig;
+      if (const Arg *SA =
+              Args.getLastArg(options::OPT_mcf_branch_label_scheme_EQ)) {
+        const StringRef Scheme{SA->getValue()};
+        if (Scheme == "unlabeled") {
+          FuncSig = false;
+        } else {
+          assert(Scheme == "func-sig" &&
+                 "-mcf-branch-label-scheme should be either `unlabeled` or "
+                 "`func-sig`");
+          FuncSig = true;
+        }
+      } else {
+        // `func-sig` is assumed if `-mcf-branch-label-scheme` is not given.
+        FuncSig = true;
+      }
+
+      if (FuncSig)
+        Features.push_back("+zicfilp-func-sig");
+      else
+        Features.push_back("+zicfilp-unlabeled");
+    }
+  }
+
   // Now add any that the user explicitly requested on the command line,
   // which may override the defaults.
   handleTargetFeaturesGroup(D, Triple, Args, Features,
diff --git a/clang/test/Driver/riscv-features.c b/clang/test/Driver/riscv-features.c
index 80dec2c71f985..8d02bb82abc30 100644
--- a/clang/test/Driver/riscv-features.c
+++ b/clang/test/Driver/riscv-features.c
@@ -85,3 +85,21 @@
 // FUCHSIA-SAME: "-target-feature" "+zbb"
 // FUCHSIA-SAME: "-target-feature" "+zbs"
 
+// RUN: %clang --target=riscv32 -fcf-protection=full -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-FUNC-SIG
+// RUN: %clang --target=riscv32 -fcf-protection=full -mcf-branch-label-scheme=func-sig -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-FUNC-SIG
+// RUN: %clang --target=riscv32 -fcf-protection=full -mcf-branch-label-scheme=unlabeled -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-UNLABELED
+// RUN: %clang --target=riscv32 -fcf-protection=branch -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-FUNC-SIG
+// RUN: %clang --target=riscv32 -fcf-protection=branch -mcf-branch-label-scheme=func-sig -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-FUNC-SIG
+// RUN: %clang --target=riscv32 -fcf-protection=branch -mcf-branch-label-scheme=unlabeled -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-UNLABELED
+// RUN: %clang --target=riscv64 -fcf-protection=full -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-FUNC-SIG
+// RUN: %clang --target=riscv64 -fcf-protection=full -mcf-branch-label-scheme=func-sig -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-FUNC-SIG
+// RUN: %clang --target=riscv64 -fcf-protection=full -mcf-branch-label-scheme=unlabeled -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-UNLABELED
+// RUN: %clang --target=riscv64 -fcf-protection=branch -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-FUNC-SIG
+// RUN: %clang --target=riscv64 -fcf-protection=branch -mcf-branch-label-scheme=func-sig -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-FUNC-SIG
+// RUN: %clang --target=riscv64 -fcf-protection=branch -mcf-branch-label-scheme=unlabeled -### %s -fsyntax-only 2>&1 | FileCheck %s -check-prefixes=ZICFILP-UNLABELED
+// ZICFILP-FUNC-SIG-NOT: "-target-feature" "-zicfilp-unlabeled"
+// ZICFILP-FUNC-SIG:     "-target-feature" "+zicfilp-func-sig"
+// ZICFILP-FUNC-SIG-NOT: "-target-feature" "-zicfilp-unlabeled"
+// ZICFILP-UNLABELED-NOT: "-target-feature" "-zicfilp-func-sig"
+// ZICFILP-UNLABELED:     "-target-feature" "+zicfilp-unlabeled"
+// ZICFILP-UNLABELED-NOT: "-target-feature" "-zicfilp-func-sig"
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
index 5abb5461f74b3..065ff48a38ab4 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
@@ -134,6 +134,10 @@ void validate(const Triple &TT, const FeatureBitset &FeatureBits) {
   if (FeatureBits[RISCV::Feature32Bit] &&
       FeatureBits[RISCV::Feature64Bit])
     reportFatalUsageError("RV32 and RV64 can't be combined");
+  if (FeatureBits[RISCV::FeatureZicfilpFuncSig] &&
+      FeatureBits[RISCV::FeatureZicfilpUnlabeled])
+    reportFatalUsageError(
+        "+zicfilp-func-sig and +zicfilp-unlabeled can't be combined");
 }
 
 llvm::Expected<std::unique_ptr<RISCVISAInfo>>
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index eb0749163cc0e..2211cbf03a7db 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -170,8 +170,6 @@ def FeatureStdExtZicfilp
 def HasStdExtZicfilp : Predicate<"Subtarget->hasStdExtZicfilp()">,
                        AssemblerPredicate<(all_of FeatureStdExtZicfilp),
                                           "'Zicfilp' (Landing pad)">;
-def HasZicfilpCFI : Predicate<"Subtarget->hasZicfilpCFI()">;
-def HasNoZicfilpCFI : Predicate<"!Subtarget->hasZicfilpCFI()">;
 
 def FeatureStdExtZicfiss
     : RISCVExperimentalExtension<1, 0, "Shadow stack",
@@ -1802,3 +1800,15 @@ def FeatureTaggedGlobals : SubtargetFeature<"tagged-globals",
     "AllowTaggedGlobals",
     "true", "Use an instruction sequence for taking the address of a global "
     "that allows a memory tag in the upper address bits">;
+
+// Zicfilp-based CFI
+def FeatureZicfilpUnlabeled
+    : SubtargetFeature<
+          "zicfilp-unlabeled", "HasZicfilpUnlabeled", "true",
+          "Enforce forward-edge control-flow integrity with ZICFILP-unlabeled">;
+def FeatureZicfilpFuncSig
+    : SubtargetFeature<
+          "zicfilp-func-sig", "HasZicfilpFuncSig", "true",
+          "Enforce forward-edge control-flow integrity with ZICFILP-func-sig">;
+def HasZicfilpCFI : Predicate<"Subtarget->hasZicfilpCFI()">;
+def HasNoZicfilpCFI : Predicate<"!Subtarget->hasZicfilpCFI()">;
diff --git a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
index f795031a866f1..fdf4e43f94f3a 100644
--- a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
+++ b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
@@ -70,7 +70,7 @@ bool RISCVIndirectBranchTracking::runOnMachineFunction(MachineFunction &MF) {
   const RISCVInstrInfo *TII = Subtarget.getInstrInfo();
 
   if (Subtarget.getZicfilpLabelScheme() !=
-      RISCVSubtarget::ZicfilpLabelSchemeKind::Unlabeled)
+      RISCVSubtarget::ZicfilpLabelSchemeEnum::Unlabeled)
     reportFatalUsageError(
         "only cf-branch-label-scheme=unlabeled is supported for now");
 
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
index 6098f3c10296f..e35ffaf2b3935 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp
@@ -99,11 +99,9 @@ RISCVSubtarget::RISCVSubtarget(const Triple &TT, StringRef CPU,
                                StringRef TuneCPU, StringRef FS,
                                StringRef ABIName, unsigned RVVVectorBitsMin,
                                unsigned RVVVectorBitsMax,
-                               RISCVSubtarget::ZicfilpLabelSchemeKind Zicfilp,
                                const TargetMachine &TM)
     : RISCVGenSubtargetInfo(TT, CPU, TuneCPU, FS),
       RVVVectorBitsMin(RVVVectorBitsMin), RVVVectorBitsMax(RVVVectorBitsMax),
-      ZicfilpLabelScheme(Zicfilp),
       FrameLowering(
           initializeSubtargetDependencies(TT, CPU, TuneCPU, FS, ABIName)),
       InstrInfo(*this), RegInfo(getHwMode()), TLInfo(TM, *this) {
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index bf545ae594e4a..1ae5e93737527 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -92,9 +92,8 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
   };
   // clang-format on
 
-  enum class ZicfilpLabelSchemeKind {
+  enum class ZicfilpLabelSchemeEnum {
     Disabled,
-    EnabledUnknown,
     Unlabeled,
     FuncSig,
   };
@@ -115,7 +114,6 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
   unsigned RVVVectorBitsMax;
   uint8_t MaxInterleaveFactor = 2;
   RISCVABI::ABI TargetABI = RISCVABI::ABI_Unknown;
-  const ZicfilpLabelSchemeKind ZicfilpLabelScheme;
   std::bitset<RISCV::NUM_TARGET_REGS> UserReservedRegister;
   const RISCVTuneInfoTable::RISCVTuneInfo *TuneInfo;
 
@@ -136,9 +134,7 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
   // Initializes the data members to match that of the specified triple.
   RISCVSubtarget(const Triple &TT, StringRef CPU, StringRef TuneCPU,
                  StringRef FS, StringRef ABIName, unsigned RVVVectorBitsMin,
-                 unsigned RVVVectorLMULMax,
-                 ZicfilpLabelSchemeKind ZicfilpLabelScheme,
-                 const TargetMachine &TM);
+                 unsigned RVVVectorLMULMax, const TargetMachine &TM);
 
   ~RISCVSubtarget() override;
 
@@ -197,11 +193,15 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
     return HasStdExtZfhmin || HasStdExtZfbfmin;
   }
 
-  ZicfilpLabelSchemeKind getZicfilpLabelScheme() const {
-    return ZicfilpLabelScheme;
+  ZicfilpLabelSchemeEnum getZicfilpLabelScheme() const {
+    if (hasZicfilpFuncSig())
+      return ZicfilpLabelSchemeEnum::FuncSig;
+    if (hasZicfilpUnlabeled())
+      return ZicfilpLabelSchemeEnum::Unlabeled;
+    return ZicfilpLabelSchemeEnum::Disabled;
   }
   bool hasZicfilpCFI() const {
-    return getZicfilpLabelScheme() != ZicfilpLabelSchemeKind::Disabled;
+    return getZicfilpLabelScheme() != ZicfilpLabelSchemeEnum::Disabled;
   }
 
   bool hasConditionalMoveFusion() const {
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index ab4f523205af1..5231211c2da26 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -17,9 +17,7 @@
 #include "RISCVTargetObjectFile.h"
 #include "RISCVTargetTransformInfo.h"
 #include "TargetInfo/RISCVTargetInfo.h"
-#include "llvm/ADT/STLForwardCompat.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/CodeGen/GlobalISel/CSEInfo.h"
 #include "llvm/CodeGen/GlobalISel/IRTranslator.h"
@@ -46,7 +44,6 @@
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/Vectorize/EVLIndVarSimplify.h"
 #include "llvm/Transforms/Vectorize/LoopIdiomVectorize.h"
-#include <cassert>
 #include <optional>
 using namespace llvm;
 
@@ -253,36 +250,9 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
   RVVBitsMax =
       llvm::bit_floor((RVVBitsMax < 64 || RVVBitsMax > 65536) ? 0 : RVVBitsMax);
 
-  using ZicfilpLabelSchemeKind = RISCVSubtarget::ZicfilpLabelSchemeKind;
-  ZicfilpLabelSchemeKind ZicfilpLabelScheme = ZicfilpLabelSchemeKind::Disabled;
-  if (const Module *const M = F.getParent()) {
-    if (const Metadata *const Flag = M->getModuleFlag("cf-protection-branch");
-        Flag && !mdconst::extract<ConstantInt>(Flag)->isZero()) {
-      StringRef CFBranchLabelScheme;
-      if (const Metadata *const MD = M->getModuleFlag("cf-branch-label-scheme"))
-        CFBranchLabelScheme = cast<MDString>(MD)->getString();
-      else
-        report_fatal_error("missing cf-branch-label-scheme module flag");
-
-      // See clang::getCFBranchLabelSchemeFlagVal() for possible
-      // CFBranchLabelScheme
-      ZicfilpLabelScheme =
-          StringSwitch<ZicfilpLabelSchemeKind>(CFBranchLabelScheme)
-              .Case("unlabeled", ZicfilpLabelSchemeKind::Unlabeled)
-              .Case("func-sig", ZicfilpLabelSchemeKind::FuncSig)
-              .Default(ZicfilpLabelSchemeKind::EnabledUnknown);
-      assert(ZicfilpLabelScheme != ZicfilpLabelSchemeKind::EnabledUnknown);
-    }
-  }
-
   SmallString<512> Key;
-  {
-    raw_svector_ostream OS(Key);
-    OS << "RVVMin" << RVVBitsMin << "RVVMax" << RVVBitsMax;
-    if (ZicfilpLabelScheme != ZicfilpLabelSchemeKind::Disabled)
-      OS << "ZicfilpLabelSchemeKind" << to_underlying(ZicfilpLabelScheme);
-    OS << CPU << TuneCPU << FS;
-  }
+  raw_svector_ostream(Key) << "RVVMin" << RVVBitsMin << "RVVMax" << RVVBitsMax
+                           << CPU << TuneCPU << FS;
   auto &I = SubtargetMap[Key];
   if (!I) {
     // This needs to be done before we create a new subtarget since any
@@ -299,9 +269,35 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
       }
       ABIName = ModuleTargetABI->getString();
     }
-    I = std::make_unique<RISCVSubtarget>(TargetTriple, CPU, TuneCPU, FS,
-                                         ABIName, RVVBitsMin, RVVBitsMax,
-                                         ZicfilpLabelScheme, *this);
+    I = std::make_unique<RISCVSubtarget>(
+        TargetTriple, CPU, TuneCPU, FS, ABIName, RVVBitsMin, RVVBitsMax, *this);
+
+    const Module &M = *F.getParent();
+    if (const Metadata *CF = M.getModuleFlag("cf-protection-branch");
+        CF && !mdconst::extract<ConstantInt>(CF)->isZero()) {
+      StringRef LabelScheme;
+      if (const Metadata *MD = M.getModuleFlag("cf-branch-label-scheme")) {
+        LabelScheme = cast<MDString>(MD)->getString();
+        if (LabelScheme != "func-sig" && LabelScheme != "unlabeled")
+          reportFatalUsageError("cf-branch-label-scheme=" + LabelScheme +
+                                " module flag is unsupported");
+      } else {
+        reportFatalUsageError("missing cf-branch-label-scheme module flag");
+      }
+
+      using ZicfilpLabelSchemeEnum = RISCVSubtarget::ZicfilpLabelSchemeEnum;
+      const ZicfilpLabelSchemeEnum SupportedScheme = I->getZicfilpLabelScheme();
+      if ((SupportedScheme != ZicfilpLabelSchemeEnum::FuncSig &&
+           LabelScheme == "func-sig") ||
+          (SupportedScheme != ZicfilpLabelSchemeEnum::Unlabeled &&
+           LabelScheme == "unlabeled"))
+        reportFatalUsageError(
+            "require target feature (+zicfilp-" + LabelScheme +
+            ") to handle cf-branch-label-scheme=" + LabelScheme +
+            " module flag");
+    } else if (I->hasZicfilpCFI()) {
+      reportFatalUsageError("require cf-protection-branch != 0 module flag");
+    }
   }
   return I.get();
 }
diff --git a/llvm/test/CodeGen/RISCV/features-info.ll b/llvm/test/CodeGen/RISCV/features-info.ll
index b94665b718ae7..0d2887e506d44 100644
--- a/llvm/test/CodeGen/RISCV/features-info.ll
+++ b/llvm/test/CodeGen/RISCV/features-info.ll
@@ -263,6 +263,8 @@
 ; CHECK-NEXT:   ziccif                           - 'Ziccif' (Main Memory Supports Instruction Fetch with Atomicity Requirement).
 ; CHECK-NEXT:   zicclsm                          - 'Zicclsm' (Main Memory Supports Misaligned Loads/Stores).
 ; CHECK-NEXT:   ziccrse                          - 'Ziccrse' (Main Memory Supports Forward Progress on LR/SC Sequences).
+; CHECK-NEXT:   zicfilp-func-sig                 - Enforce forward-edge control-flow integrity with ZICFILP-func-sig.
+; CHECK-NEXT:   zicfilp-unlabeled                - Enforce forward-edge control-flow integrity with ZICFILP-unlabeled.
 ; CHECK-NEXT:   zicntr                           - 'Zicntr' (Base Counters and Timers).
 ; CHECK-NEXT:   zicond                           - 'Zicond' (Integer Conditional Operations).
 ; CHECK-NEXT:   zicsr                            - 'Zicsr' (CSRs).
diff --git a/llvm/test/CodeGen/RISCV/jumptable-swguarded.ll b/llvm/test/CodeGen/RISCV/jumptable-swguarded.ll
index 32e3af6af5ad3..bd340c617423a 100644
--- a/llvm/test/CodeGen/RISCV/jumptable-swguarded.ll
+++ b/llvm/test/CodeGen/RISCV/jumptable-swguarded.ll
@@ -1,5 +1,5 @@
-; RUN: llc -mtriple riscv32 < %s | FileCheck %s
-; RUN: llc -mtriple riscv64 < %s | FileCheck %s
+; RUN: llc -mtriple riscv32 -mattr=+zicfilp-unlabeled < %s | FileCheck %s
+; RUN: llc -mtriple riscv64 -mattr=+zicfilp-unlabeled < %s | FileCheck %s
 
 ; Test using t2 to jump table branch.
 define void @above_threshold(i32 signext %in, ptr %out) nounwind {
diff --git a/llvm/test/CodeGen/RISCV/lpad.ll b/llvm/test/CodeGen/RISCV/lpad.ll
index 4676d06a29725..de9ddf5deb450 100644
--- a/llvm/test/CodeGen/RISCV/lpad.ll
+++ b/llvm/test/CodeGen/RISCV/lpad.ll
@@ -1,8 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple riscv32 < %s | FileCheck %s --check-prefixes=CHECK,RV32
-; RUN: llc -mtriple riscv64 < %s | FileCheck %s --check-prefixes=CHECK,RV64
-; RUN: llc -mtriple riscv32 -riscv-landing-pad-label=1 < %s | FileCheck %s --check-prefixes=FIXED-ONE,FIXED-ONE-RV32
-; RUN: llc -mtriple riscv64 -riscv-landing-pad-label=1 < %s | FileCheck %s --check-prefixes=FIXED-ONE,FIXED-ONE-RV64
+; RUN: llc -mtriple riscv32 -mattr=+zicfilp-unlabeled < %s | FileCheck %s --check-prefixes=CHECK,RV32
+; RUN: llc -mtriple riscv64 -mattr=+zicfilp-unlabeled < %s | FileCheck %s --check-prefixes=CHECK,RV64
+; RUN: llc -mtriple riscv32 -mattr=+zicfilp-unlabeled -riscv-landing-pad-label=1 < %s | FileCheck %s --check-prefixes=FIXED-ONE,FIXED-ONE-RV32
+; RUN: llc -mtriple riscv64 -mattr=+zicfilp-unlabeled -riscv-landing-pad-label=1 < %s | FileCheck %s --check-prefixes=FIXED-ONE,FIXED-ONE-RV64
 
 ; Check indirectbr.
 @__const.indirctbr.addr = private unnamed_addr constant [2 x ptr] [ptr blockaddress(@indirctbr, %labelA), ptr blockaddress(@indirctbr, %labelB)], align 8
diff --git a/llvm/test/CodeGen/RISCV/rv64-trampoline-cfi.ll b/llvm/test/CodeGen/RISCV/rv64-trampoline-cfi.ll
index a50fa1924718b..b63b9765c6f81 100644
--- a/llvm/test/CodeGen/RISCV/rv64-trampoline-cfi.ll
+++ b/llvm/test/CodeGen/RISCV/rv64-trampoline-cfi.ll
@@ -1,6 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc -O0 -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck -check-prefix=RV64 %s
-; RUN: llc -O0 -mtriple=riscv64-unknown-linux-gnu -verify-machineinstrs < %s | FileCheck -check-prefix=RV64-LINUX %s
+; RUN: llc -O0 -mtriple=riscv64 -mattr=+zicfilp-unlabeled -verify-machineinstrs < %s | FileCheck -check-prefix=RV64 %s
+; RUN: llc -O0 -mtriple=riscv64-unknown-linux-gnu -mattr=+zicfilp-unlabeled -verify-machineinstrs < %s | FileCheck -check-prefix=RV64-LINUX %s
 
 declare void @llvm.init.trampoline(ptr, ptr, ptr)
 declare ptr @llvm.adjust.trampoline(ptr)
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll b/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
index 277f0d67b1d93..c3a9a8b07687c 100644
--- a/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
+++ b/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
-; RUN: llc -mtriple=riscv64 -stop-after=finalize-isel < %s | FileCheck %s
+; RUN: llc -mtriple=riscv64 -mattr=+zicfilp-unlabeled -stop-after=finalize-isel < %s | FileCheck %s
 
 @brind.arr = internal unnamed_addr constant [2 x ptr] [ptr blockaddress(@brind, %5), ptr blockaddress(@brind, %8)], align 8
 @x = dso_local global i32 0, align 4
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-calls.ll b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-calls.ll
index 0cd8dff8cd02b..9e34f28792c0f 100644
--- a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-calls.ll
+++ b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-calls.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -code-model=large -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN: llc -code-model=large -mtriple=riscv64 -mattr=+zicfilp-unlabeled -verify-machineinstrs < %s \
 ; RUN:   | FileCheck %s
 
 declare i32 @external_function(i32)
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-nest-register.ll b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-nest-register.ll
index 89454e8f830b1..a90c470ff23a4 100644
--- a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-nest-register.ll
+++ b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-nest-register.ll
@@ -1,7 +1,7 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | \
+; RUN: llc -mtriple=riscv64 -mattr=+zicfilp-unlabeled -verify-machineinstrs < %s | \
 ; RUN:    FileCheck -check-prefix=RV64I %s
-; RUN: not llc -mtriple=riscv64 -target-abi=lp64e -verify-machineinstrs < %s \
+; RUN: not llc -mtriple=riscv64 -mattr=+zicfilp-unlabeled -target-abi=lp64e -verify-machineinstrs < %s \
 ; RUN:    2>&1 | FileCheck -check-prefix=LP64E %s
 
 ; Tests that the 'nest' parameter attribute causes the relevant parameter to be
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-tail-calls.ll b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-tail-calls.ll
index 8cfc83bb11a4c..d444ef7951d2b 100644
--- a/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-tail-calls.ll
+++ b/llvm/test/CodeGen/RISCV/zicfilp-unlabeled-tail-calls.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple riscv32-unknown-linux-gnu -code-model=large -o - %s | FileCheck %s
+; RUN: llc -mtriple riscv32-unknown-linux-gnu -mattr=+zicfilp-unlabeled -code-model=large -o - %s | FileCheck %s
 
 ; Perform tail call optimization for global address.
 declare i32 @callee_tail(i32 %i)
diff --git a/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp b/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp
index 3b6a7a143f351..2744dbc0907bf 100644
--- a/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp
+++ b/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp
@@ -61,8 +61,7 @@ class RISCVInstrInfoTest : public testing::TestWithParam<const char *> {
     ST = std::make_unique<RISCVSubtarget>(
         TM->getTargetTriple(), TM->getTargetCPU(), TM->getTargetCPU(),
         TM->getTargetFeatureString(),
-        TM->getTargetTriple().isArch64Bit() ? "lp64" : "ilp32", 0, 0,
-        RISCVSubtarget::ZicfilpLabelSchemeKind::Disabled, *TM);
+        TM->getTargetTriple().isArch64Bit() ? "lp64" : "ilp32", 0, 0, *TM);
 
     MF = std::make_unique<MachineFunction>(*F, *TM, *ST, MMI->getContext(), 42);
   }

>From ef32dbccb6c54297fa0d3593b3473bb06f1e2d76 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Fri, 22 Aug 2025 11:52:21 +0800
Subject: [PATCH 12/16] fixup: Enforce module flag cf-protection-branch != 0
 <-> Subtarget.hasZicfilpCFI() even if new Subtarget is not created

---
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 5231211c2da26..aeea19b5f85b6 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -271,12 +271,14 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
     }
     I = std::make_unique<RISCVSubtarget>(
         TargetTriple, CPU, TuneCPU, FS, ABIName, RVVBitsMin, RVVBitsMax, *this);
+  }
 
-    const Module &M = *F.getParent();
-    if (const Metadata *CF = M.getModuleFlag("cf-protection-branch");
+  if (const Module *const M = F.getParent()) {
+    if (const Metadata *const CF = M->getModuleFlag("cf-protection-branch");
         CF && !mdconst::extract<ConstantInt>(CF)->isZero()) {
       StringRef LabelScheme;
-      if (const Metadata *MD = M.getModuleFlag("cf-branch-label-scheme")) {
+      if (const Metadata *const MD =
+              M->getModuleFlag("cf-branch-label-scheme")) {
         LabelScheme = cast<MDString>(MD)->getString();
         if (LabelScheme != "func-sig" && LabelScheme != "unlabeled")
           reportFatalUsageError("cf-branch-label-scheme=" + LabelScheme +

>From 20db445e98030ce0c4c99257ba2e5fd9427c1e32 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Mon, 25 Aug 2025 17:11:25 +0800
Subject: [PATCH 13/16] fixup: Rename HasNoZicfilpCFI to NoZicfilpCFI

---
 llvm/lib/Target/RISCV/RISCVFeatures.td  | 2 +-
 llvm/lib/Target/RISCV/RISCVInstrInfo.td | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index 2211cbf03a7db..010960aaa9c21 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1811,4 +1811,4 @@ def FeatureZicfilpFuncSig
           "zicfilp-func-sig", "HasZicfilpFuncSig", "true",
           "Enforce forward-edge control-flow integrity with ZICFILP-func-sig">;
 def HasZicfilpCFI : Predicate<"Subtarget->hasZicfilpCFI()">;
-def HasNoZicfilpCFI : Predicate<"!Subtarget->hasZicfilpCFI()">;
+def NoZicfilpCFI : Predicate<"!Subtarget->hasZicfilpCFI()">;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 610edf126f0c5..6855f8d078e67 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -1746,7 +1746,7 @@ let isBarrier = 1, isBranch = 1, isTerminator = 1 in
 def PseudoBR : Pseudo<(outs), (ins simm21_lsb0_jal:$imm20), [(br bb:$imm20)]>,
                PseudoInstExpansion<(JAL X0, simm21_lsb0_jal:$imm20)>;
 
-let Predicates = [HasNoZicfilpCFI],
+let Predicates = [NoZicfilpCFI],
     isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in
 def PseudoBRIND : Pseudo<(outs), (ins GPRJALR:$rs1, simm12:$imm12), []>,
                   PseudoInstExpansion<(JALR X0, GPR:$rs1, simm12:$imm12)>;
@@ -1771,7 +1771,7 @@ def : Pat<(riscv_sw_guarded_brind (add GPRX7:$rs1, simm12:$imm12)),
           (PseudoBRINDX7 GPRX7:$rs1, simm12:$imm12)>;
 }
 
-let Predicates = [HasNoZicfilpCFI] in {
+let Predicates = [NoZicfilpCFI] in {
 def : Pat<(brind GPRJALR:$rs1), (PseudoBRIND GPRJALR:$rs1, 0)>;
 def : Pat<(brind (add GPRJALR:$rs1, simm12:$imm12)),
           (PseudoBRIND GPRJALR:$rs1, simm12:$imm12)>;
@@ -1808,7 +1808,7 @@ let Predicates = [HasStdExtSmrnmi] in
 def : Pat<(riscv_mnret_glue), (MNRET)>;
 
 let isCall = 1, Defs = [X1] in {
-let Predicates = [HasNoZicfilpCFI] in
+let Predicates = [NoZicfilpCFI] in
 def PseudoCALLIndirect : Pseudo<(outs), (ins GPRJALR:$rs1),
                                 [(riscv_call GPRJALR:$rs1)]>,
                          PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>;
@@ -1837,7 +1837,7 @@ def PseudoTAIL : Pseudo<(outs), (ins call_symbol:$dst), [],
                  Sched<[WriteIALU, WriteJalr, ReadJalr]>;
 
 let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [X2] in {
-let Predicates = [HasNoZicfilpCFI] in
+let Predicates = [NoZicfilpCFI] in
 def PseudoTAILIndirect : Pseudo<(outs), (ins GPRTC:$rs1),
                                 [(riscv_tail GPRTC:$rs1)]>,
                          PseudoInstExpansion<(JALR X0, GPR:$rs1, 0)>;

>From c583a620bce69711f9bb626fdb5da57bae762aeb Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Mon, 25 Aug 2025 17:31:11 +0800
Subject: [PATCH 14/16] fixup: Fix utils/update_llc_test_checks.py annotations

---
 llvm/test/CodeGen/RISCV/lpad.ll               | 24 +++++++++----------
 .../RISCV/zicfilp-disabled-indirect-branch.ll |  1 -
 .../CodeGen/RISCV/zicfilp-indirect-branch.ll  |  1 -
 3 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/llvm/test/CodeGen/RISCV/lpad.ll b/llvm/test/CodeGen/RISCV/lpad.ll
index de9ddf5deb450..0cc9fbc587807 100644
--- a/llvm/test/CodeGen/RISCV/lpad.ll
+++ b/llvm/test/CodeGen/RISCV/lpad.ll
@@ -143,9 +143,9 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; RV32-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
 ; RV32-NEXT:    .cfi_offset ra, -4
 ; RV32-NEXT:    .cfi_remember_state
-; RV32-NEXT:  .Ltmp0:
+; RV32-NEXT:  .Ltmp0: # EH_LABEL
 ; RV32-NEXT:    jalr a0
-; RV32-NEXT:  .Ltmp1:
+; RV32-NEXT:  .Ltmp1: # EH_LABEL
 ; RV32-NEXT:  .LBB2_1: # %try.cont
 ; RV32-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; RV32-NEXT:    .cfi_restore ra
@@ -154,7 +154,7 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; RV32-NEXT:    ret
 ; RV32-NEXT:  .LBB2_2: # %lpad
 ; RV32-NEXT:    .cfi_restore_state
-; RV32-NEXT:  .Ltmp2:
+; RV32-NEXT:  .Ltmp2: # EH_LABEL
 ; RV32-NEXT:    j .LBB2_1
 ;
 ; RV64-LABEL: invoke:
@@ -165,9 +165,9 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; RV64-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
 ; RV64-NEXT:    .cfi_offset ra, -8
 ; RV64-NEXT:    .cfi_remember_state
-; RV64-NEXT:  .Ltmp0:
+; RV64-NEXT:  .Ltmp0: # EH_LABEL
 ; RV64-NEXT:    jalr a0
-; RV64-NEXT:  .Ltmp1:
+; RV64-NEXT:  .Ltmp1: # EH_LABEL
 ; RV64-NEXT:  .LBB2_1: # %try.cont
 ; RV64-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
 ; RV64-NEXT:    .cfi_restore ra
@@ -176,7 +176,7 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; RV64-NEXT:    ret
 ; RV64-NEXT:  .LBB2_2: # %lpad
 ; RV64-NEXT:    .cfi_restore_state
-; RV64-NEXT:  .Ltmp2:
+; RV64-NEXT:  .Ltmp2: # EH_LABEL
 ; RV64-NEXT:    j .LBB2_1
 ;
 ; FIXED-ONE-RV32-LABEL: invoke:
@@ -187,10 +187,10 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; FIXED-ONE-RV32-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
 ; FIXED-ONE-RV32-NEXT:    .cfi_offset ra, -4
 ; FIXED-ONE-RV32-NEXT:    .cfi_remember_state
-; FIXED-ONE-RV32-NEXT:  .Ltmp0:
+; FIXED-ONE-RV32-NEXT:  .Ltmp0: # EH_LABEL
 ; FIXED-ONE-RV32-NEXT:    lui t2, 1
 ; FIXED-ONE-RV32-NEXT:    jalr a0
-; FIXED-ONE-RV32-NEXT:  .Ltmp1:
+; FIXED-ONE-RV32-NEXT:  .Ltmp1: # EH_LABEL
 ; FIXED-ONE-RV32-NEXT:  .LBB2_1: # %try.cont
 ; FIXED-ONE-RV32-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
 ; FIXED-ONE-RV32-NEXT:    .cfi_restore ra
@@ -199,7 +199,7 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; FIXED-ONE-RV32-NEXT:    ret
 ; FIXED-ONE-RV32-NEXT:  .LBB2_2: # %lpad
 ; FIXED-ONE-RV32-NEXT:    .cfi_restore_state
-; FIXED-ONE-RV32-NEXT:  .Ltmp2:
+; FIXED-ONE-RV32-NEXT:  .Ltmp2: # EH_LABEL
 ; FIXED-ONE-RV32-NEXT:    j .LBB2_1
 ;
 ; FIXED-ONE-RV64-LABEL: invoke:
@@ -210,10 +210,10 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; FIXED-ONE-RV64-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
 ; FIXED-ONE-RV64-NEXT:    .cfi_offset ra, -8
 ; FIXED-ONE-RV64-NEXT:    .cfi_remember_state
-; FIXED-ONE-RV64-NEXT:  .Ltmp0:
+; FIXED-ONE-RV64-NEXT:  .Ltmp0: # EH_LABEL
 ; FIXED-ONE-RV64-NEXT:    lui t2, 1
 ; FIXED-ONE-RV64-NEXT:    jalr a0
-; FIXED-ONE-RV64-NEXT:  .Ltmp1:
+; FIXED-ONE-RV64-NEXT:  .Ltmp1: # EH_LABEL
 ; FIXED-ONE-RV64-NEXT:  .LBB2_1: # %try.cont
 ; FIXED-ONE-RV64-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
 ; FIXED-ONE-RV64-NEXT:    .cfi_restore ra
@@ -222,7 +222,7 @@ define void @invoke(ptr %f) personality ptr @__gxx_personality_v0 {
 ; FIXED-ONE-RV64-NEXT:    ret
 ; FIXED-ONE-RV64-NEXT:  .LBB2_2: # %lpad
 ; FIXED-ONE-RV64-NEXT:    .cfi_restore_state
-; FIXED-ONE-RV64-NEXT:  .Ltmp2:
+; FIXED-ONE-RV64-NEXT:  .Ltmp2: # EH_LABEL
 ; FIXED-ONE-RV64-NEXT:    j .LBB2_1
 entry:
   invoke void %f() to label %try.cont unwind label %lpad
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-disabled-indirect-branch.ll b/llvm/test/CodeGen/RISCV/zicfilp-disabled-indirect-branch.ll
index 897260908075c..05982be23931f 100644
--- a/llvm/test/CodeGen/RISCV/zicfilp-disabled-indirect-branch.ll
+++ b/llvm/test/CodeGen/RISCV/zicfilp-disabled-indirect-branch.ll
@@ -1,4 +1,3 @@
-; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
 ; RUN: llc -mtriple=riscv64 -stop-after=finalize-isel < %s | FileCheck %s
 
 @brind.arr = internal unnamed_addr constant [2 x ptr] [ptr blockaddress(@brind, %5), ptr blockaddress(@brind, %8)], align 8
diff --git a/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll b/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
index c3a9a8b07687c..299bb76d9139a 100644
--- a/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
+++ b/llvm/test/CodeGen/RISCV/zicfilp-indirect-branch.ll
@@ -1,4 +1,3 @@
-; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
 ; RUN: llc -mtriple=riscv64 -mattr=+zicfilp-unlabeled -stop-after=finalize-isel < %s | FileCheck %s
 
 @brind.arr = internal unnamed_addr constant [2 x ptr] [ptr blockaddress(@brind, %5), ptr blockaddress(@brind, %8)], align 8

>From 303f8a7822b09495883e9cec8a45c3a20405ddfe Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Tue, 26 Aug 2025 17:56:08 +0800
Subject: [PATCH 15/16] fixup: Implement +zicfilp-func-sig and
 +zicfilp-unlabeled using enums

---
 llvm/lib/Target/RISCV/RISCVFeatures.td        |  4 ++--
 .../RISCV/RISCVIndirectBranchTracking.cpp     |  3 +--
 llvm/lib/Target/RISCV/RISCVSubtarget.h        | 22 ++++++++-----------
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp  |  8 +++----
 4 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index 010960aaa9c21..9437d85206753 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1804,11 +1804,11 @@ def FeatureTaggedGlobals : SubtargetFeature<"tagged-globals",
 // Zicfilp-based CFI
 def FeatureZicfilpUnlabeled
     : SubtargetFeature<
-          "zicfilp-unlabeled", "HasZicfilpUnlabeled", "true",
+          "zicfilp-unlabeled", "RISCVZicfilpCFIScheme", "ZicfilpUnlabeled",
           "Enforce forward-edge control-flow integrity with ZICFILP-unlabeled">;
 def FeatureZicfilpFuncSig
     : SubtargetFeature<
-          "zicfilp-func-sig", "HasZicfilpFuncSig", "true",
+          "zicfilp-func-sig", "RISCVZicfilpCFIScheme", "ZicfilpFuncSig",
           "Enforce forward-edge control-flow integrity with ZICFILP-func-sig">;
 def HasZicfilpCFI : Predicate<"Subtarget->hasZicfilpCFI()">;
 def NoZicfilpCFI : Predicate<"!Subtarget->hasZicfilpCFI()">;
diff --git a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
index fdf4e43f94f3a..ca120abc527f9 100644
--- a/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
+++ b/llvm/lib/Target/RISCV/RISCVIndirectBranchTracking.cpp
@@ -69,8 +69,7 @@ bool RISCVIndirectBranchTracking::runOnMachineFunction(MachineFunction &MF) {
 
   const RISCVInstrInfo *TII = Subtarget.getInstrInfo();
 
-  if (Subtarget.getZicfilpLabelScheme() !=
-      RISCVSubtarget::ZicfilpLabelSchemeEnum::Unlabeled)
+  if (Subtarget.getZicfilpCFIScheme() != RISCVSubtarget::ZicfilpUnlabeled)
     reportFatalUsageError(
         "only cf-branch-label-scheme=unlabeled is supported for now");
 
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index 1ae5e93737527..2342e026ba49d 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -90,19 +90,19 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
     Quadratic,
     NLog2N,
   };
-  // clang-format on
-
-  enum class ZicfilpLabelSchemeEnum {
-    Disabled,
-    Unlabeled,
-    FuncSig,
+  enum RISCVZicfilpCFISchemeEnum : uint8_t {
+    ZicfilpDisabled,
+    ZicfilpUnlabeled,
+    ZicfilpFuncSig,
   };
+  // clang-format on
 
 private:
   virtual void anchor();
 
   RISCVProcFamilyEnum RISCVProcFamily = Others;
   RISCVVRGatherCostModelEnum RISCVVRGatherCostModel = Quadratic;
+  RISCVZicfilpCFISchemeEnum RISCVZicfilpCFIScheme = ZicfilpDisabled;
 
 #define GET_SUBTARGETINFO_MACRO(ATTRIBUTE, DEFAULT, GETTER) \
   bool ATTRIBUTE = DEFAULT;
@@ -193,15 +193,11 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
     return HasStdExtZfhmin || HasStdExtZfbfmin;
   }
 
-  ZicfilpLabelSchemeEnum getZicfilpLabelScheme() const {
-    if (hasZicfilpFuncSig())
-      return ZicfilpLabelSchemeEnum::FuncSig;
-    if (hasZicfilpUnlabeled())
-      return ZicfilpLabelSchemeEnum::Unlabeled;
-    return ZicfilpLabelSchemeEnum::Disabled;
+  RISCVZicfilpCFISchemeEnum getZicfilpCFIScheme() const {
+    return RISCVZicfilpCFIScheme;
   }
   bool hasZicfilpCFI() const {
-    return getZicfilpLabelScheme() != ZicfilpLabelSchemeEnum::Disabled;
+    return getZicfilpCFIScheme() != ZicfilpDisabled;
   }
 
   bool hasConditionalMoveFusion() const {
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index aeea19b5f85b6..576ebd8c7f1eb 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -287,11 +287,11 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
         reportFatalUsageError("missing cf-branch-label-scheme module flag");
       }
 
-      using ZicfilpLabelSchemeEnum = RISCVSubtarget::ZicfilpLabelSchemeEnum;
-      const ZicfilpLabelSchemeEnum SupportedScheme = I->getZicfilpLabelScheme();
-      if ((SupportedScheme != ZicfilpLabelSchemeEnum::FuncSig &&
+      using CFISchemeEnum = RISCVSubtarget::RISCVZicfilpCFISchemeEnum;
+      const CFISchemeEnum SupportedScheme = I->getZicfilpCFIScheme();
+      if ((SupportedScheme != CFISchemeEnum::ZicfilpFuncSig &&
            LabelScheme == "func-sig") ||
-          (SupportedScheme != ZicfilpLabelSchemeEnum::Unlabeled &&
+          (SupportedScheme != CFISchemeEnum::ZicfilpUnlabeled &&
            LabelScheme == "unlabeled"))
         reportFatalUsageError(
             "require target feature (+zicfilp-" + LabelScheme +

>From 314fef8ce590271b7474ca62cfdafac01de7515c Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Wed, 27 Aug 2025 11:22:32 +0800
Subject: [PATCH 16/16] fixup: factor out reportBadZicfilpUsage

---
 llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 58 ++++++++++----------
 1 file changed, 30 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 576ebd8c7f1eb..82240958fdbfe 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -40,6 +40,7 @@
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Target/TargetOptions.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
 #include "llvm/Transforms/IPO.h"
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/Vectorize/EVLIndVarSimplify.h"
@@ -198,6 +199,33 @@ RISCVTargetMachine::RISCVTargetMachine(const Target &T, const Triple &TT,
   setCFIFixup(true);
 }
 
+static void reportBadZicfilpUsage(const FeatureBitset &Features,
+                                  const Module &M) {
+  if (const Metadata *const CF = M.getModuleFlag("cf-protection-branch");
+      CF && !mdconst::extract<ConstantInt>(CF)->isZero()) {
+    StringRef LabelScheme;
+    if (const Metadata *const MD = M.getModuleFlag("cf-branch-label-scheme")) {
+      LabelScheme = cast<MDString>(MD)->getString();
+      if (LabelScheme != "func-sig" && LabelScheme != "unlabeled")
+        reportFatalUsageError("cf-branch-label-scheme=" + LabelScheme +
+                              " module flag is unsupported");
+    } else {
+      reportFatalUsageError("missing cf-branch-label-scheme module flag");
+    }
+
+    if ((!Features[RISCV::FeatureZicfilpFuncSig] &&
+         LabelScheme == "func-sig") ||
+        (!Features[RISCV::FeatureZicfilpUnlabeled] &&
+         LabelScheme == "unlabeled"))
+      reportFatalUsageError(
+          "require target feature (+zicfilp-" + LabelScheme +
+          ") to handle cf-branch-label-scheme=" + LabelScheme + " module flag");
+  } else if (Features[RISCV::FeatureZicfilpFuncSig] ||
+             Features[RISCV::FeatureZicfilpUnlabeled]) {
+    reportFatalUsageError("require cf-protection-branch != 0 module flag");
+  }
+}
+
 const RISCVSubtarget *
 RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
   Attribute CPUAttr = F.getFnAttribute("target-cpu");
@@ -273,34 +301,8 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const {
         TargetTriple, CPU, TuneCPU, FS, ABIName, RVVBitsMin, RVVBitsMax, *this);
   }
 
-  if (const Module *const M = F.getParent()) {
-    if (const Metadata *const CF = M->getModuleFlag("cf-protection-branch");
-        CF && !mdconst::extract<ConstantInt>(CF)->isZero()) {
-      StringRef LabelScheme;
-      if (const Metadata *const MD =
-              M->getModuleFlag("cf-branch-label-scheme")) {
-        LabelScheme = cast<MDString>(MD)->getString();
-        if (LabelScheme != "func-sig" && LabelScheme != "unlabeled")
-          reportFatalUsageError("cf-branch-label-scheme=" + LabelScheme +
-                                " module flag is unsupported");
-      } else {
-        reportFatalUsageError("missing cf-branch-label-scheme module flag");
-      }
-
-      using CFISchemeEnum = RISCVSubtarget::RISCVZicfilpCFISchemeEnum;
-      const CFISchemeEnum SupportedScheme = I->getZicfilpCFIScheme();
-      if ((SupportedScheme != CFISchemeEnum::ZicfilpFuncSig &&
-           LabelScheme == "func-sig") ||
-          (SupportedScheme != CFISchemeEnum::ZicfilpUnlabeled &&
-           LabelScheme == "unlabeled"))
-        reportFatalUsageError(
-            "require target feature (+zicfilp-" + LabelScheme +
-            ") to handle cf-branch-label-scheme=" + LabelScheme +
-            " module flag");
-    } else if (I->hasZicfilpCFI()) {
-      reportFatalUsageError("require cf-protection-branch != 0 module flag");
-    }
-  }
+  if (const Module *const M = F.getParent())
+    reportBadZicfilpUsage(I->getFeatureBits(), *M);
   return I.get();
 }
 



More information about the llvm-commits mailing list