Skip to content

Commit

Permalink
[SPARC] Make calls to function with big return values work
Browse files Browse the repository at this point in the history
Implement CanLowerReturn and associated CallingConv changes for SPARC/SPARC64.

In particular, for SPARC64 there's new `RetCC_Sparc64_*` functions that handles the return case of the calling convention.
It uses the same analysis as `CC_Sparc64_*` family of funtions, but fails if the return value doesn't fit into the return registers.

This makes calls to functions with big return values converted to an sret function as expected, instead of crashing LLVM.

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D132465

(cherry picked from commit d3fcbee)
  • Loading branch information
koachan authored and tru committed Oct 28, 2022
1 parent 9d46557 commit 08bd84e
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 36 deletions.
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9693,6 +9693,7 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
Entry.Alignment = Alignment;
CLI.getArgs().insert(CLI.getArgs().begin(), Entry);
CLI.NumFixedArgs += 1;
CLI.getArgs()[0].IndirectType = CLI.RetTy;
CLI.RetTy = Type::getVoidTy(CLI.RetTy->getContext());

// sret demotion isn't compatible with tail-calls, since the sret argument
Expand Down
10 changes: 7 additions & 3 deletions llvm/lib/Target/Sparc/SparcCallingConv.td
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,14 @@ def CC_Sparc64 : CallingConv<[
def RetCC_Sparc64 : CallingConv<[
// A single f32 return value always goes in %f0. The ABI doesn't specify what
// happens to multiple f32 return values outside a struct.
CCIfType<[f32], CCCustom<"CC_Sparc64_Half">>,
CCIfType<[f32], CCCustom<"RetCC_Sparc64_Half">>,

// Otherwise, return values are passed exactly like arguments.
CCDelegateTo<CC_Sparc64>
// Otherwise, return values are passed exactly like arguments, except that
// returns that are too big to fit into the registers is passed as an sret
// instead.
CCIfInReg<CCIfType<[i32, f32], CCCustom<"RetCC_Sparc64_Half">>>,
CCIfType<[i32], CCPromoteToType<i64>>,
CCCustom<"RetCC_Sparc64_Full">
]>;

// Callee-saved registers are handled by the register window mechanism.
Expand Down
61 changes: 55 additions & 6 deletions llvm/lib/Target/Sparc/SparcISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ static bool CC_Sparc_Assign_Ret_Split_64(unsigned &ValNo, MVT &ValVT,
}

// Allocate a full-sized argument for the 64-bit ABI.
static bool CC_Sparc64_Full(unsigned &ValNo, MVT &ValVT,
MVT &LocVT, CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
static bool Analyze_CC_Sparc64_Full(bool IsReturn, unsigned &ValNo, MVT &ValVT,
MVT &LocVT, CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
assert((LocVT == MVT::f32 || LocVT == MVT::f128
|| LocVT.getSizeInBits() == 64) &&
"Can't handle non-64 bits locations");
Expand Down Expand Up @@ -133,6 +133,11 @@ static bool CC_Sparc64_Full(unsigned &ValNo, MVT &ValVT,
return true;
}

// Bail out if this is a return CC and we run out of registers to place
// values into.
if (IsReturn)
return false;

// This argument goes on the stack in an 8-byte slot.
// When passing floats, LocVT is smaller than 8 bytes. Adjust the offset to
// the right-aligned float. The first 4 bytes of the stack slot are undefined.
Expand All @@ -146,9 +151,9 @@ static bool CC_Sparc64_Full(unsigned &ValNo, MVT &ValVT,
// Allocate a half-sized argument for the 64-bit ABI.
//
// This is used when passing { float, int } structs by value in registers.
static bool CC_Sparc64_Half(unsigned &ValNo, MVT &ValVT,
MVT &LocVT, CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
static bool Analyze_CC_Sparc64_Half(bool IsReturn, unsigned &ValNo, MVT &ValVT,
MVT &LocVT, CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
assert(LocVT.getSizeInBits() == 32 && "Can't handle non-32 bits locations");
unsigned Offset = State.AllocateStack(4, Align(4));

Expand All @@ -174,10 +179,43 @@ static bool CC_Sparc64_Half(unsigned &ValNo, MVT &ValVT,
return true;
}

// Bail out if this is a return CC and we run out of registers to place
// values into.
if (IsReturn)
return false;

State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo));
return true;
}

static bool CC_Sparc64_Full(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
return Analyze_CC_Sparc64_Full(false, ValNo, ValVT, LocVT, LocInfo, ArgFlags,
State);
}

static bool CC_Sparc64_Half(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
return Analyze_CC_Sparc64_Half(false, ValNo, ValVT, LocVT, LocInfo, ArgFlags,
State);
}

static bool RetCC_Sparc64_Full(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
return Analyze_CC_Sparc64_Full(true, ValNo, ValVT, LocVT, LocInfo, ArgFlags,
State);
}

static bool RetCC_Sparc64_Half(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
return Analyze_CC_Sparc64_Half(true, ValNo, ValVT, LocVT, LocInfo, ArgFlags,
State);
}

#include "SparcGenCallingConv.inc"

// The calling conventions in SparcCallingConv.td are described in terms of the
Expand All @@ -191,6 +229,15 @@ static unsigned toCallerWindow(unsigned Reg) {
return Reg;
}

bool SparcTargetLowering::CanLowerReturn(
CallingConv::ID CallConv, MachineFunction &MF, bool isVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context) const {
SmallVector<CCValAssign, 16> RVLocs;
CCState CCInfo(CallConv, isVarArg, MF, RVLocs, Context);
return CCInfo.CheckReturn(Outs, Subtarget->is64Bit() ? RetCC_Sparc64
: RetCC_Sparc32);
}

SDValue
SparcTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
bool IsVarArg,
Expand Down Expand Up @@ -1031,6 +1078,7 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,

// Copy all of the result registers out of their specified physreg.
for (unsigned i = 0; i != RVLocs.size(); ++i) {
assert(RVLocs[i].isRegLoc() && "Can only return in registers!");
if (RVLocs[i].getLocVT() == MVT::v2i32) {
SDValue Vec = DAG.getNode(ISD::UNDEF, dl, MVT::v2i32);
SDValue Lo = DAG.getCopyFromReg(
Expand Down Expand Up @@ -1346,6 +1394,7 @@ SparcTargetLowering::LowerCall_64(TargetLowering::CallLoweringInfo &CLI,
// Copy all of the result registers out of their specified physreg.
for (unsigned i = 0; i != RVLocs.size(); ++i) {
CCValAssign &VA = RVLocs[i];
assert(VA.isRegLoc() && "Can only return in registers!");
unsigned Reg = toCallerWindow(VA.getLocReg());

// When returning 'inreg {i32, i32 }', two consecutive i32 arguments can
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/Sparc/SparcISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ namespace llvm {
SDValue LowerCall_64(TargetLowering::CallLoweringInfo &CLI,
SmallVectorImpl<SDValue> &InVals) const;

bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF,
bool isVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs,
LLVMContext &Context) const override;

SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
Expand Down
27 changes: 0 additions & 27 deletions llvm/test/CodeGen/SPARC/64abi.ll
Original file line number Diff line number Diff line change
Expand Up @@ -293,33 +293,6 @@ define void @call_inreg_ii(i32* %p, i32 %i1, i32 %i2) {
ret void
}

; Structs up to 32 bytes in size can be returned in registers.
; CHECK-LABEL: ret_i64_pair:
; CHECK: ldx [%i2], %i0
; CHECK: ldx [%i3], %i1
define { i64, i64 } @ret_i64_pair(i32 %a0, i32 %a1, i64* %p, i64* %q) {
%r1 = load i64, i64* %p
%rv1 = insertvalue { i64, i64 } undef, i64 %r1, 0
store i64 0, i64* %p
%r2 = load i64, i64* %q
%rv2 = insertvalue { i64, i64 } %rv1, i64 %r2, 1
ret { i64, i64 } %rv2
}

; CHECK-LABEL: call_ret_i64_pair:
; CHECK: call ret_i64_pair
; CHECK: stx %o0, [%i0]
; CHECK: stx %o1, [%i0]
define void @call_ret_i64_pair(i64* %i0) {
%rv = call { i64, i64 } @ret_i64_pair(i32 undef, i32 undef,
i64* undef, i64* undef)
%e0 = extractvalue { i64, i64 } %rv, 0
store volatile i64 %e0, i64* %i0
%e1 = extractvalue { i64, i64 } %rv, 1
store i64 %e1, i64* %i0
ret void
}

; This is not a C struct, the i32 member uses 8 bytes, but the float only 4.
; CHECK-LABEL: ret_i32_float_pair:
; CHECK: ld [%i2], %i0
Expand Down
Loading

0 comments on commit 08bd84e

Please sign in to comment.