% def get_node_kind(mnemonic) % return "#{mnemonic.gsub('.', '_').upcase}" % end % % def get_format_name(mnemonic) % return "#{mnemonic.gsub('.', '_').upcase}" + "_FORMATS" % end % % def insn2node(insn) % mnemonic = insn.mnemonic.split('.') % return mnemonic.map{|el| el == '64' ? 'Wide' : el.capitalize}.join() % end % % def is_VReg(name) % if name == :v % return true % end % end % % def is_Acc(name) % if name == :acc % return true % end % end % % def is_Imm(name) % if name == :imm % return true % end % end % % def is_Id(name) % if %i[method_id type_id field_id string_id stringId callsite_id literalarray_id].include?(name) % return true % end % end % % def get_operand_type(name, name_tmp, insn) % if is_VReg (name_tmp) % return 'uint16_t' % elsif is_Imm(name_tmp) % if insn.properties.include? 'jump' % return "std::string" % end % if insn.sig.include? 'imm:f64' % return 'double' % end % return 'int64_t' % elsif is_Id(name_tmp) % return 'std::string' % else % return nil % end % end % % def get_operands(sig) % return [] unless sig.include? ' ' % _, operands = sig.match(/(\S+) (.+)/).captures % operands = operands.split(', ') % end % % def get_ctor_args(insn) % operands = get_operands(insn.sig) % ops = Array.new % ctor_args = Array.new % imms = Array.new % imm_members = Array.new % ids = Array.new % id_members = Array.new % regs = Array.new % reg_members = Array.new % id_count = 0 % operands.map do |operand| % operand_parts = operand.split(':') % case operand_parts.size % when 1 % name = operand_parts[0] % when 2 % name, _ = operand_parts % when 3 % name, _, _ = operand_parts % else % raise 'Unexpected operand string' % end % % name_tmp = name.to_s.gsub(/[0-9]/, '').to_sym; % % if is_Id(name_tmp) % name = "stringId_" % name.concat(id_count.to_s) % id_count = id_count + 1 % end % type = get_operand_type(name, name_tmp, insn) % if type == "std::string" % ids.push(name) % id_members.push("#{type} #{name}") % elsif type == "uint16_t" % regs.push(name) % reg_members.push("#{type} #{name}") % else % imms.push(name) % imm_members.push("#{type} #{name}") % end % ctor_args.push("#{type} #{name}") % end % ops = regs + imms + ids % members = reg_members + imm_members + id_members % return ops,ctor_args,members,ids,imms,regs % end % /* * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Autogenerated file -- DO NOT EDIT! #ifndef ASSEMBLER_ASSEMBLY_INS_H #define ASSEMBLER_ASSEMBLY_INS_H #include #include #include #include #include #include #include "assembly-debug.h" #include "bytecode_emitter.h" #include "file_items.h" #include "isa.h" #include "lexer.h" namespace panda::pandasm { class Ins; using IType = std::variant; using InsPtr = std::unique_ptr; enum class Opcode { #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) opcode, PANDA_INSTRUCTION_LIST(OPLIST) #undef OPLIST INVALID, NUM_OPCODES = INVALID }; enum InstFlags { NONE = 0, JUMP = (1U << 0U), COND = (1U << 1U), CALL = (1U << 2U), RETURN = (1U << 3U), ACC_READ = (1U << 4U), ACC_WRITE = (1U << 5U), PSEUDO = (1U << 6U), THROWING = (1U << 7U), METHOD_ID = (1U << 8U), FIELD_ID = (1U << 9U), TYPE_ID = (1U << 10U), STRING_ID = (1U << 11U), LITERALARRAY_ID = (1U << 12U), CALL_RANGE = (1U << 13U), }; constexpr int INVALID_REG_IDX = -1; constexpr size_t MAX_NUMBER_OF_SRC_REGS = 4; // TODO(mbolshov): auto-generate constexpr InstFlags operator|(InstFlags a, InstFlags b) { using utype = std::underlying_type_t; return static_cast(static_cast(a) | static_cast(b)); } #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) flags, constexpr std::array(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = { PANDA_INSTRUCTION_LIST(OPLIST)}; #undef OPLIST #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) width, constexpr std::array(Opcode::NUM_OPCODES)> INST_WIDTH_TABLE = { PANDA_INSTRUCTION_LIST(OPLIST)}; #undef OPLIST #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) def_idx, constexpr std::array(Opcode::NUM_OPCODES)> DEF_IDX_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)}; #undef OPLIST #define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) use_idxs, // clang-format off constexpr std::array, static_cast(Opcode::NUM_OPCODES)> USE_IDXS_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)}; // clang-format on #undef OPLIST class Ins { public: std::string ToString(std::string endline = "", bool print_args = false, size_t first_arg_idx = 0) const; virtual std::string OpcodeToString() const { return ""; } bool HasFlag(InstFlags flag) const { if (opcode == Opcode::INVALID) { // TODO(mbolshov): introduce 'label' opcode for labels return false; } return (INST_FLAGS_TABLE[static_cast(opcode)] & flag) != 0; } bool CanThrow() const { return HasFlag(InstFlags::THROWING) || HasFlag(InstFlags::METHOD_ID) || HasFlag(InstFlags::FIELD_ID) || HasFlag(InstFlags::TYPE_ID) || HasFlag(InstFlags::STRING_ID); } bool IsJump() const { return HasFlag(InstFlags::JUMP); } bool IsConditionalJump() const { return IsJump() && HasFlag(InstFlags::COND); } bool IsCall() const { // Non-range call return HasFlag(InstFlags::CALL); } bool IsCallRange() const { // Range call return HasFlag(InstFlags::CALL_RANGE); } bool IsPseudoCall() const { return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL); } bool IsReturn() const { return HasFlag(InstFlags::RETURN); } size_t MaxRegEncodingWidth() const { if (opcode == Opcode::INVALID) { return 0; } return INST_WIDTH_TABLE[static_cast(opcode)]; } size_t OperandListLength() const { return Regs().size() + Ids().size() + Imms().size(); } bool IsValidToEmit() const { const auto INVALID_REG_NUM = 1U << MaxRegEncodingWidth(); for (auto reg : Regs()) { if (reg >= INVALID_REG_NUM) { return false; } } return true; } bool HasDebugInfo() const { return ins_debug.line_number != 0; } virtual std::vector Ids() const { return {}; } virtual std::vector Regs() const { return {}; } virtual std::vector Imms() const { return {}; } virtual bool IsLabel() const { return false; } virtual const std::string &Label() const { return EMPTY_STRING; } % max_reg_cnt = 0 % max_imm_cnt = 0 % max_id_cnt = 0 % Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| % insn = group.first % ops_list,ctor_arg_list,member_list,id_list,imm_list,reg_list = get_ctor_args(insn) % max_reg_cnt = max_reg_cnt > reg_list.length ? max_reg_cnt : reg_list.length % max_imm_cnt = max_imm_cnt > imm_list.length ? max_imm_cnt : imm_list.length % max_id_cnt = max_id_cnt > id_list.length ? max_id_cnt : id_list.length % end std::string &GetId(size_t idx) { switch(idx) { % max_id_cnt.times do |i| case <%= i %>: return GetId<%= i %>(); % end default: UNREACHABLE(); } } const std::string &GetId(size_t idx) const { switch(idx) { % max_id_cnt.times do |i| case <%= i %>: return GetId<%= i %>(); % end default: UNREACHABLE(); } } void SetId(size_t idx, const std::string &value) { switch(idx) { % max_id_cnt.times do |i| case <%= i %>: return SetId<%= i %>(value); % end default: UNREACHABLE(); } } std::uint16_t GetReg(size_t idx) const { switch(idx) { % max_reg_cnt.times do |i| case <%= i %>: return GetReg<%= i %>(); % end default: UNREACHABLE(); } } void SetReg(size_t idx, const uint16_t value) { switch(idx) { % max_reg_cnt.times do |i| case <%= i %>: return SetReg<%= i %>(value); % end default: UNREACHABLE(); } } IType GetImm(size_t idx) const { switch(idx) { % max_imm_cnt.times do |i| case <%= i %>: return GetImm<%= i %>(); % end default: UNREACHABLE(); } } void SetImm(size_t idx, const IType &value) { switch(idx) { % max_imm_cnt.times do |i| case <%= i %>: return SetImm<%= i %>(value); % end default: UNREACHABLE(); } } static bool Emit(BytecodeEmitter &emitter, const InsPtr &ins, panda_file::MethodItem *method, const std::unordered_map &methods, const std::unordered_map &fields, const std::unordered_map &classes, const std::unordered_map &strings, const std::unordered_map &literalarrays, const std::unordered_map& labels); Ins *DeepCopy() { ASSERT(!IsLabel()); auto res = Ins::CreateIns(opcode, Regs(), Imms(), Ids()); res->ins_debug = ins_debug; return res; } template static Ins *CreateIns(Opcode opcode, Args &&... args); virtual ~Ins() = default; debuginfo::Ins ins_debug; Opcode opcode; protected: explicit Ins(Opcode opcode) : opcode(opcode) {} private: % max_reg_cnt.times do |i| virtual void SetReg<%= i %>(const uint16_t reg) { UNREACHABLE(); } virtual uint16_t GetReg<%= i %>() const { UNREACHABLE(); } % end % % max_imm_cnt.times do |i| virtual void SetImm<%= i %>(const IType &imm) { UNREACHABLE(); } virtual IType GetImm<%= i %>() const { UNREACHABLE(); } % end % % max_id_cnt.times do |i| virtual void SetId<%= i %>(const std::string &id) { UNREACHABLE(); } virtual std::string &GetId<%= i %>() { UNREACHABLE(); } virtual const std::string &GetId<%= i %>() const { UNREACHABLE(); } % end static const std::string EMPTY_STRING; std::string OperandsToString(bool print_args = false, size_t first_arg_idx = 0) const; std::string RegsToString(bool &first, bool print_args = false, size_t first_arg_idx = 0) const; std::string ImmsToString(bool &first) const; std::string IdsToString(bool &first) const; std::string IdToString(size_t idx, bool is_first) const; std::string ImmToString(size_t idx, bool is_first) const; std::string RegToString(size_t idx, bool is_first, bool print_args = false, size_t first_arg_idx = 0) const; }; class LabelIns : public Ins { public: LabelIns(std::string label) : Ins(Opcode::INVALID), label_(label) {} bool IsLabel() const override { return true; } const std::string &Label() const override { return label_; } std::string OpcodeToString() const override { return label_ + ": "; } private: std::string label_; }; % Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| % insn = group.first % node_kind = get_node_kind(mnemonic) % class_name = insn2node(insn) % base_class = "Ins" % ops_list,ctor_arg_list,member_list,id_list,imm_list,reg_list = get_ctor_args(insn) % ctor_args = ctor_arg_list.map {|arg| "#{arg}"}.join(", ") % members = member_list.map {|member| "#{member}_;"}.join("\n ") % ids = id_list.map{|id| "#{id}_"}.join(", ") % imms = imm_list.map{|imm| "#{imm}_"}.join(", ") % regs = reg_list.map{|reg| "#{reg}_"}.join(", ") % id_index = imm_index = reg_index = -1 % init = "" % if id_list.length != 0 % init += "\n " + id_list.map {|id| id_index+=1; "#{id}_ = ids[#{id_index}];"}.join("\n ") % end % if imm_list.length != 0 % init += "\n " + imm_list.map {|imm| imm_index+=1; "#{imm}_ = std::holds_alternative(imms[#{imm_index}]) ? std::get(imms[#{imm_index}]) : std::get(imms[#{imm_index}]);"}.join("\n ") % end % if reg_list.length != 0 % init += "\n " + reg_list.map {|reg| reg_index+=1; "#{reg}_ = regs[#{reg_index}];"}.join("\n ") % end % ops = (ops_list.length == 0 ? "" : ", ") + ops_list.map { |op| "#{op}_(#{op})"}.join(", ") class <%= class_name %> : public <%= base_class %> { public: <%= class_name %>(<%= ctor_args %>) : <%= base_class %>(Opcode::<%= insn.asm_token %>)<%= ops %> {} <%= class_name %>(std::vector ®s, std::vector &imms, std::vector &ids) : <%= base_class %>(Opcode::<%= insn.asm_token %>) { ASSERT(regs.size() >= <%= reg_index + 1 %>); ASSERT(ids.size() >= <%= id_index + 1 %>); ASSERT(imms.size() >= <%= imm_index + 1 %>); <%= init %> } <%= class_name %>(std::vector &®s, std::vector &&imms, std::vector &&ids) : <%= base_class %>(Opcode::<%= insn.asm_token %>) { ASSERT(regs.size() >= <%= reg_index + 1 %>); ASSERT(ids.size() >= <%= id_index + 1 %>); ASSERT(imms.size() >= <%= imm_index + 1 %>); <%= init %> } std::string OpcodeToString() const override { return "<%= insn.mnemonic%>"; } std::vector Regs() const override { return {<%= regs %>}; } std::vector Imms() const override { return {<%= imms %>}; } std::vector Ids() const override { return {<%=ids %>}; } % if ops_list.length != 0 private: <%= members %> % idx = 0 % reg_list.each do |reg| void SetReg<%= idx %>(const uint16_t reg) override { <%= reg %>_ = reg; } std::uint16_t GetReg<%= idx %>() const override { return <%= reg %>_; } % idx += 1 % end % % idx = 0 % imm_list.each do |imm| void SetImm<%= idx %>(const IType &imm) override { <%= imm %>_ = std::holds_alternative(imm) ? std::get(imm) : std::get(imm); } IType GetImm<%= idx %>() const override { return <%= imm %>_; } % idx += 1 % end % % idx = 0 % id_list.each do |id| void SetId<%= idx %>(const std::string &id) override { <%= id %>_ = id; } std::string &GetId<%= idx %>() override { return <%= id %>_; } const std::string &GetId<%= idx %>() const override { return <%= id %>_; } % idx += 1 % end % end }; % end template Ins *Ins::CreateIns(Opcode opcode, Args &&... args) { switch(opcode) { % Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| % insn = group.first % class_name = insn2node(insn) case Opcode::<%= insn.asm_token %>: { return new <%= class_name %>(std::forward(args)...); } % end default: UNREACHABLE(); } } } // namespace panda::pandasm #endif // ASSEMBLER_ASSEMBLY_INS_H