[llvm] d411212 - [RISCV] Add PseudoTAILX7, PseudoTAILReg for cf-protection-branch (#220657)

via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 12 04:13:14 PDT 2026


Author: Jesse Huang
Date: 2026-09-12T19:13:09+08:00
New Revision: d41121260c37b1be088995b375d074f198942114

URL: https://github.com/llvm/llvm-project/commit/d41121260c37b1be088995b375d074f198942114
DIFF: https://github.com/llvm/llvm-project/commit/d41121260c37b1be088995b375d074f198942114.diff

LOG: [RISCV] Add PseudoTAILX7, PseudoTAILReg for cf-protection-branch (#220657)

Previously, PseudoTAIL selected between X6 and X7 at encode time by
checking the Zicfilp feature bit via getTailExpandUseRegNo. This is
incorrect for the codegen path because -fcf-protection=branch can emit
lpad without requiring Zicfilp to be enabled.

Add PseudoTAILX7 for codegen: when cf-protection-branch is active,
ISelLowering and the machine outliner now emit PseudoTAILX7 which
always expands using X7. The existing PseudoTAIL behavior is unchanged
— it still selects X6 or X7 based on Zicfilp at encode time, as
documented in the assembler manual.

Also add PseudoTAILReg ("tail address, register") assembly form that
lets users explicitly specify the scratch register for AUIPC+JALR
expansion, per the proposed syntax in
https://github.com/riscv-non-isa/riscv-asm-manual/pull/227.

Added: 
    

Modified: 
    llvm/docs/ReleaseNotes.md
    llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
    llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
    llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
    llvm/lib/Target/RISCV/RISCVISelLowering.cpp
    llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
    llvm/lib/Target/RISCV/RISCVInstrInfo.td
    llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
    llvm/test/CodeGen/RISCV/tail-calls.ll
    llvm/test/MC/RISCV/tail-call.s

Removed: 
    


################################################################################
diff  --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 291b18444f73d..1fd90f5bd3be0 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -246,6 +246,8 @@ Makes programs 10x faster by doing Special New Thing.
 * Added support for `Sspmp`, `Sspmpen` and `Smpmpdeleg` extensions.
 * Removed veyron-v1 processor definition and tuning model.
 * Removed support for the `Ventana Conditional Operations` extension.
+* Added support for `tail symbol, rt` form that takes an address (materialisation)
+  register, that is used when software guarded branch is needed.
 
 ### Changes to the WebAssembly Backend
 

diff  --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 7d5c2adfc6648..4f1f3cbfb9b26 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -217,6 +217,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
   ParseStatus parseOperandWithSpecifier(OperandVector &Operands);
   ParseStatus parseBareSymbol(OperandVector &Operands);
   ParseStatus parseCallSymbol(OperandVector &Operands);
+  ParseStatus parseTailCallSymbol(OperandVector &Operands);
   ParseStatus parsePseudoJumpSymbol(OperandVector &Operands);
   ParseStatus parseJALOffset(OperandVector &Operands);
   ParseStatus parseVTypeI(OperandVector &Operands);
@@ -613,6 +614,8 @@ struct RISCVOperand final : public MCParsedAsmOperand {
            VK == RISCV::S_CALL_PLT;
   }
 
+  bool isTailCallSymbol() const { return isCallSymbol(); }
+
   bool isPseudoJumpSymbol() const {
     int64_t Imm;
     // Must be of 'immediate' type but not a constant.
@@ -2412,6 +2415,40 @@ ParseStatus RISCVAsmParser::parseCallSymbol(OperandVector &Operands) {
   return ParseStatus::Success;
 }
 
+// Like parseCallSymbol but allows the symbol to be followed by a comma
+// (for "tail address, register" form where the symbol is not the last operand).
+ParseStatus RISCVAsmParser::parseTailCallSymbol(OperandVector &Operands) {
+  SMLoc S = getLoc();
+  const MCExpr *Res;
+
+  if (getLexer().getKind() != AsmToken::Identifier)
+    return ParseStatus::NoMatch;
+  std::string Identifier(getTok().getIdentifier());
+
+  if (getLexer().peekTok().is(AsmToken::At)) {
+    Lex();
+    Lex();
+    StringRef PLT;
+    SMLoc Loc = getLoc();
+    if (getParser().parseIdentifier(PLT) || PLT != "plt")
+      return Error(Loc, "@ (except the deprecated/ignored @plt) is disallowed");
+  } else if (!getLexer().peekTok().is(AsmToken::EndOfStatement) &&
+             !getLexer().peekTok().is(AsmToken::Comma)) {
+    return ParseStatus::NoMatch;
+  } else {
+    Lex();
+  }
+
+  SMLoc E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());
+  RISCV::Specifier Kind = RISCV::S_CALL_PLT;
+
+  MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier);
+  Res = MCSymbolRefExpr::create(Sym, getContext());
+  Res = MCSpecifierExpr::create(Res, Kind, getContext());
+  Operands.push_back(RISCVOperand::createExpr(Res, S, E, isRV64()));
+  return ParseStatus::Success;
+}
+
 ParseStatus RISCVAsmParser::parsePseudoJumpSymbol(OperandVector &Operands) {
   SMLoc S = getLoc();
   SMLoc E;

diff  --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index 23cbbf0954cd7..a4d77ee6d2e51 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -176,6 +176,9 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
   if (MI.getOpcode() == RISCV::PseudoTAIL) {
     Func = MI.getOperand(0);
     Ra = RISCVII::getTailExpandUseRegNo(STI.getFeatureBits());
+  } else if (MI.getOpcode() == RISCV::PseudoTAILReg) {
+    Func = MI.getOperand(0);
+    Ra = MI.getOperand(1).getReg();
   } else if (MI.getOpcode() == RISCV::PseudoCALLReg) {
     Func = MI.getOperand(1);
     Ra = MI.getOperand(0).getReg();
@@ -195,6 +198,7 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
   if (STI.getTargetTriple().isOSBinFormatMachO()) {
     MCOperand FuncOp = MCOperand::createExpr(CallExpr);
     if (MI.getOpcode() == RISCV::PseudoTAIL ||
+        MI.getOpcode() == RISCV::PseudoTAILReg ||
         MI.getOpcode() == RISCV::PseudoJump)
       // Emit JAL X0, Func
       TmpInst = MCInstBuilder(RISCV::JAL).addReg(RISCV::X0).addOperand(FuncOp);
@@ -211,6 +215,7 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
   support::endian::write(CB, Binary, llvm::endianness::little);
 
   if (MI.getOpcode() == RISCV::PseudoTAIL ||
+      MI.getOpcode() == RISCV::PseudoTAILReg ||
       MI.getOpcode() == RISCV::PseudoJump)
     // Emit JALR X0, Ra, 0
     TmpInst = MCInstBuilder(RISCV::JALR).addReg(RISCV::X0).addReg(Ra).addImm(0);
@@ -573,6 +578,7 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
   case RISCV::PseudoCALLReg:
   case RISCV::PseudoCALL:
   case RISCV::PseudoTAIL:
+  case RISCV::PseudoTAILReg:
   case RISCV::PseudoJump:
     expandFunctionCall(MI, CB, Fixups, STI);
     MCNumEmitted += 2;

diff  --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index 6771e9d7e7fa1..fba93dede0d02 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -396,6 +396,17 @@ void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) {
   }
 
   switch (MI->getOpcode()) {
+  case RISCV::PseudoTAILX7: {
+    // Lower to PseudoTAILReg with X7 as the register operand.
+    MCOperand SymOp;
+    lowerOperand(MI->getOperand(0), SymOp);
+    MCInst TmpInst;
+    TmpInst.setOpcode(RISCV::PseudoTAILReg);
+    TmpInst.addOperand(SymOp);
+    TmpInst.addOperand(MCOperand::createReg(RISCV::X7));
+    EmitToStreamer(*OutStreamer, TmpInst);
+    return;
+  }
   case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
     LowerHWASAN_CHECK_MEMACCESS(*MI);
     return;

diff  --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index b87e65a7f09ba..20d21b1a8da14 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -27909,14 +27909,19 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
   // Emit the call.
   SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
 
-  // 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 &&
-      MF.getInfo<RISCVMachineFunctionInfo>()->hasCFProtectionBranch() &&
-      ((CLI.CB && !CLI.CB->isIndirectCall()) || CalleeIsLargeExternalSymbol))
-    NeedSWGuarded = true;
+  // Tail calls need software guarded branch (X7) when cf-protection-branch is
+  // active: PseudoTAIL expands to JALR X0, X6, 0 which lpad rejects, while
+  // PseudoTAILX7 expands to JALR X0, X7, 0 which lpad accepts.
+  // Non-tail calls only need SW_GUARDED in Large code model: in non-Large,
+  // PseudoCALL uses X1 (JALR X1, X1, 0) which lpad accepts as a return address.
+  bool NeedSWGuardedTail = false;
+  bool NeedSWGuardedCall = false;
+  if (MF.getInfo<RISCVMachineFunctionInfo>()->hasCFProtectionBranch() &&
+      ((CLI.CB && !CLI.CB->isIndirectCall()) || CalleeIsLargeExternalSymbol)) {
+    NeedSWGuardedTail = true;
+    if (getTargetMachine().getCodeModel() == CodeModel::Large)
+      NeedSWGuardedCall = true;
+  }
 
   // Use special pseudo for returns_twice calls (e.g., setjmp) when
   // cf-protection-branch is enabled, to ensure LPAD is inserted after the call.
@@ -27927,7 +27932,7 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
   if (IsTailCall) {
     MF.getFrameInfo().setHasTailCall();
     unsigned CallOpc =
-        NeedSWGuarded ? RISCVISD::SW_GUARDED_TAIL : RISCVISD::TAIL;
+        NeedSWGuardedTail ? RISCVISD::SW_GUARDED_TAIL : RISCVISD::TAIL;
     SDValue Ret = DAG.getNode(CallOpc, DL, NodeTys, Ops);
     if (CLI.CFIType)
       Ret.getNode()->setCFIType(CLI.CFIType->getZExtValue());
@@ -27939,7 +27944,7 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
   unsigned CallOpc;
   // FIXME: Large Code Model + Zicfilp: SW_GUARDED_CALL takes priority over
   // LPAD_CALL for returns_twice calls, breaking LPAD alignment.
-  if (NeedSWGuarded)
+  if (NeedSWGuardedCall)
     CallOpc = RISCVISD::SW_GUARDED_CALL;
   else if (NeedLpadCall && CLI.CB->isIndirectCall())
     CallOpc = RISCVISD::LPAD_CALL_INDIRECT;

diff  --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index ee47963aa041e..957cc37cf5cca 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -3699,8 +3699,15 @@ static bool cannotInsertTailCall(const MachineBasicBlock &MBB) {
   // that can be used for expanding PseudoTAIL instruction,
   // then we cannot insert tail call.
   const TargetSubtargetInfo &STI = MBB.getParent()->getSubtarget();
+  const RISCVMachineFunctionInfo *RVFI =
+      MBB.getParent()->getInfo<RISCVMachineFunctionInfo>();
+  // When cf-protection-branch is active, the outliner will emit PseudoTAILX7
+  // which always uses X7. Otherwise, PseudoTAIL is emitted and the register
+  // is determined by Zicfilp at encode time.
   MCRegister TailExpandUseRegNo =
-      RISCVII::getTailExpandUseRegNo(STI.getFeatureBits());
+      RVFI->hasCFProtectionBranch()
+          ? RISCV::X7
+          : RISCVII::getTailExpandUseRegNo(STI.getFeatureBits());
   for (const MachineInstr &MI : MBB) {
     if (isMIReadsReg(MI, STI.getRegisterInfo(), TailExpandUseRegNo))
       return true;
@@ -3742,8 +3749,12 @@ bool RISCVInstrInfo::analyzeCandidate(outliner::Candidate &C) const {
   // If the expansion register for tail calls is live across the candidate
   // outlined call site, we cannot outline that candidate as the expansion
   // would clobber the register.
+  const RISCVMachineFunctionInfo *RVFI =
+      C.getMF()->getInfo<RISCVMachineFunctionInfo>();
   MCRegister TailExpandUseReg =
-      RISCVII::getTailExpandUseRegNo(STI.getFeatureBits());
+      RVFI->hasCFProtectionBranch()
+          ? RISCV::X7
+          : RISCVII::getTailExpandUseRegNo(STI.getFeatureBits());
   if (C.back().isReturn() &&
       !C.isAvailableAcrossAndOutOfSeq(TailExpandUseReg, RegInfo)) {
     LLVM_DEBUG(dbgs() << "MBB:\n" << *C.getMBB());
@@ -3927,7 +3938,11 @@ MachineBasicBlock::iterator RISCVInstrInfo::insertOutlinedCall(
     MachineFunction &MF, outliner::Candidate &C) const {
 
   if (C.CallConstructionID == MachineOutlinerTailCall) {
-    It = MBB.insert(It, BuildMI(MF, DebugLoc(), get(RISCV::PseudoTAIL))
+    const RISCVMachineFunctionInfo *RVFI =
+        MF.getInfo<RISCVMachineFunctionInfo>();
+    unsigned TailOpc =
+        RVFI->hasCFProtectionBranch() ? RISCV::PseudoTAILX7 : RISCV::PseudoTAIL;
+    It = MBB.insert(It, BuildMI(MF, DebugLoc(), get(TailOpc))
                             .addGlobalAddress(M.getNamedValue(MF.getName()),
                                               /*Offset=*/0, RISCVII::MO_CALL));
     return It;

diff  --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 3321dd8167c3b..b7a4a78b33e51 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -528,6 +528,21 @@ def call_symbol : Operand<XLenVT> {
   let ParserMatchClass = CallSymbol;
 }
 
+def TailCallSymbol : AsmOperandClass {
+  let Name = "TailCallSymbol";
+  let RenderMethod = "addImmOperands";
+  let DiagnosticType = "InvalidTailCallSymbol";
+  let DiagnosticString = "operand must be a bare symbol name";
+  let ParserMethod = "parseTailCallSymbol";
+  let SuperClasses = [CallSymbol];
+}
+
+// Like call_symbol but allows the symbol to be followed by a comma
+// (for "tail address, register" form).
+def tail_call_symbol : Operand<XLenVT> {
+  let ParserMatchClass = TailCallSymbol;
+}
+
 def PseudoJumpSymbol : AsmOperandClass {
   let Name = "PseudoJumpSymbol";
   let RenderMethod = "addImmOperands";
@@ -1919,11 +1934,26 @@ def PseudoRET : Pseudo<(outs), (ins), [(riscv_ret_glue)]>,
 // PseudoTAIL is a pseudo instruction similar to PseudoCALL and will eventually
 // expand to auipc and jalr while encoding.
 // Define AsmString to print "tail" when compile with -S flag.
+// PseudoTAILX7 is the software-guarded variant that uses X7 (t2) as the temp
+// register for AUIPC+JALR expansion, required for cf-protection-branch so that
+// the callee's lpad accepts the incoming branch (lpad rejects X6 but accepts X7).
 let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [X2],
-    Size = 8, isCodeGenOnly = 0 in
+    Size = 8 in {
+let isCodeGenOnly = 0 in
 def PseudoTAIL : Pseudo<(outs), (ins call_symbol:$dst), [],
                         "tail", "$dst">,
                  Sched<[WriteIALU, WriteJalr, ReadJalr]>;
+// PseudoTAILReg is the asm form of "tail address, register" where the
+// register specifies the temp for AUIPC+JALR expansion.
+let isCodeGenOnly = 0, hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
+def PseudoTAILReg : Pseudo<(outs), (ins tail_call_symbol:$dst, GPR:$rs), [],
+                            "tail", "$dst, $rs">,
+                    Sched<[WriteIALU, WriteJalr, ReadJalr]>;
+// PseudoTAILX7 is CodeGenOnly; used when cf-protection-branch is active.
+// Lowered to PseudoTAILReg with X7 in RISCVAsmPrinter.
+def PseudoTAILX7 : Pseudo<(outs), (ins call_symbol:$dst), []>,
+                   Sched<[WriteIALU, WriteJalr, ReadJalr]>;
+}
 
 let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [X2] in {
 // Use GPRTCNonX7 to avoid X7 which is reserved for landing pad labels
@@ -1941,6 +1971,10 @@ def : Pat<(riscv_tail (iPTR tglobaladdr:$dst)),
           (PseudoTAIL tglobaladdr:$dst)>;
 def : Pat<(riscv_tail (iPTR texternalsym:$dst)),
           (PseudoTAIL texternalsym:$dst)>;
+def : Pat<(riscv_sw_guarded_tail (iPTR tglobaladdr:$dst)),
+          (PseudoTAILX7 tglobaladdr:$dst)>;
+def : Pat<(riscv_sw_guarded_tail (iPTR texternalsym:$dst)),
+          (PseudoTAILX7 texternalsym:$dst)>;
 
 let isCall = 0, isBarrier = 1, isBranch = 1, isTerminator = 1, Size = 8,
     isCodeGenOnly = 0, hasSideEffects = 0, mayStore = 0, mayLoad = 0 in

diff  --git a/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll b/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
index a6e019981f26a..9513b95fedb16 100644
--- a/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
+++ b/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
@@ -1,12 +1,12 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc -mtriple riscv64 -mattr=+experimental-zicfilp < %s | FileCheck %s
-; RUN: llc -mtriple riscv32 -mattr=+experimental-zicfilp < %s | FileCheck %s
+; RUN: llc -mtriple riscv64 < %s | FileCheck %s
+; RUN: llc -mtriple riscv32 < %s | FileCheck %s
 
 define i16 @test1(i16 %x) #0 {
 ; CHECK-LABEL: test1:
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    lpad 0
-; CHECK-NEXT:    tail OUTLINED_FUNCTION_0
+; CHECK-NEXT:    tail OUTLINED_FUNCTION_0, t2
 entry:
   %y = add i16 5, %x
   %z = mul i16 4, %y
@@ -17,7 +17,7 @@ define i16 @test2(i16 %x) #0 {
 ; CHECK-LABEL: test2:
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    lpad 0
-; CHECK-NEXT:    tail OUTLINED_FUNCTION_0
+; CHECK-NEXT:    tail OUTLINED_FUNCTION_0, t2
 entry:
   %y = add i16 5, %x
   %z = mul i16 4, %y
@@ -28,7 +28,7 @@ define i16 @test3(i16 %x) #0 {
 ; CHECK-LABEL: test3:
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    lpad 0
-; CHECK-NEXT:    tail OUTLINED_FUNCTION_0
+; CHECK-NEXT:    tail OUTLINED_FUNCTION_0, t2
 entry:
   %y = add i16 5, %x
   %z = mul i16 4, %y
@@ -39,7 +39,7 @@ define i16 @test4(i16 %x) #0 {
 ; CHECK-LABEL: test4:
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    lpad 0
-; CHECK-NEXT:    tail OUTLINED_FUNCTION_0
+; CHECK-NEXT:    tail OUTLINED_FUNCTION_0, t2
 entry:
   %y = add i16 5, %x
   %z = mul i16 4, %y
@@ -50,7 +50,7 @@ define i16 @main(i16 %x) #0 {
 ; CHECK-LABEL: main:
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    lpad 0
-; CHECK-NEXT:    tail OUTLINED_FUNCTION_0
+; CHECK-NEXT:    tail OUTLINED_FUNCTION_0, t2
 entry:
   %y = add i16 5, %x
   %z = mul i16 4, %y

diff  --git a/llvm/test/CodeGen/RISCV/tail-calls.ll b/llvm/test/CodeGen/RISCV/tail-calls.ll
index dc42ce296abce..e43acfa56f3fd 100644
--- a/llvm/test/CodeGen/RISCV/tail-calls.ll
+++ b/llvm/test/CodeGen/RISCV/tail-calls.ll
@@ -24,12 +24,12 @@ define i32 @caller_tail(i32 %i) nounwind {
 ; CHECK-CF-RV32-LABEL: caller_tail:
 ; CHECK-CF-RV32:       # %bb.0: # %entry
 ; CHECK-CF-RV32-NEXT:    lpad 0
-; CHECK-CF-RV32-NEXT:    tail callee_tail
+; CHECK-CF-RV32-NEXT:    tail callee_tail, t2
 ;
 ; CHECK-CF-RV64-LABEL: caller_tail:
 ; CHECK-CF-RV64:       # %bb.0: # %entry
 ; CHECK-CF-RV64-NEXT:    lpad 0
-; CHECK-CF-RV64-NEXT:    tail callee_tail
+; CHECK-CF-RV64-NEXT:    tail callee_tail, t2
 ;
 ; CHECK-CF-RV32-LARGE-LABEL: caller_tail:
 ; CHECK-CF-RV32-LARGE:       # %bb.0: # %entry
@@ -640,7 +640,7 @@ define void @caller_indirect_args() nounwind {
 ; CHECK-CF-RV64-NEXT:    lui a1, 16383
 ; CHECK-CF-RV64-NEXT:    slli a1, a1, 36
 ; CHECK-CF-RV64-NEXT:    li a0, 0
-; CHECK-CF-RV64-NEXT:    tail callee_indirect_args
+; CHECK-CF-RV64-NEXT:    tail callee_indirect_args, t2
 ;
 ; CHECK-CF-RV32-LARGE-LABEL: caller_indirect_args:
 ; CHECK-CF-RV32-LARGE:       # %bb.0: # %entry
@@ -701,12 +701,12 @@ define void @caller_weak() nounwind {
 ; CHECK-CF-RV32-LABEL: caller_weak:
 ; CHECK-CF-RV32:       # %bb.0: # %entry
 ; CHECK-CF-RV32-NEXT:    lpad 0
-; CHECK-CF-RV32-NEXT:    tail callee_weak
+; CHECK-CF-RV32-NEXT:    tail callee_weak, t2
 ;
 ; CHECK-CF-RV64-LABEL: caller_weak:
 ; CHECK-CF-RV64:       # %bb.0: # %entry
 ; CHECK-CF-RV64-NEXT:    lpad 0
-; CHECK-CF-RV64-NEXT:    tail callee_weak
+; CHECK-CF-RV64-NEXT:    tail callee_weak, t2
 ;
 ; CHECK-CF-RV32-LARGE-LABEL: caller_weak:
 ; CHECK-CF-RV32-LARGE:       # %bb.0: # %entry
@@ -1264,13 +1264,13 @@ define i32 @duplicate_returns(i32 %a, i32 %b) nounwind {
 ; CHECK-CF-RV32-NEXT:  # %bb.2: # %if.else4
 ; CHECK-CF-RV32-NEXT:    bge a1, a0, .LBB14_6
 ; CHECK-CF-RV32-NEXT:  # %bb.3: # %if.then6
-; CHECK-CF-RV32-NEXT:    tail test2
+; CHECK-CF-RV32-NEXT:    tail test2, t2
 ; CHECK-CF-RV32-NEXT:  .LBB14_4: # %if.then
-; CHECK-CF-RV32-NEXT:    tail test
+; CHECK-CF-RV32-NEXT:    tail test, t2
 ; CHECK-CF-RV32-NEXT:  .LBB14_5: # %if.then2
-; CHECK-CF-RV32-NEXT:    tail test1
+; CHECK-CF-RV32-NEXT:    tail test1, t2
 ; CHECK-CF-RV32-NEXT:  .LBB14_6: # %if.else8
-; CHECK-CF-RV32-NEXT:    tail test3
+; CHECK-CF-RV32-NEXT:    tail test3, t2
 ;
 ; CHECK-CF-RV64-LABEL: duplicate_returns:
 ; CHECK-CF-RV64:       # %bb.0: # %entry
@@ -1283,13 +1283,13 @@ define i32 @duplicate_returns(i32 %a, i32 %b) nounwind {
 ; CHECK-CF-RV64-NEXT:  # %bb.2: # %if.else4
 ; CHECK-CF-RV64-NEXT:    bge a1, a0, .LBB14_6
 ; CHECK-CF-RV64-NEXT:  # %bb.3: # %if.then6
-; CHECK-CF-RV64-NEXT:    tail test2
+; CHECK-CF-RV64-NEXT:    tail test2, t2
 ; CHECK-CF-RV64-NEXT:  .LBB14_4: # %if.then
-; CHECK-CF-RV64-NEXT:    tail test
+; CHECK-CF-RV64-NEXT:    tail test, t2
 ; CHECK-CF-RV64-NEXT:  .LBB14_5: # %if.then2
-; CHECK-CF-RV64-NEXT:    tail test1
+; CHECK-CF-RV64-NEXT:    tail test1, t2
 ; CHECK-CF-RV64-NEXT:  .LBB14_6: # %if.else8
-; CHECK-CF-RV64-NEXT:    tail test3
+; CHECK-CF-RV64-NEXT:    tail test3, t2
 ;
 ; CHECK-CF-RV32-LARGE-LABEL: duplicate_returns:
 ; CHECK-CF-RV32-LARGE:       # %bb.0: # %entry

diff  --git a/llvm/test/MC/RISCV/tail-call.s b/llvm/test/MC/RISCV/tail-call.s
index 1c55bf5223caa..ab4f4af87d1cf 100644
--- a/llvm/test/MC/RISCV/tail-call.s
+++ b/llvm/test/MC/RISCV/tail-call.s
@@ -64,3 +64,18 @@ tail foo at plt
 # INSTR: jr  t1
 # INSTR-ZICFILP: auipc t2, 0
 # INSTR-ZICFILP: jr  t2
+
+# "tail address, reg" uses the specified register regardless of Zicfilp.
+tail foo, t2
+# RELOC: R_RISCV_CALL_PLT foo 0x0
+# INSTR: auipc t2, 0
+# INSTR: jr  t2
+# INSTR-ZICFILP: auipc t2, 0
+# INSTR-ZICFILP: jr  t2
+
+tail bar, t1
+# RELOC: R_RISCV_CALL_PLT bar 0x0
+# INSTR: auipc t1, 0
+# INSTR: jr  t1
+# INSTR-ZICFILP: auipc t1, 0
+# INSTR-ZICFILP: jr  t1


        


More information about the llvm-commits mailing list