1 /* 2 * Copyright (c) 2021-2025 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 STATIC_FIELD_ID = (1U << 14U), 61 STATIC_METHOD_ID = (1U << 15U) 62 }; 63 64 constexpr int INVALID_REG_IDX = -1; 65 66 constexpr InstFlags operator|(InstFlags a, InstFlags b) 67 { 68 using Utype = std::underlying_type_t<InstFlags>; 69 return static_cast<InstFlags>(static_cast<Utype>(a) | static_cast<Utype>(b)); 70 } 71 72 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 73 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (flags), 74 constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = { 75 PANDA_INSTRUCTION_LIST(OPLIST)}; 76 #undef OPLIST 77 78 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 79 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (width), 80 constexpr std::array<size_t, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_WIDTH_TABLE = { 81 PANDA_INSTRUCTION_LIST(OPLIST)}; 82 #undef OPLIST 83 84 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 85 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (def_idx), 86 constexpr std::array<int, static_cast<size_t>(Opcode::NUM_OPCODES)> DEF_IDX_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)}; 87 #undef OPLIST 88 89 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 90 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (use_idxs), 91 constexpr std::array<std::array<int, MAX_NUMBER_OF_SRC_REGS>, static_cast<size_t>(Opcode::NUM_OPCODES)> USE_IDXS_TABLE = 92 {PANDA_INSTRUCTION_LIST(OPLIST)}; // CC-OFF(G.FMT.03) any style changes will worsen readability 93 #undef OPLIST 94 95 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 96 #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) (prof_size), 97 // clang-format off 98 constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES) + 1> INST_PROFILE_SIZES = 99 {PANDA_INSTRUCTION_LIST(OPLIST) 0}; // CC-OFF(G.FMT.03) any style changes will worsen readability 100 // clang-format on 101 #undef OPLIST 102 103 // NOLINTBEGIN(misc-non-private-member-variables-in-classes) 104 struct Ins { 105 using IType = std::variant<int64_t, double>; 106 107 constexpr static uint16_t ACCUMULATOR = -1; 108 constexpr static size_t MAX_CALL_SHORT_ARGS = 2; 109 constexpr static size_t MAX_CALL_ARGS = 4; 110 constexpr static uint16_t MAX_NON_RANGE_CALL_REG = 15; 111 constexpr static uint16_t MAX_RANGE_CALL_START_REG = 255; 112 113 Opcode opcode = Opcode::INVALID; /* operation type */ 114 std::vector<uint16_t> regs; /* list of arguments - registers */ 115 std::vector<std::string> ids; /* list of arguments - identifiers */ 116 std::vector<IType> imms; /* list of arguments - immediates */ 117 std::string label; /* label at the beginning of a line */ 118 bool setLabel = false; /* whether this label is defined */ 119 debuginfo::Ins insDebug; 120 uint16_t profileId {0}; /* Index in the profile vector */ 121 122 PANDA_PUBLIC_API std::string ToString(const std::string &endline = "", bool printArgs = false, 123 size_t firstArgIdx = 0) const; 124 125 // CC-OFFNXT(G.FUN.01-CPP) solid logic 126 bool Emit(BytecodeEmitter &emitter, panda_file::MethodItem *method, 127 const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods, 128 const std::unordered_map<std::string, panda_file::BaseMethodItem *> &staticMethods, 129 const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields, 130 const std::unordered_map<std::string, panda_file::BaseFieldItem *> &staticFields, 131 const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes, 132 const std::unordered_map<std::string_view, panda_file::StringItem *> &strings, 133 const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays, 134 const std::unordered_map<std::string_view, ark::Label> &labels) const; 135 OperandListLengthIns136 size_t OperandListLength() const 137 { 138 return regs.size() + ids.size() + imms.size(); 139 } 140 HasFlagIns141 bool HasFlag(InstFlags flag) const 142 { 143 if (opcode == Opcode::INVALID) { // NOTE(mbolshov): introduce 'label' opcode for labels 144 return false; 145 } 146 return (INST_FLAGS_TABLE[static_cast<size_t>(opcode)] & flag) != 0; 147 } 148 CanThrowIns149 bool CanThrow() const 150 { 151 return HasFlag(InstFlags::THROWING) || HasFlag(InstFlags::METHOD_ID) || HasFlag(InstFlags::STATIC_METHOD_ID) || 152 HasFlag(InstFlags::FIELD_ID) || HasFlag(InstFlags::STATIC_FIELD_ID) || HasFlag(InstFlags::TYPE_ID) || 153 HasFlag(InstFlags::STRING_ID); 154 } 155 IsJumpIns156 bool IsJump() const 157 { 158 return HasFlag(InstFlags::JUMP); 159 } 160 IsConditionalJumpIns161 bool IsConditionalJump() const 162 { 163 return IsJump() && HasFlag(InstFlags::COND); 164 } 165 IsCallIns166 bool IsCall() const 167 { // Non-range call 168 return HasFlag(InstFlags::CALL); 169 } 170 IsCallRangeIns171 bool IsCallRange() const 172 { // Range call 173 return HasFlag(InstFlags::CALL_RANGE); 174 } 175 IsPseudoCallIns176 bool IsPseudoCall() const 177 { 178 return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL); 179 } 180 IsReturnIns181 bool IsReturn() const 182 { 183 return HasFlag(InstFlags::RETURN); 184 } 185 MaxRegEncodingWidthIns186 size_t MaxRegEncodingWidth() const 187 { 188 if (opcode == Opcode::INVALID) { 189 return 0; 190 } 191 return INST_WIDTH_TABLE[static_cast<size_t>(opcode)]; 192 } 193 UsesIns194 std::vector<uint16_t> Uses() const 195 { 196 if (IsPseudoCall()) { 197 return regs; 198 } 199 200 if (opcode == Opcode::INVALID) { 201 return {}; 202 } 203 204 auto useIdxs = USE_IDXS_TABLE[static_cast<size_t>(opcode)]; 205 std::vector<uint16_t> res(MAX_NUMBER_OF_SRC_REGS + 1); 206 if (HasFlag(InstFlags::ACC_READ)) { 207 res.push_back(Ins::ACCUMULATOR); 208 } 209 for (auto idx : useIdxs) { 210 if (idx == INVALID_REG_IDX) { 211 break; 212 } 213 ASSERT(static_cast<size_t>(idx) < regs.size()); 214 res.emplace_back(regs[idx]); 215 } 216 return res; 217 } 218 DefIns219 std::optional<size_t> Def() const 220 { 221 if (opcode == Opcode::INVALID) { 222 return {}; 223 } 224 auto defIdx = DEF_IDX_TABLE[static_cast<size_t>(opcode)]; 225 if (defIdx != INVALID_REG_IDX) { 226 return regs[defIdx]; 227 } 228 if (HasFlag(InstFlags::ACC_WRITE)) { 229 return Ins::ACCUMULATOR; 230 } 231 return {}; 232 } 233 IsValidToEmitIns234 bool IsValidToEmit() const 235 { 236 const auto invalidRegNum = 1U << MaxRegEncodingWidth(); 237 for (auto reg : regs) { 238 if (reg >= invalidRegNum) { 239 return false; 240 } 241 } 242 return true; 243 } 244 HasDebugInfoIns245 bool HasDebugInfo() const 246 { 247 return insDebug.lineNumber != 0; 248 } 249 250 private: 251 std::string OperandsToString(bool printArgs = false, size_t firstArgIdx = 0) const; 252 std::string RegsToString(bool &first, bool printArgs = false, size_t firstArgIdx = 0) const; 253 std::string ImmsToString(bool &first) const; 254 std::string IdsToString(bool &first) const; 255 256 std::string IdToString(size_t idx, bool isFirst) const; 257 std::string ImmToString(size_t idx, bool isFirst) const; 258 std::string RegToString(size_t idx, bool isFirst, bool printArgs = false, size_t firstArgIdx = 0) const; 259 }; 260 // NOLINTEND(misc-non-private-member-variables-in-classes) 261 262 } // namespace ark::pandasm 263 264 #endif // PANDA_ASSEMBLER_ASSEMBLY_INS_H 265