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(RegNo: Reg); |
135 | else |
136 | It.convertToMem(Offset); |
137 | State.addLoc(V: It); |
138 | } |
139 | |
140 | PendingMembers.clear(); |
141 | |
142 | return true; |
143 | } |
144 | |
145 | inline bool CC_XPLINK64_Shadow_Reg(unsigned &ValNo, MVT &ValVT, MVT &LocVT, |
146 | CCValAssign::LocInfo &LocInfo, |
147 | ISD::ArgFlagsTy &ArgFlags, CCState &State) { |
148 | if (LocVT == MVT::f32 || LocVT == MVT::f64) { |
149 | State.AllocateReg(Regs: SystemZ::XPLINK64ArgGPRs); |
150 | } |
151 | if (LocVT == MVT::f128 || LocVT.is128BitVector()) { |
152 | // Shadow next two GPRs, if available. |
153 | State.AllocateReg(Regs: SystemZ::XPLINK64ArgGPRs); |
154 | State.AllocateReg(Regs: SystemZ::XPLINK64ArgGPRs); |
155 | |
156 | // Quad precision floating point needs to |
157 | // go inside pre-defined FPR pair. |
158 | if (LocVT == MVT::f128) { |
159 | for (unsigned I = 0; I < SystemZ::XPLINK64NumArgFPRs; I += 2) |
160 | if (State.isAllocated(Reg: SystemZ::XPLINK64ArgFPRs[I])) |
161 | State.AllocateReg(Reg: SystemZ::XPLINK64ArgFPRs[I + 1]); |
162 | } |
163 | } |
164 | return false; |
165 | } |
166 | |
167 | inline bool CC_XPLINK64_Allocate128BitVararg(unsigned &ValNo, MVT &ValVT, |
168 | MVT &LocVT, |
169 | CCValAssign::LocInfo &LocInfo, |
170 | ISD::ArgFlagsTy &ArgFlags, |
171 | CCState &State) { |
172 | // For any C or C++ program, this should always be |
173 | // false, since it is illegal to have a function |
174 | // where the first argument is variadic. Therefore |
175 | // the first fixed argument should already have |
176 | // allocated GPR1 either through shadowing it or |
177 | // using it for parameter passing. |
178 | State.AllocateReg(Reg: SystemZ::R1D); |
179 | |
180 | bool AllocGPR2 = State.AllocateReg(Reg: SystemZ::R2D); |
181 | bool AllocGPR3 = State.AllocateReg(Reg: SystemZ::R3D); |
182 | |
183 | // If GPR2 and GPR3 are available, then we may pass vararg in R2Q. |
184 | // If only GPR3 is available, we need to set custom handling to copy |
185 | // hi bits into GPR3. |
186 | // Either way, we allocate on the stack. |
187 | if (AllocGPR3) { |
188 | // For f128 and vector var arg case, set the bitcast flag to bitcast to |
189 | // i128. |
190 | LocVT = MVT::i128; |
191 | LocInfo = CCValAssign::BCvt; |
192 | auto Offset = State.AllocateStack(Size: 16, Alignment: Align(8)); |
193 | if (AllocGPR2) |
194 | State.addLoc( |
195 | V: CCValAssign::getReg(ValNo, ValVT, RegNo: SystemZ::R2Q, LocVT, HTP: LocInfo)); |
196 | else |
197 | State.addLoc( |
198 | V: CCValAssign::getCustomMem(ValNo, ValVT, Offset, LocVT, HTP: LocInfo)); |
199 | return true; |
200 | } |
201 | |
202 | return false; |
203 | } |
204 | |
205 | inline bool RetCC_SystemZ_Error(unsigned &, MVT &, MVT &, |
206 | CCValAssign::LocInfo &, ISD::ArgFlagsTy &, |
207 | CCState &) { |
208 | llvm_unreachable("Return value calling convention currently unsupported." ); |
209 | } |
210 | |
211 | inline bool CC_SystemZ_Error(unsigned &, MVT &, MVT &, CCValAssign::LocInfo &, |
212 | ISD::ArgFlagsTy &, CCState &) { |
213 | llvm_unreachable("Argument calling convention currently unsupported." ); |
214 | } |
215 | |
216 | inline bool CC_SystemZ_GHC_Error(unsigned &, MVT &, MVT &, |
217 | CCValAssign::LocInfo &, ISD::ArgFlagsTy &, |
218 | CCState &) { |
219 | report_fatal_error(reason: "No registers left in GHC calling convention" ); |
220 | return false; |
221 | } |
222 | |
223 | } // end namespace llvm |
224 | |
225 | #endif |
226 | |