[llvm] [RISCV] Add PseudoTAILX7, PseudoTAILReg for cf-protection-branch (PR #220657)
Jesse Huang via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 11 22:24:10 PDT 2026
https://github.com/jaidTw updated https://github.com/llvm/llvm-project/pull/220657
>From 8400f2f9832ced87bb27ced1f09a4481491d5451 Mon Sep 17 00:00:00 2001
From: Jesse Huang <jesse.huang at sifive.com>
Date: Wed, 2 Sep 2026 09:45:43 -0700
Subject: [PATCH 1/6] [RISCV] Add PseudoTAILX7, PseudoTAILReg for
cf-protection-branch
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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.
---
.../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 37 +++++++++++++++++++
.../RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 12 ++++++
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 25 ++++++++-----
llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 21 +++++++++--
llvm/lib/Target/RISCV/RISCVInstrInfo.td | 36 +++++++++++++++++-
.../CodeGen/RISCV/machine-outliner-lpad.ll | 10 ++---
llvm/test/CodeGen/RISCV/tail-calls.ll | 26 ++++++-------
llvm/test/MC/RISCV/tail-call.s | 15 ++++++++
8 files changed, 150 insertions(+), 32 deletions(-)
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..7f572b795bff5 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -176,6 +176,12 @@ 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::PseudoTAILX7) {
+ Func = MI.getOperand(0);
+ Ra = RISCV::X7;
+ } 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 +201,8 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
if (STI.getTargetTriple().isOSBinFormatMachO()) {
MCOperand FuncOp = MCOperand::createExpr(CallExpr);
if (MI.getOpcode() == RISCV::PseudoTAIL ||
+ MI.getOpcode() == RISCV::PseudoTAILX7 ||
+ MI.getOpcode() == RISCV::PseudoTAILReg ||
MI.getOpcode() == RISCV::PseudoJump)
// Emit JAL X0, Func
TmpInst = MCInstBuilder(RISCV::JAL).addReg(RISCV::X0).addOperand(FuncOp);
@@ -211,6 +219,8 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
support::endian::write(CB, Binary, llvm::endianness::little);
if (MI.getOpcode() == RISCV::PseudoTAIL ||
+ MI.getOpcode() == RISCV::PseudoTAILX7 ||
+ 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 +583,8 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
case RISCV::PseudoCALLReg:
case RISCV::PseudoCALL:
case RISCV::PseudoTAIL:
+ case RISCV::PseudoTAILX7:
+ case RISCV::PseudoTAILReg:
case RISCV::PseudoJump:
expandFunctionCall(MI, CB, Fixups, STI);
MCNumEmitted += 2;
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..3d06613321035 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]>;
+// PseudoTAILX7 is CodeGenOnly; used when cf-protection-branch is active.
+def PseudoTAILX7 : Pseudo<(outs), (ins call_symbol:$dst), [],
+ "tail", "$dst, t2">,
+ 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]>;
+}
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..51ef094710d82 100644
--- a/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
+++ b/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
@@ -6,7 +6,7 @@ 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
>From e4d816c8c94770f99a12022c8be6fc99970e4567 Mon Sep 17 00:00:00 2001
From: Jesse Huang <jesse.huang at sifive.com>
Date: Thu, 3 Sep 2026 10:23:01 -0700
Subject: [PATCH 2/6] Update Releases Note
---
llvm/docs/ReleaseNotes.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 291b18444f73d..b6ffcd8a00433 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 offset, rt` form that takes an optional destination
+ register.
### Changes to the WebAssembly Backend
>From 35db3d4f9f00d12a2bc4125f8ba568a8fe0d3c5a Mon Sep 17 00:00:00 2001
From: Jesse Huang <jesse.huang at sifive.com>
Date: Tue, 8 Sep 2026 08:59:04 -0700
Subject: [PATCH 3/6] Update as suggested
---
.../Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 6 ------
llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 11 +++++++++++
llvm/lib/Target/RISCV/RISCVInstrInfo.td | 8 ++++----
3 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index 7f572b795bff5..a4d77ee6d2e51 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -176,9 +176,6 @@ 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::PseudoTAILX7) {
- Func = MI.getOperand(0);
- Ra = RISCV::X7;
} else if (MI.getOpcode() == RISCV::PseudoTAILReg) {
Func = MI.getOperand(0);
Ra = MI.getOperand(1).getReg();
@@ -201,7 +198,6 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
if (STI.getTargetTriple().isOSBinFormatMachO()) {
MCOperand FuncOp = MCOperand::createExpr(CallExpr);
if (MI.getOpcode() == RISCV::PseudoTAIL ||
- MI.getOpcode() == RISCV::PseudoTAILX7 ||
MI.getOpcode() == RISCV::PseudoTAILReg ||
MI.getOpcode() == RISCV::PseudoJump)
// Emit JAL X0, Func
@@ -219,7 +215,6 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
support::endian::write(CB, Binary, llvm::endianness::little);
if (MI.getOpcode() == RISCV::PseudoTAIL ||
- MI.getOpcode() == RISCV::PseudoTAILX7 ||
MI.getOpcode() == RISCV::PseudoTAILReg ||
MI.getOpcode() == RISCV::PseudoJump)
// Emit JALR X0, Ra, 0
@@ -583,7 +578,6 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
case RISCV::PseudoCALLReg:
case RISCV::PseudoCALL:
case RISCV::PseudoTAIL:
- case RISCV::PseudoTAILX7:
case RISCV::PseudoTAILReg:
case RISCV::PseudoJump:
expandFunctionCall(MI, CB, Fixups, STI);
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index 6771e9d7e7fa1..dcd9a1c5dfce4 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 MCOp;
+ lowerOperand(MI->getOperand(0), MCOp);
+ MCInst TmpInst;
+ TmpInst.setOpcode(RISCV::PseudoTAILReg);
+ TmpInst.addOperand(MCOp);
+ 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/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 3d06613321035..b7a4a78b33e51 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -1943,16 +1943,16 @@ let isCodeGenOnly = 0 in
def PseudoTAIL : Pseudo<(outs), (ins call_symbol:$dst), [],
"tail", "$dst">,
Sched<[WriteIALU, WriteJalr, ReadJalr]>;
-// PseudoTAILX7 is CodeGenOnly; used when cf-protection-branch is active.
-def PseudoTAILX7 : Pseudo<(outs), (ins call_symbol:$dst), [],
- "tail", "$dst, t2">,
- 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 {
>From 0c8079722821fbafcdeae541ce34ad69f4a0cb94 Mon Sep 17 00:00:00 2001
From: Jesse Huang <jesse.huang at sifive.com>
Date: Tue, 8 Sep 2026 10:38:14 -0700
Subject: [PATCH 4/6] Drop Zicfilp from machine-outliner-lpad.ll
---
llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll b/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
index 51ef094710d82..9513b95fedb16 100644
--- a/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
+++ b/llvm/test/CodeGen/RISCV/machine-outliner-lpad.ll
@@ -1,6 +1,6 @@
; 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:
>From 8af32d604fd839a3f511dee5b5c90779d86a0653 Mon Sep 17 00:00:00 2001
From: Jesse Huang <jesse.huang at sifive.com>
Date: Tue, 8 Sep 2026 19:55:38 -0700
Subject: [PATCH 5/6] Update Releases Note
---
llvm/docs/ReleaseNotes.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index b6ffcd8a00433..1fd90f5bd3be0 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -246,8 +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 offset, rt` form that takes an optional destination
- register.
+* 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
>From 89aa22b26ca6bfb1093b042c6d8570a781b29af4 Mon Sep 17 00:00:00 2001
From: Jesse Huang <jesse.huang at sifive.com>
Date: Fri, 11 Sep 2026 22:15:03 -0700
Subject: [PATCH 6/6] fixup! Rename MCOp to SymOp
---
llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index dcd9a1c5dfce4..fba93dede0d02 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -398,11 +398,11 @@ void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) {
switch (MI->getOpcode()) {
case RISCV::PseudoTAILX7: {
// Lower to PseudoTAILReg with X7 as the register operand.
- MCOperand MCOp;
- lowerOperand(MI->getOperand(0), MCOp);
+ MCOperand SymOp;
+ lowerOperand(MI->getOperand(0), SymOp);
MCInst TmpInst;
TmpInst.setOpcode(RISCV::PseudoTAILReg);
- TmpInst.addOperand(MCOp);
+ TmpInst.addOperand(SymOp);
TmpInst.addOperand(MCOperand::createReg(RISCV::X7));
EmitToStreamer(*OutStreamer, TmpInst);
return;
More information about the llvm-commits
mailing list