CIRCT  19.0.0git
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 
22 using namespace circt;
23 using namespace esi;
24 
25 namespace {
26 struct ESIBuildManifestPass
27  : public ESIBuildManifestBase<ESIBuildManifestPass> {
28  void runOnOperation() override;
29 
30 private:
31  /// Get the types of an operations, but only if the operation is relevant.
32  void gatherFilters(Operation *);
33  void gatherFilters(Attribute);
34 
35  /// Get a JSON representation of a type.
36  llvm::json::Value json(Operation *errorOp, Type);
37  /// Get a JSON representation of a type.
38  llvm::json::Value json(Operation *errorOp, Attribute);
39 
40  // Output a node in the appid hierarchy.
41  void emitNode(llvm::json::OStream &, AppIDHierNodeOp nodeOp);
42  // Output the manifest data of a node in the appid hierarchy.
43  void emitBlock(llvm::json::OStream &, Block &block);
44 
45  AppIDHierRootOp appidRoot;
46 
47  /// Get a JSON representation of the manifest.
48  std::string json();
49 
50  // Type table.
51  void addType(Type type) {
52  if (typeLookup.count(type))
53  return;
54  typeLookup[type] = types.size();
55  types.push_back(type);
56  }
57  SmallVector<Type, 8> types;
58  DenseMap<Type, size_t> typeLookup;
59 
60  // Symbols which are referenced.
61  DenseSet<SymbolRefAttr> symbols;
62 
63  hw::HWSymbolCache symCache;
64 };
65 } // anonymous namespace
66 
67 void ESIBuildManifestPass::runOnOperation() {
68  MLIRContext *ctxt = &getContext();
69  Operation *mod = getOperation();
70  symCache.addDefinitions(mod);
71  symCache.freeze();
72 
73  // Find the top level appid hierarchy root.
74  for (auto root : mod->getRegion(0).front().getOps<AppIDHierRootOp>())
75  if (root.getTopModuleRef() == top)
76  appidRoot = root;
77  if (!appidRoot)
78  return;
79 
80  // Gather the relevant types under the appid hierarchy root only. This avoids
81  // scraping unnecessary types.
82  appidRoot->walk([&](Operation *op) { gatherFilters(op); });
83 
84  // JSONify the manifest.
85  std::string jsonManifest = json();
86 
87  std::error_code ec;
88  llvm::raw_fd_ostream os("esi_system_manifest.json", ec);
89  if (ec) {
90  mod->emitError() << "Failed to open file for writing: " << ec.message();
91  signalPassFailure();
92  } else {
93  os << jsonManifest << "\n";
94  }
95 
96  // If zlib is available, compress the manifest and append it to the module.
97  SmallVector<uint8_t, 10 * 1024> compressedManifest;
98  if (llvm::compression::zlib::isAvailable()) {
99  // Compress the manifest.
100  llvm::compression::zlib::compress(
101  ArrayRef((uint8_t *)jsonManifest.data(), jsonManifest.length()),
102  compressedManifest, llvm::compression::zlib::BestSizeCompression);
103 
104  llvm::raw_fd_ostream bos("esi_system_manifest.json.zlib", ec);
105  if (ec) {
106  mod->emitError() << "Failed to open compressed file for writing: "
107  << ec.message();
108  signalPassFailure();
109  } else {
110  bos.write((char *)compressedManifest.data(), compressedManifest.size());
111  }
112 
113  OpBuilder b(symCache.getDefinition(appidRoot.getTopModuleRefAttr())
114  ->getRegion(0)
115  .front()
116  .getTerminator());
117  b.create<CompressedManifestOp>(b.getUnknownLoc(),
118  BlobAttr::get(ctxt, compressedManifest));
119  } else {
120  mod->emitWarning()
121  << "zlib not available but required for manifest support";
122  }
123 }
124 
125 void ESIBuildManifestPass::emitNode(llvm::json::OStream &j,
126  AppIDHierNodeOp nodeOp) {
127  j.object([&] {
128  j.attribute("app_id", json(nodeOp, nodeOp.getAppIDAttr()));
129  j.attribute("inst_of", json(nodeOp, nodeOp.getModuleRefAttr()));
130  j.attributeArray("contents",
131  [&]() { emitBlock(j, nodeOp.getChildren().front()); });
132  j.attributeArray("children", [&]() {
133  for (auto nodeOp : nodeOp.getChildren().front().getOps<AppIDHierNodeOp>())
134  emitNode(j, nodeOp);
135  });
136  });
137 }
138 
139 void ESIBuildManifestPass::emitBlock(llvm::json::OStream &j, Block &block) {
140  for (auto manifestData : block.getOps<IsManifestData>())
141  j.object([&] {
142  j.attribute("class", manifestData.getManifestClass());
143  SmallVector<NamedAttribute, 4> attrs;
144  manifestData.getDetails(attrs);
145  for (auto attr : attrs)
146  j.attribute(attr.getName().getValue(),
147  json(manifestData, attr.getValue()));
148  });
149 }
150 
151 std::string ESIBuildManifestPass::json() {
152  auto mod = getOperation();
153  std::string jsonStrBuffer;
154  llvm::raw_string_ostream os(jsonStrBuffer);
155  llvm::json::OStream j(os, 2);
156 
157  j.objectBegin();
158  j.attribute("api_version", esiApiVersion);
159 
160  j.attributeArray("symbols", [&]() {
161  for (auto symInfo : mod.getBody()->getOps<SymbolMetadataOp>()) {
162  if (!symbols.contains(symInfo.getSymbolRefAttr()))
163  continue;
164  j.object([&] {
165  SmallVector<NamedAttribute, 4> attrs;
166  symInfo.getDetails(attrs);
167  for (auto attr : attrs)
168  j.attribute(attr.getName().getValue(),
169  json(symInfo, attr.getValue()));
170  });
171  }
172  });
173 
174  j.attributeObject("design", [&]() {
175  j.attribute("inst_of", json(appidRoot, appidRoot.getTopModuleRefAttr()));
176  j.attributeArray("contents",
177  [&]() { emitBlock(j, appidRoot.getChildren().front()); });
178  j.attributeArray("children", [&]() {
179  for (auto nodeOp :
180  appidRoot.getChildren().front().getOps<AppIDHierNodeOp>())
181  emitNode(j, nodeOp);
182  });
183  });
184 
185  j.attributeArray("service_decls", [&]() {
186  for (auto svcDecl : mod.getBody()->getOps<ServiceDeclOpInterface>()) {
187  auto sym = FlatSymbolRefAttr::get(svcDecl);
188  if (!symbols.contains(sym))
189  continue;
190  j.object([&] {
191  j.attribute("symbol", sym.getValue());
192  std::optional<StringRef> typeName = svcDecl.getTypeName();
193  if (typeName)
194  j.attribute("type_name", *typeName);
195  llvm::SmallVector<ServicePortInfo, 8> ports;
196  svcDecl.getPortList(ports);
197  j.attributeArray("ports", [&]() {
198  for (auto port : ports) {
199  j.object([&] {
200  j.attribute("name", port.port.getName().getValue());
201  j.attribute("type", json(svcDecl, TypeAttr::get(port.type)));
202  });
203  }
204  });
205  });
206  }
207  });
208 
209  j.attributeArray("types", [&]() {
210  for (auto type : types) {
211  j.value(json(mod, type));
212  }
213  });
214  j.objectEnd();
215 
216  return jsonStrBuffer;
217 }
218 
219 void ESIBuildManifestPass::gatherFilters(Operation *op) {
220  for (auto oper : op->getOperands())
221  addType(oper.getType());
222  for (auto res : op->getResults())
223  addType(res.getType());
224 
225  // If op is a manifest data op, we only need to include types found in the
226  // details it reports.
227  SmallVector<NamedAttribute> attrs;
228  if (auto manifestData = dyn_cast<IsManifestData>(op))
229  manifestData.getDetails(attrs);
230  else
231  llvm::append_range(attrs, op->getAttrs());
232  for (auto attr : attrs)
233  gatherFilters(attr.getValue());
234 }
235 
236 // NOLINTNEXTLINE(misc-no-recursion)
237 void ESIBuildManifestPass::gatherFilters(Attribute attr) {
238  // This is far from complete. Build out as necessary.
239  TypeSwitch<Attribute>(attr)
240  .Case([&](TypeAttr a) { addType(a.getValue()); })
241  .Case([&](FlatSymbolRefAttr a) { symbols.insert(a); })
242  .Case([&](hw::InnerRefAttr a) { symbols.insert(a.getModuleRef()); })
243  .Case([&](ArrayAttr a) {
244  for (auto attr : a)
245  gatherFilters(attr);
246  })
247  .Case([&](DictionaryAttr a) {
248  for (const auto &entry : a.getValue())
249  gatherFilters(entry.getValue());
250  });
251 }
252 
253 /// Get a JSON representation of a type.
254 // NOLINTNEXTLINE(misc-no-recursion)
255 llvm::json::Value ESIBuildManifestPass::json(Operation *errorOp, Type type) {
256  using llvm::json::Array;
257  using llvm::json::Object;
258  using llvm::json::Value;
259 
260  std::string m;
261  Object o =
262  // This is not complete. Build out as necessary.
263  TypeSwitch<Type, Object>(type)
264  .Case([&](ChannelType t) {
265  m = "channel";
266  return Object({{"inner", json(errorOp, t.getInner())}});
267  })
268  .Case([&](ChannelBundleType t) {
269  m = "bundle";
270  Array channels;
271  for (auto field : t.getChannels())
272  channels.push_back(Object(
273  {{"name", field.name.getValue()},
274  {"direction", stringifyChannelDirection(field.direction)},
275  {"type", json(errorOp, field.type)}}));
276  return Object({{"channels", Value(std::move(channels))}});
277  })
278  .Case([&](AnyType t) {
279  m = "any";
280  return Object();
281  })
282  .Case([&](ListType t) {
283  m = "list";
284  return Object({{"element", json(errorOp, t.getElementType())}});
285  })
286  .Case([&](hw::ArrayType t) {
287  m = "array";
288  return Object({{"size", t.getNumElements()},
289  {"element", json(errorOp, t.getElementType())}});
290  })
291  .Case([&](hw::StructType t) {
292  m = "struct";
293  Array fields;
294  for (auto field : t.getElements())
295  fields.push_back(Object({{"name", field.name.getValue()},
296  {"type", json(errorOp, field.type)}}));
297  return Object({{"fields", Value(std::move(fields))}});
298  })
299  .Case([&](hw::TypeAliasType t) {
300  m = "alias";
301  return Object({{"name", t.getTypeDecl(symCache).getPreferredName()},
302  {"inner", json(errorOp, t.getInnerType())}});
303  })
304  .Case([&](IntegerType t) {
305  m = "int";
306  StringRef signedness =
307  t.isSigned() ? "signed"
308  : (t.isUnsigned() ? "unsigned" : "signless");
309  return Object({{"signedness", signedness}});
310  })
311  .Default([&](Type t) {
312  errorOp->emitWarning()
313  << "ESI system manifest: unknown type: " << t;
314  return Object();
315  });
316 
317  // Common metadata.
318  std::string circtName;
319  llvm::raw_string_ostream(circtName) << type;
320  o["circt_name"] = circtName;
321 
322  int64_t width = hw::getBitWidth(type);
323  if (auto chanType = dyn_cast<ChannelType>(type))
324  width = hw::getBitWidth(chanType.getInner());
325  if (width >= 0)
326  o["hw_bitwidth"] = width;
327 
328  o["dialect"] = type.getDialect().getNamespace();
329  if (m.length())
330  o["mnemonic"] = m;
331  return o;
332 }
333 
334 // Serialize an attribute to a JSON value.
335 // NOLINTNEXTLINE(misc-no-recursion)
336 llvm::json::Value ESIBuildManifestPass::json(Operation *errorOp,
337  Attribute attr) {
338  // This is far from complete. Build out as necessary.
339  using llvm::json::Value;
340  return TypeSwitch<Attribute, Value>(attr)
341  .Case([&](StringAttr a) { return a.getValue(); })
342  .Case([&](IntegerAttr a) { return a.getValue().getLimitedValue(); })
343  .Case([&](TypeAttr a) {
344  Type t = a.getValue();
345 
346  llvm::json::Object typeMD;
347  if (typeLookup.contains(t)) {
348  // If the type is in the type table, it'll be present in the types
349  // section. Just give the circt type name, which is guaranteed to
350  // uniquely identify the type.
351  std::string buff;
352  llvm::raw_string_ostream(buff) << a;
353  typeMD["circt_name"] = buff;
354  return typeMD;
355  }
356 
357  typeMD["type"] = json(errorOp, t);
358  return typeMD;
359  })
360  .Case([&](ArrayAttr a) {
361  return llvm::json::Array(
362  llvm::map_range(a, [&](Attribute a) { return json(errorOp, a); }));
363  })
364  .Case([&](DictionaryAttr a) {
365  llvm::json::Object dict;
366  for (const auto &entry : a.getValue())
367  dict[entry.getName().getValue()] = json(errorOp, entry.getValue());
368  return dict;
369  })
370  .Case([&](hw::InnerRefAttr ref) {
371  llvm::json::Object dict;
372  dict["outer_sym"] = ref.getModule().getValue();
373  dict["inner"] = ref.getName().getValue();
374  return dict;
375  })
376  .Case([&](AppIDAttr appid) {
377  llvm::json::Object dict;
378  dict["name"] = appid.getName().getValue();
379  auto idx = appid.getIndex();
380  if (idx)
381  dict["index"] = *idx;
382  return dict;
383  })
384  .Default([&](Attribute a) {
385  std::string buff;
386  llvm::raw_string_ostream(buff) << a;
387  return buff;
388  });
389 }
390 
391 std::unique_ptr<OperationPass<ModuleOp>>
393  return std::make_unique<ESIBuildManifestPass>();
394 }
int32_t width
Definition: FIRRTL.cpp:36
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
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
constexpr uint64_t esiApiVersion
Manifest format version number.
Definition: ESIDialect.h:32
std::unique_ptr< OperationPass< ModuleOp > > createESIBuildManifestPass()
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
evaluator::ObjectValue Object
Definition: Evaluator.h:411
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: esi.py:1