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