reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
//===- WebAssemblyDisassemblerEmitter.cpp - Disassembler tables -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is part of the WebAssembly Disassembler Emitter.
// It contains the implementation of the disassembler tables.
// Documentation for the disassembler emitter in general can be found in
// WebAssemblyDisassemblerEmitter.h.
//
//===----------------------------------------------------------------------===//

#include "WebAssemblyDisassemblerEmitter.h"
#include "llvm/TableGen/Record.h"

namespace llvm {

static constexpr int WebAssemblyInstructionTableSize = 256;

void emitWebAssemblyDisassemblerTables(
    raw_ostream &OS,
    const ArrayRef<const CodeGenInstruction *> &NumberedInstructions) {
  // First lets organize all opcodes by (prefix) byte. Prefix 0 is the
  // starting table.
  std::map<unsigned,
           std::map<unsigned, std::pair<unsigned, const CodeGenInstruction *>>>
      OpcodeTable;
  for (unsigned I = 0; I != NumberedInstructions.size(); ++I) {
    auto &CGI = *NumberedInstructions[I];
    auto &Def = *CGI.TheDef;
    if (!Def.getValue("Inst"))
      continue;
    auto &Inst = *Def.getValueAsBitsInit("Inst");
    auto Opc = static_cast<unsigned>(
        reinterpret_cast<IntInit *>(Inst.convertInitializerTo(IntRecTy::get()))
            ->getValue());
    if (Opc == 0xFFFFFFFF)
      continue; // No opcode defined.
    assert(Opc <= 0xFFFF);
    auto Prefix = Opc >> 8;
    Opc = Opc & 0xFF;
    auto &CGIP = OpcodeTable[Prefix][Opc];
    // All wasm instructions have a StackBased field of type string, we only
    // want the instructions for which this is "true".
    auto StackString =
        Def.getValue("StackBased")->getValue()->getCastTo(StringRecTy::get());
    auto IsStackBased =
        StackString &&
        reinterpret_cast<const StringInit *>(StackString)->getValue() == "true";
    if (!IsStackBased)
      continue;
    if (CGIP.second) {
      // We already have an instruction for this slot, so decide which one
      // should be the canonical one. This determines which variant gets
      // printed in a disassembly. We want e.g. "call" not "i32.call", and
      // "end" when we don't know if its "end_loop" or "end_block" etc.
      auto IsCanonicalExisting = CGIP.second->TheDef->getValue("IsCanonical")
                                     ->getValue()
                                     ->getAsString() == "1";
      // We already have one marked explicitly as canonical, so keep it.
      if (IsCanonicalExisting)
        continue;
      auto IsCanonicalNew =
          Def.getValue("IsCanonical")->getValue()->getAsString() == "1";
      // If the new one is explicitly marked as canonical, take it.
      if (!IsCanonicalNew) {
        // Neither the existing or new instruction is canonical.
        // Pick the one with the shortest name as heuristic.
        // Though ideally IsCanonical is always defined for at least one
        // variant so this never has to apply.
        if (CGIP.second->AsmString.size() <= CGI.AsmString.size())
          continue;
      }
    }
    // Set this instruction as the one to use.
    CGIP = std::make_pair(I, &CGI);
  }
  OS << "#include \"MCTargetDesc/WebAssemblyMCTargetDesc.h\"\n";
  OS << "\n";
  OS << "namespace llvm {\n\n";
  OS << "static constexpr int WebAssemblyInstructionTableSize = ";
  OS << WebAssemblyInstructionTableSize << ";\n\n";
  OS << "enum EntryType : uint8_t { ";
  OS << "ET_Unused, ET_Prefix, ET_Instruction };\n\n";
  OS << "struct WebAssemblyInstruction {\n";
  OS << "  uint16_t Opcode;\n";
  OS << "  EntryType ET;\n";
  OS << "  uint8_t NumOperands;\n";
  OS << "  uint16_t OperandStart;\n";
  OS << "};\n\n";
  std::vector<std::string> OperandTable, CurOperandList;
  // Output one table per prefix.
  for (auto &PrefixPair : OpcodeTable) {
    if (PrefixPair.second.empty())
      continue;
    OS << "WebAssemblyInstruction InstructionTable" << PrefixPair.first;
    OS << "[] = {\n";
    for (unsigned I = 0; I < WebAssemblyInstructionTableSize; I++) {
      auto InstIt = PrefixPair.second.find(I);
      if (InstIt != PrefixPair.second.end()) {
        // Regular instruction.
        assert(InstIt->second.second);
        auto &CGI = *InstIt->second.second;
        OS << "  // 0x";
        OS.write_hex(static_cast<unsigned long long>(I));
        OS << ": " << CGI.AsmString << "\n";
        OS << "  { " << InstIt->second.first << ", ET_Instruction, ";
        OS << CGI.Operands.OperandList.size() << ", ";
        // Collect operand types for storage in a shared list.
        CurOperandList.clear();
        for (auto &Op : CGI.Operands.OperandList) {
          assert(Op.OperandType != "MCOI::OPERAND_UNKNOWN");
          CurOperandList.push_back(Op.OperandType);
        }
        // See if we already have stored this sequence before. This is not
        // strictly necessary but makes the table really small.
        size_t OperandStart = OperandTable.size();
        if (CurOperandList.size() <= OperandTable.size()) {
          for (size_t J = 0; J <= OperandTable.size() - CurOperandList.size();
               ++J) {
            size_t K = 0;
            for (; K < CurOperandList.size(); ++K) {
              if (OperandTable[J + K] != CurOperandList[K]) break;
            }
            if (K == CurOperandList.size()) {
              OperandStart = J;
              break;
            }
          }
        }
        // Store operands if no prior occurrence.
        if (OperandStart == OperandTable.size()) {
          OperandTable.insert(OperandTable.end(), CurOperandList.begin(),
                              CurOperandList.end());
        }
        OS << OperandStart;
      } else {
        auto PrefixIt = OpcodeTable.find(I);
        // If we have a non-empty table for it that's not 0, this is a prefix.
        if (PrefixIt != OpcodeTable.end() && I && !PrefixPair.first) {
          OS << "  { 0, ET_Prefix, 0, 0";
        } else {
          OS << "  { 0, ET_Unused, 0, 0";
        }
      }
      OS << "  },\n";
    }
    OS << "};\n\n";
  }
  // Create a table of all operands:
  OS << "const uint8_t OperandTable[] = {\n";
  for (auto &Op : OperandTable) {
    OS << "  " << Op << ",\n";
  }
  OS << "};\n\n";
  // Create a table of all extension tables:
  OS << "struct { uint8_t Prefix; const WebAssemblyInstruction *Table; }\n";
  OS << "PrefixTable[] = {\n";
  for (auto &PrefixPair : OpcodeTable) {
    if (PrefixPair.second.empty() || !PrefixPair.first)
      continue;
    OS << "  { " << PrefixPair.first << ", InstructionTable"
       << PrefixPair.first;
    OS << " },\n";
  }
  OS << "  { 0, nullptr }\n};\n\n";
  OS << "} // end namespace llvm\n";
}

} // namespace llvm