forked from OSchip/llvm-project
[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:
parent
499c90afe9
commit
22f9429149
|
@ -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
|
||||
|
|
|
@ -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)>;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue