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