Pull in r198145 from upstream llvm trunk (by Venkatraman Govindaraju): [SparcV9]: Implement lowering of long double (fp128) arguments in Sparc64 ABI. Also, pass fp128 arguments to varargs through integer registers if necessary. Introduced here: http://svnweb.freebsd.org/changeset/base/262261 Index: test/CodeGen/SPARC/64abi.ll =================================================================== --- test/CodeGen/SPARC/64abi.ll +++ test/CodeGen/SPARC/64abi.ll @@ -411,3 +411,33 @@ entry: } declare i32 @use_buf(i32, i8*) + +; CHECK-LABEL: test_fp128_args +; CHECK-DAG: std %f0, [%fp+{{.+}}] +; CHECK-DAG: std %f2, [%fp+{{.+}}] +; CHECK-DAG: std %f6, [%fp+{{.+}}] +; CHECK-DAG: std %f4, [%fp+{{.+}}] +; CHECK: add %fp, [[Offset:[0-9]+]], %o0 +; CHECK: call _Qp_add +; CHECK: ldd [%fp+[[Offset]]], %f0 +define fp128 @test_fp128_args(fp128 %a, fp128 %b) { +entry: + %0 = fadd fp128 %a, %b + ret fp128 %0 +} + +declare i64 @receive_fp128(i64 %a, ...) + +; CHECK-LABEL: test_fp128_variable_args +; CHECK-DAG: std %f4, [%sp+[[Offset0:[0-9]+]]] +; CHECK-DAG: std %f6, [%sp+[[Offset1:[0-9]+]]] +; CHECK-DAG: ldx [%sp+[[Offset0]]], %o2 +; CHECK-DAG: ldx [%sp+[[Offset1]]], %o3 +; CHECK: call receive_fp128 +define i64 @test_fp128_variable_args(i64 %a, fp128 %b) { +entry: + %0 = call i64 (i64, ...)* @receive_fp128(i64 %a, fp128 %b) + ret i64 %0 +} + + Index: lib/Target/Sparc/SparcISelLowering.cpp =================================================================== --- lib/Target/Sparc/SparcISelLowering.cpp +++ lib/Target/Sparc/SparcISelLowering.cpp @@ -80,11 +80,14 @@ static bool CC_Sparc_Assign_f64(unsigned &ValNo, M 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) && + assert((LocVT == MVT::f32 || LocVT == MVT::f128 + || 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 size = (LocVT == MVT::f128) ? 16 : 8; + unsigned alignment = (LocVT == MVT::f128) ? 16 : 8; + unsigned Offset = State.AllocateStack(size, alignment); unsigned Reg = 0; if (LocVT == MVT::i64 && Offset < 6*8) @@ -96,6 +99,9 @@ static bool CC_Sparc64_Full(unsigned &ValNo, MVT & else if (LocVT == MVT::f32 && Offset < 16*8) // Promote floats to %f1, %f3, ... Reg = SP::F1 + Offset/4; + else if (LocVT == MVT::f128 && Offset < 16*8) + // Promote long doubles to %q0-%q28. (Which LLVM calls Q0-Q7). + Reg = SP::Q0 + Offset/16; // Promote to register when possible, otherwise use the stack slot. if (Reg) { @@ -998,9 +1004,10 @@ static void fixupVariableFloatArgs(SmallVectorImpl ArrayRef Outs) { for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { const CCValAssign &VA = ArgLocs[i]; + MVT ValTy = VA.getLocVT(); // FIXME: What about f32 arguments? C promotes them to f64 when calling // varargs functions. - if (!VA.isRegLoc() || VA.getLocVT() != MVT::f64) + if (!VA.isRegLoc() || (ValTy != MVT::f64 && ValTy != MVT::f128)) continue; // The fixed arguments to a varargs function still go in FP registers. if (Outs[VA.getValNo()].IsFixed) @@ -1010,15 +1017,25 @@ static void fixupVariableFloatArgs(SmallVectorImpl CCValAssign NewVA; // Determine the offset into the argument array. - unsigned Offset = 8 * (VA.getLocReg() - SP::D0); + unsigned firstReg = (ValTy == MVT::f64) ? SP::D0 : SP::Q0; + unsigned argSize = (ValTy == MVT::f64) ? 8 : 16; + unsigned Offset = argSize * (VA.getLocReg() - firstReg); assert(Offset < 16*8 && "Offset out of range, bad register enum?"); if (Offset < 6*8) { // This argument should go in %i0-%i5. unsigned IReg = SP::I0 + Offset/8; - // Full register, just bitconvert into i64. - NewVA = CCValAssign::getReg(VA.getValNo(), VA.getValVT(), - IReg, MVT::i64, CCValAssign::BCvt); + if (ValTy == MVT::f64) + // Full register, just bitconvert into i64. + NewVA = CCValAssign::getReg(VA.getValNo(), VA.getValVT(), + IReg, MVT::i64, CCValAssign::BCvt); + else { + assert(ValTy == MVT::f128 && "Unexpected type!"); + // Full register, just bitconvert into i128 -- We will lower this into + // two i64s in LowerCall_64. + NewVA = CCValAssign::getCustomReg(VA.getValNo(), VA.getValVT(), + IReg, MVT::i128, CCValAssign::BCvt); + } } else { // This needs to go to memory, we're out of integer registers. NewVA = CCValAssign::getMem(VA.getValNo(), VA.getValVT(), @@ -1094,11 +1111,46 @@ SparcTargetLowering::LowerCall_64(TargetLowering:: Arg = DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Arg); break; case CCValAssign::BCvt: - Arg = DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), Arg); + // fixupVariableFloatArgs() may create bitcasts from f128 to i128. But + // SPARC does not support i128 natively. Lower it into two i64, see below. + if (!VA.needsCustom() || VA.getValVT() != MVT::f128 + || VA.getLocVT() != MVT::i128) + Arg = DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), Arg); break; } if (VA.isRegLoc()) { + if (VA.needsCustom() && VA.getValVT() == MVT::f128 + && VA.getLocVT() == MVT::i128) { + // Store and reload into the interger register reg and reg+1. + unsigned Offset = 8 * (VA.getLocReg() - SP::I0); + unsigned StackOffset = Offset + Subtarget->getStackPointerBias() + 128; + SDValue StackPtr = DAG.getRegister(SP::O6, getPointerTy()); + SDValue HiPtrOff = DAG.getIntPtrConstant(StackOffset); + HiPtrOff = DAG.getNode(ISD::ADD, DL, getPointerTy(), StackPtr, + HiPtrOff); + SDValue LoPtrOff = DAG.getIntPtrConstant(StackOffset + 8); + LoPtrOff = DAG.getNode(ISD::ADD, DL, getPointerTy(), StackPtr, + LoPtrOff); + + // Store to %sp+BIAS+128+Offset + SDValue Store = DAG.getStore(Chain, DL, Arg, HiPtrOff, + MachinePointerInfo(), + false, false, 0); + // Load into Reg and Reg+1 + SDValue Hi64 = DAG.getLoad(MVT::i64, DL, Store, HiPtrOff, + MachinePointerInfo(), + false, false, false, 0); + SDValue Lo64 = DAG.getLoad(MVT::i64, DL, Store, LoPtrOff, + MachinePointerInfo(), + false, false, false, 0); + RegsToPass.push_back(std::make_pair(toCallerWindow(VA.getLocReg()), + Hi64)); + RegsToPass.push_back(std::make_pair(toCallerWindow(VA.getLocReg()+1), + Lo64)); + continue; + } + // The custom bit on an i32 return value indicates that it should be // passed in the high bits of the register. if (VA.getValVT() == MVT::i32 && VA.needsCustom()) {