[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