Index: llvm/lib/Target/SystemZ/SystemZCallingConv.h =================================================================== --- llvm/lib/Target/SystemZ/SystemZCallingConv.h +++ llvm/lib/Target/SystemZ/SystemZCallingConv.h @@ -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 @@ 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 @@ // 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(); + 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 @@ 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(&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 &) { Index: llvm/lib/Target/SystemZ/SystemZCallingConv.cpp =================================================================== --- llvm/lib/Target/SystemZ/SystemZCallingConv.cpp +++ llvm/lib/Target/SystemZ/SystemZCallingConv.cpp @@ -18,3 +18,13 @@ 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 +}; Index: llvm/lib/Target/SystemZ/SystemZCallingConv.td =================================================================== --- llvm/lib/Target/SystemZ/SystemZCallingConv.td +++ llvm/lib/Target/SystemZ/SystemZCallingConv.td @@ -20,6 +20,10 @@ class CCIfFixed : CCIf<"static_cast(&State)->IsFixed(ValNo)", A>; +// Match if this specific argument is not a fixed (i.e. vararg) argument. +class CCIfNotFixed + : CCIf<"!(static_cast(&State)->IsFixed(ValNo))", A>; + // Match if this specific argument was widened from a short vector type. class CCIfShortVector : CCIf<"static_cast(&State)->IsShortVector(ValNo)", A>; @@ -161,11 +165,133 @@ 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 i1, i8, i16, i32 to i64. + CCIfType<[i1, i8, i16, i32], CCPromoteToType>, + + // Structs of size 1-24 bytes are returned in R1D, R2D, and R3D. + CCIfType<[i64], CCIfInReg>>, + // 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 + CCIfSubtarget<"hasVector()", + CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], + CCAssignToReg<[V24]>>> +]>; + +//===----------------------------------------------------------------------===// +// 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 i1, i8, i16, i32 to i64 + // before placing the parameters either on the stack or in registers + CCIfType<[i1, i8, i16, i32], CCIfExtend>>, + + // A SwiftSelf is passed in callee-saved R10. + CCIfSwiftSelf>>, + + // A SwiftError is passed in R0. + CCIfSwiftError>>, + + // 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>>>, + CCIfSubtarget<"hasVector()", + CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], + CCIfFixed>>>, + + // 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>>, + CCIfType<[f32], CCIfFixed>>, + CCIfType<[f64], CCIfFixed>>, + // The first 2 long double arguments are passed in register FPR0/FPR2 + // and FPR4/FPR6. The rest are passed on the stack + // Unless the arguments are passed as VARARGS + CCIfType<[f128], CCIfFixed>>, + CCIfType<[f128], CCIfFixed>>, + + // Non fixed floats are passed in GPRs + // Promote f32 to f64, if it needs to be passed in GPRs + CCIfType<[f32], CCIfNotFixed>>, + // Assign f64 varargs to their proper GPRs + CCIfType<[f64], CCIfNotFixed>>, + // long double, can only be passed in GPR2 and GPR3, if available, + // hence R2Q + CCIfType<[f128], CCIfNotFixed>>, + + // Non fixed vector arguments are treated in the same way as long + // doubles. + CCIfSubtarget<"hasVector()", + CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], + CCIfNotFixed>>>, + + // 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>, // ELF Linux SystemZ CCIfSubtarget<"isTargetELF()", CCDelegateTo> @@ -176,6 +302,8 @@ // s390x argument calling conventions //===----------------------------------------------------------------------===// def CC_SystemZ : CallingConv<[ + // zOS XPLINK64 + CCIfSubtarget<"isTargetXPLINK64()", CCDelegateTo>, // ELF Linux SystemZ CCIfSubtarget<"isTargetELF()", CCDelegateTo>