9#include "../PassDetails.h"
17#include "llvm/ADT/StringExtras.h"
18#include "llvm/ADT/TypeSwitch.h"
19#include "llvm/Support/Compression.h"
20#include "llvm/Support/JSON.h"
24#define GEN_PASS_DEF_ESIBUILDMANIFEST
25#include "circt/Dialect/ESI/ESIPasses.h.inc"
35struct ESIBuildManifestPass
36 :
public circt::esi::impl::ESIBuildManifestBase<ESIBuildManifestPass> {
37 void runOnOperation()
override;
43 llvm::json::Value
json(Operation *errorOp,
Type,
bool useTable =
true);
46 llvm::json::Value
json(Operation *errorOp, Attribute,
bool elideType =
false);
49 void emitNode(llvm::json::OStream &, AppIDHierNodeOp nodeOp);
51 void emitBlock(llvm::json::OStream &,
Block &block, StringRef manifestClass);
53 AppIDHierRootOp appidRoot;
59 std::string useType(
Type type) {
61 llvm::raw_string_ostream(typeID) << type;
63 if (typeLookup.count(type))
65 typeLookup[type] = types.size();
66 types.push_back(type);
69 SmallVector<Type, 8> types;
70 DenseMap<Type, size_t> typeLookup;
73 DenseSet<SymbolRefAttr> symbols;
74 DenseSet<SymbolRefAttr> modules;
77 bool encounteredError =
false;
81void ESIBuildManifestPass::runOnOperation() {
82 MLIRContext *ctxt = &getContext();
83 Operation *mod = getOperation();
84 symCache.addDefinitions(mod);
88 for (
auto root : mod->getRegion(0).front().getOps<AppIDHierRootOp>())
89 if (root.getTopModuleRef() == top)
95 std::string jsonManifest =
json();
96 if (encounteredError) {
102 llvm::raw_fd_ostream os(
"esi_system_manifest.json", ec);
104 mod->emitError() <<
"Failed to open file for writing: " << ec.message();
107 os << jsonManifest <<
"\n";
111 SmallVector<uint8_t, 10 * 1024> compressedManifest;
112 if (llvm::compression::zlib::isAvailable()) {
114 llvm::compression::zlib::compress(
115 ArrayRef((uint8_t *)jsonManifest.data(), jsonManifest.length()),
116 compressedManifest, llvm::compression::zlib::BestSizeCompression);
118 llvm::raw_fd_ostream bos(
"esi_system_manifest.json.zlib", ec);
120 mod->emitError() <<
"Failed to open compressed file for writing: "
124 bos.write((
char *)compressedManifest.data(), compressedManifest.size());
127 OpBuilder b(symCache.getDefinition(appidRoot.getTopModuleRefAttr())
131 CompressedManifestOp::create(b, b.getUnknownLoc(),
132 BlobAttr::get(ctxt, compressedManifest));
135 <<
"zlib not available but required for manifest support";
139void ESIBuildManifestPass::emitNode(llvm::json::OStream &j,
140 AppIDHierNodeOp nodeOp) {
141 std::set<StringRef> classesToEmit;
142 for (
auto manifestData : nodeOp.getOps<IsManifestData>())
143 classesToEmit.insert(manifestData.getManifestClass());
145 j.attribute(
"appID",
json(nodeOp, nodeOp.getAppIDAttr()));
146 j.attribute(
"instanceOf",
json(nodeOp, nodeOp.getModuleRefAttr()));
147 for (StringRef manifestClass : classesToEmit)
148 j.attributeArray(manifestClass.str() +
"s", [&]() {
149 emitBlock(j, nodeOp.getChildren().front(), manifestClass);
151 j.attributeArray(
"children", [&]() {
152 for (
auto nodeOp : nodeOp.getChildren().front().getOps<AppIDHierNodeOp>())
158void ESIBuildManifestPass::emitBlock(llvm::json::OStream &j, Block &block,
159 StringRef manifestClass) {
160 for (
auto manifestData : block.getOps<IsManifestData>()) {
161 if (manifestData.getManifestClass() != manifestClass)
164 SmallVector<NamedAttribute, 4> attrs;
165 manifestData.getDetails(attrs);
166 for (
auto attr : attrs)
167 j.attribute(attr.
getName().getValue(),
168 json(manifestData, attr.getValue()));
173std::string ESIBuildManifestPass::json() {
174 auto mod = getOperation();
175 std::string jsonStrBuffer;
176 llvm::raw_string_ostream os(jsonStrBuffer);
177 llvm::json::OStream j(os, 2);
180 j.attribute(
"apiVersion", esiApiVersion);
182 std::set<StringRef> classesToEmit;
183 for (
auto manifestData : appidRoot.getOps<IsManifestData>())
184 classesToEmit.insert(manifestData.getManifestClass());
186 j.attributeObject(
"design", [&]() {
187 j.attribute(
"instanceOf",
json(appidRoot, appidRoot.getTopModuleRefAttr()));
188 modules.insert(appidRoot.getTopModuleRefAttr());
189 for (StringRef manifestClass : classesToEmit)
190 j.attributeArray(manifestClass.str() +
"s", [&]() {
191 emitBlock(j, appidRoot.getChildren().front(), manifestClass);
193 j.attributeArray(
"children", [&]() {
195 appidRoot.getChildren().front().getOps<AppIDHierNodeOp>())
200 j.attributeArray(
"serviceDeclarations", [&]() {
201 for (
auto svcDecl : mod.getBody()->getOps<ServiceDeclOpInterface>()) {
202 auto sym = FlatSymbolRefAttr::get(svcDecl);
203 if (!symbols.contains(sym))
206 j.attribute(
"symbol",
json(svcDecl, sym,
true));
207 std::optional<StringRef> typeName = svcDecl.getTypeName();
209 j.attribute(
"serviceName", *typeName);
210 llvm::SmallVector<ServicePortInfo, 8> ports;
211 svcDecl.getPortList(ports);
212 j.attributeArray(
"ports", [&]() {
213 for (
auto port : ports) {
215 j.attribute(
"name", port.port.getName().getValue());
216 j.attribute(
"typeID", useType(port.type));
224 j.attributeArray(
"modules", [&]() {
230 for (
auto modSymbol : modules)
231 symbolInfoLookup[modSymbol] = {};
233 for (
auto symInfo : mod.getBody()->getOps<IsManifestData>()) {
234 FlatSymbolRefAttr sym = symInfo.getSymbolRefAttr();
235 if (!sym || !symbols.contains(sym))
237 symbolInfoLookup[sym].push_back(symInfo);
241 for (
const auto &symNameInfo : symbolInfoLookup) {
243 std::string symbolStr;
244 llvm::raw_string_ostream(symbolStr) << symNameInfo.first;
245 j.attribute(
"symbol", symbolStr);
246 for (
auto symInfo : symNameInfo.second) {
247 j.attributeBegin(symInfo.getManifestClass());
249 SmallVector<NamedAttribute, 4> attrs;
250 symInfo.getDetails(attrs);
251 for (
auto attr : attrs) {
252 if (attr.getName().getValue() ==
"symbolRef")
254 j.attribute(attr.getName().getValue(),
255 json(symInfo, attr.getValue()));
264 j.attributeArray(
"types", [&]() {
265 for (
size_t i = 0; i < types.size(); i++)
266 j.value(
json(mod, types[i],
false));
271 return jsonStrBuffer;
276llvm::json::Value ESIBuildManifestPass::json(Operation *errorOp,
Type type,
278 using llvm::json::Array;
279 using llvm::json::Object;
280 using llvm::json::Value;
282 if (useTable && typeLookup.contains(type)) {
286 std::string typeName;
287 llvm::raw_string_ostream(typeName) << type;
294 TypeSwitch<Type, Object>(type)
299 .Case([&](ChannelBundleType t) {
302 for (
auto field : t.getChannels())
303 channels.push_back(
Object(
304 {{
"name", field.name.getValue()},
305 {
"direction", stringifyChannelDirection(field.direction)},
306 {
"type",
json(errorOp, field.type, useTable)}}));
307 return Object({{
"channels", Value(std::move(channels))}});
318 .Case([&](hw::ArrayType t) {
321 {{
"size", t.getNumElements()},
322 {
"element",
json(errorOp, t.getElementType(), useTable)}});
324 .Case([&](hw::StructType t) {
327 for (
auto field : t.getElements())
329 Object({{
"name", field.name.getValue()},
330 {
"type",
json(errorOp, field.type, useTable)}}));
331 return Object({{
"fields", Value(std::move(fields))}});
333 .Case([&](hw::UnionType t) {
336 for (
auto field : t.getElements()) {
337 if (field.offset != 0) {
339 <<
"ESI system manifest does not support nonzero union "
342 encounteredError =
true;
346 Object({{
"name", field.name.getValue()},
347 {
"type",
json(errorOp, field.type, useTable)}}));
349 return Object({{
"fields", Value(std::move(fields))}});
351 .Case([&](hw::TypeAliasType t) {
354 {{
"name", t.getTypeDecl(symCache).getPreferredName()},
355 {
"inner",
json(errorOp, t.getInnerType(), useTable)}});
359 StringRef signedness =
360 t.isSigned() ?
"signed"
361 : (t.isUnsigned() ?
"unsigned" :
"signless");
362 return Object({{
"signedness", signedness}});
367 for (
auto frame : t.getFrames()) {
369 for (
auto field : frame.getMembers()) {
370 Object fieldObj{{
"name", field.getFieldName().getValue()}};
371 if (field.getNumItems() != 0)
372 fieldObj[
"numItems"] = field.getNumItems();
373 if (field.getBulkCountWidth() != 0)
374 fieldObj[
"bulkCountWidth"] = field.getBulkCountWidth();
375 fields.push_back(Value(std::move(fieldObj)));
378 Value(
Object({{
"name", frame.getName().getValue()},
379 {
"fields", Value(std::move(fields))}})));
382 {{
"name", t.
getName().getValue()},
383 {
"into",
json(errorOp, t.getInto(), useTable)},
384 {
"frames", Value(std::move(frames))},
387 .Default([&](
Type t) {
388 errorOp->emitWarning()
389 <<
"ESI system manifest: unknown type: " << t;
395 llvm::raw_string_ostream(typeID) << type;
400 if (
auto chanType = dyn_cast<ChannelType>(type))
401 hwType = chanType.getInner();
402 if (
auto winType = dyn_cast<WindowType>(hwType))
403 hwType = winType.getLoweredType();
404 int64_t width = hw::getBitWidth(hwType);
406 o[
"hwBitwidth"] = width;
408 o[
"dialect"] = type.getDialect().getNamespace();
416llvm::json::Value ESIBuildManifestPass::json(Operation *errorOp, Attribute attr,
420 using llvm::json::Object;
421 using llvm::json::Value;
423 TypeSwitch<Attribute, Value>(attr)
424 .Case([&](FlatSymbolRefAttr ref) {
427 llvm::raw_string_ostream(value) << ref;
430 .Case([&](StringAttr a) {
return a.getValue(); })
431 .Case([&](IntegerAttr a) {
return a.getValue().getLimitedValue(); })
432 .Case([&](TypeAttr a) {
return useType(a.getValue()); })
433 .Case([&](ArrayAttr a) {
434 return llvm::json::Array(llvm::map_range(
435 a, [&](Attribute a) {
return json(errorOp, a); }));
437 .Case([&](DictionaryAttr a) {
438 llvm::json::Object dict;
439 for (
const auto &entry : a.getValue())
440 dict[entry.
getName().getValue()] =
441 json(errorOp, entry.getValue());
444 .Case([&](AppIDAttr appid) {
445 llvm::json::Object dict;
446 dict[
"name"] = appid.getName().getValue();
447 auto idx = appid.getIndex();
449 dict[
"index"] = *idx;
452 .Default([&](Attribute a) {
454 llvm::raw_string_ostream(value) << a;
459 auto typedAttr = llvm::dyn_cast<TypedAttr>(attr);
460 if (elideType || !typedAttr || isa<NoneType>(typedAttr.getType()))
465 dict[
"value"] = value;
466 dict[
"type"] = useType(typedAttr.getType());
470std::unique_ptr<OperationPass<ModuleOp>>
472 return std::make_unique<ESIBuildManifestPass>();
This stores lookup tables to make manipulating and working with the IR more efficient.
The "any" type is a special type which can be used to represent any type, as identified by the type i...
Channels are the basic communication primitives.
const Type * getInner() const
Integers are bit vectors which may be signed or unsigned and are interpreted as numbers.
Lists represent variable-length sequences of elements of a single type.
const Type * getElementType() const
Root class of the ESI type system.
Windows represent a fixed-size sliding window over a stream of data.
const Type * getLoweredType() const
const std::string & getName() const
std::unique_ptr< OperationPass< ModuleOp > > createESIBuildManifestPass()
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
evaluator::ObjectValue Object
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.