Skip to content

Commit 3f46af9

Browse files
authored
[RISCV] Support Push/Pop with Xqci (#134191)
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 258e143 commit 3f46af9

File tree

2 files changed

+2607
-287
lines changed

2 files changed

+2607
-287
lines changed

llvm/lib/Target/RISCV/RISCVFrameLowering.cpp

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,18 @@ getPushOrLibCallsSavedInfo(const MachineFunction &MF,
568568
return PushOrLibCallsCSI;
569569

570570
for (const auto &CS : CSI) {
571+
if (RVFI->useQCIInterrupt(MF)) {
572+
// Some registers are saved by both `QC.C.MIENTER(.NEST)` and
573+
// `QC.CM.PUSH(FP)`. In these cases, prioritise the CFI info that points
574+
// to the versions saved by `QC.C.MIENTER(.NEST)` which is what FP
575+
// unwinding would use.
576+
const auto *FII = llvm::find_if(FixedCSRFIQCIInterruptMap, [&](auto P) {
577+
return P.first == CS.getReg();
578+
});
579+
if (FII != std::end(FixedCSRFIQCIInterruptMap))
580+
continue;
581+
}
582+
571583
const auto *FII = llvm::find_if(
572584
FixedCSRFIMap, [&](MCPhysReg P) { return P == CS.getReg(); });
573585
if (FII != std::end(FixedCSRFIMap))
@@ -866,12 +878,12 @@ static bool isPop(unsigned Opcode) {
866878
}
867879

868880
static unsigned getPushOpcode(RISCVMachineFunctionInfo::PushPopKind Kind,
869-
bool HasFP) {
881+
bool UpdateFP) {
870882
switch (Kind) {
871883
case RISCVMachineFunctionInfo::PushPopKind::StdExtZcmp:
872884
return RISCV::CM_PUSH;
873885
case RISCVMachineFunctionInfo::PushPopKind::VendorXqccmp:
874-
return HasFP ? RISCV::QC_CM_PUSHFP : RISCV::QC_CM_PUSH;
886+
return UpdateFP ? RISCV::QC_CM_PUSHFP : RISCV::QC_CM_PUSH;
875887
default:
876888
llvm_unreachable("Unhandled PushPopKind");
877889
}
@@ -914,7 +926,10 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
914926
// Emit prologue for shadow call stack.
915927
emitSCSPrologue(MF, MBB, MBBI, DL);
916928

917-
auto FirstFrameSetup = MBBI;
929+
// We keep track of the first instruction because it might be a
930+
// `(QC.)CM.PUSH(FP)`, and we may need to adjust the immediate rather than
931+
// inserting an `addi sp, sp, -N*16`
932+
auto PossiblePush = MBBI;
918933

919934
// Skip past all callee-saved register spill instructions.
920935
while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup))
@@ -988,19 +1003,29 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
9881003
}
9891004

9901005
if (RVFI->useQCIInterrupt(MF)) {
991-
CFIBuilder.buildDefCFAOffset(QCIInterruptPushAmount);
1006+
// The function starts with `QC.C.MIENTER(.NEST)`, so the `(QC.)CM.PUSH(FP)`
1007+
// could only be the next instruction.
1008+
++PossiblePush;
1009+
1010+
// Insert the CFI metadata before where we think the `(QC.)CM.PUSH(FP)`
1011+
// could be. The PUSH will also get its own CFI metadata for its own
1012+
// modifications, which should come after the PUSH.
1013+
CFIInstBuilder PushCFIBuilder(MBB, PossiblePush, MachineInstr::FrameSetup);
1014+
PushCFIBuilder.buildDefCFAOffset(QCIInterruptPushAmount);
9921015
for (const CalleeSavedInfo &CS : getQCISavedInfo(MF, CSI))
993-
CFIBuilder.buildOffset(CS.getReg(),
994-
MFI.getObjectOffset(CS.getFrameIdx()));
995-
} else if (RVFI->isPushable(MF) && FirstFrameSetup != MBB.end() &&
996-
isPush(FirstFrameSetup->getOpcode())) {
1016+
PushCFIBuilder.buildOffset(CS.getReg(),
1017+
MFI.getObjectOffset(CS.getFrameIdx()));
1018+
}
1019+
1020+
if (RVFI->isPushable(MF) && PossiblePush != MBB.end() &&
1021+
isPush(PossiblePush->getOpcode())) {
9971022
// Use available stack adjustment in push instruction to allocate additional
9981023
// stack space. Align the stack size down to a multiple of 16. This is
9991024
// needed for RVE.
10001025
// FIXME: Can we increase the stack size to a multiple of 16 instead?
10011026
uint64_t StackAdj =
10021027
std::min(alignDown(StackSize, 16), static_cast<uint64_t>(48));
1003-
FirstFrameSetup->getOperand(1).setImm(StackAdj);
1028+
PossiblePush->getOperand(1).setImm(StackAdj);
10041029
StackSize -= StackAdj;
10051030

10061031
CFIBuilder.buildDefCFAOffset(RealStackSize - StackSize);
@@ -1305,17 +1330,21 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF,
13051330
for (const CalleeSavedInfo &CS : getPushOrLibCallsSavedInfo(MF, CSI))
13061331
CFIBuilder.buildRestore(CS.getReg());
13071332

1308-
// Update CFA offset. After CM_POP SP should be equal to CFA, so CFA
1309-
// offset should be a zero.
1310-
CFIBuilder.buildDefCFAOffset(0);
1333+
// Update CFA Offset. If this is a QCI interrupt function, there will be a
1334+
// leftover offset which is deallocated by `QC.C.MILEAVERET`, otherwise
1335+
// getQCIInterruptStackSize() will be 0.
1336+
CFIBuilder.buildDefCFAOffset(RVFI->getQCIInterruptStackSize());
13111337
}
13121338
}
13131339

13141340
emitSiFiveCLICPreemptibleRestores(MF, MBB, MBBI, DL);
13151341

1316-
// Deallocate stack if StackSize isn't a zero yet
1342+
// Deallocate stack if StackSize isn't a zero yet. If this is a QCI interrupt
1343+
// function, there will be a leftover offset which is deallocated by
1344+
// `QC.C.MILEAVERET`, otherwise getQCIInterruptStackSize() will be 0.
13171345
if (StackSize != 0)
1318-
deallocateStack(MF, MBB, MBBI, DL, StackSize, RealStackSize - StackSize);
1346+
deallocateStack(MF, MBB, MBBI, DL, StackSize,
1347+
RVFI->getQCIInterruptStackSize());
13191348

13201349
// Emit epilogue for shadow call stack.
13211350
emitSCSEpilogue(MF, MBB, MBBI, DL);
@@ -1894,10 +1923,17 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
18941923

18951924
if (RVFI->useQCIInterrupt(MF)) {
18961925
RVFI->setQCIInterruptStackSize(QCIInterruptPushAmount);
1897-
} else if (RVFI->isPushable(MF)) {
1926+
}
1927+
1928+
if (RVFI->isPushable(MF)) {
18981929
// Determine how many GPRs we need to push and save it to RVFI.
18991930
unsigned PushedRegNum = getNumPushPopRegs(CSI);
1900-
if (PushedRegNum) {
1931+
1932+
// `QC.C.MIENTER(.NEST)` will save `ra` and `s0`, so we should only push if
1933+
// we want to push more than 2 registers. Otherwise, we should push if we
1934+
// want to push more than 0 registers.
1935+
unsigned OnlyPushIfMoreThan = RVFI->useQCIInterrupt(MF) ? 2 : 0;
1936+
if (PushedRegNum > OnlyPushIfMoreThan) {
19011937
RVFI->setRVPushRegs(PushedRegNum);
19021938
RVFI->setRVPushStackSize(alignTo((STI.getXLen() / 8) * PushedRegNum, 16));
19031939
}
@@ -1923,8 +1959,9 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
19231959
CS.setFrameIdx(FrameIdx);
19241960
continue;
19251961
}
1926-
// TODO: QCI Interrupt + Push/Pop
1927-
} else if (RVFI->useSaveRestoreLibCalls(MF) || RVFI->isPushable(MF)) {
1962+
}
1963+
1964+
if (RVFI->useSaveRestoreLibCalls(MF) || RVFI->isPushable(MF)) {
19281965
const auto *FII = llvm::find_if(
19291966
FixedCSRFIMap, [&](MCPhysReg P) { return P == CS.getReg(); });
19301967
unsigned RegNum = std::distance(std::begin(FixedCSRFIMap), FII);
@@ -1937,6 +1974,9 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
19371974
else
19381975
Offset = -int64_t(RegNum + 1) * Size;
19391976

1977+
if (RVFI->useQCIInterrupt(MF))
1978+
Offset -= QCIInterruptPushAmount;
1979+
19401980
int FrameIdx = MFI.CreateFixedSpillStackObject(Size, Offset);
19411981
assert(FrameIdx < 0);
19421982
CS.setFrameIdx(FrameIdx);
@@ -1965,10 +2005,13 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
19652005
// because there are gaps which are reserved for future use.
19662006
MFI.CreateFixedSpillStackObject(
19672007
QCIInterruptPushAmount, -static_cast<int64_t>(QCIInterruptPushAmount));
1968-
} else if (RVFI->isPushable(MF)) {
2008+
}
2009+
2010+
if (RVFI->isPushable(MF)) {
2011+
int64_t QCIOffset = RVFI->useQCIInterrupt(MF) ? QCIInterruptPushAmount : 0;
19692012
// Allocate a fixed object that covers the full push.
19702013
if (int64_t PushSize = RVFI->getRVPushStackSize())
1971-
MFI.CreateFixedSpillStackObject(PushSize, -PushSize);
2014+
MFI.CreateFixedSpillStackObject(PushSize, -PushSize - QCIOffset);
19722015
} else if (int LibCallRegs = getLibCallID(MF, CSI) + 1) {
19732016
int64_t LibCallFrameSize =
19742017
alignTo((STI.getXLen() / 8) * LibCallRegs, getStackAlign());
@@ -2003,13 +2046,15 @@ bool RISCVFrameLowering::spillCalleeSavedRegisters(
20032046

20042047
for (auto [Reg, _Offset] : FixedCSRFIQCIInterruptMap)
20052048
MBB.addLiveIn(Reg);
2006-
// TODO: Handle QCI Interrupt + Push/Pop
2007-
} else if (RVFI->isPushable(*MF)) {
2049+
}
2050+
2051+
if (RVFI->isPushable(*MF)) {
20082052
// Emit CM.PUSH with base StackAdj & evaluate Push stack
20092053
unsigned PushedRegNum = RVFI->getRVPushRegs();
20102054
if (PushedRegNum > 0) {
20112055
// Use encoded number to represent registers to spill.
2012-
unsigned Opcode = getPushOpcode(RVFI->getPushPopKind(*MF), hasFP(*MF));
2056+
unsigned Opcode = getPushOpcode(
2057+
RVFI->getPushPopKind(*MF), hasFP(*MF) && !RVFI->useQCIInterrupt(*MF));
20132058
unsigned RegEnc = RISCVZC::encodeRegListNumRegs(PushedRegNum);
20142059
MachineInstrBuilder PushBuilder =
20152060
BuildMI(MBB, MI, DL, TII.get(Opcode))
@@ -2156,8 +2201,9 @@ bool RISCVFrameLowering::restoreCalleeSavedRegisters(
21562201
// QC.C.MILEAVERET which we already inserted to return.
21572202
assert(MI->getOpcode() == RISCV::QC_C_MILEAVERET &&
21582203
"Unexpected QCI Interrupt Return Instruction");
2159-
// TODO: Handle QCI + Push/Pop
2160-
} else if (RVFI->isPushable(*MF)) {
2204+
}
2205+
2206+
if (RVFI->isPushable(*MF)) {
21612207
unsigned PushedRegNum = RVFI->getRVPushRegs();
21622208
if (PushedRegNum > 0) {
21632209
unsigned Opcode = getPopOpcode(RVFI->getPushPopKind(*MF));

0 commit comments

Comments
 (0)