diff --git a/lib/Target/Sparc/SparcCallingConv.td b/lib/Target/Sparc/SparcCallingConv.td index ed610a3ba64..856f87ad1d3 100644 --- a/lib/Target/Sparc/SparcCallingConv.td +++ b/lib/Target/Sparc/SparcCallingConv.td @@ -24,6 +24,8 @@ def RetCC_Sparc32 : CallingConv<[ // Sparc 32-bit C Calling convention. def CC_Sparc32 : CallingConv<[ + //Custom assign SRet to [sp+64]. + CCIfSRet>, // i32 f32 arguments get passed in integer registers if there is space. CCIfType<[i32, f32], CCAssignToReg<[I0, I1, I2, I3, I4, I5]>>, // f64 arguments are split and passed through registers or through stack. diff --git a/lib/Target/Sparc/SparcISelLowering.cpp b/lib/Target/Sparc/SparcISelLowering.cpp index 2767862dc32..515d9d72524 100644 --- a/lib/Target/Sparc/SparcISelLowering.cpp +++ b/lib/Target/Sparc/SparcISelLowering.cpp @@ -33,6 +33,19 @@ using namespace llvm; // Calling Convention Implementation //===----------------------------------------------------------------------===// +static bool CC_Sparc_Assign_SRet(unsigned &ValNo, MVT &ValVT, + MVT &LocVT, CCValAssign::LocInfo &LocInfo, + ISD::ArgFlagsTy &ArgFlags, CCState &State) +{ + assert (ArgFlags.isSRet()); + + //Assign SRet argument + State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, + 0, + LocVT, LocInfo)); + return true; +} + static bool CC_Sparc_Assign_f64(unsigned &ValNo, MVT &ValVT, MVT &LocVT, CCValAssign::LocInfo &LocInfo, ISD::ArgFlagsTy &ArgFlags, CCState &State) @@ -70,6 +83,8 @@ SparcTargetLowering::LowerReturn(SDValue Chain, const SmallVectorImpl &OutVals, DebugLoc dl, SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + // CCValAssign - represent the assignment of the return value to locations. SmallVector RVLocs; @@ -82,10 +97,10 @@ SparcTargetLowering::LowerReturn(SDValue Chain, // If this is the first return lowered for this function, add the regs to the // liveout set for the function. - if (DAG.getMachineFunction().getRegInfo().liveout_empty()) { + if (MF.getRegInfo().liveout_empty()) { for (unsigned i = 0; i != RVLocs.size(); ++i) if (RVLocs[i].isRegLoc()) - DAG.getMachineFunction().getRegInfo().addLiveOut(RVLocs[i].getLocReg()); + MF.getRegInfo().addLiveOut(RVLocs[i].getLocReg()); } SDValue Flag; @@ -101,6 +116,18 @@ SparcTargetLowering::LowerReturn(SDValue Chain, // Guarantee that all emitted copies are stuck together with flags. Flag = Chain.getValue(1); } + // If the function returns a struct, copy the SRetReturnReg to I0 + if (MF.getFunction()->hasStructRetAttr()) { + SparcMachineFunctionInfo *SFI = MF.getInfo(); + unsigned Reg = SFI->getSRetReturnReg(); + if (!Reg) + llvm_unreachable("sret virtual register not created in the entry block"); + SDValue Val = DAG.getCopyFromReg(Chain, dl, Reg, getPointerTy()); + Chain = DAG.getCopyToReg(Chain, dl, SP::I0, Val, Flag); + Flag = Chain.getValue(1); + if (MF.getRegInfo().liveout_empty()) + MF.getRegInfo().addLiveOut(SP::I0); + } if (Flag.getNode()) return DAG.getNode(SPISD::RET_FLAG, dl, MVT::Other, Chain, Flag); @@ -134,6 +161,17 @@ SparcTargetLowering::LowerFormalArguments(SDValue Chain, for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { CCValAssign &VA = ArgLocs[i]; + if (i == 0 && Ins[i].Flags.isSRet()) { + //Get SRet from [%fp+64] + int FrameIdx = MF.getFrameInfo()->CreateFixedObject(4, 64, true); + SDValue FIPtr = DAG.getFrameIndex(FrameIdx, MVT::i32); + SDValue Arg = DAG.getLoad(MVT::i32, dl, Chain, FIPtr, + MachinePointerInfo(), + false, false, 0); + InVals.push_back(Arg); + continue; + } + if (VA.isRegLoc()) { EVT RegVT = VA.getLocVT(); @@ -244,6 +282,18 @@ SparcTargetLowering::LowerFormalArguments(SDValue Chain, InVals.push_back(Load); } + if (MF.getFunction()->hasStructRetAttr()) { + //Copy the SRet Argument to SRetReturnReg + SparcMachineFunctionInfo *SFI = MF.getInfo(); + unsigned Reg = SFI->getSRetReturnReg(); + if (!Reg) { + Reg = MF.getRegInfo().createVirtualRegister(&SP::IntRegsRegClass); + SFI->setSRetReturnReg(Reg); + } + SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), dl, Reg, InVals[0]); + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, Copy, Chain); + } + // Store remaining ArgRegs to the stack if this is a varargs function. if (isVarArg) { static const unsigned ArgRegs[] = { @@ -374,6 +424,18 @@ SparcTargetLowering::LowerCall(SDValue Chain, SDValue Callee, break; } + if (Flags.isSRet()) { + assert(VA.needsCustom()); + // store SRet argument in %sp+64 + SDValue StackPtr = DAG.getRegister(SP::O6, MVT::i32); + SDValue PtrOff = DAG.getIntPtrConstant(64); + PtrOff = DAG.getNode(ISD::ADD, dl, MVT::i32, StackPtr, PtrOff); + MemOpChains.push_back(DAG.getStore(Chain, dl, Arg, PtrOff, + MachinePointerInfo(), + false, false, 0)); + continue; + } + if (VA.needsCustom()) { assert(VA.getLocVT() == MVT::f64); diff --git a/lib/Target/Sparc/SparcMachineFunctionInfo.h b/lib/Target/Sparc/SparcMachineFunctionInfo.h index e34c1312810..0b74308eb0e 100644 --- a/lib/Target/Sparc/SparcMachineFunctionInfo.h +++ b/lib/Target/Sparc/SparcMachineFunctionInfo.h @@ -24,16 +24,23 @@ namespace llvm { /// VarArgsFrameOffset - Frame offset to start of varargs area. int VarArgsFrameOffset; + /// SRetReturnReg - Holds the virtual register into which the sret + /// argument is passed. + unsigned SRetReturnReg; public: - SparcMachineFunctionInfo() : GlobalBaseReg(0), VarArgsFrameOffset(0) {} + SparcMachineFunctionInfo() + : GlobalBaseReg(0), VarArgsFrameOffset(0), SRetReturnReg(0) {} explicit SparcMachineFunctionInfo(MachineFunction &MF) - : GlobalBaseReg(0), VarArgsFrameOffset(0) {} + : GlobalBaseReg(0), VarArgsFrameOffset(0), SRetReturnReg(0) {} unsigned getGlobalBaseReg() const { return GlobalBaseReg; } void setGlobalBaseReg(unsigned Reg) { GlobalBaseReg = Reg; } int getVarArgsFrameOffset() const { return VarArgsFrameOffset; } void setVarArgsFrameOffset(int Offset) { VarArgsFrameOffset = Offset; } + + unsigned getSRetReturnReg() const { return SRetReturnReg; } + void setSRetReturnReg(unsigned Reg) { SRetReturnReg = Reg; } }; } diff --git a/test/CodeGen/SPARC/2011-01-22-SRet.ll b/test/CodeGen/SPARC/2011-01-22-SRet.ll new file mode 100644 index 00000000000..2f684b009c9 --- /dev/null +++ b/test/CodeGen/SPARC/2011-01-22-SRet.ll @@ -0,0 +1,36 @@ +;RUN: llc -march=sparc < %s | FileCheck %s + +%struct.foo_t = type { i32, i32, i32 } + +define weak void @make_foo(%struct.foo_t* noalias sret %agg.result, i32 %a, i32 %b, i32 %c) nounwind { +entry: +;CHECK: make_foo +;CHECK: ld [%fp+64], {{.+}} +;CHECK: or {{.+}}, {{.+}}, %i0 +;CHECK: ret + %0 = getelementptr inbounds %struct.foo_t* %agg.result, i32 0, i32 0 + store i32 %a, i32* %0, align 4 + %1 = getelementptr inbounds %struct.foo_t* %agg.result, i32 0, i32 1 + store i32 %b, i32* %1, align 4 + %2 = getelementptr inbounds %struct.foo_t* %agg.result, i32 0, i32 2 + store i32 %c, i32* %2, align 4 + ret void +} + +define i32 @test() nounwind { +entry: +;CHECK: test +;CHECK: st {{.+}}, [%sp+64] +;CHECK: make_foo + %f = alloca %struct.foo_t, align 8 + call void @make_foo(%struct.foo_t* noalias sret %f, i32 10, i32 20, i32 30) nounwind + %0 = getelementptr inbounds %struct.foo_t* %f, i32 0, i32 0 + %1 = load i32* %0, align 8 + %2 = getelementptr inbounds %struct.foo_t* %f, i32 0, i32 1 + %3 = load i32* %2, align 4 + %4 = getelementptr inbounds %struct.foo_t* %f, i32 0, i32 2 + %5 = load i32* %4, align 8 + %6 = add nsw i32 %3, %1 + %7 = add nsw i32 %6, %5 + ret i32 %7 +}