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