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