| //===- 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); |
| |
| 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<UnsetInit>(&I)) { |
| return nullptr; |
| } else if (auto *Bit = dyn_cast<BitInit>(&I)) { |
| return Bit->getValue() ? 1 : 0; |
| } else if (auto *Bits = dyn_cast<BitsInit>(&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<IntInit>(&I)) { |
| return Int->getValue(); |
| } else if (auto *Str = dyn_cast<StringInit>(&I)) { |
| return Str->getValue(); |
| } else if (auto *List = dyn_cast<ListInit>(&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<DefInit>(&I)) { |
| obj["kind"] = "def"; |
| obj["def"] = Def->getDef()->getName(); |
| return std::move(obj); |
| } else if (auto *Var = dyn_cast<VarInit>(&I)) { |
| obj["kind"] = "var"; |
| obj["var"] = Var->getName(); |
| return std::move(obj); |
| } else if (auto *VarBit = dyn_cast<VarBitInit>(&I)) { |
| if (auto *Var = dyn_cast<VarInit>(VarBit->getBitVar())) { |
| obj["kind"] = "varbit"; |
| obj["var"] = Var->getName(); |
| obj["index"] = VarBit->getBitNum(); |
| return std::move(obj); |
| } |
| } else if (auto *Dag = dyn_cast<DagInit>(&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<std::string, json::Array> 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.isNonconcreteOK()) |
| 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 |