forked from OSchip/llvm-project
Complete formal arguments for the SPARC v9 64-bit ABI.
All arguments are formally assigned to stack positions and then promoted to floating point and integer registers. Since there are more floating point registers than integer registers, this can cause situations where floating point arguments are assigned to registers after integer arguments that where assigned to the stack. Use the inreg flag to indicate 32-bit fragments of structs containing both float and int members. The three-way shadowing between stack, integer, and floating point registers requires custom argument lowering. The good news is that return values are passed in the exact same way, and we can share the code. Still missing: - Update LowerReturn to handle structs returned in registers. - LowerCall. - Variadic functions. llvm-svn: 178958
This commit is contained in:
parent
bef3f8619f
commit
1c9a95ab2a
|
@ -12,25 +12,9 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Return Value Calling Conventions
|
||||
// SPARC v8 32-bit.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Sparc 32-bit C return-value convention.
|
||||
def RetCC_Sparc32 : CallingConv<[
|
||||
CCIfType<[i32], CCAssignToReg<[I0, I1, I2, I3, I4, I5]>>,
|
||||
CCIfType<[f32], CCAssignToReg<[F0, F1, F2, F3]>>,
|
||||
CCIfType<[f64], CCAssignToReg<[D0, D1]>>
|
||||
]>;
|
||||
|
||||
// Sparc 64-bit C return-value convention.
|
||||
def RetCC_Sparc64 : CallingConv<[
|
||||
CCIfType<[i32], CCPromoteToType<i64>>,
|
||||
CCIfType<[i64], CCAssignToReg<[I0, I1, I2, I3, I4, I5]>>,
|
||||
CCIfType<[f32], CCAssignToReg<[F0, F1, F2, F3]>>,
|
||||
CCIfType<[f64], CCAssignToReg<[D0, D1]>>
|
||||
]>;
|
||||
|
||||
// Sparc 32-bit C Calling convention.
|
||||
def CC_Sparc32 : CallingConv<[
|
||||
//Custom assign SRet to [sp+64].
|
||||
CCIfSRet<CCCustom<"CC_Sparc_Assign_SRet">>,
|
||||
|
@ -43,14 +27,93 @@ def CC_Sparc32 : CallingConv<[
|
|||
CCAssignToStack<4, 4>
|
||||
]>;
|
||||
|
||||
// Sparc 64-bit C Calling convention.
|
||||
def RetCC_Sparc32 : CallingConv<[
|
||||
CCIfType<[i32], CCAssignToReg<[I0, I1, I2, I3, I4, I5]>>,
|
||||
CCIfType<[f32], CCAssignToReg<[F0, F1, F2, F3]>>,
|
||||
CCIfType<[f64], CCAssignToReg<[D0, D1]>>
|
||||
]>;
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SPARC v9 64-bit.
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The 64-bit ABI conceptually assigns all function arguments to a parameter
|
||||
// array starting at [%fp+BIAS+128] in the callee's stack frame. All arguments
|
||||
// occupy a multiple of 8 bytes in the array. Integer arguments are extended to
|
||||
// 64 bits by the caller. Floats are right-aligned in their 8-byte slot, the
|
||||
// first 4 bytes in the slot are undefined.
|
||||
//
|
||||
// The integer registers %i0 to %i5 shadow the first 48 bytes of the parameter
|
||||
// array at fixed offsets. Integer arguments are promoted to registers when
|
||||
// possible.
|
||||
//
|
||||
// The floating point registers %f0 to %f31 shadow the first 128 bytes of the
|
||||
// parameter array at fixed offsets. Float and double parameters are promoted
|
||||
// to these registers when possible.
|
||||
//
|
||||
// Structs up to 16 bytes in size are passed by value. They are right-aligned
|
||||
// in one or two 8-byte slots in the parameter array. Struct members are
|
||||
// promoted to both floating point and integer registers when possible. A
|
||||
// struct containing two floats would thus be passed in %f0 and %f1, while two
|
||||
// float function arguments would occupy 8 bytes each, and be passed in %f1 and
|
||||
// %f3.
|
||||
//
|
||||
// When a struct { int, float } is passed by value, the int goes in the high
|
||||
// bits of an integer register while the float goes in a floating point
|
||||
// register.
|
||||
//
|
||||
// The difference is encoded in LLVM IR using the inreg atttribute on function
|
||||
// arguments:
|
||||
//
|
||||
// C: void f(float, float);
|
||||
// IR: declare void f(float %f1, float %f3)
|
||||
//
|
||||
// C: void f(struct { float f0, f1; });
|
||||
// IR: declare void f(float inreg %f0, float inreg %f1)
|
||||
//
|
||||
// C: void f(int, float);
|
||||
// IR: declare void f(int signext %i0, float %f3)
|
||||
//
|
||||
// C: void f(struct { int i0high; float f1; });
|
||||
// IR: declare void f(i32 inreg %i0high, float inreg %f1)
|
||||
//
|
||||
// Two ints in a struct are simply coerced to i64:
|
||||
//
|
||||
// C: void f(struct { int i0high, i0low; });
|
||||
// IR: declare void f(i64 %i0.coerced)
|
||||
//
|
||||
// The frontend and backend divide the task of producing ABI compliant code for
|
||||
// C functions. The C frontend will:
|
||||
//
|
||||
// - Annotate integer arguments with zeroext or signext attributes.
|
||||
//
|
||||
// - Split structs into one or two 64-bit sized chunks, or 32-bit chunks with
|
||||
// inreg attributes.
|
||||
//
|
||||
// - Pass structs larger than 16 bytes indirectly with an explicit pointer
|
||||
// argument. The byval attribute is not used.
|
||||
//
|
||||
// The backend will:
|
||||
//
|
||||
// - Assign all arguments to 64-bit aligned stack slots, 32-bits for inreg.
|
||||
//
|
||||
// - Promote to integer or floating point registers depending on type.
|
||||
//
|
||||
// Function return values are passed exactly like function arguments, except a
|
||||
// struct up to 32 bytes in size can be returned in registers.
|
||||
|
||||
// Function arguments AND return values.
|
||||
def CC_Sparc64 : CallingConv<[
|
||||
// The frontend uses the inreg flag to indicate i32 and float arguments from
|
||||
// structs. These arguments are not promoted to 64 bits, but they can still
|
||||
// be assigned to integer and float registers.
|
||||
CCIfInReg<CCIfType<[i32, f32], CCCustom<"CC_Sparc64_Half">>>,
|
||||
|
||||
// All integers are promoted to i64 by the caller.
|
||||
CCIfType<[i32], CCPromoteToType<i64>>,
|
||||
// Integer arguments get passed in integer registers if there is space.
|
||||
CCIfType<[i64], CCAssignToReg<[I0, I1, I2, I3, I4, I5]>>,
|
||||
// FIXME: Floating point arguments.
|
||||
|
||||
// Alternatively, they are assigned to the stack in 8-byte aligned units.
|
||||
CCAssignToStack<8, 8>
|
||||
// Custom assignment is required because stack space is reserved for all
|
||||
// arguments whether they are passed in registers or not.
|
||||
CCCustom<"CC_Sparc64_Full">
|
||||
]>;
|
||||
|
|
|
@ -74,6 +74,78 @@ static bool CC_Sparc_Assign_f64(unsigned &ValNo, MVT &ValVT,
|
|||
return true;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
assert((LocVT == MVT::f32 || LocVT.getSizeInBits() == 64) &&
|
||||
"Can't handle non-64 bits locations");
|
||||
|
||||
// Stack space is allocated for all arguments starting from [%fp+BIAS+128].
|
||||
unsigned Offset = State.AllocateStack(8, 8);
|
||||
unsigned Reg = 0;
|
||||
|
||||
if (LocVT == MVT::i64 && Offset < 6*8)
|
||||
// Promote integers to %i0-%i5.
|
||||
Reg = SP::I0 + Offset/8;
|
||||
else if (LocVT == MVT::f64 && Offset < 16*8)
|
||||
// Promote doubles to %d0-%d30. (Which LLVM calls D0-D15).
|
||||
Reg = SP::D0 + Offset/8;
|
||||
else if (LocVT == MVT::f32 && Offset < 16*8)
|
||||
// Promote floats to %f1, %f3, ...
|
||||
Reg = SP::F1 + Offset/4;
|
||||
|
||||
// Promote to register when possible, otherwise use the stack slot.
|
||||
if (Reg) {
|
||||
State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (LocVT == MVT::f32)
|
||||
Offset += 4;
|
||||
|
||||
State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
assert(LocVT.getSizeInBits() == 32 && "Can't handle non-32 bits locations");
|
||||
unsigned Offset = State.AllocateStack(4, 4);
|
||||
|
||||
if (LocVT == MVT::f32 && Offset < 16*8) {
|
||||
// Promote floats to %f0-%f31.
|
||||
State.addLoc(CCValAssign::getReg(ValNo, ValVT, SP::F0 + Offset/4,
|
||||
LocVT, LocInfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LocVT == MVT::i32 && Offset < 6*8) {
|
||||
// Promote integers to %i0-%i5, using half the register.
|
||||
unsigned Reg = SP::I0 + Offset/8;
|
||||
LocVT = MVT::i64;
|
||||
LocInfo = CCValAssign::AExt;
|
||||
|
||||
// Set the Custom bit if this i32 goes in the high bits of a register.
|
||||
if (Offset % 8 == 0)
|
||||
State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg,
|
||||
LocVT, LocInfo));
|
||||
else
|
||||
State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
#include "SparcGenCallingConv.inc"
|
||||
|
||||
SDValue
|
||||
|
@ -93,8 +165,7 @@ SparcTargetLowering::LowerReturn(SDValue Chain,
|
|||
DAG.getTarget(), RVLocs, *DAG.getContext());
|
||||
|
||||
// Analize return values.
|
||||
CCInfo.AnalyzeReturn(Outs, Subtarget->is64Bit() ?
|
||||
RetCC_Sparc64 : RetCC_Sparc32);
|
||||
CCInfo.AnalyzeReturn(Outs, Subtarget->is64Bit() ? CC_Sparc64 : RetCC_Sparc32);
|
||||
|
||||
SDValue Flag;
|
||||
SmallVector<SDValue, 4> RetOps(1, Chain);
|
||||
|
@ -384,6 +455,11 @@ LowerFormalArguments_64(SDValue Chain,
|
|||
getRegClassFor(VA.getLocVT()));
|
||||
SDValue Arg = DAG.getCopyFromReg(Chain, DL, VReg, VA.getLocVT());
|
||||
|
||||
// Get the high bits for i32 struct elements.
|
||||
if (VA.getValVT() == MVT::i32 && VA.needsCustom())
|
||||
Arg = DAG.getNode(ISD::SRL, DL, VA.getLocVT(), Arg,
|
||||
DAG.getConstant(32, MVT::i32));
|
||||
|
||||
// The caller promoted the argument, so insert an Assert?ext SDNode so we
|
||||
// won't promote the value again in this function.
|
||||
switch (VA.getLocInfo()) {
|
||||
|
@ -409,6 +485,20 @@ LowerFormalArguments_64(SDValue Chain,
|
|||
|
||||
// The registers are exhausted. This argument was passed on the stack.
|
||||
assert(VA.isMemLoc());
|
||||
// The CC_Sparc64_Full/Half functions compute stack offsets relative to the
|
||||
// beginning of the arguments area at %fp+BIAS+128.
|
||||
unsigned Offset = VA.getLocMemOffset() + 128;
|
||||
unsigned ValSize = VA.getValVT().getSizeInBits() / 8;
|
||||
// Adjust offset for extended arguments, SPARC is big-endian.
|
||||
// The caller will have written the full slot with extended bytes, but we
|
||||
// prefer our own extending loads.
|
||||
if (VA.isExtInLoc())
|
||||
Offset += 8 - ValSize;
|
||||
int FI = MF.getFrameInfo()->CreateFixedObject(ValSize, Offset, true);
|
||||
InVals.push_back(DAG.getLoad(VA.getValVT(), DL, Chain,
|
||||
DAG.getFrameIndex(FI, getPointerTy()),
|
||||
MachinePointerInfo::getFixedStack(FI),
|
||||
false, false, false, 0));
|
||||
}
|
||||
return Chain;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
; RUN: llc < %s -march=sparcv9 | FileCheck %s
|
||||
|
||||
; CHECK: intarg
|
||||
; CHECK: stb %i0, [%i4]
|
||||
; CHECK: stb %i1, [%i4]
|
||||
; CHECK: sth %i2, [%i4]
|
||||
; CHECK: st %i3, [%i4]
|
||||
; CHECK: stx %i4, [%i4]
|
||||
; CHECK: st %i5, [%i4]
|
||||
; FIXME: Stack bias
|
||||
; CHECK: ld [%fp+180], [[R:%[gilo][0-7]]]
|
||||
; CHECK: st [[R]], [%i4]
|
||||
; CHECK: ldx [%fp+184], [[R:%[gilo][0-7]]]
|
||||
; CHECK: stx [[R]], [%i4]
|
||||
define void @intarg(i8 %a0, ; %i0
|
||||
i8 %a1, ; %i1
|
||||
i16 %a2, ; %i2
|
||||
i32 %a3, ; %i3
|
||||
i8* %a4, ; %i4
|
||||
i32 %a5, ; %i5
|
||||
i32 %a6, ; [%fp+BIAS+176]
|
||||
i8* %a7) { ; [%fp+BIAS+184]
|
||||
store i8 %a0, i8* %a4
|
||||
store i8 %a1, i8* %a4
|
||||
%p16 = bitcast i8* %a4 to i16*
|
||||
store i16 %a2, i16* %p16
|
||||
%p32 = bitcast i8* %a4 to i32*
|
||||
store i32 %a3, i32* %p32
|
||||
%pp = bitcast i8* %a4 to i8**
|
||||
store i8* %a4, i8** %pp
|
||||
store i32 %a5, i32* %p32
|
||||
store i32 %a6, i32* %p32
|
||||
store i8* %a7, i8** %pp
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: floatarg
|
||||
; CHECK: fstod %f1,
|
||||
; CHECK: faddd %f2,
|
||||
; CHECK: faddd %f4,
|
||||
; CHECK: faddd %f6,
|
||||
; FIXME: Stack bias
|
||||
; CHECK: ld [%fp+260], [[F:%f[0-9]+]]
|
||||
; CHECK: fadds %f31, [[F]]
|
||||
define double @floatarg(float %a0, ; %f1
|
||||
double %a1, ; %d2
|
||||
double %a2, ; %d4
|
||||
double %a3, ; %d6
|
||||
float %a4, ; %f9
|
||||
float %a5, ; %f11
|
||||
float %a6, ; %f13
|
||||
float %a7, ; %f15
|
||||
float %a8, ; %f17
|
||||
float %a9, ; %f19
|
||||
float %a10, ; %f21
|
||||
float %a11, ; %f23
|
||||
float %a12, ; %f25
|
||||
float %a13, ; %f27
|
||||
float %a14, ; %f29
|
||||
float %a15, ; %f31
|
||||
float %a16, ; [%fp+BIAS+256] (using 8 bytes)
|
||||
float %a17) { ; [%fp+BIAS+264] (using 8 bytes)
|
||||
%d0 = fpext float %a0 to double
|
||||
%s1 = fadd double %a1, %d0
|
||||
%s2 = fadd double %a2, %s1
|
||||
%s3 = fadd double %a3, %s2
|
||||
%s16 = fadd float %a15, %a16
|
||||
%d16 = fpext float %s16 to double
|
||||
%s17 = fadd double %d16, %s3
|
||||
ret double %s17
|
||||
}
|
||||
|
||||
; CHECK: mixedarg
|
||||
; CHECK: fstod %f3
|
||||
; CHECK: faddd %f6
|
||||
; CHECK: faddd %f16
|
||||
; CHECK: ldx [%fp+184]
|
||||
; CHECK: ldx [%fp+200]
|
||||
define void @mixedarg(i8 %a0, ; %i0
|
||||
float %a1, ; %f3
|
||||
i16 %a2, ; %i2
|
||||
double %a3, ; %d6
|
||||
i13 %a4, ; %i4
|
||||
float %a5, ; %f11
|
||||
i64 %a6, ; [%fp+BIAS+176]
|
||||
double *%a7, ; [%fp+BIAS+184]
|
||||
double %a8, ; %d16
|
||||
i16* %a9) { ; [%fp+BIAS+200]
|
||||
%d1 = fpext float %a1 to double
|
||||
%s3 = fadd double %a3, %d1
|
||||
%s8 = fadd double %a8, %s3
|
||||
store double %s8, double* %a7
|
||||
store i16 %a2, i16* %a9
|
||||
ret void
|
||||
}
|
||||
|
||||
; The inreg attribute is used to indicate 32-bit sized struct elements that
|
||||
; share an 8-byte slot.
|
||||
; CHECK: inreg_fi
|
||||
; CHECK: fstoi %f1
|
||||
; CHECK: srlx %i0, 32, [[R:%[gilo][0-7]]]
|
||||
; CHECK: sub [[R]],
|
||||
define i32 @inreg_fi(i32 inreg %a0, ; high bits of %i0
|
||||
float inreg %a1) { ; %f1
|
||||
%b1 = fptosi float %a1 to i32
|
||||
%rv = sub i32 %a0, %b1
|
||||
ret i32 %rv
|
||||
}
|
||||
|
||||
; CHECK: inreg_ff
|
||||
; CHECK: fsubs %f0, %f1, %f1
|
||||
define float @inreg_ff(float inreg %a0, ; %f0
|
||||
float inreg %a1) { ; %f1
|
||||
%rv = fsub float %a0, %a1
|
||||
ret float %rv
|
||||
}
|
||||
|
||||
; CHECK: inreg_if
|
||||
; CHECK: fstoi %f0
|
||||
; CHECK: sub %i0
|
||||
define i32 @inreg_if(float inreg %a0, ; %f0
|
||||
i32 inreg %a1) { ; low bits of %i0
|
||||
%b0 = fptosi float %a0 to i32
|
||||
%rv = sub i32 %a1, %b0
|
||||
ret i32 %rv
|
||||
}
|
||||
|
||||
; The frontend shouldn't do this. Just pass i64 instead.
|
||||
; CHECK: inreg_ii
|
||||
; CHECK: srlx %i0, 32, [[R:%[gilo][0-7]]]
|
||||
; CHECK: sub %i0, [[R]], %i0
|
||||
define i32 @inreg_ii(i32 inreg %a0, ; high bits of %i0
|
||||
i32 inreg %a1) { ; low bits of %i0
|
||||
%rv = sub i32 %a1, %a0
|
||||
ret i32 %rv
|
||||
}
|
Loading…
Reference in New Issue