-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[RISCV] Support PreserveMost calling convention #148214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-ir @llvm/pr-subscribers-backend-risc-v Author: Pengcheng Wang (wangpc-pp) ChangesThis adds the simplest implementation of Fixes #148147. Full diff: https://github.com/llvm/llvm-project/pull/148214.diff 4 Files Affected:
diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.td b/llvm/lib/Target/RISCV/RISCVCallingConv.td
index cbf039edec273..90e5019019bed 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.td
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.td
@@ -93,3 +93,5 @@ def CSR_XLEN_F32_V_Interrupt_RVE: CalleeSavedRegs<(sub CSR_XLEN_F32_V_Interrupt,
// Same as CSR_XLEN_F64_V_Interrupt, but excluding X16-X31.
def CSR_XLEN_F64_V_Interrupt_RVE: CalleeSavedRegs<(sub CSR_XLEN_F64_V_Interrupt,
(sequence "X%u", 16, 31))>;
+
+def CSR_RT_MostRegs : CalleeSavedRegs<(add (sequence "X%u", 5, 31))>;
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 456f3aedbf034..6feb53202b7f2 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -22205,6 +22205,7 @@ SDValue RISCVTargetLowering::LowerFormalArguments(
case CallingConv::C:
case CallingConv::Fast:
case CallingConv::SPIR_KERNEL:
+ case CallingConv::PreserveMost:
case CallingConv::GRAAL:
case CallingConv::RISCV_VectorCall:
#define CC_VLS_CASE(ABI_VLEN) case CallingConv::RISCV_VLSCall_##ABI_VLEN:
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index 540412366026b..44c7ca3587cc1 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -68,6 +68,8 @@ RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
auto &Subtarget = MF->getSubtarget<RISCVSubtarget>();
if (MF->getFunction().getCallingConv() == CallingConv::GHC)
return CSR_NoRegs_SaveList;
+ if (MF->getFunction().getCallingConv() == CallingConv::PreserveMost)
+ return CSR_RT_MostRegs_SaveList;
if (MF->getFunction().hasFnAttribute("interrupt")) {
if (Subtarget.hasVInstructions()) {
if (Subtarget.hasStdExtD())
diff --git a/llvm/test/CodeGen/RISCV/calling-conv-preserve-most.ll b/llvm/test/CodeGen/RISCV/calling-conv-preserve-most.ll
new file mode 100644
index 0000000000000..a0345363601a5
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/calling-conv-preserve-most.ll
@@ -0,0 +1,165 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 < %s | FileCheck %s -check-prefix=RV32
+; RUN: llc -mtriple=riscv64 < %s | FileCheck %s -check-prefix=RV64
+
+; Check the PreserveMost calling convention works.
+
+declare void @standard_cc_func()
+declare preserve_mostcc void @preserve_mostcc_func()
+
+define preserve_mostcc void @preserve_mostcc1() nounwind {
+; RV32-LABEL: preserve_mostcc1:
+; RV32: # %bb.0: # %entry
+; RV32-NEXT: addi sp, sp, -64
+; RV32-NEXT: sw t0, 60(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t1, 56(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t2, 52(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a0, 48(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a1, 44(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a2, 40(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a3, 36(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a4, 32(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a5, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a6, 24(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a7, 20(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t3, 16(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t4, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t5, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t6, 4(sp) # 4-byte Folded Spill
+; RV32-NEXT: call standard_cc_func
+; RV32-NEXT: lw t0, 60(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t1, 56(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t2, 52(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a0, 48(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a1, 44(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a2, 40(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a3, 36(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a4, 32(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a5, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a6, 24(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a7, 20(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t3, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t4, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t5, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t6, 4(sp) # 4-byte Folded Reload
+; RV32-NEXT: addi sp, sp, 64
+; RV32-NEXT: ret
+;
+; RV64-LABEL: preserve_mostcc1:
+; RV64: # %bb.0: # %entry
+; RV64-NEXT: addi sp, sp, -128
+; RV64-NEXT: sd t0, 120(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t1, 112(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t2, 104(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a0, 96(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a1, 88(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a2, 80(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a3, 72(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a4, 64(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a5, 56(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a6, 48(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a7, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t3, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t4, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t5, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t6, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: call standard_cc_func
+; RV64-NEXT: ld t0, 120(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t1, 112(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t2, 104(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a0, 96(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a1, 88(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a2, 80(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a3, 72(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a4, 64(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a5, 56(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a6, 48(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a7, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t3, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t4, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t5, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t6, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: addi sp, sp, 128
+; RV64-NEXT: ret
+entry:
+ call void @standard_cc_func()
+ ret void
+}
+
+define preserve_mostcc void @preserve_mostcc2() nounwind {
+; RV32-LABEL: preserve_mostcc2:
+; RV32: # %bb.0:
+; RV32-NEXT: addi sp, sp, -64
+; RV32-NEXT: sw t0, 60(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t1, 56(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t2, 52(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a0, 48(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a1, 44(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a2, 40(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a3, 36(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a4, 32(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a5, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a6, 24(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a7, 20(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t3, 16(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t4, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t5, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t6, 4(sp) # 4-byte Folded Spill
+; RV32-NEXT: call preserve_mostcc_func
+; RV32-NEXT: lw t0, 60(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t1, 56(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t2, 52(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a0, 48(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a1, 44(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a2, 40(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a3, 36(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a4, 32(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a5, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a6, 24(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a7, 20(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t3, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t4, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t5, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t6, 4(sp) # 4-byte Folded Reload
+; RV32-NEXT: addi sp, sp, 64
+; RV32-NEXT: ret
+;
+; RV64-LABEL: preserve_mostcc2:
+; RV64: # %bb.0:
+; RV64-NEXT: addi sp, sp, -128
+; RV64-NEXT: sd t0, 120(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t1, 112(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t2, 104(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a0, 96(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a1, 88(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a2, 80(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a3, 72(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a4, 64(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a5, 56(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a6, 48(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a7, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t3, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t4, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t5, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t6, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: call preserve_mostcc_func
+; RV64-NEXT: ld t0, 120(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t1, 112(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t2, 104(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a0, 96(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a1, 88(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a2, 80(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a3, 72(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a4, 64(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a5, 56(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a6, 48(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a7, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t3, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t4, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t5, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t6, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: addi sp, sp, 128
+; RV64-NEXT: ret
+ call preserve_mostcc void @preserve_mostcc_func()
+ ret void
+}
|
It is appreciated if you can test its functionality @CaiWeiran @folkertdev. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably update the LangRef to document that RISC-V supports it?
@@ -93,3 +93,5 @@ def CSR_XLEN_F32_V_Interrupt_RVE: CalleeSavedRegs<(sub CSR_XLEN_F32_V_Interrupt, | |||
// Same as CSR_XLEN_F64_V_Interrupt, but excluding X16-X31. | |||
def CSR_XLEN_F64_V_Interrupt_RVE: CalleeSavedRegs<(sub CSR_XLEN_F64_V_Interrupt, | |||
(sequence "X%u", 16, 31))>; | |||
|
|||
def CSR_RT_MostRegs : CalleeSavedRegs<(add (sequence "X%u", 5, 31))>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC some GPRs are also used as scratch registers. Should we preserve them or do some special handling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to include X1?
This adds the simplest implementation of `PreserveMost` calling convention and we preserve `x5-x31` registers. Fixes llvm#148147.
1fff14f
to
e3a2e57
Compare
@@ -68,6 +68,8 @@ RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { | |||
auto &Subtarget = MF->getSubtarget<RISCVSubtarget>(); | |||
if (MF->getFunction().getCallingConv() == CallingConv::GHC) | |||
return CSR_NoRegs_SaveList; | |||
if (MF->getFunction().getCallingConv() == CallingConv::PreserveMost) | |||
return CSR_RT_MostRegs_SaveList; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to consider hasStdExtE here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
@@ -413,6 +413,8 @@ added in the future: | |||
- On AArch64 the callee preserve all general purpose registers, except | |||
X0-X8 and X16-X18. Not allowed with ``nest``. | |||
|
|||
- On RISC-V the callee preserve x5-x31 registers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't going to work as the PLT stub can/does clobber some temporary registers (and exactly which depend on a lot of things). This is why AArch64 does not preserve x16-x18.
ret void | ||
} | ||
|
||
define preserve_mostcc void @preserve_mostcc2() nounwind { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious about why this function emits same spills as preserve_mostcc1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RISCVRegisterInfo::getCallPreservedMask
also needs to be updated, because right now the call is marked as clobbering all the a and t registers.
We should ensure the call is still marking (some of?) the t
registers as clobbered, because the PLT stub might clobber them.
This adds the simplest implementation of
PreserveMost
callingconvention and we preserve
x5-x31
registers.Fixes #148147.