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 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>, static_cast<size_t>(Opcode::NUM_OPCODES)> USE_IDXS_TABLE = { 87 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 OperandListLengthIns118 size_t OperandListLength() const 119 { 120 return regs.size() + ids.size() + imms.size(); 121 } 122 HasFlagIns123 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 CanThrowIns131 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 IsJumpIns137 bool IsJump() const 138 { 139 return HasFlag(InstFlags::JUMP); 140 } 141 IsConditionalJumpIns142 bool IsConditionalJump() const 143 { 144 return IsJump() && HasFlag(InstFlags::COND); 145 } 146 IsCallIns147 bool IsCall() const 148 { // Non-range call 149 return HasFlag(InstFlags::CALL); 150 } 151 IsCallRangeIns152 bool IsCallRange() const 153 { // Range call 154 return HasFlag(InstFlags::CALL_RANGE); 155 } 156 IsPseudoCallIns157 bool IsPseudoCall() const 158 { 159 return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL); 160 } 161 IsReturnIns162 bool IsReturn() const 163 { 164 return HasFlag(InstFlags::RETURN); 165 } 166 MaxRegEncodingWidthIns167 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 UsesIns175 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 DefIns200 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 IsValidToEmitIns215 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 HasDebugInfoIns226 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