Index: include/llvm/TableGen/Record.h =================================================================== --- include/llvm/TableGen/Record.h +++ include/llvm/TableGen/Record.h @@ -739,6 +739,7 @@ // Clone - Clone this operator, replacing arguments with the new list virtual OpInit *clone(ArrayRef Operands) const = 0; + virtual std::string getOperatorName() const = 0; virtual unsigned getNumOperands() const = 0; virtual Init *getOperand(unsigned i) const = 0; @@ -793,6 +794,7 @@ Init *resolveReferences(Resolver &R) const override; std::string getAsString() const override; + std::string getOperatorName() const override; }; /// !op (X, Y) - Combine two inits. @@ -848,6 +850,7 @@ Init *resolveReferences(Resolver &R) const override; std::string getAsString() const override; + std::string getOperatorName() const override; }; /// !op (X, Y, Z) - Combine two inits. @@ -910,6 +913,7 @@ Init *resolveReferences(Resolver &R) const override; std::string getAsString() const override; + std::string getOperatorName() const override; }; /// !foldl (a, b, expr, start, lst) - Fold over a list. @@ -942,6 +946,12 @@ bool isComplete() const override { return false; } + Init *getStart() const { return Start; } + Init *getList() const { return List; } + Init *getA() const { return A; } + Init *getB() const { return B; } + Init *getExpr() const { return Expr; } + Init *resolveReferences(Resolver &R) const override; Init *getBit(unsigned Bit) const override; @@ -973,6 +983,9 @@ // possible to fold. Init *Fold() const; + RecTy *getCheckType() const { return CheckType; } + Init *getExpr() const { return Expr; } + bool isComplete() const override { return false; } Init *resolveReferences(Resolver &R) const override; @@ -1154,6 +1167,8 @@ std::string getAsString() const override; + Record *getClass() const { return Class; } + Init *getArg(unsigned i) const { assert(i < NumArgs && "Argument index out of range!"); return getTrailingObjects()[i]; Index: lib/TableGen/Record.cpp =================================================================== --- lib/TableGen/Record.cpp +++ lib/TableGen/Record.cpp @@ -786,15 +786,23 @@ } std::string UnOpInit::getAsString() const { - std::string Result; + std::string OpSuffix; switch (getOpcode()) { - case CAST: Result = "!cast<" + getType()->getAsString() + ">"; break; - case HEAD: Result = "!head"; break; - case TAIL: Result = "!tail"; break; - case SIZE: Result = "!size"; break; - case EMPTY: Result = "!empty"; break; + case CAST: OpSuffix += "<" + getType()->getAsString() + ">"; break; + default: break; + } + return getOperatorName() + OpSuffix + "(" + LHS->getAsString() + ")"; +} + +std::string UnOpInit::getOperatorName() const { + switch (getOpcode()) { + case CAST: return "!cast"; + case HEAD: return "!head"; + case TAIL: return "!tail"; + case SIZE: return "!size"; + case EMPTY: return "!empty"; + default: llvm_unreachable("bad unop opcode"); } - return Result + "(" + LHS->getAsString() + ")"; } static void @@ -969,25 +977,29 @@ } std::string BinOpInit::getAsString() const { - std::string Result; + return getOperatorName() + "(" + LHS->getAsString() + ", " + + RHS->getAsString() + ")"; +} + +std::string BinOpInit::getOperatorName() const { switch (getOpcode()) { - case CONCAT: Result = "!con"; break; - case ADD: Result = "!add"; break; - case AND: Result = "!and"; break; - case OR: Result = "!or"; break; - case SHL: Result = "!shl"; break; - case SRA: Result = "!sra"; break; - case SRL: Result = "!srl"; break; - case EQ: Result = "!eq"; break; - case NE: Result = "!ne"; break; - case LE: Result = "!le"; break; - case LT: Result = "!lt"; break; - case GE: Result = "!ge"; break; - case GT: Result = "!gt"; break; - case LISTCONCAT: Result = "!listconcat"; break; - case STRCONCAT: Result = "!strconcat"; break; - } - return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")"; + case CONCAT: return "!con"; + case ADD: return "!add"; + case AND: return "!and"; + case OR: return "!or"; + case SHL: return "!shl"; + case SRA: return "!sra"; + case SRL: return "!srl"; + case EQ: return "!eq"; + case NE: return "!ne"; + case LE: return "!le"; + case LT: return "!lt"; + case GE: return "!ge"; + case GT: return "!gt"; + case LISTCONCAT: return "!listconcat"; + case STRCONCAT: return "!strconcat"; + default: llvm_unreachable("bad binop opcode"); + } } static void @@ -1194,15 +1206,18 @@ } std::string TernOpInit::getAsString() const { - std::string Result; + return getOperatorName() + "(" + LHS->getAsString() + ", " + + MHS->getAsString() + ", " + RHS->getAsString() + ")"; +} + +std::string TernOpInit::getOperatorName() const { switch (getOpcode()) { - case SUBST: Result = "!subst"; break; - case FOREACH: Result = "!foreach"; break; - case IF: Result = "!if"; break; - case DAG: Result = "!dag"; break; + case SUBST: return "!subst"; + case FOREACH: return "!foreach"; + case IF: return "!if"; + case DAG: return "!dag"; + default: llvm_unreachable("bad ternop opcode"); } - return Result + "(" + LHS->getAsString() + ", " + MHS->getAsString() + ", " + - RHS->getAsString() + ")"; } static void ProfileFoldOpInit(FoldingSetNodeID &ID, Init *A, Init *B, Index: test/TableGen/JSON-check.py =================================================================== --- /dev/null +++ test/TableGen/JSON-check.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +import sys +import subprocess +import traceback +import json + +data = json.load(sys.stdin) +testfile = sys.argv[1] + +prefix = "CHECK: " + +fails = 0 +passes = 0 +with open(testfile) as testfh: + lineno = 0 + for line in iter(testfh.readline, ""): + lineno += 1 + line = line.rstrip("\r\n") + try: + prefix_pos = line.index(prefix) + except ValueError: + continue + check_expr = line[prefix_pos + len(prefix):] + + try: + exception = None + result = eval(check_expr, {"data":data}) + except Exception: + result = False + exception = traceback.format_exc().splitlines()[-1] + + if exception is not None: + sys.stderr.write( + "{file}:{line:d}: check threw exception: {expr}\n" + "{file}:{line:d}: exception was: {exception}\n".format( + file=testfile, line=lineno, + expr=check_expr, exception=exception)) + fails += 1 + elif not result: + sys.stderr.write( + "{file}:{line:d}: check returned False: {expr}\n".format( + file=testfile, line=lineno, expr=check_expr)) + fails += 1 + else: + passes += 1 + +if fails != 0: + sys.exit("{} checks failed".format(fails)) +else: + sys.stdout.write("{} checks passed\n".format(passes)) Index: test/TableGen/JSON.td =================================================================== --- /dev/null +++ test/TableGen/JSON.td @@ -0,0 +1,211 @@ +// RUN: llvm-tblgen -dump-json %s | %python %S/JSON-check.py %s + +class Base {} +class Intermediate : Base {} +class Derived : Intermediate {} + +// CHECK: 'Base' in data['classes']['Intermediate']['superclasses'] +// CHECK: 'Intermediate' in data['classes']['Base']['subclasses'] + +// CHECK: 'Intermediate' in data['classes']['Derived']['superclasses'] +// CHECK: 'Derived' in data['classes']['Intermediate']['subclasses'] + +// CHECK: 'Derived' in data['classes']['Base']['subclasses'] +// CHECK: 'Base' in data['classes']['Derived']['superclasses'] + +def D : Intermediate {} +// CHECK: 'D' in data['classes']['Base']['instances'] +// CHECK: 'D' in data['classes']['Intermediate']['instances'] +// CHECK: 'D' not in data['classes']['Derived']['instances'] + +class Foo {} + +class Templated {} + +def Op; + +class Variables { + int i; + // CHECK: data['classes']['Variables']['values']['i']['type']['kind'] == 'int' + // CHECK: data['classes']['Variables']['values']['i']['type']['printable'] == 'int' + + string s; + // CHECK: data['classes']['Variables']['values']['s']['type']['kind'] == 'string' + // CHECK: data['classes']['Variables']['values']['s']['type']['printable'] == 'string' + + bit b; + // CHECK: data['classes']['Variables']['values']['b']['type']['kind'] == 'bit' + // CHECK: data['classes']['Variables']['values']['b']['type']['printable'] == 'bit' + + bits<8> bs; + // CHECK: data['classes']['Variables']['values']['bs']['type']['kind'] == 'bits' + // CHECK: data['classes']['Variables']['values']['bs']['type']['size'] == 8 + // CHECK: data['classes']['Variables']['values']['bs']['type']['printable'] == 'bits<8>' + + code c; + // CHECK: data['classes']['Variables']['values']['c']['type']['kind'] == 'code' + // CHECK: data['classes']['Variables']['values']['c']['type']['printable'] == 'code' + + list li; + // CHECK: data['classes']['Variables']['values']['li']['type']['kind'] == 'list' + // CHECK: data['classes']['Variables']['values']['li']['type']['element']['kind'] == 'int' + // CHECK: data['classes']['Variables']['values']['li']['type']['printable'] == 'list' + + Base base; + // CHECK: data['classes']['Variables']['values']['base']['type']['kind'] == 'record' + // CHECK: data['classes']['Variables']['values']['base']['type']['classes'] == ['Base'] + // CHECK: data['classes']['Variables']['values']['base']['type']['printable'] == 'Base' + + dag d; + // CHECK: data['classes']['Variables']['values']['d']['type']['kind'] == 'dag' + // CHECK: data['classes']['Variables']['values']['d']['type']['printable'] == 'dag' +} +def VarNull : Variables { + // A variable not filled in at all has its value set to JSON + // 'null', which translates to Python None + // CHECK: data['defs']['VarNull']['values']['i']['value'] is None + // CHECK: data['defs']['VarNull']['values']['i']['printable'] == '?' + + // But it still comes with its type information from whatever + // class it inherits the variable, so you can check what _kind_ of + // value you haven't got. + // CHECK: data['defs']['VarNull']['values']['i']['type']['kind'] == 'int' +} +def VarPrim : Variables { + // Test initializers that map to primitive JSON types + + int i = 3; + // CHECK: data['defs']['VarPrim']['values']['i']['value'] == 3 + // CHECK: data['defs']['VarPrim']['values']['i']['printable'] == '3' + + string s = "hello, world"; + // CHECK: data['defs']['VarPrim']['values']['s']['value'] == 'hello, world' + // CHECK: data['defs']['VarPrim']['values']['s']['printable'] == '"hello, world"' + + bit b = 0; + // CHECK: data['defs']['VarPrim']['values']['b']['value'] == 0 + // CHECK: data['defs']['VarPrim']['values']['b']['printable'] == "0" + + bits<8> bs = { 0,0,0,1,0,1,1,1 }; + // CHECK: data['defs']['VarPrim']['values']['bs']['value'] == [ 0,0,0,1,0,1,1,1 ] + // CHECK: data['defs']['VarPrim']['values']['bs']['printable'] == '{ 0, 0, 0, 1, 0, 1, 1, 1 }' + + code c = [{ \" }]; + // CHECK: data['defs']['VarPrim']['values']['c']['value'] == r' \" ' + // CHECK: data['defs']['VarPrim']['values']['c']['printable'] == r'[{ \" }]' + + list li = [ 1, 2, 3, 4 ]; + // CHECK: data['defs']['VarPrim']['values']['li']['value'] == [ 1, 2, 3, 4 ] + // CHECK: data['defs']['VarPrim']['values']['li']['printable'] == '[1, 2, 3, 4]' +} +def VarObj : Variables { + // Test initializers that map to JSON objects containing a 'kind' + // discriminator + + Base base = D; + // CHECK: data['defs']['VarObj']['values']['base']['value']['kind'] == 'def' + // CHECK: data['defs']['VarObj']['values']['base']['value']['name'] == 'D' + // CHECK: data['defs']['VarObj']['values']['base']['printable'] == 'D' + + dag d = (Op 22, 44:$foo); + // CHECK: data['defs']['VarObj']['values']['d']['value']['kind'] == 'dag' + // CHECK: data['defs']['VarObj']['values']['d']['value']['operator']['kind'] == 'def' + // CHECK: data['defs']['VarObj']['values']['d']['value']['operator']['name'] == 'Op' + // CHECK: data['defs']['VarObj']['values']['d']['value']['args'][0]['arg'] == 22 + // CHECK: data['defs']['VarObj']['values']['d']['value']['args'][0]['name'] == None + // CHECK: data['defs']['VarObj']['values']['d']['value']['args'][1]['arg'] == 44 + // CHECK: data['defs']['VarObj']['values']['d']['value']['args'][1]['name'] == 'foo' + // CHECK: data['defs']['VarObj']['values']['d']['printable'] == '(Op 22, 44:$foo)' +} +class VarObjC { + int i1; + int i2 = i1; + // CHECK: data['classes']['VarObjC']['values']['i2']['value']['kind'] == 'var' + // CHECK: data['classes']['VarObjC']['values']['i2']['value']['var'] == 'i1' + // CHECK: data['classes']['VarObjC']['values']['i2']['printable'] == 'i1' + + bits<8> bs; + dag d = (Op bs{7-3}); + // CHECK: all(arg['kind'] == 'bitindex' for arg in data['classes']['VarObjC']['values']['d']['value']['args'][0]['arg']) + // CHECK: all(arg['operand']['kind'] == 'var' for arg in data['classes']['VarObjC']['values']['d']['value']['args'][0]['arg']) + // CHECK: all(arg['bitnum'] == 7-i for i,arg in enumerate(data['classes']['VarObjC']['values']['d']['value']['args'][0]['arg'])) + // CHECK: data['classes']['VarObjC']['values']['d']['printable'] == '(Op { bs{7}, bs{6}, bs{5}, bs{4}, bs{3} })' + + Variables vs; + int vsi = vs.i; + // CHECK: data['classes']['VarObjC']['values']['vsi']['value']['kind'] == 'field' + // CHECK: data['classes']['VarObjC']['values']['vsi']['value']['record']['kind'] == 'var' + // CHECK: data['classes']['VarObjC']['values']['vsi']['value']['record']['var'] == 'vs' + // CHECK: data['classes']['VarObjC']['values']['vsi']['value']['field'] == 'i' + // CHECK: data['classes']['VarObjC']['values']['vsi']['printable'] == 'vs.i' + + int vsi1 = !add(1, vsi); + // CHECK: data['classes']['VarObjC']['values']['vsi1']['value']['kind'] == '!add' + // CHECK: data['classes']['VarObjC']['values']['vsi1']['value']['args'][0] == 1 + // CHECK: data['classes']['VarObjC']['values']['vsi1']['value']['args'][1]['kind'] == 'var' + // CHECK: data['classes']['VarObjC']['values']['vsi1']['value']['args'][1]['var'] == 'vsi' + // CHECK: data['classes']['VarObjC']['values']['vsi1']['printable'] == '!add(1, vsi)' + + int vsi2 = vs.li[2]; + // CHECK: data['classes']['VarObjC']['values']['vsi2']['value']['kind'] == 'listindex' + // CHECK: data['classes']['VarObjC']['values']['vsi2']['value']['operand']['kind'] == 'field' + // CHECK: data['classes']['VarObjC']['values']['vsi2']['value']['index'] == 2 + // CHECK: data['classes']['VarObjC']['values']['vsi2']['printable'] == 'vs.li[2]' + + list bases; + list isas = !foreach(base, bases, !isa(base)); + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['kind'] == '!foreach' + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['args'][0] == 'base' + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['args'][1]['kind'] == 'var' + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['args'][1]['var'] == 'bases' + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['args'][2]['kind'] == '!isa' + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['args'][2]['type']['kind'] == 'record' + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['args'][2]['type']['classes'] == ['Derived'] + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['args'][2]['args'][0]['kind'] == 'var' + // CHECK: data['classes']['VarObjC']['values']['isas']['value']['args'][2]['args'][0]['var'] == 'base' + // CHECK: data['classes']['VarObjC']['values']['isas']['printable'] == '!foreach("base", bases, !isa(base))' + + list summands; + int sum = !foldl(1, summands, x, y, !add(x,y)); + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['kind'] == '!foldl' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][0] == 1 + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][1]['kind'] == 'var' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][1]['var'] == 'summands' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][2] == 'x' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][3] == 'y' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][4]['kind'] == '!add' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][4]['args'][0]['kind'] == 'var' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][4]['args'][0]['var'] == 'x' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][4]['args'][1]['kind'] == 'var' + // CHECK: data['classes']['VarObjC']['values']['sum']['value']['args'][4]['args'][1]['var'] == 'y' + // CHECK: data['classes']['VarObjC']['values']['sum']['printable'] == '!foldl(1, summands, x, y, !add(x, y))' +} +class VarObjCT { + Templated t = Templated; + // CHECK: data['classes']['VarObjCT']['values']['t']['value']['kind'] == 'vardef' + // CHECK: data['classes']['VarObjCT']['values']['t']['value']['class'] == 'Templated' + // CHECK: data['classes']['VarObjCT']['values']['t']['value']['args'][0]['kind'] == 'var' + // CHECK: data['classes']['VarObjCT']['values']['t']['value']['args'][0]['var'] == 'VarObjCT:i' + // CHECK: data['classes']['VarObjCT']['values']['t']['printable'] == 'Templated' + + bit b = !cast(i); + // CHECK: data['classes']['VarObjCT']['values']['b']['value']['kind'] == '!cast' + // CHECK: data['classes']['VarObjCT']['values']['b']['value']['type']['kind'] == 'bit' + // CHECK: data['classes']['VarObjCT']['values']['b']['value']['args'][0]['kind'] == 'var' + // CHECK: data['classes']['VarObjCT']['values']['b']['value']['args'][0]['var'] == 'VarObjCT:i' + // CHECK: data['classes']['VarObjCT']['values']['b']['printable'] == '!cast(VarObjCT:i)' +} + +class FieldKeywordTest { + int a; + field int b; + // CHECK: data['classes']['FieldKeywordTest']['values']['a']['field'] is False + // CHECK: data['classes']['FieldKeywordTest']['values']['b']['field'] is True +} + +class TemplateArgsTest {} +// CHECK: data['classes']['TemplateArgsTest']['template_args_order'] == ['TemplateArgsTest:i', 'TemplateArgsTest:j'] +// CHECK: data['classes']['TemplateArgsTest']['template_args']['TemplateArgsTest:i']['type']['kind'] == 'int' +// CHECK: data['classes']['TemplateArgsTest']['template_args']['TemplateArgsTest:i']['value'] is None +// CHECK: data['classes']['TemplateArgsTest']['template_args']['TemplateArgsTest:j']['type']['kind'] == 'int' +// CHECK: data['classes']['TemplateArgsTest']['template_args']['TemplateArgsTest:j']['value'] == 3 Index: utils/TableGen/CMakeLists.txt =================================================================== --- utils/TableGen/CMakeLists.txt +++ utils/TableGen/CMakeLists.txt @@ -28,6 +28,7 @@ InstrInfoEmitter.cpp InstrDocsEmitter.cpp IntrinsicEmitter.cpp + JSONEmitter.cpp OptParserEmitter.cpp PseudoLoweringEmitter.cpp RISCVCompressInstEmitter.cpp Index: utils/TableGen/JSONEmitter.cpp =================================================================== --- /dev/null +++ utils/TableGen/JSONEmitter.cpp @@ -0,0 +1,453 @@ +//===- RegisterBankEmitter.cpp - Generate a Register Bank Desc. -*- C++ -*-===// +// +// 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; + +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 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 values; + std::vector 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 mkobj() { + return std::make_shared(); +} +static inline std::shared_ptr mkarray() { + return std::make_shared(); +} +static inline std::shared_ptr mkstring(const std::string &s) { + return std::make_shared(s); +} +static inline std::shared_ptr mkint(int64_t i) { + return std::make_shared(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 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(true)), + False(std::make_shared(false)), + Null(std::make_shared()) {} + +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(&T)) + toret->insert("size", mkint(BRT->getNumBits())); + if (auto *LRT = dyn_cast(&T)) + toret->insert("element", translate(*LRT->getElementType())); + if (auto *RRT = dyn_cast(&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(&I)) { + return Null; + } else if (auto *Bit = dyn_cast(&I)) { + return mkint(Bit->getValue() ? 1 : 0); + } else if (auto *Bits = dyn_cast(&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(&I)) { + return mkint(Int->getValue()); + } else if (auto *Str = dyn_cast(&I)) { + return mkstring(Str->getValue()); + } else if (auto *Code = dyn_cast(&I)) { + return mkstring(Code->getValue()); + } else if (auto *Def = dyn_cast(&I)) { + auto obj = mkobj(); + obj->insert("kind", mkstring("def")); + obj->insert("name", mkstring(Def->getDef()->getName())); + return obj; + } else if (auto *Var = dyn_cast(&I)) { + auto obj = mkobj(); + obj->insert("kind", mkstring("var")); + obj->insert("var", translate(*Var->getNameInit())); + return obj; + } else if (auto *Field = dyn_cast(&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(&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(&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(&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(&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(Op)) { + if (UnOp->getOpcode() == UnOpInit::CAST) { + node->insert("type", translate(*UnOp->getType())); + } + } + + return node; + } else if (auto *List = dyn_cast(&I)) { + auto array = mkarray(); + for (auto val : *List) + array->push_back(translate(*val)); + return array; + } else if (auto *Fold = dyn_cast(&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(&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(&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 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> class_subclass_lists; + std::map> 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 Index: utils/TableGen/TableGen.cpp =================================================================== --- utils/TableGen/TableGen.cpp +++ utils/TableGen/TableGen.cpp @@ -24,6 +24,7 @@ enum ActionType { PrintRecords, + DumpJSON, GenEmitter, GenRegisterInfo, GenInstrInfo, @@ -57,6 +58,8 @@ Action(cl::desc("Action to perform:"), cl::values(clEnumValN(PrintRecords, "print-records", "Print all records to stdout (default)"), + clEnumValN(DumpJSON, "dump-json", + "Dump all records as machine-readable JSON"), clEnumValN(GenEmitter, "gen-emitter", "Generate machine code emitter"), clEnumValN(GenRegisterInfo, "gen-register-info", @@ -120,6 +123,9 @@ case PrintRecords: OS << Records; // No argument, dump all contents break; + case DumpJSON: + EmitJSON(Records, OS); + break; case GenEmitter: EmitCodeEmitter(Records, OS); break; Index: utils/TableGen/TableGenBackends.h =================================================================== --- utils/TableGen/TableGenBackends.h +++ utils/TableGen/TableGenBackends.h @@ -87,6 +87,8 @@ void EmitX86FoldTables(RecordKeeper &RK, raw_ostream &OS); void EmitRegisterBank(RecordKeeper &RK, raw_ostream &OS); +void EmitJSON(RecordKeeper &RK, raw_ostream &OS); + } // End llvm namespace #endif