[clang] [llvm] [clang][llvm][PPC] PreserveAll attribute (PR #175145)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 9 01:30:42 PST 2026
https://github.com/Luminyx1 created https://github.com/llvm/llvm-project/pull/175145
This patch extends the `preserve_all` calling convention to PowerPC targets. Replaces #174320
>From 46343d8b637d3d82e6316c9e9248ce7610b9399e Mon Sep 17 00:00:00 2001
From: Luminyx <27lumi at protonmail.com>
Date: Fri, 9 Jan 2026 04:26:53 -0500
Subject: [PATCH] [clang][llvm][PPC] PreserveAll attribute
---
clang/include/clang/Basic/AttrDocs.td | 7 +-
clang/lib/Basic/Targets/PPC.h | 11 +++
llvm/lib/Target/PowerPC/PPCCallingConv.td | 84 +++++++++++++++++++++
llvm/lib/Target/PowerPC/PPCISelLowering.cpp | 7 ++
llvm/lib/Target/PowerPC/PPCRegisterInfo.cpp | 81 ++++++++++++++++++++
5 files changed, 188 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 812b48058d189..7e4bb5eaf2f39 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6584,7 +6584,7 @@ in the future.
def PreserveAllDocs : Documentation {
let Category = DocCatCallingConvs;
let Content = [{
-On X86-64 and AArch64 targets, this attribute changes the calling convention of
+On X86-64, AArch64, and PowerPC targets, this attribute changes the calling convention of
a function. The ``preserve_all`` calling convention attempts to make the code
in the caller even less intrusive than the ``preserve_most`` calling convention.
This calling convention also behaves identical to the ``C`` calling convention
@@ -6599,10 +6599,13 @@ returned in callee-saved registers.
R11. R11 can be used as a scratch register. Furthermore it also preserves
all floating-point registers (XMMs/YMMs).
-- On AArch64 the callee preserve all general purpose registers, except X0-X8 and
+- On AArch64 the callee preserves all general purpose registers, except X0-X8 and
X16-X18. Furthermore it also preserves lower 128 bits of V8-V31 SIMD - floating
point registers.
+- On PowerPC the callee preserves all general purpose registers, except R1 and R2.
+ Furthermore it also preserves all floating-point and vector registers.
+
The idea behind this convention is to support calls to runtime functions
that don't need to call out to any other functions.
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index 664c9e15d8d18..d49f23b3fa68d 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -428,6 +428,16 @@ class LLVM_LIBRARY_VISIBILITY PPC32TargetInfo : public PPCTargetInfo {
std::pair<unsigned, unsigned> hardwareInterferenceSizes() const override {
return std::make_pair(32, 32);
}
+
+ CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
+ switch (CC) {
+ default:
+ return CCCR_Warning;
+ case CC_PreserveAll:
+ case CC_C:
+ return CCCR_OK;
+ }
+ }
};
// Note: ABI differences may eventually require us to have a separate
@@ -490,6 +500,7 @@ class LLVM_LIBRARY_VISIBILITY PPC64TargetInfo : public PPCTargetInfo {
CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
switch (CC) {
case CC_Swift:
+ case CC_PreserveAll:
return CCCR_OK;
case CC_SwiftAsync:
return CCCR_Error;
diff --git a/llvm/lib/Target/PowerPC/PPCCallingConv.td b/llvm/lib/Target/PowerPC/PPCCallingConv.td
index 5d4fe06ebdddd..b564b30a94bbe 100644
--- a/llvm/lib/Target/PowerPC/PPCCallingConv.td
+++ b/llvm/lib/Target/PowerPC/PPCCallingConv.td
@@ -343,6 +343,90 @@ def CSR_PPC64_R2_Altivec : CalleeSavedRegs<(add CSR_PPC64_Altivec, X2)>;
def CSR_NoRegs : CalleeSavedRegs<(add)>;
+def CSR_PPC32_PreserveMost : CalleeSavedRegs<(add R0, (sequence "R%u", 3, 31),
+ (sequence "CR%u", 0, 7))>;
+
+def CSR_PPC32_PreserveMost_NonVoid
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveMost, R3)>;
+
+def CSR_PPC32_PreserveAll
+ : CalleeSavedRegs<(add CSR_PPC32_PreserveMost, (sequence "F%u", 0, 31))>;
+
+def CSR_PPC32_PreserveAll_Unknown
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll, R3, R4, F1)>;
+
+def CSR_PPC32_PreserveAll_NonVoid
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll, R3)>;
+
+def CSR_PPC32_PreserveAll_NonVoid_64
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll_NonVoid, R4)>;
+
+def CSR_PPC32_PreserveAll_NonVoid_Float
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll, F1)>;
+
+def CSR_PPC32_PreserveAll_Altivec
+ : CalleeSavedRegs<(add CSR_PPC32_PreserveAll, (sequence "V%u", 0, 31))>;
+
+def CSR_PPC32_PreserveAll_Altivec_Unknown
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll_Altivec, R3, R4, F1, V2)>;
+
+def CSR_PPC32_PreserveAll_Altivec_NonVoid
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll_Altivec, R3)>;
+
+def CSR_PPC32_PreserveAll_Altivec_NonVoid_64
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll_Altivec_NonVoid, R4)>;
+
+def CSR_PPC32_PreserveAll_Altivec_NonVoid_Vec
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll_Altivec, V2)>;
+
+def CSR_PPC32_PreserveAll_Altivec_NonVoid_Float
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll_Altivec, F1)>;
+
+def CSR_PPC32_PreserveAll_SPE
+ : CalleeSavedRegs<(add CSR_PPC32_PreserveMost, (sequence "S%u", 3, 31))>;
+
+def CSR_PPC32_PreserveAll_SPE_NonVoid
+ : CalleeSavedRegs<(sub CSR_PPC32_PreserveAll_SPE, R3, S3)>;
+
+def CSR_PPC64_PreserveMost : CalleeSavedRegs<(add X0, (sequence "X%u", 3, 31),
+ (sequence "CR%u", 0, 7))>;
+
+def CSR_PPC64_PreserveMost_NonVoid
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveMost, X3)>;
+
+def CSR_PPC64_PreserveAll
+ : CalleeSavedRegs<(add CSR_PPC64_PreserveMost, (sequence "F%u", 0, 31))>;
+
+def CSR_PPC64_PreserveAll_Unknown
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll, X3, X4, F1)>;
+
+def CSR_PPC64_PreserveAll_NonVoid
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll, X3)>;
+
+def CSR_PPC64_PreserveAll_NonVoid_Float
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll, F1)>;
+
+def CSR_PPC64_PreserveAll_NonVoid_128
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll_NonVoid, X4)>;
+
+def CSR_PPC64_PreserveAll_Altivec
+ : CalleeSavedRegs<(add CSR_PPC64_PreserveAll, (sequence "V%u", 0, 31))>;
+
+def CSR_PPC64_PreserveAll_Altivec_NonVoid
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll_Altivec, X3)>;
+
+def CSR_PPC64_PreserveAll_Altivec_Unknown
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll_Altivec, X3, X4, F1, V2)>;
+
+def CSR_PPC64_PreserveAll_Altivec_NonVoid_Float
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll_Altivec, F1)>;
+
+def CSR_PPC64_PreserveAll_Altivec_NonVoid_Vec
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll_Altivec, V2)>;
+
+def CSR_PPC64_PreserveAll_Altivec_NonVoid_128
+ : CalleeSavedRegs<(sub CSR_PPC64_PreserveAll_Altivec_NonVoid, X4)>;
+
// coldcc calling convection marks most registers as non-volatile.
// Do not include r1 since the stack pointer is never considered a CSR.
// Do not include r2, since it is the TOC register and is added depending
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index ef211bf8c8982..2b68774e73937 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -7804,11 +7804,18 @@ PPCTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
SDValue Glue;
SmallVector<SDValue, 4> RetOps(1, Chain);
+ MachineFunction &MF = DAG.getMachineFunction();
+ bool ShouldDisableCalleeSavedRegister =
+ MF.getFunction().hasFnAttribute("preserve_all");
+
// Copy the result values into the output registers.
for (unsigned i = 0, RealResIdx = 0; i != RVLocs.size(); ++i, ++RealResIdx) {
CCValAssign &VA = RVLocs[i];
assert(VA.isRegLoc() && "Can only return in registers!");
+ if (ShouldDisableCalleeSavedRegister)
+ MF.getRegInfo().disableCalleeSavedRegister(VA.getLocReg());
+
SDValue Arg = OutVals[RealResIdx];
switch (VA.getLocInfo()) {
diff --git a/llvm/lib/Target/PowerPC/PPCRegisterInfo.cpp b/llvm/lib/Target/PowerPC/PPCRegisterInfo.cpp
index b3a7c829958ec..2712eb6b1f7cb 100644
--- a/llvm/lib/Target/PowerPC/PPCRegisterInfo.cpp
+++ b/llvm/lib/Target/PowerPC/PPCRegisterInfo.cpp
@@ -199,6 +199,76 @@ PPCRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
return CSR_64_AllRegs_SaveList;
}
+ const Type *const ReturnTy = MF->getFunction().getReturnType();
+
+ // Preserve registers for PreserveAll attribute
+ // We need to skip r3 (return register) if the function isn't void
+ // however for floats it's f1 instead
+ // We also need to skip r4 if the return value is 64-bit on PPC32
+ // or 128-bit on PPC64
+ bool RetVec = ReturnTy->isVectorTy();
+ bool RetFloat = ReturnTy->isFloatingPointTy();
+ bool RetInt = ReturnTy->isIntOrPtrTy();
+ TypeSize RetIntSize = ReturnTy->getPrimitiveSizeInBits();
+ bool RetVoid = ReturnTy->isVoidTy();
+
+ if (MF->getFunction().getCallingConv() == CallingConv::PreserveAll) {
+ if (Subtarget.hasSPE() || Subtarget.pairedVectorMemops() ||
+ Subtarget.isAIXABI())
+ report_fatal_error("PreserveAll unimplemented on this target.");
+
+ if (TM.isPPC64()) {
+ if (RetVoid) {
+ if (Subtarget.hasAltivec())
+ return CSR_PPC64_PreserveAll_Altivec_SaveList;
+ return CSR_PPC64_PreserveAll_SaveList;
+ }
+ if (RetVec) {
+ if (Subtarget.hasAltivec())
+ return CSR_PPC64_PreserveAll_Altivec_NonVoid_Vec_SaveList;
+ report_fatal_error("Returning vector without vector extension");
+ }
+ if (RetFloat) {
+ if (Subtarget.hasAltivec())
+ return CSR_PPC64_PreserveAll_Altivec_NonVoid_Float_SaveList;
+ return CSR_PPC64_PreserveAll_NonVoid_Float_SaveList;
+ }
+ if (RetInt) {
+ if (Subtarget.hasAltivec())
+ return RetIntSize == 128
+ ? CSR_PPC64_PreserveAll_Altivec_NonVoid_128_SaveList
+ : CSR_PPC64_PreserveAll_Altivec_NonVoid_SaveList;
+ return RetIntSize == 128 ? CSR_PPC64_PreserveAll_NonVoid_128_SaveList
+ : CSR_PPC64_PreserveAll_NonVoid_SaveList;
+ }
+ report_fatal_error("Unhandled return type");
+ }
+ // PPC32:
+ if (RetVoid) {
+ if (Subtarget.hasAltivec())
+ return CSR_PPC32_PreserveAll_Altivec_SaveList;
+ return CSR_PPC32_PreserveAll_SaveList;
+ }
+ if (RetVec) {
+ if (Subtarget.hasAltivec())
+ return CSR_PPC32_PreserveAll_Altivec_NonVoid_Vec_SaveList;
+ report_fatal_error("Returning vector without vector extension");
+ }
+ if (RetFloat) {
+ if (Subtarget.hasAltivec())
+ return CSR_PPC32_PreserveAll_Altivec_NonVoid_Float_SaveList;
+ return CSR_PPC32_PreserveAll_NonVoid_Float_SaveList;
+ }
+ if (RetInt) {
+ if (Subtarget.hasAltivec())
+ return RetIntSize == 64
+ ? CSR_PPC32_PreserveAll_Altivec_NonVoid_64_SaveList
+ : CSR_PPC32_PreserveAll_Altivec_NonVoid_SaveList;
+ return RetIntSize == 64 ? CSR_PPC32_PreserveAll_NonVoid_64_SaveList
+ : CSR_PPC32_PreserveAll_NonVoid_SaveList;
+ }
+ }
+
// On PPC64, we might need to save r2 (but only if it is not reserved).
// We do not need to treat R2 as callee-saved when using PC-Relative calls
// because any direct uses of R2 will cause it to be reserved. If the function
@@ -291,6 +361,17 @@ PPCRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
return CSR_64_AllRegs_RegMask;
}
+ if (CC == CallingConv::PreserveAll) {
+ if (TM.isPPC64()) {
+ if (Subtarget.hasAltivec())
+ return CSR_PPC64_PreserveAll_Altivec_Unknown_RegMask;
+ return CSR_PPC64_PreserveAll_Unknown_RegMask;
+ }
+ if (Subtarget.hasAltivec())
+ return CSR_PPC32_PreserveAll_Altivec_Unknown_RegMask;
+ return CSR_PPC32_PreserveAll_Unknown_RegMask;
+ }
+
if (Subtarget.isAIXABI()) {
if (Subtarget.pairedVectorMemops()) {
if (!TM.getAIXExtendedAltivecABI())
More information about the llvm-commits
mailing list