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