Simon Tatham | 9cfd4e5 | 2018-07-11 08:40:19 +0000 | [diff] [blame] | 1 | //===- JSONBackend.cpp - Generate a JSON dump of all records. -*- C++ -*-=====// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This TableGen back end generates a machine-readable representation |
| 11 | // of all the classes and records defined by the input, in JSON format. |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #include "llvm/ADT/BitVector.h" |
| 16 | #include "llvm/Support/Debug.h" |
| 17 | #include "llvm/TableGen/Error.h" |
| 18 | #include "llvm/TableGen/Record.h" |
| 19 | #include "llvm/TableGen/TableGenBackend.h" |
| 20 | #include "llvm/Support/JSON.h" |
| 21 | |
| 22 | #define DEBUG_TYPE "json-emitter" |
| 23 | |
| 24 | using namespace llvm; |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | class JSONEmitter { |
| 29 | private: |
| 30 | RecordKeeper &Records; |
| 31 | |
| 32 | json::Value translateInit(const Init &I); |
| 33 | json::Array listSuperclasses(const Record &R); |
| 34 | |
| 35 | public: |
| 36 | JSONEmitter(RecordKeeper &R); |
| 37 | |
| 38 | void run(raw_ostream &OS); |
| 39 | }; |
| 40 | |
| 41 | } // end anonymous namespace |
| 42 | |
| 43 | JSONEmitter::JSONEmitter(RecordKeeper &R) : Records(R) {} |
| 44 | |
| 45 | json::Value JSONEmitter::translateInit(const Init &I) { |
| 46 | |
| 47 | // Init subclasses that we return as JSON primitive values of one |
| 48 | // kind or another. |
| 49 | |
| 50 | if (isa<UnsetInit>(&I)) { |
| 51 | return nullptr; |
| 52 | } else if (auto *Bit = dyn_cast<BitInit>(&I)) { |
| 53 | return Bit->getValue() ? 1 : 0; |
| 54 | } else if (auto *Bits = dyn_cast<BitsInit>(&I)) { |
| 55 | json::Array array; |
| 56 | for (unsigned i = 0, limit = Bits->getNumBits(); i < limit; i++) |
| 57 | array.push_back(translateInit(*Bits->getBit(i))); |
Simon Tatham | 7adaa42 | 2018-07-11 08:57:56 +0000 | [diff] [blame] | 58 | return std::move(array); |
Simon Tatham | 9cfd4e5 | 2018-07-11 08:40:19 +0000 | [diff] [blame] | 59 | } else if (auto *Int = dyn_cast<IntInit>(&I)) { |
| 60 | return Int->getValue(); |
| 61 | } else if (auto *Str = dyn_cast<StringInit>(&I)) { |
| 62 | return Str->getValue(); |
| 63 | } else if (auto *Code = dyn_cast<CodeInit>(&I)) { |
| 64 | return Code->getValue(); |
| 65 | } else if (auto *List = dyn_cast<ListInit>(&I)) { |
| 66 | json::Array array; |
| 67 | for (auto val : *List) |
| 68 | array.push_back(translateInit(*val)); |
Simon Tatham | 7adaa42 | 2018-07-11 08:57:56 +0000 | [diff] [blame] | 69 | return std::move(array); |
Simon Tatham | 9cfd4e5 | 2018-07-11 08:40:19 +0000 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | // Init subclasses that we return as JSON objects containing a |
| 73 | // 'kind' discriminator. For these, we also provide the same |
| 74 | // translation back into TableGen input syntax that -print-records |
| 75 | // would give. |
| 76 | |
| 77 | json::Object obj; |
| 78 | obj["printable"] = I.getAsString(); |
| 79 | |
| 80 | if (auto *Def = dyn_cast<DefInit>(&I)) { |
| 81 | obj["kind"] = "def"; |
| 82 | obj["def"] = Def->getDef()->getName(); |
Simon Tatham | 7adaa42 | 2018-07-11 08:57:56 +0000 | [diff] [blame] | 83 | return std::move(obj); |
Simon Tatham | 9cfd4e5 | 2018-07-11 08:40:19 +0000 | [diff] [blame] | 84 | } else if (auto *Var = dyn_cast<VarInit>(&I)) { |
| 85 | obj["kind"] = "var"; |
| 86 | obj["var"] = Var->getName(); |
Simon Tatham | 7adaa42 | 2018-07-11 08:57:56 +0000 | [diff] [blame] | 87 | return std::move(obj); |
Simon Tatham | 9cfd4e5 | 2018-07-11 08:40:19 +0000 | [diff] [blame] | 88 | } else if (auto *VarBit = dyn_cast<VarBitInit>(&I)) { |
| 89 | if (auto *Var = dyn_cast<VarInit>(VarBit->getBitVar())) { |
| 90 | obj["kind"] = "varbit"; |
| 91 | obj["var"] = Var->getName(); |
| 92 | obj["index"] = VarBit->getBitNum(); |
Simon Tatham | 7adaa42 | 2018-07-11 08:57:56 +0000 | [diff] [blame] | 93 | return std::move(obj); |
Simon Tatham | 9cfd4e5 | 2018-07-11 08:40:19 +0000 | [diff] [blame] | 94 | } |
| 95 | } else if (auto *Dag = dyn_cast<DagInit>(&I)) { |
| 96 | obj["kind"] = "dag"; |
| 97 | obj["operator"] = translateInit(*Dag->getOperator()); |
| 98 | if (auto name = Dag->getName()) |
| 99 | obj["name"] = name->getAsUnquotedString(); |
| 100 | json::Array args; |
| 101 | for (unsigned i = 0, limit = Dag->getNumArgs(); i < limit; ++i) { |
| 102 | json::Array arg; |
| 103 | arg.push_back(translateInit(*Dag->getArg(i))); |
| 104 | if (auto argname = Dag->getArgName(i)) |
| 105 | arg.push_back(argname->getAsUnquotedString()); |
| 106 | else |
| 107 | arg.push_back(nullptr); |
| 108 | args.push_back(std::move(arg)); |
| 109 | } |
| 110 | obj["args"] = std::move(args); |
Simon Tatham | 7adaa42 | 2018-07-11 08:57:56 +0000 | [diff] [blame] | 111 | return std::move(obj); |
Simon Tatham | 9cfd4e5 | 2018-07-11 08:40:19 +0000 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | // Final fallback: anything that gets past here is simply given a |
| 115 | // kind field of 'complex', and the only other field is the standard |
| 116 | // 'printable' representation. |
| 117 | |
| 118 | assert(!I.isConcrete()); |
| 119 | obj["kind"] = "complex"; |
Simon Tatham | 7adaa42 | 2018-07-11 08:57:56 +0000 | [diff] [blame] | 120 | return std::move(obj); |
Simon Tatham | 9cfd4e5 | 2018-07-11 08:40:19 +0000 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | void JSONEmitter::run(raw_ostream &OS) { |
| 124 | json::Object root; |
| 125 | |
| 126 | root["!tablegen_json_version"] = 1; |
| 127 | |
| 128 | // Prepare the arrays that will list the instances of every class. |
| 129 | // We mostly fill those in by iterating over the superclasses of |
| 130 | // each def, but we also want to ensure we store an empty list for a |
| 131 | // class with no instances at all, so we do a preliminary iteration |
| 132 | // over the classes, invoking std::map::operator[] to default- |
| 133 | // construct the array for each one. |
| 134 | std::map<std::string, json::Array> instance_lists; |
| 135 | for (const auto &C : Records.getClasses()) { |
| 136 | auto &Name = C.second->getNameInitAsString(); |
| 137 | (void)instance_lists[Name]; |
| 138 | } |
| 139 | |
| 140 | // Main iteration over the defs. |
| 141 | for (const auto &D : Records.getDefs()) { |
| 142 | auto &Name = D.second->getNameInitAsString(); |
| 143 | auto &Def = *D.second; |
| 144 | |
| 145 | json::Object obj; |
| 146 | json::Array fields; |
| 147 | |
| 148 | for (const RecordVal &RV : Def.getValues()) { |
| 149 | if (!Def.isTemplateArg(RV.getNameInit())) { |
| 150 | auto Name = RV.getNameInitAsString(); |
| 151 | if (RV.getPrefix()) |
| 152 | fields.push_back(Name); |
| 153 | obj[Name] = translateInit(*RV.getValue()); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | obj["!fields"] = std::move(fields); |
| 158 | |
| 159 | json::Array superclasses; |
| 160 | for (const auto &SuperPair : Def.getSuperClasses()) |
| 161 | superclasses.push_back(SuperPair.first->getNameInitAsString()); |
| 162 | obj["!superclasses"] = std::move(superclasses); |
| 163 | |
| 164 | obj["!name"] = Name; |
| 165 | obj["!anonymous"] = Def.isAnonymous(); |
| 166 | |
| 167 | root[Name] = std::move(obj); |
| 168 | |
| 169 | // Add this def to the instance list for each of its superclasses. |
| 170 | for (const auto &SuperPair : Def.getSuperClasses()) { |
| 171 | auto SuperName = SuperPair.first->getNameInitAsString(); |
| 172 | instance_lists[SuperName].push_back(Name); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | // Make a JSON object from the std::map of instance lists. |
| 177 | json::Object instanceof; |
| 178 | for (auto kv: instance_lists) |
| 179 | instanceof[kv.first] = std::move(kv.second); |
| 180 | root["!instanceof"] = std::move(instanceof); |
| 181 | |
| 182 | // Done. Write the output. |
| 183 | OS << json::Value(std::move(root)) << "\n"; |
| 184 | } |
| 185 | |
| 186 | namespace llvm { |
| 187 | |
| 188 | void EmitJSON(RecordKeeper &RK, raw_ostream &OS) { JSONEmitter(RK).run(OS); } |
| 189 | } // end namespace llvm |