//===- JSONBackend.cpp - Generate a JSON dump of all records. -*- 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 TableGen back end generates a machine-readable representation // of all the classes and records defined by the input, in JSON format. // //===----------------------------------------------------------------------===// #include "llvm/ADT/BitVector.h" #include "llvm/Support/Debug.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include "llvm/Support/JSON.h" #define DEBUG_TYPE "json-emitter" using namespace llvm; namespace { class JSONEmitter { private: RecordKeeper &Records; json::Value translateInit(const Init &I); json::Array listSuperclasses(const Record &R); public: JSONEmitter(RecordKeeper &R); void run(raw_ostream &OS); }; } // end anonymous namespace JSONEmitter::JSONEmitter(RecordKeeper &R) : Records(R) {} json::Value JSONEmitter::translateInit(const Init &I) { // Init subclasses that we return as JSON primitive values of one // kind or another. if (isa(&I)) { return nullptr; } else if (auto *Bit = dyn_cast(&I)) { return Bit->getValue() ? 1 : 0; } else if (auto *Bits = dyn_cast(&I)) { json::Array array; for (unsigned i = 0, limit = Bits->getNumBits(); i < limit; i++) array.push_back(translateInit(*Bits->getBit(i))); return std::move(array); } else if (auto *Int = dyn_cast(&I)) { return Int->getValue(); } else if (auto *Str = dyn_cast(&I)) { return Str->getValue(); } else if (auto *Code = dyn_cast(&I)) { return Code->getValue(); } else if (auto *List = dyn_cast(&I)) { json::Array array; for (auto val : *List) array.push_back(translateInit(*val)); return std::move(array); } // Init subclasses that we return as JSON objects containing a // 'kind' discriminator. For these, we also provide the same // translation back into TableGen input syntax that -print-records // would give. json::Object obj; obj["printable"] = I.getAsString(); if (auto *Def = dyn_cast(&I)) { obj["kind"] = "def"; obj["def"] = Def->getDef()->getName(); return std::move(obj); } else if (auto *Var = dyn_cast(&I)) { obj["kind"] = "var"; obj["var"] = Var->getName(); return std::move(obj); } else if (auto *VarBit = dyn_cast(&I)) { if (auto *Var = dyn_cast(VarBit->getBitVar())) { obj["kind"] = "varbit"; obj["var"] = Var->getName(); obj["index"] = VarBit->getBitNum(); return std::move(obj); } } else if (auto *Dag = dyn_cast(&I)) { obj["kind"] = "dag"; obj["operator"] = translateInit(*Dag->getOperator()); if (auto name = Dag->getName()) obj["name"] = name->getAsUnquotedString(); json::Array args; for (unsigned i = 0, limit = Dag->getNumArgs(); i < limit; ++i) { json::Array arg; arg.push_back(translateInit(*Dag->getArg(i))); if (auto argname = Dag->getArgName(i)) arg.push_back(argname->getAsUnquotedString()); else arg.push_back(nullptr); args.push_back(std::move(arg)); } obj["args"] = std::move(args); return std::move(obj); } // Final fallback: anything that gets past here is simply given a // kind field of 'complex', and the only other field is the standard // 'printable' representation. assert(!I.isConcrete()); obj["kind"] = "complex"; return std::move(obj); } void JSONEmitter::run(raw_ostream &OS) { json::Object root; root["!tablegen_json_version"] = 1; // Prepare the arrays that will list the instances of every class. // We mostly fill those in by iterating over the superclasses of // each def, but we also want to ensure we store an empty list for a // class with no instances at all, so we do a preliminary iteration // over the classes, invoking std::map::operator[] to default- // construct the array for each one. std::map instance_lists; for (const auto &C : Records.getClasses()) { auto &Name = C.second->getNameInitAsString(); (void)instance_lists[Name]; } // Main iteration over the defs. for (const auto &D : Records.getDefs()) { auto &Name = D.second->getNameInitAsString(); auto &Def = *D.second; json::Object obj; json::Array fields; for (const RecordVal &RV : Def.getValues()) { if (!Def.isTemplateArg(RV.getNameInit())) { auto Name = RV.getNameInitAsString(); if (RV.getPrefix()) fields.push_back(Name); obj[Name] = translateInit(*RV.getValue()); } } obj["!fields"] = std::move(fields); json::Array superclasses; for (const auto &SuperPair : Def.getSuperClasses()) superclasses.push_back(SuperPair.first->getNameInitAsString()); obj["!superclasses"] = std::move(superclasses); obj["!name"] = Name; obj["!anonymous"] = Def.isAnonymous(); root[Name] = std::move(obj); // Add this def to the instance list for each of its superclasses. for (const auto &SuperPair : Def.getSuperClasses()) { auto SuperName = SuperPair.first->getNameInitAsString(); instance_lists[SuperName].push_back(Name); } } // Make a JSON object from the std::map of instance lists. json::Object instanceof; for (auto kv: instance_lists) instanceof[kv.first] = std::move(kv.second); root["!instanceof"] = std::move(instanceof); // Done. Write the output. OS << json::Value(std::move(root)) << "\n"; } namespace llvm { void EmitJSON(RecordKeeper &RK, raw_ostream &OS) { JSONEmitter(RK).run(OS); } } // end namespace llvm