1 /* 2 * Copyright (c) 2021 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 16 #ifndef PANDA_RUNTIME_INCLUDE_CFRAME_ITERATORS_H_ 17 #define PANDA_RUNTIME_INCLUDE_CFRAME_ITERATORS_H_ 18 19 #include "libpandabase/utils/arch.h" 20 #include "libpandafile/shorty_iterator.h" 21 #include "runtime/arch/helpers.h" 22 #include "runtime/include/cframe.h" 23 #include "runtime/include/method.h" 24 #include "libpandafile/shorty_iterator.h" 25 #include "utils/bit_utils.h" 26 27 namespace panda { 28 29 template <typename It> 30 class Range { 31 public: Range(It begin,It end)32 Range(It begin, It end) : begin_(begin), end_(end) {} 33 begin()34 It begin() // NOLINT(readability-identifier-naming) 35 { 36 return begin_; 37 } 38 end()39 It end() // NOLINT(readability-identifier-naming) 40 { 41 return end_; 42 } 43 44 private: 45 It begin_; 46 It end_; 47 }; 48 49 template <Arch arch = RUNTIME_ARCH> 50 class CFrameJniMethodIterator { 51 using SlotType = typename CFrame::SlotType; 52 53 static constexpr size_t ARG_FP_REGS_COUNG = arch::ExtArchTraits<arch>::NUM_FP_ARG_REGS; 54 static constexpr size_t ARG_GP_REGS_COUNG = arch::ExtArchTraits<arch>::NUM_GP_ARG_REGS; 55 56 public: MakeRange(CFrame * cframe)57 static auto MakeRange(CFrame *cframe) 58 { 59 CFrameLayout cframe_layout(arch, 0); 60 61 const ptrdiff_t IN_REGS_START_SLOT = 62 cframe_layout.GetCallerRegsStartSlot() - 63 cframe_layout.GetStackStartSlot() 64 // skipped the slot to align the stack, CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK) 65 + (arch == Arch::X86_64 ? 1 : 0); 66 const ptrdiff_t IN_STACK_START_SLOT = cframe_layout.GetStackArgsStartSlot() - cframe_layout.GetStackStartSlot(); 67 68 ptrdiff_t fp_end_slot = IN_REGS_START_SLOT - 1; 69 ptrdiff_t fp_begin_slot = fp_end_slot + ARG_FP_REGS_COUNG; 70 ptrdiff_t gpr_end_slot = fp_begin_slot; 71 ptrdiff_t gpr_begin_slot = gpr_end_slot + ARG_GP_REGS_COUNG; 72 ptrdiff_t stack_begin_slot = IN_STACK_START_SLOT + 1; 73 74 Method *method = cframe->GetMethod(); 75 bool is_static = method->IsStatic(); 76 if (!is_static) { 77 --gpr_begin_slot; // skip Method* 78 } 79 80 uint32_t num_args = method->GetNumArgs(); 81 uint32_t vreg_num = num_args + (is_static ? 1 : 0); 82 83 return Range<CFrameJniMethodIterator>( 84 CFrameJniMethodIterator(0, vreg_num, method->GetShorty(), gpr_begin_slot, gpr_end_slot, fp_begin_slot + 1, 85 fp_end_slot, stack_begin_slot), 86 CFrameJniMethodIterator(vreg_num, vreg_num, method->GetShorty(), 0, 0, 0, 0, 0)); 87 } 88 89 VRegInfo operator*() 90 { 91 return VRegInfo(current_slot_, VRegInfo::Location::SLOT, vreg_type_, false, vreg_index_); 92 } 93 94 auto operator++() 95 { 96 if (++vreg_index_ >= vreg_num_) { 97 return *this; 98 } 99 100 // Update vreg_type_ 101 vreg_type_ = ConvertType(*shorty_it_); 102 ++shorty_it_; 103 104 // Update current_slot_ 105 if (vreg_type_ == VRegInfo::Type::FLOAT32 || vreg_type_ == VRegInfo::Type::FLOAT64) { 106 if ((fp_current_slot_ - 1) > fp_end_slot_) { 107 --fp_current_slot_; 108 current_slot_ = fp_current_slot_; 109 } else { 110 --stack_current_slot_; 111 current_slot_ = stack_current_slot_; 112 } 113 } else { 114 if ((gpr_current_slot_ - 1) > gpr_end_slot_) { 115 --gpr_current_slot_; 116 current_slot_ = gpr_current_slot_; 117 } else { 118 --stack_current_slot_; 119 current_slot_ = stack_current_slot_; 120 } 121 } 122 123 return *this; 124 } 125 126 auto operator++(int) // NOLINT(cert-dcl21-cpp) 127 { 128 auto res = *this; 129 this->operator++(); 130 return res; 131 } 132 133 bool operator==(const CFrameJniMethodIterator &it) const 134 { 135 return vreg_index_ == it.vreg_index_; 136 } 137 138 bool operator!=(const CFrameJniMethodIterator &it) const 139 { 140 return !(*this == it); 141 } 142 143 private: CFrameJniMethodIterator(uint32_t vreg_index,uint32_t vreg_num,const uint16_t * shorty,ptrdiff_t gpr_begin_slot,ptrdiff_t gpr_end_slot,ptrdiff_t fp_begin_slot,ptrdiff_t fp_end_slot,ptrdiff_t stack_current_slot)144 CFrameJniMethodIterator(uint32_t vreg_index, uint32_t vreg_num, const uint16_t *shorty, ptrdiff_t gpr_begin_slot, 145 ptrdiff_t gpr_end_slot, ptrdiff_t fp_begin_slot, ptrdiff_t fp_end_slot, 146 ptrdiff_t stack_current_slot) 147 : vreg_index_(vreg_index), 148 vreg_num_(vreg_num), 149 shorty_it_(shorty), 150 current_slot_(gpr_begin_slot), 151 gpr_current_slot_(gpr_begin_slot), 152 gpr_end_slot_(gpr_end_slot), 153 fp_current_slot_(fp_begin_slot), 154 fp_end_slot_(fp_end_slot), 155 stack_current_slot_(stack_current_slot) 156 { 157 ++shorty_it_; // skip return value 158 } 159 ConvertType(panda_file::Type type)160 VRegInfo::Type ConvertType(panda_file::Type type) const 161 { 162 switch (type.GetId()) { 163 case panda_file::Type::TypeId::U1: 164 return VRegInfo::Type::BOOL; 165 case panda_file::Type::TypeId::I8: 166 case panda_file::Type::TypeId::U8: 167 case panda_file::Type::TypeId::I16: 168 case panda_file::Type::TypeId::U16: 169 case panda_file::Type::TypeId::I32: 170 case panda_file::Type::TypeId::U32: 171 return VRegInfo::Type::INT32; 172 case panda_file::Type::TypeId::F32: 173 return VRegInfo::Type::FLOAT32; 174 case panda_file::Type::TypeId::F64: 175 return VRegInfo::Type::FLOAT64; 176 case panda_file::Type::TypeId::I64: 177 case panda_file::Type::TypeId::U64: 178 return VRegInfo::Type::INT64; 179 case panda_file::Type::TypeId::REFERENCE: 180 return VRegInfo::Type::OBJECT; 181 case panda_file::Type::TypeId::TAGGED: 182 return VRegInfo::Type::INT64; 183 default: 184 UNREACHABLE(); 185 } 186 return VRegInfo::Type::INT32; 187 } 188 189 private: 190 uint32_t vreg_index_; 191 uint32_t vreg_num_; 192 panda_file::ShortyIterator shorty_it_; 193 ptrdiff_t current_slot_; 194 ptrdiff_t gpr_current_slot_; 195 ptrdiff_t gpr_end_slot_; 196 ptrdiff_t fp_current_slot_; 197 ptrdiff_t fp_end_slot_; 198 ptrdiff_t stack_current_slot_; 199 VRegInfo::Type vreg_type_ = VRegInfo::Type::OBJECT; 200 }; 201 202 template <> 203 class CFrameJniMethodIterator<Arch::AARCH32> { 204 using SlotType = typename CFrame::SlotType; 205 206 static constexpr size_t ARG_FP_REGS_COUNG = arch::ExtArchTraits<Arch::AARCH32>::NUM_FP_ARG_REGS; 207 static constexpr size_t ARG_GP_REGS_COUNG = arch::ExtArchTraits<Arch::AARCH32>::NUM_GP_ARG_REGS; 208 209 static constexpr ptrdiff_t IN_REGS_START_SLOT = 24; 210 static constexpr ptrdiff_t IN_STACK_START_SLOT = -11; 211 static constexpr ptrdiff_t FP_END_SLOT = IN_REGS_START_SLOT - 1; 212 static constexpr ptrdiff_t FP_BEGIN_SLOT = FP_END_SLOT + ARG_FP_REGS_COUNG; 213 static constexpr ptrdiff_t GPR_END_SLOT = FP_BEGIN_SLOT; 214 static constexpr ptrdiff_t GPR_BEGIN_SLOT = GPR_END_SLOT + ARG_GP_REGS_COUNG; 215 static constexpr ptrdiff_t STACK_BEGIN_SLOT = IN_STACK_START_SLOT + 1; 216 217 public: MakeRange(CFrame * cframe)218 static auto MakeRange(CFrame *cframe) 219 { 220 ptrdiff_t gpr_begin_slot = GPR_BEGIN_SLOT; 221 Method *method = cframe->GetMethod(); 222 bool is_static = method->IsStatic(); 223 if (!is_static) { 224 --gpr_begin_slot; // skip Method* 225 } 226 227 uint32_t num_args = method->GetNumArgs(); 228 uint32_t vreg_num = num_args + (is_static ? 1 : 0); 229 230 return Range<CFrameJniMethodIterator>( 231 CFrameJniMethodIterator(0, vreg_num, method->GetShorty(), gpr_begin_slot, GPR_END_SLOT, FP_BEGIN_SLOT, 232 FP_END_SLOT, STACK_BEGIN_SLOT), 233 CFrameJniMethodIterator(vreg_num, vreg_num, method->GetShorty(), 0, 0, 0, 0, 0)); 234 } 235 236 VRegInfo operator*() 237 { 238 return VRegInfo(current_slot_, VRegInfo::Location::SLOT, vreg_type_, false, vreg_index_); 239 } 240 GetSlotsCountForType(VRegInfo::Type vreg_type)241 uint32_t GetSlotsCountForType(VRegInfo::Type vreg_type) 242 { 243 static_assert(arch::ExtArchTraits<Arch::AARCH32>::GPR_SIZE == 4); // 4 bytes -- register size on AARCH32 244 245 if (vreg_type == VRegInfo::Type::INT64 || vreg_type == VRegInfo::Type::FLOAT64) { 246 return 2; // 2 slots 247 } 248 return 1; 249 } 250 251 auto operator++() 252 { 253 if (++vreg_index_ >= vreg_num_) { 254 return *this; 255 } 256 257 // Update type 258 vreg_type_ = ConvertType(*shorty_it_); 259 ++shorty_it_; 260 261 // Update slots 262 ptrdiff_t inc = GetSlotsCountForType(vreg_type_); 263 ASSERT(inc == 1 || inc == 2); // 1 or 2 slots 264 if (inc == 1) { 265 if constexpr (arch::ExtArchTraits<Arch::AARCH32>::HARDFP) { 266 if (vreg_type_ == VRegInfo::Type::FLOAT32) { // in this case one takes 1 slots 267 return HandleHardFloat(); 268 } 269 } 270 if ((gpr_current_slot_ - 1) > gpr_end_slot_) { 271 --gpr_current_slot_; 272 current_slot_ = gpr_current_slot_; 273 } else { 274 gpr_current_slot_ = gpr_end_slot_; 275 276 --stack_current_slot_; 277 current_slot_ = stack_current_slot_; 278 } 279 } else { 280 if constexpr (arch::ExtArchTraits<Arch::AARCH32>::HARDFP) { 281 if (vreg_type_ == VRegInfo::Type::FLOAT64) { // in this case one takes 2 slots 282 return HandleHardDouble(); 283 } 284 } 285 gpr_current_slot_ = RoundUp(gpr_current_slot_ - 1, 2) - 1; // 2 286 if (gpr_current_slot_ > gpr_end_slot_) { 287 current_slot_ = gpr_current_slot_; 288 gpr_current_slot_ -= 1; 289 } else { 290 stack_current_slot_ = RoundUp(stack_current_slot_ - 1, 2) - 1; // 2 291 current_slot_ = stack_current_slot_; 292 stack_current_slot_ -= 1; 293 } 294 } 295 296 return *this; 297 } 298 299 auto operator++(int) // NOLINT(cert-dcl21-cpp) 300 { 301 auto res = *this; 302 this->operator++(); 303 return res; 304 } 305 306 bool operator==(const CFrameJniMethodIterator &it) const 307 { 308 return vreg_index_ == it.vreg_index_; 309 } 310 311 bool operator!=(const CFrameJniMethodIterator &it) const 312 { 313 return !(*this == it); 314 } 315 316 private: CFrameJniMethodIterator(uint32_t vreg_index,uint32_t vreg_num,const uint16_t * shorty,ptrdiff_t gpr_begin_slot,ptrdiff_t gpr_end_slot,ptrdiff_t fp_begin_slot,ptrdiff_t fp_end_slot,ptrdiff_t stack_current_slot)317 CFrameJniMethodIterator(uint32_t vreg_index, uint32_t vreg_num, const uint16_t *shorty, ptrdiff_t gpr_begin_slot, 318 ptrdiff_t gpr_end_slot, ptrdiff_t fp_begin_slot, ptrdiff_t fp_end_slot, 319 ptrdiff_t stack_current_slot) 320 : vreg_index_(vreg_index), 321 vreg_num_(vreg_num), 322 shorty_it_(shorty), 323 current_slot_(gpr_begin_slot), 324 gpr_current_slot_(gpr_begin_slot), 325 gpr_end_slot_(gpr_end_slot), 326 fp_current_slot_(fp_begin_slot), 327 fp_end_slot_(fp_end_slot), 328 stack_current_slot_(stack_current_slot) 329 { 330 ++shorty_it_; // skip return value 331 } 332 ConvertType(panda_file::Type type)333 VRegInfo::Type ConvertType(panda_file::Type type) const 334 { 335 switch (type.GetId()) { 336 case panda_file::Type::TypeId::U1: 337 return VRegInfo::Type::BOOL; 338 case panda_file::Type::TypeId::I8: 339 case panda_file::Type::TypeId::U8: 340 case panda_file::Type::TypeId::I16: 341 case panda_file::Type::TypeId::U16: 342 case panda_file::Type::TypeId::I32: 343 case panda_file::Type::TypeId::U32: 344 return VRegInfo::Type::INT32; 345 case panda_file::Type::TypeId::F32: 346 return VRegInfo::Type::FLOAT32; 347 case panda_file::Type::TypeId::F64: 348 return VRegInfo::Type::FLOAT64; 349 case panda_file::Type::TypeId::I64: 350 case panda_file::Type::TypeId::U64: 351 return VRegInfo::Type::INT64; 352 case panda_file::Type::TypeId::REFERENCE: 353 return VRegInfo::Type::OBJECT; 354 case panda_file::Type::TypeId::TAGGED: 355 return VRegInfo::Type::INT64; 356 default: 357 UNREACHABLE(); 358 } 359 return VRegInfo::Type::INT32; 360 } 361 HandleHardFloat()362 CFrameJniMethodIterator &HandleHardFloat() 363 { 364 ASSERT(vreg_type_ == VRegInfo::Type::FLOAT32); 365 if (fp_current_slot_ > fp_end_slot_) { 366 current_slot_ = fp_current_slot_; 367 --fp_current_slot_; 368 } else { 369 --stack_current_slot_; 370 current_slot_ = stack_current_slot_; 371 } 372 return *this; 373 } 374 HandleHardDouble()375 CFrameJniMethodIterator &HandleHardDouble() 376 { 377 ASSERT(vreg_type_ == VRegInfo::Type::FLOAT64); 378 fp_current_slot_ = RoundDown(static_cast<uintptr_t>(fp_current_slot_) + 1, 2U) - 1; 379 if (fp_current_slot_ > fp_end_slot_) { 380 current_slot_ = fp_current_slot_; 381 fp_current_slot_ -= 2U; 382 } else { 383 stack_current_slot_ = RoundUp(stack_current_slot_ - 1, 2U) - 1; 384 current_slot_ = stack_current_slot_; 385 stack_current_slot_ -= 1; 386 } 387 return *this; 388 } 389 390 private: 391 uint32_t vreg_index_; 392 uint32_t vreg_num_; 393 panda_file::ShortyIterator shorty_it_; 394 ptrdiff_t current_slot_; 395 ptrdiff_t gpr_current_slot_; 396 ptrdiff_t gpr_end_slot_; 397 ptrdiff_t fp_current_slot_; 398 ptrdiff_t fp_end_slot_; 399 ptrdiff_t stack_current_slot_; 400 VRegInfo::Type vreg_type_ = VRegInfo::Type::OBJECT; 401 }; 402 403 template <Arch arch = RUNTIME_ARCH> 404 class CFrameDynamicNativeMethodIterator { 405 using SlotType = typename CFrame::SlotType; 406 407 public: MakeRange(CFrame * cframe)408 static auto MakeRange(CFrame *cframe) 409 { 410 size_t arg_regs_count = arch::ExtArchTraits<arch>::NUM_GP_ARG_REGS; 411 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 412 Span<SlotType> callers(cframe->GetCallerSaveStack() - arg_regs_count, arg_regs_count); 413 // In dynamic methods the first two args are Method* method and uint32_t num_args 414 // Read num_args 415 auto num_args = static_cast<uint32_t>(callers[1]); 416 ++num_args; // count function object 417 size_t num_arg_slots = num_args * sizeof(Frame::VRegister) / sizeof(SlotType); 418 419 CFrameLayout cframe_layout(arch, 0); 420 size_t caller_end_slot = cframe_layout.GetCallerRegsStartSlot(); 421 size_t caller_start_slot = caller_end_slot + arg_regs_count; 422 size_t gpr_arg_start_slot = caller_start_slot - 2; // skip Method and num_args, 2 - offset 423 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) 424 if constexpr (arch != Arch::X86_64) { 425 gpr_arg_start_slot = RoundDown(gpr_arg_start_slot, sizeof(Frame::VRegister) / sizeof(SlotType)); 426 } 427 size_t num_gpr_arg_slots = std::min(gpr_arg_start_slot - caller_end_slot, num_arg_slots); 428 429 size_t num_stack_arg_slots = num_arg_slots - num_gpr_arg_slots; 430 ptrdiff_t stack_arg_start_slot = cframe_layout.GetStackArgsStartSlot(); 431 ptrdiff_t stack_arg_end_slot = stack_arg_start_slot - num_stack_arg_slots; 432 433 // Since all stack slots are calculated relative STACK_START_SLOT 434 // subtract it from each value 435 ptrdiff_t stack_start_slot = cframe_layout.GetStackStartSlot(); 436 gpr_arg_start_slot -= stack_start_slot; 437 caller_end_slot -= stack_start_slot; 438 stack_arg_start_slot -= stack_start_slot; 439 stack_arg_end_slot -= stack_start_slot; 440 return Range<CFrameDynamicNativeMethodIterator>( 441 CFrameDynamicNativeMethodIterator(cframe, gpr_arg_start_slot - 1, caller_end_slot - 1, stack_arg_start_slot, 442 stack_arg_end_slot), 443 CFrameDynamicNativeMethodIterator(cframe, caller_end_slot - 1, caller_end_slot - 1, stack_arg_end_slot, 444 stack_arg_end_slot)); 445 } 446 447 VRegInfo operator*() 448 { 449 if (gpr_start_slot_ > gpr_end_slot_) { 450 return VRegInfo(gpr_start_slot_, VRegInfo::Location::SLOT, VRegInfo::Type::INT64, false, vreg_num_); 451 } 452 ASSERT(stack_start_slot_ > stack_end_slot_); 453 return VRegInfo(stack_start_slot_, VRegInfo::Location::SLOT, VRegInfo::Type::INT64, false, vreg_num_); 454 } 455 456 CFrameDynamicNativeMethodIterator &operator++() 457 { 458 size_t inc = sizeof(Frame::VRegister) / sizeof(SlotType); 459 if (gpr_start_slot_ > gpr_end_slot_) { 460 gpr_start_slot_ -= inc; 461 ++vreg_num_; 462 } else if (stack_start_slot_ > stack_end_slot_) { 463 stack_start_slot_ -= inc; 464 ++vreg_num_; 465 } 466 return *this; 467 } 468 469 // NOLINTNEXTLINE(cert-dcl21-cpp) 470 CFrameDynamicNativeMethodIterator operator++(int) 471 { 472 auto res = *this; 473 this->operator++(); 474 return res; 475 } 476 477 bool operator==(const CFrameDynamicNativeMethodIterator &it) const 478 { 479 return gpr_start_slot_ == it.gpr_start_slot_ && stack_start_slot_ == it.stack_start_slot_; 480 } 481 482 bool operator!=(const CFrameDynamicNativeMethodIterator &it) const 483 { 484 return !(*this == it); 485 } 486 487 private: CFrameDynamicNativeMethodIterator(CFrame * cframe,ptrdiff_t gpr_start_slot,ptrdiff_t gpr_end_slot,ptrdiff_t stack_start_slot,ptrdiff_t stack_end_slot)488 CFrameDynamicNativeMethodIterator(CFrame *cframe, ptrdiff_t gpr_start_slot, ptrdiff_t gpr_end_slot, 489 ptrdiff_t stack_start_slot, ptrdiff_t stack_end_slot) 490 : cframe_(cframe), 491 gpr_start_slot_(gpr_start_slot), 492 gpr_end_slot_(gpr_end_slot), 493 stack_start_slot_(stack_start_slot), 494 stack_end_slot_(stack_end_slot) 495 { 496 } 497 498 private: 499 CFrame *cframe_; 500 uint32_t vreg_num_ = 0; 501 ptrdiff_t gpr_start_slot_; 502 ptrdiff_t gpr_end_slot_; 503 ptrdiff_t stack_start_slot_; 504 ptrdiff_t stack_end_slot_; 505 }; 506 507 } // namespace panda 508 509 #endif // PANDA_RUNTIME_INCLUDE_CFRAME_ITERATORS_H_ 510