• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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