[SystemZ] [z/OS] Add XPLINK64 Calling Convention to SystemZ

This patch adds the XPLINK64 calling convention to the SystemZ
backend. It specifies and implements the argument passing and
return value conventions.

Reviewed By: uweigand

Differential Revision: https://reviews.llvm.org/D101010
This commit is contained in:
Neumann Hon 2021-05-18 15:02:11 -04:00 committed by Kai Nacke
parent 9f57675e52
commit ec4706be8e
3 changed files with 229 additions and 1 deletions

View File

@ -18,3 +18,13 @@ const MCPhysReg SystemZ::ELFArgGPRs[SystemZ::ELFNumArgGPRs] = {
const MCPhysReg SystemZ::ELFArgFPRs[SystemZ::ELFNumArgFPRs] = {
SystemZ::F0D, SystemZ::F2D, SystemZ::F4D, SystemZ::F6D
};
// The XPLINK64 ABI-defined param passing general purpose registers
const MCPhysReg SystemZ::XPLINK64ArgGPRs[SystemZ::XPLINK64NumArgGPRs] = {
SystemZ::R1D, SystemZ::R2D, SystemZ::R3D
};
// The XPLINK64 ABI-defined param passing floating point registers
const MCPhysReg SystemZ::XPLINK64ArgFPRs[SystemZ::XPLINK64NumArgFPRs] = {
SystemZ::F0D, SystemZ::F2D, SystemZ::F4D, SystemZ::F6D
};

View File

@ -9,6 +9,7 @@
#ifndef LLVM_LIB_TARGET_SYSTEMZ_SYSTEMZCALLINGCONV_H
#define LLVM_LIB_TARGET_SYSTEMZ_SYSTEMZCALLINGCONV_H
#include "SystemZSubtarget.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/MC/MCRegisterInfo.h"
@ -20,6 +21,12 @@ namespace SystemZ {
const unsigned ELFNumArgFPRs = 4;
extern const MCPhysReg ELFArgFPRs[ELFNumArgFPRs];
const unsigned XPLINK64NumArgGPRs = 3;
extern const MCPhysReg XPLINK64ArgGPRs[XPLINK64NumArgGPRs];
const unsigned XPLINK64NumArgFPRs = 4;
extern const MCPhysReg XPLINK64ArgFPRs[XPLINK64NumArgFPRs];
} // end namespace SystemZ
class SystemZCCState : public CCState {
@ -107,7 +114,16 @@ inline bool CC_SystemZ_I128Indirect(unsigned &ValNo, MVT &ValVT,
// OK, we've collected all parts in the pending list. Allocate
// the location (register or stack slot) for the indirect pointer.
// (This duplicates the usual i64 calling convention rules.)
unsigned Reg = State.AllocateReg(SystemZ::ELFArgGPRs);
unsigned Reg;
const SystemZSubtarget &Subtarget =
State.getMachineFunction().getSubtarget<SystemZSubtarget>();
if (Subtarget.isTargetELF())
Reg = State.AllocateReg(SystemZ::ELFArgGPRs);
else if (Subtarget.isTargetXPLINK64())
Reg = State.AllocateReg(SystemZ::XPLINK64ArgGPRs);
else
llvm_unreachable("Unknown Calling Convention!");
unsigned Offset = Reg ? 0 : State.AllocateStack(8, Align(8));
// Use that same location for all the pending parts.
@ -124,6 +140,80 @@ inline bool CC_SystemZ_I128Indirect(unsigned &ValNo, MVT &ValVT,
return true;
}
inline bool CC_XPLINK64_Shadow_Reg(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
if (LocVT == MVT::f32 || LocVT == MVT::f64) {
State.AllocateReg(SystemZ::XPLINK64ArgGPRs);
}
if (LocVT == MVT::f128 || LocVT.is128BitVector()) {
// Shadow next two GPRs, if available.
State.AllocateReg(SystemZ::XPLINK64ArgGPRs);
State.AllocateReg(SystemZ::XPLINK64ArgGPRs);
// Quad precision floating point needs to
// go inside pre-defined FPR pair.
if (LocVT == MVT::f128) {
for (unsigned I = 0; I < SystemZ::XPLINK64NumArgFPRs; I += 2)
if (State.isAllocated(SystemZ::XPLINK64ArgFPRs[I]))
State.AllocateReg(SystemZ::XPLINK64ArgFPRs[I + 1]);
}
}
return false;
}
inline bool CC_XPLINK64_Allocate128BitVararg(unsigned &ValNo, MVT &ValVT,
MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags,
CCState &State) {
if (LocVT.getSizeInBits() < 128)
return false;
if (static_cast<SystemZCCState *>(&State)->IsFixed(ValNo))
return false;
// For any C or C++ program, this should always be
// false, since it is illegal to have a function
// where the first argument is variadic. Therefore
// the first fixed argument should already have
// allocated GPR1 either through shadowing it or
// using it for parameter passing.
State.AllocateReg(SystemZ::R1D);
bool AllocGPR2 = State.AllocateReg(SystemZ::R2D);
bool AllocGPR3 = State.AllocateReg(SystemZ::R3D);
// If GPR2 and GPR3 are available, then we may pass vararg in R2Q.
if (AllocGPR2 && AllocGPR3) {
State.addLoc(
CCValAssign::getReg(ValNo, ValVT, SystemZ::R2Q, LocVT, LocInfo));
return true;
}
// If only GPR3 is available, we allocate on stack but need to
// set custom handling to copy hi bits into GPR3.
if (!AllocGPR2 && AllocGPR3) {
auto Offset = State.AllocateStack(16, Align(8));
State.addLoc(
CCValAssign::getCustomMem(ValNo, ValVT, Offset, LocVT, LocInfo));
return true;
}
return false;
}
inline bool RetCC_SystemZ_Error(unsigned &, MVT &, MVT &,
CCValAssign::LocInfo &, ISD::ArgFlagsTy &,
CCState &) {
llvm_unreachable("Return value calling convention currently unsupported.");
}
inline bool CC_SystemZ_Error(unsigned &, MVT &, MVT &, CCValAssign::LocInfo &,
ISD::ArgFlagsTy &, CCState &) {
llvm_unreachable("Argument calling convention currently unsupported.");
}
inline bool CC_SystemZ_GHC_Error(unsigned &, MVT &, MVT &,
CCValAssign::LocInfo &, ISD::ArgFlagsTy &,
CCState &) {

View File

@ -20,6 +20,10 @@ class CCIfSubtarget<string F, CCAction A>
class CCIfFixed<CCAction A>
: CCIf<"static_cast<SystemZCCState *>(&State)->IsFixed(ValNo)", A>;
// Match if this specific argument is not a fixed (i.e. vararg) argument.
class CCIfNotFixed<CCAction A>
: CCIf<"!(static_cast<SystemZCCState *>(&State)->IsFixed(ValNo))", A>;
// Match if this specific argument was widened from a short vector type.
class CCIfShortVector<CCAction A>
: CCIf<"static_cast<SystemZCCState *>(&State)->IsShortVector(ValNo)", A>;
@ -161,11 +165,133 @@ def CSR_SystemZ_NoRegs : CalleeSavedRegs<(add)>;
def CSR_SystemZ_XPLINK64 : CalleeSavedRegs<(add (sequence "R%dD", 8, 15),
(sequence "F%dD", 8, 15))>;
def CSR_SystemZ_XPLINK64_Vector : CalleeSavedRegs<(add (sequence "R%dD", 8, 15),
(sequence "F%dD", 15, 8),
(sequence "V%d", 23, 16))>;
//===----------------------------------------------------------------------===//
// z/OS XPLINK64 return value calling convention
//===----------------------------------------------------------------------===//
def RetCC_SystemZ_XPLINK64 : CallingConv<[
// XPLINK64 ABI compliant code widens integral types smaller than i64
// to i64.
CCIfType<[i32], CCPromoteToType<i64>>,
// Structs of size 1-24 bytes are returned in R1D, R2D, and R3D.
CCIfType<[i64], CCIfInReg<CCAssignToReg<[R1D, R2D, R3D]>>>,
// An i64 is returned in R3D. R2D and R1D provided for ABI non-compliant
// code.
CCIfType<[i64], CCAssignToReg<[R3D, R2D, R1D]>>,
// ABI compliant code returns floating point values in FPR0, FPR2, FPR4
// and FPR6, using as many registers as required.
// All floating point return-value registers are call-clobbered.
CCIfType<[f32], CCAssignToReg<[F0S, F2S, F4S, F6S]>>,
CCIfType<[f64], CCAssignToReg<[F0D, F2D, F4D, F6D]>>,
// ABI compliant code returns f128 in F0D and F2D, hence F0Q.
// F4D and F6D, hence F4Q are used for complex long double types.
CCIfType<[f128], CCAssignToReg<[F0Q,F4Q]>>,
// ABI compliant code returns vectors in VR24 but other registers
// are provided for code that does not care about the ABI.
CCIfSubtarget<"hasVector()",
CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
CCAssignToReg<[V24, V25, V26, V27, V28, V29, V30, V31]>>>
]>;
//===----------------------------------------------------------------------===//
// z/OS XPLINK64 argument calling conventions
//===----------------------------------------------------------------------===//
// XPLink uses a logical argument list consisting of contiguous register-size
// words (8 bytes in 64-Bit mode) where some arguments are passed in registers
// and some in storage.
// Even though 3 GPRs, 4 FPRs, and 8 VRs may be used,
// space must be reserved for all the args on stack.
// The first three register-sized words of the parameter area are passed in
// GPRs 1-3. FP values and vector-type arguments are instead passed in FPRs
// and VRs respectively, but if a FP value or vector argument occupies one of
// the first three register-sized words of the parameter area, the corresponding
// GPR's value is not used to pass arguments.
//
// The XPLINK64 Calling Convention is fully specified in Chapter 22 of the z/OS
// Language Environment Vendor Interfaces. Appendix B of the same document contains
// examples.
def CC_SystemZ_XPLINK64 : CallingConv<[
// XPLINK64 ABI compliant code widens integral types smaller than i64
// to i64 before placing the parameters either on the stack or in registers.
CCIfType<[i32], CCIfExtend<CCPromoteToType<i64>>>,
// A SwiftSelf is passed in callee-saved R10.
CCIfSwiftSelf<CCIfType<[i64], CCAssignToReg<[R10D]>>>,
// A SwiftError is passed in R0.
CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R0D]>>>,
// First i128 values. These are already split into two i64 here,
// so we have to use a custom handler and assign into registers, if possible
// We need to deal with this first
CCIfType<[i64], CCCustom<"CC_SystemZ_I128Indirect">>,
// The first 3 integer arguments are passed in registers R1D-R3D.
// The rest will be passed in the user area. The address offset of the user
// area can be found in register R4D.
CCIfType<[i32], CCAssignToReg<[R1L, R2L, R3L]>>,
CCIfType<[i64], CCAssignToReg<[R1D, R2D, R3D]>>,
// The first 8 named vector arguments are passed in V24-V31. Sub-128 vectors
// are passed in the same way, but they're widened to one of these types
// during type legalization.
CCIfSubtarget<"hasVector()",
CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
CCIfFixed<CCCustom<"CC_XPLINK64_Shadow_Reg">>>>,
CCIfSubtarget<"hasVector()",
CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
CCIfFixed<CCAssignToReg<[V24, V25, V26, V27,
V28, V29, V30, V31]>>>>,
// The first 4 named float and double arguments are passed in registers FPR0-FPR6.
// The rest will be passed in the user area.
CCIfType<[f32, f64], CCIfFixed<CCCustom<"CC_XPLINK64_Shadow_Reg">>>,
CCIfType<[f32], CCIfFixed<CCAssignToReg<[F0S, F2S, F4S, F6S]>>>,
CCIfType<[f64], CCIfFixed<CCAssignToReg<[F0D, F2D, F4D, F6D]>>>,
// The first 2 long double arguments are passed in register FPR0/FPR2
// and FPR4/FPR6. The rest will be passed in the user area.
CCIfType<[f128], CCIfFixed<CCCustom<"CC_XPLINK64_Shadow_Reg">>>,
CCIfType<[f128], CCIfFixed<CCAssignToReg<[F0Q, F4Q]>>>,
// Non fixed floats are passed in GPRs
// Promote f32 to f64, if it needs to be passed in GPRs.
CCIfType<[f32], CCIfNotFixed<CCPromoteToType<f64>>>,
// Assign f64 varargs to their proper GPRs.
CCIfType<[f64], CCIfNotFixed<CCAssignToReg<[R1D, R2D, R3D]>>>,
// long double, can only be passed in GPR2 and GPR3, if available,
// hence R2Q
CCIfType<[f128], CCIfNotFixed<CCCustom<"CC_XPLINK64_Allocate128BitVararg">>>,
// Non fixed vector arguments are treated in the same way as long
// doubles.
CCIfSubtarget<"hasVector()",
CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
CCIfNotFixed<CCCustom<"CC_XPLINK64_Allocate128BitVararg">>>>,
// Other arguments are passed in 8-byte-aligned 8-byte stack slots.
CCIfType<[i32, i64, f32, f64], CCAssignToStack<8, 8>>,
// Other f128 arguments are passed in 8-byte-aligned 16-byte stack slots.
CCIfType<[f128], CCAssignToStack<16, 8>>,
// Vector arguments are passed in 8-byte-alinged 16-byte stack slots too.
CCIfSubtarget<"hasVector()",
CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
CCAssignToStack<16, 8>>>
]>;
//===----------------------------------------------------------------------===//
// s390x return value calling convention
//===----------------------------------------------------------------------===//
def RetCC_SystemZ : CallingConv<[
// zOS XPLINK64
CCIfSubtarget<"isTargetXPLINK64()", CCDelegateTo<RetCC_SystemZ_XPLINK64>>,
// ELF Linux SystemZ
CCIfSubtarget<"isTargetELF()", CCDelegateTo<RetCC_SystemZ_ELF>>
@ -176,6 +302,8 @@ def RetCC_SystemZ : CallingConv<[
// s390x argument calling conventions
//===----------------------------------------------------------------------===//
def CC_SystemZ : CallingConv<[
// zOS XPLINK64
CCIfSubtarget<"isTargetXPLINK64()", CCDelegateTo<CC_SystemZ_XPLINK64>>,
// ELF Linux SystemZ
CCIfSubtarget<"isTargetELF()", CCDelegateTo<CC_SystemZ_ELF>>