• 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 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
36 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) opcode,
37     PANDA_INSTRUCTION_LIST(OPLIST)
38 #undef OPLIST
39         INVALID,
40     NUM_OPCODES = INVALID
41 };
42 
43 enum InstFlags {
44     NONE = 0,
45     JUMP = (1U << 0U),
46     COND = (1U << 1U),
47     CALL = (1U << 2U),
48     RETURN = (1U << 3U),
49     ACC_READ = (1U << 4U),
50     ACC_WRITE = (1U << 5U),
51     PSEUDO = (1U << 6U),
52     THROWING = (1U << 7U),
53     METHOD_ID = (1U << 8U),
54     FIELD_ID = (1U << 9U),
55     TYPE_ID = (1U << 10U),
56     STRING_ID = (1U << 11U),
57     LITERALARRAY_ID = (1U << 12U),
58     CALL_RANGE = (1U << 13U)
59 };
60 
61 constexpr int INVALID_REG_IDX = -1;
62 
63 constexpr InstFlags operator|(InstFlags a, InstFlags b)
64 {
65     using Utype = std::underlying_type_t<InstFlags>;
66     return static_cast<InstFlags>(static_cast<Utype>(a) | static_cast<Utype>(b));
67 }
68 
69 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
70 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (flags),
71 constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = {
72     PANDA_INSTRUCTION_LIST(OPLIST)};
73 #undef OPLIST
74 
75 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
76 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (width),
77 constexpr std::array<size_t, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_WIDTH_TABLE = {
78     PANDA_INSTRUCTION_LIST(OPLIST)};
79 #undef OPLIST
80 
81 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
82 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (def_idx),
83 constexpr std::array<int, static_cast<size_t>(Opcode::NUM_OPCODES)> DEF_IDX_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
84 #undef OPLIST
85 
86 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
87 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (use_idxs),
88 // clang-format off
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)};
91 // clang-format on
92 #undef OPLIST
93 
94 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
95 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (prof_size),
96 constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES) + 1> INST_PROFILE_SIZES = {
97     PANDA_INSTRUCTION_LIST(OPLIST) 0};
98 #undef OPLIST
99 
100 // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
101 struct Ins {
102     using IType = std::variant<int64_t, double>;
103 
104     constexpr static uint16_t ACCUMULATOR = -1;
105     constexpr static size_t MAX_CALL_SHORT_ARGS = 2;
106     constexpr static size_t MAX_CALL_ARGS = 4;
107     constexpr static uint16_t MAX_NON_RANGE_CALL_REG = 15;
108     constexpr static uint16_t MAX_RANGE_CALL_START_REG = 255;
109 
110     Opcode opcode = Opcode::INVALID; /* operation type */
111     std::vector<uint16_t> regs;      /* list of arguments - registers */
112     std::vector<std::string> ids;    /* list of arguments - identifiers */
113     std::vector<IType> imms;         /* list of arguments - immediates */
114     std::string label;               /* label at the beginning of a line */
115     bool setLabel = false;           /* whether this label is defined */
116     debuginfo::Ins insDebug;
117     uint16_t profileId {0}; /* Index in the profile vector */
118 
119     PANDA_PUBLIC_API std::string ToString(const std::string &endline = "", bool printArgs = false,
120                                           size_t firstArgIdx = 0) const;
121 
122     bool Emit(BytecodeEmitter &emitter, panda_file::MethodItem *method,
123               const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods,
124               const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
125               const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
126               const std::unordered_map<std::string_view, panda_file::StringItem *> &strings,
127               const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays,
128               const std::unordered_map<std::string_view, ark::Label> &labels) const;
129 
OperandListLengthIns130     size_t OperandListLength() const
131     {
132         return regs.size() + ids.size() + imms.size();
133     }
134 
HasFlagIns135     bool HasFlag(InstFlags flag) const
136     {
137         if (opcode == Opcode::INVALID) {  // NOTE(mbolshov): introduce 'label' opcode for labels
138             return false;
139         }
140         return (INST_FLAGS_TABLE[static_cast<size_t>(opcode)] & flag) != 0;
141     }
142 
CanThrowIns143     bool CanThrow() const
144     {
145         return HasFlag(InstFlags::THROWING) || HasFlag(InstFlags::METHOD_ID) || HasFlag(InstFlags::FIELD_ID) ||
146                HasFlag(InstFlags::TYPE_ID) || HasFlag(InstFlags::STRING_ID);
147     }
148 
IsJumpIns149     bool IsJump() const
150     {
151         return HasFlag(InstFlags::JUMP);
152     }
153 
IsConditionalJumpIns154     bool IsConditionalJump() const
155     {
156         return IsJump() && HasFlag(InstFlags::COND);
157     }
158 
IsCallIns159     bool IsCall() const
160     {  // Non-range call
161         return HasFlag(InstFlags::CALL);
162     }
163 
IsCallRangeIns164     bool IsCallRange() const
165     {  // Range call
166         return HasFlag(InstFlags::CALL_RANGE);
167     }
168 
IsPseudoCallIns169     bool IsPseudoCall() const
170     {
171         return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL);
172     }
173 
IsReturnIns174     bool IsReturn() const
175     {
176         return HasFlag(InstFlags::RETURN);
177     }
178 
MaxRegEncodingWidthIns179     size_t MaxRegEncodingWidth() const
180     {
181         if (opcode == Opcode::INVALID) {
182             return 0;
183         }
184         return INST_WIDTH_TABLE[static_cast<size_t>(opcode)];
185     }
186 
UsesIns187     std::vector<uint16_t> Uses() const
188     {
189         if (IsPseudoCall()) {
190             return regs;
191         }
192 
193         if (opcode == Opcode::INVALID) {
194             return {};
195         }
196 
197         auto useIdxs = USE_IDXS_TABLE[static_cast<size_t>(opcode)];
198         std::vector<uint16_t> res(MAX_NUMBER_OF_SRC_REGS + 1);
199         if (HasFlag(InstFlags::ACC_READ)) {
200             res.push_back(Ins::ACCUMULATOR);
201         }
202         for (auto idx : useIdxs) {
203             if (idx == INVALID_REG_IDX) {
204                 break;
205             }
206             ASSERT(static_cast<size_t>(idx) < regs.size());
207             res.emplace_back(regs[idx]);
208         }
209         return res;
210     }
211 
DefIns212     std::optional<size_t> Def() const
213     {
214         if (opcode == Opcode::INVALID) {
215             return {};
216         }
217         auto defIdx = DEF_IDX_TABLE[static_cast<size_t>(opcode)];
218         if (defIdx != INVALID_REG_IDX) {
219             return regs[defIdx];
220         }
221         if (HasFlag(InstFlags::ACC_WRITE)) {
222             return Ins::ACCUMULATOR;
223         }
224         return {};
225     }
226 
IsValidToEmitIns227     bool IsValidToEmit() const
228     {
229         const auto invalidRegNum = 1U << MaxRegEncodingWidth();
230         for (auto reg : regs) {
231             if (reg >= invalidRegNum) {
232                 return false;
233             }
234         }
235         return true;
236     }
237 
HasDebugInfoIns238     bool HasDebugInfo() const
239     {
240         return insDebug.lineNumber != 0;
241     }
242 
243 private:
244     std::string OperandsToString(bool printArgs = false, size_t firstArgIdx = 0) const;
245     std::string RegsToString(bool &first, bool printArgs = false, size_t firstArgIdx = 0) const;
246     std::string ImmsToString(bool &first) const;
247     std::string IdsToString(bool &first) const;
248 
249     std::string IdToString(size_t idx, bool isFirst) const;
250     std::string ImmToString(size_t idx, bool isFirst) const;
251     std::string RegToString(size_t idx, bool isFirst, bool printArgs = false, size_t firstArgIdx = 0) const;
252 };
253 // NOLINTEND(misc-non-private-member-variables-in-classes)
254 
255 }  // namespace ark::pandasm
256 
257 #endif  // PANDA_ASSEMBLER_ASSEMBLY_INS_H
258