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 16 #ifndef LIBPANDAFILE_BYTECODE_INSTRUCTION_H_ 17 #define LIBPANDAFILE_BYTECODE_INSTRUCTION_H_ 18 19 #include "file.h" 20 21 #include <cstdint> 22 #include <cstddef> 23 #include <type_traits> 24 25 #include "utils/bit_helpers.h" 26 27 #if !PANDA_TARGET_WINDOWS 28 #include "securec.h" 29 #endif 30 31 namespace ark { 32 33 enum class BytecodeInstMode { FAST, SAFE }; 34 35 template <const BytecodeInstMode> 36 class BytecodeInstBase; 37 38 class BytecodeId { 39 public: BytecodeId(uint32_t id)40 constexpr explicit BytecodeId(uint32_t id) : id_(id) {} 41 42 constexpr BytecodeId() = default; 43 44 ~BytecodeId() = default; 45 46 DEFAULT_COPY_SEMANTIC(BytecodeId); 47 NO_MOVE_SEMANTIC(BytecodeId); 48 AsIndex() const49 panda_file::File::Index AsIndex() const 50 { 51 ASSERT(id_ < std::numeric_limits<uint16_t>::max()); 52 return id_; 53 } 54 55 panda_file::File::EntityId AsFileId() const 56 { 57 return panda_file::File::EntityId(id_); 58 } 59 60 uint32_t AsRawValue() const 61 { 62 return id_; 63 } 64 65 bool IsValid() const 66 { 67 return id_ != INVALID; 68 } 69 70 bool operator==(BytecodeId id) const noexcept 71 { 72 return id_ == id.id_; 73 } 74 75 friend std::ostream &operator<<(std::ostream &stream, BytecodeId id) 76 { 77 return stream << id.id_; 78 } 79 80 private: 81 static constexpr size_t INVALID = std::numeric_limits<uint32_t>::max(); 82 83 uint32_t id_ {INVALID}; 84 }; 85 86 template <> 87 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) 88 class BytecodeInstBase<BytecodeInstMode::FAST> { 89 public: 90 BytecodeInstBase() = default; 91 explicit BytecodeInstBase(const uint8_t *pc) : pc_ {pc} {} 92 ~BytecodeInstBase() = default; 93 94 protected: 95 const uint8_t *GetPointer(int32_t offset) const 96 { 97 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 98 return pc_ + offset; 99 } 100 101 const uint8_t *GetAddress() const 102 { 103 return pc_; 104 } 105 106 const uint8_t *GetAddress() volatile const 107 { 108 return pc_; 109 } 110 111 template <class T> 112 T Read(size_t offset) const 113 { 114 using UnalignedType __attribute__((aligned(1))) = T; 115 return *reinterpret_cast<const UnalignedType *>(GetPointer(static_cast<int32_t>(offset))); 116 } 117 118 void Write(uint32_t value, uint32_t offset, uint32_t width) 119 { 120 auto *dst = const_cast<uint8_t *>(GetPointer(static_cast<int32_t>(offset))); 121 if (memcpy_s(dst, width, &value, width) != 0) { 122 LOG(FATAL, PANDAFILE) << "Cannot write value : " << value << "at the dst offset : " << offset; 123 } 124 } 125 126 uint8_t ReadByte(size_t offset) const 127 { 128 return Read<uint8_t>(offset); 129 } 130 131 private: 132 const uint8_t *pc_ {nullptr}; 133 }; 134 135 template <> 136 class BytecodeInstBase<BytecodeInstMode::SAFE> { 137 public: 138 BytecodeInstBase() = default; 139 explicit BytecodeInstBase(const uint8_t *pc, const uint8_t *from, const uint8_t *to) 140 : pc_ {pc}, from_ {from}, to_ {to}, valid_ {true} 141 { 142 ASSERT(from_ <= to_ && pc_ >= from_ && pc_ < to_); 143 } 144 145 protected: 146 const uint8_t *GetPointer(int32_t offset) const 147 { 148 return GetPointer(offset, 1); 149 } 150 151 bool IsLast(size_t size) const 152 { 153 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 154 const uint8_t *ptrNext = pc_ + size; 155 return ptrNext >= to_; 156 } 157 158 NO_UB_SANITIZE const uint8_t *GetPointer(int32_t offset, size_t size) const 159 { 160 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 161 const uint8_t *ptrFrom = pc_ + offset; 162 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 163 const uint8_t *ptrTo = ptrFrom + size; 164 if (from_ == nullptr || ptrFrom < from_ || ptrTo > to_) { 165 valid_ = false; 166 return from_; 167 } 168 return ptrFrom; 169 } 170 171 const uint8_t *GetAddress() const 172 { 173 return pc_; 174 } 175 176 const uint8_t *GetFrom() const 177 { 178 return from_; 179 } 180 181 const uint8_t *GetTo() const 182 { 183 return to_; 184 } 185 186 uint32_t GetOffset() const 187 { 188 return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc_) - reinterpret_cast<uintptr_t>(from_)); 189 } 190 191 const uint8_t *GetAddress() volatile const 192 { 193 return pc_; 194 } 195 196 template <class T> 197 T Read(size_t offset) const 198 { 199 using UnalignedType __attribute__((aligned(1))) = T; 200 auto ptr = reinterpret_cast<const UnalignedType *>(GetPointer(static_cast<int32_t>(offset), sizeof(T))); 201 if (IsValid()) { 202 return *ptr; 203 } 204 return {}; 205 } 206 207 bool IsValid() const 208 { 209 return valid_; 210 } 211 212 private: 213 const uint8_t *pc_ {nullptr}; 214 const uint8_t *from_ {nullptr}; 215 const uint8_t *to_ {nullptr}; 216 mutable bool valid_ {false}; 217 }; 218 219 template <const BytecodeInstMode MODE = BytecodeInstMode::FAST> 220 221 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) 222 class BytecodeInst : public BytecodeInstBase<MODE> { 223 using Base = BytecodeInstBase<MODE>; 224 225 public: 226 #include <bytecode_instruction_enum_gen.h> 227 228 BytecodeInst() = default; 229 230 ~BytecodeInst() = default; 231 232 template <const BytecodeInstMode M = MODE, typename = std::enable_if_t<M == BytecodeInstMode::FAST>> 233 explicit BytecodeInst(const uint8_t *pc) : Base {pc} 234 { 235 } 236 237 template <const BytecodeInstMode M = MODE, typename = std::enable_if_t<M == BytecodeInstMode::SAFE>> 238 explicit BytecodeInst(const uint8_t *pc, const uint8_t *from, const uint8_t *to) : Base {pc, from, to} 239 { 240 } 241 242 template <Format FORMAT, typename EnumT = BytecodeInst<MODE>::Opcode, size_t IDX = 0> 243 BytecodeId GetId() const; 244 245 template <Format FORMAT, size_t IDX = 0> 246 uint16_t GetVReg() const; 247 248 template <Format FORMAT> 249 uint16_t GetVReg(size_t idx) const; 250 251 template <Format FORMAT, size_t IDX = 0> 252 auto GetImm() const; 253 254 template <typename EnumT = BytecodeInst<MODE>::Opcode> 255 BytecodeId GetId(size_t idx = 0) const; 256 257 void UpdateId(BytecodeId newId, uint32_t idx = 0); 258 259 template <typename EnumT = BytecodeInst<MODE>::Opcode> 260 uint16_t GetVReg(size_t idx = 0) const; 261 262 // Read imm and return it as int64_t/uint64_t 263 template <typename EnumT = BytecodeInst<MODE>::Opcode> 264 auto GetImm64(size_t idx = 0) const; 265 266 /// Return profile id if instruction supports profiling, otherwise return -1. 267 int GetProfileId() const; 268 269 /** 270 * Primary and Secondary Opcodes are used in interpreter/verifier instruction dispatch 271 * while full Opcode is typically used for various instruction property query. 272 * 273 * Implementation note: one can describe Opcode in terms of Primary/Secondary opcodes 274 * or vice versa. The first way is more preferable, because Primary/Secondary opcodes 275 * are more performance critical and compiler is not always clever enough to reduce them 276 * to simple byte reads. 277 */ 278 template <typename EnumT = BytecodeInst<MODE>::Opcode> 279 EnumT GetOpcode() const; 280 281 uint8_t GetPrimaryOpcode() const 282 { 283 return ReadByte(0); 284 } 285 286 bool IsPrimaryOpcodeValid() const; 287 288 uint8_t GetSecondaryOpcode() const; 289 290 bool IsPrefixed() const; 291 292 static constexpr uint8_t GetMinPrefixOpcodeIndex(); 293 294 template <const BytecodeInstMode M = MODE> 295 auto JumpTo(int32_t offset) const -> std::enable_if_t<M == BytecodeInstMode::FAST, BytecodeInst> 296 { 297 return BytecodeInst(Base::GetPointer(offset)); 298 } 299 300 template <const BytecodeInstMode M = MODE> 301 auto JumpTo(int32_t offset) const -> std::enable_if_t<M == BytecodeInstMode::SAFE, BytecodeInst> 302 { 303 if (!IsValid()) { 304 return {}; 305 } 306 const uint8_t *ptr = Base::GetPointer(offset); 307 if (!IsValid()) { 308 return {}; 309 } 310 return BytecodeInst(ptr, Base::GetFrom(), Base::GetTo()); 311 } 312 313 template <const BytecodeInstMode M = MODE> 314 auto IsLast() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, bool> 315 { 316 return Base::IsLast(GetSize()); 317 } 318 319 template <const BytecodeInstMode M = MODE> 320 auto IsValid() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, bool> 321 { 322 return Base::IsValid(); 323 } 324 325 template <Format FORMAT> 326 BytecodeInst GetNext() const 327 { 328 return JumpTo(Size(FORMAT)); 329 } 330 331 template <typename EnumT = BytecodeInst<MODE>::Opcode> 332 BytecodeInst GetNext() const 333 { 334 return JumpTo(GetSize<EnumT>()); 335 } 336 337 const uint8_t *GetAddress() const 338 { 339 return Base::GetAddress(); 340 } 341 342 const uint8_t *GetAddress() volatile const 343 { 344 return Base::GetAddress(); 345 } 346 347 template <const BytecodeInstMode M = MODE> 348 auto GetFrom() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, const uint8_t *> 349 { 350 return Base::GetFrom(); 351 } 352 353 template <const BytecodeInstMode M = MODE> 354 auto GetTo() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, const uint8_t *> 355 { 356 return Base::GetTo(); 357 } 358 359 template <const BytecodeInstMode M = MODE> 360 auto GetOffset() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, uint32_t> 361 { 362 return Base::GetOffset(); 363 } 364 365 uint8_t ReadByte(size_t offset) const 366 { 367 return Base::template Read<uint8_t>(offset); 368 } 369 370 template <class R, class S> 371 auto ReadHelper(size_t byteoffset, size_t bytecount, size_t offset, size_t width) const; 372 373 template <size_t OFFSET, size_t WIDTH, bool IS_SIGNED = false> 374 auto Read() const; 375 376 template <bool IS_SIGNED = false> 377 auto Read64(size_t offset, size_t width) const; 378 379 template <typename EnumT = BytecodeInst<MODE>::Opcode> 380 size_t GetSize() const; 381 382 template <typename EnumT = BytecodeInst<MODE>::Opcode> 383 Format GetFormat() const; 384 385 template <typename EnumT = BytecodeInst<MODE>::Opcode> 386 bool HasFlag(Flags flag) const; 387 388 bool IsThrow(Exceptions exception) const; 389 390 bool IsJump() const 391 { 392 return HasFlag(Flags::JUMP); 393 } 394 395 bool CanThrow() const; 396 397 bool IsTerminator() const 398 { 399 return HasFlag(Flags::RETURN) || HasFlag(Flags::JUMP) || IsThrow(Exceptions::X_THROW); 400 } 401 402 bool IsSuspend() const 403 { 404 return HasFlag(Flags::SUSPEND); 405 } 406 407 static constexpr bool HasId(Format format, size_t idx); 408 409 static constexpr bool HasVReg(Format format, size_t idx); 410 411 static constexpr bool HasImm(Format format, size_t idx); 412 413 static constexpr size_t Size(Format format); 414 415 /* Checks if format is used to pass arguments in vregisters (without accumulator) */ 416 static constexpr bool IsVregArgsShort(Format format); 417 static constexpr bool IsVregArgs(Format format); 418 static constexpr bool IsVregArgsRange(Format format); 419 420 template <BytecodeInst<MODE>::Opcode OPCODE> 421 static constexpr auto GetQuickened(); 422 423 template <BytecodeInst<MODE>::Format FORMAT> 424 static constexpr auto GetQuickened(); 425 }; 426 427 template <const BytecodeInstMode MODE> 428 std::ostream &operator<<(std::ostream &os, const BytecodeInst<MODE> &inst); 429 430 using BytecodeInstruction = BytecodeInst<BytecodeInstMode::FAST>; 431 using BytecodeInstructionSafe = BytecodeInst<BytecodeInstMode::SAFE>; 432 433 template <bool IS_QUICKENED> 434 class BytecodeInstructionResolver { 435 public: 436 template <BytecodeInstruction::Opcode OPCODE> 437 static constexpr auto Get() 438 { 439 // NOLINTNEXTLINE(readability-braces-around-statements) 440 if constexpr (IS_QUICKENED) { 441 // NOLINTNEXTLINE(readability-magic-numbers) 442 return BytecodeInstruction::GetQuickened<OPCODE>(); 443 // NOLINTNEXTLINE(readability-misleading-indentation) 444 } else { 445 // NOLINTNEXTLINE(readability-magic-numbers) 446 return OPCODE; 447 } 448 } 449 450 template <BytecodeInstruction::Format FORMAT> 451 static constexpr auto Get() 452 { 453 // NOLINTNEXTLINE(readability-braces-around-statements) 454 if constexpr (IS_QUICKENED) { 455 // NOLINTNEXTLINE(readability-magic-numbers) 456 return BytecodeInstruction::GetQuickened<FORMAT>(); 457 // NOLINTNEXTLINE(readability-misleading-indentation) 458 } else { 459 // NOLINTNEXTLINE(readability-magic-numbers) 460 return FORMAT; 461 } 462 } 463 }; 464 465 } // namespace ark 466 467 #endif // LIBANDAFILE_BYTECODE_INSTRUCTION_H_ 468