[llvm] [BOLT][RISCV] Support frame optimization (PR #214145)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 5 00:19:31 PDT 2026
https://github.com/WangJee created https://github.com/llvm/llvm-project/pull/214145
Implement the RISC-V MCPlusBuilder hooks used by frame analysis and shrink wrapping, including stack-pointer tracking, stack accesses, register moves, immediate adjustment, and spill/reload construction.
Make the initial SP-to-CFA offset target-dependent, account for registers clobbered by call instructions, and keep adjacent stack adjustments separate when the combined immediate cannot be encoded.
Add tests for shrink-wrapped spills, CFI reconstruction, and large stack frames.
>From bef7cc5c523ecf388e24dcbe8bd1d924897ca1f7 Mon Sep 17 00:00:00 2001
From: "wangjue.wangjue" <wangjue.wangjue at alibaba-inc.com>
Date: Wed, 5 Aug 2026 16:12:57 +0900
Subject: [PATCH] [BOLT][RISCV] Support frame optimization
Implement the RISC-V MCPlusBuilder hooks used by frame analysis and shrink wrapping, including stack-pointer tracking, stack accesses, register moves, immediate adjustment, and spill/reload construction.
Make the initial SP-to-CFA offset target-dependent, account for registers clobbered by call instructions, and keep adjacent stack adjustments separate when the combined immediate cannot be encoded.
Add tests for shrink-wrapped spills, CFI reconstruction, and large stack frames.
---
bolt/include/bolt/Core/MCPlusBuilder.h | 3 +
.../bolt/Passes/StackPointerTracking.h | 5 +-
bolt/lib/Passes/AllocCombiner.cpp | 5 +-
bolt/lib/Passes/FrameAnalysis.cpp | 9 +-
bolt/lib/Passes/FrameOptimizer.cpp | 5 +-
bolt/lib/Passes/RegAnalysis.cpp | 5 +
bolt/lib/Passes/ShrinkWrapping.cpp | 2 +-
bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp | 466 ++++++++++++++++++
bolt/test/RISCV/alloc-combiner-large-stack.s | 53 ++
bolt/test/RISCV/frame-opt-basic.s | 47 ++
10 files changed, 591 insertions(+), 9 deletions(-)
create mode 100644 bolt/test/RISCV/alloc-combiner-large-stack.s
create mode 100644 bolt/test/RISCV/frame-opt-basic.s
diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index aae520d5afe54..276dc1c5ffc70 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -1286,6 +1286,9 @@ class MCPlusBuilder {
return 0;
}
+ /// Return the stack pointer offset from CFA at function entry.
+ virtual int64_t getInitialStackPointerOffset() const { return -8; }
+
virtual MCPhysReg getFramePointer() const {
llvm_unreachable("not implemented");
return 0;
diff --git a/bolt/include/bolt/Passes/StackPointerTracking.h b/bolt/include/bolt/Passes/StackPointerTracking.h
index cbb4f88302bc6..333157b563e29 100644
--- a/bolt/include/bolt/Passes/StackPointerTracking.h
+++ b/bolt/include/bolt/Passes/StackPointerTracking.h
@@ -32,10 +32,11 @@ class StackPointerTrackingBase
int getEmpty() { return EMPTY; }
std::pair<int, int> getStartingStateAtBB(const BinaryBasicBlock &BB) {
- // Entry BB start with offset 8 from CFA.
+ // The entry block starts at the target ABI's initial offset from CFA.
// All others start with EMPTY (meaning we don't know anything).
if (BB.isEntryPoint())
- return std::make_pair(-8, getEmpty());
+ return std::make_pair(this->BC.MIB->getInitialStackPointerOffset(),
+ getEmpty());
return std::make_pair(getEmpty(), getEmpty());
}
diff --git a/bolt/lib/Passes/AllocCombiner.cpp b/bolt/lib/Passes/AllocCombiner.cpp
index ea620bb82184d..e59b42bddb253 100644
--- a/bolt/lib/Passes/AllocCombiner.cpp
+++ b/bolt/lib/Passes/AllocCombiner.cpp
@@ -88,7 +88,10 @@ void AllocCombinerPass::combineAdjustments(BinaryFunction &BF) {
if (BC.MIB->isSUB(Inst))
Adjustment = -Adjustment;
- BC.MIB->addToImm(Inst, Adjustment, BC.Ctx.get());
+ if (!BC.MIB->addToImm(Inst, Adjustment, BC.Ctx.get())) {
+ Prev = &Inst;
+ continue;
+ }
LLVM_DEBUG({
dbgs() << "After adjustment:\n";
diff --git a/bolt/lib/Passes/FrameAnalysis.cpp b/bolt/lib/Passes/FrameAnalysis.cpp
index 0b26da3371234..eda08aa5f6727 100644
--- a/bolt/lib/Passes/FrameAnalysis.cpp
+++ b/bolt/lib/Passes/FrameAnalysis.cpp
@@ -100,8 +100,8 @@ class FrameAccessAnalysis {
// is used in this function
int SPOffset{0};
int FPOffset{0};
- int64_t CfaOffset{-8};
- uint16_t CfaReg{7};
+ int64_t CfaOffset;
+ uint16_t CfaReg;
std::stack<std::pair<int64_t, uint16_t>> CFIStack;
/// Our pointer to access SPT info
const MCInst *Prev{nullptr};
@@ -163,7 +163,10 @@ class FrameAccessAnalysis {
public:
FrameAccessAnalysis(BinaryFunction &BF, StackPointerTracking &SPT)
- : SPT(SPT), BC(BF.getBinaryContext()), BF(BF) {}
+ : SPT(SPT), BC(BF.getBinaryContext()), BF(BF),
+ CfaOffset(BC.MIB->getInitialStackPointerOffset()),
+ CfaReg(BC.MRI->getDwarfRegNum(BC.MIB->getStackPointer(),
+ /*isEH=*/false)) {}
void enterNewBB() { Prev = nullptr; }
const FrameIndexEntry &getFIE() const { return FIE; }
diff --git a/bolt/lib/Passes/FrameOptimizer.cpp b/bolt/lib/Passes/FrameOptimizer.cpp
index b0b7207feac01..74707acbd8116 100644
--- a/bolt/lib/Passes/FrameOptimizer.cpp
+++ b/bolt/lib/Passes/FrameOptimizer.cpp
@@ -224,8 +224,9 @@ Error FrameOptimizerPass::runOnFunctions(BinaryContext &BC) {
if (opts::FrameOptimization == FOP_NONE)
return Error::success();
- if (!BC.isX86()) {
- BC.errs() << "BOLT-ERROR: " << getName() << " is supported only on X86\n";
+ if (!BC.isX86() && !BC.isRISCV()) {
+ BC.errs() << "BOLT-ERROR: " << getName()
+ << " is supported only on X86 and RISC-V\n";
exit(1);
}
diff --git a/bolt/lib/Passes/RegAnalysis.cpp b/bolt/lib/Passes/RegAnalysis.cpp
index 66ca6800b1a5f..524d5e3f475c3 100644
--- a/bolt/lib/Passes/RegAnalysis.cpp
+++ b/bolt/lib/Passes/RegAnalysis.cpp
@@ -150,6 +150,11 @@ void RegAnalysis::getInstUsedRegsList(const MCInst &Inst, BitVector &RegSet,
return;
}
+ // Account for registers clobbered by the call instruction itself, such as
+ // the link register on RISC-V and AArch64.
+ if (GetClobbers)
+ BC.MIB->getClobberedRegs(Inst, RegSet);
+
// If no call graph supplied...
if (RegsKilledMap.size() == 0) {
beConservative(RegSet);
diff --git a/bolt/lib/Passes/ShrinkWrapping.cpp b/bolt/lib/Passes/ShrinkWrapping.cpp
index 5f0af2d68182f..23a3285e0fedf 100644
--- a/bolt/lib/Passes/ShrinkWrapping.cpp
+++ b/bolt/lib/Passes/ShrinkWrapping.cpp
@@ -1601,7 +1601,7 @@ void ShrinkWrapping::rebuildCFIForSP() {
}
}
- int PrevSPVal = -8;
+ int PrevSPVal = BC.MIB->getInitialStackPointerOffset();
BinaryBasicBlock *PrevBB = nullptr;
StackPointerTracking &SPT = Info.getStackPointerTracking();
for (BinaryBasicBlock *BB : BF.getLayout().blocks()) {
diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
index d1a0572277874..ace6795fa4bae 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -19,6 +19,7 @@
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
#define DEBUG_TYPE "mcplus"
@@ -39,6 +40,17 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
public:
using MCPlusBuilder::MCPlusBuilder;
+ BitVector getRegsUsedAsParams() const override {
+ BitVector Regs(RegInfo->getNumRegs(), false);
+ for (MCPhysReg Reg :
+ {RISCV::X10, RISCV::X11, RISCV::X12, RISCV::X13, RISCV::X14,
+ RISCV::X15, RISCV::X16, RISCV::X17, RISCV::F10_D, RISCV::F11_D,
+ RISCV::F12_D, RISCV::F13_D, RISCV::F14_D, RISCV::F15_D, RISCV::F16_D,
+ RISCV::F17_D})
+ Regs |= getAliases(Reg);
+ return Regs;
+ }
+
bool equals(const MCSpecifierExpr &A, const MCSpecifierExpr &B,
CompFuncTy Comp) const override {
const auto &RISCVExprA = cast<MCSpecifierExpr>(A);
@@ -66,6 +78,460 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
Regs |= getAliases(RISCV::X27);
}
+ MCPhysReg getStackPointer() const override { return RISCV::X2; }
+
+ int64_t getInitialStackPointerOffset() const override { return 0; }
+
+ MCPhysReg getFramePointer() const override { return RISCV::X8; }
+
+ MCPhysReg getFlagsReg() const override { return RISCV::NoRegister; }
+
+ void getDefaultLiveOut(BitVector &Regs) const override {
+ Regs |= getAliases(RISCV::X10);
+ Regs |= getAliases(RISCV::X11);
+ Regs |= getAliases(RISCV::F10_D);
+ Regs |= getAliases(RISCV::F11_D);
+ }
+
+ bool isPush(const MCInst &Inst) const override { return false; }
+
+ int getPushSize(const MCInst &Inst) const override { return 0; }
+
+ int getPopSize(const MCInst &Inst) const override { return 0; }
+
+ bool isSUB(const MCInst &Inst) const override {
+ return Inst.getOpcode() == RISCV::SUB;
+ }
+
+ bool requiresAlignedAddress(const MCInst &Inst) const override {
+ return false;
+ }
+
+ bool isPacked(const MCInst &Inst) const override { return false; }
+
+ bool isCleanRegXOR(const MCInst &Inst) const override { return false; }
+
+ bool isRegToRegMove(const MCInst &Inst, MCPhysReg &From,
+ MCPhysReg &To) const override {
+ switch (Inst.getOpcode()) {
+ default:
+ return false;
+ case RISCV::ADDI:
+ if (!Inst.getOperand(2).isImm() || Inst.getOperand(2).getImm() != 0)
+ return false;
+ To = Inst.getOperand(0).getReg();
+ From = Inst.getOperand(1).getReg();
+ return true;
+ case RISCV::C_MV:
+ To = Inst.getOperand(0).getReg();
+ From = Inst.getOperand(1).getReg();
+ return true;
+ }
+ }
+
+ bool isRedundantMove(const MCInst &Inst) const override {
+ MCPhysReg From, To;
+ return isRegToRegMove(Inst, From, To) && From == To;
+ }
+
+ bool replaceMemOperandWithReg(MCInst &Inst, MCPhysReg RegNum) const override {
+ if (Inst.getNumOperands() == 0 || !Inst.getOperand(0).isReg())
+ return false;
+ const MCPhysReg DestReg = Inst.getOperand(0).getReg();
+ if (getRISCVMCRegisterClass(RISCV::GPRRegClassID).contains(DestReg) &&
+ getRISCVMCRegisterClass(RISCV::GPRRegClassID).contains(RegNum)) {
+ Inst =
+ MCInstBuilder(RISCV::ADDI).addReg(DestReg).addReg(RegNum).addImm(0);
+ return true;
+ }
+
+ unsigned Opcode;
+ switch (Inst.getOpcode()) {
+ default:
+ return false;
+ case RISCV::FLW:
+ case RISCV::C_FLW:
+ case RISCV::C_FLWSP:
+ Opcode = RISCV::FSGNJ_S;
+ break;
+ case RISCV::FLD:
+ case RISCV::C_FLD:
+ case RISCV::C_FLDSP:
+ Opcode = RISCV::FSGNJ_D;
+ break;
+ }
+ Inst = MCInstBuilder(Opcode).addReg(DestReg).addReg(RegNum).addReg(RegNum);
+ return true;
+ }
+
+ bool replaceMemOperandWithImm(MCInst &Inst, StringRef ConstantData,
+ uint64_t Offset) const override {
+ return false;
+ }
+
+ bool isStackAdjustment(const MCInst &Inst) const override {
+ switch (Inst.getOpcode()) {
+ default:
+ return false;
+ case RISCV::ADDI:
+ case RISCV::C_ADDI:
+ case RISCV::C_ADDI16SP:
+ return Inst.getNumOperands() >= 3 && Inst.getOperand(0).isReg() &&
+ Inst.getOperand(0).getReg() == RISCV::X2 &&
+ Inst.getOperand(1).isReg() &&
+ Inst.getOperand(1).getReg() == RISCV::X2 &&
+ Inst.getOperand(2).isImm();
+ }
+ }
+
+ bool
+ evaluateStackOffsetExpr(const MCInst &Inst, int64_t &Output,
+ std::pair<MCPhysReg, int64_t> Input1,
+ std::pair<MCPhysReg, int64_t> Input2) const override {
+ auto getInput = [&](MCPhysReg Reg) -> std::optional<int64_t> {
+ if (Reg == Input1.first)
+ return Input1.second;
+ if (Reg == Input2.first)
+ return Input2.second;
+ return std::nullopt;
+ };
+
+ switch (Inst.getOpcode()) {
+ default:
+ return false;
+ case RISCV::ADDI:
+ case RISCV::C_ADDI:
+ case RISCV::C_ADDI16SP: {
+ if (Inst.getNumOperands() < 3 || !Inst.getOperand(1).isReg() ||
+ !Inst.getOperand(2).isImm())
+ return false;
+ std::optional<int64_t> Input = getInput(Inst.getOperand(1).getReg());
+ if (!Input)
+ return false;
+ Output = *Input + Inst.getOperand(2).getImm();
+ return true;
+ }
+ case RISCV::C_ADDI4SPN: {
+ if (Inst.getNumOperands() < 3 || !Inst.getOperand(1).isReg() ||
+ !Inst.getOperand(2).isImm())
+ return false;
+ std::optional<int64_t> Input = getInput(Inst.getOperand(1).getReg());
+ if (!Input)
+ return false;
+ Output = *Input + Inst.getOperand(2).getImm();
+ return true;
+ }
+ case RISCV::C_MV: {
+ if (Inst.getNumOperands() < 2 || !Inst.getOperand(1).isReg())
+ return false;
+ std::optional<int64_t> Input = getInput(Inst.getOperand(1).getReg());
+ if (!Input)
+ return false;
+ Output = *Input;
+ return true;
+ }
+ }
+ }
+
+ bool isStackAccess(const MCInst &Inst, bool &IsLoad, bool &IsStore,
+ bool &IsStoreFromReg, MCPhysReg &Reg, int32_t &SrcImm,
+ uint16_t &StackPtrReg, int64_t &StackOffset, uint8_t &Size,
+ bool &IsSimple, bool &IsIndexed) const override {
+ IsLoad = false;
+ IsStore = false;
+ IsStoreFromReg = false;
+ Reg = 0;
+ SrcImm = 0;
+ StackPtrReg = 0;
+ StackOffset = 0;
+ Size = 0;
+ IsSimple = false;
+ IsIndexed = false;
+
+ struct StackAccessInfo {
+ uint8_t Size;
+ bool IsLoad;
+ };
+ std::optional<StackAccessInfo> Access;
+ switch (Inst.getOpcode()) {
+ default:
+ return false;
+ case RISCV::LB:
+ case RISCV::LBU:
+ Access = StackAccessInfo{1, true};
+ break;
+ case RISCV::LH:
+ case RISCV::LHU:
+ Access = StackAccessInfo{2, true};
+ break;
+ case RISCV::LW:
+ case RISCV::LWU:
+ case RISCV::FLW:
+ case RISCV::C_LW:
+ case RISCV::C_FLW:
+ case RISCV::C_LWSP:
+ case RISCV::C_FLWSP:
+ Access = StackAccessInfo{4, true};
+ break;
+ case RISCV::LD:
+ case RISCV::FLD:
+ case RISCV::C_LD:
+ case RISCV::C_FLD:
+ case RISCV::C_LDSP:
+ case RISCV::C_FLDSP:
+ Access = StackAccessInfo{8, true};
+ break;
+ case RISCV::SB:
+ Access = StackAccessInfo{1, false};
+ break;
+ case RISCV::SH:
+ Access = StackAccessInfo{2, false};
+ break;
+ case RISCV::SW:
+ case RISCV::FSW:
+ case RISCV::C_SW:
+ case RISCV::C_FSW:
+ case RISCV::C_SWSP:
+ case RISCV::C_FSWSP:
+ Access = StackAccessInfo{4, false};
+ break;
+ case RISCV::SD:
+ case RISCV::FSD:
+ case RISCV::C_SD:
+ case RISCV::C_FSD:
+ case RISCV::C_SDSP:
+ case RISCV::C_FSDSP:
+ Access = StackAccessInfo{8, false};
+ break;
+ }
+
+ if (Inst.getNumOperands() < 3 || !Inst.getOperand(0).isReg() ||
+ !Inst.getOperand(1).isReg() || !Inst.getOperand(2).isImm())
+ return false;
+
+ MCPhysReg Base = Inst.getOperand(1).getReg();
+ if (Base != getStackPointer() && Base != getFramePointer())
+ return false;
+
+ IsLoad = Access->IsLoad;
+ IsStore = !Access->IsLoad;
+ IsStoreFromReg = IsStore;
+ Reg = Inst.getOperand(0).getReg();
+ StackPtrReg = Base;
+ StackOffset = Inst.getOperand(2).getImm();
+ Size = Access->Size;
+ IsSimple = true;
+ return true;
+ }
+
+ bool escapesVariable(const MCInst &Inst,
+ bool HasFramePointer) const override {
+ bool IsLoad, IsStore, IsStoreFromReg, IsSimple, IsIndexed;
+ MCPhysReg Reg;
+ int32_t SrcImm;
+ uint16_t StackPtrReg;
+ int64_t StackOffset;
+ uint8_t Size;
+ if (isStackAccess(Inst, IsLoad, IsStore, IsStoreFromReg, Reg, SrcImm,
+ StackPtrReg, StackOffset, Size, IsSimple, IsIndexed))
+ return false;
+
+ const MCInstrDesc &Desc = Info->get(Inst.getOpcode());
+ const unsigned NumDefs = Desc.getNumDefs();
+ bool UsesFrameAddress = false;
+ for (unsigned I = NumDefs, E = MCPlus::getNumPrimeOperands(Inst); I != E;
+ ++I) {
+ const MCOperand &Operand = Inst.getOperand(I);
+ if (!Operand.isReg())
+ continue;
+ const MCPhysReg UsedReg = Operand.getReg();
+ if (UsedReg == getStackPointer() ||
+ (HasFramePointer && UsedReg == getFramePointer())) {
+ UsesFrameAddress = true;
+ break;
+ }
+ }
+ if (!UsesFrameAddress)
+ return false;
+
+ return !any_of(defOperands(Inst), [&](const MCOperand &Operand) {
+ if (!Operand.isReg())
+ return false;
+ const MCPhysReg DefReg = Operand.getReg();
+ return DefReg == getStackPointer() ||
+ (HasFramePointer && DefReg == getFramePointer());
+ });
+ }
+
+ static bool isValidCompressedOffset(unsigned Opcode, int64_t Offset) {
+ auto isShiftedUInt = [&](unsigned Bits, unsigned Alignment) {
+ return Offset >= 0 && isUIntN(Bits, Offset) && Offset % Alignment == 0;
+ };
+
+ switch (Opcode) {
+ default:
+ return false;
+ case RISCV::C_LW:
+ case RISCV::C_FLW:
+ case RISCV::C_SW:
+ case RISCV::C_FSW:
+ return isShiftedUInt(7, 4);
+ case RISCV::C_LD:
+ case RISCV::C_FLD:
+ case RISCV::C_SD:
+ case RISCV::C_FSD:
+ return isShiftedUInt(8, 8);
+ case RISCV::C_LWSP:
+ case RISCV::C_FLWSP:
+ case RISCV::C_SWSP:
+ case RISCV::C_FSWSP:
+ return isShiftedUInt(8, 4);
+ case RISCV::C_LDSP:
+ case RISCV::C_FLDSP:
+ case RISCV::C_SDSP:
+ case RISCV::C_FSDSP:
+ return isShiftedUInt(9, 8);
+ }
+ }
+
+ bool addToImm(MCInst &Inst, int64_t &Amt, MCContext *Ctx) const override {
+ if (Inst.getNumOperands() < 3 || !Inst.getOperand(2).isImm())
+ return false;
+
+ const int64_t NewImm = Inst.getOperand(2).getImm() + Amt;
+ switch (Inst.getOpcode()) {
+ default:
+ return false;
+ case RISCV::ADDI:
+ case RISCV::LB:
+ case RISCV::LBU:
+ case RISCV::LH:
+ case RISCV::LHU:
+ case RISCV::LW:
+ case RISCV::LWU:
+ case RISCV::LD:
+ case RISCV::FLW:
+ case RISCV::FLD:
+ case RISCV::SB:
+ case RISCV::SH:
+ case RISCV::SW:
+ case RISCV::SD:
+ case RISCV::FSW:
+ case RISCV::FSD:
+ if (!isInt<12>(NewImm))
+ return false;
+ break;
+ case RISCV::C_ADDI:
+ if (!isInt<6>(NewImm))
+ return false;
+ break;
+ case RISCV::C_ADDI16SP:
+ if (!isInt<10>(NewImm) || NewImm == 0 || NewImm % 16 != 0)
+ return false;
+ break;
+ case RISCV::C_ADDI4SPN:
+ if (NewImm <= 0 || !isUInt<10>(NewImm) || NewImm % 4 != 0)
+ return false;
+ break;
+ case RISCV::C_LW:
+ case RISCV::C_FLW:
+ case RISCV::C_SW:
+ case RISCV::C_FSW:
+ case RISCV::C_LD:
+ case RISCV::C_FLD:
+ case RISCV::C_SD:
+ case RISCV::C_FSD:
+ case RISCV::C_LWSP:
+ case RISCV::C_FLWSP:
+ case RISCV::C_SWSP:
+ case RISCV::C_FSWSP:
+ case RISCV::C_LDSP:
+ case RISCV::C_FLDSP:
+ case RISCV::C_SDSP:
+ case RISCV::C_FSDSP:
+ if (!isValidCompressedOffset(Inst.getOpcode(), NewImm))
+ return false;
+ break;
+ }
+
+ Amt = NewImm;
+ Inst.getOperand(2).setImm(NewImm);
+ return true;
+ }
+
+ void createSaveToStack(MCInst &Inst, const MCPhysReg &StackReg, int Offset,
+ const MCPhysReg &SrcReg, int Size) const override {
+ unsigned Opcode;
+ const bool IsGPR =
+ getRISCVMCRegisterClass(RISCV::GPRRegClassID).contains(SrcReg);
+ if (IsGPR) {
+ switch (Size) {
+ default:
+ llvm_unreachable("Invalid GPR spill size");
+ case 1:
+ Opcode = RISCV::SB;
+ break;
+ case 2:
+ Opcode = RISCV::SH;
+ break;
+ case 4:
+ Opcode = RISCV::SW;
+ break;
+ case 8:
+ Opcode = RISCV::SD;
+ break;
+ }
+ } else {
+ if (!getRISCVMCRegisterClass(RISCV::FPR64RegClassID).contains(SrcReg))
+ llvm_unreachable("Unsupported spill register class");
+ if (Size == 4)
+ Opcode = RISCV::FSW;
+ else if (Size == 8)
+ Opcode = RISCV::FSD;
+ else
+ llvm_unreachable("Invalid FPR spill size");
+ }
+ assert(isInt<12>(Offset) && "Stack spill offset is out of range");
+ Inst = MCInstBuilder(Opcode).addReg(SrcReg).addReg(StackReg).addImm(Offset);
+ }
+
+ void createRestoreFromStack(MCInst &Inst, const MCPhysReg &StackReg,
+ int Offset, const MCPhysReg &DstReg,
+ int Size) const override {
+ unsigned Opcode;
+ const bool IsGPR =
+ getRISCVMCRegisterClass(RISCV::GPRRegClassID).contains(DstReg);
+ if (IsGPR) {
+ switch (Size) {
+ default:
+ llvm_unreachable("Invalid GPR reload size");
+ case 1:
+ Opcode = RISCV::LB;
+ break;
+ case 2:
+ Opcode = RISCV::LH;
+ break;
+ case 4:
+ Opcode = RISCV::LW;
+ break;
+ case 8:
+ Opcode = RISCV::LD;
+ break;
+ }
+ } else {
+ if (!getRISCVMCRegisterClass(RISCV::FPR64RegClassID).contains(DstReg))
+ llvm_unreachable("Unsupported reload register class");
+ if (Size == 4)
+ Opcode = RISCV::FLW;
+ else if (Size == 8)
+ Opcode = RISCV::FLD;
+ else
+ llvm_unreachable("Invalid FPR reload size");
+ }
+ assert(isInt<12>(Offset) && "Stack reload offset is out of range");
+ Inst = MCInstBuilder(Opcode).addReg(DstReg).addReg(StackReg).addImm(Offset);
+ }
+
bool shouldRecordCodeRelocation(uint32_t RelType) const override {
switch (RelType) {
case ELF::R_RISCV_JAL:
diff --git a/bolt/test/RISCV/alloc-combiner-large-stack.s b/bolt/test/RISCV/alloc-combiner-large-stack.s
new file mode 100644
index 0000000000000..65b36b7ae7ae4
--- /dev/null
+++ b/bolt/test/RISCV/alloc-combiner-large-stack.s
@@ -0,0 +1,53 @@
+# REQUIRES: system-linux
+
+# RUN: %clang %cflags64 -march=rv64gc %s -o %t.exe
+# RUN: link_fdata --no-lbr --nmtool llvm-nm %s %t.exe %t.fdata
+# RUN: llvm-bolt %t.exe -relocs -o %t.out -data %t.fdata \
+# RUN: -frame-opt=all -simplify-conditional-tail-calls=false \
+# RUN: -eliminate-unreachable=false
+# RUN: llvm-objdump -d --no-show-raw-insn %t.out | \
+# RUN: FileCheck %s
+
+ .text
+ .reloc 0, R_RISCV_NONE
+ .globl _start
+ .type _start, @function
+_start:
+ .cfi_startproc
+# FDATA: 1 _start #_start# 100
+ addi sp, sp, -0x1f0
+ .cfi_adjust_cfa_offset 0x1f0
+ sd ra, 0x1e8(sp)
+ .cfi_offset ra, -8
+ addi sp, sp, -0x800
+ .cfi_adjust_cfa_offset 0x800
+ addi sp, sp, -0x6f0
+ .cfi_adjust_cfa_offset 0x6f0
+ sd a0, 0(sp)
+ call callee
+ ld a0, 0(sp)
+ addi sp, sp, 0x7f0
+ .cfi_adjust_cfa_offset -0x7f0
+ addi sp, sp, 0x700
+ .cfi_adjust_cfa_offset -0x700
+ ld ra, 0x1e8(sp)
+ .cfi_restore ra
+ addi sp, sp, 0x1f0
+ .cfi_adjust_cfa_offset -0x1f0
+ ret
+ .cfi_endproc
+ .size _start, .-_start
+
+ .type callee, @function
+callee:
+ ret
+ .size callee, .-callee
+
+# CHECK-LABEL: <_start>:
+# CHECK: addi sp, sp, -0x1f0
+# CHECK: addi sp, sp, -0x800
+# CHECK-NEXT: addi sp, sp, -0x6f0
+# CHECK: addi sp, sp, 0x7f0
+# CHECK-NEXT: addi sp, sp, 0x700
+# CHECK: ld ra, 0x1e8(sp)
+# CHECK: addi sp, sp, 0x1f0
diff --git a/bolt/test/RISCV/frame-opt-basic.s b/bolt/test/RISCV/frame-opt-basic.s
new file mode 100644
index 0000000000000..e7c4bfbaab914
--- /dev/null
+++ b/bolt/test/RISCV/frame-opt-basic.s
@@ -0,0 +1,47 @@
+# REQUIRES: system-linux
+
+# RUN: %clang %cflags64 -march=rv64gc %s -o %t.exe
+# RUN: link_fdata --no-lbr --nmtool llvm-nm %s %t.exe %t.fdata
+# RUN: llvm-bolt %t.exe -relocs -o %t.out -data %t.fdata \
+# RUN: -frame-opt=all -simplify-conditional-tail-calls=false \
+# RUN: -eliminate-unreachable=false | FileCheck %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t.out | \
+# RUN: FileCheck --check-prefix=CHECK-OBJDUMP %s
+# RUN: llvm-readelf --unwind %t.out | FileCheck --check-prefix=CHECK-CFI %s
+
+ .text
+ .reloc 0, R_RISCV_NONE
+ .globl _start
+ .type _start, @function
+_start:
+ .cfi_startproc
+# FDATA: 1 _start #_start# 100
+ addi sp, sp, -16
+ .cfi_def_cfa sp, 16
+ sd s1, 8(sp)
+ .cfi_offset s1, -8
+.Lbranch:
+ beqz a0, .Lcold
+.Lhot:
+ addi s1, s1, 1
+ addi a0, s1, 0
+.Lexit:
+ ld s1, 8(sp)
+ .cfi_restore s1
+ addi sp, sp, 16
+ .cfi_def_cfa sp, 0
+ ret
+.Lcold:
+ addi a0, a0, 2
+ j .Lexit
+ .cfi_endproc
+ .size _start, .-_start
+
+# CHECK: BOLT-INFO: FOP optimized
+# CHECK: BOLT-INFO: FRAME ANALYSIS: 0 function(s) {{.*}} could not have its frame indices restored.
+# CHECK: BOLT-INFO: Shrink wrapping moved 1 spills inserting load/stores
+# CHECK-OBJDUMP: <_start>:
+# CHECK-OBJDUMP: beqz
+# CHECK-OBJDUMP-NEXT: sd s1, 0x8(sp)
+# CHECK-CFI: DW_CFA_def_cfa: reg2 +0
+# CHECK-CFI: DW_CFA_offset: reg9 -8
More information about the llvm-commits
mailing list