14ba319b5SDimitry Andric //===- WebAssemblyDisassemblerEmitter.cpp - Disassembler tables -*- C++ -*-===//
24ba319b5SDimitry Andric //
34ba319b5SDimitry Andric //                     The LLVM Compiler Infrastructure
44ba319b5SDimitry Andric //
54ba319b5SDimitry Andric // This file is distributed under the University of Illinois Open Source
64ba319b5SDimitry Andric // License. See LICENSE.TXT for details.
74ba319b5SDimitry Andric //
84ba319b5SDimitry Andric //===----------------------------------------------------------------------===//
94ba319b5SDimitry Andric //
104ba319b5SDimitry Andric // This file is part of the WebAssembly Disassembler Emitter.
114ba319b5SDimitry Andric // It contains the implementation of the disassembler tables.
124ba319b5SDimitry Andric // Documentation for the disassembler emitter in general can be found in
134ba319b5SDimitry Andric // WebAssemblyDisassemblerEmitter.h.
144ba319b5SDimitry Andric //
154ba319b5SDimitry Andric //===----------------------------------------------------------------------===//
164ba319b5SDimitry Andric 
174ba319b5SDimitry Andric #include "WebAssemblyDisassemblerEmitter.h"
184ba319b5SDimitry Andric #include "llvm/TableGen/Record.h"
194ba319b5SDimitry Andric 
204ba319b5SDimitry Andric namespace llvm {
214ba319b5SDimitry Andric 
22*b5893f02SDimitry Andric static constexpr int WebAssemblyInstructionTableSize = 256;
23*b5893f02SDimitry Andric 
emitWebAssemblyDisassemblerTables(raw_ostream & OS,const ArrayRef<const CodeGenInstruction * > & NumberedInstructions)244ba319b5SDimitry Andric void emitWebAssemblyDisassemblerTables(
254ba319b5SDimitry Andric     raw_ostream &OS,
264ba319b5SDimitry Andric     const ArrayRef<const CodeGenInstruction *> &NumberedInstructions) {
274ba319b5SDimitry Andric   // First lets organize all opcodes by (prefix) byte. Prefix 0 is the
284ba319b5SDimitry Andric   // starting table.
294ba319b5SDimitry Andric   std::map<unsigned,
304ba319b5SDimitry Andric            std::map<unsigned, std::pair<unsigned, const CodeGenInstruction *>>>
314ba319b5SDimitry Andric       OpcodeTable;
324ba319b5SDimitry Andric   for (unsigned I = 0; I != NumberedInstructions.size(); ++I) {
334ba319b5SDimitry Andric     auto &CGI = *NumberedInstructions[I];
344ba319b5SDimitry Andric     auto &Def = *CGI.TheDef;
354ba319b5SDimitry Andric     if (!Def.getValue("Inst"))
364ba319b5SDimitry Andric       continue;
374ba319b5SDimitry Andric     auto &Inst = *Def.getValueAsBitsInit("Inst");
384ba319b5SDimitry Andric     auto Opc = static_cast<unsigned>(
394ba319b5SDimitry Andric         reinterpret_cast<IntInit *>(Inst.convertInitializerTo(IntRecTy::get()))
404ba319b5SDimitry Andric             ->getValue());
414ba319b5SDimitry Andric     if (Opc == 0xFFFFFFFF)
424ba319b5SDimitry Andric       continue; // No opcode defined.
434ba319b5SDimitry Andric     assert(Opc <= 0xFFFF);
444ba319b5SDimitry Andric     auto Prefix = Opc >> 8;
454ba319b5SDimitry Andric     Opc = Opc & 0xFF;
464ba319b5SDimitry Andric     auto &CGIP = OpcodeTable[Prefix][Opc];
47*b5893f02SDimitry Andric     // All wasm instructions have a StackBased field of type string, we only
48*b5893f02SDimitry Andric     // want the instructions for which this is "true".
49*b5893f02SDimitry Andric     auto StackString =
50*b5893f02SDimitry Andric         Def.getValue("StackBased")->getValue()->getCastTo(StringRecTy::get());
51*b5893f02SDimitry Andric     auto IsStackBased =
52*b5893f02SDimitry Andric         StackString &&
53*b5893f02SDimitry Andric         reinterpret_cast<const StringInit *>(StackString)->getValue() == "true";
54*b5893f02SDimitry Andric     if (IsStackBased && !CGIP.second) {
55*b5893f02SDimitry Andric       // this picks the first of many typed variants, which is
564ba319b5SDimitry Andric       // currently the except_ref one, though this shouldn't matter for
574ba319b5SDimitry Andric       // disassembly purposes.
584ba319b5SDimitry Andric       CGIP = std::make_pair(I, &CGI);
594ba319b5SDimitry Andric     }
604ba319b5SDimitry Andric   }
614ba319b5SDimitry Andric   OS << "#include \"MCTargetDesc/WebAssemblyMCTargetDesc.h\"\n";
624ba319b5SDimitry Andric   OS << "\n";
634ba319b5SDimitry Andric   OS << "namespace llvm {\n\n";
64*b5893f02SDimitry Andric   OS << "static constexpr int WebAssemblyInstructionTableSize = ";
65*b5893f02SDimitry Andric   OS << WebAssemblyInstructionTableSize << ";\n\n";
664ba319b5SDimitry Andric   OS << "enum EntryType : uint8_t { ";
674ba319b5SDimitry Andric   OS << "ET_Unused, ET_Prefix, ET_Instruction };\n\n";
684ba319b5SDimitry Andric   OS << "struct WebAssemblyInstruction {\n";
694ba319b5SDimitry Andric   OS << "  uint16_t Opcode;\n";
704ba319b5SDimitry Andric   OS << "  EntryType ET;\n";
714ba319b5SDimitry Andric   OS << "  uint8_t NumOperands;\n";
72*b5893f02SDimitry Andric   OS << "  uint16_t OperandStart;\n";
734ba319b5SDimitry Andric   OS << "};\n\n";
74*b5893f02SDimitry Andric   std::vector<std::string> OperandTable, CurOperandList;
754ba319b5SDimitry Andric   // Output one table per prefix.
764ba319b5SDimitry Andric   for (auto &PrefixPair : OpcodeTable) {
774ba319b5SDimitry Andric     if (PrefixPair.second.empty())
784ba319b5SDimitry Andric       continue;
794ba319b5SDimitry Andric     OS << "WebAssemblyInstruction InstructionTable" << PrefixPair.first;
804ba319b5SDimitry Andric     OS << "[] = {\n";
81*b5893f02SDimitry Andric     for (unsigned I = 0; I < WebAssemblyInstructionTableSize; I++) {
824ba319b5SDimitry Andric       auto InstIt = PrefixPair.second.find(I);
834ba319b5SDimitry Andric       if (InstIt != PrefixPair.second.end()) {
844ba319b5SDimitry Andric         // Regular instruction.
854ba319b5SDimitry Andric         assert(InstIt->second.second);
864ba319b5SDimitry Andric         auto &CGI = *InstIt->second.second;
874ba319b5SDimitry Andric         OS << "  // 0x";
884ba319b5SDimitry Andric         OS.write_hex(static_cast<unsigned long long>(I));
894ba319b5SDimitry Andric         OS << ": " << CGI.AsmString << "\n";
904ba319b5SDimitry Andric         OS << "  { " << InstIt->second.first << ", ET_Instruction, ";
91*b5893f02SDimitry Andric         OS << CGI.Operands.OperandList.size() << ", ";
92*b5893f02SDimitry Andric         // Collect operand types for storage in a shared list.
93*b5893f02SDimitry Andric         CurOperandList.clear();
944ba319b5SDimitry Andric         for (auto &Op : CGI.Operands.OperandList) {
95*b5893f02SDimitry Andric           assert(Op.OperandType != "MCOI::OPERAND_UNKNOWN");
96*b5893f02SDimitry Andric           CurOperandList.push_back(Op.OperandType);
974ba319b5SDimitry Andric         }
98*b5893f02SDimitry Andric         // See if we already have stored this sequence before. This is not
99*b5893f02SDimitry Andric         // strictly necessary but makes the table really small.
100*b5893f02SDimitry Andric         size_t OperandStart = OperandTable.size();
101*b5893f02SDimitry Andric         if (CurOperandList.size() <= OperandTable.size()) {
102*b5893f02SDimitry Andric           for (size_t J = 0; J <= OperandTable.size() - CurOperandList.size();
103*b5893f02SDimitry Andric                ++J) {
104*b5893f02SDimitry Andric             size_t K = 0;
105*b5893f02SDimitry Andric             for (; K < CurOperandList.size(); ++K) {
106*b5893f02SDimitry Andric               if (OperandTable[J + K] != CurOperandList[K]) break;
107*b5893f02SDimitry Andric             }
108*b5893f02SDimitry Andric             if (K == CurOperandList.size()) {
109*b5893f02SDimitry Andric               OperandStart = J;
110*b5893f02SDimitry Andric               break;
111*b5893f02SDimitry Andric             }
112*b5893f02SDimitry Andric           }
113*b5893f02SDimitry Andric         }
114*b5893f02SDimitry Andric         // Store operands if no prior occurrence.
115*b5893f02SDimitry Andric         if (OperandStart == OperandTable.size()) {
116*b5893f02SDimitry Andric           OperandTable.insert(OperandTable.end(), CurOperandList.begin(),
117*b5893f02SDimitry Andric                               CurOperandList.end());
118*b5893f02SDimitry Andric         }
119*b5893f02SDimitry Andric         OS << OperandStart;
1204ba319b5SDimitry Andric       } else {
1214ba319b5SDimitry Andric         auto PrefixIt = OpcodeTable.find(I);
1224ba319b5SDimitry Andric         // If we have a non-empty table for it that's not 0, this is a prefix.
1234ba319b5SDimitry Andric         if (PrefixIt != OpcodeTable.end() && I && !PrefixPair.first) {
124*b5893f02SDimitry Andric           OS << "  { 0, ET_Prefix, 0, 0";
1254ba319b5SDimitry Andric         } else {
126*b5893f02SDimitry Andric           OS << "  { 0, ET_Unused, 0, 0";
1274ba319b5SDimitry Andric         }
1284ba319b5SDimitry Andric       }
1294ba319b5SDimitry Andric       OS << "  },\n";
1304ba319b5SDimitry Andric     }
1314ba319b5SDimitry Andric     OS << "};\n\n";
1324ba319b5SDimitry Andric   }
133*b5893f02SDimitry Andric   // Create a table of all operands:
134*b5893f02SDimitry Andric   OS << "const uint8_t OperandTable[] = {\n";
135*b5893f02SDimitry Andric   for (auto &Op : OperandTable) {
136*b5893f02SDimitry Andric     OS << "  " << Op << ",\n";
137*b5893f02SDimitry Andric   }
138*b5893f02SDimitry Andric   OS << "};\n\n";
1394ba319b5SDimitry Andric   // Create a table of all extension tables:
1404ba319b5SDimitry Andric   OS << "struct { uint8_t Prefix; const WebAssemblyInstruction *Table; }\n";
1414ba319b5SDimitry Andric   OS << "PrefixTable[] = {\n";
1424ba319b5SDimitry Andric   for (auto &PrefixPair : OpcodeTable) {
1434ba319b5SDimitry Andric     if (PrefixPair.second.empty() || !PrefixPair.first)
1444ba319b5SDimitry Andric       continue;
1454ba319b5SDimitry Andric     OS << "  { " << PrefixPair.first << ", InstructionTable"
1464ba319b5SDimitry Andric        << PrefixPair.first;
1474ba319b5SDimitry Andric     OS << " },\n";
1484ba319b5SDimitry Andric   }
1494ba319b5SDimitry Andric   OS << "  { 0, nullptr }\n};\n\n";
1504ba319b5SDimitry Andric   OS << "} // End llvm namespace\n";
1514ba319b5SDimitry Andric }
1524ba319b5SDimitry Andric 
1534ba319b5SDimitry Andric } // namespace llvm
154