CIRCT 24.0.0git
Loading...
Searching...
No Matches
ESIBuildManifest.cpp
Go to the documentation of this file.
1//===- ESIBuildManifest.cpp - Build ESI system manifest ---------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "../PassDetails.h"
10
14
16
17#include "llvm/ADT/StringExtras.h"
18#include "llvm/ADT/TypeSwitch.h"
19#include "llvm/Support/Compression.h"
20#include "llvm/Support/JSON.h"
21
22namespace circt {
23namespace esi {
24#define GEN_PASS_DEF_ESIBUILDMANIFEST
25#include "circt/Dialect/ESI/ESIPasses.h.inc"
26} // namespace esi
27} // namespace circt
28
29using namespace circt;
30using namespace esi;
31
32// TODO: The code herein is a bit ugly, but it works. Consider cleaning it up.
33
34namespace {
35struct ESIBuildManifestPass
36 : public circt::esi::impl::ESIBuildManifestBase<ESIBuildManifestPass> {
37 void runOnOperation() override;
38
39private:
40 /// Get a JSON representation of a type. 'useTable' indicates whether to use
41 /// the type table to determine if the type should be emitted as a reference
42 /// if it already exists in the type table.
43 llvm::json::Value json(Operation *errorOp, Type, bool useTable = true);
44 /// Get a JSON representation of a type. 'elideType' indicates to not print
45 /// the type if it would have been printed.
46 llvm::json::Value json(Operation *errorOp, Attribute, bool elideType = false);
47
48 // Output a node in the appid hierarchy.
49 void emitNode(llvm::json::OStream &, AppIDHierNodeOp nodeOp);
50 // Output the manifest data of a node in the appid hierarchy.
51 void emitBlock(llvm::json::OStream &, Block &block, StringRef manifestClass);
52
53 AppIDHierRootOp appidRoot;
54
55 /// Get a JSON representation of the manifest.
56 std::string json();
57
58 // Type table.
59 std::string useType(Type type) {
60 std::string typeID;
61 llvm::raw_string_ostream(typeID) << type;
62
63 if (typeLookup.count(type))
64 return typeID;
65 typeLookup[type] = types.size();
66 types.push_back(type);
67 return typeID;
68 }
69 SmallVector<Type, 8> types;
70 DenseMap<Type, size_t> typeLookup;
71
72 // Symbols / modules which are referenced.
73 DenseSet<SymbolRefAttr> symbols;
74 DenseSet<SymbolRefAttr> modules;
75
76 hw::HWSymbolCache symCache;
77 bool encounteredError = false;
78};
79} // anonymous namespace
80
81void ESIBuildManifestPass::runOnOperation() {
82 MLIRContext *ctxt = &getContext();
83 Operation *mod = getOperation();
84 symCache.addDefinitions(mod);
85 symCache.freeze();
86
87 // Find the top level appid hierarchy root.
88 for (auto root : mod->getRegion(0).front().getOps<AppIDHierRootOp>())
89 if (root.getTopModuleRef() == top)
90 appidRoot = root;
91 if (!appidRoot)
92 return;
93
94 // JSONify the manifest.
95 std::string jsonManifest = json();
96 if (encounteredError) {
97 signalPassFailure();
98 return;
99 }
100
101 std::error_code ec;
102 llvm::raw_fd_ostream os("esi_system_manifest.json", ec);
103 if (ec) {
104 mod->emitError() << "Failed to open file for writing: " << ec.message();
105 signalPassFailure();
106 } else {
107 os << jsonManifest << "\n";
108 }
109
110 // If zlib is available, compress the manifest and append it to the module.
111 SmallVector<uint8_t, 10 * 1024> compressedManifest;
112 if (llvm::compression::zlib::isAvailable()) {
113 // Compress the manifest.
114 llvm::compression::zlib::compress(
115 ArrayRef((uint8_t *)jsonManifest.data(), jsonManifest.length()),
116 compressedManifest, llvm::compression::zlib::BestSizeCompression);
117
118 llvm::raw_fd_ostream bos("esi_system_manifest.json.zlib", ec);
119 if (ec) {
120 mod->emitError() << "Failed to open compressed file for writing: "
121 << ec.message();
122 signalPassFailure();
123 } else {
124 bos.write((char *)compressedManifest.data(), compressedManifest.size());
125 }
126
127 OpBuilder b(symCache.getDefinition(appidRoot.getTopModuleRefAttr())
128 ->getRegion(0)
129 .front()
130 .getTerminator());
131 CompressedManifestOp::create(b, b.getUnknownLoc(),
132 BlobAttr::get(ctxt, compressedManifest));
133 } else {
134 mod->emitWarning()
135 << "zlib not available but required for manifest support";
136 }
137}
138
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());
144 j.object([&] {
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);
150 });
151 j.attributeArray("children", [&]() {
152 for (auto nodeOp : nodeOp.getChildren().front().getOps<AppIDHierNodeOp>())
153 emitNode(j, nodeOp);
154 });
155 });
156}
157
158void ESIBuildManifestPass::emitBlock(llvm::json::OStream &j, Block &block,
159 StringRef manifestClass) {
160 for (auto manifestData : block.getOps<IsManifestData>()) {
161 if (manifestData.getManifestClass() != manifestClass)
162 continue;
163 j.object([&] {
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()));
169 });
170 }
171}
172
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);
178
179 j.objectBegin(); // Top level object.
180 j.attribute("apiVersion", esiApiVersion);
181
182 std::set<StringRef> classesToEmit;
183 for (auto manifestData : appidRoot.getOps<IsManifestData>())
184 classesToEmit.insert(manifestData.getManifestClass());
185
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);
192 });
193 j.attributeArray("children", [&]() {
194 for (auto nodeOp :
195 appidRoot.getChildren().front().getOps<AppIDHierNodeOp>())
196 emitNode(j, nodeOp);
197 });
198 });
199
200 j.attributeArray("serviceDeclarations", [&]() {
201 for (auto svcDecl : mod.getBody()->getOps<ServiceDeclOpInterface>()) {
202 auto sym = FlatSymbolRefAttr::get(svcDecl);
203 if (!symbols.contains(sym))
204 continue;
205 j.object([&] {
206 j.attribute("symbol", json(svcDecl, sym, /*elideType=*/true));
207 std::optional<StringRef> typeName = svcDecl.getTypeName();
208 if (typeName)
209 j.attribute("serviceName", *typeName);
210 llvm::SmallVector<ServicePortInfo, 8> ports;
211 svcDecl.getPortList(ports);
212 j.attributeArray("ports", [&]() {
213 for (auto port : ports) {
214 j.object([&] {
215 j.attribute("name", port.port.getName().getValue());
216 j.attribute("typeID", useType(port.type));
217 });
218 }
219 });
220 });
221 }
222 });
223
224 j.attributeArray("modules", [&]() {
225 // Map from symbol to all manifest data ops related to said symbol.
227 symbolInfoLookup;
228 // Ensure that all symbols are present in the lookup even if they have no
229 // manifest metadata.
230 for (auto modSymbol : modules)
231 symbolInfoLookup[modSymbol] = {};
232 // Gather all manifest data for each symbol.
233 for (auto symInfo : mod.getBody()->getOps<IsManifestData>()) {
234 FlatSymbolRefAttr sym = symInfo.getSymbolRefAttr();
235 if (!sym || !symbols.contains(sym))
236 continue;
237 symbolInfoLookup[sym].push_back(symInfo);
238 }
239
240 // Now, emit a JSON object for each symbol.
241 for (const auto &symNameInfo : symbolInfoLookup) {
242 j.object([&] {
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());
248 j.object([&] {
249 SmallVector<NamedAttribute, 4> attrs;
250 symInfo.getDetails(attrs);
251 for (auto attr : attrs) {
252 if (attr.getName().getValue() == "symbolRef")
253 continue;
254 j.attribute(attr.getName().getValue(),
255 json(symInfo, attr.getValue()));
256 }
257 });
258 j.attributeEnd();
259 }
260 });
261 }
262 });
263
264 j.attributeArray("types", [&]() {
265 for (size_t i = 0; i < types.size(); i++)
266 j.value(json(mod, types[i], /*useTable=*/false));
267 });
268
269 j.objectEnd(); // Top level object.
270
271 return jsonStrBuffer;
272}
273
274/// Get a JSON representation of a type.
275// NOLINTNEXTLINE(misc-no-recursion)
276llvm::json::Value ESIBuildManifestPass::json(Operation *errorOp, Type type,
277 bool useTable) {
278 using llvm::json::Array;
279 using llvm::json::Object;
280 using llvm::json::Value;
281
282 if (useTable && typeLookup.contains(type)) {
283 // If the type is in the type table, it'll be present in the types
284 // section. Just give the circt type name, which is guaranteed to
285 // uniquely identify the type.
286 std::string typeName;
287 llvm::raw_string_ostream(typeName) << type;
288 return typeName;
289 }
290
291 std::string m;
292 Object o =
293 // This is not complete. Build out as necessary.
294 TypeSwitch<Type, Object>(type)
295 .Case([&](ChannelType t) {
296 m = "channel";
297 return Object({{"inner", json(errorOp, t.getInner(), false)}});
298 })
299 .Case([&](ChannelBundleType t) {
300 m = "bundle";
301 Array channels;
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))}});
308 })
309 .Case([&](AnyType t) {
310 m = "any";
311 return Object();
312 })
313 .Case([&](ListType t) {
314 m = "list";
315 return Object(
316 {{"element", json(errorOp, t.getElementType(), useTable)}});
317 })
318 .Case([&](hw::ArrayType t) {
319 m = "array";
320 return Object(
321 {{"size", t.getNumElements()},
322 {"element", json(errorOp, t.getElementType(), useTable)}});
323 })
324 .Case([&](hw::StructType t) {
325 m = "struct";
326 Array fields;
327 for (auto field : t.getElements())
328 fields.push_back(
329 Object({{"name", field.name.getValue()},
330 {"type", json(errorOp, field.type, useTable)}}));
331 return Object({{"fields", Value(std::move(fields))}});
332 })
333 .Case([&](hw::UnionType t) {
334 m = "union";
335 Array fields;
336 for (auto field : t.getElements()) {
337 if (field.offset != 0) {
338 errorOp->emitError()
339 << "ESI system manifest does not support nonzero union "
340 "member offsets: "
341 << t;
342 encounteredError = true;
343 return Object();
344 }
345 fields.push_back(
346 Object({{"name", field.name.getValue()},
347 {"type", json(errorOp, field.type, useTable)}}));
348 }
349 return Object({{"fields", Value(std::move(fields))}});
350 })
351 .Case([&](hw::TypeAliasType t) {
352 m = "alias";
353 return Object(
354 {{"name", t.getTypeDecl(symCache).getPreferredName()},
355 {"inner", json(errorOp, t.getInnerType(), useTable)}});
356 })
357 .Case([&](IntegerType t) {
358 m = "int";
359 StringRef signedness =
360 t.isSigned() ? "signed"
361 : (t.isUnsigned() ? "unsigned" : "signless");
362 return Object({{"signedness", signedness}});
363 })
364 .Case([&](WindowType t) {
365 m = "window";
366 Array frames;
367 for (auto frame : t.getFrames()) {
368 Array fields;
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)));
376 }
377 frames.push_back(
378 Value(Object({{"name", frame.getName().getValue()},
379 {"fields", Value(std::move(fields))}})));
380 }
381 return Object(
382 {{"name", t.getName().getValue()},
383 {"into", json(errorOp, t.getInto(), useTable)},
384 {"frames", Value(std::move(frames))},
385 {"loweredType", json(errorOp, t.getLoweredType(), useTable)}});
386 })
387 .Default([&](Type t) {
388 errorOp->emitWarning()
389 << "ESI system manifest: unknown type: " << t;
390 return Object();
391 });
392
393 // Common metadata.
394 std::string typeID;
395 llvm::raw_string_ostream(typeID) << type;
396 o["id"] = typeID;
397
398 // Get the hw bitwidth if possible.
399 Type hwType = 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);
405 if (width >= 0)
406 o["hwBitwidth"] = width;
407
408 o["dialect"] = type.getDialect().getNamespace();
409 if (m.length())
410 o["mnemonic"] = m;
411 return o;
412}
413
414// Serialize an attribute to a JSON value.
415// NOLINTNEXTLINE(misc-no-recursion)
416llvm::json::Value ESIBuildManifestPass::json(Operation *errorOp, Attribute attr,
417 bool elideType) {
418
419 // This is far from complete. Build out as necessary.
420 using llvm::json::Object;
421 using llvm::json::Value;
422 Value value =
423 TypeSwitch<Attribute, Value>(attr)
424 .Case([&](FlatSymbolRefAttr ref) {
425 symbols.insert(ref);
426 std::string value;
427 llvm::raw_string_ostream(value) << ref;
428 return value;
429 })
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); }));
436 })
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());
442 return dict;
443 })
444 .Case([&](AppIDAttr appid) {
445 llvm::json::Object dict;
446 dict["name"] = appid.getName().getValue();
447 auto idx = appid.getIndex();
448 if (idx)
449 dict["index"] = *idx;
450 return dict;
451 })
452 .Default([&](Attribute a) {
453 std::string value;
454 llvm::raw_string_ostream(value) << a;
455 return value;
456 });
457
458 // Don't print the type if it's None or we're eliding it.
459 auto typedAttr = llvm::dyn_cast<TypedAttr>(attr);
460 if (elideType || !typedAttr || isa<NoneType>(typedAttr.getType()))
461 return value;
462
463 // Otherwise, return an object with the value and type.
464 Object dict;
465 dict["value"] = value;
466 dict["type"] = useType(typedAttr.getType());
467 return dict;
468}
469
470std::unique_ptr<OperationPass<ModuleOp>>
472 return std::make_unique<ESIBuildManifestPass>();
473}
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:28
The "any" type is a special type which can be used to represent any type, as identified by the type i...
Definition Types.h:159
Channels are the basic communication primitives.
Definition Types.h:125
const Type * getInner() const
Definition Types.h:129
Integers are bit vectors which may be signed or unsigned and are interpreted as numbers.
Definition Types.h:218
Lists represent variable-length sequences of elements of a single type.
Definition Types.h:387
const Type * getElementType() const
Definition Types.h:392
Root class of the ESI type system.
Definition Types.h:36
Windows represent a fixed-size sliding window over a stream of data.
Definition Types.h:316
const Type * getLoweredType() const
Definition Types.h:339
const std::string & getName() const
Definition Types.h:337
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
Definition Evaluator.h:291
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition esi.py:1