CIRCT 24.0.0git
Loading...
Searching...
No Matches
Manifest.cpp
Go to the documentation of this file.
1//===- Manifest.cpp - Metadata on the accelerator -------------------------===//
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// DO NOT EDIT!
10// This file is distributed as part of an ESI package. The source for this file
11// should always be modified within CIRCT (lib/dialect/ESI/runtime/cpp/).
12//
13//===----------------------------------------------------------------------===//
14
15#include "esi/Manifest.h"
16#include "esi/Accelerator.h"
17#include "esi/Services.h"
18
19#if defined(__GNUC__)
20#pragma GCC diagnostic push
21#pragma GCC diagnostic ignored "-Wcovered-switch-default"
22#endif
23#include <nlohmann/json.hpp>
24#if defined(__GNUC__)
25#pragma GCC diagnostic pop
26#endif
27#include <sstream>
28
29using namespace ::esi;
30
31// This is a proxy class to the manifest JSON. It is used to avoid having to
32// include the JSON parser in the header. Forward references don't work since
33// nlohmann::json is a rather complex template.
34//
35// Plus, it allows us to hide some implementation functions from the header
36// file.
38 friend class ::esi::Manifest;
39
40public:
41 Impl(Context &ctxt, const std::string &jsonManifest);
42
43 auto at(const std::string &key) const { return manifestJson.at(key); }
44
45 // Get the module info (if any) for the module instance in 'json'.
46 std::optional<ModuleInfo> getModInfo(const nlohmann::json &) const;
47
48 /// Go through the "service_decls" section of the manifest and populate the
49 /// services table as appropriate.
50 void scanServiceDecls(AcceleratorConnection &, const nlohmann::json &,
51 ServiceTable &) const;
52
53 void createEngine(AcceleratorConnection &, AppIDPath appID,
54 const nlohmann::json &) const;
55
56 /// Get a Service for the service specified in 'json'. Update the
57 /// activeServices table. TODO: re-using this for the engines section is a
58 /// terrible hack. Figure out a better way.
59 services::Service *getService(AppIDPath idPath, AcceleratorConnection &,
60 const nlohmann::json &,
61 ServiceTable &activeServices,
62 bool isEngine = false) const;
63
64 /// Get all the services in the description of an instance. Update the active
65 /// services table.
66 std::vector<services::Service *>
67 getServices(AppIDPath idPath, AcceleratorConnection &, const nlohmann::json &,
68 ServiceTable &activeServices) const;
69
70 /// Get the bundle ports for the instance at 'idPath' and specified in
71 /// 'instJson'. Look them up in 'activeServies'.
72 std::vector<std::unique_ptr<BundlePort>>
73 getBundlePorts(AcceleratorConnection &acc, AppIDPath idPath,
74 const ServiceTable &activeServices,
75 const nlohmann::json &instJson) const;
76
77 /// Build the set of child instances (recursively) for the module instance
78 /// description.
79 std::vector<std::unique_ptr<Instance>>
80 getChildInstances(AppIDPath idPath, AcceleratorConnection &acc,
81 const ServiceTable &activeServices,
82 const nlohmann::json &instJson) const;
83
84 /// Get a single child instance. Implicitly copy the active services table so
85 /// that it can be safely updated for the child's branch of the tree.
86 std::unique_ptr<Instance>
87 getChildInstance(AppIDPath idPath, AcceleratorConnection &acc,
88 ServiceTable activeServices,
89 const nlohmann::json &childJson) const;
90
91 /// Parse all the types and populate the types table.
92 void populateTypes(const nlohmann::json &typesJson);
93
94 /// Get the ordered list of types from the manifest.
95 const std::vector<const Type *> &getTypeTable() const { return _typeTable; }
96
97 /// Build a dynamic API for the Accelerator connection 'acc' based on the
98 /// manifest stored herein.
99 std::unique_ptr<Accelerator>
100 buildAccelerator(AcceleratorConnection &acc) const;
101
102 const Type *parseType(const nlohmann::json &typeJson);
103
104 const std::map<std::string, const ModuleInfo> &getSymbolInfo() const {
105 return symbolInfoCache;
106 }
107
108private:
110 std::vector<const Type *> _typeTable;
111
112 std::optional<const Type *> getType(Type::ID id) const {
113 return ctxt.getType(id);
114 }
115
116 std::any getAny(const nlohmann::json &value) const;
117 void parseModuleMetadata(ModuleInfo &info, const nlohmann::json &mod) const;
118 void parseModuleConsts(ModuleInfo &info, const nlohmann::json &mod) const;
119
120 // The parsed json.
121 nlohmann::json manifestJson;
122 // Cache the module info for each symbol.
123 std::map<std::string, const ModuleInfo> symbolInfoCache;
124};
125
126//===----------------------------------------------------------------------===//
127// Simple JSON -> object parsers.
128//===----------------------------------------------------------------------===//
129
130static std::optional<AppID> parseID(const nlohmann::json &jsonID) {
131 if (!jsonID.is_object())
132 return std::nullopt;
133 std::optional<uint32_t> idx;
134 if (jsonID.contains("index"))
135 idx = jsonID.at("index").get<uint32_t>();
136 if (jsonID.contains("name") && jsonID.size() <= 2)
137 return AppID(jsonID.at("name").get<std::string>(), idx);
138 return std::nullopt;
139}
140
141static AppID parseIDChecked(const nlohmann::json &jsonID) {
142 std::optional<AppID> id = parseID(jsonID);
143 if (!id)
144 throw std::runtime_error("Malformed manifest: invalid appID");
145 return *id;
146}
147static AppIDPath parseIDPath(const nlohmann::json &jsonIDPath) {
148 AppIDPath ret;
149 for (auto &idJson : jsonIDPath)
150 ret.push_back(parseIDChecked(idJson));
151 return ret;
152}
153
154static ServicePortDesc parseServicePort(const nlohmann::json &jsonPort) {
155 return ServicePortDesc{jsonPort.at("serviceName").get<std::string>(),
156 jsonPort.at("port").get<std::string>()};
157}
158
159/// Convert the json value to a 'std::any', which can be exposed outside of this
160/// file.
161std::any Manifest::Impl::getAny(const nlohmann::json &value) const {
162 auto getObject = [this](const nlohmann::json &json) -> std::any {
163 std::map<std::string, std::any> ret;
164 for (auto &e : json.items())
165 ret[e.key()] = getAny(e.value());
166
167 // If this can be converted to a constant, do so.
168 if (ret.size() != 2 || !ret.contains("type") || !ret.contains("value"))
169 return ret;
170 std::any value = ret.at("value");
171 std::any typeID = ret.at("type");
172 if (typeID.type() != typeid(std::string))
173 return ret;
174 std::optional<const Type *> type =
175 getType(std::any_cast<std::string>(type));
176 if (!type)
177 return ret;
178 // TODO: Check or guide the conversion of the value to the type based on the
179 // type.
180 return Constant{value, type};
181 };
182
183 auto getArray = [this](const nlohmann::json &json) -> std::any {
184 std::vector<std::any> ret;
185 for (auto &e : json)
186 ret.push_back(getAny(e));
187 return ret;
188 };
189
190 auto getValue = [&](const nlohmann::json &innerValue) -> std::any {
191 if (innerValue.is_string())
192 return innerValue.get<std::string>();
193 else if (innerValue.is_number_unsigned())
194 return innerValue.get<uint64_t>();
195 else if (innerValue.is_number_integer())
196 return innerValue.get<int64_t>();
197 else if (innerValue.is_number_float())
198 return innerValue.get<double>();
199 else if (innerValue.is_boolean())
200 return innerValue.get<bool>();
201 else if (innerValue.is_null())
202 return innerValue.get<std::nullptr_t>();
203 else if (innerValue.is_object())
204 return getObject(innerValue);
205 else if (innerValue.is_array())
206 return getArray(innerValue);
207 else
208 throw std::runtime_error("Unknown type in manifest: " +
209 innerValue.dump(2));
210 };
211
212 std::optional<AppID> appid = parseID(value);
213 if (appid)
214 return *appid;
215 if (!value.is_object() || !value.contains("type") || !value.contains("value"))
216 return getValue(value);
217 return Constant{getValue(value.at("value")), getType(value.at("type"))};
218}
219
221 const nlohmann::json &mod) const {
222 for (auto &extra : mod.items())
223 if (extra.key() != "name" && extra.key() != "summary" &&
224 extra.key() != "version" && extra.key() != "repo" &&
225 extra.key() != "commitHash")
226 info.extra[extra.key()] = getAny(extra.value());
227
228 auto value = [&](const std::string &key) -> std::optional<std::string> {
229 auto f = mod.find(key);
230 if (f == mod.end())
231 return std::nullopt;
232 return f.value();
233 };
234 info.name = value("name");
235 info.summary = value("summary");
236 info.version = value("version");
237 info.repo = value("repo");
238 info.commitHash = value("commitHash");
239}
240
242 const nlohmann::json &mod) const {
243 for (auto &item : mod.items()) {
244 std::any value = getAny(item.value());
245 auto *c = std::any_cast<Constant>(&value);
246 if (c)
247 info.constants[item.key()] = *c;
248 else
249 // If the value isn't a "proper" constant, present it as one with no type.
250 info.constants[item.key()] = Constant{value, std::nullopt};
251 }
252}
253
254//===----------------------------------------------------------------------===//
255// Manifest::Impl class implementation.
256//===----------------------------------------------------------------------===//
257
258Manifest::Impl::Impl(Context &ctxt, const std::string &manifestStr)
259 : ctxt(ctxt) {
260 manifestJson = nlohmann::ordered_json::parse(manifestStr);
261
262 const auto &version = manifestJson.at("apiVersion");
263 if (!version.is_number_unsigned() || version != ExpectedVersionNumber)
264 throw std::runtime_error("Unsupported ESI ABI version: " + version.dump() +
265 " (expected " +
266 std::to_string(ExpectedVersionNumber) + ")");
267
268 try {
269 // Populate the types table first since anything else might need it.
270 populateTypes(manifestJson.at("types"));
271
272 // Populate the symbol info cache.
273 for (auto &mod : manifestJson.at("modules")) {
274 ModuleInfo info;
275 if (mod.contains("symInfo"))
276 parseModuleMetadata(info, mod.at("symInfo"));
277 if (mod.contains("symConsts"))
278 parseModuleConsts(info, mod.at("symConsts"));
279 symbolInfoCache.insert(make_pair(mod.at("symbol"), info));
280 }
281 } catch (const std::exception &e) {
282 std::string msg = "malformed manifest: " + std::string(e.what());
283 throw std::runtime_error(msg);
284 }
285}
286
287std::unique_ptr<Accelerator>
288Manifest::Impl::buildAccelerator(AcceleratorConnection &acc) const {
289 ServiceTable activeSvcs;
290
291 auto designJson = manifestJson.at("design");
292
293 // Create all of the engines at the top level of the design.
294 // TODO: support engines at lower levels.
295 auto enginesIter = designJson.find("engines");
296 if (enginesIter != designJson.end())
297 for (auto &engineDesc : enginesIter.value())
298 createEngine(acc, {}, engineDesc);
299
300 // Get the initial active services table. Update it as we descend down.
301 auto svcDecls = manifestJson.at("serviceDeclarations");
302 scanServiceDecls(acc, svcDecls, activeSvcs);
303
304 // Get the services instantiated at the top level.
305 std::vector<services::Service *> services =
306 getServices({}, acc, designJson, activeSvcs);
307
308 // Get the ports at the top level.
309 auto ports = getBundlePorts(acc, {}, activeSvcs, designJson);
310
311 return std::make_unique<Accelerator>(
312 getModInfo(designJson),
313 getChildInstances({}, acc, activeSvcs, designJson), services,
314 std::move(ports));
315}
316
317std::optional<ModuleInfo>
318Manifest::Impl::getModInfo(const nlohmann::json &json) const {
319 auto instOfIter = json.find("instanceOf");
320 if (instOfIter == json.end())
321 return std::nullopt;
322 auto f = symbolInfoCache.find(instOfIter.value());
323 if (f != symbolInfoCache.end())
324 return f->second;
325 return std::nullopt;
326}
327
328/// TODO: Hack. This method is a giant hack to reuse the getService method for
329/// engines. It works, but it ain't pretty and it ain't right.
330void Manifest::Impl::createEngine(AcceleratorConnection &acc, AppIDPath idPath,
331 const nlohmann::json &eng) const {
332 ServiceTable dummy;
333 getService(idPath, acc, eng, dummy, /*isEngine=*/true);
334}
335
336void Manifest::Impl::scanServiceDecls(AcceleratorConnection &acc,
337 const nlohmann::json &svcDecls,
338 ServiceTable &activeServices) const {
339 for (auto &svcDecl : svcDecls) {
340 // Get the implementation details.
341 ServiceImplDetails svcDetails;
342 for (auto &detail : svcDecl.items())
343 svcDetails[detail.key()] = getAny(detail.value());
344
345 // Create the service.
346 auto serviceNameIter = svcDecl.find("serviceName");
347 std::string serviceName;
348 if (serviceNameIter != svcDecl.end())
349 serviceName = serviceNameIter.value();
350 services::Service::Type svcId =
351 services::ServiceRegistry::lookupServiceType(serviceName);
352 auto svc = acc.getService(svcId, /*id=*/{}, /*implName=*/"",
353 /*details=*/svcDetails, /*clients=*/{});
354 if (svc)
355 activeServices[svcDecl.at("symbol")] = svc;
356 }
357}
358
359std::vector<std::unique_ptr<Instance>>
360Manifest::Impl::getChildInstances(AppIDPath idPath, AcceleratorConnection &acc,
361 const ServiceTable &activeServices,
362 const nlohmann::json &instJson) const {
363 std::vector<std::unique_ptr<Instance>> ret;
364 auto childrenIter = instJson.find("children");
365 if (childrenIter == instJson.end())
366 return ret;
367 for (auto &child : childrenIter.value())
368 ret.emplace_back(getChildInstance(idPath, acc, activeServices, child));
369 return ret;
370}
371
372std::unique_ptr<Instance>
373Manifest::Impl::getChildInstance(AppIDPath idPath, AcceleratorConnection &acc,
374 ServiceTable activeServices,
375 const nlohmann::json &child) const {
376 AppID childID = parseIDChecked(child.at("appID"));
377 idPath.push_back(childID);
378
379 std::vector<services::Service *> services =
380 getServices(idPath, acc, child, activeServices);
381
382 auto children = getChildInstances(idPath, acc, activeServices, child);
383 auto ports = getBundlePorts(acc, idPath, activeServices, child);
384 return std::make_unique<Instance>(parseIDChecked(child.at("appID")),
385 getModInfo(child), std::move(children),
386 services, std::move(ports));
387}
388
389services::Service *Manifest::Impl::getService(AppIDPath idPath,
390 AcceleratorConnection &acc,
391 const nlohmann::json &svcJson,
392 ServiceTable &activeServices,
393 bool isEngine) const {
394
395 AppID id = parseIDChecked(svcJson.at("appID"));
396 idPath.push_back(id);
397
398 // Get all the client info, including the implementation details.
399 HWClientDetails clientDetails;
400 for (auto &client : svcJson.at("clientDetails")) {
401 HWClientDetail clientDetail;
402 for (auto &detail : client.items()) {
403 if (detail.key() == "relAppIDPath")
404 clientDetail.relPath = parseIDPath(detail.value());
405 else if (detail.key() == "servicePort")
406 clientDetail.port = parseServicePort(detail.value());
407 else if (detail.key() == "channelAssignments") {
408 for (auto &chan : detail.value().items()) {
409 ChannelAssignment chanAssign;
410 for (auto &assign : chan.value().items())
411 if (assign.key() == "type")
412 chanAssign.type = assign.value();
413 else
414 chanAssign.implOptions[assign.key()] = getAny(assign.value());
415 clientDetail.channelAssignments[chan.key()] = chanAssign;
416 }
417 } else
418 clientDetail.implOptions[detail.key()] = getAny(detail.value());
419 }
420 clientDetails.push_back(clientDetail);
421 }
422
423 // Get the implementation details.
424 ServiceImplDetails svcDetails;
425 std::string implName;
426 std::string service;
427 for (auto &detail : svcJson.items()) {
428 if (detail.key() == "appID" || detail.key() == "clientDetails")
429 continue;
430 if (detail.key() == "serviceImplName")
431 implName = detail.value();
432 else if (detail.key() == "service")
433 service = detail.value().get<std::string>();
434 else
435 svcDetails[detail.key()] = getAny(detail.value());
436 }
437
438 // Create the service.
439 services::Service *svc = nullptr;
440 auto activeServiceIter = activeServices.find(service);
441 if (activeServiceIter != activeServices.end()) {
442 services::Service::Type svcType =
443 services::ServiceRegistry::lookupServiceType(
444 activeServiceIter->second->getServiceSymbol());
445 svc = activeServiceIter->second->getChildService(svcType, idPath, implName,
446 svcDetails, clientDetails);
447 } else {
448 services::Service::Type svcType =
449 services::ServiceRegistry::lookupServiceType(service);
450 if (isEngine)
451 acc.createEngine(implName, idPath, svcDetails, clientDetails);
452 else
453 svc =
454 acc.getService(svcType, idPath, implName, svcDetails, clientDetails);
455 }
456
457 if (svc)
458 // Update the active services table.
459 activeServices[service] = svc;
460 return svc;
461}
462
463std::vector<services::Service *>
464Manifest::Impl::getServices(AppIDPath idPath, AcceleratorConnection &acc,
465 const nlohmann::json &svcsJson,
466 ServiceTable &activeServices) const {
467 std::vector<services::Service *> ret;
468 auto svcsIter = svcsJson.find("services");
469 if (svcsIter == svcsJson.end())
470 return ret;
471
472 for (auto &svc : svcsIter.value())
473 ret.emplace_back(getService(idPath, acc, svc, activeServices));
474 return ret;
475}
476
477std::vector<std::unique_ptr<BundlePort>>
478Manifest::Impl::getBundlePorts(AcceleratorConnection &acc, AppIDPath idPath,
479 const ServiceTable &activeServices,
480 const nlohmann::json &instJson) const {
481 std::vector<std::unique_ptr<BundlePort>> ret;
482 auto clientPortsIter = instJson.find("clientPorts");
483 if (clientPortsIter == instJson.end())
484 return ret;
485
486 for (auto &content : clientPortsIter.value()) {
487 // Lookup the requested service in the active services table.
488 std::string serviceName = "";
489 if (auto f = content.find("servicePort"); f != content.end())
490 serviceName = parseServicePort(f.value()).name;
491 auto svcIter = activeServices.find(serviceName);
492 if (svcIter == activeServices.end()) {
493 // If a specific service isn't found, search for the default service
494 // (typically provided by a BSP).
495 if (svcIter = activeServices.find(""); svcIter == activeServices.end())
496 throw std::runtime_error(
497 "Malformed manifest: could not find active service '" +
498 serviceName + "'");
499 }
500 services::Service *svc = svcIter->second;
501
502 std::string typeName = content.at("typeID");
503 auto type = getType(typeName);
504 if (!type)
505 throw std::runtime_error(
506 "Malformed manifest: could not find port type '" + typeName + "'");
507 const BundleType *bundleType = dynamic_cast<const BundleType *>(*type);
508 if (!bundleType)
509 throw std::runtime_error("Malformed manifest: type '" + typeName +
510 "' is not a bundle type");
511
512 idPath.push_back(parseIDChecked(content.at("appID")));
513
514 BundlePort *svcPort = svc->getPort(idPath, bundleType);
515 if (svcPort)
516 ret.emplace_back(svcPort);
517 // Since we share idPath between iterations, pop the last element before the
518 // next iteration.
519 idPath.pop_back();
520 }
521 return ret;
522}
523
524namespace {
525const Type *parseType(const nlohmann::json &typeJson, Context &ctxt);
526
527BundleType *parseBundleType(const nlohmann::json &typeJson, Context &cache) {
528 assert(typeJson.at("mnemonic") == "bundle");
529
530 std::vector<std::tuple<std::string, BundleType::Direction, const Type *>>
531 channels;
532 for (auto &chanJson : typeJson["channels"]) {
533 std::string dirStr = chanJson.at("direction");
534 BundleType::Direction dir;
535 if (dirStr == "to")
536 dir = BundleType::Direction::To;
537 else if (dirStr == "from")
538 dir = BundleType::Direction::From;
539 else
540 throw std::runtime_error("Malformed manifest: unknown direction '" +
541 dirStr + "'");
542 channels.emplace_back(chanJson.at("name"), dir,
543 parseType(chanJson["type"], cache));
544 }
545 return new BundleType(typeJson.at("id"), channels);
546}
547
548ChannelType *parseChannelType(const nlohmann::json &typeJson, Context &cache) {
549 assert(typeJson.at("mnemonic") == "channel");
550 return new ChannelType(typeJson.at("id"),
551 parseType(typeJson.at("inner"), cache));
552}
553
554Type *parseInt(const nlohmann::json &typeJson, Context &cache) {
555 assert(typeJson.at("mnemonic") == "int");
556 std::string sign = typeJson.at("signedness");
557 uint64_t width = typeJson.at("hwBitwidth");
558 Type::ID id = typeJson.at("id");
559
560 if (sign == "signed")
561 return new SIntType(id, width);
562 else if (sign == "unsigned")
563 return new UIntType(id, width);
564 else if (sign == "signless" && width == 0)
565 // By convention, a zero-width signless integer is a void type.
566 return new VoidType(id);
567 else if (sign == "signless" && width > 0)
568 return new BitsType(id, width);
569 else
570 throw std::runtime_error("Malformed manifest: unknown sign '" + sign + "'");
571}
572
573StructType *parseStruct(const nlohmann::json &typeJson, Context &cache) {
574 assert(typeJson.at("mnemonic") == "struct");
575 std::vector<std::pair<std::string, const Type *>> fields;
576 for (auto &fieldJson : typeJson["fields"])
577 fields.emplace_back(fieldJson.at("name"),
578 parseType(fieldJson["type"], cache));
579 return new StructType(typeJson.at("id"), fields);
580}
581
582UnionType *parseUnion(const nlohmann::json &typeJson, Context &cache) {
583 assert(typeJson.at("mnemonic") == "union");
584 std::vector<std::pair<std::string, const Type *>> fields;
585 for (auto &fieldJson : typeJson["fields"])
586 fields.emplace_back(fieldJson.at("name"),
587 parseType(fieldJson["type"], cache));
588 return new UnionType(typeJson.at("id"), fields);
589}
590
591ArrayType *parseArray(const nlohmann::json &typeJson, Context &cache) {
592 assert(typeJson.at("mnemonic") == "array");
593 uint64_t size = typeJson.at("size");
594 return new ArrayType(typeJson.at("id"),
595 parseType(typeJson.at("element"), cache), size);
596}
597
598WindowType *parseWindow(const nlohmann::json &typeJson, Context &cache) {
599 assert(typeJson.at("mnemonic") == "window");
600 std::string name = typeJson.at("name");
601 const Type *intoType = parseType(typeJson.at("into"), cache);
602 const Type *loweredType = parseType(typeJson.at("loweredType"), cache);
603
604 // Parse the frames information.
605 std::vector<WindowType::Frame> frames;
606 for (auto &frameJson : typeJson.at("frames")) {
607 WindowType::Frame frame;
608 frame.name = frameJson.at("name");
609 for (auto &fieldJson : frameJson.at("fields")) {
610 WindowType::Field field;
611 field.name = fieldJson.at("name");
612 if (fieldJson.contains("numItems"))
613 field.numItems = fieldJson.at("numItems");
614 if (fieldJson.contains("bulkCountWidth"))
615 field.bulkCountWidth = fieldJson.at("bulkCountWidth");
616 frame.fields.push_back(field);
617 }
618 frames.push_back(frame);
619 }
620
621 return new WindowType(typeJson.at("id"), name, intoType, loweredType, frames);
622}
623
624ListType *parseList(const nlohmann::json &typeJson, Context &cache) {
625 assert(typeJson.at("mnemonic") == "list");
626 return new ListType(typeJson.at("id"),
627 parseType(typeJson.at("element"), cache));
628}
629
630TypeAliasType *parseTypeAlias(const nlohmann::json &typeJson, Context &cache) {
631 assert(typeJson.at("mnemonic") == "alias");
632 std::string id = typeJson.at("id");
633 std::string name = typeJson.value("name", "");
634
635 const Type *innerType = nullptr;
636 if (typeJson.contains("inner"))
637 innerType = parseType(typeJson.at("inner"), cache);
638 else
639 throw std::runtime_error("typealias missing inner type");
640
641 if (name.empty())
642 name = id;
643
644 return new TypeAliasType(id, name, innerType);
645}
646
647using TypeParser = std::function<Type *(const nlohmann::json &, Context &)>;
648const std::map<std::string_view, TypeParser> typeParsers = {
649 {"bundle", parseBundleType},
650 {"channel", parseChannelType},
651 {"std::any", [](const nlohmann::json &typeJson,
652 Context &cache) { return new AnyType(typeJson.at("id")); }},
653 {"int", parseInt},
654 {"alias", parseTypeAlias},
655 {"struct", parseStruct},
656 {"union", parseUnion},
657 {"array", parseArray},
658 {"window", parseWindow},
659 {"list", parseList},
660
661};
662
663// Parse a type if it doesn't already exist in the cache.
664const Type *parseType(const nlohmann::json &typeJson, Context &cache) {
665 std::string id;
666 if (typeJson.is_string())
667 id = typeJson.get<std::string>();
668 else
669 id = typeJson.at("id");
670 if (std::optional<const Type *> t = cache.getType(id))
671 return *t;
672 if (typeJson.is_string())
673 throw std::runtime_error("malformed manifest: unknown type '" + id + "'");
674
675 Type *t;
676 std::string mnemonic = typeJson.at("mnemonic");
677 auto f = typeParsers.find(mnemonic);
678 if (f != typeParsers.end())
679 t = f->second(typeJson, cache);
680 else
681 // Types we don't know about are opaque.
682 t = new Type(id);
683
684 // Store hwBitwidth from the manifest so even opaque/unrecognized types
685 // (e.g. unions) report their width correctly.
686 if (typeJson.contains("hwBitwidth"))
687 t->setHWBitwidth(typeJson.at("hwBitwidth").get<std::ptrdiff_t>());
688
689 // Insert into the cache.
690 cache.registerType(t);
691 return t;
692}
693} // namespace
694
695const Type *Manifest::Impl::parseType(const nlohmann::json &typeJson) {
696 return ::parseType(typeJson, ctxt);
697}
698
699void Manifest::Impl::populateTypes(const nlohmann::json &typesJson) {
700 for (auto &typeJson : typesJson)
701 _typeTable.push_back(parseType(typeJson));
702}
703
704//===----------------------------------------------------------------------===//
705// Manifest class implementation.
706//===----------------------------------------------------------------------===//
707
708Manifest::Manifest(Context &ctxt, const std::string &jsonManifest)
709 : impl(new Impl(ctxt, jsonManifest)) {}
710
711Manifest::~Manifest() { delete impl; }
712
713uint32_t Manifest::getApiVersion() const {
714 return impl->at("apiVersion").get<uint32_t>();
715}
716
717std::vector<ModuleInfo> Manifest::getModuleInfos() const {
718 std::vector<ModuleInfo> ret;
719 for (auto &[symbol, info] : impl->getSymbolInfo())
720 ret.push_back(info);
721 return ret;
722}
723
724Accelerator *Manifest::buildAccelerator(AcceleratorConnection &acc) const {
725 try {
726 return acc.takeOwnership(impl->buildAccelerator(acc));
727 } catch (const std::exception &e) {
728 std::string msg = "malformed manifest: " + std::string(e.what());
729 throw std::runtime_error(msg);
730 }
731}
732
733const std::vector<const Type *> &Manifest::getTypeTable() const {
734 return impl->getTypeTable();
735}
736
737//===----------------------------------------------------------------------===//
738// POCO helpers.
739//===----------------------------------------------------------------------===//
740
741// Print a module info, including the extra metadata.
742std::ostream &operator<<(std::ostream &os, const ModuleInfo &m) {
743 auto printAny = [&os](std::any a) {
744 if (std::any_cast<Constant>(&a))
745 a = std::any_cast<Constant>(a).value;
746
747 const std::type_info &t = a.type();
748 if (t == typeid(std::string))
749 os << std::any_cast<std::string>(a);
750 else if (t == typeid(int64_t))
751 os << std::any_cast<int64_t>(a);
752 else if (t == typeid(uint64_t))
753 os << std::any_cast<uint64_t>(a);
754 else if (t == typeid(double))
755 os << std::any_cast<double>(a);
756 else if (t == typeid(bool))
757 os << std::any_cast<bool>(a);
758 else if (t == typeid(std::nullptr_t))
759 os << "null";
760 else
761 os << "unknown";
762 };
763
764 if (m.name)
765 os << *m.name << " ";
766 if (m.version)
767 os << *m.version << " ";
768 if (m.name || m.version)
769 os << std::endl;
770 if (m.repo || m.commitHash) {
771 os << " Version control: ";
772 if (m.repo)
773 os << *m.repo;
774 if (m.commitHash)
775 os << "@" << *m.commitHash;
776 os << std::endl;
777 }
778 if (m.summary)
779 os << " " << *m.summary;
780 os << "\n";
781
782 if (!m.constants.empty()) {
783 os << " Constants:\n";
784 for (auto &e : m.constants) {
785 os << " " << e.first << ": ";
786 printAny(e.second);
787 os << "\n";
788 }
789 }
790
791 if (!m.extra.empty()) {
792 os << " Extra metadata:\n";
793 for (auto &e : m.extra) {
794 os << " " << e.first << ": ";
795 printAny(e.second);
796 os << "\n";
797 }
798 }
799 return os;
800}
801
802namespace esi {
803AppIDPath AppIDPath::operator+(const AppIDPath &b) const {
804 AppIDPath ret = *this;
805 ret.insert(ret.end(), b.begin(), b.end());
806 return ret;
807}
808
809AppIDPath AppIDPath::parent() const {
810 AppIDPath ret = *this;
811 if (!ret.empty())
812 ret.pop_back();
813 return ret;
814}
815
816std::string AppIDPath::toStr() const {
817 std::ostringstream os;
818 os << *this;
819 return os.str();
820}
821
822bool operator<(const AppID &a, const AppID &b) {
823 if (a.name != b.name)
824 return a.name < b.name;
825 return a.idx < b.idx;
826}
827bool operator<(const AppIDPath &a, const AppIDPath &b) {
828 if (a.size() != b.size())
829 return a.size() < b.size();
830 for (size_t i = 0, e = a.size(); i < e; ++i)
831 if (a[i] != b[i])
832 return a[i] < b[i];
833 return false;
834}
835
836std::ostream &operator<<(std::ostream &os, const AppID &id) {
837 os << id.name;
838 if (id.idx)
839 os << "[" << *id.idx << "]";
840 return os;
841}
842std::ostream &operator<<(std::ostream &os, const AppIDPath &path) {
843 for (size_t i = 0, e = path.size(); i < e; ++i) {
844 if (i > 0)
845 os << '.';
846 os << path[i];
847 }
848 return os;
849}
850
851} // namespace esi
assert(baseType &&"element must be base type")
static ParseResult parseType(Type &result, StringRef name, AsmParser &parser)
Parse a type defined by this dialect.
static std::optional< AppID > parseID(const nlohmann::json &jsonID)
Definition Manifest.cpp:130
static AppIDPath parseIDPath(const nlohmann::json &jsonIDPath)
Definition Manifest.cpp:147
static AppID parseIDChecked(const nlohmann::json &jsonID)
Definition Manifest.cpp:141
static ServicePortDesc parseServicePort(const nlohmann::json &jsonPort)
Definition Manifest.cpp:154
nlohmann::json json
Get a JSON representation of a type.
Definition RpcClient.cpp:43
static llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const bool a)
This class provides a thread-safe interface to access the analysis results.
const std::vector< const Type * > & getTypeTable() const
Get the ordered list of types from the manifest.
Definition Manifest.cpp:95
std::optional< const Type * > getType(Type::ID id) const
Definition Manifest.cpp:112
const Type * parseType(const nlohmann::json &typeJson)
Definition Manifest.cpp:695
auto at(const std::string &key) const
Definition Manifest.cpp:43
void populateTypes(const nlohmann::json &typesJson)
Parse all the types and populate the types table.
Definition Manifest.cpp:699
std::optional< ModuleInfo > getModInfo(const nlohmann::json &) const
Definition Manifest.cpp:318
void createEngine(AcceleratorConnection &, AppIDPath appID, const nlohmann::json &) const
TODO: Hack.
Definition Manifest.cpp:330
std::vector< std::unique_ptr< BundlePort > > getBundlePorts(AcceleratorConnection &acc, AppIDPath idPath, const ServiceTable &activeServices, const nlohmann::json &instJson) const
Get the bundle ports for the instance at 'idPath' and specified in 'instJson'.
Definition Manifest.cpp:478
std::vector< std::unique_ptr< Instance > > getChildInstances(AppIDPath idPath, AcceleratorConnection &acc, const ServiceTable &activeServices, const nlohmann::json &instJson) const
Build the set of child instances (recursively) for the module instance description.
Definition Manifest.cpp:360
nlohmann::json manifestJson
Definition Manifest.cpp:121
std::vector< services::Service * > getServices(AppIDPath idPath, AcceleratorConnection &, const nlohmann::json &, ServiceTable &activeServices) const
Get all the services in the description of an instance.
Definition Manifest.cpp:464
std::map< std::string, const ModuleInfo > symbolInfoCache
Definition Manifest.cpp:123
services::Service * getService(AppIDPath idPath, AcceleratorConnection &, const nlohmann::json &, ServiceTable &activeServices, bool isEngine=false) const
Get a Service for the service specified in 'json'.
Definition Manifest.cpp:389
const std::map< std::string, const ModuleInfo > & getSymbolInfo() const
Definition Manifest.cpp:104
std::any getAny(const nlohmann::json &value) const
Convert the json value to a 'std::any', which can be exposed outside of this file.
Definition Manifest.cpp:161
void parseModuleMetadata(ModuleInfo &info, const nlohmann::json &mod) const
Definition Manifest.cpp:220
std::unique_ptr< Accelerator > buildAccelerator(AcceleratorConnection &acc) const
Build a dynamic API for the Accelerator connection 'acc' based on the manifest stored herein.
Definition Manifest.cpp:288
void parseModuleConsts(ModuleInfo &info, const nlohmann::json &mod) const
Definition Manifest.cpp:241
void scanServiceDecls(AcceleratorConnection &, const nlohmann::json &, ServiceTable &) const
Go through the "service_decls" section of the manifest and populate the services table as appropriate...
Definition Manifest.cpp:336
Context & ctxt
Definition Manifest.cpp:109
std::vector< const Type * > _typeTable
Definition Manifest.cpp:110
std::unique_ptr< Instance > getChildInstance(AppIDPath idPath, AcceleratorConnection &acc, ServiceTable activeServices, const nlohmann::json &childJson) const
Get a single child instance.
Definition Manifest.cpp:373
Impl(Context &ctxt, int port)
mlir::Type innerType(mlir::Type type)
Definition ESITypes.cpp:423
void info(Twine message)
Definition LSPUtils.cpp:20
Definition esi.py:1
std::string name
Definition Common.h:36
std::optional< uint32_t > idx
Definition Common.h:37