1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef PANDA_RUNTIME_ARCH_HELPERS_H_
16 #define PANDA_RUNTIME_ARCH_HELPERS_H_
17 
18 #include "libpandabase/utils/arch.h"
19 #include "libpandabase/utils/bit_utils.h"
20 #include "libpandabase/utils/span.h"
21 #include "runtime/include/value.h"
22 #include "runtime/include/mem/panda_containers.h"
23 
24 namespace ark::arch {
25 
26 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
27 #define ARCH_COPY_METHOD_ARGS_DISPATCH                  \
28     it.IncrementWithoutCheck();                         \
29     encoding = (*it).GetEncoding();                     \
30     ASSERT(!(encoding & ~SHORTY_ELEM_MAX));             \
31     /* CC-OFFNXT(G.PRE.05, G.PRE.09) code generation */ \
32     goto *dispatch_table[encoding];
33 
34 // We use macro instead of function because it's impossible to inline a dispatch table
35 // We should inline the dispatch table for performance reasons.
36 // LABEL_TYPEID_INVALID before LABEL_TYPEID_REFERENCE refers to tagged type (types.yaml) and does not handles here
37 // CC-OFFNXT(C_RULE_ID_DEFINE_LENGTH_LIMIT) solid logic
38 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
39 #define ARCH_COPY_METHOD_ARGS(METHOD, ARG_READER, ARG_WRITER)                                                        \
40     do {                                                                                                             \
41         [[maybe_unused]] static constexpr size_t SHORTY_ELEM_MAX = 0xF;                                              \
42         static constexpr std::array dispatch_table = {                                                               \
43             static_cast<const void *>(&&LABEL_TYPEID_END),       static_cast<const void *>(&&LABEL_TYPEID_VOID),     \
44             static_cast<const void *>(&&LABEL_TYPEID_U8),        static_cast<const void *>(&&LABEL_TYPEID_I8),       \
45             static_cast<const void *>(&&LABEL_TYPEID_U8),        static_cast<const void *>(&&LABEL_TYPEID_I16),      \
46             static_cast<const void *>(&&LABEL_TYPEID_U16),       static_cast<const void *>(&&LABEL_TYPEID_I32),      \
47             static_cast<const void *>(&&LABEL_TYPEID_U32),       static_cast<const void *>(&&LABEL_TYPEID_F32),      \
48             static_cast<const void *>(&&LABEL_TYPEID_F64),       static_cast<const void *>(&&LABEL_TYPEID_I64),      \
49             static_cast<const void *>(&&LABEL_TYPEID_U64),       static_cast<const void *>(&&LABEL_TYPEID_INVALID),  \
50             static_cast<const void *>(&&LABEL_TYPEID_REFERENCE), static_cast<const void *>(&&LABEL_TYPEID_INVALID)}; \
51                                                                                                                      \
52         static_assert(dispatch_table.size() - 1 == SHORTY_ELEM_MAX);                                                 \
53         ASSERT(dispatch_table[static_cast<uint8_t>((*panda_file::ShortyIterator()).GetId())] == &&LABEL_TYPEID_END); \
54         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::U1)] == &&LABEL_TYPEID_U8);                               \
55         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::I8)] == &&LABEL_TYPEID_I8);                               \
56         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::U8)] == &&LABEL_TYPEID_U8);                               \
57         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::I16)] == &&LABEL_TYPEID_I16);                             \
58         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::U16)] == &&LABEL_TYPEID_U16);                             \
59         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::I32)] == &&LABEL_TYPEID_I32);                             \
60         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::U32)] == &&LABEL_TYPEID_U32);                             \
61         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::F32)] == &&LABEL_TYPEID_F32);                             \
62         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::F64)] == &&LABEL_TYPEID_F64);                             \
63         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::I64)] == &&LABEL_TYPEID_I64);                             \
64         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::U64)] == &&LABEL_TYPEID_U64);                             \
65         ASSERT(dispatch_table[static_cast<uint8_t>(TypeId::REFERENCE)] == &&LABEL_TYPEID_REFERENCE);                 \
66                                                                                                                      \
67         uint8_t encoding = 0;                                                                                        \
68         panda_file::ShortyIterator it((METHOD)->GetShorty());                                                        \
69         /* Skip the return value */                                                                                  \
70         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
71                                                                                                                      \
72     LABEL_TYPEID_VOID : {                                                                                            \
73         LOG(FATAL, RUNTIME) << "Void argument is impossible";                                                        \
74         UNREACHABLE();                                                                                               \
75     }                                                                                                                \
76     LABEL_TYPEID_I8 : {                                                                                              \
77         auto v = (ARG_READER).template Read<int8_t>();                                                               \
78         (ARG_WRITER).template Write<int8_t>(v);                                                                      \
79         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
80     }                                                                                                                \
81     LABEL_TYPEID_U8 : {                                                                                              \
82         auto v = (ARG_READER).template Read<uint8_t>();                                                              \
83         (ARG_WRITER).template Write<uint8_t>(v);                                                                     \
84         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
85     }                                                                                                                \
86     LABEL_TYPEID_I16 : {                                                                                             \
87         auto v = (ARG_READER).template Read<int16_t>();                                                              \
88         (ARG_WRITER).template Write<int16_t>(v);                                                                     \
89         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
90     }                                                                                                                \
91     LABEL_TYPEID_U16 : {                                                                                             \
92         auto v = (ARG_READER).template Read<uint16_t>();                                                             \
93         (ARG_WRITER).template Write<uint16_t>(v);                                                                    \
94         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
95     }                                                                                                                \
96     LABEL_TYPEID_I32 : {                                                                                             \
97         auto v = (ARG_READER).template Read<int32_t>();                                                              \
98         (ARG_WRITER).template Write<int32_t>(v);                                                                     \
99         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
100     }                                                                                                                \
101     LABEL_TYPEID_U32 : {                                                                                             \
102         auto v = (ARG_READER).template Read<uint32_t>();                                                             \
103         (ARG_WRITER).template Write<uint32_t>(v);                                                                    \
104         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
105     }                                                                                                                \
106     LABEL_TYPEID_F32 : {                                                                                             \
107         auto v = (ARG_READER).template Read<float>();                                                                \
108         (ARG_WRITER).template Write<float>(v);                                                                       \
109         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
110     }                                                                                                                \
111     LABEL_TYPEID_F64 : {                                                                                             \
112         auto v = (ARG_READER).template Read<double>();                                                               \
113         (ARG_WRITER).template Write<double>(v);                                                                      \
114         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
115     }                                                                                                                \
116     LABEL_TYPEID_I64 : {                                                                                             \
117         auto v = (ARG_READER).template Read<int64_t>();                                                              \
118         (ARG_WRITER).template Write<int64_t>(v);                                                                     \
119         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
120     }                                                                                                                \
121     LABEL_TYPEID_U64 : {                                                                                             \
122         auto v = (ARG_READER).template Read<uint64_t>();                                                             \
123         (ARG_WRITER).template Write<uint64_t>(v);                                                                    \
124         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
125     }                                                                                                                \
126     LABEL_TYPEID_REFERENCE : {                                                                                       \
127         auto v = const_cast<ObjectHeader **>((ARG_READER).template ReadPtr<ObjectHeader *>());                       \
128         (ARG_WRITER).template Write<ObjectHeader **>(v);                                                             \
129         ARCH_COPY_METHOD_ARGS_DISPATCH                                                                               \
130     }                                                                                                                \
131     LABEL_TYPEID_INVALID : {                                                                                         \
132         LOG(FATAL, RUNTIME) << "Invalid method's shorty, unreachable type ID";                                       \
133         UNREACHABLE();                                                                                               \
134     }                                                                                                                \
135     LABEL_TYPEID_END:;                                                                                               \
136     } while (false)
137 
138 template <Arch A>
139 struct ExtArchTraits;
140 
141 #if !defined(PANDA_TARGET_ARM32_ABI_HARD)
142 template <>
143 struct ExtArchTraits<Arch::AARCH32> {
144     using SignedWordType = int32_t;
145     using UnsignedWordType = uint32_t;
146 
147     static constexpr size_t NUM_GP_ARG_REGS = 4;
148     static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits<Arch::AARCH32>::POINTER_SIZE;
149     static constexpr size_t NUM_FP_ARG_REGS = 0;
150     static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits<Arch::AARCH32>::POINTER_SIZE;
151     static constexpr size_t GPR_SIZE = ArchTraits<Arch::AARCH32>::POINTER_SIZE;
152     static constexpr size_t FPR_SIZE = 0;
153     static constexpr bool HARDFP = false;
154 };
155 #else   // !defined(PANDA_TARGET_ARM32_ABI_HARD)
156 template <>
157 struct ExtArchTraits<Arch::AARCH32> {
158     using SignedWordType = int32_t;
159     using UnsignedWordType = uint32_t;
160 
161     static constexpr size_t NUM_GP_ARG_REGS = 4;
162     static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits<Arch::AARCH32>::POINTER_SIZE;
163     static constexpr size_t NUM_FP_ARG_REGS = 16; /* s0 - s15 */
164     static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits<Arch::AARCH32>::POINTER_SIZE;
165     static constexpr size_t GPR_SIZE = ArchTraits<Arch::AARCH32>::POINTER_SIZE;
166     static constexpr size_t FPR_SIZE = ArchTraits<Arch::AARCH32>::POINTER_SIZE;
167     static constexpr bool HARDFP = true;
168 };
169 #endif  // !defined(PANDA_TARGET_ARM32_ABI_HARD)
170 
171 template <>
172 struct ExtArchTraits<Arch::AARCH64> {
173     using SignedWordType = int64_t;
174     using UnsignedWordType = uint64_t;
175 
176     static constexpr size_t NUM_GP_ARG_REGS = 8;
177     static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits<Arch::AARCH64>::POINTER_SIZE;
178     static constexpr size_t NUM_FP_ARG_REGS = 8;
179     static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits<Arch::AARCH64>::POINTER_SIZE;
180     static constexpr size_t GPR_SIZE = ArchTraits<Arch::AARCH64>::POINTER_SIZE;
181     static constexpr size_t FPR_SIZE = ArchTraits<Arch::AARCH64>::POINTER_SIZE;
182     static constexpr bool HARDFP = true;
183 };
184 
185 template <>
186 struct ExtArchTraits<Arch::X86_64> {
187     using SignedWordType = int64_t;
188     using UnsignedWordType = uint64_t;
189 
190     static constexpr size_t NUM_GP_ARG_REGS = 6;
191     static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits<Arch::X86_64>::POINTER_SIZE;
192     static constexpr size_t NUM_FP_ARG_REGS = 8;
193     static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits<Arch::X86_64>::POINTER_SIZE;
194     static constexpr size_t GPR_SIZE = ArchTraits<Arch::X86_64>::POINTER_SIZE;
195     static constexpr size_t FPR_SIZE = ArchTraits<Arch::X86_64>::POINTER_SIZE;
196     static constexpr bool HARDFP = true;
197 };
198 
199 template <class T>
AlignPtr(uint8_t *ptr)200 inline uint8_t *AlignPtr(uint8_t *ptr)
201 {
202     return reinterpret_cast<uint8_t *>(RoundUp(reinterpret_cast<uintptr_t>(ptr), sizeof(T)));
203 }
204 
205 template <class T>
AlignPtr(const uint8_t *ptr)206 inline const uint8_t *AlignPtr(const uint8_t *ptr)
207 {
208     return reinterpret_cast<const uint8_t *>(RoundUp(reinterpret_cast<uintptr_t>(ptr), sizeof(T)));
209 }
210 
211 template <typename T>
212 typename std::enable_if<sizeof(T) < sizeof(uint32_t), uint8_t *>::type WriteToMem(T v, uint8_t *mem)
213 {
214     /*
215      * When the type is less than 4 bytes
216      * We write 4 bytes to stack with 0 in high bytes
217      * To avoid of unspecified behavior
218      */
219     static_assert(!std::is_floating_point<T>::value);
220     ASSERT(reinterpret_cast<std::uintptr_t>(mem) % sizeof(std::uintptr_t) == 0);
221 
222     *reinterpret_cast<uint32_t *>(mem) = 0;
223     mem = AlignPtr<T>(mem);
224     *reinterpret_cast<T *>(mem) = v;
225 
226     return mem;
227 }
228 
229 template <typename T>
WriteToMem(T v, uint8_t *mem)230 typename std::enable_if<(sizeof(T) >= sizeof(uint32_t)), uint8_t *>::type WriteToMem(T v, uint8_t *mem)
231 {
232     ASSERT(reinterpret_cast<std::uintptr_t>(mem) % sizeof(std::uintptr_t) == 0);
233 
234     mem = AlignPtr<T>(mem);
235     *reinterpret_cast<T *>(mem) = v;
236     return mem;
237 }
238 
239 template <Arch A>
240 class ArgCounter {
241 public:
242     template <class T>
Count()243     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP, void> Count()
244     {
245         constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits<A>::FPR_SIZE);
246         fprArgSize_ = RoundUp(fprArgSize_, NUM_BYTES);
247         if (fprArgSize_ < ExtArchTraits<A>::FP_ARG_NUM_BYTES) {
248             fprArgSize_ += NUM_BYTES;
249         } else {
250             stackSize_ = RoundUp(stackSize_, NUM_BYTES);
251             stackSize_ += NUM_BYTES;
252         }
253     }
254 
255     template <class T>
Count()256     ALWAYS_INLINE typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP), void> Count()
257     {
258         constexpr size_t NUM_BYTES = std::max(sizeof(T), PTR_SIZE);
259         gprArgSize_ = RoundUp(gprArgSize_, NUM_BYTES);
260         if (gprArgSize_ < ExtArchTraits<A>::GP_ARG_NUM_BYTES) {
261             gprArgSize_ += NUM_BYTES;
262         } else {
263             stackSize_ = RoundUp(stackSize_, NUM_BYTES);
264             stackSize_ += NUM_BYTES;
265         }
266     }
267 
GetOnlyStackSize() const268     size_t GetOnlyStackSize() const
269     {
270         return stackSize_;
271     }
272 
GetStackSize() const273     size_t GetStackSize() const
274     {
275         return GetStackSpaceSize() / ArchTraits<A>::POINTER_SIZE;
276     }
277 
GetStackSpaceSize() const278     size_t GetStackSpaceSize() const
279     {
280         return RoundUp(ExtArchTraits<A>::FP_ARG_NUM_BYTES + ExtArchTraits<A>::GP_ARG_NUM_BYTES + stackSize_,
281                        2U * ArchTraits<A>::POINTER_SIZE);
282     }
283 
284 private:
285     static constexpr size_t PTR_SIZE = ArchTraits<A>::POINTER_SIZE;
286     size_t gprArgSize_ = 0;
287     size_t fprArgSize_ = 0;
288     size_t stackSize_ = 0;
289 };
290 
291 template <Arch A>
292 class ArgReader {
293 public:
ArgReader(Span<uint8_t> gprArgs, Span<uint8_t> fprArgs, const uint8_t *stackArgs)294     ArgReader(Span<uint8_t> gprArgs, Span<uint8_t> fprArgs, const uint8_t *stackArgs)
295         : gprArgs_(gprArgs), fprArgs_(fprArgs), stackArgs_(stackArgs)
296     {
297     }
298 
299     template <class T>
Read()300     ALWAYS_INLINE T Read()
301     {
302         return *ReadPtr<T>();
303     }
304 
305     template <class T>
306     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP, const T *>
ReadPtr()307     ReadPtr()
308     {
309         constexpr size_t READ_BYTES = std::max(sizeof(T), ExtArchTraits<A>::FPR_SIZE);
310         fpArgBytesRead_ = RoundUp(fpArgBytesRead_, READ_BYTES);
311         if (fpArgBytesRead_ < ExtArchTraits<A>::FP_ARG_NUM_BYTES) {
312             const T *v = reinterpret_cast<const T *>(fprArgs_.data() + fpArgBytesRead_);
313             fpArgBytesRead_ += READ_BYTES;
314             return v;
315         }
316         stackArgs_ = AlignPtr<T>(stackArgs_);
317         const T *v = reinterpret_cast<const T *>(stackArgs_);
318         stackArgs_ += READ_BYTES;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
319         return v;
320     }
321 
322     template <class T>
323     ALWAYS_INLINE typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP), const T *>
ReadPtr()324     ReadPtr()
325     {
326         constexpr size_t READ_BYTES = std::max(sizeof(T), PTR_SIZE);
327         gpArgBytesRead_ = RoundUp(gpArgBytesRead_, READ_BYTES);
328         if (gpArgBytesRead_ < ExtArchTraits<A>::GP_ARG_NUM_BYTES) {
329             const T *v = reinterpret_cast<const T *>(gprArgs_.data() + gpArgBytesRead_);
330             gpArgBytesRead_ += READ_BYTES;
331             return v;
332         }
333         stackArgs_ = AlignPtr<T>(stackArgs_);
334         const T *v = reinterpret_cast<const T *>(stackArgs_);
335         stackArgs_ += READ_BYTES;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
336         return v;
337     }
338 
339 private:
340     static constexpr size_t PTR_SIZE = ArchTraits<A>::POINTER_SIZE;
341     Span<uint8_t> gprArgs_;
342     Span<uint8_t> fprArgs_;
343     const uint8_t *stackArgs_;
344     size_t gpArgBytesRead_ = 0;
345     size_t fpArgBytesRead_ = 0;
346 };
347 
348 template <Arch A, class T>
349 using ExtArchTraitsWorldType = std::conditional_t<std::is_signed_v<T>, typename ExtArchTraits<A>::SignedWordType,
350                                                   typename ExtArchTraits<A>::UnsignedWordType>;
351 
352 template <Arch A>
353 class ArgWriterBase {
354 public:
ArgWriterBase(Span<uint8_t> gprArgs, Span<uint8_t> fprArgs, uint8_t *stackArgs)355     ArgWriterBase(Span<uint8_t> gprArgs, Span<uint8_t> fprArgs, uint8_t *stackArgs)
356         : gprArgs_(gprArgs), fprArgs_(fprArgs), stackArgs_(stackArgs)
357     {
358     }
359     ~ArgWriterBase() = default;
360 
361 protected:
362     template <class T>
363     ALWAYS_INLINE typename std::enable_if_t<std::is_integral_v<T> && sizeof(T) < ArchTraits<A>::POINTER_SIZE, void>
364     RegisterValueWrite(T v)
365     {
366         *reinterpret_cast<ExtArchTraitsWorldType<A, T> *>(gprArgs_.data() + gpArgBytesWritten_) = v;
367     }
368 
369     template <class T>
370     ALWAYS_INLINE typename std::enable_if_t<!(std::is_integral_v<T> && sizeof(T) < ArchTraits<A>::POINTER_SIZE), void>
371     RegisterValueWrite(T v)
372     {
373         *reinterpret_cast<T *>(gprArgs_.data() + gpArgBytesWritten_) = v;
374     }
375 
376     template <class T>
377     void WriteNonFloatingPointValue(T v)
378     {
379         static_assert(!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP));
380 
381         constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE);
382         gpArgBytesWritten_ = RoundUp(gpArgBytesWritten_, WRITE_BYTES);
383         if (gpArgBytesWritten_ < ExtArchTraits<A>::GP_ARG_NUM_BYTES) {
384             ArgWriterBase<A>::RegisterValueWrite(v);
385             gpArgBytesWritten_ += WRITE_BYTES;
386         } else {
387             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
388             stackArgs_ = WriteToMem(v, stackArgs_) + WRITE_BYTES;
389         }
390     }
391 
392     NO_COPY_SEMANTIC(ArgWriterBase);
393     NO_MOVE_SEMANTIC(ArgWriterBase);
394 
395     static constexpr size_t PTR_SIZE =
396         ArchTraits<A>::POINTER_SIZE;  // NOLINT(misc-non-private-member-variables-in-classes)
397     Span<uint8_t> gprArgs_;           // NOLINT(misc-non-private-member-variables-in-classes)
398     Span<uint8_t> fprArgs_;           // NOLINT(misc-non-private-member-variables-in-classes)
399     uint8_t *stackArgs_;              // NOLINT(misc-non-private-member-variables-in-classes)
400     size_t gpArgBytesWritten_ = 0;    // NOLINT(misc-non-private-member-variables-in-classes)
401     size_t fpArgBytesWritten_ = 0;    // NOLINT(misc-non-private-member-variables-in-classes)
402 };
403 
404 template <Arch A>
405 class ArgWriter : private ArgWriterBase<A> {
406 public:
407     using ArgWriterBase<A>::gprArgs_;
408     using ArgWriterBase<A>::fprArgs_;
409     using ArgWriterBase<A>::stackArgs_;
410     using ArgWriterBase<A>::gpArgBytesWritten_;
411     using ArgWriterBase<A>::fpArgBytesWritten_;
412     using ArgWriterBase<A>::PTR_SIZE;
413 
414     // NOLINTNEXTLINE(readability-non-const-parameter)
415     ArgWriter(Span<uint8_t> gprArgs, Span<uint8_t> fprArgs, uint8_t *stackArgs)
416         : ArgWriterBase<A>(gprArgs, fprArgs, stackArgs)
417     {
418     }
419     ~ArgWriter() = default;
420 
421     template <class T>
422     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP, void> Write(T v)
423     {
424         constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE);
425 
426         constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits<A>::FPR_SIZE);
427         if (fpArgBytesWritten_ < ExtArchTraits<A>::FP_ARG_NUM_BYTES) {
428             *reinterpret_cast<T *>(fprArgs_.data() + fpArgBytesWritten_) = v;
429             fpArgBytesWritten_ += NUM_BYTES;
430         } else {
431             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
432             stackArgs_ = WriteToMem(v, stackArgs_) + WRITE_BYTES;
433         }
434     }
435 
436     template <class T>
437     ALWAYS_INLINE typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP), void> Write(T v)
438     {
439         ArgWriterBase<A>::WriteNonFloatingPointValue(v);
440     }
441 
442     NO_COPY_SEMANTIC(ArgWriter);
443     NO_MOVE_SEMANTIC(ArgWriter);
444 };
445 
446 // This class is required due to specific calling conventions in AARCH32
447 template <>
448 class ArgWriter<Arch::AARCH32> : private ArgWriterBase<Arch::AARCH32> {
449 public:
450     using ArgWriterBase<Arch::AARCH32>::gprArgs_;
451     using ArgWriterBase<Arch::AARCH32>::fprArgs_;
452     using ArgWriterBase<Arch::AARCH32>::stackArgs_;
453     using ArgWriterBase<Arch::AARCH32>::gpArgBytesWritten_;
454     using ArgWriterBase<Arch::AARCH32>::fpArgBytesWritten_;
455     using ArgWriterBase<Arch::AARCH32>::PTR_SIZE;
456 
457     // NOLINTNEXTLINE(readability-non-const-parameter)
458     ArgWriter(Span<uint8_t> gprArgs, Span<uint8_t> fprArgs, uint8_t *stackArgs)
459         : ArgWriterBase<Arch::AARCH32>(gprArgs, fprArgs, stackArgs)
460     {
461     }
462     ~ArgWriter() = default;
463 
464     template <class T>
465     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<Arch::AARCH32>::HARDFP, void>
466     Write(T v)
467     {
468         constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE);
469 
470         if (fpArgBytesWritten_ < ExtArchTraits<Arch::AARCH32>::FP_ARG_NUM_BYTES &&
471             (std::is_same_v<T, float> ||
472              (fpArgBytesWritten_ < ExtArchTraits<Arch::AARCH32>::FP_ARG_NUM_BYTES - sizeof(float))) &&
473             !isFloatArmStackHasBeenWritten_) {
474             RegisterFloatingPointValueWriteArm32(v);
475             return;
476         }
477 
478         isFloatArmStackHasBeenWritten_ = true;
479         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
480         stackArgs_ = WriteToMem(v, stackArgs_) + WRITE_BYTES;
481     }
482 
483     template <class T>
484     ALWAYS_INLINE
485         typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<Arch::AARCH32>::HARDFP), void>
486         Write(T v)
487     {
488         ArgWriterBase<Arch::AARCH32>::WriteNonFloatingPointValue(v);
489     }
490 
491     NO_COPY_SEMANTIC(ArgWriter);
492     NO_MOVE_SEMANTIC(ArgWriter);
493 
494 private:
495     template <class T>
496     ALWAYS_INLINE typename std::enable_if_t<(std::is_same_v<T, float>), void> RegisterFloatingPointValueWriteArm32(T v)
497     {
498         constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits<Arch::AARCH32>::FPR_SIZE);
499         if (halfEmptyRegisterOffset_ == 0) {
500             halfEmptyRegisterOffset_ = fpArgBytesWritten_ + sizeof(float);
501             *reinterpret_cast<T *>(fprArgs_.data() + fpArgBytesWritten_) = v;
502             fpArgBytesWritten_ += NUM_BYTES;
503         } else {
504             *reinterpret_cast<T *>(fprArgs_.data() + halfEmptyRegisterOffset_) = v;
505             if (halfEmptyRegisterOffset_ == fpArgBytesWritten_) {
506                 fpArgBytesWritten_ += NUM_BYTES;
507             }
508             halfEmptyRegisterOffset_ = 0;
509         }
510     }
511 
512     template <class T>
513     ALWAYS_INLINE typename std::enable_if_t<!(std::is_same_v<T, float>), void> RegisterFloatingPointValueWriteArm32(T v)
514     {
515         constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits<Arch::AARCH32>::FPR_SIZE);
516         fpArgBytesWritten_ = RoundUp(fpArgBytesWritten_, sizeof(T));
517         *reinterpret_cast<T *>(fprArgs_.data() + fpArgBytesWritten_) = v;
518         fpArgBytesWritten_ += NUM_BYTES;
519     }
520 
521     size_t halfEmptyRegisterOffset_ = 0;
522     bool isFloatArmStackHasBeenWritten_ = false;
523 };
524 
525 class ValueWriter {
526 public:
527     explicit ValueWriter(PandaVector<Value> *values) : values_(values) {}
528     ~ValueWriter() = default;
529 
530     template <class T>
531     ALWAYS_INLINE typename std::enable_if_t<std::is_same<T, ObjectHeader **>::value, void> Write(T v)
532     {
533         values_->push_back(Value(*v));
534     }
535 
536     template <class T>
537     ALWAYS_INLINE typename std::enable_if_t<!std::is_same<T, ObjectHeader **>::value, void> Write(T v)
538     {
539         values_->push_back(Value(v));
540     }
541 
542     NO_COPY_SEMANTIC(ValueWriter);
543     NO_MOVE_SEMANTIC(ValueWriter);
544 
545 private:
546     PandaVector<Value> *values_;
547 };
548 
549 template <Arch A>
550 class ArgReaderStack {
551 public:
552     explicit ArgReaderStack(const uint8_t *stackArgs) : stackArgs_(stackArgs) {}
553 
554     template <class T>
555     ALWAYS_INLINE T Read()
556     {
557         return *ReadPtr<T>();
558     }
559 
560     template <class T>
561     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP, const T *>
562     ReadPtr()
563     {
564         constexpr size_t READ_BYTES = sizeof(uint64_t);
565         const T *v = reinterpret_cast<const T *>(stackArgs_);
566         stackArgs_ += READ_BYTES;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
567         return v;
568     }
569 
570     template <class T>
571     ALWAYS_INLINE typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP), const T *>
572     ReadPtr()
573     {
574         constexpr size_t READ_BYTES = sizeof(uint64_t);
575         const T *v = reinterpret_cast<const T *>(stackArgs_);
576         stackArgs_ += READ_BYTES;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
577         return v;
578     }
579 
580 private:
581     const uint8_t *stackArgs_;
582 };
583 
584 }  // namespace ark::arch
585 
586 #endif  // PANDA_RUNTIME_ARCH_HELPERS_H_
587