Skip to content

Commit d3fcbee

Browse files
koachanMaskRay
authored andcommitted
[SPARC] Make calls to function with big return values work
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
1 parent c93430b commit d3fcbee

File tree

6 files changed

+322
-36
lines changed

6 files changed

+322
-36
lines changed

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -9858,6 +9858,7 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
98589858
Entry.Alignment = Alignment;
98599859
CLI.getArgs().insert(CLI.getArgs().begin(), Entry);
98609860
CLI.NumFixedArgs += 1;
9861+
CLI.getArgs()[0].IndirectType = CLI.RetTy;
98619862
CLI.RetTy = Type::getVoidTy(CLI.RetTy->getContext());
98629863

98639864
// sret demotion isn't compatible with tail-calls, since the sret argument

llvm/lib/Target/Sparc/SparcCallingConv.td

+7-3
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,14 @@ def CC_Sparc64 : CallingConv<[
125125
def RetCC_Sparc64 : CallingConv<[
126126
// A single f32 return value always goes in %f0. The ABI doesn't specify what
127127
// happens to multiple f32 return values outside a struct.
128-
CCIfType<[f32], CCCustom<"CC_Sparc64_Half">>,
128+
CCIfType<[f32], CCCustom<"RetCC_Sparc64_Half">>,
129129

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

134138
// Callee-saved registers are handled by the register window mechanism.

llvm/lib/Target/Sparc/SparcISelLowering.cpp

+55-6
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ static bool CC_Sparc_Assign_Ret_Split_64(unsigned &ValNo, MVT &ValVT,
101101
}
102102

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

136+
// Bail out if this is a return CC and we run out of registers to place
137+
// values into.
138+
if (IsReturn)
139+
return false;
140+
136141
// This argument goes on the stack in an 8-byte slot.
137142
// When passing floats, LocVT is smaller than 8 bytes. Adjust the offset to
138143
// the right-aligned float. The first 4 bytes of the stack slot are undefined.
@@ -146,9 +151,9 @@ static bool CC_Sparc64_Full(unsigned &ValNo, MVT &ValVT,
146151
// Allocate a half-sized argument for the 64-bit ABI.
147152
//
148153
// This is used when passing { float, int } structs by value in registers.
149-
static bool CC_Sparc64_Half(unsigned &ValNo, MVT &ValVT,
150-
MVT &LocVT, CCValAssign::LocInfo &LocInfo,
151-
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
154+
static bool Analyze_CC_Sparc64_Half(bool IsReturn, unsigned &ValNo, MVT &ValVT,
155+
MVT &LocVT, CCValAssign::LocInfo &LocInfo,
156+
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
152157
assert(LocVT.getSizeInBits() == 32 && "Can't handle non-32 bits locations");
153158
unsigned Offset = State.AllocateStack(4, Align(4));
154159

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

182+
// Bail out if this is a return CC and we run out of registers to place
183+
// values into.
184+
if (IsReturn)
185+
return false;
186+
177187
State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo));
178188
return true;
179189
}
180190

191+
static bool CC_Sparc64_Full(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
192+
CCValAssign::LocInfo &LocInfo,
193+
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
194+
return Analyze_CC_Sparc64_Full(false, ValNo, ValVT, LocVT, LocInfo, ArgFlags,
195+
State);
196+
}
197+
198+
static bool CC_Sparc64_Half(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
199+
CCValAssign::LocInfo &LocInfo,
200+
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
201+
return Analyze_CC_Sparc64_Half(false, ValNo, ValVT, LocVT, LocInfo, ArgFlags,
202+
State);
203+
}
204+
205+
static bool RetCC_Sparc64_Full(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
206+
CCValAssign::LocInfo &LocInfo,
207+
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
208+
return Analyze_CC_Sparc64_Full(true, ValNo, ValVT, LocVT, LocInfo, ArgFlags,
209+
State);
210+
}
211+
212+
static bool RetCC_Sparc64_Half(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
213+
CCValAssign::LocInfo &LocInfo,
214+
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
215+
return Analyze_CC_Sparc64_Half(true, ValNo, ValVT, LocVT, LocInfo, ArgFlags,
216+
State);
217+
}
218+
181219
#include "SparcGenCallingConv.inc"
182220

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

232+
bool SparcTargetLowering::CanLowerReturn(
233+
CallingConv::ID CallConv, MachineFunction &MF, bool isVarArg,
234+
const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context) const {
235+
SmallVector<CCValAssign, 16> RVLocs;
236+
CCState CCInfo(CallConv, isVarArg, MF, RVLocs, Context);
237+
return CCInfo.CheckReturn(Outs, Subtarget->is64Bit() ? RetCC_Sparc64
238+
: RetCC_Sparc32);
239+
}
240+
194241
SDValue
195242
SparcTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
196243
bool IsVarArg,
@@ -1030,6 +1077,7 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,
10301077

10311078
// Copy all of the result registers out of their specified physreg.
10321079
for (unsigned i = 0; i != RVLocs.size(); ++i) {
1080+
assert(RVLocs[i].isRegLoc() && "Can only return in registers!");
10331081
if (RVLocs[i].getLocVT() == MVT::v2i32) {
10341082
SDValue Vec = DAG.getNode(ISD::UNDEF, dl, MVT::v2i32);
10351083
SDValue Lo = DAG.getCopyFromReg(
@@ -1344,6 +1392,7 @@ SparcTargetLowering::LowerCall_64(TargetLowering::CallLoweringInfo &CLI,
13441392
// Copy all of the result registers out of their specified physreg.
13451393
for (unsigned i = 0; i != RVLocs.size(); ++i) {
13461394
CCValAssign &VA = RVLocs[i];
1395+
assert(VA.isRegLoc() && "Can only return in registers!");
13471396
unsigned Reg = toCallerWindow(VA.getLocReg());
13481397

13491398
// When returning 'inreg {i32, i32 }', two consecutive i32 arguments can

llvm/lib/Target/Sparc/SparcISelLowering.h

+5
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ namespace llvm {
144144
SDValue LowerCall_64(TargetLowering::CallLoweringInfo &CLI,
145145
SmallVectorImpl<SDValue> &InVals) const;
146146

147+
bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF,
148+
bool isVarArg,
149+
const SmallVectorImpl<ISD::OutputArg> &Outs,
150+
LLVMContext &Context) const override;
151+
147152
SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg,
148153
const SmallVectorImpl<ISD::OutputArg> &Outs,
149154
const SmallVectorImpl<SDValue> &OutVals,

llvm/test/CodeGen/SPARC/64abi.ll

-27
Original file line numberDiff line numberDiff line change
@@ -293,33 +293,6 @@ define void @call_inreg_ii(i32* %p, i32 %i1, i32 %i2) {
293293
ret void
294294
}
295295

296-
; Structs up to 32 bytes in size can be returned in registers.
297-
; CHECK-LABEL: ret_i64_pair:
298-
; CHECK: ldx [%i2], %i0
299-
; CHECK: ldx [%i3], %i1
300-
define { i64, i64 } @ret_i64_pair(i32 %a0, i32 %a1, i64* %p, i64* %q) {
301-
%r1 = load i64, i64* %p
302-
%rv1 = insertvalue { i64, i64 } undef, i64 %r1, 0
303-
store i64 0, i64* %p
304-
%r2 = load i64, i64* %q
305-
%rv2 = insertvalue { i64, i64 } %rv1, i64 %r2, 1
306-
ret { i64, i64 } %rv2
307-
}
308-
309-
; CHECK-LABEL: call_ret_i64_pair:
310-
; CHECK: call ret_i64_pair
311-
; CHECK: stx %o0, [%i0]
312-
; CHECK: stx %o1, [%i0]
313-
define void @call_ret_i64_pair(i64* %i0) {
314-
%rv = call { i64, i64 } @ret_i64_pair(i32 undef, i32 undef,
315-
i64* undef, i64* undef)
316-
%e0 = extractvalue { i64, i64 } %rv, 0
317-
store volatile i64 %e0, i64* %i0
318-
%e1 = extractvalue { i64, i64 } %rv, 1
319-
store i64 %e1, i64* %i0
320-
ret void
321-
}
322-
323296
; This is not a C struct, the i32 member uses 8 bytes, but the float only 4.
324297
; CHECK-LABEL: ret_i32_float_pair:
325298
; CHECK: ld [%i2], %i0

0 commit comments

Comments
 (0)