arm: Fix some bugs in the aapcs64 implementation.
[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/syscall_emul_buf.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<
81 (std::is_integral<E>::value || std::is_floating_point<E>::value) &&
82 (sizeof(E) * N == 8 || sizeof(E) * N == 16)>::type> :
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<
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 >::type> : 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<std::is_floating_point<E>::value &&
120 N <= 4>::type> : 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<IsAapcs64ShortVector<E>::value &&
133 N <= 4>::type> : 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<
142 IsAapcs64Hfa<T>::value || IsAapcs64Hva<T>::value>::type> :
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 TypedBufferArg<T> val(state.nsaa);
162 val.copyIn(tc->getVirtProxy());
163
164 // Move the nsaa past this argument.
165 state.nsaa += size;
166
167 // Return the value we extracted.
168 return gtoh(*val, ArmISA::byteOrder(tc));
169 }
170 };
171
172
173 /*
174 * Floating point and Short-Vector arguments and return values.
175 */
176
177 template <typename Float>
178 struct Argument<Aapcs64, Float, typename std::enable_if<
179 std::is_floating_point<Float>::value ||
180 IsAapcs64ShortVector<Float>::value>::type> :
181 public Aapcs64ArgumentBase
182 {
183 static Float
184 get(ThreadContext *tc, Aapcs64::State &state)
185 {
186 if (state.nsrn <= state.MAX_SRN) {
187 RegId id(VecRegClass, state.nsrn++);
188 return tc->readVecReg(id).laneView<Float, 0>();
189 }
190
191 return loadFromStack<Float>(tc, state);
192 }
193 };
194
195 template <typename Float>
196 struct Result<Aapcs64, Float, typename std::enable_if<
197 std::is_floating_point<Float>::value ||
198 IsAapcs64ShortVector<Float>::value>::type>
199 {
200 static void
201 store(ThreadContext *tc, const Float &f)
202 {
203 RegId id(VecRegClass, 0);
204 auto reg = tc->readVecReg(id);
205 reg.laneView<Float, 0>() = f;
206 tc->setVecReg(id, reg);
207 }
208 };
209
210
211 /*
212 * Integer arguments and return values.
213 */
214
215 // This will pick up Addr as well, which should be used for guest pointers.
216 template <typename Integer>
217 struct Argument<Aapcs64, Integer, typename std::enable_if<
218 std::is_integral<Integer>::value && (sizeof(Integer) <= 8)
219 >::type> : public Aapcs64ArgumentBase
220 {
221 static Integer
222 get(ThreadContext *tc, Aapcs64::State &state)
223 {
224 if (state.ngrn <= state.MAX_GRN)
225 return tc->readIntReg(state.ngrn++);
226
227 // Max out ngrn since we've effectively saturated it.
228 state.ngrn = state.MAX_GRN + 1;
229
230 return loadFromStack<Integer>(tc, state);
231 }
232 };
233
234 template <typename Integer>
235 struct Argument<Aapcs64, Integer, typename std::enable_if<
236 std::is_integral<Integer>::value && (sizeof(Integer) > 8)
237 >::type> : public Aapcs64ArgumentBase
238 {
239 static Integer
240 get(ThreadContext *tc, Aapcs64::State &state)
241 {
242 if (alignof(Integer) == 16 && (state.ngrn % 2))
243 state.ngrn++;
244
245 if (sizeof(Integer) == 16 && state.ngrn + 1 <= state.MAX_GRN) {
246 Integer low = tc->readIntReg(state.ngrn++);
247 Integer high = tc->readIntReg(state.ngrn++);
248 high = high << 64;
249 return high | low;
250 }
251
252 // Max out ngrn since we've effectively saturated it.
253 state.ngrn = state.MAX_GRN + 1;
254
255 return loadFromStack<Integer>(tc, state);
256 }
257 };
258
259 template <typename Integer>
260 struct Result<Aapcs64, Integer, typename std::enable_if<
261 std::is_integral<Integer>::value && (sizeof(Integer) <= 8)
262 >::type>
263 {
264 static void
265 store(ThreadContext *tc, const Integer &i)
266 {
267 tc->setIntReg(0, i);
268 }
269 };
270
271 template <typename Integer>
272 struct Result<Aapcs64, Integer, typename std::enable_if<
273 std::is_integral<Integer>::value && (sizeof(Integer) > 8)
274 >::type>
275 {
276 static void
277 store(ThreadContext *tc, const Integer &i)
278 {
279 tc->setIntReg(0, (uint64_t)i);
280 tc->setIntReg(1, (uint64_t)(i >> 64));
281 }
282 };
283
284
285 /*
286 * Homogeneous Floating-Point and Short-Vector Aggregates (HFAs and HVAs)
287 * argument and return values.
288 */
289
290 template <typename T>
291 struct Aapcs64ArrayType { using Type = void; };
292
293 template <typename E, size_t N>
294 struct Aapcs64ArrayType<E[N]> { using Type = E; };
295
296 template <typename HA>
297 struct Argument<Aapcs64, HA, typename std::enable_if<
298 IsAapcs64Hxa<HA>::value>::type> : public Aapcs64ArgumentBase
299 {
300 static HA
301 get(ThreadContext *tc, Aapcs64::State &state)
302 {
303 using Elem = typename Aapcs64ArrayType<HA>::Type;
304 constexpr size_t Count = sizeof(HA) / sizeof(Elem);
305
306 if (state.nsrn + Count - 1 <= state.MAX_SRN) {
307 HA ha;
308 for (int i = 0; i < Count; i++)
309 ha[i] = Argument<Aapcs64, Elem>::get(tc, state);
310 return ha;
311 }
312
313 // Max out the nsrn since we effectively exhausted it.
314 state.nsrn = state.MAX_SRN + 1;
315
316 return loadFromStack<HA>(tc, state);
317 }
318 };
319
320 template <typename HA>
321 struct Result<Aapcs64, HA,
322 typename std::enable_if<IsAapcs64Hxa<HA>::value>::type>
323 {
324 static HA
325 store(ThreadContext *tc, const HA &ha)
326 {
327 using Elem = typename Aapcs64ArrayType<HA>::Type;
328 constexpr size_t Count = sizeof(HA) / sizeof(Elem);
329
330 for (int i = 0; i < Count; i++)
331 Result<Aapcs64, Elem>::store(tc, ha[i]);
332 }
333 };
334
335
336 /*
337 * Composite arguments and return values which are not HVAs or HFAs.
338 */
339
340 template <typename Composite>
341 struct Argument<Aapcs64, Composite, typename std::enable_if<
342 IsAapcs64Composite<Composite>::value && !IsAapcs64Hxa<Composite>::value
343 >::type> : public Aapcs64ArgumentBase
344 {
345 static Composite
346 get(ThreadContext *tc, Aapcs64::State &state)
347 {
348 if (sizeof(Composite) > 16) {
349 // Composite values larger than 16 which aren't HFAs or HVAs are
350 // kept in a buffer, and the argument is actually a pointer to that
351 // buffer.
352 Addr addr = Argument<Aapcs64, Addr>::get(tc, state);
353 TypedBufferArg<Composite> composite(addr);
354 composite.copyIn(tc->getVirtProxy());
355 return gtoh(*composite, ArmISA::byteOrder(tc));
356 }
357
358 // The size of Composite must be 16 bytes or less after this point.
359
360 size_t bytes = sizeof(Composite);
361 using Chunk = uint64_t;
362
363 const int chunk_size = sizeof(Chunk);
364 const int regs = (bytes + chunk_size - 1) / chunk_size;
365
366 // Can it fit in GPRs?
367 if (state.ngrn + regs - 1 <= state.MAX_GRN) {
368 alignas(alignof(Composite)) uint8_t buf[bytes];
369 for (int i = 0; i < regs; i++) {
370 Chunk val = tc->readIntReg(state.ngrn++);
371 val = htog(val, ArmISA::byteOrder(tc));
372 size_t to_copy = std::min<size_t>(bytes, chunk_size);
373 memcpy(buf + i * chunk_size, &val, to_copy);
374 bytes -= to_copy;
375 }
376 return gtoh(*(Composite *)buf, ArmISA::byteOrder(tc));
377 }
378
379 // Max out the ngrn since we effectively exhausted it.
380 state.ngrn = state.MAX_GRN;
381
382 return loadFromStack<Composite>(tc, state);
383 }
384 };
385
386 template <typename Composite>
387 struct Result<Aapcs64, Composite, typename std::enable_if<
388 IsAapcs64Composite<Composite>::value && !IsAapcs64Hxa<Composite>::value
389 >::type>
390 {
391 static void
392 store(ThreadContext *tc, const Composite &c)
393 {
394 if (sizeof(Composite) > 16) {
395 Addr addr = tc->readIntReg(ArmISA::INTREG_X8);
396 TypedBufferArg<Composite> composite(addr);
397 *composite = htog(c, ArmISA::byteOrder(tc));
398 return;
399 }
400
401 // The size of Composite must be 16 bytes or less after this point.
402
403 size_t bytes = sizeof(Composite);
404 using Chunk = uint64_t;
405
406 const int chunk_size = sizeof(Chunk);
407 const int regs = (bytes + chunk_size - 1) / chunk_size;
408
409 Composite cp = htog(c, ArmISA::byteOrder(tc));
410 uint8_t *buf = (uint8_t *)&cp;
411 for (int i = 0; i < regs; i++) {
412 size_t to_copy = std::min<size_t>(bytes, chunk_size);
413
414 Chunk val;
415 memcpy(&val, buf, to_copy);
416 val = gtoh(val, ArmISA::byteOrder(tc));
417
418 tc->setIntReg(i, val);
419
420 bytes -= to_copy;
421 buf += to_copy;
422 }
423 }
424 };
425
426 } // namespace GuestABI
427
428 #endif // __ARCH_ARM_AAPCS64_HH__