• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 
22 namespace panda::arch {
23 
24 template <Arch A>
25 struct ExtArchTraits;
26 
27 #if !defined(PANDA_TARGET_ARM32_ABI_HARD)
28 template <>
29 struct ExtArchTraits<Arch::AARCH32> {
30     using signed_word_type = int32_t;
31     using unsigned_word_type = uint32_t;
32 
33     static constexpr size_t NUM_GP_ARG_REGS = 4;
34     static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits<Arch::AARCH32>::POINTER_SIZE;
35     static constexpr size_t NUM_FP_ARG_REGS = 0;
36     static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits<Arch::AARCH32>::POINTER_SIZE;
37     static constexpr size_t GPR_SIZE = ArchTraits<Arch::AARCH32>::POINTER_SIZE;
38     static constexpr size_t FPR_SIZE = 0;
39     static constexpr bool HARDFP = false;
40 };
41 #else   // !defined(PANDA_TARGET_ARM32_ABI_HARD)
42 template <>
43 struct ExtArchTraits<Arch::AARCH32> {
44     using signed_word_type = int32_t;
45     using unsigned_word_type = uint32_t;
46 
47     static constexpr size_t NUM_GP_ARG_REGS = 4;
48     static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits<Arch::AARCH32>::POINTER_SIZE;
49     static constexpr size_t NUM_FP_ARG_REGS = 16; /* s0 - s15 */
50     static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits<Arch::AARCH32>::POINTER_SIZE;
51     static constexpr size_t GPR_SIZE = ArchTraits<Arch::AARCH32>::POINTER_SIZE;
52     static constexpr size_t FPR_SIZE = ArchTraits<Arch::AARCH32>::POINTER_SIZE;
53     static constexpr bool HARDFP = true;
54 };
55 #endif  // !defined(PANDA_TARGET_ARM32_ABI_HARD)
56 
57 template <>
58 struct ExtArchTraits<Arch::AARCH64> {
59     using signed_word_type = int64_t;
60     using unsigned_word_type = uint64_t;
61 
62     static constexpr size_t NUM_GP_ARG_REGS = 8;
63     static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits<Arch::AARCH64>::POINTER_SIZE;
64     static constexpr size_t NUM_FP_ARG_REGS = 8;
65     static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits<Arch::AARCH64>::POINTER_SIZE;
66     static constexpr size_t GPR_SIZE = ArchTraits<Arch::AARCH64>::POINTER_SIZE;
67     static constexpr size_t FPR_SIZE = ArchTraits<Arch::AARCH64>::POINTER_SIZE;
68     static constexpr bool HARDFP = true;
69 };
70 
71 template <>
72 struct ExtArchTraits<Arch::X86_64> {
73     using signed_word_type = int64_t;
74     using unsigned_word_type = uint64_t;
75 
76     static constexpr size_t NUM_GP_ARG_REGS = 6;
77     static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits<Arch::X86_64>::POINTER_SIZE;
78     static constexpr size_t NUM_FP_ARG_REGS = 8;
79     static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits<Arch::X86_64>::POINTER_SIZE;
80     static constexpr size_t GPR_SIZE = ArchTraits<Arch::X86_64>::POINTER_SIZE;
81     static constexpr size_t FPR_SIZE = ArchTraits<Arch::X86_64>::POINTER_SIZE;
82     static constexpr bool HARDFP = true;
83 };
84 
85 template <class T>
86 inline uint8_t *AlignPtr(uint8_t *ptr)
87 {
88     return reinterpret_cast<uint8_t *>(RoundUp(reinterpret_cast<uintptr_t>(ptr), sizeof(T)));
89 }
90 
91 template <class T>
92 inline const uint8_t *AlignPtr(const uint8_t *ptr)
93 {
94     return reinterpret_cast<const uint8_t *>(RoundUp(reinterpret_cast<uintptr_t>(ptr), sizeof(T)));
95 }
96 
97 template <typename T>
98 typename std::enable_if<sizeof(T) < sizeof(uint32_t), uint8_t *>::type WriteToMem(T v, uint8_t *mem)
99 {
100     /*
101      * When the type is less than 4 bytes
102      * We write 4 bytes to stack with 0 in high bytes
103      * To avoid of unspecified behavior
104      */
105     static_assert(!std::is_floating_point<T>::value);
106     ASSERT(reinterpret_cast<std::uintptr_t>(mem) % sizeof(std::uintptr_t) == 0);
107 
108     *reinterpret_cast<uint32_t *>(mem) = 0;
109     mem = AlignPtr<T>(mem);
110     *reinterpret_cast<T *>(mem) = v;
111 
112     return mem;
113 }
114 
115 template <typename T>
116 typename std::enable_if<(sizeof(T) >= sizeof(uint32_t)), uint8_t *>::type WriteToMem(T v, uint8_t *mem)
117 {
118     ASSERT(reinterpret_cast<std::uintptr_t>(mem) % sizeof(std::uintptr_t) == 0);
119 
120     mem = AlignPtr<T>(mem);
121     *reinterpret_cast<T *>(mem) = v;
122     return mem;
123 }
124 
125 template <Arch A>
126 class ArgCounter {
127 public:
128     template <class T>
129     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP, void> Count()
130     {
131         constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits<A>::FPR_SIZE);
132         fpr_arg_size_ = RoundUp(fpr_arg_size_, NUM_BYTES);
133         if (fpr_arg_size_ < ExtArchTraits<A>::FP_ARG_NUM_BYTES) {
134             fpr_arg_size_ += NUM_BYTES;
135         } else {
136             stack_size_ = RoundUp(stack_size_, NUM_BYTES);
137             stack_size_ += NUM_BYTES;
138         }
139     }
140 
141     template <class T>
142     ALWAYS_INLINE typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP), void> Count()
143     {
144         constexpr size_t NUM_BYTES = std::max(sizeof(T), PTR_SIZE);
145         gpr_arg_size_ = RoundUp(gpr_arg_size_, NUM_BYTES);
146         if (gpr_arg_size_ < ExtArchTraits<A>::GP_ARG_NUM_BYTES) {
147             gpr_arg_size_ += NUM_BYTES;
148         } else {
149             stack_size_ = RoundUp(stack_size_, NUM_BYTES);
150             stack_size_ += NUM_BYTES;
151         }
152     }
153 
154     size_t GetStackSize() const
155     {
156         return GetStackSpaceSize() / ArchTraits<A>::POINTER_SIZE;
157     }
158 
159     size_t GetStackSpaceSize() const
160     {
161         return RoundUp(ExtArchTraits<A>::FP_ARG_NUM_BYTES + ExtArchTraits<A>::GP_ARG_NUM_BYTES + stack_size_,
162                        2 * ArchTraits<A>::POINTER_SIZE);
163     }
164 
165 private:
166     static constexpr size_t PTR_SIZE = ArchTraits<A>::POINTER_SIZE;
167     size_t gpr_arg_size_ = 0;
168     size_t fpr_arg_size_ = 0;
169     size_t stack_size_ = 0;
170 };
171 
172 template <Arch A>
173 class ArgReader {
174 public:
175     ArgReader(const Span<uint8_t> &gpr_args, const Span<uint8_t> &fpr_args, const uint8_t *stack_args)
176         : gpr_args_(gpr_args), fpr_args_(fpr_args), stack_args_(stack_args)
177     {
178     }
179 
180     template <class T>
181     ALWAYS_INLINE T Read()
182     {
183         return *ReadPtr<T>();
184     }
185 
186     template <class T>
187     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP, const T *>
188     ReadPtr()
189     {
190         constexpr size_t READ_BYTES = std::max(sizeof(T), ExtArchTraits<A>::FPR_SIZE);
191         fp_arg_bytes_read_ = RoundUp(fp_arg_bytes_read_, READ_BYTES);
192         if (fp_arg_bytes_read_ < ExtArchTraits<A>::FP_ARG_NUM_BYTES) {
193             const T *v = reinterpret_cast<const T *>(fpr_args_.data() + fp_arg_bytes_read_);
194             fp_arg_bytes_read_ += READ_BYTES;
195             return v;
196         }
197         stack_args_ = AlignPtr<T>(stack_args_);
198         const T *v = reinterpret_cast<const T *>(stack_args_);
199         stack_args_ += READ_BYTES;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
200         return v;
201     }
202 
203     template <class T>
204     ALWAYS_INLINE typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP), const T *>
205     ReadPtr()
206     {
207         constexpr size_t READ_BYTES = std::max(sizeof(T), PTR_SIZE);
208         gp_arg_bytes_read_ = RoundUp(gp_arg_bytes_read_, READ_BYTES);
209         if (gp_arg_bytes_read_ < ExtArchTraits<A>::GP_ARG_NUM_BYTES) {
210             const T *v = reinterpret_cast<const T *>(gpr_args_.data() + gp_arg_bytes_read_);
211             gp_arg_bytes_read_ += READ_BYTES;
212             return v;
213         }
214         stack_args_ = AlignPtr<T>(stack_args_);
215         const T *v = reinterpret_cast<const T *>(stack_args_);
216         stack_args_ += READ_BYTES;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
217         return v;
218     }
219 
220 private:
221     static constexpr size_t PTR_SIZE = ArchTraits<A>::POINTER_SIZE;
222     const Span<uint8_t> &gpr_args_;
223     const Span<uint8_t> &fpr_args_;
224     const uint8_t *stack_args_;
225     size_t gp_arg_bytes_read_ = 0;
226     size_t fp_arg_bytes_read_ = 0;
227 };
228 
229 template <Arch A, class T>
230 using ExtArchTraitsWorldType = std::conditional_t<std::is_signed_v<T>, typename ExtArchTraits<A>::signed_word_type,
231                                                   typename ExtArchTraits<A>::unsigned_word_type>;
232 
233 template <Arch A>
234 class ArgWriterBase {
235 public:
236     ArgWriterBase(Span<uint8_t> *gpr_args, Span<uint8_t> *fpr_args, uint8_t *stack_args)
237         : gpr_args_(gpr_args), fpr_args_(fpr_args), stack_args_(stack_args)
238     {
239     }
240     ~ArgWriterBase() = default;
241 
242 protected:
243     template <class T>
244     ALWAYS_INLINE typename std::enable_if_t<std::is_integral_v<T> && sizeof(T) < ArchTraits<A>::POINTER_SIZE, void>
245     RegisterValueWrite(T v)
246     {
247         *reinterpret_cast<ExtArchTraitsWorldType<A, T> *>(gpr_args_->data() + gp_arg_bytes_written_) = v;
248     }
249 
250     template <class T>
251     ALWAYS_INLINE typename std::enable_if_t<!(std::is_integral_v<T> && sizeof(T) < ArchTraits<A>::POINTER_SIZE), void>
252     RegisterValueWrite(T v)
253     {
254         *reinterpret_cast<T *>(gpr_args_->data() + gp_arg_bytes_written_) = v;
255     }
256 
257     template <class T>
258     void WriteNonFloatingPointValue(T v)
259     {
260         static_assert(!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP));
261 
262         constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE);
263         gp_arg_bytes_written_ = RoundUp(gp_arg_bytes_written_, WRITE_BYTES);
264 
265         if (gp_arg_bytes_written_ < ExtArchTraits<A>::GP_ARG_NUM_BYTES) {
266             ArgWriterBase<A>::RegisterValueWrite(v);
267             gp_arg_bytes_written_ += WRITE_BYTES;
268         } else {
269             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
270             stack_args_ = WriteToMem(v, stack_args_) + WRITE_BYTES;
271         }
272     }
273 
274     NO_COPY_SEMANTIC(ArgWriterBase);
275     NO_MOVE_SEMANTIC(ArgWriterBase);
276 
277     static constexpr size_t PTR_SIZE =
278         ArchTraits<A>::POINTER_SIZE;   // NOLINT(misc-non-private-member-variables-in-classes)
279     Span<uint8_t> *gpr_args_;          // NOLINT(misc-non-private-member-variables-in-classes)
280     Span<uint8_t> *fpr_args_;          // NOLINT(misc-non-private-member-variables-in-classes)
281     uint8_t *stack_args_;              // NOLINT(misc-non-private-member-variables-in-classes)
282     size_t gp_arg_bytes_written_ = 0;  // NOLINT(misc-non-private-member-variables-in-classes)
283     size_t fp_arg_bytes_written_ = 0;  // NOLINT(misc-non-private-member-variables-in-classes)
284 };
285 
286 template <Arch A>
287 class ArgWriter : private ArgWriterBase<A> {
288 public:
289     using ArgWriterBase<A>::gpr_args_;
290     using ArgWriterBase<A>::fpr_args_;
291     using ArgWriterBase<A>::stack_args_;
292     using ArgWriterBase<A>::gp_arg_bytes_written_;
293     using ArgWriterBase<A>::fp_arg_bytes_written_;
294     using ArgWriterBase<A>::PTR_SIZE;
295 
296     // NOLINTNEXTLINE(readability-non-const-parameter)
297     ArgWriter(Span<uint8_t> *gpr_args, Span<uint8_t> *fpr_args, uint8_t *stack_args)
298         : ArgWriterBase<A>(gpr_args, fpr_args, stack_args)
299     {
300     }
301     ~ArgWriter() = default;
302 
303     template <class T>
304     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP, void> Write(T v)
305     {
306         constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE);
307 
308         constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits<A>::FPR_SIZE);
309         if (fp_arg_bytes_written_ < ExtArchTraits<A>::FP_ARG_NUM_BYTES) {
310             *reinterpret_cast<T *>(fpr_args_->data() + fp_arg_bytes_written_) = v;
311             fp_arg_bytes_written_ += NUM_BYTES;
312         } else {
313             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
314             stack_args_ = WriteToMem(v, stack_args_) + WRITE_BYTES;
315         }
316     }
317 
318     template <class T>
319     ALWAYS_INLINE typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<A>::HARDFP), void> Write(T v)
320     {
321         ArgWriterBase<A>::WriteNonFloatingPointValue(v);
322     }
323 
324     NO_COPY_SEMANTIC(ArgWriter);
325     NO_MOVE_SEMANTIC(ArgWriter);
326 };
327 
328 // This class is required due to specific calling conventions in AARCH32
329 template <>
330 class ArgWriter<Arch::AARCH32> : private ArgWriterBase<Arch::AARCH32> {
331 public:
332     using ArgWriterBase<Arch::AARCH32>::gpr_args_;
333     using ArgWriterBase<Arch::AARCH32>::fpr_args_;
334     using ArgWriterBase<Arch::AARCH32>::stack_args_;
335     using ArgWriterBase<Arch::AARCH32>::gp_arg_bytes_written_;
336     using ArgWriterBase<Arch::AARCH32>::fp_arg_bytes_written_;
337     using ArgWriterBase<Arch::AARCH32>::PTR_SIZE;
338 
339     // NOLINTNEXTLINE(readability-non-const-parameter)
340     ArgWriter(Span<uint8_t> *gpr_args, Span<uint8_t> *fpr_args, uint8_t *stack_args)
341         : ArgWriterBase<Arch::AARCH32>(gpr_args, fpr_args, stack_args)
342     {
343     }
344     ~ArgWriter() = default;
345 
346     template <class T>
347     ALWAYS_INLINE typename std::enable_if_t<std::is_floating_point_v<T> && ExtArchTraits<Arch::AARCH32>::HARDFP, void>
348     Write(T v)
349     {
350         constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE);
351 
352         if (fp_arg_bytes_written_ < ExtArchTraits<Arch::AARCH32>::FP_ARG_NUM_BYTES &&
353             (std::is_same_v<T, float> ||
354              (fp_arg_bytes_written_ < ExtArchTraits<Arch::AARCH32>::FP_ARG_NUM_BYTES - sizeof(float))) &&
355             !is_float_arm_stack_has_been_written_) {
356             RegisterFloatingPointValueWriteArm32(v);
357             return;
358         }
359 
360         is_float_arm_stack_has_been_written_ = true;
361         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
362         stack_args_ = WriteToMem(v, stack_args_) + WRITE_BYTES;
363     }
364 
365     template <class T>
366     ALWAYS_INLINE
367         typename std::enable_if_t<!(std::is_floating_point_v<T> && ExtArchTraits<Arch::AARCH32>::HARDFP), void>
368         Write(T v)
369     {
370         ArgWriterBase<Arch::AARCH32>::WriteNonFloatingPointValue(v);
371     }
372 
373     NO_COPY_SEMANTIC(ArgWriter);
374     NO_MOVE_SEMANTIC(ArgWriter);
375 
376 private:
377     template <class T>
378     ALWAYS_INLINE typename std::enable_if_t<(std::is_same_v<T, float>), void> RegisterFloatingPointValueWriteArm32(T v)
379     {
380         constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits<Arch::AARCH32>::FPR_SIZE);
381         if (half_empty_register_offset_ == 0) {
382             half_empty_register_offset_ = fp_arg_bytes_written_ + sizeof(float);
383             *reinterpret_cast<T *>(fpr_args_->data() + fp_arg_bytes_written_) = v;
384             fp_arg_bytes_written_ += NUM_BYTES;
385         } else {
386             *reinterpret_cast<T *>(fpr_args_->data() + half_empty_register_offset_) = v;
387             if (half_empty_register_offset_ == fp_arg_bytes_written_) {
388                 fp_arg_bytes_written_ += NUM_BYTES;
389             }
390             half_empty_register_offset_ = 0;
391         }
392     }
393 
394     template <class T>
395     ALWAYS_INLINE typename std::enable_if_t<!(std::is_same_v<T, float>), void> RegisterFloatingPointValueWriteArm32(T v)
396     {
397         constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits<Arch::AARCH32>::FPR_SIZE);
398         fp_arg_bytes_written_ = RoundUp(fp_arg_bytes_written_, sizeof(T));
399         *reinterpret_cast<T *>(fpr_args_->data() + fp_arg_bytes_written_) = v;
400         fp_arg_bytes_written_ += NUM_BYTES;
401     }
402 
403     size_t half_empty_register_offset_ = 0;
404     bool is_float_arm_stack_has_been_written_ = false;
405 };
406 
407 }  // namespace panda::arch
408 
409 #endif  // PANDA_RUNTIME_ARCH_HELPERS_H_
410