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