4"""Code generation from ESI manifests to source code.
6Uses a two-pass approach for C++: first collect and name all reachable types,
7then emit structs/aliases in a dependency-ordered sequence so headers are
8standalone and deterministic.
14from typing
import Dict, List, Set, TextIO, Tuple, Type, Optional
15from .accelerator
import AcceleratorConnection, Context
16from .esiCppAccel
import ModuleInfo
18from .types
import (BundlePort
as _BundlePort, FunctionPort
as _FunctionPort,
19 CallbackPort
as _CallbackPort, ToHostPort
as _ToHostPort,
20 FromHostPort
as _FromHostPort, MMIORegion
as
21 _MMIORegionPort, MetricPort
as _MetricPort)
27from dataclasses
import dataclass, field
as _dc_field
28from pathlib
import Path
30_thisdir = Path(__file__).absolute().
resolve().parent
35 """All strings needed to emit one port slot (member + ctor + find) in Connected."""
36 struct_decls: List[str] = _dc_field(default_factory=list)
38 ctor_params: List[str] = _dc_field(default_factory=list)
41 make_unique_args: List[str] = _dc_field(default_factory=list)
42 post_connect: str =
""
43 using_aliases: List[Tuple[str, str]] = _dc_field(default_factory=list)
47 """Base class for all generators."""
49 language: Optional[str] =
None
51 def __init__(self, conn: AcceleratorConnection):
54 def generate(self, output_dir: Path, system_name: str):
55 raise NotImplementedError(
"Generator.generate() must be overridden")
59 """Generate C++ headers from an ESI manifest."""
63 def __init__(self, conn: AcceleratorConnection):
70 """Get the C++ code for a constant in a module."""
71 const_strs: List[str] = [
72 f
"static constexpr {self.type_emitter.type_identifier(const.type)} "
73 f
"{name} = 0x{const.value:x};"
74 for name, const
in module_info.constants.items()
76 return "\n".join(const_strs)
84 """Return a C++-safe identifier from an AppID name."""
87 result.append(ch
if (ch.isalnum()
or ch ==
"_")
else "_")
92 return "".join(result)
95 """Walk the live hierarchy and return {module_name: first Instance}."""
96 accel = self.
_conn.build_accelerator()
97 result: Dict[str, object] = {}
98 queue = list(accel.children.values())
101 info = inst.cpp_hwmodule.info
104 if name
is not None and name
not in result:
106 queue.extend(inst.children.values())
110 """Return the C++ member type string for a port (no member name).
112 For typed ports (function/callback/to-host/from-host channels) `alias_prefix`
113 is required; the returned template parameters are written using the alias
114 names (`<prefix>Args`, `<prefix>Result`, `<prefix>Data`) that should be
115 emitted at module-class scope via `_port_using_aliases`. For non-typed
116 ports (MMIO regions, telemetry metrics, plain bundles) `alias_prefix` is
117 ignored and the runtime reference/pointer type is returned directly.
119 if isinstance(port, _FunctionPort):
120 assert alias_prefix
is not None, (
121 "alias_prefix is required for FunctionPort to avoid emitting "
122 "long mangled type names inline (which would also collide across "
123 "modules' `using` declarations)")
124 return (f
"esi::TypedFunction<{alias_prefix}Args, "
125 f
"{alias_prefix}Result>")
126 if isinstance(port, _CallbackPort):
127 assert alias_prefix
is not None, (
128 "alias_prefix is required for CallbackPort")
129 return (f
"esi::TypedCallback<{alias_prefix}Args, "
130 f
"{alias_prefix}Result>")
131 if isinstance(port, _ToHostPort):
132 assert alias_prefix
is not None, (
133 "alias_prefix is required for ToHostPort")
134 return f
"esi::TypedReadPort<{alias_prefix}Data>"
135 if isinstance(port, _FromHostPort):
136 assert alias_prefix
is not None, (
137 "alias_prefix is required for FromHostPort")
138 return f
"esi::TypedWritePort<{alias_prefix}Data>"
139 if isinstance(port, _MMIORegionPort):
140 return "esi::services::MMIO::MMIORegion &"
141 if isinstance(port, _MetricPort):
142 return "esi::services::TelemetryService::Metric &"
143 return "esi::BundlePort &"
146 port) -> List[Tuple[str, str]]:
147 """Return (alias_name, type_id) pairs to emit as `using` declarations at
148 module-class scope for the typed-port template parameters."""
149 if isinstance(port, (_FunctionPort, _CallbackPort)):
150 arg = self.
type_emitter.type_identifier(port.arg_window_type
or
152 res = self.
type_emitter.type_identifier(port.result_window_type
or
154 return [(f
"{alias_prefix}Args", arg), (f
"{alias_prefix}Result", res)]
155 if isinstance(port, (_ToHostPort, _FromHostPort)):
156 data = self.
type_emitter.type_identifier(port.data_window_type
or
158 return [(f
"{alias_prefix}Data", data)]
163 alias_prefix: Optional[str] =
None) -> str:
164 """Return the storage type used inside an `IndexedPorts<T>` for `port`.
166 Typed ports use the same `TypedFunction<...>` / `TypedReadPort<...>` etc.
167 that `_cpp_member_type` produces. MMIO regions, telemetry metrics, and
168 plain bundle ports are stored as raw pointers because `std::map<int, T&>`
171 if isinstance(port, _MMIORegionPort):
172 return "esi::services::MMIO::MMIORegion *"
173 if isinstance(port, _MetricPort):
174 return "esi::services::TelemetryService::Metric *"
176 (_FunctionPort, _CallbackPort, _ToHostPort, _FromHostPort)):
178 return "esi::BundlePort *"
181 """Return the C++ constructor parameter type for a port."""
182 if isinstance(port, _FunctionPort):
183 return "esi::services::FuncService::Function *"
184 if isinstance(port, _CallbackPort):
185 return "esi::services::CallService::Callback *"
186 if isinstance(port, _ToHostPort):
187 return "esi::ReadChannelPort &"
188 if isinstance(port, _FromHostPort):
189 return "esi::WriteChannelPort &"
190 if isinstance(port, _MMIORegionPort):
191 return "esi::services::MMIO::MMIORegion &"
192 if isinstance(port, _MetricPort):
193 return "esi::services::TelemetryService::Metric &"
194 return "esi::BundlePort &"
198 """Return the parameter name suffix ('_chan', '_svc', or '_port')."""
199 if isinstance(port, (_ToHostPort, _FromHostPort)):
201 if isinstance(port, (_MMIORegionPort, _MetricPort)):
207 """Return `esi::AppID(...)` expression for an AppID."""
211 return f
'esi::AppID("{name}")'
212 return f
'esi::AppID("{name}", {idx})'
215 """Return the code snippet that resolves a scalar port in connect()."""
217 if isinstance(port, _FunctionPort):
218 v = f
"{member_name}_port"
221 f
" esi::findPortAsOrThrow<esi::services::FuncService::Function>(\n"
222 f
" rawModule, {ae});")
223 if isinstance(port, _CallbackPort):
224 v = f
"{member_name}_port"
227 f
" esi::findPortAsOrThrow<esi::services::CallService::Callback>(\n"
228 f
" rawModule, {ae});")
229 if isinstance(port, _ToHostPort):
230 v = f
"{member_name}_chan"
233 f
" esi::findPortAsOrThrow<esi::services::ChannelService::ToHost>(\n"
234 f
' rawModule, {ae})->getRawRead("data");')
235 if isinstance(port, _FromHostPort):
236 v = f
"{member_name}_chan"
239 f
" esi::findPortAsOrThrow<esi::services::ChannelService::FromHost>(\n"
240 f
' rawModule, {ae})->getRawWrite("data");')
241 if isinstance(port, _MMIORegionPort):
242 v = f
"{member_name}_svc"
243 return (f
"auto &{v} =\n"
244 f
" *esi::findPortAsOrThrow<esi::services::MMIO::MMIORegion>(\n"
245 f
" rawModule, {ae});")
246 if isinstance(port, _MetricPort):
247 v = f
"{member_name}_svc"
250 f
" *esi::findPortAsOrThrow<esi::services::TelemetryService::Metric>(\n"
251 f
" rawModule, {ae});")
253 v = f
"{member_name}_port"
254 return f
"auto &{v} = esi::findPortOrThrow(rawModule, {ae});"
258 """Return the argument expression for make_unique<Connected>(...)."""
259 if isinstance(port, (_ToHostPort, _FromHostPort)):
260 return f
"{member_name}_chan"
261 if isinstance(port, (_MMIORegionPort, _MetricPort)):
262 return f
"{member_name}_svc"
263 return f
"{member_name}_port"
267 """True if the generated connect() should call .connect() on this port.
269 CallbackPort.connect() requires a user-supplied callback — skip.
270 MMIORegion, BundlePort — no .connect() method."""
271 return isinstance(port,
272 (_FunctionPort, _ToHostPort, _FromHostPort, _MetricPort))
275 """Build a _PortGroup for a single scalar (non-indexed) port."""
277 alias_prefix = member_name
if aliases
else None
279 is_ref = member_type.endswith(
" &")
282 param_name = f
"{member_name}{param_suffix}"
285 member_decl = f
"{member_type}{member_name};"
287 member_decl = f
"{member_type} {member_name};"
291 post = f
"connected->{member_name}.connect();"
294 member_decl=member_decl,
295 ctor_params=[f
"{param_type} {param_name}"],
296 init_entry=f
"{member_name}({param_name})",
300 using_aliases=aliases,
304 port_list) -> _PortGroup:
305 """Build a _PortGroup for a same-name, same-type indexed port array."""
307 first_port = port_list[0][1]
309 alias_prefix = member_name
if aliases
else None
311 alias_prefix=alias_prefix)
312 indexed_type = f
"esi::IndexedPorts<{elem_type}>"
313 map_var = f
"{member_name}_backing"
314 map_type = f
"std::map<int, {elem_type}>"
315 indexed_var = f
"{member_name}_map"
323 f
"{map_type} {map_var};",
324 f
"for (uint32_t idx : esi::findPortIndices(rawModule, "
325 f
"\"{appid_name}\")) {{",
327 appid_expr = f
'esi::AppID("{appid_name}", idx)'
328 if isinstance(first_port, _FunctionPort):
330 f
" {map_var}.try_emplace(\n"
331 f
" static_cast<int>(idx),\n"
332 f
" esi::findPortAsOrThrow<esi::services::FuncService::Function>"
334 f
" rawModule, {appid_expr}));")
335 elif isinstance(first_port, _CallbackPort):
337 f
" {map_var}.try_emplace(\n"
338 f
" static_cast<int>(idx),\n"
339 f
" esi::findPortAsOrThrow<esi::services::CallService::Callback>"
341 f
" rawModule, {appid_expr}));")
342 elif isinstance(first_port, _ToHostPort):
347 f
" esi::findPortAsOrThrow<esi::services::ChannelService::ToHost>"
349 f
" rawModule, {appid_expr});\n"
350 f
" {map_var}.try_emplace(\n"
351 f
" static_cast<int>(idx),\n"
352 f
" svc->getRawRead(\"data\"));")
353 elif isinstance(first_port, _FromHostPort):
356 f
" esi::findPortAsOrThrow<esi::services::ChannelService::"
358 f
" rawModule, {appid_expr});\n"
359 f
" {map_var}.try_emplace(\n"
360 f
" static_cast<int>(idx),\n"
361 f
" svc->getRawWrite(\"data\"));")
362 elif isinstance(first_port, _MMIORegionPort):
364 f
" {map_var}.try_emplace(\n"
365 f
" static_cast<int>(idx),\n"
366 f
" esi::findPortAsOrThrow<esi::services::MMIO::MMIORegion>(\n"
367 f
" rawModule, {appid_expr}));")
368 elif isinstance(first_port, _MetricPort):
370 f
" {map_var}.try_emplace(\n"
371 f
" static_cast<int>(idx),\n"
372 f
" esi::findPortAsOrThrow<esi::services::TelemetryService::"
374 f
" rawModule, {appid_expr}));")
379 f
" {map_var}.try_emplace(\n"
380 f
" static_cast<int>(idx),\n"
381 f
" &esi::findPortOrThrow(rawModule, {appid_expr}));")
382 find_parts.append(
"}")
383 find_parts.append(f
"{indexed_type} {indexed_var}(std::move({map_var}));")
388 post = (f
"for (auto &[idx, port] : connected->{member_name})\n"
392 member_decl=f
"{indexed_type} {member_name};",
393 ctor_params=[f
"{indexed_type} {indexed_var}"],
394 init_entry=f
"{member_name}(std::move({indexed_var}))",
395 find_code=
"\n".join(find_parts),
396 make_unique_args=[f
"std::move({indexed_var})"],
398 using_aliases=aliases,
402 port_list) -> _PortGroup:
403 """Build a _PortGroup for a same-name, mixed-type indexed port group."""
404 struct_name = f
"{member_name}_ports"
405 sub_member_decls: List[str] = []
406 ctor_params: List[str] = []
407 init_args: List[str] = []
408 find_parts: List[str] = []
409 make_args: List[str] = []
410 post_parts: List[str] = []
411 using_aliases: List[Tuple[str, str]] = []
413 for appid, port
in port_list:
414 idx = appid.idx
if appid.idx
is not None else 0
416 sub_alias_prefix = f
"{member_name}_{idx}"
418 using_aliases.extend(sub_aliases)
419 alias_prefix = sub_alias_prefix
if sub_aliases
else None
421 is_ref = member_type.endswith(
" &")
423 sub_member_decls.append(f
"{member_type}{sub_name};")
425 sub_member_decls.append(f
"{member_type} {sub_name};")
429 param_name = f
"{member_name}_{idx}{param_suffix}"
430 ctor_params.append(f
"{param_type} {param_name}")
431 init_args.append(param_name)
435 post_parts.append(f
"connected->{member_name}.{sub_name}.connect();")
437 struct_decl = (f
"struct {struct_name} {{\n" +
438 "".join(f
" {d}\n" for d
in sub_member_decls) +
" };")
439 init_entry = f
"{member_name}{{{', '.join(init_args)}}}"
442 struct_decls=[struct_decl],
443 member_decl=f
"{struct_name} {member_name};",
444 ctor_params=ctor_params,
445 init_entry=init_entry,
446 find_code=
"\n".join(find_parts),
447 make_unique_args=make_args,
448 post_connect=
"\n".join(post_parts),
449 using_aliases=using_aliases,
453 """Group `ports` (AppID → BundlePort) into _PortGroup list."""
455 groups_by_name: Dict[str, list] = {}
456 for appid, port
in ports.items():
458 if n
not in groups_by_name:
459 groups_by_name[n] = []
460 groups_by_name[n].
append((appid, port))
462 result: List[_PortGroup] = []
463 for appid_name, port_list
in groups_by_name.items():
466 port_list.sort(key=
lambda x: x[0].idx
if x[0].idx
is not None else -1)
468 if len(port_list) == 1:
469 appid, port = port_list[0]
470 if appid.idx
is None:
478 all_indexed = all(a.idx
is not None for a, _
in port_list)
486 all_same_type = len({type(p)
for _, p
in port_list}) == 1
497 module_info: ModuleInfo, port_groups: List[_PortGroup],
498 out: TextIO) ->
None:
499 """Emit the full module class to `out`."""
500 out.write(f
"/// Generated header for {system_name} module {name}.\n"
502 '#include "types.h"\n'
503 '#include "esi/TypedPorts.h"\n'
507 "#include <optional>\n"
508 "#include <string>\n"
510 f
"namespace {system_name} {{\n"
514 metadata_lines: List[str] = []
515 summary = getattr(module_info,
"summary",
None)
517 for line
in summary.splitlines():
518 metadata_lines.append(line)
519 for label, attr
in ((
"Version",
"version"), (
"Repository",
"repo"),
520 (
"Commit",
"commit_hash")):
521 val = getattr(module_info, attr,
None)
523 metadata_lines.append(f
"{label}: {val}")
526 for line
in metadata_lines:
527 out.write(f
"/// {line}\n" if line
else "///\n")
530 out.write(f
"class {name} {{\n"
535 out.write(
" // Module constants.\n")
536 out.write(f
" {consts}\n\n")
540 aliases = [a
for grp
in port_groups
for a
in grp.using_aliases]
542 for alias_name, alias_type
in aliases:
543 out.write(f
" using {alias_name} = {alias_type};\n")
548 " /// Holds the resolved, typed ports for this module instance.\n"
549 " /// Returned by `connect()`.\n"
550 " class Connected {\n public:\n")
553 for grp
in port_groups:
554 for decl
in grp.struct_decls:
555 out.write(f
" {decl}\n")
556 if any(grp.struct_decls
for grp
in port_groups):
560 for grp
in port_groups:
561 out.write(f
" {grp.member_decl}\n")
565 all_params = [p
for grp
in port_groups
for p
in grp.ctor_params]
566 out.write(
" Connected(\n")
567 for i, param
in enumerate(all_params):
568 comma =
"," if i < len(all_params) - 1
else ""
569 out.write(f
" {param}{comma}\n")
571 inits = [grp.init_entry
for grp
in port_groups]
572 out.write(
",\n ".join(inits))
573 out.write(
" {}\n };\n\n")
577 f
" {name}(esi::HWModule *rawModule) : rawModule(rawModule) {{}}\n\n")
583 " /// The connected module's name as reported by the manifest, or\n"
584 " /// std::nullopt if the module has no metadata.\n"
585 " std::optional<std::string> name() const {\n"
586 " auto info = rawModule->getInfo();\n"
587 " return info ? info->name : std::nullopt;\n"
589 " /// The connected module's summary string, if any.\n"
590 " std::optional<std::string> summary() const {\n"
591 " auto info = rawModule->getInfo();\n"
592 " return info ? info->summary : std::nullopt;\n"
594 " /// The connected module's version string, if any.\n"
595 " std::optional<std::string> version() const {\n"
596 " auto info = rawModule->getInfo();\n"
597 " return info ? info->version : std::nullopt;\n"
599 " /// The connected module's source repository, if any.\n"
600 " std::optional<std::string> repo() const {\n"
601 " auto info = rawModule->getInfo();\n"
602 " return info ? info->repo : std::nullopt;\n"
604 " /// The connected module's source commit hash, if any.\n"
605 " std::optional<std::string> commitHash() const {\n"
606 " auto info = rawModule->getInfo();\n"
607 " return info ? info->commitHash : std::nullopt;\n"
609 " /// Designer-specified constants for the connected module.\n"
610 " /// Returns an empty map if the module has no metadata.\n"
611 " std::map<std::string, esi::Constant> constants() const {\n"
612 " auto info = rawModule->getInfo();\n"
613 " return info ? info->constants\n"
614 " : std::map<std::string, esi::Constant>{};\n"
616 " /// Free-form designer-supplied metadata for the connected module.\n"
617 " /// Returns an empty map if the module has no metadata.\n"
618 " std::map<std::string, std::any> extra() const {\n"
619 " auto info = rawModule->getInfo();\n"
620 " return info ? info->extra : std::map<std::string, std::any>{};\n"
624 out.write(
" std::unique_ptr<Connected> connect() {\n")
627 for grp
in port_groups:
629 for line
in grp.find_code.splitlines():
630 out.write(f
" {line}\n")
634 all_args = [a
for grp
in port_groups
for a
in grp.make_unique_args]
635 out.write(
" auto connected = std::make_unique<Connected>(\n")
636 for i, arg
in enumerate(all_args):
637 comma =
"," if i < len(all_args) - 1
else ""
638 out.write(f
" {arg}{comma}\n")
642 for grp
in port_groups:
644 for line
in grp.post_connect.splitlines():
645 out.write(f
" {line}\n")
647 out.write(
" return connected;\n }\n\n")
649 out.write(
"private:\n esi::HWModule *rawModule;\n};\n\n")
650 out.write(f
"}} // namespace {system_name}\n")
653 """Write the C++ header. One for each module in the manifest."""
656 for module_info
in self.
manifest.module_infos:
657 if module_info.name
is None:
659 name = module_info.name
660 instance = module_instances.get(name)
662 if instance
is not None:
666 except (NotImplementedError, ValueError)
as e:
667 sys.stderr.write(f
"Warning: skipping module '{name}': {e}\n")
668 hdr_file = output_dir / f
"{name}.h"
669 with open(hdr_file,
"w", encoding=
"utf-8")
as hdr:
670 hdr.write(f
"// Skipped: {e}\n")
673 hdr_file = output_dir / f
"{name}.h"
674 with open(hdr_file,
"w", encoding=
"utf-8")
as hdr:
678 def generate(self, output_dir: Path, system_name: str):
684 """Plan C++ type naming and ordering from an ESI manifest."""
687 """Initialize the generator with the manifest and target namespace."""
691 self.used_names: Dict[str, bool] = {}
693 self.alias_base_names: Set[str] = set()
699 """Name the types and prepare for emission by registering all reachable
700 types and assigning."""
701 visited: Set[str] = set()
716 """Create a C++-safe identifier from the manifest-provided name."""
717 name = name.replace(
"::",
"_")
718 if name.startswith(
"@"):
722 if ch.isalnum()
or ch ==
"_":
725 sanitized.append(
"_")
729 sanitized.insert(0,
"_")
730 return "".join(sanitized)
733 """Reserve a globally unique identifier using the sanitized base name."""
735 if is_alias
and base
in self.alias_base_names:
737 f
"Warning: duplicate alias name '{base}' detected; disambiguating.\n")
739 self.alias_base_names.add(base)
742 while name
in self.used_names:
743 name = f
"{base}_{idx}"
745 self.used_names[name] = is_alias
749 """Derive a deterministic name for anonymous structs from their fields."""
751 for field_name, field_type
in struct_type.fields:
752 parts.append(field_name)
757 """Derive a deterministic name for anonymous unions from their fields."""
759 for field_name, field_type
in union_type.fields:
760 parts.append(field_name)
765 """Derive a deterministic name for generated window helpers.
767 Two distinct windows can wrap the same `into` struct (e.g. serial and
768 parallel encodings of the same payload), so the helper name must be
769 derived from BOTH the inner type's name and the window's own name/id.
772 into_name = self.type_id_map.get(into_type)
773 window_part = (window_type.name
776 base = f
"{into_name}_{window_part}"
777 elif window_type.name:
780 base = f
"_window_{window_part}"
785 wrapped = wrapped.inner_type
798 for field_name, field_type
in into_type.fields:
800 list_fields.append(field_name)
801 if len(list_fields) != 1:
804 list_field_name = list_fields[0]
809 for frame
in current_type.frames:
810 for field
in frame.fields:
811 if field.name != list_field_name:
813 if field.bulk_count_width > 0:
814 if header_field
is not None:
817 elif field.num_items > 0:
818 if data_field
is not None:
821 return (header_field
is not None and data_field
is not None and
822 data_field.num_items == 1)
825 """Return child types in a stable order for traversal."""
827 return [t.inner_type]
if t.inner_type
is not None else []
829 return [channel.type
for channel
in t.channels]
833 return [field_type
for _, field_type
in t.fields]
835 return [field_type
for _, field_type
in t.fields]
837 return [t.element_type]
841 return [t.element_type]
844 def _visit_types(self, t: types.ESIType, visited: Set[str], visit_fn) ->
None:
845 """Traverse types with alphabetical child ordering in post-order."""
847 raise TypeError(f
"Expected ESIType, got {type(t)}")
853 for child
in children:
858 """Scan for aliases and reserve their names (recursive)."""
861 def visit(alias_type: types.ESIType) ->
None:
864 if alias_type
not in self.type_id_map:
865 alias_name = self.
_reserve_name(alias_type.name, is_alias=
True)
866 self.type_id_map[alias_type] = alias_name
871 """Scan for structs/unions needing auto-names and reserve them."""
874 def visit(current_type: types.ESIType) ->
None:
875 if current_type
in self.type_id_map:
885 """Scan for supported window types and reserve helper names."""
887 def visit(current_type: types.ESIType) ->
None:
891 if current_type
in self.type_id_map:
899 """Collect types that require top-level declarations for a given type."""
904 def visit(current: types.ESIType) ->
None:
906 inner = current.inner_type
907 if inner
is not None and (isinstance(
923 """Collect only the declarations referenced by a generated window helper."""
929 for _, field_type
in into_type.fields:
938 """Return True if `esi_type` is or transitively contains a WindowType.
940 Structs (and aliases/unions that reference them) which embed a window
941 cannot be emitted as C++ packed structs because the C++ window helper
942 is a variable-size multi-frame container. This helper is used to
943 exclude such types from the emission list entirely.
957 """Collect and order types for deterministic emission."""
959 for esi_type
in self.type_id_map.keys():
965 for esi_type
in self.type_id_map.keys():
966 if isinstance(esi_type,
970 inner = esi_type.inner_type
972 inner)
in window_into_types:
979 if (isinstance(esi_type,
982 emit_types.append(esi_type)
985 name_to_type = {self.type_id_map[t]: t
for t
in emit_types}
986 sorted_names = sorted(name_to_type.keys(),
988 (0
if self.used_names.get(name,
False)
else 1, name))
996 def visit(current: types.ESIType) ->
None:
998 if current
in visited:
1000 if current
in visiting:
1003 visiting.add(current)
1007 inner = current.inner_type
1008 if inner
is not None:
1011 for _, field_type
in current.fields:
1016 for dep
in sorted(deps, key=
lambda dep: self.type_id_map[dep]):
1019 visiting.remove(current)
1020 visited.add(current)
1021 ordered.append(current)
1023 for name
in sorted_names:
1024 visit(name_to_type[name])
1026 return ordered, has_cycle
1030 """Emit C++ headers from precomputed type ordering."""
1038 """Get the C++ type string for an ESI type."""
1042 """Escape a Python string for use as a C++ string literal."""
1043 escaped = value.replace(
"\\",
"\\\\").replace(
'"',
'\\"')
1044 return f
'"{escaped}"'
1047 """Get the textual code for the storage class of an integer type."""
1054 """Get the textual code for a byte-addressable integer storage type."""
1058 elif bit_width <= 8:
1060 elif bit_width <= 16:
1062 elif bit_width <= 32:
1064 elif bit_width <= 64:
1067 raise ValueError(f
"Unsupported integer width: {bit_width}")
1070 return f
"uint{storage_width}_t"
1071 return f
"int{storage_width}_t"
1074 """Return the size of a fixed-width type in bytes."""
1075 if wrapped.bit_width < 0:
1076 raise ValueError(f
"Unsupported unbounded type width for '{wrapped}'")
1077 return (wrapped.bit_width + 7) // 8
1080 self, array_type: types.ArrayType) -> Tuple[str, List[int]]:
1081 """Return the base C++ type and outer-to-inner dimensions of a nested array."""
1082 dims: List[int] = []
1085 dims.append(inner.size)
1086 inner = inner.element_type
1088 return base_cpp, dims
1091 """Return the equivalent nested `std::array<...>` type for an array.
1093 `std::array<T, N>` is layout-compatible in practice with `T[N]` on every
1094 major implementation (and identical under `#pragma pack(1)`), so the
1095 generator uses it everywhere a fixed-size array would appear. This keeps
1096 field/value/ctor types storable in `std::vector` and assignable with `=`.
1100 for size
in reversed(dims):
1101 result = f
"std::array<{result}, {size}>"
1105 """Resolve an ESI type to its C++ identifier."""
1108 if isinstance(wrapped,
1115 if wrapped.bit_width == 0:
1123 raise ValueError(
"List types require a generated window wrapper")
1131 if wrapped.bit_width == 0:
1137 if wrapped.bit_width == 0:
1142 raise NotImplementedError(
1143 f
"Type '{wrapped}' not supported for C++ generation")
1146 """Strip alias wrappers to reach the underlying type."""
1148 wrapped = wrapped.inner_type
1153 """Emit a packed field declaration for generated window helpers.
1155 Arrays use `std::array` (handled by `_cpp_type`), so no bracket syntax is
1156 needed and a single uniform declaration form covers every type.
1161 wrapped.bit_width % 8 != 0:
1162 return f
"{field_cpp} {field_name} : {wrapped.bit_width};"
1163 return f
"{field_cpp} {field_name};"
1167 """Emit a constructor parameter for generated window helpers.
1169 Small scalar header fields are cheaper to pass by value than by reference.
1170 Larger aggregates stay as const references.
1175 return f
"{field_cpp} {field_name}"
1176 return f
"const {field_cpp} &{field_name}"
1180 """Copy a generated window field."""
1181 hdr.write(f
" {dest_expr} = {src_expr};\n")
1184 """Compute the byte width of a field type, rounding up to full bytes."""
1185 return (field_type.bit_width + 7) // 8
1188 """Return the bounded byte width of `esi_type`, or `None` if it has no
1189 well-defined static size (e.g. unbounded `!esi.any` or recursive types).
1192 bit_width = esi_type.bit_width
1195 if bit_width
is None or bit_width < 0:
1197 return (bit_width + 7) // 8
1202 expected_bytes: Optional[int],
1203 indent: str =
"") ->
None:
1204 """Emit a `static_assert` that pins the C++ `sizeof` of a packed type to
1205 the byte width derived from the manifest.
1207 `std::array` and bit-field layout are technically implementation-defined,
1208 so this assertion is the safety net that catches a toolchain that lays
1209 them out differently from the wire format. Skipped silently for types
1210 without a bounded static size.
1212 if expected_bytes
is None:
1215 f
"{indent}static_assert(sizeof({type_name}) == {expected_bytes},\n"
1216 f
"{indent} \"{type_name}: packed layout does not match "
1217 f
"manifest size\");\n")
1220 """Extract the metadata needed to emit a bulk list window wrapper."""
1223 raise ValueError(
"window codegen currently requires a struct into-type")
1225 field_map = {name: field_type
for name, field_type
in into_type.fields}
1228 for name, field_type
in into_type.fields
1231 if len(list_fields) != 1:
1232 raise ValueError(
"window codegen currently supports exactly one list")
1234 list_field_name, list_type = list_fields[0]
1241 for frame
in window_type.frames:
1242 for field
in frame.fields:
1243 if field.name != list_field_name:
1245 if field.bulk_count_width > 0:
1246 header_frame = frame
1247 header_field = field
1248 elif field.num_items > 0:
1252 if header_frame
is None or header_field
is None:
1253 raise ValueError(
"window codegen requires a bulk-count header frame")
1254 if data_frame
is None or data_field
is None:
1255 raise ValueError(
"window codegen requires a data frame for the list")
1256 if data_field.num_items != 1:
1257 raise ValueError(
"window codegen currently supports numItems == 1")
1259 ctor_params = [(name, field_type)
1260 for name, field_type
in into_type.fields
1261 if name != list_field_name]
1265 count_field_name = f
"{list_field_name}_count"
1266 count_width = header_field.bulk_count_width
1268 count_bytes = (count_width + 7) // 8
1269 for field
in reversed(header_frame.fields):
1270 if field.name == list_field_name:
1271 header_fields.append((count_field_name,
None))
1272 header_bytes += count_bytes
1274 field_type = field_map[field.name]
1275 header_fields.append((field.name, field_type))
1280 for field
in reversed(data_frame.fields):
1281 if field.name == list_field_name:
1282 data_fields.append((list_field_name, list_type.element_type))
1285 field_type = field_map[field.name]
1286 data_fields.append((field.name, field_type))
1289 frame_bytes = max(header_bytes, data_bytes)
1292 "ctor_params": ctor_params,
1293 "count_cpp": count_cpp,
1294 "count_field_name": count_field_name,
1295 "count_width": count_width,
1296 "data_fields": data_fields,
1297 "data_pad_bytes": frame_bytes - data_bytes,
1298 "element_cpp": self.
_cpp_type(list_type.element_type),
1299 "frame_bytes": frame_bytes,
1300 "header_fields": header_fields,
1301 "header_pad_bytes": frame_bytes - header_bytes,
1302 "list_field_name": list_field_name,
1307 """Emit a packed struct declaration plus its type id string."""
1310 if struct_type.bit_width == 0:
1312 fields = list(struct_type.fields)
1313 if struct_type.cpp_type.reverse:
1314 fields = list(reversed(fields))
1315 field_decls: List[str] = []
1316 for field_name, field_type
in fields:
1318 if field_cpp ==
"void":
1321 field_decls.append(f
"// void {field_name};")
1327 bitfield_width = wrapped.bit_width
1328 if bitfield_width >= 0:
1329 field_decls.append(f
"{field_cpp} {field_name} : {bitfield_width};")
1331 field_decls.append(f
"{field_cpp} {field_name};")
1333 field_decls.append(f
"{field_cpp} {field_name};")
1335 hdr.write(
"#pragma pack(push, 1)\n")
1336 hdr.write(f
"struct {struct_name} {{\n")
1337 for decl
in field_decls:
1338 hdr.write(f
" {decl}\n")
1343 logical_fields = [(name, ftype)
1344 for name, ftype
in struct_type.fields
1347 hdr.write(f
" {struct_name}() = default;\n")
1348 ctor_params =
", ".join(
1350 for name, ftype
in logical_fields)
1351 inits =
", ".join(f
"{name}({name})" for name, _
in logical_fields)
1352 hdr.write(f
" {struct_name}({ctor_params}) : {inits} {{}}\n\n")
1354 f
" static constexpr std::string_view _ESI_ID = {self._cpp_string_literal(struct_type.id)};\n"
1363 if expected_bytes
is not None and expected_bytes > 0:
1365 hdr.write(
"#pragma pack(pop)\n\n")
1368 """Emit a packed union declaration plus its type id string.
1370 Fields narrower than the union width get wrapper structs with a `_pad`
1371 byte array so the data sits at the MSB end, matching SV packed union
1372 layout where padding occupies the LSBs / lower addresses.
1376 if union_type.bit_width == 0:
1379 fields = list(union_type.fields)
1382 hdr.write(
"#pragma pack(push, 1)\n")
1384 wrapper_names: Dict[str, str] = {}
1385 for field_name, field_type
in fields:
1386 if self.
_cpp_type(field_type) ==
"void":
1389 pad_bytes = union_bytes - field_bytes
1391 wrapper = f
"{union_name}_{field_name}"
1392 wrapper_names[field_name] = wrapper
1393 hdr.write(f
"struct {wrapper} {{\n")
1394 hdr.write(f
" uint8_t _pad[{pad_bytes}];\n")
1396 hdr.write(f
" {field_cpp} {field_name};\n")
1401 union_field_decls: List[str] = []
1402 for field_name, field_type
in fields:
1404 if field_cpp ==
"void":
1406 union_field_decls.append(f
"// void {field_name};")
1407 elif field_name
in wrapper_names:
1408 union_field_decls.append(f
"{wrapper_names[field_name]} {field_name};")
1410 union_field_decls.append(f
"{field_cpp} {field_name};")
1411 hdr.write(f
"union {union_name} {{\n")
1412 for decl
in union_field_decls:
1413 hdr.write(f
" {decl}\n")
1416 f
" static constexpr std::string_view _ESI_ID = {self._cpp_string_literal(union_type.id)};\n"
1423 hdr.write(
"#pragma pack(pop)\n\n")
1426 """Emit a SegmentedMessageData helper for a serial list window."""
1430 for name, field_type
in info[
"ctor_params"]
1432 value_ctor_params = list(ctor_params)
1433 value_ctor_params.append(
1434 f
"const std::vector<value_type> &{info['list_field_name']}")
1435 value_ctor_signature =
", ".join(value_ctor_params)
1436 frame_ctor_params = list(ctor_params)
1437 frame_ctor_params.append(
"std::vector<data_frame> frames")
1438 frame_ctor_signature =
", ".join(frame_ctor_params)
1439 helper_args =
", ".join(name
for name, _
in info[
"ctor_params"])
1440 helper_call = f
"{helper_args}, std::move(frames)" if helper_args
else "std::move(frames)"
1443 f
"struct {info['window_name']} : public esi::SegmentedMessageData {{\n")
1444 hdr.write(
"public:\n")
1445 hdr.write(f
" using value_type = {info['element_cpp']};\n")
1446 hdr.write(f
" using count_type = {info['count_cpp']};\n\n")
1447 hdr.write(
"#pragma pack(push, 1)\n")
1448 hdr.write(
" struct data_frame {\n")
1449 if info[
"data_pad_bytes"] > 0:
1450 hdr.write(f
" uint8_t _pad[{info['data_pad_bytes']}];\n")
1451 for field_name, field_type
in info[
"data_fields"]:
1453 hdr.write(f
" {decl}\n")
1456 hdr.write(
"#pragma pack(pop)\n\n")
1457 hdr.write(
"private:\n")
1458 hdr.write(
"#pragma pack(push, 1)\n")
1459 hdr.write(
" struct header_frame {\n")
1460 if info[
"header_pad_bytes"] > 0:
1461 hdr.write(f
" uint8_t _pad[{info['header_pad_bytes']}];\n")
1462 for field_name, field_type
in info[
"header_fields"]:
1463 if field_type
is None:
1464 if info[
"count_width"] % 8 == 0:
1465 decl = f
"count_type {field_name};"
1467 decl = f
"count_type {field_name} : {info['count_width']};"
1470 hdr.write(f
" {decl}\n")
1474 info[
"frame_bytes"],
1476 hdr.write(
"#pragma pack(pop)\n\n")
1477 hdr.write(
" header_frame header{};\n")
1478 hdr.write(
" std::vector<data_frame> data_frames;\n")
1479 hdr.write(
" header_frame footer{};\n\n")
1480 hdr.write(f
" void construct({frame_ctor_signature}) {{\n")
1481 hdr.write(
" if (frames.empty())\n")
1483 f
" throw std::invalid_argument(\"{info['window_name']}: bulk windowed lists cannot be empty\");\n"
1486 " if (frames.size() > std::numeric_limits<count_type>::max())\n")
1488 f
" throw std::invalid_argument(\"{info['window_name']}: list too large for encoded count\");\n"
1491 f
" header.{info['count_field_name']} = static_cast<count_type>(frames.size());\n"
1493 for name, _
in info[
"ctor_params"]:
1495 field_type
for field_name, field_type
in info[
"ctor_params"]
1496 if field_name == name)
1498 hdr.write(f
" footer.{info['count_field_name']} = 0;\n")
1499 hdr.write(
" data_frames = std::move(frames);\n")
1501 hdr.write(
"public:\n")
1502 hdr.write(f
" {info['window_name']}({frame_ctor_signature}) {{\n")
1503 hdr.write(f
" construct({helper_call});\n")
1505 hdr.write(f
" {info['window_name']}({value_ctor_signature}) {{\n")
1506 hdr.write(
" std::vector<data_frame> frames;\n")
1507 hdr.write(f
" frames.reserve({info['list_field_name']}.size());\n")
1508 hdr.write(f
" for (const auto &element : {info['list_field_name']}) {{\n")
1509 hdr.write(
" auto &frame = frames.emplace_back();\n")
1510 hdr.write(f
" frame.{info['list_field_name']} = element;\n")
1512 hdr.write(f
" construct({helper_call});\n")
1514 hdr.write(
" size_t numSegments() const override { return 3; }\n")
1515 hdr.write(
" esi::Segment segment(size_t idx) const override {\n")
1516 hdr.write(
" if (idx == 0)\n")
1518 " return {reinterpret_cast<const uint8_t *>(&header), sizeof(header)};\n"
1520 hdr.write(
" if (idx == 1)\n")
1522 " return {reinterpret_cast<const uint8_t *>(data_frames.data()),\n"
1524 hdr.write(
" data_frames.size() * sizeof(data_frame)};\n")
1525 hdr.write(
" if (idx == 2)\n")
1527 " return {reinterpret_cast<const uint8_t *>(&footer), sizeof(footer)};\n"
1530 f
" throw std::out_of_range(\"{info['window_name']}: invalid segment index\");\n"
1534 f
" static constexpr std::string_view _ESI_ID = {self._cpp_string_literal(self._unwrap_aliases(window_type.into_type).id)};\n"
1540 f
" static constexpr std::string_view _ESI_WINDOW_ID = {self._cpp_string_literal(window_type.id)};\n"
1547 """Emit accessors for the header and data fields of a window helper.
1549 Exposes each static header field as a scalar accessor, the count of data
1550 frames, and one vector-valued accessor per data field so decoded values
1551 are easy to inspect on the read side.
1553 list_field_name = info[
"list_field_name"]
1555 for field_name, field_type
in info[
"header_fields"]:
1558 if field_type
is None:
1564 if isinstance(unwrapped_header,
1567 f
" {cpp} {field_name}() const {{ return header.{field_name}; }}\n")
1570 f
" const {cpp} &{field_name}() const {{ return header.{field_name}; }}\n"
1573 f
" size_t {list_field_name}_count() const {{ return data_frames.size(); }}\n"
1575 for field_name, field_type
in info[
"data_fields"]:
1580 if field_name == list_field_name:
1581 elem_cpp =
"value_type"
1588 is_bitfield = (isinstance(unwrapped_data,
1590 unwrapped_data.bit_width % 8 != 0)
1592 projection = f
"[](const data_frame &f) {{ return f.{field_name}; }}"
1594 projection = f
"&data_frame::{field_name}"
1596 f
" auto {field_name}() const {{\n"
1597 f
" return std::views::transform(data_frames, {projection});\n"
1599 hdr.write(f
" std::vector<{elem_cpp}> {field_name}_vector() const {{\n"
1600 f
" std::vector<{elem_cpp}> out;\n"
1601 f
" out.reserve(data_frames.size());\n"
1602 f
" for (const auto &frame : data_frames)\n"
1603 f
" out.push_back(frame.{field_name});\n"
1608 """Emit a few bridge helpers + a `TypeDeserializer` alias.
1610 The actual decoder lives in `esi::SerialListTypeDeserializer<T>`, which
1611 walks the header/data/footer burst protocol generically. Each window
1612 helper only has to expose:
1614 - `_headerCount(const header_frame &)` -> `count_type`
1615 - `_fromFrames(const header_frame &, std::vector<data_frame> &&)`
1616 -> `std::unique_ptr<T>`
1618 plus a `friend class esi::SerialListTypeDeserializer<T>;` so the template
1619 can reach the (private) `header_frame` definition.
1621 window_name = info[
"window_name"]
1622 count_field_name = info[
"count_field_name"]
1623 ctor_args =
", ".join(f
"h.{name}" for name, _
in info[
"ctor_params"])
1625 ctor_args = f
"{ctor_args}, std::move(frames)"
1627 ctor_args =
"std::move(frames)"
1630 hdr.write(
"private:\n")
1632 " // Bridge helpers used by esi::SerialListTypeDeserializer<T>; the\n")
1634 " // template walks the serial-list burst protocol generically and\n")
1636 " // reaches into `header_frame` via the friend declaration below.\n")
1637 hdr.write(
" static count_type _headerCount(const header_frame &h) {\n")
1638 hdr.write(f
" return h.{count_field_name};\n")
1640 hdr.write(f
" static std::unique_ptr<{window_name}> _fromFrames(\n")
1642 " const header_frame &h, std::vector<data_frame> &&frames) {\n")
1643 hdr.write(f
" return std::make_unique<{window_name}>({ctor_args});\n")
1646 f
" friend class esi::SerialListTypeDeserializer<{window_name}>;\n\n")
1647 hdr.write(
"public:\n")
1649 f
" using TypeDeserializer = esi::SerialListTypeDeserializer<{window_name}>;\n"
1653 """Emit a using alias when the alias targets a different C++ type."""
1654 inner_wrapped = alias_type.inner_type
1657 if inner_wrapped
is not None:
1658 inner_cpp = self.
_cpp_type(inner_wrapped)
1659 if inner_cpp
is None:
1661 if inner_cpp != alias_name:
1662 hdr.write(f
"using {alias_name} = {inner_cpp};\n\n")
1665 """Emit the fully ordered types.h header into the output directory."""
1666 hdr_file = output_dir /
"types.h"
1667 with open(hdr_file,
"w", encoding=
"utf-8")
as hdr:
1669 textwrap.dedent(f
"""
1670 // Generated header for {system_name} types.
1679 #include <stdexcept>
1680 #include <string_view>
1684 #include "esi/Common.h"
1685 #include "esi/TypedPorts.h"
1687 namespace {system_name} {{
1691 sys.stderr.write(
"Warning: cyclic type dependencies detected.\n")
1692 sys.stderr.write(
" Logically this should not be possible.\n")
1694 " Emitted code may fail to compile due to ordering issues.\n")
1706 except (ValueError, NotImplementedError)
as e:
1707 sys.stderr.write(f
"Warning: skipping type '{emit_type}': {e}\n")
1708 hdr.write(f
"// Unsupported type '{emit_type}': {e}\n\n")
1710 hdr.write(textwrap.dedent(f
"""
1711 }} // namespace {system_name}
1715def run(generator: Type[Generator] = CppGenerator,
1716 cmdline_args=sys.argv) -> int:
1717 """Create and run a generator reading options from the command line."""
1719 argparser = argparse.ArgumentParser(
1720 description=f
"Generate {generator.language} headers from an ESI manifest",
1721 formatter_class=argparse.RawDescriptionHelpFormatter,
1722 epilog=textwrap.dedent(
"""
1723 Can read the manifest from either a file OR a running accelerator.
1726 # To read the manifest from a file:
1727 esi-cppgen --file /path/to/manifest.json
1729 # To read the manifest from a running accelerator:
1730 esi-cppgen --platform cosim --connection localhost:1234
1733 argparser.add_argument(
"--file",
1736 help=
"Path to the manifest file.")
1737 argparser.add_argument(
1740 help=
"Name of platform for live accelerator connection.")
1741 argparser.add_argument(
1744 help=
"Connection string for live accelerator connection.")
1745 argparser.add_argument(
1749 help=
"Output directory for generated files. Recommend adding either `esi`"
1750 " or the system name to the end of the path so as to avoid header name"
1751 "conflicts. Defaults to `esi`")
1752 argparser.add_argument(
1755 default=
"esi_system",
1756 help=
"Name of the ESI system. For C++, this will be the namespace.")
1758 if (len(cmdline_args) <= 1):
1759 argparser.print_help()
1761 args = argparser.parse_args(cmdline_args[1:])
1763 if args.file
is not None and args.platform
is not None:
1764 print(
"Cannot specify both --file and --platform")
1767 conn: AcceleratorConnection
1768 if args.file
is not None:
1771 conn = Context.default().
connect(
"trace", f
"-{os.pathsep}{args.file}")
1772 elif args.platform
is not None:
1773 if args.connection
is None:
1774 print(
"Must specify --connection with --platform")
1776 conn = Context.default().
connect(args.platform, args.connection)
1778 print(
"Must specify either --file or --platform")
1781 output_dir = Path(args.output_dir)
1782 if output_dir.exists()
and not output_dir.is_dir():
1783 print(f
"Output directory {output_dir} is not a directory")
1785 if not output_dir.exists():
1786 output_dir.mkdir(parents=
True)
1788 gen = generator(conn)
1789 gen.generate(output_dir, args.system_name)
1793if __name__ ==
'__main__':
static void print(TypedAttr val, llvm::raw_ostream &os)
static mlir::Operation * resolve(Context &context, mlir::SymbolRefAttr sym)
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
_PortGroup _scalar_port_group(self, str member_name, port, appid)
None _emit_module_class(self, str name, str system_name, ModuleInfo module_info, List[_PortGroup] port_groups, TextIO out)
str _cpp_indexed_elem_type(self, port, Optional[str] alias_prefix=None)
Dict[str, object] _build_module_instance_map(self)
str _cpp_member_type(self, port, Optional[str] alias_prefix=None)
str get_consts_str(self, ModuleInfo module_info)
str _cpp_ctor_param_type(self, port)
str _sanitize_id(str name)
List[Tuple[str, str]] _port_using_aliases(self, str alias_prefix, port)
str _port_make_unique_arg(str member_name, port)
__init__(self, AcceleratorConnection conn)
_PortGroup _indexed_ports_group(self, str member_name, str appid_name, port_list)
write_modules(self, Path output_dir, str system_name)
generate(self, Path output_dir, str system_name)
str _cpp_ctor_param_suffix(port)
_PortGroup _mixed_struct_group(self, str member_name, str appid_name, port_list)
List[_PortGroup] _collect_port_groups(self, dict ports)
str _port_find_code(self, str member_name, port, appid)
bool _port_is_connectable(port)
None _emit_struct(self, TextIO hdr, types.StructType struct_type)
str _format_window_field_decl(self, str field_name, types.ESIType field_type)
_analyze_window(self, types.WindowType window_type)
None _emit_size_assert(self, TextIO hdr, str type_name, Optional[int] expected_bytes, str indent="")
None _emit_window_deserializer(self, TextIO hdr, info)
None _emit_window_data_accessors(self, TextIO hdr, info)
str _storage_type(self, int bit_width, bool signed)
str _std_array_type(self, types.ArrayType array_type)
str _get_bitvector_str(self, types.ESIType type)
None write_header(self, Path output_dir, str system_name)
str _format_window_ctor_param(self, str field_name, types.ESIType field_type)
None _emit_alias(self, TextIO hdr, types.TypeAlias alias_type)
None __init__(self, CppTypePlanner planner)
int _field_byte_width(self, types.ESIType field_type)
Tuple[str, List[int]] _array_base_and_dims(self, types.ArrayType array_type)
int _type_byte_width(self, types.ESIType wrapped)
types.ESIType _unwrap_aliases(self, types.ESIType wrapped)
str type_identifier(self, types.ESIType type)
None _emit_union(self, TextIO hdr, types.UnionType union_type)
str _cpp_string_literal(self, str value)
None _emit_window(self, TextIO hdr, types.WindowType window_type)
Optional[int] _safe_byte_width(self, types.ESIType esi_type)
None _emit_window_field_copy(self, TextIO hdr, str dest_expr, str src_expr, types.ESIType field_type)
str _cpp_type(self, types.ESIType wrapped)
str _auto_window_name(self, types.WindowType window_type)
bool _contains_window(self, types.ESIType esi_type)
Set[types.ESIType] _collect_decls_from_window(self, types.WindowType window_type)
types.ESIType _unwrap_aliases(self, types.ESIType wrapped)
str _sanitize_name(self, str name)
bool _is_supported_window(self, types.ESIType current_type)
None _visit_types(self, types.ESIType t, Set[str] visited, visit_fn)
str _reserve_name(self, str base, bool is_alias)
Tuple[List[types.ESIType], bool] _ordered_emit_types(self)
List[types.ESIType] _iter_type_children(self, types.ESIType t)
None __init__(self, type_table)
Set[types.ESIType] _collect_decls_from_type(self, types.ESIType wrapped)
str _auto_struct_name(self, types.StructType struct_type)
str _auto_union_name(self, types.UnionType union_type)
None _collect_windows(self, types.ESIType t, Set[str] visited)
None _collect_structs(self, types.ESIType t, Set[str] visited)
None _prepare_types(self, type_table)
None _collect_aliases(self, types.ESIType t, Set[str] visited)
generate(self, Path output_dir, str system_name)
__init__(self, AcceleratorConnection conn)
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
"AcceleratorConnection" connect(str platform, str connection_str)