CIRCT 22.0.0git
Loading...
Searching...
No Matches
esiCppAccel.cpp
Go to the documentation of this file.
1//===- esiaccel.cpp - ESI runtime python bindings ---------------*- 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// Simply wrap the C++ API into a Python module called 'esiaccel'.
10//
11//===----------------------------------------------------------------------===//
12
13#include "esi/Accelerator.h"
14#include "esi/Services.h"
15
16#include "esi/backends/Cosim.h"
17
18#include <ranges>
19#include <sstream>
20
21// nanobind includes.
22// Python world does not respect constness. So it doesn't make sense to have
23// const checks. Disable related warnings.
24#if defined(__GNUC__)
25#pragma GCC diagnostic push
26#pragma GCC diagnostic ignored "-Wcast-qual"
27#endif
28#include <nanobind/nanobind.h>
29#include <nanobind/stl/function.h>
30#include <nanobind/stl/map.h>
31#include <nanobind/stl/optional.h>
32#include <nanobind/stl/pair.h>
33#include <nanobind/stl/string.h>
34#include <nanobind/stl/unique_ptr.h>
35#include <nanobind/stl/vector.h>
36#if defined(__GNUC__)
37#pragma GCC diagnostic pop
38#endif
39
40namespace nb = nanobind;
41
42using namespace esi;
43using namespace esi::services;
44
45namespace nanobind {
46namespace detail {
47
48template <>
49struct type_hook<ChannelPort> {
50 static const std::type_info *get(const ChannelPort *port) {
51 if (dynamic_cast<const WriteChannelPort *>(port))
52 return &typeid(WriteChannelPort);
53 if (dynamic_cast<const ReadChannelPort *>(port))
54 return &typeid(ReadChannelPort);
55 return &typeid(ChannelPort);
56 }
57};
58
59template <>
60struct type_hook<Service> {
61 static const std::type_info *get(const Service *svc) {
62 if (dynamic_cast<const MMIO *>(svc))
63 return &typeid(MMIO);
64 if (dynamic_cast<const SysInfo *>(svc))
65 return &typeid(SysInfo);
66 if (dynamic_cast<const HostMem *>(svc))
67 return &typeid(HostMem);
68 if (dynamic_cast<const TelemetryService *>(svc))
69 return &typeid(TelemetryService);
70 return &typeid(Service);
71 }
72};
73
74/// Nanobind doesn't have a built-in type caster for std::any.
75/// We must provide one which knows about all of the potential types which the
76/// any might be.
77template <>
78struct type_caster<std::any> {
79 NB_TYPE_CASTER(std::any, const_name("object"))
80
81 static handle from_cpp(const std::any &src, rv_policy /* policy */,
82 cleanup_list * /* cleanup */) {
83 const std::type_info &t = src.type();
84 if (t == typeid(std::string))
85 return nb::str(std::any_cast<std::string>(src).c_str()).release();
86 else if (t == typeid(int64_t))
87 return nb::int_(std::any_cast<int64_t>(src)).release();
88 else if (t == typeid(uint64_t))
89 return nb::int_(std::any_cast<uint64_t>(src)).release();
90 else if (t == typeid(double))
91 return nb::float_(std::any_cast<double>(src)).release();
92 else if (t == typeid(bool))
93 return nb::bool_(std::any_cast<bool>(src)).release();
94 else if (t == typeid(std::nullptr_t))
95 return nb::none().release();
96 return nb::none().release();
97 }
98};
99} // namespace detail
100} // namespace nanobind
101
102/// Resolve a Type to the Python wrapper object.
103nb::object getPyType(std::optional<const Type *> t) {
104 nb::object typesModule = nb::module_::import_("esiaccel.types");
105 if (!t)
106 return nb::none();
107 return typesModule.attr("_get_esi_type")(*t);
108}
109
110// NOLINTNEXTLINE(readability-identifier-naming)
111NB_MODULE(esiCppAccel, m) {
112 // TODO: Fix leaks! The one I know of is in the callback code -- if one
113 // registers a python callback it creates a leak.
114 nb::set_leak_warnings(false);
115
116 nb::class_<Type>(m, "Type")
117 .def(nb::init<const Type::ID &>(), nb::arg("id"))
118 .def_prop_ro("id", &Type::getID)
119 .def("__repr__", [](Type &t) { return "<" + t.getID() + ">"; });
120 nb::class_<ChannelType, Type>(m, "ChannelType")
121 .def(nb::init<const Type::ID &, const Type *>(), nb::arg("id"),
122 nb::arg("inner"))
123 .def_prop_ro("inner", &ChannelType::getInner, nb::rv_policy::reference);
124 nb::enum_<BundleType::Direction>(m, "Direction")
125 .value("To", BundleType::Direction::To)
126 .value("From", BundleType::Direction::From)
127 .export_values();
128 nb::class_<BundleType, Type>(m, "BundleType")
129 .def(nb::init<const Type::ID &, const BundleType::ChannelVector &>(),
130 nb::arg("id"), nb::arg("channels"))
131 .def_prop_ro("channels", &BundleType::getChannels,
132 nb::rv_policy::reference);
133 nb::class_<VoidType, Type>(m, "VoidType")
134 .def(nb::init<const Type::ID &>(), nb::arg("id"));
135 nb::class_<AnyType, Type>(m, "AnyType")
136 .def(nb::init<const Type::ID &>(), nb::arg("id"));
137 nb::class_<BitVectorType, Type>(m, "BitVectorType")
138 .def(nb::init<const Type::ID &, uint64_t>(), nb::arg("id"),
139 nb::arg("width"))
140 .def_prop_ro("width", &BitVectorType::getWidth);
141 nb::class_<BitsType, BitVectorType>(m, "BitsType")
142 .def(nb::init<const Type::ID &, uint64_t>(), nb::arg("id"),
143 nb::arg("width"));
144 nb::class_<IntegerType, BitVectorType>(m, "IntegerType")
145 .def(nb::init<const Type::ID &, uint64_t>(), nb::arg("id"),
146 nb::arg("width"));
147 nb::class_<SIntType, IntegerType>(m, "SIntType")
148 .def(nb::init<const Type::ID &, uint64_t>(), nb::arg("id"),
149 nb::arg("width"));
150 nb::class_<UIntType, IntegerType>(m, "UIntType")
151 .def(nb::init<const Type::ID &, uint64_t>(), nb::arg("id"),
152 nb::arg("width"));
153 nb::class_<StructType, Type>(m, "StructType")
154 .def(nb::init<const Type::ID &, const StructType::FieldVector &, bool>(),
155 nb::arg("id"), nb::arg("fields"), nb::arg("reverse") = true)
156 .def_prop_ro("fields", &StructType::getFields, nb::rv_policy::reference)
157 .def_prop_ro("reverse", &StructType::isReverse);
158 nb::class_<ArrayType, Type>(m, "ArrayType")
159 .def(nb::init<const Type::ID &, const Type *, uint64_t>(), nb::arg("id"),
160 nb::arg("element_type"), nb::arg("size"))
161 .def_prop_ro("element", &ArrayType::getElementType,
162 nb::rv_policy::reference)
163 .def_prop_ro("size", &ArrayType::getSize);
164
165 nb::class_<Constant>(m, "Constant")
166 .def_prop_ro("value", [](Constant &c) { return c.value; })
167 .def_prop_ro("type", [](Constant &c) { return getPyType(*c.type); });
168
169 nb::class_<AppID>(m, "AppID")
170 .def(nb::init<std::string, std::optional<uint32_t>>(), nb::arg("name"),
171 nb::arg("idx") = std::nullopt)
172 .def_prop_ro("name", [](AppID &id) { return id.name; })
173 .def_prop_ro("idx",
174 [](AppID &id) -> nb::object {
175 if (id.idx)
176 return nb::cast(id.idx);
177 return nb::none();
178 })
179 .def("__repr__",
180 [](AppID &id) {
181 std::string ret = "<" + id.name;
182 if (id.idx)
183 ret = ret + "[" + std::to_string(*id.idx) + "]";
184 ret = ret + ">";
185 return ret;
186 })
187 .def("__eq__", [](AppID &a, AppID &b) { return a == b; })
188 .def("__hash__", [](AppID &id) {
189 return utils::hash_combine(std::hash<std::string>{}(id.name),
190 std::hash<uint32_t>{}(id.idx.value_or(-1)));
191 });
192 nb::class_<AppIDPath>(m, "AppIDPath").def("__repr__", &AppIDPath::toStr);
193
194 nb::class_<ModuleInfo>(m, "ModuleInfo")
195 .def_prop_ro("name", [](ModuleInfo &info) { return info.name; })
196 .def_prop_ro("summary", [](ModuleInfo &info) { return info.summary; })
197 .def_prop_ro("version", [](ModuleInfo &info) { return info.version; })
198 .def_prop_ro("repo", [](ModuleInfo &info) { return info.repo; })
199 .def_prop_ro("commit_hash",
200 [](ModuleInfo &info) { return info.commitHash; })
201 .def_prop_ro("constants", [](ModuleInfo &info) { return info.constants; })
202 // TODO: "extra" field.
203 .def("__repr__", [](ModuleInfo &info) {
204 std::string ret;
205 std::stringstream os(ret);
206 os << info;
207 return os.str();
208 });
209
210 nb::enum_<Logger::Level>(m, "LogLevel")
211 .value("Debug", Logger::Level::Debug)
212 .value("Info", Logger::Level::Info)
213 .value("Warning", Logger::Level::Warning)
214 .value("Error", Logger::Level::Error)
215 .export_values();
216 nb::class_<Logger>(m, "Logger");
217
218 nb::class_<services::Service>(m, "Service")
219 .def("get_service_symbol", &services::Service::getServiceSymbol);
220
221 nb::class_<SysInfo, services::Service>(m, "SysInfo")
222 .def("esi_version", &SysInfo::getEsiVersion)
223 .def("json_manifest", &SysInfo::getJsonManifest);
224
225 nb::class_<MMIO::RegionDescriptor>(m, "MMIORegionDescriptor")
226 .def_prop_ro("base", [](MMIO::RegionDescriptor &r) { return r.base; })
227 .def_prop_ro("size", [](MMIO::RegionDescriptor &r) { return r.size; });
228 nb::class_<services::MMIO, services::Service>(m, "MMIO")
229 .def("read", &services::MMIO::read)
230 .def("write", &services::MMIO::write)
231 .def_prop_ro("regions", &services::MMIO::getRegions,
232 nb::rv_policy::reference);
233
234 nb::class_<services::HostMem::HostMemRegion>(m, "HostMemRegion")
235 .def_prop_ro("ptr",
237 return reinterpret_cast<uintptr_t>(mem.getPtr());
238 })
239 .def_prop_ro("size", &services::HostMem::HostMemRegion::getSize);
240
241 nb::class_<services::HostMem::Options>(m, "HostMemOptions")
242 .def(nb::init<>())
243 .def_rw("writeable", &services::HostMem::Options::writeable)
244 .def_rw("use_large_pages", &services::HostMem::Options::useLargePages)
245 .def("__repr__", [](services::HostMem::Options &opts) {
246 std::string ret = "HostMemOptions(";
247 if (opts.writeable)
248 ret += "writeable ";
249 if (opts.useLargePages)
250 ret += "use_large_pages";
251 ret += ")";
252 return ret;
253 });
254
255 nb::class_<services::HostMem, services::Service>(m, "HostMem")
256 .def("allocate", &services::HostMem::allocate, nb::arg("size"),
257 nb::arg("options") = services::HostMem::Options(),
258 nb::rv_policy::take_ownership)
259 .def(
260 "map_memory",
261 [](HostMem &self, uintptr_t ptr, size_t size, HostMem::Options opts) {
262 return self.mapMemory(reinterpret_cast<void *>(ptr), size, opts);
263 },
264 nb::arg("ptr"), nb::arg("size"),
265 nb::arg("options") = services::HostMem::Options())
266 .def(
267 "unmap_memory",
268 [](HostMem &self, uintptr_t ptr) {
269 return self.unmapMemory(reinterpret_cast<void *>(ptr));
270 },
271 nb::arg("ptr"));
272 nb::class_<services::TelemetryService, services::Service>(m,
273 "TelemetryService");
274
275 nb::class_<std::future<MessageData>>(m, "MessageDataFuture")
276 .def("valid", [](std::future<MessageData> &f) { return f.valid(); })
277 .def("wait",
278 [](std::future<MessageData> &f) {
279 // Yield the GIL while waiting for the future to complete, in case
280 // of python callbacks occurring from other threads while waiting.
281 nb::gil_scoped_release release{};
282 f.wait();
283 })
284 .def("get", [](std::future<MessageData> &f) {
285 std::optional<MessageData> data;
286 {
287 // Yield the GIL while waiting for the future to complete, in case of
288 // python callbacks occurring from other threads while waiting.
289 nb::gil_scoped_release release{};
290 data.emplace(f.get());
291 }
292 return nb::bytearray((const char *)data->getBytes(), data->getSize());
293 });
294
295 nb::class_<ChannelPort::ConnectOptions>(m, "ConnectOptions")
296 .def(nb::init<>())
297 .def_rw("buffer_size", &ChannelPort::ConnectOptions::bufferSize,
298 nb::arg("buffer_size").none())
299 .def_rw("translate_message",
301
302 nb::class_<ChannelPort>(m, "ChannelPort")
303 .def("connect", &ChannelPort::connect, nb::arg("options"),
304 "Connect with specified options")
305 .def("disconnect", &ChannelPort::disconnect)
306 .def_prop_ro("type", &ChannelPort::getType, nb::rv_policy::reference);
307
308 nb::class_<WriteChannelPort, ChannelPort>(m, "WriteChannelPort")
309 .def("write",
310 [](WriteChannelPort &p, nb::bytearray data) {
311 std::vector<uint8_t> dataVec((const uint8_t *)data.c_str(),
312 (const uint8_t *)data.c_str() +
313 data.size());
314 p.write(dataVec);
315 })
316 .def("tryWrite", [](WriteChannelPort &p, nb::bytearray data) {
317 std::vector<uint8_t> dataVec((const uint8_t *)data.c_str(),
318 (const uint8_t *)data.c_str() +
319 data.size());
320 return p.tryWrite(dataVec);
321 });
322 nb::class_<ReadChannelPort, ChannelPort>(m, "ReadChannelPort")
323 .def(
324 "read",
325 [](ReadChannelPort &p) -> nb::bytearray {
326 MessageData data;
327 p.read(data);
328 return nb::bytearray((const char *)data.getBytes(), data.getSize());
329 },
330 "Read data from the channel. Blocking.")
331 .def("read_async", &ReadChannelPort::readAsync);
332
333 nb::class_<BundlePort>(m, "BundlePort")
334 .def_prop_ro("id", &BundlePort::getID)
335 .def_prop_ro("channels", &BundlePort::getChannels,
336 nb::rv_policy::reference)
337 .def("getWrite", &BundlePort::getRawWrite, nb::rv_policy::reference)
338 .def("getRead", &BundlePort::getRawRead, nb::rv_policy::reference);
339
340 nb::class_<ServicePort, BundlePort>(m, "ServicePort");
341
342 nb::class_<MMIO::MMIORegion, ServicePort>(m, "MMIORegion")
343 .def_prop_ro("descriptor", &MMIO::MMIORegion::getDescriptor)
344 .def("read", &MMIO::MMIORegion::read)
345 .def("write", &MMIO::MMIORegion::write);
346
347 nb::class_<FuncService::Function, ServicePort>(m, "Function")
348 .def("call",
349 [](FuncService::Function &self,
350 nb::bytearray msg) -> std::future<MessageData> {
351 std::vector<uint8_t> dataVec((const uint8_t *)msg.c_str(),
352 (const uint8_t *)msg.c_str() +
353 msg.size());
354 MessageData data(dataVec);
355 return self.call(data);
356 })
357 .def("connect", &FuncService::Function::connect);
358
359 nb::class_<CallService::Callback, ServicePort>(m, "Callback")
360 .def("connect", [](CallService::Callback &self,
361 std::function<nb::object(nb::object)> pyCallback) {
362 // TODO: Under certain conditions this will cause python to crash. I
363 // don't remember how to replicate these crashes, but IIRC they are
364 // deterministic.
365 self.connect([pyCallback](const MessageData &req) -> MessageData {
366 nb::gil_scoped_acquire acquire{};
367 std::vector<uint8_t> arg(req.getBytes(),
368 req.getBytes() + req.getSize());
369 nb::bytes argObj((const char *)arg.data(), arg.size());
370 auto ret = pyCallback(argObj);
371 if (ret.is_none())
372 return MessageData();
373 nb::bytearray retBytes = nb::cast<nb::bytearray>(ret);
374 std::vector<uint8_t> dataVec((const uint8_t *)retBytes.c_str(),
375 (const uint8_t *)retBytes.c_str() +
376 retBytes.size());
377 return MessageData(dataVec);
378 });
379 });
380
381 nb::class_<TelemetryService::Metric, ServicePort>(m, "Metric")
382 .def("connect", &TelemetryService::Metric::connect)
383 .def("read", &TelemetryService::Metric::read)
384 .def("readInt", &TelemetryService::Metric::readInt);
385
386 // Store this variable (not commonly done) as the "children" method needs for
387 // "Instance" to be defined first.
388 auto hwmodule =
389 nb::class_<HWModule>(m, "HWModule")
390 .def_prop_ro("info", &HWModule::getInfo)
391 .def_prop_ro("ports", &HWModule::getPorts, nb::rv_policy::reference)
392 .def_prop_ro("services", &HWModule::getServices,
393 nb::rv_policy::reference);
394
395 // In order to inherit methods from "HWModule", it needs to be defined first.
396 nb::class_<Instance, HWModule>(m, "Instance")
397 .def_prop_ro("id", &Instance::getID);
398
399 nb::class_<Accelerator, HWModule>(m, "Accelerator");
400
401 // Since this returns a vector of Instance*, we need to define Instance first
402 // or else stubgen complains.
403 hwmodule.def_prop_ro("children", &HWModule::getChildren,
404 nb::rv_policy::reference);
405
406 auto accConn = nb::class_<AcceleratorConnection>(m, "AcceleratorConnection");
407
408 nb::class_<Context>(
409 m, "Context",
410 "An ESI context owns everything -- types, accelerator connections, and "
411 "the accelerator facade (aka Accelerator) itself. It MUST NOT be garbage "
412 "collected while the accelerator is still in use. When it is destroyed, "
413 "all accelerator connections are disconnected.")
414 .def(nb::init<>(), "Create a context with a default logger.")
415 .def("connect", &Context::connect, nb::rv_policy::reference)
416 .def("set_stdio_logger", [](Context &ctxt, Logger::Level level) {
417 ctxt.setLogger(std::make_unique<StreamLogger>(level));
418 });
419
420 accConn
421 .def(
422 "sysinfo",
423 [](AcceleratorConnection &acc) {
424 return acc.getService<services::SysInfo>({});
425 },
426 nb::rv_policy::reference)
427 .def(
428 "get_service_mmio",
429 [](AcceleratorConnection &acc) {
430 return acc.getService<services::MMIO>({});
431 },
432 nb::rv_policy::reference)
433 .def(
434 "get_service_hostmem",
435 [](AcceleratorConnection &acc) {
436 return acc.getService<services::HostMem>({});
437 },
438 nb::rv_policy::reference)
439 .def("get_accelerator", &AcceleratorConnection::getAccelerator,
440 nb::rv_policy::reference);
441
442 nb::class_<Manifest>(m, "Manifest")
443 .def(nb::init<Context &, std::string>())
444 .def_prop_ro("api_version", &Manifest::getApiVersion)
445 .def(
446 "build_accelerator",
447 [&](Manifest &m, AcceleratorConnection &conn) -> Accelerator * {
448 auto *acc = m.buildAccelerator(conn);
449 conn.getServiceThread()->addPoll(*acc);
450 return acc;
451 },
452 nb::rv_policy::reference)
453 .def_prop_ro("type_table",
454 [](Manifest &m) {
455 std::vector<nb::object> ret;
456 std::ranges::transform(m.getTypeTable(),
457 std::back_inserter(ret), getPyType);
458 return ret;
459 })
460 .def_prop_ro("module_infos", &Manifest::getModuleInfos);
461}
Abstract class representing a connection to an accelerator.
Definition Accelerator.h:79
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
Accelerator & getAccelerator()
AcceleratorServiceThread * getServiceThread()
Return a pointer to the accelerator 'service' thread (or threads).
Top level accelerator class.
Definition Accelerator.h:60
std::string toStr() const
Definition Manifest.cpp:781
uint64_t getWidth() const
Definition Types.h:161
Unidirectional channels are the basic communication primitive between the host and accelerator.
Definition Ports.h:36
const Type * getType() const
Definition Ports.h:130
virtual void connect(const ConnectOptions &options=ConnectOptions())=0
Set up a connection to the accelerator.
virtual void disconnect()=0
AcceleratorConnections, Accelerators, and Manifests must all share a context.
Definition Context.h:34
void setLogger(std::unique_ptr< Logger > logger)
Register a logger with the accelerator. Assumes ownership of the logger.
Definition Context.h:64
const std::map< AppID, BundlePort & > & getPorts() const
Access the module's ports by ID.
Definition Design.h:80
const std::map< AppID, Instance * > & getChildren() const
Access the module's children by ID.
Definition Design.h:71
const std::vector< services::Service * > & getServices() const
Access the services provided by this module.
Definition Design.h:82
std::optional< ModuleInfo > getInfo() const
Access the module's metadata, if any.
Definition Design.h:62
AppID getID() const
Get the instance's ID, which it will always have.
Definition Design.h:124
Class to parse a manifest.
Definition Manifest.h:39
A logical chunk of data representing serialized data.
Definition Common.h:113
const uint8_t * getBytes() const
Definition Common.h:124
size_t getSize() const
Get the size of the data in bytes.
Definition Common.h:138
A ChannelPort which reads data from the accelerator.
Definition Ports.h:318
virtual std::future< MessageData > readAsync()
Asynchronous read.
Definition Ports.cpp:126
Root class of the ESI type system.
Definition Types.h:34
ID getID() const
Definition Types.h:40
A ChannelPort which sends data to the accelerator.
Definition Ports.h:206
void write(const MessageData &data)
A very basic blocking write API.
Definition Ports.h:222
bool tryWrite(const MessageData &data)
A basic non-blocking write API.
Definition Ports.h:241
A function call which gets attached to a service port.
Definition Services.h:329
A function call which gets attached to a service port.
Definition Services.h:277
virtual std::unique_ptr< HostMemRegion > allocate(std::size_t size, Options opts) const =0
Allocate a region of host memory in accelerator accessible address space.
virtual void unmapMemory(void *ptr) const
Unmap memory which was previously mapped with 'mapMemory'.
Definition Services.h:263
virtual bool mapMemory(void *ptr, std::size_t size, Options opts) const
Try to make a region of host memory accessible to the accelerator.
Definition Services.h:258
virtual uint64_t read(uint32_t addr) const
Read a 64-bit value from this region, not the global address space.
Definition Services.cpp:132
virtual void write(uint32_t addr, uint64_t data)
Write a 64-bit value to this region, not the global address space.
Definition Services.cpp:137
virtual RegionDescriptor getDescriptor() const
Get the offset (and size) of the region in the parent (usually global) MMIO address space.
Definition Services.h:180
virtual uint64_t read(uint32_t addr) const =0
Read a 64-bit value from the global MMIO space.
virtual void write(uint32_t addr, uint64_t data)=0
Write a 64-bit value to the global MMIO space.
const std::map< AppIDPath, RegionDescriptor > & getRegions() const
Get the regions of MMIO space that this service manages.
Definition Services.h:150
Parent class of all APIs modeled as 'services'.
Definition Services.h:59
virtual std::string getServiceSymbol() const =0
Information about the Accelerator system.
Definition Services.h:113
virtual std::string getJsonManifest() const
Return the JSON-formatted system manifest.
Definition Services.cpp:40
virtual uint32_t getEsiVersion() const =0
Get the ESI version number to check version compatibility.
void connect()
Connect to a particular telemetry port. Offset should be non-nullopt.
Definition Services.cpp:378
std::future< MessageData > read()
Definition Services.cpp:385
Service for retrieving telemetry data from the accelerator.
Definition Services.h:376
nb::object getPyType(std::optional< const Type * > t)
Resolve a Type to the Python wrapper object.
NB_MODULE(esiCppAccel, m)
size_t hash_combine(size_t h1, size_t h2)
C++'s stdlib doesn't have a hash_combine function. This is a simple one.
Definition Utils.h:32
Definition esi.py:1
std::optional< unsigned > bufferSize
The buffer size is optional and should be considered merely a hint.
Definition Ports.h:45
bool translateMessage
If the type of this port is a window, translate the incoming/outgoing data into its underlying ('into...
Definition Ports.h:104
std::any value
Definition Common.h:68
std::optional< const Type * > type
Definition Common.h:69
RAII memory region for host memory.
Definition Services.h:223
virtual void * getPtr() const =0
Get a pointer to the host memory.
virtual std::size_t getSize() const =0
Options for allocating host memory.
Definition Services.h:241
Describe a region (slice) of MMIO space.
Definition Services.h:135
static const std::type_info * get(const ChannelPort *port)
static const std::type_info * get(const Service *svc)