CIRCT 20.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};
78} // anonymous namespace
79
80void ESIBuildManifestPass::runOnOperation() {
81 MLIRContext *ctxt = &getContext();
82 Operation *mod = getOperation();
83 symCache.addDefinitions(mod);
84 symCache.freeze();
85
86 // Find the top level appid hierarchy root.
87 for (auto root : mod->getRegion(0).front().getOps<AppIDHierRootOp>())
88 if (root.getTopModuleRef() == top)
89 appidRoot = root;
90 if (!appidRoot)
91 return;
92
93 // JSONify the manifest.
94 std::string jsonManifest = json();
95
96 std::error_code ec;
97 llvm::raw_fd_ostream os("esi_system_manifest.json", ec);
98 if (ec) {
99 mod->emitError() << "Failed to open file for writing: " << ec.message();
100 signalPassFailure();
101 } else {
102 os << jsonManifest << "\n";
103 }
104
105 // If zlib is available, compress the manifest and append it to the module.
106 SmallVector<uint8_t, 10 * 1024> compressedManifest;
107 if (llvm::compression::zlib::isAvailable()) {
108 // Compress the manifest.
109 llvm::compression::zlib::compress(
110 ArrayRef((uint8_t *)jsonManifest.data(), jsonManifest.length()),
111 compressedManifest, llvm::compression::zlib::BestSizeCompression);
112
113 llvm::raw_fd_ostream bos("esi_system_manifest.json.zlib", ec);
114 if (ec) {
115 mod->emitError() << "Failed to open compressed file for writing: "
116 << ec.message();
117 signalPassFailure();
118 } else {
119 bos.write((char *)compressedManifest.data(), compressedManifest.size());
120 }
121
122 OpBuilder b(symCache.getDefinition(appidRoot.getTopModuleRefAttr())
123 ->getRegion(0)
124 .front()
125 .getTerminator());
126 b.create<CompressedManifestOp>(b.getUnknownLoc(),
127 BlobAttr::get(ctxt, compressedManifest));
128 } else {
129 mod->emitWarning()
130 << "zlib not available but required for manifest support";
131 }
132}
133
134void ESIBuildManifestPass::emitNode(llvm::json::OStream &j,
135 AppIDHierNodeOp nodeOp) {
136 std::set<StringRef> classesToEmit;
137 for (auto manifestData : nodeOp.getOps<IsManifestData>())
138 classesToEmit.insert(manifestData.getManifestClass());
139 j.object([&] {
140 j.attribute("appID", json(nodeOp, nodeOp.getAppIDAttr()));
141 j.attribute("instanceOf", json(nodeOp, nodeOp.getModuleRefAttr()));
142 for (StringRef manifestClass : classesToEmit)
143 j.attributeArray(manifestClass.str() + "s", [&]() {
144 emitBlock(j, nodeOp.getChildren().front(), manifestClass);
145 });
146 j.attributeArray("children", [&]() {
147 for (auto nodeOp : nodeOp.getChildren().front().getOps<AppIDHierNodeOp>())
148 emitNode(j, nodeOp);
149 });
150 });
151}
152
153void ESIBuildManifestPass::emitBlock(llvm::json::OStream &j, Block &block,
154 StringRef manifestClass) {
155 for (auto manifestData : block.getOps<IsManifestData>()) {
156 if (manifestData.getManifestClass() != manifestClass)
157 continue;
158 j.object([&] {
159 SmallVector<NamedAttribute, 4> attrs;
160 manifestData.getDetails(attrs);
161 for (auto attr : attrs)
162 j.attribute(attr.getName().getValue(),
163 json(manifestData, attr.getValue()));
164 });
165 }
166}
167
168std::string ESIBuildManifestPass::json() {
169 auto mod = getOperation();
170 std::string jsonStrBuffer;
171 llvm::raw_string_ostream os(jsonStrBuffer);
172 llvm::json::OStream j(os, 2);
173
174 j.objectBegin(); // Top level object.
175 j.attribute("apiVersion", esiApiVersion);
176
177 std::set<StringRef> classesToEmit;
178 for (auto manifestData : appidRoot.getOps<IsManifestData>())
179 classesToEmit.insert(manifestData.getManifestClass());
180
181 j.attributeObject("design", [&]() {
182 j.attribute("instanceOf", json(appidRoot, appidRoot.getTopModuleRefAttr()));
183 modules.insert(appidRoot.getTopModuleRefAttr());
184 for (StringRef manifestClass : classesToEmit)
185 j.attributeArray(manifestClass.str() + "s", [&]() {
186 emitBlock(j, appidRoot.getChildren().front(), manifestClass);
187 });
188 j.attributeArray("children", [&]() {
189 for (auto nodeOp :
190 appidRoot.getChildren().front().getOps<AppIDHierNodeOp>())
191 emitNode(j, nodeOp);
192 });
193 });
194
195 j.attributeArray("serviceDeclarations", [&]() {
196 for (auto svcDecl : mod.getBody()->getOps<ServiceDeclOpInterface>()) {
197 auto sym = FlatSymbolRefAttr::get(svcDecl);
198 if (!symbols.contains(sym))
199 continue;
200 j.object([&] {
201 j.attribute("symbol", json(svcDecl, sym, /*elideType=*/true));
202 std::optional<StringRef> typeName = svcDecl.getTypeName();
203 if (typeName)
204 j.attribute("serviceName", *typeName);
205 llvm::SmallVector<ServicePortInfo, 8> ports;
206 svcDecl.getPortList(ports);
207 j.attributeArray("ports", [&]() {
208 for (auto port : ports) {
209 j.object([&] {
210 j.attribute("name", port.port.getName().getValue());
211 j.attribute("typeID", useType(port.type));
212 });
213 }
214 });
215 });
216 }
217 });
218
219 j.attributeArray("modules", [&]() {
220 // Map from symbol to all manifest data ops related to said symbol.
221 llvm::MapVector<SymbolRefAttr, SmallVector<IsManifestData>>
222 symbolInfoLookup;
223 // Ensure that all symbols are present in the lookup even if they have no
224 // manifest metadata.
225 for (auto modSymbol : modules)
226 symbolInfoLookup[modSymbol] = {};
227 // Gather all manifest data for each symbol.
228 for (auto symInfo : mod.getBody()->getOps<IsManifestData>()) {
229 FlatSymbolRefAttr sym = symInfo.getSymbolRefAttr();
230 if (!sym || !symbols.contains(sym))
231 continue;
232 symbolInfoLookup[sym].push_back(symInfo);
233 }
234
235 // Now, emit a JSON object for each symbol.
236 for (const auto &symNameInfo : symbolInfoLookup) {
237 j.object([&] {
238 std::string symbolStr;
239 llvm::raw_string_ostream(symbolStr) << symNameInfo.first;
240 j.attribute("symbol", symbolStr);
241 for (auto symInfo : symNameInfo.second) {
242 j.attributeBegin(symInfo.getManifestClass());
243 j.object([&] {
244 SmallVector<NamedAttribute, 4> attrs;
245 symInfo.getDetails(attrs);
246 for (auto attr : attrs) {
247 if (attr.getName().getValue() == "symbolRef")
248 continue;
249 j.attribute(attr.getName().getValue(),
250 json(symInfo, attr.getValue()));
251 }
252 });
253 j.attributeEnd();
254 }
255 });
256 }
257 });
258
259 j.attributeArray("types", [&]() {
260 for (size_t i = 0; i < types.size(); i++)
261 j.value(json(mod, types[i], /*useTable=*/false));
262 });
263
264 j.objectEnd(); // Top level object.
265
266 return jsonStrBuffer;
267}
268
269/// Get a JSON representation of a type.
270// NOLINTNEXTLINE(misc-no-recursion)
271llvm::json::Value ESIBuildManifestPass::json(Operation *errorOp, Type type,
272 bool useTable) {
273 using llvm::json::Array;
274 using llvm::json::Object;
275 using llvm::json::Value;
276
277 if (useTable && typeLookup.contains(type)) {
278 // If the type is in the type table, it'll be present in the types
279 // section. Just give the circt type name, which is guaranteed to
280 // uniquely identify the type.
281 std::string typeName;
282 llvm::raw_string_ostream(typeName) << type;
283 return typeName;
284 }
285
286 std::string m;
287 Object o =
288 // This is not complete. Build out as necessary.
289 TypeSwitch<Type, Object>(type)
290 .Case([&](ChannelType t) {
291 m = "channel";
292 return Object({{"inner", json(errorOp, t.getInner(), false)}});
293 })
294 .Case([&](ChannelBundleType t) {
295 m = "bundle";
296 Array channels;
297 for (auto field : t.getChannels())
298 channels.push_back(Object(
299 {{"name", field.name.getValue()},
300 {"direction", stringifyChannelDirection(field.direction)},
301 {"type", json(errorOp, field.type, useTable)}}));
302 return Object({{"channels", Value(std::move(channels))}});
303 })
304 .Case([&](AnyType t) {
305 m = "any";
306 return Object();
307 })
308 .Case([&](ListType t) {
309 m = "list";
310 return Object(
311 {{"element", json(errorOp, t.getElementType(), useTable)}});
312 })
313 .Case([&](hw::ArrayType t) {
314 m = "array";
315 return Object(
316 {{"size", t.getNumElements()},
317 {"element", json(errorOp, t.getElementType(), useTable)}});
318 })
319 .Case([&](hw::StructType t) {
320 m = "struct";
321 Array fields;
322 for (auto field : t.getElements())
323 fields.push_back(
324 Object({{"name", field.name.getValue()},
325 {"type", json(errorOp, field.type, useTable)}}));
326 return Object({{"fields", Value(std::move(fields))}});
327 })
328 .Case([&](hw::TypeAliasType t) {
329 m = "alias";
330 return Object(
331 {{"name", t.getTypeDecl(symCache).getPreferredName()},
332 {"inner", json(errorOp, t.getInnerType(), useTable)}});
333 })
334 .Case([&](IntegerType t) {
335 m = "int";
336 StringRef signedness =
337 t.isSigned() ? "signed"
338 : (t.isUnsigned() ? "unsigned" : "signless");
339 return Object({{"signedness", signedness}});
340 })
341 .Default([&](Type t) {
342 errorOp->emitWarning()
343 << "ESI system manifest: unknown type: " << t;
344 return Object();
345 });
346
347 // Common metadata.
348 std::string typeID;
349 llvm::raw_string_ostream(typeID) << type;
350 o["id"] = typeID;
351
352 int64_t width = hw::getBitWidth(type);
353 if (auto chanType = dyn_cast<ChannelType>(type))
354 width = hw::getBitWidth(chanType.getInner());
355 if (width >= 0)
356 o["hwBitwidth"] = width;
357
358 o["dialect"] = type.getDialect().getNamespace();
359 if (m.length())
360 o["mnemonic"] = m;
361 return o;
362}
363
364// Serialize an attribute to a JSON value.
365// NOLINTNEXTLINE(misc-no-recursion)
366llvm::json::Value ESIBuildManifestPass::json(Operation *errorOp, Attribute attr,
367 bool elideType) {
368
369 // This is far from complete. Build out as necessary.
370 using llvm::json::Object;
371 using llvm::json::Value;
372 Value value =
373 TypeSwitch<Attribute, Value>(attr)
374 .Case([&](FlatSymbolRefAttr ref) {
375 symbols.insert(ref);
376 std::string value;
377 llvm::raw_string_ostream(value) << ref;
378 return value;
379 })
380 .Case([&](StringAttr a) { return a.getValue(); })
381 .Case([&](IntegerAttr a) { return a.getValue().getLimitedValue(); })
382 .Case([&](TypeAttr a) { return useType(a.getValue()); })
383 .Case([&](ArrayAttr a) {
384 return llvm::json::Array(llvm::map_range(
385 a, [&](Attribute a) { return json(errorOp, a); }));
386 })
387 .Case([&](DictionaryAttr a) {
388 llvm::json::Object dict;
389 for (const auto &entry : a.getValue())
390 dict[entry.getName().getValue()] =
391 json(errorOp, entry.getValue());
392 return dict;
393 })
394 .Case([&](AppIDAttr appid) {
395 llvm::json::Object dict;
396 dict["name"] = appid.getName().getValue();
397 auto idx = appid.getIndex();
398 if (idx)
399 dict["index"] = *idx;
400 return dict;
401 })
402 .Default([&](Attribute a) {
403 std::string value;
404 llvm::raw_string_ostream(value) << a;
405 return value;
406 });
407
408 // Don't print the type if it's None or we're eliding it.
409 auto typedAttr = llvm::dyn_cast<TypedAttr>(attr);
410 if (elideType || !typedAttr || isa<NoneType>(typedAttr.getType()))
411 return value;
412
413 // Otherwise, return an object with the value and type.
414 Object dict;
415 dict["value"] = value;
416 dict["type"] = useType(typedAttr.getType());
417 return dict;
418}
419
420std::unique_ptr<OperationPass<ModuleOp>>
422 return std::make_unique<ESIBuildManifestPass>();
423}
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:27
The "any" type is a special type which can be used to represent any type, as identified by the type i...
Definition Types.h:85
Channels are the basic communication primitives.
Definition Types.h:63
const Type * getInner() const
Definition Types.h:66
Integers are bit vectors which may be signed or unsigned and are interpreted as numbers.
Definition Types.h:112
Root class of the ESI type system.
Definition Types.h:27
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:411
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition esi.py:1