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
32namespace panda::pandasm {
33
34enum 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
42enum 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
60constexpr int INVALID_REG_IDX = -1;
61
62constexpr size_t MAX_NUMBER_OF_SRC_REGS = 4;  // TODO(mbolshov): auto-generate
63
64constexpr 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,
71constexpr 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,
76constexpr 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,
81constexpr 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
86constexpr std::array<std::array<int, MAX_NUMBER_OF_SRC_REGS>,
87                     static_cast<size_t>(Opcode::NUM_OPCODES)> USE_IDXS_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
88// clang-format on
89#undef OPLIST
90
91struct 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
118    size_t OperandListLength() const
119    {
120        return regs.size() + ids.size() + imms.size();
121    }
122
123    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
131    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
137    bool IsJump() const
138    {
139        return HasFlag(InstFlags::JUMP);
140    }
141
142    bool IsConditionalJump() const
143    {
144        return IsJump() && HasFlag(InstFlags::COND);
145    }
146
147    bool IsCall() const
148    {  // Non-range call
149        return HasFlag(InstFlags::CALL);
150    }
151
152    bool IsCallRange() const
153    {  // Range call
154        return HasFlag(InstFlags::CALL_RANGE);
155    }
156
157    bool IsPseudoCall() const
158    {
159        return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL);
160    }
161
162    bool IsReturn() const
163    {
164        return HasFlag(InstFlags::RETURN);
165    }
166
167    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
175    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
200    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
215    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
226    bool HasDebugInfo() const
227    {
228        return ins_debug.line_number != 0;
229    }
230
231private:
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