[llvm] [RISCV][GISel] Implement canLowerReturn. (PR #105465)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 20 21:09:46 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-globalisel
@llvm/pr-subscribers-backend-risc-v
Author: Craig Topper (topperc)
<details>
<summary>Changes</summary>
This allows us to handle return values that are too large to fit in x10 and x11. They will be converted to a sret by passing a pointer to where to store the return value.
---
Full diff: https://github.com/llvm/llvm-project/pull/105465.diff
4 Files Affected:
- (modified) llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp (+56-30)
- (modified) llvm/lib/Target/RISCV/GISel/RISCVCallLowering.h (+4)
- (modified) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll (+48)
- (modified) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64-lp64f-lp64d-common.ll (+48)
``````````diff
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp b/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
index 33371512706469..3aff9b1c3d91fa 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
@@ -17,6 +17,7 @@
#include "RISCVMachineFunctionInfo.h"
#include "RISCVSubtarget.h"
#include "llvm/CodeGen/Analysis.h"
+#include "llvm/CodeGen/FunctionLoweringInfo.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
@@ -360,13 +361,7 @@ static bool isSupportedArgumentType(Type *T, const RISCVSubtarget &Subtarget,
// lowerCall.
static bool isSupportedReturnType(Type *T, const RISCVSubtarget &Subtarget,
bool IsLowerRetVal = false) {
- // TODO: Integers larger than 2*XLen are passed indirectly which is not
- // supported yet.
- if (T->isIntegerTy())
- return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2;
- if (T->isHalfTy() || T->isFloatTy() || T->isDoubleTy())
- return true;
- if (T->isPointerTy())
+ if (T->isIntegerTy() || T->isFloatingPointTy() || T->isPointerTy())
return true;
if (T->isArrayTy())
@@ -394,10 +389,13 @@ bool RISCVCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
assert(!Val == VRegs.empty() && "Return value without a vreg");
MachineInstrBuilder Ret = MIRBuilder.buildInstrNoInsert(RISCV::PseudoRET);
- if (!VRegs.empty()) {
+ if (!FLI.CanLowerReturn) {
+ insertSRetStores(MIRBuilder, Val->getType(), VRegs, FLI.DemoteRegister);
+ } else if (!VRegs.empty()) {
const RISCVSubtarget &Subtarget =
MIRBuilder.getMF().getSubtarget<RISCVSubtarget>();
- if (!isSupportedReturnType(Val->getType(), Subtarget, /*IsLowerRetVal=*/true))
+ if (!isSupportedReturnType(Val->getType(), Subtarget,
+ /*IsLowerRetVal=*/true))
return false;
MachineFunction &MF = MIRBuilder.getMF();
@@ -418,7 +416,7 @@ bool RISCVCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
/*IsRet=*/true, Dispatcher);
RISCVOutgoingValueHandler Handler(MIRBuilder, MF.getRegInfo(), Ret);
if (!determineAndHandleAssignments(Handler, Assigner, SplitRetInfos,
- MIRBuilder, CC, F.isVarArg()))
+ MIRBuilder, CC, F.isVarArg()))
return false;
}
@@ -426,6 +424,29 @@ bool RISCVCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
return true;
}
+bool RISCVCallLowering::canLowerReturn(MachineFunction &MF,
+ CallingConv::ID CallConv,
+ SmallVectorImpl<BaseArgInfo> &Outs,
+ bool IsVarArg) const {
+ SmallVector<CCValAssign, 16> ArgLocs;
+ const auto &TLI = *getTLI<RISCVTargetLowering>();
+ CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs,
+ MF.getFunction().getContext());
+
+ RVVArgDispatcher Dispatcher{&MF, &TLI,
+ ArrayRef(MF.getFunction().getReturnType())};
+
+ for (unsigned I = 0, E = Outs.size(); I < E; ++I) {
+ MVT VT = MVT::getVT(Outs[I].Ty);
+ RISCVABI::ABI ABI = MF.getSubtarget<RISCVSubtarget>().getTargetABI();
+ if (RISCV::CC_RISCV(MF.getDataLayout(), ABI, I, VT, VT, CCValAssign::Full,
+ Outs[I].Flags[0], CCInfo, /*IsFixed*/ true,
+ /*isRet*/ true, nullptr, TLI, Dispatcher))
+ return false;
+ }
+ return true;
+}
+
/// If there are varargs that were passed in a0-a7, the data in those registers
/// must be copied to the varargs save area on the stack.
void RISCVCallLowering::saveVarArgRegisters(
@@ -498,24 +519,26 @@ bool RISCVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
const Function &F,
ArrayRef<ArrayRef<Register>> VRegs,
FunctionLoweringInfo &FLI) const {
- // Early exit if there are no arguments. varargs are not part of F.args() but
- // must be lowered.
- if (F.arg_empty() && !F.isVarArg())
- return true;
+ MachineFunction &MF = MIRBuilder.getMF();
- const RISCVSubtarget &Subtarget =
- MIRBuilder.getMF().getSubtarget<RISCVSubtarget>();
+ const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>();
for (auto &Arg : F.args()) {
if (!isSupportedArgumentType(Arg.getType(), Subtarget,
/*IsLowerArgs=*/true))
return false;
}
- MachineFunction &MF = MIRBuilder.getMF();
+ MachineRegisterInfo &MRI = MF.getRegInfo();
const DataLayout &DL = MF.getDataLayout();
CallingConv::ID CC = F.getCallingConv();
SmallVector<ArgInfo, 32> SplitArgInfos;
+
+ // Insert the hidden sret parameter if the return value won't fit in the
+ // return registers.
+ if (!FLI.CanLowerReturn)
+ insertSRetIncomingArgument(F, SplitArgInfos, FLI.DemoteRegister, MRI, DL);
+
SmallVector<Type *, 4> TypeList;
unsigned Index = 0;
for (auto &Arg : F.args()) {
@@ -625,21 +648,24 @@ bool RISCVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
*Subtarget.getRegBankInfo(), *Call,
Call->getDesc(), Call->getOperand(0), 0);
- if (Info.OrigRet.Ty->isVoidTy())
- return true;
+ if (Info.CanLowerReturn && !Info.OrigRet.Ty->isVoidTy()) {
+ SmallVector<ArgInfo, 4> SplitRetInfos;
+ splitToValueTypes(Info.OrigRet, SplitRetInfos, DL, CC);
- SmallVector<ArgInfo, 4> SplitRetInfos;
- splitToValueTypes(Info.OrigRet, SplitRetInfos, DL, CC);
+ RVVArgDispatcher RetDispatcher{&MF, getTLI<RISCVTargetLowering>(),
+ ArrayRef(F.getReturnType())};
+ RISCVIncomingValueAssigner RetAssigner(
+ CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV,
+ /*IsRet=*/true, RetDispatcher);
+ RISCVCallReturnHandler RetHandler(MIRBuilder, MF.getRegInfo(), Call);
+ if (!determineAndHandleAssignments(RetHandler, RetAssigner, SplitRetInfos,
+ MIRBuilder, CC, Info.IsVarArg))
+ return false;
+ }
- RVVArgDispatcher RetDispatcher{&MF, getTLI<RISCVTargetLowering>(),
- ArrayRef(F.getReturnType())};
- RISCVIncomingValueAssigner RetAssigner(
- CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV,
- /*IsRet=*/true, RetDispatcher);
- RISCVCallReturnHandler RetHandler(MIRBuilder, MF.getRegInfo(), Call);
- if (!determineAndHandleAssignments(RetHandler, RetAssigner, SplitRetInfos,
- MIRBuilder, CC, Info.IsVarArg))
- return false;
+ if (!Info.CanLowerReturn)
+ insertSRetLoads(MIRBuilder, Info.OrigRet.Ty, Info.OrigRet.Regs,
+ Info.DemoteRegister, Info.DemoteStackIndex);
return true;
}
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.h b/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.h
index ec7fdbc26e24e8..1154449a580e7e 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.h
+++ b/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.h
@@ -32,6 +32,10 @@ class RISCVCallLowering : public CallLowering {
ArrayRef<Register> VRegs,
FunctionLoweringInfo &FLI) const override;
+ bool canLowerReturn(MachineFunction &MF, CallingConv::ID CallConv,
+ SmallVectorImpl<BaseArgInfo> &Outs,
+ bool IsVarArg) const override;
+
bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
ArrayRef<ArrayRef<Register>> VRegs,
FunctionLoweringInfo &FLI) const override;
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
index 5ca1bf7467858e..0ae007000239f8 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
@@ -945,6 +945,54 @@ define i32 @caller_small_struct_ret() nounwind {
ret i32 %5
}
+; Check return of >2x xlen scalars
+
+define fp128 @callee_large_scalar_ret() nounwind {
+ ; RV32I-LABEL: name: callee_large_scalar_ret
+ ; RV32I: bb.1 (%ir-block.0):
+ ; RV32I-NEXT: liveins: $x10
+ ; RV32I-NEXT: {{ $}}
+ ; RV32I-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+ ; RV32I-NEXT: [[C:%[0-9]+]]:_(s128) = G_FCONSTANT fp128 0xL00000000000000007FFF000000000000
+ ; RV32I-NEXT: G_STORE [[C]](s128), [[COPY]](p0) :: (store (s128))
+ ; RV32I-NEXT: PseudoRET
+ ret fp128 0xL00000000000000007FFF000000000000
+}
+
+define void @caller_large_scalar_ret() nounwind {
+ ; ILP32-LABEL: name: caller_large_scalar_ret
+ ; ILP32: bb.1 (%ir-block.0):
+ ; ILP32-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0
+ ; ILP32-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+ ; ILP32-NEXT: $x10 = COPY [[FRAME_INDEX]](p0)
+ ; ILP32-NEXT: PseudoCALL target-flags(riscv-call) @callee_large_scalar_ret, csr_ilp32_lp64, implicit-def $x1, implicit $x10
+ ; ILP32-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+ ; ILP32-NEXT: [[LOAD:%[0-9]+]]:_(s128) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s128) from %stack.0)
+ ; ILP32-NEXT: PseudoRET
+ ;
+ ; ILP32F-LABEL: name: caller_large_scalar_ret
+ ; ILP32F: bb.1 (%ir-block.0):
+ ; ILP32F-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0
+ ; ILP32F-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+ ; ILP32F-NEXT: $x10 = COPY [[FRAME_INDEX]](p0)
+ ; ILP32F-NEXT: PseudoCALL target-flags(riscv-call) @callee_large_scalar_ret, csr_ilp32f_lp64f, implicit-def $x1, implicit $x10
+ ; ILP32F-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+ ; ILP32F-NEXT: [[LOAD:%[0-9]+]]:_(s128) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s128) from %stack.0)
+ ; ILP32F-NEXT: PseudoRET
+ ;
+ ; ILP32D-LABEL: name: caller_large_scalar_ret
+ ; ILP32D: bb.1 (%ir-block.0):
+ ; ILP32D-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0
+ ; ILP32D-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+ ; ILP32D-NEXT: $x10 = COPY [[FRAME_INDEX]](p0)
+ ; ILP32D-NEXT: PseudoCALL target-flags(riscv-call) @callee_large_scalar_ret, csr_ilp32d_lp64d, implicit-def $x1, implicit $x10
+ ; ILP32D-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+ ; ILP32D-NEXT: [[LOAD:%[0-9]+]]:_(s128) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s128) from %stack.0)
+ ; ILP32D-NEXT: PseudoRET
+ %1 = call fp128 @callee_large_scalar_ret()
+ ret void
+}
+
; Check return of >2x xlen structs
%struct.large = type { i32, i32, i32, i32 }
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64-lp64f-lp64d-common.ll b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64-lp64f-lp64d-common.ll
index 2499f8caf02bcf..0521164bf37cfc 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64-lp64f-lp64d-common.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64-lp64f-lp64d-common.ll
@@ -600,6 +600,54 @@ define i64 @caller_small_struct_ret() nounwind {
ret i64 %5
}
+; Check return of >2x xlen scalars
+
+define i256 @callee_large_scalar_ret() nounwind {
+ ; RV64I-LABEL: name: callee_large_scalar_ret
+ ; RV64I: bb.1 (%ir-block.0):
+ ; RV64I-NEXT: liveins: $x10
+ ; RV64I-NEXT: {{ $}}
+ ; RV64I-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+ ; RV64I-NEXT: [[C:%[0-9]+]]:_(s256) = G_CONSTANT i256 -123456789
+ ; RV64I-NEXT: G_STORE [[C]](s256), [[COPY]](p0) :: (store (s256), align 16)
+ ; RV64I-NEXT: PseudoRET
+ ret i256 -123456789
+}
+
+define void @caller_large_scalar_ret() nounwind {
+ ; LP64-LABEL: name: caller_large_scalar_ret
+ ; LP64: bb.1 (%ir-block.0):
+ ; LP64-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0
+ ; LP64-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+ ; LP64-NEXT: $x10 = COPY [[FRAME_INDEX]](p0)
+ ; LP64-NEXT: PseudoCALL target-flags(riscv-call) @callee_large_scalar_ret, csr_ilp32_lp64, implicit-def $x1, implicit $x10
+ ; LP64-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+ ; LP64-NEXT: [[LOAD:%[0-9]+]]:_(s256) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s256) from %stack.0, align 16)
+ ; LP64-NEXT: PseudoRET
+ ;
+ ; LP64F-LABEL: name: caller_large_scalar_ret
+ ; LP64F: bb.1 (%ir-block.0):
+ ; LP64F-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0
+ ; LP64F-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+ ; LP64F-NEXT: $x10 = COPY [[FRAME_INDEX]](p0)
+ ; LP64F-NEXT: PseudoCALL target-flags(riscv-call) @callee_large_scalar_ret, csr_ilp32f_lp64f, implicit-def $x1, implicit $x10
+ ; LP64F-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+ ; LP64F-NEXT: [[LOAD:%[0-9]+]]:_(s256) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s256) from %stack.0, align 16)
+ ; LP64F-NEXT: PseudoRET
+ ;
+ ; LP64D-LABEL: name: caller_large_scalar_ret
+ ; LP64D: bb.1 (%ir-block.0):
+ ; LP64D-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0
+ ; LP64D-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+ ; LP64D-NEXT: $x10 = COPY [[FRAME_INDEX]](p0)
+ ; LP64D-NEXT: PseudoCALL target-flags(riscv-call) @callee_large_scalar_ret, csr_ilp32d_lp64d, implicit-def $x1, implicit $x10
+ ; LP64D-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+ ; LP64D-NEXT: [[LOAD:%[0-9]+]]:_(s256) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s256) from %stack.0, align 16)
+ ; LP64D-NEXT: PseudoRET
+ %1 = call i256 @callee_large_scalar_ret()
+ ret void
+}
+
; Check return of >2x xlen structs
%struct.large = type { i64, i64, i64, i64 }
``````````
</details>
https://github.com/llvm/llvm-project/pull/105465
More information about the llvm-commits
mailing list