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_LIBPANDAFILE_BYTECODE_INSTRUCTION_H_ 17 #define PANDA_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 namespace panda { 28 29 enum class BytecodeInstMode { FAST, SAFE }; 30 31 template <const BytecodeInstMode> 32 class BytecodeInstBase; 33 34 class BytecodeId { 35 public: BytecodeId(uint32_t id)36 constexpr explicit BytecodeId(uint32_t id) : id_(id) {} 37 38 constexpr BytecodeId() = default; 39 40 ~BytecodeId() = default; 41 42 DEFAULT_COPY_SEMANTIC(BytecodeId); 43 NO_MOVE_SEMANTIC(BytecodeId); 44 AsIndex()45 panda_file::File::Index AsIndex() const 46 { 47 return id_; 48 } 49 AsFileId()50 panda_file::File::EntityId AsFileId() const 51 { 52 return panda_file::File::EntityId(id_); 53 } 54 IsValid()55 bool IsValid() const 56 { 57 return id_ != INVALID; 58 } 59 60 bool operator==(BytecodeId id) const noexcept 61 { 62 return id_ == id.id_; 63 } 64 65 friend std::ostream &operator<<(std::ostream &stream, BytecodeId id) 66 { 67 return stream << id.id_; 68 } 69 70 private: 71 static constexpr size_t INVALID = std::numeric_limits<uint32_t>::max(); 72 73 uint32_t id_ {INVALID}; 74 }; 75 76 template <> 77 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) 78 class BytecodeInstBase<BytecodeInstMode::FAST> { 79 public: 80 BytecodeInstBase() = default; BytecodeInstBase(const uint8_t * pc)81 explicit BytecodeInstBase(const uint8_t *pc) : pc_ {pc} {} 82 ~BytecodeInstBase() = default; 83 84 protected: GetPointer(int32_t offset)85 const uint8_t *GetPointer(int32_t offset) const 86 { 87 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 88 return pc_ + offset; 89 } 90 GetAddress()91 const uint8_t *GetAddress() const 92 { 93 return pc_; 94 } 95 GetAddress()96 const uint8_t *GetAddress() volatile const 97 { 98 return pc_; 99 } 100 101 template <class T> Read(size_t offset)102 T Read(size_t offset) const 103 { 104 using unaligned_type __attribute__((aligned(1))) = const T; 105 return *reinterpret_cast<unaligned_type *>(GetPointer(offset)); 106 } 107 ReadByte(size_t offset)108 uint8_t ReadByte(size_t offset) const 109 { 110 return Read<uint8_t>(offset); 111 } 112 113 private: 114 const uint8_t *pc_ {nullptr}; 115 }; 116 117 template <> 118 class BytecodeInstBase<BytecodeInstMode::SAFE> { 119 public: 120 BytecodeInstBase() = default; BytecodeInstBase(const uint8_t * pc,const uint8_t * from,const uint8_t * to)121 explicit BytecodeInstBase(const uint8_t *pc, const uint8_t *from, const uint8_t *to) 122 : pc_ {pc}, from_ {from}, to_ {to}, valid_ {true} 123 { 124 ASSERT(from_ <= to_); 125 ASSERT(pc_ >= from_); 126 ASSERT(pc_ <= to_); 127 } 128 129 protected: GetPointer(int32_t offset)130 const uint8_t *GetPointer(int32_t offset) const 131 { 132 return GetPointer(offset, 1); 133 } 134 IsLast(size_t size)135 bool IsLast(size_t size) const 136 { 137 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 138 const uint8_t *ptr_next = pc_ + size; 139 return ptr_next > to_; 140 } 141 GetPointer(int32_t offset,size_t size)142 const uint8_t *GetPointer(int32_t offset, size_t size) const 143 { 144 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 145 const uint8_t *ptr_from = pc_ + offset; 146 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 147 const uint8_t *ptr_to = ptr_from + size - 1; 148 if (from_ == nullptr || ptr_from < from_ || ptr_to > to_) { 149 valid_ = false; 150 return from_; 151 } 152 return ptr_from; 153 } 154 GetAddress()155 const uint8_t *GetAddress() const 156 { 157 return pc_; 158 } 159 GetFrom()160 const uint8_t *GetFrom() const 161 { 162 return from_; 163 } 164 GetTo()165 const uint8_t *GetTo() const 166 { 167 return to_; 168 } 169 GetOffset()170 uint32_t GetOffset() const 171 { 172 return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc_) - reinterpret_cast<uintptr_t>(from_)); 173 } 174 GetAddress()175 const uint8_t *GetAddress() volatile const 176 { 177 return pc_; 178 } 179 180 template <class T> Read(size_t offset)181 T Read(size_t offset) const 182 { 183 using unaligned_type __attribute__((aligned(1))) = const T; 184 auto ptr = reinterpret_cast<unaligned_type *>(GetPointer(offset, sizeof(T))); 185 if (IsValid()) { 186 return *ptr; 187 } 188 return {}; 189 } 190 IsValid()191 bool IsValid() const 192 { 193 return valid_; 194 } 195 196 private: 197 const uint8_t *pc_ {nullptr}; 198 const uint8_t *from_ {nullptr}; 199 const uint8_t *to_ {nullptr}; 200 mutable bool valid_ {false}; 201 }; 202 203 template <const BytecodeInstMode Mode = BytecodeInstMode::FAST> 204 205 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) 206 class BytecodeInst : public BytecodeInstBase<Mode> { 207 using Base = BytecodeInstBase<Mode>; 208 209 public: 210 #include <bytecode_instruction_enum_gen.h> 211 212 BytecodeInst() = default; 213 214 ~BytecodeInst() = default; 215 216 template <const BytecodeInstMode M = Mode, typename = std::enable_if_t<M == BytecodeInstMode::FAST>> BytecodeInst(const uint8_t * pc)217 explicit BytecodeInst(const uint8_t *pc) : Base {pc} 218 { 219 } 220 221 template <const BytecodeInstMode M = Mode, typename = std::enable_if_t<M == BytecodeInstMode::SAFE>> BytecodeInst(const uint8_t * pc,const uint8_t * from,const uint8_t * to)222 explicit BytecodeInst(const uint8_t *pc, const uint8_t *from, const uint8_t *to) : Base {pc, from, to} 223 { 224 } 225 226 template <Format format, size_t idx = 0> 227 BytecodeId GetId() const; 228 229 template <Format format, size_t idx = 0> 230 uint16_t GetVReg() const; 231 232 template <Format format, size_t idx = 0> 233 auto GetImm() const; 234 235 BytecodeId GetId(size_t idx = 0) const; 236 237 uint16_t GetVReg(size_t idx = 0) const; 238 239 // Read imm and return it as int64_t/uint64_t 240 auto GetImm64(size_t idx = 0) const; 241 242 BytecodeInst::Opcode GetOpcode() const; 243 GetPrimaryOpcode()244 uint8_t GetPrimaryOpcode() const 245 { 246 return static_cast<unsigned>(GetOpcode()) & std::numeric_limits<uint8_t>::max(); 247 } 248 249 bool IsPrimaryOpcodeValid() const; 250 GetSecondaryOpcode()251 uint8_t GetSecondaryOpcode() const 252 { 253 // NOLINTNEXTLINE(hicpp-signed-bitwise) 254 return (static_cast<unsigned>(GetOpcode()) >> std::numeric_limits<uint8_t>::digits) & 255 std::numeric_limits<uint8_t>::max(); 256 } 257 258 template <const BytecodeInstMode M = Mode> 259 auto JumpTo(int32_t offset) const -> std::enable_if_t<M == BytecodeInstMode::FAST, BytecodeInst> 260 { 261 return BytecodeInst(Base::GetPointer(offset)); 262 } 263 264 template <const BytecodeInstMode M = Mode> 265 auto JumpTo(int32_t offset) const -> std::enable_if_t<M == BytecodeInstMode::SAFE, BytecodeInst> 266 { 267 if (!IsValid()) { 268 return {}; 269 } 270 const uint8_t *ptr = Base::GetPointer(offset); 271 if (!IsValid()) { 272 return {}; 273 } 274 return BytecodeInst(ptr, Base::GetFrom(), Base::GetTo()); 275 } 276 277 template <const BytecodeInstMode M = Mode> 278 auto IsLast() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, bool> 279 { 280 return Base::IsLast(GetSize()); 281 } 282 283 template <const BytecodeInstMode M = Mode> 284 auto IsValid() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, bool> 285 { 286 return Base::IsValid(); 287 } 288 289 template <Format format> GetNext()290 BytecodeInst GetNext() const 291 { 292 return JumpTo(Size(format)); 293 } 294 GetNext()295 BytecodeInst GetNext() const 296 { 297 return JumpTo(GetSize()); 298 } 299 GetAddress()300 const uint8_t *GetAddress() const 301 { 302 return Base::GetAddress(); 303 } 304 GetAddress()305 const uint8_t *GetAddress() volatile const 306 { 307 return Base::GetAddress(); 308 } 309 310 template <const BytecodeInstMode M = Mode> 311 auto GetFrom() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, const uint8_t *> 312 { 313 return Base::GetFrom(); 314 } 315 316 template <const BytecodeInstMode M = Mode> 317 auto GetTo() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, const uint8_t *> 318 { 319 return Base::GetTo(); 320 } 321 322 template <const BytecodeInstMode M = Mode> 323 auto GetOffset() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, uint32_t> 324 { 325 return Base::GetOffset(); 326 } 327 ReadByte(size_t offset)328 uint8_t ReadByte(size_t offset) const 329 { 330 return Base::template Read<uint8_t>(offset); 331 } 332 333 template <class R, class S> 334 auto ReadHelper(size_t byteoffset, size_t bytecount, size_t offset, size_t width) const; 335 336 template <size_t offset, size_t width, bool is_signed = false> 337 auto Read() const; 338 339 template <bool is_signed = false> 340 auto Read64(size_t offset, size_t width) const; 341 342 size_t GetSize() const; 343 344 Format GetFormat() const; 345 346 bool HasFlag(Flags flag) const; 347 348 bool CanThrow() const; 349 IsTerminator()350 bool IsTerminator() const 351 { 352 return HasFlag(Flags::RETURN) || HasFlag(Flags::JUMP) || (GetOpcode() == Opcode::THROW_V8); 353 } 354 355 static constexpr bool HasId(Format format, size_t idx); 356 357 static constexpr bool HasVReg(Format format, size_t idx); 358 359 static constexpr bool HasImm(Format format, size_t idx); 360 361 static constexpr size_t Size(Format format); 362 }; 363 364 template <const BytecodeInstMode Mode> 365 std::ostream &operator<<(std::ostream &os, const BytecodeInst<Mode> &inst); 366 367 using BytecodeInstruction = BytecodeInst<BytecodeInstMode::FAST>; 368 using BytecodeInstructionSafe = BytecodeInst<BytecodeInstMode::SAFE>; 369 370 } // namespace panda 371 372 #endif // PANDA_LIBPANDAFILE_BYTECODE_INSTRUCTION_H_ 373