[llvm-branch-commits] [llvm] [WIP] Users/ppenzin/ra saverestore codegen (PR #170611)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Dec 3 22:26:59 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-risc-v
Author: Petr Penzin (ppenzin)
<details>
<summary>Changes</summary>
Use register allocator to save callee-saved registers.
This has been split out from https://github.com/mgudim/llvm-project/tree/save_csr_in_ra3, and is PR 5 out of 5.
Co-authored-by: Mikhail Gudim <mgudim@<!-- -->ventanamicro.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/170611.diff
9 Files Affected:
- (modified) llvm/lib/CodeGen/ReachingDefAnalysis.cpp (+12)
- (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+92)
- (modified) llvm/lib/Target/RISCV/RISCVISelLowering.h (+2)
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.cpp (+8)
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.h (+2)
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.td (+3)
- (modified) llvm/test/CodeGen/RISCV/pr53662.mir (+4)
- (modified) llvm/test/CodeGen/RISCV/rvv/fixed-vectors-emergency-slot.mir (+1-1)
- (added) llvm/test/CodeGen/RISCV/save-csr-early.ll (+113)
``````````diff
diff --git a/llvm/lib/CodeGen/ReachingDefAnalysis.cpp b/llvm/lib/CodeGen/ReachingDefAnalysis.cpp
index b12a5bc64ca0b..7014fd4bf890b 100644
--- a/llvm/lib/CodeGen/ReachingDefAnalysis.cpp
+++ b/llvm/lib/CodeGen/ReachingDefAnalysis.cpp
@@ -9,6 +9,8 @@
#include "llvm/CodeGen/ReachingDefAnalysis.h"
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/LiveRegUnits.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
@@ -288,6 +290,16 @@ void ReachingDefInfo::run(MachineFunction &mf) {
TRI = STI.getRegisterInfo();
TII = STI.getInstrInfo();
LLVM_DEBUG(dbgs() << "********** REACHING DEFINITION ANALYSIS **********\n");
+
+ MachineFunctionProperties &Props = MF->getProperties();
+ if (!Props.hasTracksLiveness()) {
+ Props.setTracksLiveness();
+
+ SmallVector<MachineBasicBlock *> AllMBBsInPostOrder;
+ for (MachineBasicBlock *MBB : post_order(MF))
+ AllMBBsInPostOrder.push_back(MBB);
+ fullyRecomputeLiveIns(AllMBBsInPostOrder);
+ }
init();
traverse();
}
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index ab2652eac3823..7e7af8d48da0b 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -25827,3 +25827,95 @@ bool RISCVTargetLowering::isReassocProfitable(SelectionDAG &DAG, SDValue N0,
return true;
}
+
+static MachineInstr *findInstrWhichNeedAllCSRs(MachineBasicBlock &MBB) {
+ // Some instructions may require (implicitly) all CSRs to be saved.
+ // For example, call to __cxa_throw is noreturn, but expects that all CSRs are
+ // taken care of.
+ // TODO: try to speedup this?
+ for (MachineInstr &MI : MBB) {
+ unsigned Opc = MI.getOpcode();
+ if (Opc != RISCV::PseudoCALL && Opc != RISCV::PseudoTAIL)
+ continue;
+ MachineOperand &MO = MI.getOperand(0);
+ StringRef Name = "";
+ if (MO.isSymbol()) {
+ Name = MO.getSymbolName();
+ } else if (MO.isGlobal()) {
+ Name = MO.getGlobal()->getName();
+ } else {
+ llvm_unreachable("Unexpected operand type.");
+ }
+ if (Name == "__cxa_throw" || Name == "__cxa_rethrow" ||
+ Name == "_Unwind_Resume")
+ return &MI;
+ }
+ return nullptr;
+}
+
+void RISCVTargetLowering::finalizeLowering(MachineFunction &MF) const {
+ if (!Subtarget.savesCSRsEarly()) {
+ TargetLoweringBase::finalizeLowering(MF);
+ return;
+ }
+
+ MachineRegisterInfo &MRI = MF.getRegInfo();
+ const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
+ const RISCVRegisterInfo &TRI = *Subtarget.getRegisterInfo();
+ const RISCVFrameLowering &TFI = *Subtarget.getFrameLowering();
+
+ SmallVector<MachineInstr *, 4> RestorePoints;
+ SmallVector<MachineBasicBlock *, 4> SaveMBBs;
+ SaveMBBs.push_back(&MF.front());
+ for (MachineBasicBlock &MBB : MF) {
+ if (MBB.isReturnBlock())
+ RestorePoints.push_back(&MBB.back());
+ if (MachineInstr *CallToCxaThrow = findInstrWhichNeedAllCSRs(MBB)) {
+ MachineBasicBlock::iterator MII = MBB.getFirstTerminator();
+ MachineInstr *NewRetMI = BuildMI(MBB, MII, CallToCxaThrow->getDebugLoc(),
+ TII.get(RISCV::UnreachableRET));
+ RestorePoints.push_back(NewRetMI);
+ MII = ++NewRetMI->getIterator();
+ MBB.erase(MII, MBB.end());
+ }
+ }
+
+ BitVector EarlyCSRs;
+ TFI.determineEarlyCalleeSaves(MF, EarlyCSRs);
+
+ SmallVector<Register, 4> VRegs;
+ for (MachineBasicBlock *SaveMBB : SaveMBBs) {
+ for (unsigned Reg = 0; Reg < EarlyCSRs.size(); ++Reg) {
+ if (!EarlyCSRs[Reg])
+ continue;
+ SaveMBB->addLiveIn(Reg);
+ Register VReg = MRI.createVirtualRegister(
+ TRI.getLargestLegalSuperClass(TRI.getMinimalPhysRegClass(Reg), MF));
+ VRegs.push_back(VReg);
+ BuildMI(*SaveMBB, SaveMBB->begin(),
+ SaveMBB->findDebugLoc(SaveMBB->begin()),
+ TII.get(TargetOpcode::COPY), VReg)
+ .addReg(Reg);
+ MRI.setSimpleHint(VReg, Reg);
+ }
+ }
+
+ for (MachineInstr *RestorePoint : RestorePoints) {
+ auto VRegI = VRegs.begin();
+ for (unsigned Reg = 0; Reg < EarlyCSRs.size(); ++Reg) {
+ if (!EarlyCSRs[Reg])
+ continue;
+ Register VReg = *VRegI;
+ BuildMI(*RestorePoint->getParent(), RestorePoint->getIterator(),
+ RestorePoint->getDebugLoc(), TII.get(TargetOpcode::COPY), Reg)
+ .addReg(VReg);
+ RestorePoint->addOperand(MF,
+ MachineOperand::CreateReg(Reg,
+ /*isDef=*/false,
+ /*isImplicit=*/true));
+ VRegI++;
+ }
+ }
+
+ TargetLoweringBase::finalizeLowering(MF);
+}
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 8a55a5634452c..bd21bb9e8593e 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -652,6 +652,8 @@ class RISCVTargetLowering : public TargetLowering {
std::pair<const TargetRegisterClass *, uint8_t>
findRepresentativeClass(const TargetRegisterInfo *TRI, MVT VT) const override;
+
+ void finalizeLowering(MachineFunction &MF) const override;
};
namespace RISCVVIntrinsicsTable {
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 89ec4a2a4a3e1..5bbf558644987 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -5127,3 +5127,11 @@ bool RISCVInstrInfo::isHighLatencyDef(int Opc) const {
return true;
}
}
+
+bool RISCVInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
+ if (MI.getOpcode() == RISCV::UnreachableRET) {
+ MI.eraseFromParent();
+ return true;
+ }
+ return false;
+}
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
index 0ffe015b9fac8..e98ec29d57446 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
@@ -343,6 +343,8 @@ class RISCVInstrInfo : public RISCVGenInstrInfo {
static bool isFromLoadImm(const MachineRegisterInfo &MRI,
const MachineOperand &Op, int64_t &Imm);
+ bool expandPostRAPseudo(MachineInstr &MI) const override;
+
protected:
const RISCVSubtarget &STI;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 84b962b2a8607..3cc6cb6567cfe 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -1836,6 +1836,9 @@ let isBarrier = 1, isReturn = 1, isTerminator = 1 in
def PseudoRET : Pseudo<(outs), (ins), [(riscv_ret_glue)]>,
PseudoInstExpansion<(JALR X0, X1, 0)>;
+let isBarrier = 1, isReturn = 1, isTerminator = 1, isMeta = 1, hasSideEffects = 1, mayLoad = 0, mayStore = 0 in
+def UnreachableRET : Pseudo<(outs), (ins), []>;
+
// PseudoTAIL is a pseudo instruction similar to PseudoCALL and will eventually
// expand to auipc and jalr while encoding.
// Define AsmString to print "tail" when compile with -S flag.
diff --git a/llvm/test/CodeGen/RISCV/pr53662.mir b/llvm/test/CodeGen/RISCV/pr53662.mir
index dccad40368111..834bcbc1cf82c 100644
--- a/llvm/test/CodeGen/RISCV/pr53662.mir
+++ b/llvm/test/CodeGen/RISCV/pr53662.mir
@@ -18,15 +18,19 @@ body: |
; CHECK-LABEL: name: b
; CHECK: bb.0:
; CHECK-NEXT: successors: %bb.1(0x80000000)
+ ; CHECK-NEXT: liveins: $x10
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: PseudoBR %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1:
; CHECK-NEXT: successors: %bb.2(0x80000000)
+ ; CHECK-NEXT: liveins: $x10
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: DBG_VALUE $noreg
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2:
+ ; CHECK-NEXT: liveins: $x10
+ ; CHECK-NEXT: {{ $}}
; CHECK-NEXT: PseudoRET implicit killed $x10
bb.0 :
PseudoBR %bb.1
diff --git a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-emergency-slot.mir b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-emergency-slot.mir
index c728fcb8d8b0d..44f60a43a2790 100644
--- a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-emergency-slot.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-emergency-slot.mir
@@ -47,7 +47,7 @@ body: |
SD $x10, %stack.0, 0
SD $x10, %stack.2, 0
- dead renamable $x15 = PseudoVSETIVLI 1, 72, implicit-def $vl, implicit-def $vtype
+ renamable $x15 = PseudoVSETIVLI 1, 72, implicit-def $vl, implicit-def $vtype
VS1R_V killed renamable $v25, %stack.1 :: (store (<vscale x 1 x s64>) into %stack.1, align 8)
; This is here just to make all the eligible registers live at this point.
; This way when we replace the frame index %stack.1 with its actual address
diff --git a/llvm/test/CodeGen/RISCV/save-csr-early.ll b/llvm/test/CodeGen/RISCV/save-csr-early.ll
new file mode 100644
index 0000000000000..65feb5d867aab
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/save-csr-early.ll
@@ -0,0 +1,113 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+; RUN: llc %s -mtriple=riscv64 -riscv-save-csrs-early=true \
+; RUN: -stop-after=finalize-isel -o - | FileCheck %s
+
+define void @test0() {
+ ; CHECK-LABEL: name: test0
+ ; CHECK: bb.0 (%ir-block.0):
+ ; CHECK-NEXT: liveins: $x1, $x8, $x9, $x18, $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gprjalrnonx7_and_gprnox31 = COPY $x27
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gprjalrnonx7_and_gprnox31 = COPY $x26
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprjalrnonx7_and_gprnox31 = COPY $x25
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gprjalrnonx7_and_gprnox31 = COPY $x24
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:sr07 = COPY $x23
+ ; CHECK-NEXT: [[COPY5:%[0-9]+]]:sr07 = COPY $x22
+ ; CHECK-NEXT: [[COPY6:%[0-9]+]]:sr07 = COPY $x21
+ ; CHECK-NEXT: [[COPY7:%[0-9]+]]:sr07 = COPY $x20
+ ; CHECK-NEXT: [[COPY8:%[0-9]+]]:sr07 = COPY $x19
+ ; CHECK-NEXT: [[COPY9:%[0-9]+]]:sr07 = COPY $x18
+ ; CHECK-NEXT: [[COPY10:%[0-9]+]]:gprc_and_sr07 = COPY $x9
+ ; CHECK-NEXT: [[COPY11:%[0-9]+]]:gprc_and_sr07 = COPY $x8
+ ; CHECK-NEXT: [[COPY12:%[0-9]+]]:gprx1 = COPY $x1
+ ; CHECK-NEXT: $x1 = COPY [[COPY12]]
+ ; CHECK-NEXT: $x8 = COPY [[COPY11]]
+ ; CHECK-NEXT: $x9 = COPY [[COPY10]]
+ ; CHECK-NEXT: $x18 = COPY [[COPY9]]
+ ; CHECK-NEXT: $x19 = COPY [[COPY8]]
+ ; CHECK-NEXT: $x20 = COPY [[COPY7]]
+ ; CHECK-NEXT: $x21 = COPY [[COPY6]]
+ ; CHECK-NEXT: $x22 = COPY [[COPY5]]
+ ; CHECK-NEXT: $x23 = COPY [[COPY4]]
+ ; CHECK-NEXT: $x24 = COPY [[COPY3]]
+ ; CHECK-NEXT: $x25 = COPY [[COPY2]]
+ ; CHECK-NEXT: $x26 = COPY [[COPY1]]
+ ; CHECK-NEXT: $x27 = COPY [[COPY]]
+ ; CHECK-NEXT: PseudoRET implicit $x1, implicit $x8, implicit $x9, implicit $x18, implicit $x19, implicit $x20, implicit $x21, implicit $x22, implicit $x23, implicit $x24, implicit $x25, implicit $x26, implicit $x27
+ ret void
+}
+
+declare void @__cxa_throw(ptr, ptr, ptr)
+
+define void @test1(i1 %x, ptr %p0, ptr %p1, ptr %p2) {
+ ; CHECK-LABEL: name: test1
+ ; CHECK: bb.0.entry:
+ ; CHECK-NEXT: successors: %bb.1(0x00000000), %bb.2(0x80000000)
+ ; CHECK-NEXT: liveins: $x10, $x11, $x12, $x13, $x1, $x8, $x9, $x18, $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gprjalrnonx7_and_gprnox31 = COPY $x27
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gprjalrnonx7_and_gprnox31 = COPY $x26
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprjalrnonx7_and_gprnox31 = COPY $x25
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gprjalrnonx7_and_gprnox31 = COPY $x24
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:sr07 = COPY $x23
+ ; CHECK-NEXT: [[COPY5:%[0-9]+]]:sr07 = COPY $x22
+ ; CHECK-NEXT: [[COPY6:%[0-9]+]]:sr07 = COPY $x21
+ ; CHECK-NEXT: [[COPY7:%[0-9]+]]:sr07 = COPY $x20
+ ; CHECK-NEXT: [[COPY8:%[0-9]+]]:sr07 = COPY $x19
+ ; CHECK-NEXT: [[COPY9:%[0-9]+]]:sr07 = COPY $x18
+ ; CHECK-NEXT: [[COPY10:%[0-9]+]]:gprc_and_sr07 = COPY $x9
+ ; CHECK-NEXT: [[COPY11:%[0-9]+]]:gprc_and_sr07 = COPY $x8
+ ; CHECK-NEXT: [[COPY12:%[0-9]+]]:gprx1 = COPY $x1
+ ; CHECK-NEXT: [[COPY13:%[0-9]+]]:gpr = COPY $x13
+ ; CHECK-NEXT: [[COPY14:%[0-9]+]]:gpr = COPY $x12
+ ; CHECK-NEXT: [[COPY15:%[0-9]+]]:gpr = COPY $x11
+ ; CHECK-NEXT: [[COPY16:%[0-9]+]]:gpr = COPY $x10
+ ; CHECK-NEXT: [[ANDI:%[0-9]+]]:gpr = ANDI [[COPY16]], 1
+ ; CHECK-NEXT: BEQ killed [[ANDI]], $x0, %bb.2
+ ; CHECK-NEXT: PseudoBR %bb.1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.1.throw:
+ ; CHECK-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
+ ; CHECK-NEXT: $x10 = COPY [[COPY15]]
+ ; CHECK-NEXT: $x11 = COPY [[COPY14]]
+ ; CHECK-NEXT: $x12 = COPY [[COPY13]]
+ ; CHECK-NEXT: PseudoCALL target-flags(riscv-call) @__cxa_throw, csr_ilp32_lp64, implicit-def dead $x1, implicit $x10, implicit $x11, implicit $x12, implicit-def $x2
+ ; CHECK-NEXT: ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
+ ; CHECK-NEXT: $x1 = COPY [[COPY12]]
+ ; CHECK-NEXT: $x8 = COPY [[COPY11]]
+ ; CHECK-NEXT: $x9 = COPY [[COPY10]]
+ ; CHECK-NEXT: $x18 = COPY [[COPY9]]
+ ; CHECK-NEXT: $x19 = COPY [[COPY8]]
+ ; CHECK-NEXT: $x20 = COPY [[COPY7]]
+ ; CHECK-NEXT: $x21 = COPY [[COPY6]]
+ ; CHECK-NEXT: $x22 = COPY [[COPY5]]
+ ; CHECK-NEXT: $x23 = COPY [[COPY4]]
+ ; CHECK-NEXT: $x24 = COPY [[COPY3]]
+ ; CHECK-NEXT: $x25 = COPY [[COPY2]]
+ ; CHECK-NEXT: $x26 = COPY [[COPY1]]
+ ; CHECK-NEXT: $x27 = COPY [[COPY]]
+ ; CHECK-NEXT: UnreachableRET implicit $x1, implicit $x8, implicit $x9, implicit $x18, implicit $x19, implicit $x20, implicit $x21, implicit $x22, implicit $x23, implicit $x24, implicit $x25, implicit $x26, implicit $x27
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.2.return:
+ ; CHECK-NEXT: $x1 = COPY [[COPY12]]
+ ; CHECK-NEXT: $x8 = COPY [[COPY11]]
+ ; CHECK-NEXT: $x9 = COPY [[COPY10]]
+ ; CHECK-NEXT: $x18 = COPY [[COPY9]]
+ ; CHECK-NEXT: $x19 = COPY [[COPY8]]
+ ; CHECK-NEXT: $x20 = COPY [[COPY7]]
+ ; CHECK-NEXT: $x21 = COPY [[COPY6]]
+ ; CHECK-NEXT: $x22 = COPY [[COPY5]]
+ ; CHECK-NEXT: $x23 = COPY [[COPY4]]
+ ; CHECK-NEXT: $x24 = COPY [[COPY3]]
+ ; CHECK-NEXT: $x25 = COPY [[COPY2]]
+ ; CHECK-NEXT: $x26 = COPY [[COPY1]]
+ ; CHECK-NEXT: $x27 = COPY [[COPY]]
+ ; CHECK-NEXT: PseudoRET implicit $x1, implicit $x8, implicit $x9, implicit $x18, implicit $x19, implicit $x20, implicit $x21, implicit $x22, implicit $x23, implicit $x24, implicit $x25, implicit $x26, implicit $x27
+entry:
+ br i1 %x, label %throw, label %return
+throw:
+ call void @__cxa_throw(ptr %p0, ptr %p1, ptr %p2)
+ unreachable
+return:
+ ret void
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/170611
More information about the llvm-branch-commits
mailing list