lewis-revill updated this revision to Diff 226166.
lewis-revill added a comment.
Rebase on top of shrink wrapping patch.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D62686/new/
https://reviews.llvm.org/D62686
Files:
clang/lib/Driver/ToolChains/Arch/RISCV.cpp
clang/test/Driver/riscv-features.c
llvm/lib/Target/RISCV/RISCV.td
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
llvm/lib/Target/RISCV/RISCVFrameLowering.h
llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
llvm/lib/Target/RISCV/RISCVRegisterInfo.h
llvm/lib/Target/RISCV/RISCVSubtarget.h
llvm/test/CodeGen/RISCV/saverestore.ll
Index: llvm/test/CodeGen/RISCV/saverestore.ll
===================================================================
--- /dev/null
+++ 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
+}
Index: llvm/lib/Target/RISCV/RISCVSubtarget.h
===================================================================
--- llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ 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; }
Index: llvm/lib/Target/RISCV/RISCVRegisterInfo.h
===================================================================
--- llvm/lib/Target/RISCV/RISCVRegisterInfo.h
+++ 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;
Index: llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
===================================================================
--- llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ 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"
@@ -100,6 +101,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<unsigned, int> 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<RISCVMachineFunctionInfo>();
+ 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 {
Index: llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
===================================================================
--- llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
+++ 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<RISCVSubtarget>().enableSaveRestore() &&
+ VarArgsSaveSize == 0 && !MF.getFrameInfo().hasTailCall();
+ }
};
} // end namespace llvm
Index: llvm/lib/Target/RISCV/RISCVFrameLowering.h
===================================================================
--- llvm/lib/Target/RISCV/RISCVFrameLowering.h
+++ llvm/lib/Target/RISCV/RISCVFrameLowering.h
@@ -44,6 +44,15 @@
MachineBasicBlock::iterator
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI) const override;
+ bool spillCalleeSavedRegisters(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MI,
+ const std::vector<CalleeSavedInfo> &CSI,
+ const TargetRegisterInfo *TRI) const override;
+ bool
+ restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MI,
+ std::vector<CalleeSavedInfo> &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
Index: llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
===================================================================
--- llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ 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<CalleeSavedInfo> &CSI) {
+ const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+ 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<CalleeSavedInfo> &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<CalleeSavedInfo> &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();
@@ -98,6 +192,17 @@
// Returns the register used to hold the stack pointer.
static Register getSPReg(const RISCVSubtarget &STI) { return RISCV::X2; }
+static SmallVector<CalleeSavedInfo, 8>
+getNonLibcallCSI(const std::vector<CalleeSavedInfo> &CSI) {
+ SmallVector<CalleeSavedInfo, 8> 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();
@@ -115,6 +220,11 @@
Register FPReg = getFPReg(STI);
Register SPReg = getSPReg(STI);
+ // 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;
@@ -122,12 +232,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
@@ -138,8 +274,10 @@
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);
@@ -150,24 +288,26 @@
BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
- // 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<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
- std::advance(MBBI, 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());
- Register Reg = Entry.getReg();
- unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
- nullptr, RI->getDwarfRegNum(Reg, true), Offset));
- BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
- .addCFIIndex(CFIIndex);
+ const auto &CSI = getNonLibcallCSI(MFI.getCalleeSavedInfo());
+ if (!CSI.empty()) {
+ // 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.
+ std::advance(MBBI, 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());
+ Register Reg = Entry.getReg();
+ unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
+ nullptr, RI->getDwarfRegNum(Reg, true), Offset));
+ BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
+ .addCFIIndex(CFIIndex);
+ }
}
// Generate new FP.
@@ -177,7 +317,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(
@@ -249,15 +390,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
@@ -307,15 +459,16 @@
}
// Add CFI directives for callee-saved registers.
- const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
- // Iterate over list of callee-saved registers and emit .cfi_restore
- // directives.
- for (const auto &Entry : CSI) {
- Register Reg = Entry.getReg();
- unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestore(
- nullptr, RI->getDwarfRegNum(Reg, true)));
- BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
- .addCFIIndex(CFIIndex);
+ if (!CSI.empty()) {
+ // Iterate over list of callee-saved registers and emit .cfi_restore
+ // directives.
+ for (const auto &Entry : CSI) {
+ Register Reg = Entry.getReg();
+ unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestore(
+ nullptr, RI->getDwarfRegNum(Reg, true)));
+ BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
+ .addCFIIndex(CFIIndex);
+ }
}
if (FirstSPAdjustAmount)
@@ -342,7 +495,7 @@
// Callee-saved registers should be referenced relative to the stack
// pointer (positive offset), otherwise use the frame pointer (negative
// offset).
- const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
+ const auto &CSI = getNonLibcallCSI(MFI.getCalleeSavedInfo());
int MinCSFI = 0;
int MaxCSFI = -1;
@@ -362,20 +515,27 @@
if (FirstSPAdjustAmount)
Offset += FirstSPAdjustAmount;
else
- Offset += MF.getFrameInfo().getStackSize();
+ Offset += MFI.getStackSize();
} else if (RI->needsStackRealignment(MF)) {
assert(!MFI.hasVarSizedObjects() &&
"Unexpected combination of stack realignment and varsized objects");
// If the stack was realigned, the frame pointer is set in order to allow
// SP to be restored, but we still access stack objects using SP.
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;
}
@@ -488,16 +648,18 @@
// add sp,sp,-64
uint64_t
RISCVFrameLowering::getFirstSPAdjustAmount(const MachineFunction &MF) const {
+ const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
const MachineFrameInfo &MFI = MF.getFrameInfo();
const std::vector<CalleeSavedInfo> &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)) {
@@ -511,3 +673,81 @@
}
return 0;
}
+
+bool RISCVFrameLowering::spillCalleeSavedRegisters(
+ MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
+ const std::vector<CalleeSavedInfo> &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<CalleeSavedInfo> &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;
+}
Index: llvm/lib/Target/RISCV/RISCV.td
===================================================================
--- llvm/lib/Target/RISCV/RISCV.td
+++ llvm/lib/Target/RISCV/RISCV.td
@@ -74,6 +74,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.
//===----------------------------------------------------------------------===//
Index: clang/test/Driver/riscv-features.c
===================================================================
--- clang/test/Driver/riscv-features.c
+++ 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
Index: clang/lib/Driver/ToolChains/Arch/RISCV.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Arch/RISCV.cpp
+++ clang/lib/Driver/ToolChains/Arch/RISCV.cpp
@@ -438,12 +438,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.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits