• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 PANDA_ASSEMBLER_ASSEMBLY_INS_H
17 #define PANDA_ASSEMBLER_ASSEMBLY_INS_H
18 
19 #include <array>
20 #include <string>
21 #include <string_view>
22 #include <unordered_map>
23 #include <variant>
24 #include <vector>
25 
26 #include "assembly-debug.h"
27 #include "bytecode_emitter.h"
28 #include "file_items.h"
29 #include "isa.h"
30 #include "lexer.h"
31 
32 namespace ark::pandasm {
33 
34 enum class Opcode {
35 // CC-OFFNXT(G.PRE.02) opcode is class member
36 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
37 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) opcode,
38     PANDA_INSTRUCTION_LIST(OPLIST)
39 #undef OPLIST
40         INVALID,
41     NUM_OPCODES = INVALID
42 };
43 
44 enum InstFlags {
45     NONE = 0,
46     JUMP = (1U << 0U),
47     COND = (1U << 1U),
48     CALL = (1U << 2U),
49     RETURN = (1U << 3U),
50     ACC_READ = (1U << 4U),
51     ACC_WRITE = (1U << 5U),
52     PSEUDO = (1U << 6U),
53     THROWING = (1U << 7U),
54     METHOD_ID = (1U << 8U),
55     FIELD_ID = (1U << 9U),
56     TYPE_ID = (1U << 10U),
57     STRING_ID = (1U << 11U),
58     LITERALARRAY_ID = (1U << 12U),
59     CALL_RANGE = (1U << 13U)
60 };
61 
62 constexpr int INVALID_REG_IDX = -1;
63 
64 constexpr InstFlags operator|(InstFlags a, InstFlags b)
65 {
66     using Utype = std::underlying_type_t<InstFlags>;
67     return static_cast<InstFlags>(static_cast<Utype>(a) | static_cast<Utype>(b));
68 }
69 
70 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
71 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (flags),
72 constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = {
73     PANDA_INSTRUCTION_LIST(OPLIST)};
74 #undef OPLIST
75 
76 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
77 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (width),
78 constexpr std::array<size_t, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_WIDTH_TABLE = {
79     PANDA_INSTRUCTION_LIST(OPLIST)};
80 #undef OPLIST
81 
82 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
83 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (def_idx),
84 constexpr std::array<int, static_cast<size_t>(Opcode::NUM_OPCODES)> DEF_IDX_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
85 #undef OPLIST
86 
87 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
88 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (use_idxs),
89 constexpr std::array<std::array<int, MAX_NUMBER_OF_SRC_REGS>, static_cast<size_t>(Opcode::NUM_OPCODES)> USE_IDXS_TABLE =
90     {PANDA_INSTRUCTION_LIST(OPLIST)};  // CC-OFF(G.FMT.03) any style changes will worsen readability
91 #undef OPLIST
92 
93 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
94 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (prof_size),
95 // clang-format off
96 constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES) + 1> INST_PROFILE_SIZES =
97     {PANDA_INSTRUCTION_LIST(OPLIST) 0};  // CC-OFF(G.FMT.03) any style changes will worsen readability
98 // clang-format on
99 #undef OPLIST
100 
101 // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
102 struct Ins {
103     using IType = std::variant<int64_t, double>;
104 
105     constexpr static uint16_t ACCUMULATOR = -1;
106     constexpr static size_t MAX_CALL_SHORT_ARGS = 2;
107     constexpr static size_t MAX_CALL_ARGS = 4;
108     constexpr static uint16_t MAX_NON_RANGE_CALL_REG = 15;
109     constexpr static uint16_t MAX_RANGE_CALL_START_REG = 255;
110 
111     Opcode opcode = Opcode::INVALID; /* operation type */
112     std::vector<uint16_t> regs;      /* list of arguments - registers */
113     std::vector<std::string> ids;    /* list of arguments - identifiers */
114     std::vector<IType> imms;         /* list of arguments - immediates */
115     std::string label;               /* label at the beginning of a line */
116     bool setLabel = false;           /* whether this label is defined */
117     debuginfo::Ins insDebug;
118     uint16_t profileId {0}; /* Index in the profile vector */
119 
120     PANDA_PUBLIC_API std::string ToString(const std::string &endline = "", bool printArgs = false,
121                                           size_t firstArgIdx = 0) const;
122 
123     // CC-OFFNXT(G.FUN.01-CPP) solid logic
124     bool Emit(BytecodeEmitter &emitter, panda_file::MethodItem *method,
125               const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods,
126               const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
127               const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
128               const std::unordered_map<std::string_view, panda_file::StringItem *> &strings,
129               const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays,
130               const std::unordered_map<std::string_view, ark::Label> &labels) const;
131 
OperandListLengthIns132     size_t OperandListLength() const
133     {
134         return regs.size() + ids.size() + imms.size();
135     }
136 
HasFlagIns137     bool HasFlag(InstFlags flag) const
138     {
139         if (opcode == Opcode::INVALID) {  // NOTE(mbolshov): introduce 'label' opcode for labels
140             return false;
141         }
142         return (INST_FLAGS_TABLE[static_cast<size_t>(opcode)] & flag) != 0;
143     }
144 
CanThrowIns145     bool CanThrow() const
146     {
147         return HasFlag(InstFlags::THROWING) || HasFlag(InstFlags::METHOD_ID) || HasFlag(InstFlags::FIELD_ID) ||
148                HasFlag(InstFlags::TYPE_ID) || HasFlag(InstFlags::STRING_ID);
149     }
150 
IsJumpIns151     bool IsJump() const
152     {
153         return HasFlag(InstFlags::JUMP);
154     }
155 
IsConditionalJumpIns156     bool IsConditionalJump() const
157     {
158         return IsJump() && HasFlag(InstFlags::COND);
159     }
160 
IsCallIns161     bool IsCall() const
162     {  // Non-range call
163         return HasFlag(InstFlags::CALL);
164     }
165 
IsCallRangeIns166     bool IsCallRange() const
167     {  // Range call
168         return HasFlag(InstFlags::CALL_RANGE);
169     }
170 
IsPseudoCallIns171     bool IsPseudoCall() const
172     {
173         return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL);
174     }
175 
IsReturnIns176     bool IsReturn() const
177     {
178         return HasFlag(InstFlags::RETURN);
179     }
180 
MaxRegEncodingWidthIns181     size_t MaxRegEncodingWidth() const
182     {
183         if (opcode == Opcode::INVALID) {
184             return 0;
185         }
186         return INST_WIDTH_TABLE[static_cast<size_t>(opcode)];
187     }
188 
UsesIns189     std::vector<uint16_t> Uses() const
190     {
191         if (IsPseudoCall()) {
192             return regs;
193         }
194 
195         if (opcode == Opcode::INVALID) {
196             return {};
197         }
198 
199         auto useIdxs = USE_IDXS_TABLE[static_cast<size_t>(opcode)];
200         std::vector<uint16_t> res(MAX_NUMBER_OF_SRC_REGS + 1);
201         if (HasFlag(InstFlags::ACC_READ)) {
202             res.push_back(Ins::ACCUMULATOR);
203         }
204         for (auto idx : useIdxs) {
205             if (idx == INVALID_REG_IDX) {
206                 break;
207             }
208             ASSERT(static_cast<size_t>(idx) < regs.size());
209             res.emplace_back(regs[idx]);
210         }
211         return res;
212     }
213 
DefIns214     std::optional<size_t> Def() const
215     {
216         if (opcode == Opcode::INVALID) {
217             return {};
218         }
219         auto defIdx = DEF_IDX_TABLE[static_cast<size_t>(opcode)];
220         if (defIdx != INVALID_REG_IDX) {
221             return regs[defIdx];
222         }
223         if (HasFlag(InstFlags::ACC_WRITE)) {
224             return Ins::ACCUMULATOR;
225         }
226         return {};
227     }
228 
IsValidToEmitIns229     bool IsValidToEmit() const
230     {
231         const auto invalidRegNum = 1U << MaxRegEncodingWidth();
232         for (auto reg : regs) {
233             if (reg >= invalidRegNum) {
234                 return false;
235             }
236         }
237         return true;
238     }
239 
HasDebugInfoIns240     bool HasDebugInfo() const
241     {
242         return insDebug.lineNumber != 0;
243     }
244 
245 private:
246     std::string OperandsToString(bool printArgs = false, size_t firstArgIdx = 0) const;
247     std::string RegsToString(bool &first, bool printArgs = false, size_t firstArgIdx = 0) const;
248     std::string ImmsToString(bool &first) const;
249     std::string IdsToString(bool &first) const;
250 
251     std::string IdToString(size_t idx, bool isFirst) const;
252     std::string ImmToString(size_t idx, bool isFirst) const;
253     std::string RegToString(size_t idx, bool isFirst, bool printArgs = false, size_t firstArgIdx = 0) const;
254 };
255 // NOLINTEND(misc-non-private-member-variables-in-classes)
256 
257 }  // namespace ark::pandasm
258 
259 #endif  // PANDA_ASSEMBLER_ASSEMBLY_INS_H
260