1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is part of the WebAssembly Disassembler Emitter. |
10 | // It contains the implementation of the disassembler tables. |
11 | // Documentation for the disassembler emitter in general can be found in |
12 | // WebAssemblyDisassemblerEmitter.h. |
13 | // |
14 | //===----------------------------------------------------------------------===// |
15 | |
16 | #include "WebAssemblyDisassemblerEmitter.h" |
17 | #include "Common/CodeGenInstruction.h" |
18 | #include "llvm/ADT/STLExtras.h" |
19 | #include "llvm/ADT/iterator.h" |
20 | #include "llvm/Support/raw_ostream.h" |
21 | #include "llvm/TableGen/Record.h" |
22 | |
23 | constexpr int WebAssemblyInstructionTableSize = 256; |
24 | |
25 | void llvm::emitWebAssemblyDisassemblerTables( |
26 | raw_ostream &OS, |
27 | ArrayRef<const CodeGenInstruction *> NumberedInstructions) { |
28 | // First lets organize all opcodes by (prefix) byte. Prefix 0 is the |
29 | // starting table. |
30 | std::map<unsigned, |
31 | std::map<unsigned, std::pair<unsigned, const CodeGenInstruction *>>> |
32 | OpcodeTable; |
33 | for (const auto &[Idx, CGI] : |
34 | enumerate(First: make_pointee_range(Range&: NumberedInstructions))) { |
35 | const Record &Def = *CGI.TheDef; |
36 | if (!Def.getValue(Name: "Inst" )) |
37 | continue; |
38 | const BitsInit &Inst = *Def.getValueAsBitsInit(FieldName: "Inst" ); |
39 | unsigned Opc = static_cast<unsigned>(*Inst.convertInitializerToInt()); |
40 | if (Opc == 0xFFFFFFFF) |
41 | continue; // No opcode defined. |
42 | assert(Opc <= 0xFFFFFF); |
43 | unsigned Prefix; |
44 | if (Opc <= 0xFFFF) { |
45 | Prefix = Opc >> 8; |
46 | Opc = Opc & 0xFF; |
47 | } else { |
48 | Prefix = Opc >> 16; |
49 | Opc = Opc & 0xFFFF; |
50 | } |
51 | auto &CGIP = OpcodeTable[Prefix][Opc]; |
52 | // All wasm instructions have a StackBased field of type string, we only |
53 | // want the instructions for which this is "true". |
54 | bool IsStackBased = Def.getValueAsBit(FieldName: "StackBased" ); |
55 | if (!IsStackBased) |
56 | continue; |
57 | if (CGIP.second) { |
58 | // We already have an instruction for this slot, so decide which one |
59 | // should be the canonical one. This determines which variant gets |
60 | // printed in a disassembly. We want e.g. "call" not "i32.call", and |
61 | // "end" when we don't know if its "end_loop" or "end_block" etc. |
62 | bool IsCanonicalExisting = |
63 | CGIP.second->TheDef->getValueAsBit(FieldName: "IsCanonical" ); |
64 | // We already have one marked explicitly as canonical, so keep it. |
65 | if (IsCanonicalExisting) |
66 | continue; |
67 | bool IsCanonicalNew = Def.getValueAsBit(FieldName: "IsCanonical" ); |
68 | // If the new one is explicitly marked as canonical, take it. |
69 | if (!IsCanonicalNew) { |
70 | // Neither the existing or new instruction is canonical. |
71 | // Pick the one with the shortest name as heuristic. |
72 | // Though ideally IsCanonical is always defined for at least one |
73 | // variant so this never has to apply. |
74 | if (CGIP.second->AsmString.size() <= CGI.AsmString.size()) |
75 | continue; |
76 | } |
77 | } |
78 | // Set this instruction as the one to use. |
79 | CGIP = {Idx, &CGI}; |
80 | } |
81 | |
82 | OS << R"( |
83 | #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" |
84 | |
85 | namespace { |
86 | enum EntryType : uint8_t { ET_Unused, ET_Prefix, ET_Instruction }; |
87 | |
88 | struct WebAssemblyInstruction { |
89 | uint16_t Opcode; |
90 | EntryType ET; |
91 | uint8_t NumOperands; |
92 | uint16_t OperandStart; |
93 | }; |
94 | } // end anonymous namespace |
95 | |
96 | )" ; |
97 | |
98 | std::vector<std::string> OperandTable, CurOperandList; |
99 | // Output one table per prefix. |
100 | for (const auto &[Prefix, Table] : OpcodeTable) { |
101 | if (Table.empty()) |
102 | continue; |
103 | OS << "constexpr WebAssemblyInstruction InstructionTable" << Prefix; |
104 | OS << "[] = {\n" ; |
105 | for (unsigned I = 0; I < WebAssemblyInstructionTableSize; I++) { |
106 | auto InstIt = Table.find(x: I); |
107 | if (InstIt != Table.end()) { |
108 | // Regular instruction. |
109 | assert(InstIt->second.second); |
110 | auto &CGI = *InstIt->second.second; |
111 | OS << " // 0x" ; |
112 | OS.write_hex(N: static_cast<unsigned long long>(I)); |
113 | OS << ": " << CGI.AsmString << "\n" ; |
114 | OS << " { " << InstIt->second.first << ", ET_Instruction, " ; |
115 | OS << CGI.Operands.OperandList.size() << ", " ; |
116 | // Collect operand types for storage in a shared list. |
117 | CurOperandList.clear(); |
118 | for (auto &Op : CGI.Operands.OperandList) { |
119 | assert(Op.OperandType != "MCOI::OPERAND_UNKNOWN" ); |
120 | CurOperandList.push_back(x: Op.OperandType); |
121 | } |
122 | // See if we already have stored this sequence before. This is not |
123 | // strictly necessary but makes the table really small. |
124 | auto SearchI = |
125 | std::search(first1: OperandTable.begin(), last1: OperandTable.end(), |
126 | first2: CurOperandList.begin(), last2: CurOperandList.end()); |
127 | OS << std::distance(first: OperandTable.begin(), last: SearchI); |
128 | // Store operands if no prior occurrence. |
129 | if (SearchI == OperandTable.end()) |
130 | llvm::append_range(C&: OperandTable, R&: CurOperandList); |
131 | } else { |
132 | auto PrefixIt = OpcodeTable.find(x: I); |
133 | // If we have a non-empty table for it that's not 0, this is a prefix. |
134 | if (PrefixIt != OpcodeTable.end() && I && !Prefix) |
135 | OS << " { 0, ET_Prefix, 0, 0" ; |
136 | else |
137 | OS << " { 0, ET_Unused, 0, 0" ; |
138 | } |
139 | OS << " },\n" ; |
140 | } |
141 | OS << "};\n\n" ; |
142 | } |
143 | // Create a table of all operands: |
144 | OS << "constexpr uint8_t OperandTable[] = {\n" ; |
145 | for (const auto &Op : OperandTable) |
146 | OS << " " << Op << ",\n" ; |
147 | OS << "};\n\n" ; |
148 | |
149 | // Create a table of all extension tables: |
150 | OS << R"( |
151 | constexpr struct { |
152 | uint8_t Prefix; |
153 | const WebAssemblyInstruction *Table; |
154 | } PrefixTable[] = { |
155 | )" ; |
156 | |
157 | for (const auto &[Prefix, Table] : OpcodeTable) { |
158 | if (Table.empty() || !Prefix) |
159 | continue; |
160 | OS << " { " << Prefix << ", InstructionTable" << Prefix << " },\n" ; |
161 | } |
162 | OS << "};\n" ; |
163 | } |
164 | |