diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp --- a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp +++ b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp @@ -433,12 +433,11 @@ Features.push_back("-relax"); // GCC Compatibility: -mno-save-restore is default, unless -msave-restore is - // specified... - if (Args.hasFlag(options::OPT_msave_restore, options::OPT_mno_save_restore, false)) { - // ... but we don't support -msave-restore, so issue a warning. - D.Diag(diag::warn_drv_clang_unsupported) - << Args.getLastArg(options::OPT_msave_restore)->getAsString(Args); - } + // specified. + if (Args.hasFlag(options::OPT_msave_restore, options::OPT_mno_save_restore, false)) + Features.push_back("+save-restore"); + else + Features.push_back("-save-restore"); // Now add any that the user explicitly requested on the command line, // which may override the defaults. diff --git a/clang/test/Driver/riscv-features.c b/clang/test/Driver/riscv-features.c --- a/clang/test/Driver/riscv-features.c +++ b/clang/test/Driver/riscv-features.c @@ -16,9 +16,10 @@ // RUN: %clang -target riscv32-unknown-elf -### %s -msave-restore 2>&1 | FileCheck %s -check-prefix=SAVE-RESTORE // RUN: %clang -target riscv32-unknown-elf -### %s -mno-save-restore 2>&1 | FileCheck %s -check-prefix=NO-SAVE-RESTORE -// SAVE-RESTORE: warning: the clang compiler does not support '-msave-restore' -// NO-SAVE-RESTORE-NOT: warning: the clang compiler does not support -// DEFAULT-NOT: warning: the clang compiler does not support +// SAVE-RESTORE: "-target-feature" "+save-restore" +// NO-SAVE-RESTORE: "-target-feature" "-save-restore" +// DEFAULT: "-target-feature" "-save-restore" +// DEFAULT-NOT: "-target-feature" "+save-restore" // RUN: %clang -target riscv32-linux -### %s -fsyntax-only 2>&1 \ // RUN: | FileCheck %s -check-prefix=DEFAULT-LINUX diff --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td --- a/llvm/lib/Target/RISCV/RISCV.td +++ b/llvm/lib/Target/RISCV/RISCV.td @@ -82,6 +82,9 @@ SubtargetFeature<"reserve-x"#i, "UserReservedRegister[RISCV::X"#i#"]", "true", "Reserve X"#i>; +def FeatureSaveRestore : SubtargetFeature<"save-restore", "EnableSaveRestore", + "true", "Enable save/restore.">; + //===----------------------------------------------------------------------===// // Named operands for CSR instructions. //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.h b/llvm/lib/Target/RISCV/RISCVFrameLowering.h --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.h +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.h @@ -46,12 +46,24 @@ MachineBasicBlock::iterator eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator MI) const override; + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const override; + bool + restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + std::vector &CSI, + const TargetRegisterInfo *TRI) const override; // Get the first stack adjustment amount for SplitSPAdjust. // Return 0 if we don't want to to split the SP adjustment in prologue and // epilogue. uint64_t getFirstSPAdjustAmount(const MachineFunction &MF) const; + bool canUseAsPrologue(const MachineBasicBlock &MBB) const override; + bool canUseAsEpilogue(const MachineBasicBlock &MBB) const override; + protected: const RISCVSubtarget &STI; diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -23,6 +23,100 @@ using namespace llvm; +// Get the ID of the libcall used for spilling and restoring callee saved +// registers. The ID is representative of the number of registers saved or +// restored by the libcall, except it is zero-indexed - ID 0 corresponds to a +// single register. +static int getLibCallID(const MachineFunction &MF, + const std::vector &CSI) { + const auto *RVFI = MF.getInfo(); + + if (CSI.empty() || !RVFI->useSaveRestoreLibCalls()) + return -1; + + Register MaxReg = RISCV::NoRegister; + for (auto &CS : CSI) + // RISCVRegisterInfo::hasReservedSpillSlot assigns negative frame indexes to + // registers which can be saved by libcall. + if (CS.getFrameIdx() < 0) + MaxReg = std::max(MaxReg.id(), CS.getReg()); + + if (MaxReg == RISCV::NoRegister) + return -1; + + switch (MaxReg) { + default: + llvm_unreachable("Something has gone wrong!"); + case /*s11*/ RISCV::X27: return 12; + case /*s10*/ RISCV::X26: return 11; + case /*s9*/ RISCV::X25: return 10; + case /*s8*/ RISCV::X24: return 9; + case /*s7*/ RISCV::X23: return 8; + case /*s6*/ RISCV::X22: return 7; + case /*s5*/ RISCV::X21: return 6; + case /*s4*/ RISCV::X20: return 5; + case /*s3*/ RISCV::X19: return 4; + case /*s2*/ RISCV::X18: return 3; + case /*s1*/ RISCV::X9: return 2; + case /*s0*/ RISCV::X8: return 1; + case /*ra*/ RISCV::X1: return 0; + } +} + +// Get the name of the libcall used for spilling callee saved registers. +// If this function will not use save/restore libcalls, then return a nullptr. +static const char * +getSpillLibCallName(const MachineFunction &MF, + const std::vector &CSI) { + static const char *const SpillLibCalls[] = { + "__riscv_save_0", + "__riscv_save_1", + "__riscv_save_2", + "__riscv_save_3", + "__riscv_save_4", + "__riscv_save_5", + "__riscv_save_6", + "__riscv_save_7", + "__riscv_save_8", + "__riscv_save_9", + "__riscv_save_10", + "__riscv_save_11", + "__riscv_save_12" + }; + + int LibCallID = getLibCallID(MF, CSI); + if (LibCallID == -1) + return nullptr; + return SpillLibCalls[LibCallID]; +} + +// Get the name of the libcall used for restoring callee saved registers. +// If this function will not use save/restore libcalls, then return a nullptr. +static const char * +getRestoreLibCallName(const MachineFunction &MF, + const std::vector &CSI) { + static const char *const RestoreLibCalls[] = { + "__riscv_restore_0", + "__riscv_restore_1", + "__riscv_restore_2", + "__riscv_restore_3", + "__riscv_restore_4", + "__riscv_restore_5", + "__riscv_restore_6", + "__riscv_restore_7", + "__riscv_restore_8", + "__riscv_restore_9", + "__riscv_restore_10", + "__riscv_restore_11", + "__riscv_restore_12" + }; + + int LibCallID = getLibCallID(MF, CSI); + if (LibCallID == -1) + return nullptr; + return RestoreLibCalls[LibCallID]; +} + bool RISCVFrameLowering::hasFP(const MachineFunction &MF) const { const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); @@ -105,6 +199,17 @@ // Returns the register used to hold the stack pointer. static Register getSPReg(const RISCVSubtarget &STI) { return RISCV::X2; } +static SmallVector +getNonLibcallCSI(const std::vector &CSI) { + SmallVector NonLibcallCSI; + + for (auto &CS : CSI) + if (CS.getFrameIdx() >= 0) + NonLibcallCSI.push_back(CS); + + return NonLibcallCSI; +} + void RISCVFrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineFrameInfo &MFI = MF.getFrameInfo(); @@ -117,6 +222,11 @@ Register SPReg = getSPReg(STI); Register BPReg = RISCVABI::getBPReg(); + // Since spillCalleeSavedRegisters may have inserted a libcall, skip past + // any instructions marked as FrameSetup + while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup)) + ++MBBI; + // Debug location must be unknown since the first debug location is used // to determine the end of the prologue. DebugLoc DL; @@ -124,12 +234,38 @@ // Determine the correct frame layout determineFrameLayout(MF); + // If libcalls are used to spill and restore callee-saved registers, the frame + // has two sections; the opaque section managed by the libcalls, and the + // section managed by MachineFrameInfo which can also hold callee saved + // registers in fixed stack slots, both of which have negative frame indices. + // This gets even more complicated when incoming arguments are passed via the + // stack, as these too have negative frame indices. An example is detailed + // below: + // + // | incoming arg | <- FI[-3] + // | libcallspill | + // | calleespill | <- FI[-2] + // | calleespill | <- FI[-1] + // | this_frame | <- FI[0] + // + // For negative frame indices, the offset from the frame pointer will differ + // depending on which of these groups the frame index applies to. + // The following calculates the correct offset knowing the number of callee + // saved registers spilt by the two methods. + if (int LibCallRegs = getLibCallID(MF, MFI.getCalleeSavedInfo()) + 1) { + // Calculate the size of the frame managed by the libcall. The libcalls are + // implemented such that the stack will always be 16 byte aligned. + unsigned LibCallFrameSize = alignTo((STI.getXLen() / 8) * LibCallRegs, 16); + RVFI->setLibCallStackSize(LibCallFrameSize); + } + // FIXME (note copied from Lanai): This appears to be overallocating. Needs // investigation. Get the number of bytes to allocate from the FrameInfo. uint64_t StackSize = MFI.getStackSize(); + uint64_t RealStackSize = StackSize + RVFI->getLibCallStackSize(); // Early exit if there is no need to allocate on the stack - if (StackSize == 0 && !MFI.adjustsStack()) + if (RealStackSize == 0 && !MFI.adjustsStack()) return; // If the stack pointer has been marked as reserved, then produce an error if @@ -140,31 +276,42 @@ uint64_t FirstSPAdjustAmount = getFirstSPAdjustAmount(MF); // Split the SP adjustment to reduce the offsets of callee saved spill. - if (FirstSPAdjustAmount) + if (FirstSPAdjustAmount) { StackSize = FirstSPAdjustAmount; + RealStackSize = FirstSPAdjustAmount; + } // Allocate space on the stack if necessary. adjustReg(MBB, MBBI, DL, SPReg, SPReg, -StackSize, MachineInstr::FrameSetup); - // Emit ".cfi_def_cfa_offset StackSize" + // Emit ".cfi_def_cfa_offset RealStackSize" unsigned CFIIndex = MF.addFrameInst( - MCCFIInstruction::createDefCfaOffset(nullptr, -StackSize)); + MCCFIInstruction::createDefCfaOffset(nullptr, -RealStackSize)); BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex); + const auto &CSI = MFI.getCalleeSavedInfo(); + // The frame pointer is callee-saved, and code has been generated for us to // save it to the stack. We need to skip over the storing of callee-saved // registers as the frame pointer must be modified after it has been saved // to the stack, not before. // FIXME: assumes exactly one instruction is used to save each callee-saved // register. - const std::vector &CSI = MFI.getCalleeSavedInfo(); - std::advance(MBBI, CSI.size()); + std::advance(MBBI, getNonLibcallCSI(CSI).size()); // Iterate over list of callee-saved registers and emit .cfi_offset // directives. for (const auto &Entry : CSI) { - int64_t Offset = MFI.getObjectOffset(Entry.getFrameIdx()); + int FrameIdx = Entry.getFrameIdx(); + int64_t Offset; + // Offsets for objects with fixed locations (IE: those saved by libcall) are + // simply calculated from the frame index. + if (FrameIdx < 0) + Offset = FrameIdx * STI.getXLen() / 8; + else + Offset = MFI.getObjectOffset(Entry.getFrameIdx()) - + RVFI->getLibCallStackSize(); Register Reg = Entry.getReg(); unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( nullptr, RI->getDwarfRegNum(Reg, true), Offset)); @@ -179,7 +326,8 @@ MF.getFunction(), "Frame pointer required, but has been reserved."}); adjustReg(MBB, MBBI, DL, FPReg, SPReg, - StackSize - RVFI->getVarArgsSaveSize(), MachineInstr::FrameSetup); + RealStackSize - RVFI->getVarArgsSaveSize(), + MachineInstr::FrameSetup); // Emit ".cfi_def_cfa $fp, 0" unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createDefCfa( @@ -264,15 +412,26 @@ // last instruction. if (!MBBI->isTerminator()) MBBI = std::next(MBBI); + + // If callee-saved registers are saved via libcall, place stack adjustment + // before this call. + while (MBBI != MBB.begin() && + std::prev(MBBI)->getFlag(MachineInstr::FrameDestroy)) + --MBBI; } + const auto &CSI = getNonLibcallCSI(MFI.getCalleeSavedInfo()); + // Skip to before the restores of callee-saved registers // FIXME: assumes exactly one instruction is used to restore each // callee-saved register. - auto LastFrameDestroy = std::prev(MBBI, MFI.getCalleeSavedInfo().size()); + auto LastFrameDestroy = MBBI; + if (!CSI.empty()) + LastFrameDestroy = std::prev(MBBI, CSI.size()); uint64_t StackSize = MFI.getStackSize(); - uint64_t FPOffset = StackSize - RVFI->getVarArgsSaveSize(); + uint64_t RealStackSize = StackSize + RVFI->getLibCallStackSize(); + uint64_t FPOffset = RealStackSize - RVFI->getVarArgsSaveSize(); // Restore the stack pointer using the value of the frame pointer. Only // necessary if the stack pointer was modified, meaning the stack size is @@ -310,7 +469,7 @@ // Callee-saved registers should be referenced relative to the stack // pointer (positive offset), otherwise use the frame pointer (negative // offset). - const std::vector &CSI = MFI.getCalleeSavedInfo(); + const auto &CSI = getNonLibcallCSI(MFI.getCalleeSavedInfo()); int MinCSFI = 0; int MaxCSFI = -1; @@ -330,7 +489,7 @@ if (FirstSPAdjustAmount) Offset += FirstSPAdjustAmount; else - Offset += MF.getFrameInfo().getStackSize(); + Offset += MFI.getStackSize(); } else if (RI->needsStackRealignment(MF) && !MFI.isFixedObjectIndex(FI)) { // If the stack was realigned, the frame pointer is set in order to allow // SP to be restored, so we need another base register to record the stack @@ -339,13 +498,20 @@ FrameReg = RISCVABI::getBPReg(); else FrameReg = RISCV::X2; - Offset += MF.getFrameInfo().getStackSize(); + Offset += MFI.getStackSize(); + if (FI < 0) + Offset += RVFI->getLibCallStackSize(); } else { FrameReg = RI->getFrameRegister(MF); - if (hasFP(MF)) + if (hasFP(MF)) { Offset += RVFI->getVarArgsSaveSize(); - else - Offset += MF.getFrameInfo().getStackSize(); + if (FI >= 0) + Offset -= RVFI->getLibCallStackSize(); + } else { + Offset += MFI.getStackSize(); + if (FI < 0) + Offset += RVFI->getLibCallStackSize(); + } } return Offset; } @@ -461,16 +627,18 @@ // add sp,sp,-64 uint64_t RISCVFrameLowering::getFirstSPAdjustAmount(const MachineFunction &MF) const { + const auto *RVFI = MF.getInfo(); const MachineFrameInfo &MFI = MF.getFrameInfo(); const std::vector &CSI = MFI.getCalleeSavedInfo(); uint64_t StackSize = MFI.getStackSize(); uint64_t StackAlign = getStackAlignment(); - // FIXME: Disable SplitSPAdjust if save-restore libcall enabled when the patch - // landing. The callee saved registers will be pushed by the - // save-restore libcalls, so we don't have to split the SP adjustment - // in this case. - // + // Disable SplitSPAdjust if save-restore libcall used. The callee saved + // registers will be pushed by the save-restore libcalls, so we don't have to + // split the SP adjustment in this case. + if (RVFI->getLibCallStackSize()) + return 0; + // Return the FirstSPAdjustAmount if the StackSize can not fit in signed // 12-bit and there exists a callee saved register need to be pushed. if (!isInt<12>(StackSize) && (CSI.size() > 0)) { @@ -484,3 +652,125 @@ } return 0; } + +bool RISCVFrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const { + if (CSI.empty()) + return true; + + MachineFunction *MF = MBB.getParent(); + const TargetInstrInfo &TII = *MF->getSubtarget().getInstrInfo(); + DebugLoc DL; + if (MI != MBB.end() && !MI->isDebugInstr()) + DL = MI->getDebugLoc(); + + const char *SpillLibCall = getSpillLibCallName(*MF, CSI); + if (SpillLibCall) { + // Add spill libcall via non-callee-saved register t0. + BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoCALLReg), RISCV::X5) + .addExternalSymbol(SpillLibCall, RISCVII::MO_CALL) + .setMIFlag(MachineInstr::FrameSetup); + + // Add registers spilled in libcall as liveins. + for (auto &CS : CSI) + MBB.addLiveIn(CS.getReg()); + } + + // Manually spill values not spilled by libcall. + const auto &NonLibcallCSI = getNonLibcallCSI(CSI); + for (auto &CS : NonLibcallCSI) { + // Insert the spill to the stack frame. + Register Reg = CS.getReg(); + const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); + TII.storeRegToStackSlot(MBB, MI, Reg, true, CS.getFrameIdx(), RC, TRI); + } + + return true; +} + +bool RISCVFrameLowering::restoreCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + std::vector &CSI, const TargetRegisterInfo *TRI) const { + if (CSI.empty()) + return true; + + MachineFunction *MF = MBB.getParent(); + const TargetInstrInfo &TII = *MF->getSubtarget().getInstrInfo(); + DebugLoc DL; + if (MI != MBB.end() && !MI->isDebugInstr()) + DL = MI->getDebugLoc(); + + // Manually restore values not restored by libcall. Insert in reverse order. + // loadRegFromStackSlot can insert multiple instructions. + const auto &NonLibcallCSI = getNonLibcallCSI(CSI); + for (auto &CS : reverse(NonLibcallCSI)) { + Register Reg = CS.getReg(); + const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); + TII.loadRegFromStackSlot(MBB, MI, Reg, CS.getFrameIdx(), RC, TRI); + assert(MI != MBB.begin() && "loadRegFromStackSlot didn't insert any code!"); + } + + const char *RestoreLibCall = getRestoreLibCallName(*MF, CSI); + 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) { + NewMI->copyImplicitOps(*MF, *MI); + MI->eraseFromParent(); + } + } + + return true; +} + +bool RISCVFrameLowering::canUseAsPrologue(const MachineBasicBlock &MBB) const { + MachineBasicBlock *TmpMBB = const_cast(&MBB); + const auto *RVFI = MBB.getParent()->getInfo(); + + if (!RVFI->useSaveRestoreLibCalls()) + return true; + + // Inserting a call to a __riscv_save libcall requires the use of the register + // t0 (X5) to hold the return address. Therefore if this register is already + // used we can't insert the call. + + RegScavenger RS; + RS.enterBasicBlock(*TmpMBB); + return !RS.isRegUsed(RISCV::X5); +} + +bool RISCVFrameLowering::canUseAsEpilogue(const MachineBasicBlock &MBB) const { + MachineBasicBlock *TmpMBB = const_cast(&MBB); + const auto *RVFI = MBB.getParent()->getInfo(); + + if (!RVFI->useSaveRestoreLibCalls()) + return true; + + // Using the __riscv_restore libcalls to restore CSRs requires a tail call. + // This means if we still need to continue executing code within this function + // the restore cannot take place in this basic block. + + if (MBB.succ_size() > 1) + return false; + + MachineBasicBlock *SuccMBB = + MBB.succ_empty() ? TmpMBB->getFallThrough() : *MBB.succ_begin(); + + // Doing a tail call should be safe if there are no successors, because either + // we have a returning block or the end of the block is unreachable, so the + // restore will be eliminated regardless. + if (!SuccMBB) + return true; + + // The successor can only contain a return, since we would effectively be + // replacing the successor with our own tail return at the end of our block. + return SuccMBB->isReturnBlock() && SuccMBB->size() == 1; +} diff --git a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h --- a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h +++ b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h @@ -13,6 +13,7 @@ #ifndef LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H #define LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H +#include "RISCVSubtarget.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" @@ -30,6 +31,8 @@ /// FrameIndex used for transferring values between 64-bit FPRs and a pair /// of 32-bit GPRs via the stack. int MoveF64FrameIndex = -1; + /// Size of any opaque stack adjustment due to save/restore libcalls. + unsigned LibCallStackSize = 0; public: RISCVMachineFunctionInfo(MachineFunction &MF) : MF(MF) {} @@ -45,6 +48,16 @@ MoveF64FrameIndex = MF.getFrameInfo().CreateStackObject(8, 8, false); return MoveF64FrameIndex; } + + unsigned getLibCallStackSize() const { return LibCallStackSize; } + void setLibCallStackSize(unsigned Size) { LibCallStackSize = Size; } + + bool useSaveRestoreLibCalls() const { + // We cannot use fixed locations for the callee saved spill slots if the + // function uses a varargs save area. + return MF.getSubtarget().enableSaveRestore() && + VarArgsSaveSize == 0 && !MF.getFrameInfo().hasTailCall(); + } }; } // end namespace llvm diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h @@ -37,6 +37,9 @@ const uint32_t *getNoPreservedMask() const override; + bool hasReservedSpillSlot(const MachineFunction &MF, unsigned Reg, + int &FrameIdx) const override; + void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj, unsigned FIOperandNum, RegScavenger *RS = nullptr) const override; diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -12,6 +12,7 @@ #include "RISCVRegisterInfo.h" #include "RISCV.h" +#include "RISCVMachineFunctionInfo.h" #include "RISCVSubtarget.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" @@ -103,6 +104,39 @@ return CSR_NoRegs_RegMask; } +// Frame indexes representing locations of CSRs which are given a fixed location +// by save/restore libcalls. +static const std::map FixedCSRFIMap = { + {/*ra*/ RISCV::X1, -1}, + {/*s0*/ RISCV::X8, -2}, + {/*s1*/ RISCV::X9, -3}, + {/*s2*/ RISCV::X18, -4}, + {/*s3*/ RISCV::X19, -5}, + {/*s4*/ RISCV::X20, -6}, + {/*s5*/ RISCV::X21, -7}, + {/*s6*/ RISCV::X22, -8}, + {/*s7*/ RISCV::X23, -9}, + {/*s8*/ RISCV::X24, -10}, + {/*s9*/ RISCV::X25, -11}, + {/*s10*/ RISCV::X26, -12}, + {/*s11*/ RISCV::X27, -13} +}; + +bool RISCVRegisterInfo::hasReservedSpillSlot(const MachineFunction &MF, + unsigned Reg, + int &FrameIdx) const { + const auto *RVFI = MF.getInfo(); + if (!RVFI->useSaveRestoreLibCalls()) + return false; + + auto FII = FixedCSRFIMap.find(Reg); + if (FII == FixedCSRFIMap.end()) + return false; + + FrameIdx = FII->second; + return true; +} + void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, unsigned FIOperandNum, RegScavenger *RS) const { diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h --- a/llvm/lib/Target/RISCV/RISCVSubtarget.h +++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h @@ -43,6 +43,7 @@ bool IsRV32E = false; bool EnableLinkerRelax = false; bool EnableRVCHintInstrs = false; + bool EnableSaveRestore = false; unsigned XLen = 32; MVT XLenVT = MVT::i32; RISCVABI::ABI TargetABI = RISCVABI::ABI_Unknown; @@ -91,6 +92,7 @@ bool isRV32E() const { return IsRV32E; } bool enableLinkerRelax() const { return EnableLinkerRelax; } bool enableRVCHintInstrs() const { return EnableRVCHintInstrs; } + bool enableSaveRestore() const { return EnableSaveRestore; } MVT getXLenVT() const { return XLenVT; } unsigned getXLen() const { return XLen; } RISCVABI::ABI getTargetABI() const { return TargetABI; } diff --git a/llvm/test/CodeGen/RISCV/saverestore.ll b/llvm/test/CodeGen/RISCV/saverestore.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/saverestore.ll @@ -0,0 +1,299 @@ +; RUN: llc -mtriple=riscv32 < %s | FileCheck %s -check-prefix=RV32I +; RUN: llc -mtriple=riscv64 < %s | FileCheck %s -check-prefix=RV64I +; RUN: llc -mtriple=riscv32 -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV32I-SR +; RUN: llc -mtriple=riscv64 -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV64I-SR +; RUN: llc -mtriple=riscv32 -mattr=+f,+save-restore -target-abi=ilp32f < %s | FileCheck %s -check-prefix=RV32I-FP-SR +; RUN: llc -mtriple=riscv64 -mattr=+f,+d,+save-restore -target-abi=lp64d < %s | FileCheck %s -check-prefix=RV64I-FP-SR + +; Check that the correct save/restore libcalls are generated. + +@var0 = global [18 x i32] zeroinitializer +@var1 = global [24 x i32] zeroinitializer +@var2 = global [30 x i32] zeroinitializer + +define void @callee_saved0() nounwind { +; RV32I-LABEL: callee_saved0: +; RV32I-NOT: call t0, __riscv_save +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: callee_saved0: +; RV64I-NOT: call t0, __riscv_save +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-LABEL: callee_saved0: +; RV32I-SR: call t0, __riscv_save_5 +; RV32I-SR: tail __riscv_restore_5 +; +; RV64I-SR-LABEL: callee_saved0: +; RV64I-SR: call t0, __riscv_save_5 +; RV64I-SR: tail __riscv_restore_5 +; +; RV32I-FP-SR-LABEL: callee_saved0: +; RV32I-FP-SR: call t0, __riscv_save_5 +; RV32I-FP-SR: tail __riscv_restore_5 +; +; RV64I-FP-SR-LABEL: callee_saved0: +; RV64I-FP-SR: call t0, __riscv_save_5 +; RV64I-FP-SR: tail __riscv_restore_5 + %val = load [18 x i32], [18 x i32]* @var0 + store volatile [18 x i32] %val, [18 x i32]* @var0 + ret void +} + +define void @callee_saved1() nounwind { +; RV32I-LABEL: callee_saved1: +; RV32I-NOT: call t0, __riscv_save +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: callee_saved1: +; RV64I-NOT: call t0, __riscv_save +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-LABEL: callee_saved1: +; RV32I-SR: call t0, __riscv_save_11 +; RV32I-SR: tail __riscv_restore_11 +; +; RV64I-SR-LABEL: callee_saved1: +; RV64I-SR: call t0, __riscv_save_11 +; RV64I-SR: tail __riscv_restore_11 +; +; RV32I-FP-SR-LABEL: callee_saved1: +; RV32I-FP-SR: call t0, __riscv_save_11 +; RV32I-FP-SR: tail __riscv_restore_11 +; +; RV64I-FP-SR-LABEL: callee_saved1: +; RV64I-FP-SR: call t0, __riscv_save_11 +; RV64I-FP-SR: tail __riscv_restore_11 + %val = load [24 x i32], [24 x i32]* @var1 + store volatile [24 x i32] %val, [24 x i32]* @var1 + ret void +} + +define void @callee_saved2() nounwind { +; RV32I-LABEL: callee_saved2: +; RV32I-NOT: call t0, __riscv_save +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: callee_saved2: +; RV64I-NOT: call t0, __riscv_save +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-LABEL: callee_saved2: +; RV32I-SR: call t0, __riscv_save_12 +; RV32I-SR: tail __riscv_restore_12 +; +; RV64I-SR-LABEL: callee_saved2: +; RV64I-SR: call t0, __riscv_save_12 +; RV64I-SR: tail __riscv_restore_12 +; +; RV32I-FP-SR-LABEL: callee_saved2: +; RV32I-FP-SR: call t0, __riscv_save_12 +; RV32I-FP-SR: tail __riscv_restore_12 +; +; RV64I-FP-SR-LABEL: callee_saved2: +; RV64I-FP-SR: call t0, __riscv_save_12 +; RV64I-FP-SR: tail __riscv_restore_12 + %val = load [30 x i32], [30 x i32]* @var2 + store volatile [30 x i32] %val, [30 x i32]* @var2 + ret void +} + +; Check that floating point callee saved registers are still manually saved and +; restored. + +define void @callee_saved_fp() nounwind { +; RV32I-LABEL: callee_saved_fp: +; RV32I-NOT: call t0, __riscv_save +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: callee_saved_fp: +; RV64I-NOT: call t0, __riscv_save +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-LABEL: callee_saved_fp: +; RV32I-SR: call t0, __riscv_save_7 +; RV32I-SR: tail __riscv_restore_7 +; +; RV64I-SR-LABEL: callee_saved_fp: +; RV64I-SR: call t0, __riscv_save_7 +; RV64I-SR: tail __riscv_restore_7 +; +; RV32I-FP-SR-LABEL: callee_saved_fp: +; RV32I-FP-SR: call t0, __riscv_save_7 +; RV32I-FP-SR-NEXT: addi sp, sp, -16 +; RV32I-FP-SR-NEXT: fsw fs0, 12(sp) +; RV32I-FP-SR: flw fs0, 12(sp) +; RV32I-FP-SR-NEXT: addi sp, sp, 16 +; RV32I-FP-SR-NEXT: tail __riscv_restore_7 +; +; RV64I-FP-SR-LABEL: callee_saved_fp: +; RV64I-FP-SR: call t0, __riscv_save_7 +; RV64I-FP-SR-NEXT: addi sp, sp, -16 +; RV64I-FP-SR-NEXT: fsd fs0, 8(sp) +; RV64I-FP-SR: fld fs0, 8(sp) +; RV64I-FP-SR-NEXT: addi sp, sp, 16 +; RV64I-FP-SR-NEXT: tail __riscv_restore_7 + call void asm sideeffect "", "~{f8},~{x9},~{x18},~{x19},~{x20},~{x21},~{x22}"() + 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*) +declare void @llvm.va_end(i8*) + +define i32 @varargs(i8* %fmt, ...) nounwind { +; RV32I-LABEL: varargs: +; RV32I-NOT: call t0, __riscv_save +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: varargs: +; RV64I-NOT: call t0, __riscv_save +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-LABEL: varargs: +; RV32I-SR-NOT: call t0, __riscv_save +; RV32I-SR-NOT: tail __riscv_restore +; +; RV64I-SR-LABEL: varargs: +; RV64I-SR-NOT: call t0, __riscv_save +; RV64I-SR-NOT: tail __riscv_restore +; +; RV32I-FP-SR-LABEL: varargs: +; RV32I-FP-SR-NOT: call t0, __riscv_save +; RV32I-FP-SR-NOT: tail __riscv_restore +; +; RV64I-FP-SR-LABEL: varargs: +; RV64I-FP-SR-NOT: call t0, __riscv_save +; RV64I-FP-SR-NOT: tail __riscv_restore + %va = alloca i8*, align 4 + %1 = bitcast i8** %va to i8* + call void @llvm.va_start(i8* %1) + %argp.cur = load i8*, i8** %va, align 4 + %argp.next = getelementptr inbounds i8, i8* %argp.cur, i32 4 + store i8* %argp.next, i8** %va, align 4 + %2 = bitcast i8* %argp.cur to i32* + %3 = load i32, i32* %2, align 4 + call void @llvm.va_end(i8* %1) + ret i32 %3 +} + +define void @many_args(i32, i32, i32, i32, i32, i32, i32, i32, i32) nounwind { +; RV32I-LABEL: many_args: +; RV32I-NOT: call t0, __riscv_save +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: many_args: +; RV64I-NOT: call t0, __riscv_save +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-LABEL: many_args: +; RV32I-SR: call t0, __riscv_save_5 +; RV32I-SR: tail __riscv_restore_5 +; +; RV64I-SR-LABEL: many_args: +; RV64I-SR: call t0, __riscv_save_5 +; RV64I-SR: tail __riscv_restore_5 +; +; RV32I-FP-SR-LABEL: many_args: +; RV32I-FP-SR: call t0, __riscv_save_5 +; RV32I-FP-SR: tail __riscv_restore_5 +; +; RV64I-FP-SR-LABEL: many_args: +; RV64I-FP-SR: call t0, __riscv_save_5 +; RV64I-FP-SR: tail __riscv_restore_5 +entry: + %val = load [18 x i32], [18 x i32]* @var0 + store volatile [18 x i32] %val, [18 x i32]* @var0 + ret void +} + +; Check that dynamic allocation calculations remain correct + +declare i8* @llvm.stacksave() +declare void @llvm.stackrestore(i8*) +declare void @notdead(i8*) + +define void @alloca(i32 %n) nounwind { +; RV32I-LABEL: alloca: +; RV32I-NOT: call t0, __riscv_save +; RV32I: addi s0, sp, 16 +; RV32I: addi sp, s0, -16 +; RV32I-NOT: tail __riscv_restore +; +; RV64I-LABEL: alloca: +; RV64I-NOT: call t0, __riscv_save +; RV64I: addi s0, sp, 32 +; RV64I: addi sp, s0, -32 +; RV64I-NOT: tail __riscv_restore +; +; RV32I-SR-LABEL: alloca: +; RV32I-SR: call t0, __riscv_save_2 +; RV32I-SR: addi s0, sp, 16 +; RV32I-SR: addi sp, s0, -16 +; RV32I-SR: tail __riscv_restore_2 +; +; RV64I-SR-LABEL: alloca: +; RV64I-SR: call t0, __riscv_save_2 +; RV64I-SR: addi s0, sp, 32 +; RV64I-SR: addi sp, s0, -32 +; RV64I-SR: tail __riscv_restore_2 +; +; RV32I-FP-SR-LABEL: alloca: +; RV32I-FP-SR: call t0, __riscv_save_2 +; RV32I-FP-SR: addi s0, sp, 16 +; RV32I-FP-SR: addi sp, s0, -16 +; RV32I-FP-SR: tail __riscv_restore_2 +; +; RV64I-FP-SR-LABEL: alloca: +; RV64I-FP-SR: call t0, __riscv_save_2 +; RV64I-FP-SR: addi s0, sp, 32 +; RV64I-FP-SR: addi sp, s0, -32 +; RV64I-FP-SR: tail __riscv_restore_2 + %sp = call i8* @llvm.stacksave() + %addr = alloca i8, i32 %n + call void @notdead(i8* %addr) + call void @llvm.stackrestore(i8* %sp) + ret void +} diff --git a/llvm/test/CodeGen/RISCV/shrinkwrap.ll b/llvm/test/CodeGen/RISCV/shrinkwrap.ll --- a/llvm/test/CodeGen/RISCV/shrinkwrap.ll +++ b/llvm/test/CodeGen/RISCV/shrinkwrap.ll @@ -1,6 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple riscv32 < %s | FileCheck %s -check-prefix=RV32I-NOSW ; RUN: llc -mtriple riscv32 -enable-shrink-wrap < %s | FileCheck %s -check-prefix=RV32I-SW +; RUN: llc -mtriple riscv32 -enable-shrink-wrap -mattr=+save-restore < %s \ +; RUN: | FileCheck %s -check-prefix=RV32I-SW-SR declare void @abort() @@ -29,6 +31,16 @@ ; RV32I-SW-NEXT: addi sp, sp, -16 ; RV32I-SW-NEXT: sw ra, 12(sp) ; RV32I-SW-NEXT: call abort +; +; RV32I-SW-SR-LABEL: eliminate_restore: +; RV32I-SW-SR: # %bb.0: +; RV32I-SW-SR-NEXT: addi a1, zero, 32 +; RV32I-SW-SR-NEXT: bgeu a1, a0, .LBB0_2 +; RV32I-SW-SR-NEXT: # %bb.1: # %if.end +; RV32I-SW-SR-NEXT: ret +; RV32I-SW-SR-NEXT: .LBB0_2: # %if.then +; RV32I-SW-SR-NEXT: call t0, __riscv_save_0 +; RV32I-SW-SR-NEXT: call abort %cmp = icmp ule i32 %n, 32 br i1 %cmp, label %if.then, label %if.end @@ -84,6 +96,23 @@ ; RV32I-SW-NEXT: addi sp, sp, 16 ; RV32I-SW-NEXT: .LBB1_2: # %if.end ; RV32I-SW-NEXT: ret +; +; RV32I-SW-SR-LABEL: conditional_alloca: +; RV32I-SW-SR: # %bb.0: +; RV32I-SW-SR-NEXT: addi a1, zero, 32 +; RV32I-SW-SR-NEXT: bltu a1, a0, .LBB1_2 +; RV32I-SW-SR-NEXT: # %bb.1: # %if.then +; RV32I-SW-SR-NEXT: call t0, __riscv_save_1 +; RV32I-SW-SR-NEXT: addi s0, sp, 16 +; RV32I-SW-SR-NEXT: addi a0, a0, 15 +; RV32I-SW-SR-NEXT: andi a0, a0, -16 +; RV32I-SW-SR-NEXT: sub a0, sp, a0 +; RV32I-SW-SR-NEXT: mv sp, a0 +; RV32I-SW-SR-NEXT: call notdead +; RV32I-SW-SR-NEXT: addi sp, s0, -16 +; RV32I-SW-SR-NEXT: tail __riscv_restore_1 +; RV32I-SW-SR-NEXT: .LBB1_2: # %if.end +; RV32I-SW-SR-NEXT: ret %cmp = icmp ule i32 %n, 32 br i1 %cmp, label %if.then, label %if.end