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