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 @@ -57,10 +57,35 @@ CCAssignToReg<[V24, V26, V28, V30, V25, V27, V29, V31]>>> ]>; +//===----------------------------------------------------------------------===// +// 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 argument calling conventions //===----------------------------------------------------------------------===// def CC_SystemZ : CallingConv<[ + CCIfCC<"CallingConv::GHC", CCDelegateTo>, + // Promote i32 to i64 if it has an explicit extension type. // The convention is that true integer arguments that are smaller // than 64 bits should be marked as extended, but structures that @@ -128,3 +153,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,23 @@ 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"); + } + if (HasFP) { + report_fatal_error( + "In GHC calling convention a frame pointer is not supported"); + } + 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 +495,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 @@ -1675,6 +1675,9 @@ if (RetLocs.empty()) return DAG.getNode(SystemZISD::RET_FLAG, DL, MVT::Other, Chain); + if (CallConv == CallingConv::GHC) + report_fatal_error("GHC functions return void only"); + // Copy the result values into the output registers. SDValue Glue; SmallVector RetOps; @@ -2865,6 +2868,10 @@ SDValue Chain = DAG.getEntryNode(); SDValue Glue; + if (DAG.getMachineFunction().getFunction().getCallingConv() == + CallingConv::GHC) + report_fatal_error("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 +2938,10 @@ EVT PtrVT = getPointerTy(DAG.getDataLayout()); TLSModel::Model model = DAG.getTarget().getTLSModel(GV); + if (DAG.getMachineFunction().getFunction().getCallingConv() == + CallingConv::GHC) + report_fatal_error("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 +3872,9 @@ SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); MF.getInfo()->setManipulatesSP(true); + if (MF.getFunction().getCallingConv() == CallingConv::GHC) + report_fatal_error("Variable-sized stack allocations are not supported " + "in GHC calling convention"); return DAG.getCopyFromReg(Op.getOperand(0), SDLoc(Op), SystemZ::R15D, Op.getValueType()); } @@ -3871,6 +3885,10 @@ MF.getInfo()->setManipulatesSP(true); bool StoreBackchain = MF.getFunction().hasFnAttribute("backchain"); + if (MF.getFunction().getCallingConv() == CallingConv::GHC) + report_fatal_error("Variable-sized stack allocations are not supported " + "in GHC calling convention"); + SDValue Chain = Op.getOperand(0); SDValue NewSP = Op.getOperand(1); SDValue Backchain; 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,103 @@ +; Check that the GHC calling convention works (s390x) +; +; RUN: llc -mtriple=s390x-ibm-linux < %s | FileCheck %s + +@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 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,14 @@ +; Check that the GHC calling convention works (s390x) +; Check that no more than 12 integer arguments are passed +; +; RUN: not llc -mtriple=s390x-ibm-linux < %s 2>&1 | FileCheck %s + +define ghccc void @foo() nounwind { +entry: + 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(...) + +; CHECK: LLVM ERROR: No registers left in GHC calling convention diff --git a/llvm/test/CodeGen/SystemZ/ghc-cc-03.ll b/llvm/test/CodeGen/SystemZ/ghc-cc-03.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/ghc-cc-03.ll @@ -0,0 +1,11 @@ +; Check that the GHC calling convention works (s390x) +; In GHC calling convention the only allowed return type is void +; +; RUN: not llc -mtriple=s390x-ibm-linux < %s 2>&1 | FileCheck %s + +define ghccc i64 @foo() nounwind { +entry: + ret i64 42 +} + +; CHECK: LLVM ERROR: GHC functions return void only diff --git a/llvm/test/CodeGen/SystemZ/ghc-cc-04.ll b/llvm/test/CodeGen/SystemZ/ghc-cc-04.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/ghc-cc-04.ll @@ -0,0 +1,16 @@ +; Check that the GHC calling convention works (s390x) +; Thread local storage is not supported in GHC calling convention +; +; RUN: not llc -mtriple=s390x-ibm-linux < %s 2>&1 | FileCheck %s + +@x = thread_local global i32 0 + +define ghccc void @foo() nounwind { +entry: + call void @bar(i32 *@x) + ret void +} + +declare void @bar(i32*) + +; CHECK: LLVM ERROR: In GHC calling convention TLS is not supported diff --git a/llvm/test/CodeGen/SystemZ/ghc-cc-05.ll b/llvm/test/CodeGen/SystemZ/ghc-cc-05.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/ghc-cc-05.ll @@ -0,0 +1,16 @@ +; Check that the GHC calling convention works (s390x) +; Variable-sized stack allocations are not supported in GHC calling convention +; +; RUN: not llc -mtriple=s390x-ibm-linux < %s 2>&1 | FileCheck %s + +define ghccc void @foo() nounwind { +entry: + %0 = call i8* @llvm.stacksave() + call void @llvm.stackrestore(i8* %0) + ret void +} + +declare i8* @llvm.stacksave() +declare void @llvm.stackrestore(i8*) + +; CHECK: LLVM ERROR: Variable-sized stack allocations are not supported in GHC calling convention diff --git a/llvm/test/CodeGen/SystemZ/ghc-cc-06.ll b/llvm/test/CodeGen/SystemZ/ghc-cc-06.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/ghc-cc-06.ll @@ -0,0 +1,12 @@ +; Check that the GHC calling convention works (s390x) +; At most 2048*sizeof(long)=16384 bytes of stack space may be used +; +; RUN: not llc -mtriple=s390x-ibm-linux < %s 2>&1 | FileCheck %s + +define ghccc void @foo() nounwind { +entry: + alloca [16385 x i8], align 1 + ret void +} + +; CHECK: LLVM ERROR: Pre allocated stack space for GHC function is too small diff --git a/llvm/test/CodeGen/SystemZ/ghc-cc-07.ll b/llvm/test/CodeGen/SystemZ/ghc-cc-07.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/ghc-cc-07.ll @@ -0,0 +1,12 @@ +; Check that the GHC calling convention works (s390x) +; In GHC calling convention a frame pointer is not supported +; +; RUN: not llc -mtriple=s390x-ibm-linux < %s 2>&1 | FileCheck %s + +define ghccc void @foo(i64 %0) nounwind { +entry: + alloca i64, i64 %0 + ret void +} + +; CHECK: LLVM ERROR: In GHC calling convention a frame pointer is not supported