[SystemZ] Add GHC calling convention

This is a special calling convention to be used by the GHC compiler.

Author: Stefan Schulze Frielinghaus
Differential Revision: https://reviews.llvm.org/D69024
This commit is contained in:
Ulrich Weigand 2019-11-04 13:26:38 +01:00
parent 499c90afe9
commit 22f9429149
12 changed files with 261 additions and 0 deletions

View File

@ -124,6 +124,13 @@ inline bool CC_SystemZ_I128Indirect(unsigned &ValNo, MVT &ValVT,
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

View File

@ -57,10 +57,35 @@ def RetCC_SystemZ : CallingConv<[
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<CCAssignToReg<[V16, V17, V18, V19, V20, V21]>>>>,
// Fail otherwise
CCCustom<"CC_SystemZ_GHC_Error">
]>;
//===----------------------------------------------------------------------===//
// z/Linux argument calling conventions
//===----------------------------------------------------------------------===//
def CC_SystemZ : CallingConv<[
CCIfCC<"CallingConv::GHC", CCDelegateTo<CC_SystemZ_GHC>>,
// 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 : CalleeSavedRegs<(add (sequence "R%dD", 2, 15),
def CSR_SystemZ_AllRegs_Vector : CalleeSavedRegs<(add (sequence "R%dD", 2, 15),
(sequence "V%d", 0, 31))>;
def CSR_SystemZ_NoRegs : CalleeSavedRegs<(add)>;

View File

@ -351,6 +351,23 @@ void SystemZFrameLowering::emitPrologue(MachineFunction &MF,
const std::vector<CalleeSavedInfo> &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 @@ void SystemZFrameLowering::emitEpilogue(MachineFunction &MF,
SystemZMachineFunctionInfo *ZFI = MF.getInfo<SystemZMachineFunctionInfo>();
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");

View File

@ -1675,6 +1675,9 @@ SystemZTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
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<SDValue, 4> RetOps;
@ -2874,6 +2877,10 @@ SDValue SystemZTargetLowering::lowerTLSGetOffset(GlobalAddressSDNode *Node,
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);
@ -2940,6 +2947,10 @@ SDValue SystemZTargetLowering::lowerGlobalTLSAddress(GlobalAddressSDNode *Node,
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.
@ -3870,6 +3881,9 @@ SDValue SystemZTargetLowering::lowerSTACKSAVE(SDValue Op,
SelectionDAG &DAG) const {
MachineFunction &MF = DAG.getMachineFunction();
MF.getInfo<SystemZMachineFunctionInfo>()->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());
}
@ -3880,6 +3894,10 @@ SDValue SystemZTargetLowering::lowerSTACKRESTORE(SDValue Op,
MF.getInfo<SystemZMachineFunctionInfo>()->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;

View File

@ -195,6 +195,8 @@ SystemZRegisterInfo::getRegAllocationHints(unsigned VirtReg,
const MCPhysReg *
SystemZRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
const SystemZSubtarget &Subtarget = MF->getSubtarget<SystemZSubtarget>();
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 @@ const uint32_t *
SystemZRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID CC) const {
const SystemZSubtarget &Subtarget = MF.getSubtarget<SystemZSubtarget>();
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;

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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