Changeset View
Changeset View
Standalone View
Standalone View
utils/TableGen/JSONEmitter.cpp
- This file was added.
//===- RegisterBankEmitter.cpp - Generate a Register Bank Desc. -*- C++ -*-===// | |||||
labath: You should take a look at <D45753>, which is about to add a JSON library to llvm. It would be a… | |||||
simon_tathamAuthorUnsubmitted Not Done ReplyInline ActionsHa! You're right, I hadn't noticed that. I'll replace my ad-hockery with calls to that code with great pleasure as soon as it lands – even without looking I'm sure it will be better than the 'only just enough' code I have here. Thanks for pointing it out! simon_tatham: Ha! You're right, I hadn't noticed that. I'll replace my ad-hockery with calls to that code… | |||||
// | |||||
// The LLVM Compiler Infrastructure | |||||
// | |||||
// This file is distributed under the University of Illinois Open Source | |||||
// License. See LICENSE.TXT for details. | |||||
// | |||||
//===----------------------------------------------------------------------===// | |||||
// | |||||
// 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" | |||||
#define DEBUG_TYPE "json-emitter" | |||||
using namespace llvm; | |||||
namespace { | |||||
class JSONValue { | |||||
public: | |||||
virtual ~JSONValue() = default; | |||||
virtual void write(raw_ostream &OS) const = 0; | |||||
}; | |||||
using PVal = std::shared_ptr<const JSONValue>; | |||||
class JSONNull : public JSONValue { | |||||
public: | |||||
void write(raw_ostream &OS) const override { OS << "null"; } | |||||
JSONNull() = default; | |||||
}; | |||||
class JSONBool : public JSONValue { | |||||
bool val; | |||||
public: | |||||
void write(raw_ostream &OS) const override { OS << (val ? "true" : "false"); } | |||||
JSONBool(bool val_) : val(val_) {} | |||||
}; | |||||
class JSONInteger : public JSONValue { | |||||
int64_t val; | |||||
public: | |||||
void write(raw_ostream &OS) const override { OS << val; } | |||||
JSONInteger(int64_t val_) : val(val_) {} | |||||
}; | |||||
class JSONString : public JSONValue { | |||||
std::string val; | |||||
public: | |||||
void write(raw_ostream &OS) const override { | |||||
OS << '"'; | |||||
for (char c : val) { | |||||
// Restrict to simple ASCII, avoiding Unicode pain | |||||
assert((unsigned char)c < 0x80); | |||||
switch (c) { | |||||
case '\n': | |||||
OS << "\\n"; | |||||
break; | |||||
case '\r': | |||||
OS << "\\r"; | |||||
break; | |||||
case '\t': | |||||
OS << "\\t"; | |||||
break; | |||||
case '\f': | |||||
OS << "\\f"; | |||||
break; | |||||
case '\b': | |||||
OS << "\\b"; | |||||
break; | |||||
case '\\': | |||||
case '"': | |||||
OS << '\\' << c; | |||||
break; | |||||
default: | |||||
if (c < 0x20) { | |||||
static const char hex[] = "0123456789abcdef"; | |||||
OS << "\\u00" << hex[c >> 4] << hex[c & 0xF]; | |||||
} else { | |||||
OS << c; | |||||
} | |||||
} | |||||
} | |||||
OS << '"'; | |||||
} | |||||
JSONString(const std::string &val_) : val(val_) {} | |||||
}; | |||||
class JSONArray : public JSONValue { | |||||
std::vector<PVal> values; | |||||
public: | |||||
void write(raw_ostream &OS) const override { | |||||
OS << '['; | |||||
const char *sep = ""; | |||||
for (PVal v : values) { | |||||
OS << sep; | |||||
v->write(OS); | |||||
sep = ","; | |||||
} | |||||
OS << ']'; | |||||
} | |||||
JSONArray() = default; | |||||
void push_back(PVal v) { values.push_back(v); } | |||||
}; | |||||
class JSONObject : public JSONValue { | |||||
std::map<std::string, PVal> values; | |||||
std::vector<std::string> value_order; | |||||
public: | |||||
void write(raw_ostream &OS) const override { | |||||
OS << '{'; | |||||
const char *sep = ""; | |||||
for (const auto &key : value_order) { | |||||
auto it = values.find(key); | |||||
assert(it != values.end()); | |||||
OS << sep; | |||||
JSONString(key).write(OS); | |||||
OS << ':'; | |||||
it->second->write(OS); | |||||
sep = ","; | |||||
} | |||||
OS << '}'; | |||||
} | |||||
JSONObject() = default; | |||||
void insert(std::string key, PVal value) { | |||||
assert(values.find(key) == values.end()); | |||||
values[key] = value; | |||||
value_order.push_back(key); | |||||
} | |||||
}; | |||||
static inline std::shared_ptr<JSONObject> mkobj() { | |||||
return std::make_shared<JSONObject>(); | |||||
} | |||||
static inline std::shared_ptr<JSONArray> mkarray() { | |||||
return std::make_shared<JSONArray>(); | |||||
} | |||||
static inline std::shared_ptr<JSONString> mkstring(const std::string &s) { | |||||
return std::make_shared<JSONString>(s); | |||||
} | |||||
static inline std::shared_ptr<JSONInteger> mkint(int64_t i) { | |||||
return std::make_shared<JSONInteger>(i); | |||||
} | |||||
class JSONEmitter { | |||||
private: | |||||
RecordKeeper &Records; | |||||
PVal True, False, Null; | |||||
PVal translate(const RecTy &T); | |||||
PVal translate(const Init &I); | |||||
PVal translate(const RecordVal &V); | |||||
std::shared_ptr<JSONObject> translate(const Record &R); | |||||
public: | |||||
JSONEmitter(RecordKeeper &R); | |||||
void run(raw_ostream &OS); | |||||
}; | |||||
} // end anonymous namespace | |||||
JSONEmitter::JSONEmitter(RecordKeeper &R) | |||||
: Records(R), True(std::make_shared<JSONBool>(true)), | |||||
False(std::make_shared<JSONBool>(false)), | |||||
Null(std::make_shared<JSONNull>()) {} | |||||
PVal JSONEmitter::translate(const RecTy &T) { | |||||
auto toret = mkobj(); | |||||
toret->insert("printable", mkstring(T.getAsString())); | |||||
std::string kind; | |||||
switch (T.getRecTyKind()) { | |||||
case RecTy::BitRecTyKind: | |||||
kind = "bit"; | |||||
break; | |||||
case RecTy::BitsRecTyKind: | |||||
kind = "bits"; | |||||
break; | |||||
case RecTy::CodeRecTyKind: | |||||
kind = "code"; | |||||
break; | |||||
case RecTy::IntRecTyKind: | |||||
kind = "int"; | |||||
break; | |||||
case RecTy::StringRecTyKind: | |||||
kind = "string"; | |||||
break; | |||||
case RecTy::ListRecTyKind: | |||||
kind = "list"; | |||||
break; | |||||
case RecTy::DagRecTyKind: | |||||
kind = "dag"; | |||||
break; | |||||
case RecTy::RecordRecTyKind: | |||||
kind = "record"; | |||||
break; | |||||
default: | |||||
llvm_unreachable("Bad RecTyKind"); | |||||
} | |||||
toret->insert("kind", mkstring(kind)); | |||||
if (auto *BRT = dyn_cast<BitsRecTy>(&T)) | |||||
toret->insert("size", mkint(BRT->getNumBits())); | |||||
if (auto *LRT = dyn_cast<ListRecTy>(&T)) | |||||
toret->insert("element", translate(*LRT->getElementType())); | |||||
if (auto *RRT = dyn_cast<RecordRecTy>(&T)) { | |||||
auto classes = mkarray(); | |||||
toret->insert("classes", classes); | |||||
for (Record *R : RRT->getClasses()) | |||||
classes->push_back(mkstring(R->getNameInitAsString())); | |||||
} | |||||
return toret; | |||||
} | |||||
PVal JSONEmitter::translate(const Init &I) { | |||||
if (dyn_cast<UnsetInit>(&I)) { | |||||
return Null; | |||||
} else if (auto *Bit = dyn_cast<BitInit>(&I)) { | |||||
return mkint(Bit->getValue() ? 1 : 0); | |||||
} else if (auto *Bits = dyn_cast<BitsInit>(&I)) { | |||||
auto array = mkarray(); | |||||
for (unsigned i = Bits->getNumBits(); i-- > 0;) | |||||
array->push_back(translate(*Bits->getBit(i))); | |||||
return array; | |||||
} else if (auto *Int = dyn_cast<IntInit>(&I)) { | |||||
return mkint(Int->getValue()); | |||||
} else if (auto *Str = dyn_cast<StringInit>(&I)) { | |||||
return mkstring(Str->getValue()); | |||||
} else if (auto *Code = dyn_cast<CodeInit>(&I)) { | |||||
return mkstring(Code->getValue()); | |||||
} else if (auto *Def = dyn_cast<DefInit>(&I)) { | |||||
auto obj = mkobj(); | |||||
obj->insert("kind", mkstring("def")); | |||||
obj->insert("name", mkstring(Def->getDef()->getName())); | |||||
return obj; | |||||
} else if (auto *Var = dyn_cast<VarInit>(&I)) { | |||||
auto obj = mkobj(); | |||||
obj->insert("kind", mkstring("var")); | |||||
obj->insert("var", translate(*Var->getNameInit())); | |||||
return obj; | |||||
} else if (auto *Field = dyn_cast<FieldInit>(&I)) { | |||||
auto obj = mkobj(); | |||||
obj->insert("kind", mkstring("field")); | |||||
obj->insert("record", translate(*Field->getRecord())); | |||||
obj->insert("field", | |||||
mkstring(Field->getFieldName()->getAsUnquotedString())); | |||||
return obj; | |||||
} else if (auto *VarBit = dyn_cast<VarBitInit>(&I)) { | |||||
auto obj = mkobj(); | |||||
obj->insert("kind", mkstring("bitindex")); | |||||
obj->insert("operand", translate(*VarBit->getBitVar())); | |||||
obj->insert("bitnum", mkint(VarBit->getBitNum())); | |||||
return obj; | |||||
} else if (auto *VarDef = dyn_cast<VarDefInit>(&I)) { | |||||
auto obj = mkobj(); | |||||
obj->insert("kind", mkstring("vardef")); | |||||
obj->insert("class", mkstring(VarDef->getClass()->getNameInitAsString())); | |||||
auto args = mkarray(); | |||||
obj->insert("args", args); | |||||
for (auto arg : VarDef->args()) | |||||
args->push_back(translate(*arg)); | |||||
return obj; | |||||
} else if (auto *Dag = dyn_cast<DagInit>(&I)) { | |||||
auto node = mkobj(); | |||||
node->insert("kind", mkstring("dag")); | |||||
node->insert("operator", translate(*Dag->getOperator())); | |||||
if (auto name = Dag->getName()) | |||||
node->insert("name", mkstring(name->getAsUnquotedString())); | |||||
auto args = mkarray(); | |||||
node->insert("args", args); | |||||
for (unsigned i = 0, limit = Dag->getNumArgs(); i < limit; ++i) { | |||||
auto arg = mkobj(); | |||||
args->push_back(arg); | |||||
arg->insert("arg", translate(*Dag->getArg(i))); | |||||
if (auto argname = Dag->getArgName(i)) | |||||
arg->insert("name", mkstring(argname->getAsUnquotedString())); | |||||
else | |||||
arg->insert("name", Null); | |||||
} | |||||
return node; | |||||
} else if (auto *Op = dyn_cast<OpInit>(&I)) { | |||||
auto node = mkobj(); | |||||
node->insert("kind", mkstring(Op->getOperatorName())); | |||||
auto args = mkarray(); | |||||
node->insert("args", args); | |||||
for (unsigned i = 0, limit = Op->getNumOperands(); i < limit; ++i) | |||||
args->push_back(translate(*Op->getOperand(i))); | |||||
if (auto *UnOp = dyn_cast<UnOpInit>(Op)) { | |||||
if (UnOp->getOpcode() == UnOpInit::CAST) { | |||||
node->insert("type", translate(*UnOp->getType())); | |||||
} | |||||
} | |||||
return node; | |||||
} else if (auto *List = dyn_cast<ListInit>(&I)) { | |||||
auto array = mkarray(); | |||||
for (auto val : *List) | |||||
array->push_back(translate(*val)); | |||||
return array; | |||||
} else if (auto *Fold = dyn_cast<FoldOpInit>(&I)) { | |||||
auto obj = mkobj(); | |||||
obj->insert("kind", mkstring("!foldl")); | |||||
auto args = mkarray(); | |||||
obj->insert("args", args); | |||||
args->push_back(translate(*Fold->getStart())); | |||||
args->push_back(translate(*Fold->getList())); | |||||
args->push_back(translate(*Fold->getA())); | |||||
args->push_back(translate(*Fold->getB())); | |||||
args->push_back(translate(*Fold->getExpr())); | |||||
return obj; | |||||
} else if (auto *IsA = dyn_cast<IsAOpInit>(&I)) { | |||||
auto obj = mkobj(); | |||||
obj->insert("kind", mkstring("!isa")); | |||||
obj->insert("type", translate(*IsA->getCheckType())); | |||||
auto args = mkarray(); | |||||
obj->insert("args", args); | |||||
args->push_back(translate(*IsA->getExpr())); | |||||
return obj; | |||||
} else if (auto *VLE = dyn_cast<VarListElementInit>(&I)) { | |||||
auto obj = mkobj(); | |||||
obj->insert("kind", mkstring("listindex")); | |||||
obj->insert("operand", translate(*VLE->getVariable())); | |||||
obj->insert("index", mkint(VLE->getElementNum())); | |||||
return obj; | |||||
} | |||||
llvm_unreachable("Bad type in translate(Init)"); | |||||
} | |||||
PVal JSONEmitter::translate(const RecordVal &V) { | |||||
auto toret = mkobj(); | |||||
toret->insert("type", translate(*V.getType())); | |||||
if (auto val = V.getValue()) { | |||||
toret->insert("printable", mkstring(val->getAsString())); | |||||
toret->insert("value", translate(*val)); | |||||
} | |||||
toret->insert("field", V.getPrefix() ? True : False); | |||||
return toret; | |||||
} | |||||
std::shared_ptr<JSONObject> JSONEmitter::translate(const Record &R) { | |||||
auto toret = mkobj(); | |||||
{ | |||||
auto sc = mkarray(); | |||||
toret->insert("superclasses", sc); | |||||
for (const auto &SuperPair : R.getSuperClasses()) | |||||
sc->push_back(mkstring(SuperPair.first->getNameInitAsString())); | |||||
} | |||||
{ | |||||
auto ta_list = mkarray(); | |||||
auto ta_dict = mkobj(); | |||||
toret->insert("template_args_order", ta_list); | |||||
toret->insert("template_args", ta_dict); | |||||
for (const auto TA : R.getTemplateArgs()) { | |||||
const RecordVal *RV = R.getValue(TA); | |||||
assert(RV && "Template argument record not found??"); | |||||
std::string name = RV->getNameInitAsString(); | |||||
ta_list->push_back(mkstring(name)); | |||||
ta_dict->insert(name, translate(*RV)); | |||||
} | |||||
} | |||||
{ | |||||
auto vals = mkobj(); | |||||
toret->insert("values", vals); | |||||
for (const RecordVal &Val : R.getValues()) | |||||
if (!R.isTemplateArg(Val.getNameInit())) | |||||
vals->insert(Val.getNameInitAsString(), translate(Val)); | |||||
} | |||||
return toret; | |||||
} | |||||
void JSONEmitter::run(raw_ostream &OS) { | |||||
auto root = mkobj(); | |||||
std::map<std::string, std::shared_ptr<JSONArray>> class_subclass_lists; | |||||
std::map<std::string, std::shared_ptr<JSONArray>> class_instance_lists; | |||||
auto classes = mkobj(); | |||||
root->insert("classes", classes); | |||||
for (const auto &C : Records.getClasses()) { | |||||
auto &Name = C.second->getNameInitAsString(); | |||||
auto &Class = *C.second; | |||||
auto Translated = translate(Class); | |||||
classes->insert(Name, Translated); | |||||
auto subclasses = mkarray(); | |||||
Translated->insert("subclasses", subclasses); | |||||
class_subclass_lists[Name] = subclasses; | |||||
auto instances = mkarray(); | |||||
Translated->insert("instances", instances); | |||||
class_instance_lists[Name] = instances; | |||||
} | |||||
for (const auto &C : Records.getClasses()) { | |||||
auto &Name = C.second->getNameInitAsString(); | |||||
auto &Class = *C.second; | |||||
for (const auto &SuperPair : Class.getSuperClasses()) { | |||||
auto SuperName = SuperPair.first->getNameInitAsString(); | |||||
auto it = class_subclass_lists.find(SuperName); | |||||
if (it != class_subclass_lists.end()) | |||||
it->second->push_back(mkstring(Name)); | |||||
} | |||||
} | |||||
auto defs = mkobj(); | |||||
root->insert("defs", defs); | |||||
for (const auto &D : Records.getDefs()) { | |||||
auto &Name = D.second->getNameInitAsString(); | |||||
auto &Def = *D.second; | |||||
auto Translated = translate(Def); | |||||
defs->insert(Name, Translated); | |||||
for (const auto &SuperPair : Def.getSuperClasses()) { | |||||
auto SuperName = SuperPair.first->getNameInitAsString(); | |||||
auto it = class_instance_lists.find(SuperName); | |||||
if (it != class_instance_lists.end()) | |||||
it->second->push_back(mkstring(Name)); | |||||
} | |||||
} | |||||
root->write(OS); | |||||
} | |||||
namespace llvm { | |||||
void EmitJSON(RecordKeeper &RK, raw_ostream &OS) { JSONEmitter(RK).run(OS); } | |||||
} // end namespace llvm |
You should take a look at <D45753>, which is about to add a JSON library to llvm. It would be a shame to add two of them in the same week. :)