2 * Copyright 2019 Google Inc.
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.
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.
28 #ifndef __ARCH_ARM_AAPCS64_HH__
29 #define __ARCH_ARM_AAPCS64_HH__
33 #include <type_traits>
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"
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.
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;
58 explicit State(const ThreadContext *tc) :
59 nsaa(tc->readIntReg(ArmISA::INTREG_SPX))
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
75 template <typename T, typename Enabled=void>
76 struct IsAapcs64ShortVector : public std::false_type {};
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)>> :
90 template <typename T, typename Enabled=void>
91 struct IsAapcs64Composite : public std::false_type {};
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
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.
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.
114 template <typename T, typename Enabled=void>
115 struct IsAapcs64Hfa : public std::false_type {};
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
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.
127 template <typename T, typename Enabled=void>
128 struct IsAapcs64Hva : public std::false_type {};
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
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 {};
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
146 struct Aapcs64ArgumentBase
148 template <typename T>
150 loadFromStack(ThreadContext *tc, Aapcs64::State &state)
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);
158 state.nsaa = roundUp(state.nsaa, align);
160 // Extract the value from it.
161 ConstVPtr<T> val(state.nsaa, tc);
163 // Move the nsaa past this argument.
166 // Return the value we extracted.
167 return gtoh(*val, ArmISA::byteOrder(tc));
173 * Floating point and Short-Vector arguments and return values.
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
183 get(ThreadContext *tc, Aapcs64::State &state)
185 if (state.nsrn <= state.MAX_SRN) {
186 RegId id(VecRegClass, state.nsrn++);
187 return tc->readVecReg(id).laneView<Float, 0>();
190 return loadFromStack<Float>(tc, state);
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>>
200 store(ThreadContext *tc, const Float &f)
202 RegId id(VecRegClass, 0);
203 auto reg = tc->readVecReg(id);
204 reg.laneView<Float, 0>() = f;
205 tc->setVecReg(id, reg);
211 * Integer arguments and return values.
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
221 get(ThreadContext *tc, Aapcs64::State &state)
223 if (state.ngrn <= state.MAX_GRN)
224 return tc->readIntReg(state.ngrn++);
226 // Max out ngrn since we've effectively saturated it.
227 state.ngrn = state.MAX_GRN + 1;
229 return loadFromStack<Integer>(tc, state);
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
239 get(ThreadContext *tc, Aapcs64::State &state)
241 if (alignof(Integer) == 16 && (state.ngrn % 2))
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++);
251 // Max out ngrn since we've effectively saturated it.
252 state.ngrn = state.MAX_GRN + 1;
254 return loadFromStack<Integer>(tc, state);
258 template <typename Integer>
259 struct Result<Aapcs64, Integer, typename std::enable_if_t<
260 std::is_integral<Integer>::value && (sizeof(Integer) <= 8)>>
263 store(ThreadContext *tc, const Integer &i)
269 template <typename Integer>
270 struct Result<Aapcs64, Integer, typename std::enable_if_t<
271 std::is_integral<Integer>::value && (sizeof(Integer) > 8)>>
274 store(ThreadContext *tc, const Integer &i)
276 tc->setIntReg(0, (uint64_t)i);
277 tc->setIntReg(1, (uint64_t)(i >> 64));
283 * Homogeneous Floating-Point and Short-Vector Aggregates (HFAs and HVAs)
284 * argument and return values.
287 template <typename T>
288 struct Aapcs64ArrayType { using Type = void; };
290 template <typename E, size_t N>
291 struct Aapcs64ArrayType<E[N]> { using Type = E; };
293 template <typename HA>
294 struct Argument<Aapcs64, HA, typename std::enable_if_t<
295 IsAapcs64Hxa<HA>::value>> : public Aapcs64ArgumentBase
298 get(ThreadContext *tc, Aapcs64::State &state)
300 using Elem = typename Aapcs64ArrayType<HA>::Type;
301 constexpr size_t Count = sizeof(HA) / sizeof(Elem);
303 if (state.nsrn + Count - 1 <= state.MAX_SRN) {
305 for (int i = 0; i < Count; i++)
306 ha[i] = Argument<Aapcs64, Elem>::get(tc, state);
310 // Max out the nsrn since we effectively exhausted it.
311 state.nsrn = state.MAX_SRN + 1;
313 return loadFromStack<HA>(tc, state);
317 template <typename HA>
318 struct Result<Aapcs64, HA,
319 typename std::enable_if_t<IsAapcs64Hxa<HA>::value>>
322 store(ThreadContext *tc, const HA &ha)
324 using Elem = typename Aapcs64ArrayType<HA>::Type;
325 constexpr size_t Count = sizeof(HA) / sizeof(Elem);
327 for (int i = 0; i < Count; i++)
328 Result<Aapcs64, Elem>::store(tc, ha[i]);
334 * Composite arguments and return values which are not HVAs or HFAs.
337 template <typename Composite>
338 struct Argument<Aapcs64, Composite, typename std::enable_if_t<
339 IsAapcs64Composite<Composite>::value && !IsAapcs64Hxa<Composite>::value>> :
340 public Aapcs64ArgumentBase
343 get(ThreadContext *tc, Aapcs64::State &state)
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
349 Addr addr = Argument<Aapcs64, Addr>::get(tc, state);
350 ConstVPtr<Composite> composite(addr, tc);
351 return gtoh(*composite, ArmISA::byteOrder(tc));
354 // The size of Composite must be 16 bytes or less after this point.
356 size_t bytes = sizeof(Composite);
357 using Chunk = uint64_t;
359 const int chunk_size = sizeof(Chunk);
360 const int regs = (bytes + chunk_size - 1) / chunk_size;
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);
372 return gtoh(*(Composite *)buf, ArmISA::byteOrder(tc));
375 // Max out the ngrn since we effectively exhausted it.
376 state.ngrn = state.MAX_GRN;
378 return loadFromStack<Composite>(tc, state);
382 template <typename Composite>
383 struct Result<Aapcs64, Composite, typename std::enable_if_t<
384 IsAapcs64Composite<Composite>::value && !IsAapcs64Hxa<Composite>::value>>
387 store(ThreadContext *tc, const Composite &c)
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));
396 // The size of Composite must be 16 bytes or less after this point.
398 size_t bytes = sizeof(Composite);
399 using Chunk = uint64_t;
401 const int chunk_size = sizeof(Chunk);
402 const int regs = (bytes + chunk_size - 1) / chunk_size;
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);
410 memcpy(&val, buf, to_copy);
411 val = gtoh(val, ArmISA::byteOrder(tc));
413 tc->setIntReg(i, val);
421 } // namespace GuestABI
423 #endif // __ARCH_ARM_AAPCS64_HH__