| 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 | |