| 1 | //===-- SystemZCallingConv.h - Calling conventions for SystemZ --*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLVM_LIB_TARGET_SYSTEMZ_SYSTEMZCALLINGCONV_H |
| 10 | #define LLVM_LIB_TARGET_SYSTEMZ_SYSTEMZCALLINGCONV_H |
| 11 | |
| 12 | #include "SystemZSubtarget.h" |
| 13 | #include "llvm/ADT/SmallVector.h" |
| 14 | #include "llvm/CodeGen/CallingConvLower.h" |
| 15 | #include "llvm/MC/MCRegisterInfo.h" |
| 16 | |
| 17 | namespace llvm { |
| 18 | namespace SystemZ { |
| 19 | const unsigned ELFNumArgGPRs = 5; |
| 20 | extern const MCPhysReg ELFArgGPRs[ELFNumArgGPRs]; |
| 21 | |
| 22 | const unsigned ELFNumArgFPRs = 4; |
| 23 | extern const MCPhysReg ELFArgFPRs[ELFNumArgFPRs]; |
| 24 | |
| 25 | const unsigned XPLINK64NumArgGPRs = 3; |
| 26 | extern const MCPhysReg XPLINK64ArgGPRs[XPLINK64NumArgGPRs]; |
| 27 | |
| 28 | const unsigned XPLINK64NumArgFPRs = 4; |
| 29 | extern const MCPhysReg XPLINK64ArgFPRs[XPLINK64NumArgFPRs]; |
| 30 | } // end namespace SystemZ |
| 31 | |
| 32 | class SystemZCCState : public CCState { |
| 33 | private: |
| 34 | /// Records whether the value was a fixed argument. |
| 35 | /// See ISD::OutputArg::IsFixed. |
| 36 | SmallVector<bool, 4> ArgIsFixed; |
| 37 | |
| 38 | /// Records whether the value was widened from a short vector type. |
| 39 | SmallVector<bool, 4> ArgIsShortVector; |
| 40 | |
| 41 | // Check whether ArgVT is a short vector type. |
| 42 | bool IsShortVectorType(EVT ArgVT) { |
| 43 | return ArgVT.isVector() && ArgVT.getStoreSize() <= 8; |
| 44 | } |
| 45 | |
| 46 | public: |
| 47 | SystemZCCState(CallingConv::ID CC, bool isVarArg, MachineFunction &MF, |
| 48 | SmallVectorImpl<CCValAssign> &locs, LLVMContext &C) |
| 49 | : CCState(CC, isVarArg, MF, locs, C) {} |
| 50 | |
| 51 | void AnalyzeFormalArguments(const SmallVectorImpl<ISD::InputArg> &Ins, |
| 52 | CCAssignFn Fn) { |
| 53 | // Formal arguments are always fixed. |
| 54 | ArgIsFixed.clear(); |
| 55 | for (unsigned i = 0; i < Ins.size(); ++i) |
| 56 | ArgIsFixed.push_back(Elt: true); |
| 57 | // Record whether the call operand was a short vector. |
| 58 | ArgIsShortVector.clear(); |
| 59 | for (unsigned i = 0; i < Ins.size(); ++i) |
| 60 | ArgIsShortVector.push_back(Elt: IsShortVectorType(ArgVT: Ins[i].ArgVT)); |
| 61 | |
| 62 | CCState::AnalyzeFormalArguments(Ins, Fn); |
| 63 | } |
| 64 | |
| 65 | void AnalyzeCallOperands(const SmallVectorImpl<ISD::OutputArg> &Outs, |
| 66 | CCAssignFn Fn) { |
| 67 | // Record whether the call operand was a fixed argument. |
| 68 | ArgIsFixed.clear(); |
| 69 | for (unsigned i = 0; i < Outs.size(); ++i) |
| 70 | ArgIsFixed.push_back(Elt: Outs[i].IsFixed); |
| 71 | // Record whether the call operand was a short vector. |
| 72 | ArgIsShortVector.clear(); |
| 73 | for (unsigned i = 0; i < Outs.size(); ++i) |
| 74 | ArgIsShortVector.push_back(Elt: IsShortVectorType(ArgVT: Outs[i].ArgVT)); |
| 75 | |
| 76 | CCState::AnalyzeCallOperands(Outs, Fn); |
| 77 | } |
| 78 | |
| 79 | // This version of AnalyzeCallOperands in the base class is not usable |
| 80 | // since we must provide a means of accessing ISD::OutputArg::IsFixed. |
| 81 | void AnalyzeCallOperands(const SmallVectorImpl<MVT> &Outs, |
| 82 | SmallVectorImpl<ISD::ArgFlagsTy> &Flags, |
| 83 | CCAssignFn Fn) = delete; |
| 84 | |
| 85 | bool IsFixed(unsigned ValNo) { return ArgIsFixed[ValNo]; } |
| 86 | bool IsShortVector(unsigned ValNo) { return ArgIsShortVector[ValNo]; } |
| 87 | }; |
| 88 | |
| 89 | // Handle i128 argument types. These need to be passed by implicit |
| 90 | // reference. This could be as simple as the following .td line: |
| 91 | // CCIfType<[i128], CCPassIndirect<i64>>, |
| 92 | // except that i128 is not a legal type, and therefore gets split by |
| 93 | // common code into a pair of i64 arguments. |
| 94 | inline bool CC_SystemZ_I128Indirect(unsigned &ValNo, MVT &ValVT, |
| 95 | MVT &LocVT, |
| 96 | CCValAssign::LocInfo &LocInfo, |
| 97 | ISD::ArgFlagsTy &ArgFlags, |
| 98 | CCState &State) { |
| 99 | SmallVectorImpl<CCValAssign> &PendingMembers = State.getPendingLocs(); |
| 100 | |
| 101 | // ArgFlags.isSplit() is true on the first part of a i128 argument; |
| 102 | // PendingMembers.empty() is false on all subsequent parts. |
| 103 | if (!ArgFlags.isSplit() && PendingMembers.empty()) |
| 104 | return false; |
| 105 | |
| 106 | // Push a pending Indirect value location for each part. |
| 107 | LocVT = MVT::i64; |
| 108 | LocInfo = CCValAssign::Indirect; |
| 109 | PendingMembers.push_back(Elt: CCValAssign::getPending(ValNo, ValVT, |
| 110 | LocVT, HTP: LocInfo)); |
| 111 | if (!ArgFlags.isSplitEnd()) |
| 112 | return true; |
| 113 | |
| 114 | // OK, we've collected all parts in the pending list. Allocate |
| 115 | // the location (register or stack slot) for the indirect pointer. |
| 116 | // (This duplicates the usual i64 calling convention rules.) |
| 117 | unsigned Reg; |
| 118 | const SystemZSubtarget &Subtarget = |
| 119 | State.getMachineFunction().getSubtarget<SystemZSubtarget>(); |
| 120 | if (Subtarget.isTargetELF()) |
| 121 | Reg = State.AllocateReg(Regs: SystemZ::ELFArgGPRs); |
| 122 | else if (Subtarget.isTargetXPLINK64()) |
| 123 | Reg = State.AllocateReg(Regs: SystemZ::XPLINK64ArgGPRs); |
| 124 | else |
| 125 | llvm_unreachable("Unknown Calling Convention!" ); |
| 126 | |
| 127 | unsigned Offset = Reg && !Subtarget.isTargetXPLINK64() |
| 128 | ? 0 |
| 129 | : State.AllocateStack(Size: 8, Alignment: Align(8)); |
| 130 | |
| 131 | // Use that same location for all the pending parts. |
| 132 | for (auto &It : PendingMembers) { |
| 133 | if (Reg) |
| 134 | It.convertToReg(Reg); |
| 135 | else |
| 136 | It.convertToMem(Offset); |
| 137 | State.addLoc(V: It); |
| 138 | } |
| 139 | |
| 140 | PendingMembers.clear(); |
| 141 | |
| 142 | return true; |
| 143 | } |
| 144 | |
| 145 | // A pointer in 64bit mode is always passed as 64bit. |
| 146 | inline bool CC_XPLINK64_Pointer(unsigned &ValNo, MVT &ValVT, MVT &LocVT, |
| 147 | CCValAssign::LocInfo &LocInfo, |
| 148 | ISD::ArgFlagsTy &ArgFlags, CCState &State) { |
| 149 | if (LocVT != MVT::i64) { |
| 150 | LocVT = MVT::i64; |
| 151 | LocInfo = CCValAssign::ZExt; |
| 152 | } |
| 153 | return false; |
| 154 | } |
| 155 | |
| 156 | inline bool CC_XPLINK64_Shadow_Reg(unsigned &ValNo, MVT &ValVT, MVT &LocVT, |
| 157 | CCValAssign::LocInfo &LocInfo, |
| 158 | ISD::ArgFlagsTy &ArgFlags, CCState &State) { |
| 159 | if (LocVT == MVT::f32 || LocVT == MVT::f64) { |
| 160 | State.AllocateReg(Regs: SystemZ::XPLINK64ArgGPRs); |
| 161 | } |
| 162 | if (LocVT == MVT::f128 || LocVT.is128BitVector()) { |
| 163 | // Shadow next two GPRs, if available. |
| 164 | State.AllocateReg(Regs: SystemZ::XPLINK64ArgGPRs); |
| 165 | State.AllocateReg(Regs: SystemZ::XPLINK64ArgGPRs); |
| 166 | |
| 167 | // Quad precision floating point needs to |
| 168 | // go inside pre-defined FPR pair. |
| 169 | if (LocVT == MVT::f128) { |
| 170 | for (unsigned I = 0; I < SystemZ::XPLINK64NumArgFPRs; I += 2) |
| 171 | if (State.isAllocated(Reg: SystemZ::XPLINK64ArgFPRs[I])) |
| 172 | State.AllocateReg(Reg: SystemZ::XPLINK64ArgFPRs[I + 1]); |
| 173 | } |
| 174 | } |
| 175 | return false; |
| 176 | } |
| 177 | |
| 178 | inline bool CC_XPLINK64_Allocate128BitVararg(unsigned &ValNo, MVT &ValVT, |
| 179 | MVT &LocVT, |
| 180 | CCValAssign::LocInfo &LocInfo, |
| 181 | ISD::ArgFlagsTy &ArgFlags, |
| 182 | CCState &State) { |
| 183 | // For any C or C++ program, this should always be |
| 184 | // false, since it is illegal to have a function |
| 185 | // where the first argument is variadic. Therefore |
| 186 | // the first fixed argument should already have |
| 187 | // allocated GPR1 either through shadowing it or |
| 188 | // using it for parameter passing. |
| 189 | State.AllocateReg(Reg: SystemZ::R1D); |
| 190 | |
| 191 | bool AllocGPR2 = State.AllocateReg(Reg: SystemZ::R2D); |
| 192 | bool AllocGPR3 = State.AllocateReg(Reg: SystemZ::R3D); |
| 193 | |
| 194 | // If GPR2 and GPR3 are available, then we may pass vararg in R2Q. |
| 195 | // If only GPR3 is available, we need to set custom handling to copy |
| 196 | // hi bits into GPR3. |
| 197 | // Either way, we allocate on the stack. |
| 198 | if (AllocGPR3) { |
| 199 | // For f128 and vector var arg case, set the bitcast flag to bitcast to |
| 200 | // i128. |
| 201 | LocVT = MVT::i128; |
| 202 | LocInfo = CCValAssign::BCvt; |
| 203 | auto Offset = State.AllocateStack(Size: 16, Alignment: Align(8)); |
| 204 | if (AllocGPR2) |
| 205 | State.addLoc( |
| 206 | V: CCValAssign::getReg(ValNo, ValVT, Reg: SystemZ::R2Q, LocVT, HTP: LocInfo)); |
| 207 | else |
| 208 | State.addLoc( |
| 209 | V: CCValAssign::getCustomMem(ValNo, ValVT, Offset, LocVT, HTP: LocInfo)); |
| 210 | return true; |
| 211 | } |
| 212 | |
| 213 | return false; |
| 214 | } |
| 215 | |
| 216 | inline bool RetCC_SystemZ_Error(unsigned &, MVT &, MVT &, |
| 217 | CCValAssign::LocInfo &, ISD::ArgFlagsTy &, |
| 218 | CCState &) { |
| 219 | llvm_unreachable("Return value calling convention currently unsupported." ); |
| 220 | } |
| 221 | |
| 222 | inline bool CC_SystemZ_Error(unsigned &, MVT &, MVT &, CCValAssign::LocInfo &, |
| 223 | ISD::ArgFlagsTy &, CCState &) { |
| 224 | llvm_unreachable("Argument calling convention currently unsupported." ); |
| 225 | } |
| 226 | |
| 227 | inline bool CC_SystemZ_GHC_Error(unsigned &, MVT &, MVT &, |
| 228 | CCValAssign::LocInfo &, ISD::ArgFlagsTy &, |
| 229 | CCState &) { |
| 230 | report_fatal_error(reason: "No registers left in GHC calling convention" ); |
| 231 | return false; |
| 232 | } |
| 233 | |
| 234 | } // end namespace llvm |
| 235 | |
| 236 | #endif |
| 237 | |