misc: Merge branch v20.1.0.3 hotfix into develop
[gem5.git] / src / arch / arm / aapcs64.hh
1 /*
2 * Copyright 2019 Google Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met: redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer;
8 * redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution;
11 * neither the name of the copyright holders nor the names of its
12 * contributors may be used to endorse or promote products derived from
13 * this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #ifndef __ARCH_ARM_AAPCS64_HH__
29 #define __ARCH_ARM_AAPCS64_HH__
30
31 #include <algorithm>
32 #include <array>
33 #include <type_traits>
34 #include <utility>
35
36 #include "arch/arm/intregs.hh"
37 #include "arch/arm/utility.hh"
38 #include "base/intmath.hh"
39 #include "cpu/thread_context.hh"
40 #include "sim/guest_abi.hh"
41 #include "sim/proxy_ptr.hh"
42
43 class ThreadContext;
44
45 struct Aapcs64
46 {
47 struct State
48 {
49 int ngrn=0; // Next general purpose register number.
50 int nsrn=0; // Next SIMD and floating point register number.
51 Addr nsaa; // Next stacked argument address.
52
53 // The maximum allowed general purpose register number.
54 static const int MAX_GRN = 7;
55 // The maximum allowed SIMD and floating point register number.
56 static const int MAX_SRN = 7;
57
58 explicit State(const ThreadContext *tc) :
59 nsaa(tc->readIntReg(ArmISA::INTREG_SPX))
60 {}
61 };
62 };
63
64 namespace GuestABI
65 {
66
67 /*
68 * Short Vectors
69 */
70
71 // A short vector is a machine type that is composed of repeated instances of
72 // one fundamental integral or floating- point type. It may be 8 or 16 bytes
73 // in total size.
74
75 template <typename T, typename Enabled=void>
76 struct IsAapcs64ShortVector : public std::false_type {};
77
78 template <typename E, size_t N>
79 struct IsAapcs64ShortVector<E[N],
80 typename std::enable_if_t<
81 (std::is_integral<E>::value || std::is_floating_point<E>::value) &&
82 (sizeof(E) * N == 8 || sizeof(E) * N == 16)>> :
83 public std::true_type
84 {};
85
86 /*
87 * Composite Types
88 */
89
90 template <typename T, typename Enabled=void>
91 struct IsAapcs64Composite : public std::false_type {};
92
93 template <typename T>
94 struct IsAapcs64Composite<T, typename std::enable_if_t<
95 (std::is_array<T>::value ||
96 std::is_class<T>::value ||
97 std::is_union<T>::value) &&
98 // VarArgs is technically a composite type, but it's not a normal argument.
99 !IsVarArgs<T>::value &&
100 // Short vectors are also composite types, but don't treat them as one.
101 !IsAapcs64ShortVector<T>::value
102 >> : public std::true_type
103 {};
104
105 // Homogeneous Aggregates
106 // These *should* be any aggregate type which has only one type of member, but
107 // we can't actually detect that or manipulate that with templates. Instead,
108 // we approximate that by detecting only arrays with that property.
109
110 // An Homogeneous Floating-Point Aggregate (HFA) is an Homogeneous Aggregate
111 // with a Fundemental Data Type that is a Floating-Point type and at most four
112 // uniquely addressable members.
113
114 template <typename T, typename Enabled=void>
115 struct IsAapcs64Hfa : public std::false_type {};
116
117 template <typename E, size_t N>
118 struct IsAapcs64Hfa<E[N],
119 typename std::enable_if_t<std::is_floating_point<E>::value && N <= 4>> :
120 public std::true_type
121 {};
122
123 // An Homogeneous Short-Vector Aggregate (HVA) is an Homogeneous Aggregate with
124 // a Fundamental Data Type that is a Short-Vector type and at most four
125 // uniquely addressable members.
126
127 template <typename T, typename Enabled=void>
128 struct IsAapcs64Hva : public std::false_type {};
129
130 template <typename E, size_t N>
131 struct IsAapcs64Hva<E[N],
132 typename std::enable_if_t<IsAapcs64ShortVector<E>::value && N <= 4>> :
133 public std::true_type
134 {};
135
136 // A shorthand to test if a type is an HVA or an HFA.
137 template <typename T, typename Enabled=void>
138 struct IsAapcs64Hxa : public std::false_type {};
139
140 template <typename T>
141 struct IsAapcs64Hxa<T, typename std::enable_if_t<
142 IsAapcs64Hfa<T>::value || IsAapcs64Hva<T>::value>> :
143 public std::true_type
144 {};
145
146 struct Aapcs64ArgumentBase
147 {
148 template <typename T>
149 static T
150 loadFromStack(ThreadContext *tc, Aapcs64::State &state)
151 {
152 // The alignment is the larger of 8 or the natural alignment of T.
153 size_t align = std::max<size_t>(8, alignof(T));
154 // Increase the size to the next multiple of 8.
155 size_t size = roundUp(sizeof(T), 8);
156
157 // Align the stack.
158 state.nsaa = roundUp(state.nsaa, align);
159
160 // Extract the value from it.
161 ConstVPtr<T> val(state.nsaa, tc);
162
163 // Move the nsaa past this argument.
164 state.nsaa += size;
165
166 // Return the value we extracted.
167 return gtoh(*val, ArmISA::byteOrder(tc));
168 }
169 };
170
171
172 /*
173 * Floating point and Short-Vector arguments and return values.
174 */
175
176 template <typename Float>
177 struct Argument<Aapcs64, Float, typename std::enable_if_t<
178 std::is_floating_point<Float>::value ||
179 IsAapcs64ShortVector<Float>::value>> :
180 public Aapcs64ArgumentBase
181 {
182 static Float
183 get(ThreadContext *tc, Aapcs64::State &state)
184 {
185 if (state.nsrn <= state.MAX_SRN) {
186 RegId id(VecRegClass, state.nsrn++);
187 return tc->readVecReg(id).laneView<Float, 0>();
188 }
189
190 return loadFromStack<Float>(tc, state);
191 }
192 };
193
194 template <typename Float>
195 struct Result<Aapcs64, Float, typename std::enable_if_t<
196 std::is_floating_point<Float>::value ||
197 IsAapcs64ShortVector<Float>::value>>
198 {
199 static void
200 store(ThreadContext *tc, const Float &f)
201 {
202 RegId id(VecRegClass, 0);
203 auto reg = tc->readVecReg(id);
204 reg.laneView<Float, 0>() = f;
205 tc->setVecReg(id, reg);
206 }
207 };
208
209
210 /*
211 * Integer arguments and return values.
212 */
213
214 // This will pick up Addr as well, which should be used for guest pointers.
215 template <typename Integer>
216 struct Argument<Aapcs64, Integer, typename std::enable_if_t<
217 std::is_integral<Integer>::value && (sizeof(Integer) <= 8)>> :
218 public Aapcs64ArgumentBase
219 {
220 static Integer
221 get(ThreadContext *tc, Aapcs64::State &state)
222 {
223 if (state.ngrn <= state.MAX_GRN)
224 return tc->readIntReg(state.ngrn++);
225
226 // Max out ngrn since we've effectively saturated it.
227 state.ngrn = state.MAX_GRN + 1;
228
229 return loadFromStack<Integer>(tc, state);
230 }
231 };
232
233 template <typename Integer>
234 struct Argument<Aapcs64, Integer, typename std::enable_if_t<
235 std::is_integral<Integer>::value && (sizeof(Integer) > 8)>> :
236 public Aapcs64ArgumentBase
237 {
238 static Integer
239 get(ThreadContext *tc, Aapcs64::State &state)
240 {
241 if (alignof(Integer) == 16 && (state.ngrn % 2))
242 state.ngrn++;
243
244 if (sizeof(Integer) == 16 && state.ngrn + 1 <= state.MAX_GRN) {
245 Integer low = tc->readIntReg(state.ngrn++);
246 Integer high = tc->readIntReg(state.ngrn++);
247 high = high << 64;
248 return high | low;
249 }
250
251 // Max out ngrn since we've effectively saturated it.
252 state.ngrn = state.MAX_GRN + 1;
253
254 return loadFromStack<Integer>(tc, state);
255 }
256 };
257
258 template <typename Integer>
259 struct Result<Aapcs64, Integer, typename std::enable_if_t<
260 std::is_integral<Integer>::value && (sizeof(Integer) <= 8)>>
261 {
262 static void
263 store(ThreadContext *tc, const Integer &i)
264 {
265 tc->setIntReg(0, i);
266 }
267 };
268
269 template <typename Integer>
270 struct Result<Aapcs64, Integer, typename std::enable_if_t<
271 std::is_integral<Integer>::value && (sizeof(Integer) > 8)>>
272 {
273 static void
274 store(ThreadContext *tc, const Integer &i)
275 {
276 tc->setIntReg(0, (uint64_t)i);
277 tc->setIntReg(1, (uint64_t)(i >> 64));
278 }
279 };
280
281
282 /*
283 * Homogeneous Floating-Point and Short-Vector Aggregates (HFAs and HVAs)
284 * argument and return values.
285 */
286
287 template <typename T>
288 struct Aapcs64ArrayType { using Type = void; };
289
290 template <typename E, size_t N>
291 struct Aapcs64ArrayType<E[N]> { using Type = E; };
292
293 template <typename HA>
294 struct Argument<Aapcs64, HA, typename std::enable_if_t<
295 IsAapcs64Hxa<HA>::value>> : public Aapcs64ArgumentBase
296 {
297 static HA
298 get(ThreadContext *tc, Aapcs64::State &state)
299 {
300 using Elem = typename Aapcs64ArrayType<HA>::Type;
301 constexpr size_t Count = sizeof(HA) / sizeof(Elem);
302
303 if (state.nsrn + Count - 1 <= state.MAX_SRN) {
304 HA ha;
305 for (int i = 0; i < Count; i++)
306 ha[i] = Argument<Aapcs64, Elem>::get(tc, state);
307 return ha;
308 }
309
310 // Max out the nsrn since we effectively exhausted it.
311 state.nsrn = state.MAX_SRN + 1;
312
313 return loadFromStack<HA>(tc, state);
314 }
315 };
316
317 template <typename HA>
318 struct Result<Aapcs64, HA,
319 typename std::enable_if_t<IsAapcs64Hxa<HA>::value>>
320 {
321 static HA
322 store(ThreadContext *tc, const HA &ha)
323 {
324 using Elem = typename Aapcs64ArrayType<HA>::Type;
325 constexpr size_t Count = sizeof(HA) / sizeof(Elem);
326
327 for (int i = 0; i < Count; i++)
328 Result<Aapcs64, Elem>::store(tc, ha[i]);
329 }
330 };
331
332
333 /*
334 * Composite arguments and return values which are not HVAs or HFAs.
335 */
336
337 template <typename Composite>
338 struct Argument<Aapcs64, Composite, typename std::enable_if_t<
339 IsAapcs64Composite<Composite>::value && !IsAapcs64Hxa<Composite>::value>> :
340 public Aapcs64ArgumentBase
341 {
342 static Composite
343 get(ThreadContext *tc, Aapcs64::State &state)
344 {
345 if (sizeof(Composite) > 16) {
346 // Composite values larger than 16 which aren't HFAs or HVAs are
347 // kept in a buffer, and the argument is actually a pointer to that
348 // buffer.
349 Addr addr = Argument<Aapcs64, Addr>::get(tc, state);
350 ConstVPtr<Composite> composite(addr, tc);
351 return gtoh(*composite, ArmISA::byteOrder(tc));
352 }
353
354 // The size of Composite must be 16 bytes or less after this point.
355
356 size_t bytes = sizeof(Composite);
357 using Chunk = uint64_t;
358
359 const int chunk_size = sizeof(Chunk);
360 const int regs = (bytes + chunk_size - 1) / chunk_size;
361
362 // Can it fit in GPRs?
363 if (state.ngrn + regs - 1 <= state.MAX_GRN) {
364 alignas(alignof(Composite)) uint8_t buf[bytes];
365 for (int i = 0; i < regs; i++) {
366 Chunk val = tc->readIntReg(state.ngrn++);
367 val = htog(val, ArmISA::byteOrder(tc));
368 size_t to_copy = std::min<size_t>(bytes, chunk_size);
369 memcpy(buf + i * chunk_size, &val, to_copy);
370 bytes -= to_copy;
371 }
372 return gtoh(*(Composite *)buf, ArmISA::byteOrder(tc));
373 }
374
375 // Max out the ngrn since we effectively exhausted it.
376 state.ngrn = state.MAX_GRN;
377
378 return loadFromStack<Composite>(tc, state);
379 }
380 };
381
382 template <typename Composite>
383 struct Result<Aapcs64, Composite, typename std::enable_if_t<
384 IsAapcs64Composite<Composite>::value && !IsAapcs64Hxa<Composite>::value>>
385 {
386 static void
387 store(ThreadContext *tc, const Composite &c)
388 {
389 if (sizeof(Composite) > 16) {
390 Addr addr = tc->readIntReg(ArmISA::INTREG_X8);
391 VPtr<Composite> composite(addr, tc);
392 *composite = htog(c, ArmISA::byteOrder(tc));
393 return;
394 }
395
396 // The size of Composite must be 16 bytes or less after this point.
397
398 size_t bytes = sizeof(Composite);
399 using Chunk = uint64_t;
400
401 const int chunk_size = sizeof(Chunk);
402 const int regs = (bytes + chunk_size - 1) / chunk_size;
403
404 Composite cp = htog(c, ArmISA::byteOrder(tc));
405 uint8_t *buf = (uint8_t *)&cp;
406 for (int i = 0; i < regs; i++) {
407 size_t to_copy = std::min<size_t>(bytes, chunk_size);
408
409 Chunk val;
410 memcpy(&val, buf, to_copy);
411 val = gtoh(val, ArmISA::byteOrder(tc));
412
413 tc->setIntReg(i, val);
414
415 bytes -= to_copy;
416 buf += to_copy;
417 }
418 }
419 };
420
421 } // namespace GuestABI
422
423 #endif // __ARCH_ARM_AAPCS64_HH__