diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.h b/llvm/lib/Target/SystemZ/SystemZCallingConv.h --- a/llvm/lib/Target/SystemZ/SystemZCallingConv.h +++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.h @@ -124,6 +124,13 @@ return true; } +inline bool CC_SystemZ_GHC_Error(unsigned &, MVT &, MVT &, + CCValAssign::LocInfo &, ISD::ArgFlagsTy &, + CCState &) { + report_fatal_error("No registers left in GHC calling convention."); + return false; +} + } // end namespace llvm #endif diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.td b/llvm/lib/Target/SystemZ/SystemZCallingConv.td --- a/llvm/lib/Target/SystemZ/SystemZCallingConv.td +++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.td @@ -111,6 +111,29 @@ CCIfType<[i32, i64, f32, f64], CCAssignToStack<8, 8>> ]>; +//===----------------------------------------------------------------------===// +// z/Linux argument calling conventions for GHC +//===----------------------------------------------------------------------===// +def CC_SystemZ_GHC : CallingConv<[ + // Pass in STG registers: Base, Sp, Hp, R1, R2, R3, R4, R5, R6, R7, R8, SpLim + CCIfType<[i64], CCAssignToReg<[R7D, R8D, R10D, R11D, R12D, R13D, + R6D, R2D, R3D, R4D, R5D, R9D]>>, + + // Pass in STG registers: F1, ..., F6 + CCIfType<[f32], CCAssignToReg<[F8S, F9S, F10S, F11S, F0S, F1S]>>, + + // Pass in STG registers: D1, ..., D6 + CCIfType<[f64], CCAssignToReg<[F12D, F13D, F14D, F15D, F2D, F3D]>>, + + // Pass in STG registers: XMM1, ..., XMM6 + CCIfSubtarget<"hasVector()", + CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], + CCIfFixed>>>, + + // Fail otherwise + CCCustom<"CC_SystemZ_GHC_Error"> +]>; + //===----------------------------------------------------------------------===// // z/Linux callee-saved registers //===----------------------------------------------------------------------===// @@ -128,3 +151,5 @@ def CSR_SystemZ_AllRegs_Vector : CalleeSavedRegs<(add (sequence "R%dD", 2, 15), (sequence "V%d", 0, 31))>; +def CSR_SystemZ_NoRegs : CalleeSavedRegs<(add)>; + diff --git a/llvm/lib/Target/SystemZ/SystemZFrameLowering.cpp b/llvm/lib/Target/SystemZ/SystemZFrameLowering.cpp --- a/llvm/lib/Target/SystemZ/SystemZFrameLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZFrameLowering.cpp @@ -351,6 +351,19 @@ const std::vector &CSI = MFFrame.getCalleeSavedInfo(); bool HasFP = hasFP(MF); + // In GHC calling convention C stack space, including the ABI-defined + // 160-byte base area, is (de)allocated by GHC itself. This stack space may + // be used by LLVM as spill slots for the tail recursive GHC functions. Thus + // do not allocate stack space here, too. + if (MF.getFunction().getCallingConv() == CallingConv::GHC) { + if (MFFrame.getStackSize() > 2048 * sizeof(long)) { + report_fatal_error( + "Pre allocated stack space for GHC function is too small"); + } + MFFrame.setStackSize(MFFrame.getStackSize() + SystemZMC::CallFrameSize); + return; + } + // Debug location must be unknown since the first debug location is used // to determine the end of the prologue. DebugLoc DL; @@ -478,6 +491,10 @@ SystemZMachineFunctionInfo *ZFI = MF.getInfo(); MachineFrameInfo &MFFrame = MF.getFrameInfo(); + // See SystemZFrameLowering::emitPrologue + if (MF.getFunction().getCallingConv() == CallingConv::GHC) + return; + // Skip the return instruction. assert(MBBI->isReturn() && "Can only insert epilogue into returning blocks"); diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -1290,6 +1290,20 @@ } } +static CCAssignFn *CCAssignFnForCall(CallingConv::ID CC) { + switch (CC) { + case CallingConv::AnyReg: + case CallingConv::C: + case CallingConv::Fast: + case CallingConv::Swift: + return CC_SystemZ; + case CallingConv::GHC: + return CC_SystemZ_GHC; + default: + report_fatal_error("Unsupported calling convention"); + } +} + SDValue SystemZTargetLowering::LowerFormalArguments( SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl &Ins, const SDLoc &DL, @@ -1310,7 +1324,7 @@ // Assign locations to all of the incoming arguments. SmallVector ArgLocs; SystemZCCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); - CCInfo.AnalyzeFormalArguments(Ins, CC_SystemZ); + CCInfo.AnalyzeFormalArguments(Ins, CCAssignFnForCall(CallConv)); unsigned NumFixedGPRs = 0; unsigned NumFixedFPRs = 0; @@ -1480,7 +1494,7 @@ // Analyze the operands of the call, assigning locations to each operand. SmallVector ArgLocs; SystemZCCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); - ArgCCInfo.AnalyzeCallOperands(Outs, CC_SystemZ); + ArgCCInfo.AnalyzeCallOperands(Outs, CCAssignFnForCall(CallConv)); // We don't support GuaranteedTailCallOpt, only automatically-detected // sibling calls. @@ -1675,6 +1689,8 @@ if (RetLocs.empty()) return DAG.getNode(SystemZISD::RET_FLAG, DL, MVT::Other, Chain); + assert(CallConv != CallingConv::GHC && "GHC functions return void only."); + // Copy the result values into the output registers. SDValue Glue; SmallVector RetOps; @@ -2865,6 +2881,10 @@ SDValue Chain = DAG.getEntryNode(); SDValue Glue; + assert(DAG.getMachineFunction().getFunction().getCallingConv() != + CallingConv::GHC && + "In GHC calling convention TLS is not supported."); + // __tls_get_offset takes the GOT offset in %r2 and the GOT in %r12. SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(PtrVT); Chain = DAG.getCopyToReg(Chain, DL, SystemZ::R12D, GOT, Glue); @@ -2931,6 +2951,10 @@ EVT PtrVT = getPointerTy(DAG.getDataLayout()); TLSModel::Model model = DAG.getTarget().getTLSModel(GV); + assert(DAG.getMachineFunction().getFunction().getCallingConv() != + CallingConv::GHC && + "In GHC calling convention TLS is not supported."); + SDValue TP = lowerThreadPointer(DL, DAG); // Get the offset of GA from the thread pointer, based on the TLS model. @@ -3861,6 +3885,7 @@ SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); MF.getInfo()->setManipulatesSP(true); + assert(MF.getFunction().getCallingConv() != CallingConv::GHC); return DAG.getCopyFromReg(Op.getOperand(0), SDLoc(Op), SystemZ::R15D, Op.getValueType()); } @@ -3870,6 +3895,7 @@ MachineFunction &MF = DAG.getMachineFunction(); MF.getInfo()->setManipulatesSP(true); bool StoreBackchain = MF.getFunction().hasFnAttribute("backchain"); + assert(MF.getFunction().getCallingConv() != CallingConv::GHC); SDValue Chain = Op.getOperand(0); SDValue NewSP = Op.getOperand(1); diff --git a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp --- a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp +++ b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp @@ -195,6 +195,8 @@ const MCPhysReg * SystemZRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { const SystemZSubtarget &Subtarget = MF->getSubtarget(); + if (MF->getFunction().getCallingConv() == CallingConv::GHC) + return CSR_SystemZ_NoRegs_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::AnyReg) return Subtarget.hasVector()? CSR_SystemZ_AllRegs_Vector_SaveList : CSR_SystemZ_AllRegs_SaveList; @@ -209,6 +211,8 @@ SystemZRegisterInfo::getCallPreservedMask(const MachineFunction &MF, CallingConv::ID CC) const { const SystemZSubtarget &Subtarget = MF.getSubtarget(); + if (CC == CallingConv::GHC) + return CSR_SystemZ_NoRegs_RegMask; if (CC == CallingConv::AnyReg) return Subtarget.hasVector()? CSR_SystemZ_AllRegs_Vector_RegMask : CSR_SystemZ_AllRegs_RegMask; diff --git a/llvm/test/CodeGen/SystemZ/ghc-cc-01.ll b/llvm/test/CodeGen/SystemZ/ghc-cc-01.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/ghc-cc-01.ll @@ -0,0 +1,123 @@ +; RUN: llc -mtriple=s390x-ibm-linux < %s | FileCheck %s + +; Check the GHC call convention works (s390x) + +@base = external global i64 ; assigned to register: r7 +@sp = external global i64 ; assigned to register: r8 +@hp = external global i64 ; assigned to register: r10 +@r1 = external global i64 ; assigned to register: r11 +@r2 = external global i64 ; assigned to register: r12 +@r3 = external global i64 ; assigned to register: r13 +@r4 = external global i64 ; assigned to register: r6 +@r5 = external global i64 ; assigned to register: r2 +@r6 = external global i64 ; assigned to register: r3 +@r7 = external global i64 ; assigned to register: r4 +@r8 = external global i64 ; assigned to register: r5 +@splim = external global i64 ; assigned to register: r9 + +@f1 = external global float ; assigned to register: s8 +@f2 = external global float ; assigned to register: s9 +@f3 = external global float ; assigned to register: s10 +@f4 = external global float ; assigned to register: s11 +@f5 = external global float ; assigned to register: s0 +@f6 = external global float ; assigned to register: s1 + +@d1 = external global double ; assigned to register: d12 +@d2 = external global double ; assigned to register: d13 +@d3 = external global double ; assigned to register: d14 +@d4 = external global double ; assigned to register: d15 +@d5 = external global double ; assigned to register: d2 +@d6 = external global double ; assigned to register: d3 + +define void @zap(i64 %a, i64 %b) nounwind { +entry: + ; CHECK: lgr %r7, %r2 + ; CHECK-NEXT: lgr %r8, %r3 + ; CHECK-NEXT: brasl %r14, addtwo + %0 = call ghccc i64 @addtwo(i64 %a, i64 %b) + ; CHECK: brasl %r14, foo + call void @foo() nounwind + ret void +} + +define ghccc i64 @addtwo(i64 %x, i64 %y) nounwind { +entry: + ; CHECK: lgr %r2, %r7 + ; CHECK-NEXT: agr %r2, %r8 + %0 = add i64 %x, %y + ; CHECK-NEXT: br %r14 + ret i64 %0 +} + +define ghccc void @foo() nounwind { +entry: + ; CHECK: larl {{%r[0-9]+}}, d6 + ; CHECK-NEXT: ld %f3, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, d5 + ; CHECK-NEXT: ld %f2, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, d4 + ; CHECK-NEXT: ld %f15, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, d3 + ; CHECK-NEXT: ld %f14, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, d2 + ; CHECK-NEXT: ld %f13, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, d1 + ; CHECK-NEXT: ld %f12, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, f6 + ; CHECK-NEXT: le %f1, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, f5 + ; CHECK-NEXT: le %f0, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, f4 + ; CHECK-NEXT: le %f11, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, f3 + ; CHECK-NEXT: le %f10, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, f2 + ; CHECK-NEXT: le %f9, 0({{%r[0-9]+}}) + ; CHECK-NEXT: larl {{%r[0-9]+}}, f1 + ; CHECK-NEXT: le %f8, 0({{%r[0-9]+}}) + ; CHECK-NEXT: lgrl %r9, splim + ; CHECK-NEXT: lgrl %r5, r8 + ; CHECK-NEXT: lgrl %r4, r7 + ; CHECK-NEXT: lgrl %r3, r6 + ; CHECK-NEXT: lgrl %r2, r5 + ; CHECK-NEXT: lgrl %r6, r4 + ; CHECK-NEXT: lgrl %r13, r3 + ; CHECK-NEXT: lgrl %r12, r2 + ; CHECK-NEXT: lgrl %r11, r1 + ; CHECK-NEXT: lgrl %r10, hp + ; CHECK-NEXT: lgrl %r8, sp + ; CHECK-NEXT: lgrl %r7, base + %0 = load double, double* @d6 + %1 = load double, double* @d5 + %2 = load double, double* @d4 + %3 = load double, double* @d3 + %4 = load double, double* @d2 + %5 = load double, double* @d1 + %6 = load float, float* @f6 + %7 = load float, float* @f5 + %8 = load float, float* @f4 + %9 = load float, float* @f3 + %10 = load float, float* @f2 + %11 = load float, float* @f1 + %12 = load i64, i64* @splim + %13 = load i64, i64* @r8 + %14 = load i64, i64* @r7 + %15 = load i64, i64* @r6 + %16 = load i64, i64* @r5 + %17 = load i64, i64* @r4 + %18 = load i64, i64* @r3 + %19 = load i64, i64* @r2 + %20 = load i64, i64* @r1 + %21 = load i64, i64* @hp + %22 = load i64, i64* @sp + %23 = load i64, i64* @base + ; CHECK: brasl %r14, bar + tail call ghccc void @bar(i64 %23, i64 %22, i64 %21, i64 %20, i64 %19, i64 %18, i64 %17, i64 %16, i64 %15, i64 %14, i64 %13, i64 %12, + float %11, float %10, float %9, float %8, float %7, float %6, + double %5, double %4, double %3, double %2, double %1, double %0) nounwind + ret void +} + +declare ghccc void @bar(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, + float, float, float, float, float, float, + double, double, double, double, double, double) diff --git a/llvm/test/CodeGen/SystemZ/ghc-cc-02.ll b/llvm/test/CodeGen/SystemZ/ghc-cc-02.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/ghc-cc-02.ll @@ -0,0 +1,25 @@ +; RUN: llc -mtriple=s390x-ibm-linux < %s | FileCheck %s +; XFAIL: * + +; Check the GHC call convention works (s390x) +; Check that no more than 12 integer arguments are passed + +define ghccc void @foo() nounwind { +entry: + ; CHECK: lghi %r7, 1 + ; CHECK-NEXT: lghi %r8, 2 + ; CHECK-NEXT: lghi %r10, 3 + ; CHECK-NEXT: lghi %r11, 4 + ; CHECK-NEXT: lghi %r12, 5 + ; CHECK-NEXT: lghi %r13, 6 + ; CHECK-NEXT: lghi %r6, 7 + ; CHECK-NEXT: lghi %r2, 8 + ; CHECK-NEXT: lghi %r3, 9 + ; CHECK-NEXT: lghi %r4, 10 + ; CHECK-NEXT: lghi %r5, 11 + ; CHECK-NEXT: lghi %r9, 12 + tail call ghccc void (...) @bar(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, i64 8, i64 9, i64 10, i64 11, i64 12, i64 13); + ret void +} + +declare ghccc void @bar(...)