Index: llvm/lib/Target/RISCV/RISCVFrameLowering.h =================================================================== --- llvm/lib/Target/RISCV/RISCVFrameLowering.h +++ llvm/lib/Target/RISCV/RISCVFrameLowering.h @@ -51,6 +51,9 @@ MachineBasicBlock::iterator MI, ArrayRef CSI, const TargetRegisterInfo *TRI) const override; + void buildAddr(MachineBasicBlock &MBB, MachineBasicBlock::iterator InsertPos, + const DebugLoc &DL, Register DestReg, const MachineOperand &Op, + bool IsLocal, MachineInstr::MIFlag MIFlag) const; bool restoreCalleeSavedRegisters(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Index: llvm/lib/Target/RISCV/RISCVFrameLowering.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -197,7 +197,8 @@ // If this function will not use save/restore libcalls, then return a nullptr. static const char * getRestoreLibCallName(const MachineFunction &MF, - const std::vector &CSI) { + const std::vector &CSI, + bool TailCallSite) { static const char *const RestoreLibCalls[] = { "__riscv_restore_0", "__riscv_restore_1", @@ -213,10 +214,27 @@ "__riscv_restore_11", "__riscv_restore_12" }; + static const char *const RestoreLibCallsTC[] = { + "__riscv_restore_tailcall_0", + "__riscv_restore_tailcall_1", + "__riscv_restore_tailcall_2", + "__riscv_restore_tailcall_3", + "__riscv_restore_tailcall_4", + "__riscv_restore_tailcall_5", + "__riscv_restore_tailcall_6", + "__riscv_restore_tailcall_7", + "__riscv_restore_tailcall_8", + "__riscv_restore_tailcall_9", + "__riscv_restore_tailcall_10", + "__riscv_restore_tailcall_11", + "__riscv_restore_tailcall_12" + }; int LibCallID = getLibCallID(MF, CSI); if (LibCallID == -1) return nullptr; + if (TailCallSite) + return RestoreLibCallsTC[LibCallID]; return RestoreLibCalls[LibCallID]; } @@ -1082,6 +1100,53 @@ return true; } +void RISCVFrameLowering::buildAddr(MachineBasicBlock &MBB, + MachineBasicBlock::iterator InsertPos, + const DebugLoc &DL, Register DestReg, + const MachineOperand &Op, bool IsLocal, + MachineInstr::MIFlag MIFlag) const { + assert(DestReg.isPhysical()); + assert((Op.getType() == MachineOperand::MO_GlobalAddress || + Op.getType() == MachineOperand::MO_ExternalSymbol) && + "Unhandled operand type"); + + MachineFunction &MF = *MBB.getParent(); + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + + if (MF.getTarget().isPositionIndependent()) { + if (IsLocal) + BuildMI(MBB, InsertPos, DL, TII.get(RISCV::PseudoLLA), DestReg).add(Op); + else + BuildMI(MBB, InsertPos, DL, TII.get(RISCV::PseudoLA), DestReg).add(Op); + return; + } + + switch (MF.getTarget().getCodeModel()) { + default: + report_fatal_error("Unsupported code model for lowering"); + case CodeModel::Small: { + MachineInstr *NewMI = + BuildMI(MBB, InsertPos, DL, TII.get(RISCV::LUI), DestReg) + .add(Op) + .setMIFlag(MIFlag); + NewMI->getOperand(1).setTargetFlags(RISCVII::MO_HI); + + NewMI = BuildMI(MBB, InsertPos, DL, TII.get(RISCV::ADDI), DestReg) + .addReg(DestReg) + .add(Op) + .setMIFlag(MIFlag); + NewMI->getOperand(2).setTargetFlags(RISCVII::MO_LO); + break; + } + case CodeModel::Medium: { + BuildMI(MBB, InsertPos, DL, TII.get(RISCV::PseudoLLA), DestReg) + .add(Op) + .setMIFlag(MIFlag); + break; + } + } +} + bool RISCVFrameLowering::restoreCalleeSavedRegisters( MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, MutableArrayRef CSI, const TargetRegisterInfo *TRI) const { @@ -1108,19 +1173,54 @@ assert(MI != MBB.begin() && "loadRegFromStackSlot didn't insert any code!"); } - const char *RestoreLibCall = getRestoreLibCallName(*MF, CSI); + bool IsTailCall = + MI != MBB.end() && (MI->getOpcode() == RISCV::PseudoTAIL || + MI->getOpcode() == RISCV::PseudoTAILIndirect); + const char *RestoreLibCall = getRestoreLibCallName(*MF, CSI, IsTailCall); if (RestoreLibCall) { - // Add restore libcall via tail call. - MachineBasicBlock::iterator NewMI = - BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoTAIL)) - .addExternalSymbol(RestoreLibCall, RISCVII::MO_CALL) - .setMIFlag(MachineInstr::FrameDestroy); - - // Remove trailing returns, since the terminator is now a tail call to the - // restore function. - if (MI != MBB.end() && MI->getOpcode() == RISCV::PseudoRET) { + if (IsTailCall) { + assert(MF->getSubtarget().enableSaveRestoreTailCall()); + + // Add restore libcall via tail call. X6/T1 is added as use as this holds + // the pointer to tailcall to. + MachineBasicBlock::iterator NewMI = + BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoTAIL)) + .addExternalSymbol(RestoreLibCall, RISCVII::MO_CALL) + .addReg(RISCV::X6 /*T1*/, RegState::Implicit) + .setMIFlag(MachineInstr::FrameDestroy); + + if (MI->getOperand(0).getType() == MachineOperand::MO_Register) { + if (MI->getOperand(0).getReg() != RISCV::X6 /*T1*/) { + BuildMI(MBB, NewMI, DL, TII.get(RISCV::ADDI), RISCV::X6 /*T1*/) + .addReg(MI->getOperand(0).getReg()) + .addImm(0) + .setMIFlag(MachineInstr::FrameDestroy); + } + } else { + const MachineOperand &Op = MI->getOperand(0); + assert(Op.isGlobal() || Op.isSymbol()); + bool IsLocal = MF->getTarget().shouldAssumeDSOLocal( + *MF->getFunction().getParent(), + Op.isGlobal() ? Op.getGlobal() : nullptr); + buildAddr(MBB, NewMI, DL, RISCV::X6, MI->getOperand(0), IsLocal, + MachineInstr::FrameDestroy); + } + NewMI->copyImplicitOps(*MF, *MI); MI->eraseFromParent(); + } else { + // Add restore libcall via tail call. + MachineBasicBlock::iterator NewMI = + BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoTAIL)) + .addExternalSymbol(RestoreLibCall, RISCVII::MO_CALL) + .setMIFlag(MachineInstr::FrameDestroy); + + // Remove trailing returns, since the terminator is now a tail call to the + // restore function. + if (MI != MBB.end() && MI->getOpcode() == RISCV::PseudoRET) { + NewMI->copyImplicitOps(*MF, *MI); + MI->eraseFromParent(); + } } } Index: llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h =================================================================== --- llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h +++ llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h @@ -84,8 +84,11 @@ bool useSaveRestoreLibCalls(const MachineFunction &MF) const { // We cannot use fixed locations for the callee saved spill slots if the // function uses a varargs save area, or is an interrupt handler. - return MF.getSubtarget().enableSaveRestore() && - VarArgsSaveSize == 0 && !MF.getFrameInfo().hasTailCall() && + const auto &Subtarget = MF.getSubtarget(); + bool IsTailCall = MF.getFrameInfo().hasTailCall(); + return Subtarget.enableSaveRestore() && + (!IsTailCall || Subtarget.enableSaveRestoreTailCall()) && + VarArgsSaveSize == 0 && !MF.getFunction().hasFnAttribute("interrupt"); } Index: llvm/lib/Target/RISCV/RISCVSubtarget.h =================================================================== --- llvm/lib/Target/RISCV/RISCVSubtarget.h +++ llvm/lib/Target/RISCV/RISCVSubtarget.h @@ -246,6 +246,10 @@ // pool if exceeded. unsigned getMaxBuildIntsCost() const; + // Returns true if the special tailcall versions of the save-restore + // entry points can also be used. + bool enableSaveRestoreTailCall() const; + // Return the known range for the bit length of RVV data registers. A value // of 0 means nothing is known about that particular limit beyond what's // implied by the architecture. Index: llvm/lib/Target/RISCV/RISCVSubtarget.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVSubtarget.cpp +++ llvm/lib/Target/RISCV/RISCVSubtarget.cpp @@ -61,6 +61,11 @@ cl::desc("The maximum cost used for building integers."), cl::init(0), cl::Hidden); +static cl::opt SaveRestoreTailCall( + "riscv-save-restore-tailcall", + cl::desc("Enable save-restore optimization in a tailcall context."), + cl::init(false), cl::Hidden); + void RISCVSubtarget::anchor() {} RISCVSubtarget & @@ -133,6 +138,10 @@ : std::max(2, RISCVMaxBuildIntsCost); } +bool RISCVSubtarget::enableSaveRestoreTailCall() const { + return SaveRestoreTailCall; +} + unsigned RISCVSubtarget::getMaxRVVVectorSizeInBits() const { assert(hasVInstructions() && "Tried to get vector length without Zve or V extension support!"); Index: llvm/test/CodeGen/RISCV/saverestore.ll =================================================================== --- llvm/test/CodeGen/RISCV/saverestore.ll +++ llvm/test/CodeGen/RISCV/saverestore.ll @@ -137,47 +137,6 @@ ret void } -; Check that preserving tail calls is preferred over save/restore - -declare i32 @tail_callee(i32 %i) - -define i32 @tail_call(i32 %i) nounwind { -; RV32I-LABEL: tail_call: -; RV32I-NOT: call t0, __riscv_save -; RV32I: tail tail_callee -; RV32I-NOT: tail __riscv_restore -; -; RV64I-LABEL: tail_call: -; RV64I-NOT: call t0, __riscv_save -; RV64I: tail tail_callee -; RV64I-NOT: tail __riscv_restore -; -; RV32I-SR-LABEL: tail_call: -; RV32I-SR-NOT: call t0, __riscv_save -; RV32I-SR: tail tail_callee -; RV32I-SR-NOT: tail __riscv_restore -; -; RV64I-SR-LABEL: tail_call: -; RV64I-SR-NOT: call t0, __riscv_save -; RV64I-SR: tail tail_callee -; RV64I-SR-NOT: tail __riscv_restore -; -; RV32I-FP-SR-LABEL: tail_call: -; RV32I-FP-SR-NOT: call t0, __riscv_save -; RV32I-FP-SR: tail tail_callee -; RV32I-FP-SR-NOT: tail __riscv_restore -; -; RV64I-FP-SR-LABEL: tail_call: -; RV64I-FP-SR-NOT: call t0, __riscv_save -; RV64I-FP-SR: tail tail_callee -; RV64I-FP-SR-NOT: tail __riscv_restore -entry: - %val = load [18 x i32], [18 x i32]* @var0 - store volatile [18 x i32] %val, [18 x i32]* @var0 - %r = tail call i32 @tail_callee(i32 %i) - ret i32 %r -} - ; Check that functions with varargs do not use save/restore code declare void @llvm.va_start(i8*) Index: llvm/test/CodeGen/RISCV/saverestore_tailcall.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/saverestore_tailcall.ll @@ -0,0 +1,191 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=riscv32 < %s | FileCheck %s -check-prefix=RV32I +; RUN: llc -mtriple=riscv64 < %s | FileCheck %s -check-prefix=RV64I +; RUN: llc -mtriple=riscv32 --code-model=small -riscv-save-restore-tailcall -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV32I-SR-SMALL +; RUN: llc -mtriple=riscv64 --code-model=small -riscv-save-restore-tailcall -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV64I-SR-SMALL +; RUN: llc -mtriple=riscv32 --code-model=small -riscv-save-restore-tailcall -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV32I-SR-SMALL +; RUN: llc -mtriple=riscv64 --code-model=small -riscv-save-restore-tailcall -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV64I-SR-SMALL +; RUN: llc -mtriple=riscv32 --code-model=small -riscv-save-restore-tailcall -mattr=+f,+save-restore -target-abi=ilp32f < %s | FileCheck %s -check-prefix=RV32I-FP-SR-SMALL +; RUN: llc -mtriple=riscv64 --code-model=small -riscv-save-restore-tailcall -mattr=+f,+d,+save-restore -target-abi=lp64d < %s | FileCheck %s -check-prefix=RV64I-FP-SR-SMALL +; RUN: llc -mtriple=riscv32 --code-model=medium -riscv-save-restore-tailcall -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV32I-SR-MEDIUM +; RUN: llc -mtriple=riscv64 --code-model=medium -riscv-save-restore-tailcall -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV64I-SR-MEDIUM +; RUN: llc -mtriple=riscv32 --relocation-model=pic -riscv-save-restore-tailcall -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV32I-SR-PIC +; RUN: llc -mtriple=riscv64 --relocation-model=pic -riscv-save-restore-tailcall -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV64I-SR-PIC +; RUN: llc -mtriple=riscv32 --code-model=small -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV32I-SR-SMALL-NO-TAILCALL +; RUN: llc -mtriple=riscv64 --code-model=small -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV64I-SR-SMALL-NO-TAILCALL + +; Check that the correct save/restore libcalls are generated for tail calls + +@var0 = global [18 x i32] zeroinitializer +@var1 = global [24 x i32] zeroinitializer +@var2 = global [30 x i32] zeroinitializer + +declare i32 @tail_callee(i32 %i) + +define i32 @tail_call(i32 %i) nounwind { +; RV32I-LABEL: tail_call: +; RV32I-NOT: call t0, __riscv_save +; RV32I: tail tail_callee +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: tail_call: +; RV64I-NOT: call t0, __riscv_save +; RV64I: tail tail_callee +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-SMALL-LABEL: tail_call: +; RV32I-SR-SMALL: call t0, __riscv_save_6 +; RV32I-SR-SMALL: lui t1, %hi(tail_callee) +; RV32I-SR-SMALL: addi t1, t1, %lo(tail_callee) +; RV32I-SR-SMALL: tail __riscv_restore_tailcall_6 +; +; RV64I-SR-SMALL-LABEL: tail_call: +; RV64I-SR-SMALL: call t0, __riscv_save_6 +; RV64I-SR-SMALL: lui t1, %hi(tail_callee) +; RV64I-SR-SMALL: addi t1, t1, %lo(tail_callee) +; RV64I-SR-SMALL: tail __riscv_restore_tailcall_6 +; +; RV32I-FP-SR-SMALL-LABEL: tail_call: +; RV32I-FP-SR-SMALL: call t0, __riscv_save_6 +; RV32I-FP-SR-SMALL: lui t1, %hi(tail_callee) +; RV32I-FP-SR-SMALL: addi t1, t1, %lo(tail_callee) +; RV32I-FP-SR-SMALL: tail __riscv_restore_tailcall_6 +; +; RV64I-FP-SR-SMALL-LABEL: tail_call: +; RV64I-FP-SR-SMALL: call t0, __riscv_save_6 +; RV64I-FP-SR-SMALL: lui t1, %hi(tail_callee) +; RV64I-FP-SR-SMALL: addi t1, t1, %lo(tail_callee) +; RV64I-FP-SR-SMALL: tail __riscv_restore_tailcall_6 +; +; RV32I-SR-MEDIUM-LABEL: tail_call: +; RV32I-SR-MEDIUM: call t0, __riscv_save_5 +; RV32I-SR-MEDIUM: .LBB0_2: # %entry +; RV32I-SR-MEDIUM: # Label of block must be emitted +; RV32I-SR-MEDIUM: auipc t1, %pcrel_hi(tail_callee) +; RV32I-SR-MEDIUM: addi t1, t1, %pcrel_lo(.LBB0_2) +; RV32I-SR-MEDIUM: tail __riscv_restore_tailcall_5 +; +; RV64I-SR-MEDIUM-LABEL: tail_call: +; RV64I-SR-MEDIUM: call t0, __riscv_save_5 +; RV64I-SR-MEDIUM: .LBB0_2: # %entry +; RV64I-SR-MEDIUM: # Label of block must be emitted +; RV64I-SR-MEDIUM: auipc t1, %pcrel_hi(tail_callee) +; RV64I-SR-MEDIUM: addi t1, t1, %pcrel_lo(.LBB0_2) +; RV64I-SR-MEDIUM: tail __riscv_restore_tailcall_5 +; +; RV32I-SR-PIC-LABEL: tail_call: +; RV32I-SR-PIC: call t0, __riscv_save_5 +; RV32I-SR-PIC: .LBB0_2: # %entry +; RV32I-SR-PIC: # Label of block must be emitted +; RV32I-SR-PIC: auipc t1, %got_pcrel_hi(tail_callee) +; RV32I-SR-PIC: lw t1, %pcrel_lo(.LBB0_2)(t1) +; RV32I-SR-PIC: tail __riscv_restore_tailcall_5 +; +; RV64I-SR-PIC-LABEL: tail_call: +; RV64I-SR-PIC: call t0, __riscv_save_5 +; RV64I-SR-PIC: .LBB0_2: # %entry +; RV64I-SR-PIC: # Label of block must be emitted +; RV64I-SR-PIC: auipc t1, %got_pcrel_hi(tail_callee) +; RV64I-SR-PIC: ld t1, %pcrel_lo(.LBB0_2)(t1) +; RV64I-SR-PIC: tail __riscv_restore_tailcall_5 +; +; RV32I-SR-SMALL-NO-TAILCALL-LABEL: tail_call: +; RV32I-SR-SMALL-NO-TAILCALL-NOT: call t0, __riscv_save +; RV32I-SR-SMALL-NO-TAILCALL: tail tail_callee +; RV32I-SR-SMALL-NO-TAILCALL-NOT: tail __riscv_restore +; +; RV64I-SR-SMALL-NO-TAILCALL-LABEL: tail_call: +; RV64I-SR-SMALL-NO-TAILCALL-NOT: call t0, __riscv_save +; RV64I-SR-SMALL-NO-TAILCALL: tail tail_callee +; RV64I-SR-SMALL-NO-TAILCALL-NOT: tail __riscv_restore +entry: + %val = load [18 x i32], [18 x i32]* @var0 + store volatile [18 x i32] %val, [18 x i32]* @var0 + %r = tail call i32 @tail_callee(i32 %i) + ret i32 %r +} + +declare dso_local i32 @dso_local_tail_callee(i32 %i) + +define i32 @tail_call_to_dso_local(i32 %i) nounwind { +; RV32I-LABEL: tail_call_to_dso_local: +; RV32I-NOT: call t0, __riscv_save +; RV32I: tail dso_local_tail_callee +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: tail_call_to_dso_local: +; RV64I-NOT: call t0, __riscv_save +; RV64I: tail dso_local_tail_callee +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-SMALL-LABEL: tail_call_to_dso_local: +; RV32I-SR-SMALL: call t0, __riscv_save_6 +; RV32I-SR-SMALL: lui t1, %hi(dso_local_tail_callee) +; RV32I-SR-SMALL: addi t1, t1, %lo(dso_local_tail_callee) +; RV32I-SR-SMALL: tail __riscv_restore_tailcall_6 +; +; RV64I-SR-SMALL-LABEL: tail_call_to_dso_local: +; RV64I-SR-SMALL: call t0, __riscv_save_6 +; RV64I-SR-SMALL: lui t1, %hi(dso_local_tail_callee) +; RV64I-SR-SMALL: addi t1, t1, %lo(dso_local_tail_callee) +; RV64I-SR-SMALL: tail __riscv_restore_tailcall_6 +; +; RV32I-FP-SR-SMALL-LABEL: tail_call_to_dso_local: +; RV32I-FP-SR-SMALL: call t0, __riscv_save_6 +; RV32I-FP-SR-SMALL: lui t1, %hi(dso_local_tail_callee) +; RV32I-FP-SR-SMALL: addi t1, t1, %lo(dso_local_tail_callee) +; RV32I-FP-SR-SMALL: tail __riscv_restore_tailcall_6 +; +; RV64I-FP-SR-SMALL-LABEL: tail_call_to_dso_local: +; RV64I-FP-SR-SMALL: call t0, __riscv_save_6 +; RV64I-FP-SR-SMALL: lui t1, %hi(dso_local_tail_callee) +; RV64I-FP-SR-SMALL: addi t1, t1, %lo(dso_local_tail_callee) +; RV64I-FP-SR-SMALL: tail __riscv_restore_tailcall_6 +; +; RV32I-SR-MEDIUM-LABEL: tail_call_to_dso_local: +; RV32I-SR-MEDIUM: call t0, __riscv_save_5 +; RV32I-SR-MEDIUM: .LBB1_2: # %entry +; RV32I-SR-MEDIUM: # Label of block must be emitted +; RV32I-SR-MEDIUM: auipc t1, %pcrel_hi(dso_local_tail_callee) +; RV32I-SR-MEDIUM: addi t1, t1, %pcrel_lo(.LBB1_2) +; RV32I-SR-MEDIUM: tail __riscv_restore_tailcall_5 +; +; RV64I-SR-MEDIUM-LABEL: tail_call_to_dso_local: +; RV64I-SR-MEDIUM: call t0, __riscv_save_5 +; RV64I-SR-MEDIUM: .LBB1_2: # %entry +; RV64I-SR-MEDIUM: # Label of block must be emitted +; RV64I-SR-MEDIUM: auipc t1, %pcrel_hi(dso_local_tail_callee) +; RV64I-SR-MEDIUM: addi t1, t1, %pcrel_lo(.LBB1_2) +; RV64I-SR-MEDIUM: tail __riscv_restore_tailcall_5 +; +; RV32I-SR-PIC-LABEL: tail_call_to_dso_local: +; RV32I-SR-PIC: call t0, __riscv_save_5 +; RV32I-SR-PIC: .LBB1_2: # %entry +; RV32I-SR-PIC: # Label of block must be emitted +; RV32I-SR-PIC: auipc t1, %pcrel_hi(dso_local_tail_callee) +; RV32I-SR-PIC: addi t1, t1, %pcrel_lo(.LBB1_2) +; RV32I-SR-PIC: tail __riscv_restore_tailcall_5 +; +; RV64I-SR-PIC-LABEL: tail_call_to_dso_local: +; RV64I-SR-PIC: call t0, __riscv_save_5 +; RV64I-SR-PIC: .LBB1_2: # %entry +; RV64I-SR-PIC: # Label of block must be emitted +; RV64I-SR-PIC: auipc t1, %pcrel_hi(dso_local_tail_callee) +; RV64I-SR-PIC: addi t1, t1, %pcrel_lo(.LBB1_2) +; RV64I-SR-PIC: tail __riscv_restore_tailcall_5 +; +; RV32I-SR-SMALL-NO-TAILCALL-LABEL: tail_call_to_dso_local: +; RV32I-SR-SMALL-NO-TAILCALL-NOT: call t0, __riscv_save +; RV32I-SR-SMALL-NO-TAILCALL: tail dso_local_tail_callee +; RV32I-SR-SMALL-NO-TAILCALL-NOT: tail __riscv_restore +; +; RV64I-SR-SMALL-NO-TAILCALL-LABEL: tail_call_to_dso_local: +; RV64I-SR-SMALL-NO-TAILCALL-NOT: call t0, __riscv_save +; RV64I-SR-SMALL-NO-TAILCALL: tail dso_local_tail_callee +; RV64I-SR-SMALL-NO-TAILCALL-NOT: tail __riscv_restore +entry: + %val = load [18 x i32], [18 x i32]* @var0 + store volatile [18 x i32] %val, [18 x i32]* @var0 + %r = tail call i32 @dso_local_tail_callee(i32 %i) + ret i32 %r +}