1 /**
2 * Copyright (c) 2021-2022 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 ASSEMBLER_ASSEMBLY_INS_H
17 #define 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 panda::pandasm {
33
34 enum class Opcode {
35 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) opcode,
36 PANDA_INSTRUCTION_LIST(OPLIST)
37 #undef OPLIST
38 INVALID,
39 NUM_OPCODES = INVALID
40 };
41
42 enum InstFlags {
43 NONE = 0,
44 JUMP = (1U << 0U),
45 COND = (1U << 1U),
46 CALL = (1U << 2U),
47 RETURN = (1U << 3U),
48 ACC_READ = (1U << 4U),
49 ACC_WRITE = (1U << 5U),
50 PSEUDO = (1U << 6U),
51 THROWING = (1U << 7U),
52 METHOD_ID = (1U << 8U),
53 FIELD_ID = (1U << 9U),
54 TYPE_ID = (1U << 10U),
55 STRING_ID = (1U << 11U),
56 LITERALARRAY_ID = (1U << 12U),
57 CALL_RANGE = (1U << 13U)
58 };
59
60 constexpr int INVALID_REG_IDX = -1;
61
62 constexpr size_t MAX_NUMBER_OF_SRC_REGS = 4; // TODO(mbolshov): auto-generate
63
operator |(InstFlags a, InstFlags b)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 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) 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 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) width,
76 constexpr std::array<size_t, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_WIDTH_TABLE = {
77 PANDA_INSTRUCTION_LIST(OPLIST)};
78 #undef OPLIST
79
80 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) def_idx,
81 constexpr std::array<int, static_cast<size_t>(Opcode::NUM_OPCODES)> DEF_IDX_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
82 #undef OPLIST
83
84 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) use_idxs,
85 // clang-format off
86 constexpr std::array<std::array<int, MAX_NUMBER_OF_SRC_REGS>,
87 static_cast<size_t>(Opcode::NUM_OPCODES)> USE_IDXS_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
88 // clang-format on
89 #undef OPLIST
90
91 struct Ins {
92 using IType = std::variant<int64_t, double>;
93
94 constexpr static uint16_t ACCUMULATOR = -1;
95 constexpr static size_t MAX_CALL_SHORT_ARGS = 2;
96 constexpr static size_t MAX_CALL_ARGS = 4;
97 constexpr static uint16_t MAX_NON_RANGE_CALL_REG = 15;
98 constexpr static uint16_t MAX_RANGE_CALL_START_REG = 255;
99
100 Opcode opcode = Opcode::INVALID; /* operation type */
101 std::vector<uint16_t> regs; /* list of arguments - registers */
102 std::vector<std::string> ids; /* list of arguments - identifiers */
103 std::vector<IType> imms; /* list of arguments - immediates */
104 std::string label; /* label at the beginning of a line */
105 bool set_label = false; /* whether this label is defined */
106 debuginfo::Ins ins_debug;
107
108 std::string ToString(std::string endline = "", bool print_args = false, size_t first_arg_idx = 0) const;
109
110 bool Emit(BytecodeEmitter &emitter, panda_file::MethodItem *method,
111 const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods,
112 const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
113 const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
114 const std::unordered_map<std::string, panda_file::StringItem *> &strings,
115 const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays,
116 const std::unordered_map<std::string_view, panda::Label> &labels) const;
117
OperandListLengthpanda::pandasm::Opcode::Ins118 size_t OperandListLength() const
119 {
120 return regs.size() + ids.size() + imms.size();
121 }
122
HasFlagpanda::pandasm::Opcode::Ins123 bool HasFlag(InstFlags flag) const
124 {
125 if (opcode == Opcode::INVALID) { // TODO(mbolshov): introduce 'label' opcode for labels
126 return false;
127 }
128 return (INST_FLAGS_TABLE[static_cast<size_t>(opcode)] & flag) != 0;
129 }
130
CanThrowpanda::pandasm::Opcode::Ins131 bool CanThrow() const
132 {
133 return HasFlag(InstFlags::THROWING) || HasFlag(InstFlags::METHOD_ID) || HasFlag(InstFlags::FIELD_ID) ||
134 HasFlag(InstFlags::TYPE_ID) || HasFlag(InstFlags::STRING_ID);
135 }
136
IsJumppanda::pandasm::Opcode::Ins137 bool IsJump() const
138 {
139 return HasFlag(InstFlags::JUMP);
140 }
141
IsConditionalJumppanda::pandasm::Opcode::Ins142 bool IsConditionalJump() const
143 {
144 return IsJump() && HasFlag(InstFlags::COND);
145 }
146
IsCallpanda::pandasm::Opcode::Ins147 bool IsCall() const
148 { // Non-range call
149 return HasFlag(InstFlags::CALL);
150 }
151
IsCallRangepanda::pandasm::Opcode::Ins152 bool IsCallRange() const
153 { // Range call
154 return HasFlag(InstFlags::CALL_RANGE);
155 }
156
IsPseudoCallpanda::pandasm::Opcode::Ins157 bool IsPseudoCall() const
158 {
159 return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL);
160 }
161
IsReturnpanda::pandasm::Opcode::Ins162 bool IsReturn() const
163 {
164 return HasFlag(InstFlags::RETURN);
165 }
166
MaxRegEncodingWidthpanda::pandasm::Opcode::Ins167 size_t MaxRegEncodingWidth() const
168 {
169 if (opcode == Opcode::INVALID) {
170 return 0;
171 }
172 return INST_WIDTH_TABLE[static_cast<size_t>(opcode)];
173 }
174
Usespanda::pandasm::Opcode::Ins175 std::vector<uint16_t> Uses() const
176 {
177 if (IsPseudoCall()) {
178 return regs;
179 }
180
181 if (opcode == Opcode::INVALID) {
182 return {};
183 }
184
185 auto use_idxs = USE_IDXS_TABLE[static_cast<size_t>(opcode)];
186 std::vector<uint16_t> res(MAX_NUMBER_OF_SRC_REGS + 1);
187 if (HasFlag(InstFlags::ACC_READ)) {
188 res.push_back(Ins::ACCUMULATOR);
189 }
190 for (auto idx : use_idxs) {
191 if (idx == INVALID_REG_IDX) {
192 break;
193 }
194 ASSERT(static_cast<size_t>(idx) < regs.size());
195 res.emplace_back(regs[idx]);
196 }
197 return res;
198 }
199
Defpanda::pandasm::Opcode::Ins200 std::optional<size_t> Def() const
201 {
202 if (opcode == Opcode::INVALID) {
203 return {};
204 }
205 auto def_idx = DEF_IDX_TABLE[static_cast<size_t>(opcode)];
206 if (def_idx != INVALID_REG_IDX) {
207 return regs[def_idx];
208 }
209 if (HasFlag(InstFlags::ACC_WRITE)) {
210 return Ins::ACCUMULATOR;
211 }
212 return {};
213 }
214
IsValidToEmitpanda::pandasm::Opcode::Ins215 bool IsValidToEmit() const
216 {
217 const auto INVALID_REG_NUM = 1U << MaxRegEncodingWidth();
218 for (auto reg : regs) {
219 if (reg >= INVALID_REG_NUM) {
220 return false;
221 }
222 }
223 return true;
224 }
225
HasDebugInfopanda::pandasm::Opcode::Ins226 bool HasDebugInfo() const
227 {
228 return ins_debug.line_number != 0;
229 }
230
231 private:
232 std::string OperandsToString(bool print_args = false, size_t first_arg_idx = 0) const;
233 std::string RegsToString(bool &first, bool print_args = false, size_t first_arg_idx = 0) const;
234 std::string ImmsToString(bool &first) const;
235 std::string IdsToString(bool &first) const;
236
237 std::string IdToString(size_t idx, bool is_first) const;
238 std::string ImmToString(size_t idx, bool is_first) const;
239 std::string RegToString(size_t idx, bool is_first, bool print_args = false, size_t first_arg_idx = 0) const;
240 };
241 } // namespace panda::pandasm
242
243 #endif // ASSEMBLER_ASSEMBLY_INS_H
244