Skip to content

Commit 176419d

Browse files
committed
[RISCV] Support Push/Pop with Xqci
The `qc.c.mienter` and `qc.c.mienter.nest` instructions, broadly only save the argument and temporary registers. The exceptions are that they also save `fp` (`s0`) to construct a frame chain from the signal handler to the frame below, and they also save `ra`. They are designed this way so that (if needed) push and pop instructions can be used to save the callee-saved registers. This patch implements this optimisation, constructing the following rather than a long sequence of `sw` and `lw` instructions for saving the callee-saved registers: ```asm qc.c.mienter qc.cm.push {ra, s0-sN}, -M ... qc.cm.pop {ra, s0-sN}, M qc.c.mileaveret ``` There are some carefully-worked-out details here, especially around CFI information. For any register saved by both `qc.c.mienter(.nest)` and the push (which is `ra` and `s0` at most), we point the CFI information at the version saved by `qc.c.mienter(.nest)`. This ensures the CFI points at the same `fp` copy as a frame pointer unwinder would find.
1 parent d4ee63e commit 176419d

File tree

2 files changed

+627
-839
lines changed

2 files changed

+627
-839
lines changed

llvm/lib/Target/RISCV/RISCVFrameLowering.cpp

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,18 @@ getPushOrLibCallsSavedInfo(const MachineFunction &MF,
486486
return PushOrLibCallsCSI;
487487

488488
for (const auto &CS : CSI) {
489+
if (RVFI->useQCIInterrupt(MF)) {
490+
// Some registers are saved by both `QC.C.MIENTER(.NEST)` and
491+
// `QC.CM.PUSH(FP)`. In these cases, prioritise the CFI info that points
492+
// to the versions saved by `QC.C.MIENTER(.NEST)` which is what FP
493+
// unwinding would use.
494+
const auto *FII = llvm::find_if(FixedCSRFIQCIInterruptMap, [&](auto P) {
495+
return P.first == CS.getReg();
496+
});
497+
if (FII != std::end(FixedCSRFIQCIInterruptMap))
498+
continue;
499+
}
500+
489501
const auto *FII = llvm::find_if(
490502
FixedCSRFIMap, [&](MCPhysReg P) { return P == CS.getReg(); });
491503
if (FII != std::end(FixedCSRFIMap))
@@ -826,12 +838,12 @@ static bool isPop(unsigned Opcode) {
826838
}
827839

828840
static unsigned getPushOpcode(RISCVMachineFunctionInfo::PushPopKind Kind,
829-
bool HasFP) {
841+
bool UpdateFP) {
830842
switch (Kind) {
831843
case RISCVMachineFunctionInfo::PushPopKind::StdExtZcmp:
832844
return RISCV::CM_PUSH;
833845
case RISCVMachineFunctionInfo::PushPopKind::VendorXqccmp:
834-
return HasFP ? RISCV::QC_CM_PUSHFP : RISCV::QC_CM_PUSH;
846+
return UpdateFP ? RISCV::QC_CM_PUSHFP : RISCV::QC_CM_PUSH;
835847
default:
836848
llvm_unreachable("Unhandled PushPopKind");
837849
}
@@ -872,7 +884,10 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
872884
// Emit prologue for shadow call stack.
873885
emitSCSPrologue(MF, MBB, MBBI, DL);
874886

875-
auto FirstFrameSetup = MBBI;
887+
// We keep track of the first instruction because it might be a
888+
// `(QC.)CM.PUSH(FP)`, and we may need to adjust the immediate rather than
889+
// inserting an `addi sp, sp, -N*16`
890+
auto PossiblePush = MBBI;
876891

877892
// Skip past all callee-saved register spill instructions.
878893
while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup))
@@ -949,22 +964,32 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
949964
}
950965

951966
if (RVFI->useQCIInterrupt(MF)) {
967+
// The function starts with `QC.C.MIENTER(.NEST)`, so the `(QC.)CM.PUSH(FP)`
968+
// could only be the next instruction.
969+
PossiblePush = std::next(PossiblePush);
970+
971+
// Insert the CFI metadata before where we think the `(QC.)CM.PUSH(FP)`
972+
// could be. The PUSH will also get its own CFI metadata for its own
973+
// modifications, which should come after the PUSH.
952974
unsigned CFIIndex = MF.addFrameInst(
953975
MCCFIInstruction::cfiDefCfaOffset(nullptr, QCIInterruptPushAmount));
954-
BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
976+
BuildMI(MBB, PossiblePush, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
955977
.addCFIIndex(CFIIndex)
956978
.setMIFlag(MachineInstr::FrameSetup);
957979

958-
emitCFIForCSI<CFISaveRegisterEmitter>(MBB, MBBI, getQCISavedInfo(MF, CSI));
959-
} else if (RVFI->isPushable(MF) && FirstFrameSetup != MBB.end() &&
960-
isPush(FirstFrameSetup->getOpcode())) {
980+
emitCFIForCSI<CFISaveRegisterEmitter>(MBB, PossiblePush,
981+
getQCISavedInfo(MF, CSI));
982+
}
983+
984+
if (RVFI->isPushable(MF) && PossiblePush != MBB.end() &&
985+
isPush(PossiblePush->getOpcode())) {
961986
// Use available stack adjustment in push instruction to allocate additional
962987
// stack space. Align the stack size down to a multiple of 16. This is
963988
// needed for RVE.
964989
// FIXME: Can we increase the stack size to a multiple of 16 instead?
965990
uint64_t Spimm =
966991
std::min(alignDown(StackSize, 16), static_cast<uint64_t>(48));
967-
FirstFrameSetup->getOperand(1).setImm(Spimm);
992+
PossiblePush->getOperand(1).setImm(Spimm);
968993
StackSize -= Spimm;
969994

970995
unsigned CFIIndex = MF.addFrameInst(
@@ -1868,10 +1893,13 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
18681893
auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
18691894
if (RVFI->useQCIInterrupt(MF)) {
18701895
RVFI->setQCIInterruptStackSize(QCIInterruptPushAmount);
1871-
} else if (RVFI->isPushable(MF)) {
1896+
}
1897+
1898+
if (RVFI->isPushable(MF)) {
18721899
// Determine how many GPRs we need to push and save it to RVFI.
18731900
unsigned PushedRegNum = getNumPushPopRegs(CSI);
1874-
if (PushedRegNum) {
1901+
unsigned PushMoreThan = RVFI->useQCIInterrupt(MF) ? 2 : 0;
1902+
if (PushedRegNum > PushMoreThan) {
18751903
RVFI->setRVPushRegs(PushedRegNum);
18761904
RVFI->setRVPushStackSize(alignTo((STI.getXLen() / 8) * PushedRegNum, 16));
18771905
}
@@ -1897,8 +1925,9 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
18971925
CS.setFrameIdx(FrameIdx);
18981926
continue;
18991927
}
1900-
// TODO: QCI Interrupt + Push/Pop
1901-
} else if (RVFI->useSaveRestoreLibCalls(MF) || RVFI->isPushable(MF)) {
1928+
}
1929+
1930+
if (RVFI->useSaveRestoreLibCalls(MF) || RVFI->isPushable(MF)) {
19021931
const auto *FII = llvm::find_if(
19031932
FixedCSRFIMap, [&](MCPhysReg P) { return P == CS.getReg(); });
19041933
unsigned RegNum = std::distance(std::begin(FixedCSRFIMap), FII);
@@ -1911,6 +1940,9 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
19111940
else
19121941
Offset = -int64_t(RegNum + 1) * Size;
19131942

1943+
if (RVFI->useQCIInterrupt(MF))
1944+
Offset -= QCIInterruptPushAmount;
1945+
19141946
int FrameIdx = MFI.CreateFixedSpillStackObject(Size, Offset);
19151947
assert(FrameIdx < 0);
19161948
CS.setFrameIdx(FrameIdx);
@@ -1939,12 +1971,17 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
19391971
// because there are gaps which are reserved for future use.
19401972
MFI.CreateFixedSpillStackObject(
19411973
QCIInterruptPushAmount, -static_cast<int64_t>(QCIInterruptPushAmount));
1942-
} else if (RVFI->isPushable(MF)) {
1974+
}
1975+
1976+
if (RVFI->isPushable(MF)) {
19431977
// Allocate a fixed object that covers all the registers that are pushed.
19441978
if (unsigned PushedRegs = RVFI->getRVPushRegs()) {
19451979
int64_t PushedRegsBytes =
19461980
static_cast<int64_t>(PushedRegs) * (STI.getXLen() / 8);
1947-
MFI.CreateFixedSpillStackObject(PushedRegsBytes, -PushedRegsBytes);
1981+
int64_t QCIOffset =
1982+
RVFI->useQCIInterrupt(MF) ? QCIInterruptPushAmount : 0;
1983+
MFI.CreateFixedSpillStackObject(PushedRegsBytes,
1984+
-PushedRegsBytes - QCIOffset);
19481985
}
19491986
} else if (int LibCallRegs = getLibCallID(MF, CSI) + 1) {
19501987
// Allocate a fixed object that covers all of the stack allocated by the
@@ -1982,13 +2019,15 @@ bool RISCVFrameLowering::spillCalleeSavedRegisters(
19822019

19832020
for (auto [Reg, _Offset] : FixedCSRFIQCIInterruptMap)
19842021
MBB.addLiveIn(Reg);
1985-
// TODO: Handle QCI Interrupt + Push/Pop
1986-
} else if (RVFI->isPushable(*MF)) {
2022+
}
2023+
2024+
if (RVFI->isPushable(*MF)) {
19872025
// Emit CM.PUSH with base SPimm & evaluate Push stack
19882026
unsigned PushedRegNum = RVFI->getRVPushRegs();
19892027
if (PushedRegNum > 0) {
19902028
// Use encoded number to represent registers to spill.
1991-
unsigned Opcode = getPushOpcode(RVFI->getPushPopKind(*MF), hasFP(*MF));
2029+
unsigned Opcode = getPushOpcode(
2030+
RVFI->getPushPopKind(*MF), hasFP(*MF) && !RVFI->useQCIInterrupt(*MF));
19922031
unsigned RegEnc = RISCVZC::encodeRlistNumRegs(PushedRegNum);
19932032
MachineInstrBuilder PushBuilder =
19942033
BuildMI(MBB, MI, DL, TII.get(Opcode))
@@ -2146,8 +2185,9 @@ bool RISCVFrameLowering::restoreCalleeSavedRegisters(
21462185
// QC.C.MILEAVERET which we already inserted to return.
21472186
assert(MI->getOpcode() == RISCV::QC_C_MILEAVERET &&
21482187
"Unexpected QCI Interrupt Return Instruction");
2149-
// TODO: Handle QCI + Push/Pop
2150-
} else if (RVFI->isPushable(*MF)) {
2188+
}
2189+
2190+
if (RVFI->isPushable(*MF)) {
21512191
unsigned PushedRegNum = RVFI->getRVPushRegs();
21522192
if (PushedRegNum > 0) {
21532193
unsigned Opcode = getPopOpcode(RVFI->getPushPopKind(*MF));

0 commit comments

Comments
 (0)