1 | //===- WebAssemblyDisassemblerEmitter.cpp - Disassembler tables -*- C++ -*-===// |
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/Support/raw_ostream.h" |
20 | #include "llvm/TableGen/Record.h" |
21 | |
22 | namespace llvm { |
23 | |
24 | static constexpr int WebAssemblyInstructionTableSize = 256; |
25 | |
26 | void emitWebAssemblyDisassemblerTables( |
27 | raw_ostream &OS, |
28 | const ArrayRef<const CodeGenInstruction *> &NumberedInstructions) { |
29 | // First lets organize all opcodes by (prefix) byte. Prefix 0 is the |
30 | // starting table. |
31 | std::map<unsigned, |
32 | std::map<unsigned, std::pair<unsigned, const CodeGenInstruction *>>> |
33 | OpcodeTable; |
34 | for (unsigned I = 0; I != NumberedInstructions.size(); ++I) { |
35 | auto &CGI = *NumberedInstructions[I]; |
36 | auto &Def = *CGI.TheDef; |
37 | if (!Def.getValue(Name: "Inst" )) |
38 | continue; |
39 | auto &Inst = *Def.getValueAsBitsInit(FieldName: "Inst" ); |
40 | RecordKeeper &RK = Inst.getRecordKeeper(); |
41 | unsigned Opc = static_cast<unsigned>( |
42 | cast<IntInit>(Val: Inst.convertInitializerTo(Ty: IntRecTy::get(RK))) |
43 | ->getValue()); |
44 | if (Opc == 0xFFFFFFFF) |
45 | continue; // No opcode defined. |
46 | assert(Opc <= 0xFFFFFF); |
47 | unsigned Prefix; |
48 | if (Opc <= 0xFFFF) { |
49 | Prefix = Opc >> 8; |
50 | Opc = Opc & 0xFF; |
51 | } else { |
52 | Prefix = Opc >> 16; |
53 | Opc = Opc & 0xFFFF; |
54 | } |
55 | auto &CGIP = OpcodeTable[Prefix][Opc]; |
56 | // All wasm instructions have a StackBased field of type string, we only |
57 | // want the instructions for which this is "true". |
58 | bool IsStackBased = Def.getValueAsBit(FieldName: "StackBased" ); |
59 | if (!IsStackBased) |
60 | continue; |
61 | if (CGIP.second) { |
62 | // We already have an instruction for this slot, so decide which one |
63 | // should be the canonical one. This determines which variant gets |
64 | // printed in a disassembly. We want e.g. "call" not "i32.call", and |
65 | // "end" when we don't know if its "end_loop" or "end_block" etc. |
66 | bool IsCanonicalExisting = |
67 | CGIP.second->TheDef->getValueAsBit(FieldName: "IsCanonical" ); |
68 | // We already have one marked explicitly as canonical, so keep it. |
69 | if (IsCanonicalExisting) |
70 | continue; |
71 | bool IsCanonicalNew = Def.getValueAsBit(FieldName: "IsCanonical" ); |
72 | // If the new one is explicitly marked as canonical, take it. |
73 | if (!IsCanonicalNew) { |
74 | // Neither the existing or new instruction is canonical. |
75 | // Pick the one with the shortest name as heuristic. |
76 | // Though ideally IsCanonical is always defined for at least one |
77 | // variant so this never has to apply. |
78 | if (CGIP.second->AsmString.size() <= CGI.AsmString.size()) |
79 | continue; |
80 | } |
81 | } |
82 | // Set this instruction as the one to use. |
83 | CGIP = std::pair(I, &CGI); |
84 | } |
85 | OS << "#include \"MCTargetDesc/WebAssemblyMCTargetDesc.h\"\n" ; |
86 | OS << "\n" ; |
87 | OS << "namespace llvm {\n\n" ; |
88 | OS << "static constexpr int WebAssemblyInstructionTableSize = " ; |
89 | OS << WebAssemblyInstructionTableSize << ";\n\n" ; |
90 | OS << "enum EntryType : uint8_t { " ; |
91 | OS << "ET_Unused, ET_Prefix, ET_Instruction };\n\n" ; |
92 | OS << "struct WebAssemblyInstruction {\n" ; |
93 | OS << " uint16_t Opcode;\n" ; |
94 | OS << " EntryType ET;\n" ; |
95 | OS << " uint8_t NumOperands;\n" ; |
96 | OS << " uint16_t OperandStart;\n" ; |
97 | OS << "};\n\n" ; |
98 | std::vector<std::string> OperandTable, CurOperandList; |
99 | // Output one table per prefix. |
100 | for (auto &PrefixPair : OpcodeTable) { |
101 | if (PrefixPair.second.empty()) |
102 | continue; |
103 | OS << "WebAssemblyInstruction InstructionTable" << PrefixPair.first; |
104 | OS << "[] = {\n" ; |
105 | for (unsigned I = 0; I < WebAssemblyInstructionTableSize; I++) { |
106 | auto InstIt = PrefixPair.second.find(x: I); |
107 | if (InstIt != PrefixPair.second.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 | size_t OperandStart = OperandTable.size(); |
125 | if (CurOperandList.size() <= OperandTable.size()) { |
126 | for (size_t J = 0; J <= OperandTable.size() - CurOperandList.size(); |
127 | ++J) { |
128 | size_t K = 0; |
129 | for (; K < CurOperandList.size(); ++K) { |
130 | if (OperandTable[J + K] != CurOperandList[K]) |
131 | break; |
132 | } |
133 | if (K == CurOperandList.size()) { |
134 | OperandStart = J; |
135 | break; |
136 | } |
137 | } |
138 | } |
139 | // Store operands if no prior occurrence. |
140 | if (OperandStart == OperandTable.size()) { |
141 | llvm::append_range(C&: OperandTable, R&: CurOperandList); |
142 | } |
143 | OS << OperandStart; |
144 | } else { |
145 | auto PrefixIt = OpcodeTable.find(x: I); |
146 | // If we have a non-empty table for it that's not 0, this is a prefix. |
147 | if (PrefixIt != OpcodeTable.end() && I && !PrefixPair.first) { |
148 | OS << " { 0, ET_Prefix, 0, 0" ; |
149 | } else { |
150 | OS << " { 0, ET_Unused, 0, 0" ; |
151 | } |
152 | } |
153 | OS << " },\n" ; |
154 | } |
155 | OS << "};\n\n" ; |
156 | } |
157 | // Create a table of all operands: |
158 | OS << "const uint8_t OperandTable[] = {\n" ; |
159 | for (auto &Op : OperandTable) { |
160 | OS << " " << Op << ",\n" ; |
161 | } |
162 | OS << "};\n\n" ; |
163 | // Create a table of all extension tables: |
164 | OS << "struct { uint8_t Prefix; const WebAssemblyInstruction *Table; }\n" ; |
165 | OS << "PrefixTable[] = {\n" ; |
166 | for (auto &PrefixPair : OpcodeTable) { |
167 | if (PrefixPair.second.empty() || !PrefixPair.first) |
168 | continue; |
169 | OS << " { " << PrefixPair.first << ", InstructionTable" |
170 | << PrefixPair.first; |
171 | OS << " },\n" ; |
172 | } |
173 | OS << " { 0, nullptr }\n};\n\n" ; |
174 | OS << "} // end namespace llvm\n" ; |
175 | } |
176 | |
177 | } // namespace llvm |
178 | |