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 hdr_file = output_dir / f
"{name}.h"
668 with open(hdr_file,
"w", encoding=
"utf-8")
as hdr:
669 hdr.write(f
"// Skipped: {e}\n")
672 hdr_file = output_dir / f
"{name}.h"
673 with open(hdr_file,
"w", encoding=
"utf-8")
as hdr:
677 def generate(self, output_dir: Path, system_name: str):
683 """Plan C++ type naming and ordering from an ESI manifest."""
686 """Initialize the generator with the manifest and target namespace."""
690 self.used_names: Dict[str, bool] = {}
692 self.alias_base_names: Set[str] = set()
702 """Name the types and prepare for emission by registering all reachable
703 types and assigning."""
704 visited: Set[str] = set()
719 """Create a C++-safe identifier from the manifest-provided name."""
720 name = name.replace(
"::",
"_")
721 if name.startswith(
"@"):
725 if ch.isalnum()
or ch ==
"_":
728 sanitized.append(
"_")
732 sanitized.insert(0,
"_")
733 return "".join(sanitized)
736 """Reserve a globally unique identifier using the sanitized base name."""
738 if is_alias
and base
in self.alias_base_names:
740 f
"Warning: duplicate alias name '{base}' detected; disambiguating.\n")
742 self.alias_base_names.add(base)
745 while name
in self.used_names:
746 name = f
"{base}_{idx}"
748 self.used_names[name] = is_alias
752 """Derive a deterministic name for anonymous structs from their fields."""
754 for field_name, field_type
in struct_type.fields:
755 parts.append(field_name)
760 """Derive a deterministic name for anonymous unions from their fields."""
762 for field_name, field_type
in union_type.fields:
763 parts.append(field_name)
768 """Derive a deterministic name for generated window helpers.
770 Two distinct windows can wrap the same `into` struct (e.g. serial and
771 parallel encodings of the same payload), so the helper name must be
772 derived from BOTH the inner type's name and the window's own name/id.
775 into_name = self.type_id_map.get(into_type)
776 window_part = (window_type.name
779 base = f
"{into_name}_{window_part}"
780 elif window_type.name:
783 base = f
"_window_{window_part}"
788 wrapped = wrapped.inner_type
801 for field_name, field_type
in into_type.fields:
803 list_fields.append(field_name)
804 if len(list_fields) != 1:
807 list_field_name = list_fields[0]
812 for frame
in current_type.frames:
813 for field
in frame.fields:
814 if field.name != list_field_name:
816 if field.bulk_count_width > 0:
817 if header_field
is not None:
820 elif field.num_items > 0:
821 if data_field
is not None:
824 return (header_field
is not None and data_field
is not None and
825 data_field.num_items == 1)
828 """Return child types in a stable order for traversal."""
830 return [t.inner_type]
if t.inner_type
is not None else []
832 return [channel.type
for channel
in t.channels]
836 return [field_type
for _, field_type
in t.fields]
838 return [field_type
for _, field_type
in t.fields]
840 return [t.element_type]
844 return [t.element_type]
847 def _visit_types(self, t: types.ESIType, visited: Set[str], visit_fn) ->
None:
848 """Traverse types with alphabetical child ordering in post-order."""
850 raise TypeError(f
"Expected ESIType, got {type(t)}")
856 for child
in children:
861 """Scan for aliases and reserve their names (recursive)."""
864 def visit(alias_type: types.ESIType) ->
None:
867 if alias_type
not in self.type_id_map:
868 alias_name = self.
_reserve_name(alias_type.name, is_alias=
True)
869 self.type_id_map[alias_type] = alias_name
874 """Scan for structs/unions needing auto-names and reserve them."""
877 def visit(current_type: types.ESIType) ->
None:
878 if current_type
in self.type_id_map:
888 """Scan for supported window types and reserve helper names."""
890 def visit(current_type: types.ESIType) ->
None:
894 if current_type
in self.type_id_map:
902 """Collect types that require top-level declarations for a given type."""
907 def visit(current: types.ESIType) ->
None:
909 inner = current.inner_type
910 if inner
is not None and (isinstance(
926 """Collect only the declarations referenced by a generated window helper."""
932 for _, field_type
in into_type.fields:
941 """Return True if `esi_type` is or transitively contains a WindowType.
943 Structs (and aliases/unions that reference them) which embed a window
944 cannot be emitted as C++ packed structs because the C++ window helper
945 is a variable-size multi-frame container. This helper is used to
946 exclude such types from the emission list entirely.
960 """Return True if `esi_type` is or transitively contains a type with
961 no bounded bit width (e.g. `!esi.any`, or a list that the window
964 A struct can't be emitted as a fixed-size raw-bytes buffer if any
965 field's width is unbounded — there's no `std::array<uint8_t, N>` size
966 that would match the wire layout — so the planner excludes such
967 structs (and aliases/unions/arrays that reach one) from the emission
968 list, the same way `_contains_window` does for nested windows.
972 if unwrapped.bit_width < 0:
985 """Collect and order types for deterministic emission."""
987 for esi_type
in self.type_id_map.keys():
997 for esi_type
in self.type_id_map.keys():
998 if isinstance(esi_type,
1000 skip_set.add(esi_type)
1001 self.skipped_types.
append(
1003 "into-type of a windowed helper; subsumed by the helper class"))
1006 inner = esi_type.inner_type
1008 inner)
in window_into_types:
1009 skip_set.add(esi_type)
1010 self.skipped_types.
append(
1011 (esi_type,
"alias of a windowed helper's into-type"))
1018 skip_set.add(esi_type)
1019 self.skipped_types.
append((esi_type,
"contains a windowed sub-type"))
1028 skip_set.add(esi_type)
1029 self.skipped_types.
append(
1030 (esi_type,
"contains an unbounded sub-type (e.g. !esi.any)"))
1034 for esi_type
in self.type_id_map.keys():
1035 if esi_type
in skip_set:
1037 if (isinstance(esi_type,
1040 emit_types.append(esi_type)
1043 name_to_type = {self.type_id_map[t]: t
for t
in emit_types}
1044 sorted_names = sorted(name_to_type.keys(),
1046 (0
if self.used_names.get(name,
False)
else 1, name))
1057 def visit(current: types.ESIType) ->
None:
1059 if current
in visited:
1061 if current
in visiting:
1064 visiting.add(current)
1068 inner = current.inner_type
1069 if inner
is not None:
1072 for _, field_type
in current.fields:
1077 for dep
in sorted(deps, key=
lambda dep: self.type_id_map[dep]):
1082 visiting.remove(current)
1083 visited.add(current)
1084 ordered.append(current)
1086 for name
in sorted_names:
1087 visit(name_to_type[name])
1089 return ordered, has_cycle
1093 """Emit C++ headers from precomputed type ordering."""
1102 """Get the C++ type string for an ESI type."""
1106 """Escape a Python string for use as a C++ string literal."""
1107 escaped = value.replace(
"\\",
"\\\\").replace(
'"',
'\\"')
1108 return f
'"{escaped}"'
1111 """Get the textual code for the C++ type used to represent an integer
1112 field's value at the API boundary.
1114 Integers up to 64 bits map to the native `int{N}_t` / `uint{N}_t`
1115 (or `bool` for a single bit) storage types so common scalars stay
1116 zero-overhead, behave identically to plain C ints, and stay valid as
1117 template parameters for the existing `TypedReadPort<T>` /
1118 `TypedWritePort<T>` / `TypedFunction<...>` machinery.
1120 Wider integers (signed > 64 bits, unsigned > 64 bits, or any
1121 `BitsType` > 64 bits) fall back to the non-owning view classes from
1122 `esi/Values.h`: `esi::BitVector` for `BitsType`, `esi::IntView` for
1123 signed integers, `esi::UIntView` for unsigned. All three are
1124 non-owning views over the parent struct's bytes, so generated
1125 getters are zero-allocation; see the lifetime note on `BitVector`
1126 and at the top of the generated header.
1130 if type.bit_width > 64:
1132 return "esi::BitVector"
1134 return "esi::UIntView"
1135 return "esi::IntView"
1141 """Get the textual code for a native byte-addressable integer storage
1142 type. Only valid for widths 1..64; wider integers are handled by
1143 `_get_bitvector_str` via the `esi::IntView` / `esi::UIntView` view
1149 elif bit_width <= 8:
1151 elif bit_width <= 16:
1153 elif bit_width <= 32:
1155 elif bit_width <= 64:
1158 raise ValueError(f
"Unsupported native integer width: {bit_width}")
1161 return f
"uint{storage_width}_t"
1162 return f
"int{storage_width}_t"
1165 """True if the C++ representation of this integer field is one of
1166 the `esi::{BitVector,IntView,UIntView}` view classes from
1167 `esi/Values.h` rather than a native integer.
1172 return wrapped.bit_width > 64
1175 """Return the size of a fixed-width type in bytes."""
1176 if wrapped.bit_width < 0:
1177 raise ValueError(f
"Unsupported unbounded type width for '{wrapped}'")
1178 return (wrapped.bit_width + 7) // 8
1181 self, array_type: types.ArrayType) -> Tuple[str, List[int]]:
1182 """Return the base C++ type and outer-to-inner dimensions of a nested array."""
1183 dims: List[int] = []
1186 dims.append(inner.size)
1187 inner = inner.element_type
1189 return base_cpp, dims
1192 """Return the equivalent nested `std::array<...>` type for an array.
1194 `std::array<T, N>` is layout-compatible in practice with `T[N]` on every
1195 major implementation (and identical under `#pragma pack(1)`), so the
1196 generator uses it everywhere a fixed-size array would appear. This keeps
1197 field/value/ctor types storable in `std::vector` and assignable with `=`.
1201 for size
in reversed(dims):
1202 result = f
"std::array<{result}, {size}>"
1206 """Resolve an ESI type to its C++ identifier."""
1209 if isinstance(wrapped,
1216 if wrapped.bit_width == 0:
1224 raise ValueError(
"List types require a generated window wrapper")
1232 if wrapped.bit_width == 0:
1238 if wrapped.bit_width == 0:
1243 raise NotImplementedError(
1244 f
"Type '{wrapped}' not supported for C++ generation")
1247 """Strip alias wrappers to reach the underlying type."""
1249 wrapped = wrapped.inner_type
1254 """Emit a constructor parameter for generated window helpers.
1256 Small scalar header fields are cheaper to pass by value than by reference.
1257 Larger aggregates stay as const references.
1263 return f
"const {field_cpp} &{field_name}"
1264 return f
"{field_cpp} {field_name}"
1265 return f
"const {field_cpp} &{field_name}"
1268 """Compute the byte width of a field type, rounding up to full bytes."""
1269 return (field_type.bit_width + 7) // 8
1272 """Return the bounded byte width of `esi_type`, or `None` if it has no
1273 well-defined static size (e.g. unbounded `!esi.any` or recursive types).
1276 bit_width = esi_type.bit_width
1279 if bit_width
is None or bit_width < 0:
1281 return (bit_width + 7) // 8
1286 expected_bytes: Optional[int],
1287 indent: str =
"") ->
None:
1288 """Emit a `static_assert` that pins the C++ `sizeof` of a packed type to
1289 the byte width derived from the manifest.
1291 `std::array` and bit-field layout are technically implementation-defined,
1292 so this assertion is the safety net that catches a toolchain that lays
1293 them out differently from the wire format. Skipped silently for types
1294 without a bounded static size.
1296 if expected_bytes
is None:
1299 f
"{indent}static_assert(sizeof({type_name}) == {expected_bytes},\n"
1300 f
"{indent} \"{type_name}: packed layout does not match "
1301 f
"manifest size\");\n")
1304 """Extract the metadata needed to emit a bulk list window wrapper."""
1307 raise ValueError(
"window codegen currently requires a struct into-type")
1309 field_map = {name: field_type
for name, field_type
in into_type.fields}
1312 for name, field_type
in into_type.fields
1315 if len(list_fields) != 1:
1316 raise ValueError(
"window codegen currently supports exactly one list")
1318 list_field_name, list_type = list_fields[0]
1325 for frame
in window_type.frames:
1326 for field
in frame.fields:
1327 if field.name != list_field_name:
1329 if field.bulk_count_width > 0:
1330 header_frame = frame
1331 header_field = field
1332 elif field.num_items > 0:
1336 if header_frame
is None or header_field
is None:
1337 raise ValueError(
"window codegen requires a bulk-count header frame")
1338 if data_frame
is None or data_field
is None:
1339 raise ValueError(
"window codegen requires a data frame for the list")
1340 if data_field.num_items != 1:
1341 raise ValueError(
"window codegen currently supports numItems == 1")
1343 ctor_params = [(name, field_type)
1344 for name, field_type
in into_type.fields
1345 if name != list_field_name]
1349 count_field_name = f
"{list_field_name}_count"
1350 count_width = header_field.bulk_count_width
1352 count_bytes = (count_width + 7) // 8
1353 for field
in reversed(header_frame.fields):
1354 if field.name == list_field_name:
1355 header_fields.append((count_field_name,
None))
1356 header_bytes += count_bytes
1358 field_type = field_map[field.name]
1359 header_fields.append((field.name, field_type))
1364 for field
in reversed(data_frame.fields):
1365 if field.name == list_field_name:
1366 data_fields.append((list_field_name, list_type.element_type))
1369 field_type = field_map[field.name]
1370 data_fields.append((field.name, field_type))
1373 frame_bytes = max(header_bytes, data_bytes)
1376 "ctor_params": ctor_params,
1377 "count_cpp": count_cpp,
1378 "count_field_name": count_field_name,
1379 "count_width": count_width,
1380 "data_fields": data_fields,
1381 "data_pad_bytes": frame_bytes - data_bytes,
1382 "element_cpp": self.
_cpp_type(list_type.element_type),
1383 "frame_bytes": frame_bytes,
1384 "header_fields": header_fields,
1385 "header_pad_bytes": frame_bytes - header_bytes,
1386 "list_field_name": list_field_name,
1391 self, struct_type: types.StructType) -> Tuple[Dict[str, int], int]:
1392 """Return `(bit_offset_by_name, total_bits)` for non-void fields.
1394 Fields are laid out LSB-first in wire order, which is reversed manifest
1395 order when `cpp_type.reverse` is set. Caller already knows each field's
1396 type/width from `struct_type.fields`; we only need the per-name offset
1397 and the resulting total bit width to size the storage array.
1399 declared = struct_type.fields
1400 if struct_type.cpp_type.reverse:
1401 declared = list(reversed(declared))
1402 bit_offsets: Dict[str, int] = {}
1404 for field_name, field_type
in declared:
1405 if self.
_cpp_type(field_type) ==
"void":
1407 bit_offsets[field_name] = bit_offset
1408 bit_offset += field_type.bit_width
1409 return bit_offsets, bit_offset
1412 """True if the field is a signed integer (only `IntType`, not `UIntType`
1419 """True if the type's in-memory C++ layout is byte-for-byte identical to
1420 its on-wire bit layout, so a flat per-byte copy round-trips it correctly.
1422 This holds only when every scalar leaf occupies a whole number of bytes
1423 that exactly matches its native storage size:
1425 * an integer / bits type whose width is one of the native storage
1426 widths (8/16/32/64). `ui3`, `si5`, a 1-bit `bool`, and even `ui24`
1427 (stored in a wider `uint32_t`) all have storage wider than their
1428 wire width and are therefore *not* byte-packable.
1429 * a struct / union whose total width is a whole number of bytes -- its
1430 `_bytes` buffer already mirrors the wire layout.
1431 * an array whose element type is itself byte-packable, so successive
1432 elements share the same byte stride on the wire and in memory.
1434 Anything that is not byte-packable must be (un)packed element-by-element
1435 from its individual wire bit offset rather than flat-copied.
1439 return wrapped.bit_width
in (8, 16, 32, 64)
1443 bit_width = wrapped.bit_width
1444 return bit_width
is not None and bit_width > 0
and bit_width % 8 == 0
1448 self_type: str, field_name: str,
1450 bit_width: int) ->
None:
1451 """Emit a getter/setter pair that reads/writes `field_name` out of the
1452 raw bytes member `raw_member` at `bit_offset` / `bit_width`.
1454 Setters share the field's name (no `set_` prefix) and return
1455 `self_type &` so the caller can chain calls (e.g.
1456 `Foo{}.a(1).b(2).inner(x)`).
1458 For integer fields the emitter picks between four inline strategies,
1461 * Path A — 8/16/32/64-bit fields at a byte-aligned offset. Read/write
1462 via a `reinterpret_cast` through the underlying `std::array<uint8_t>`,
1463 matching the same aliasing pattern the runtime already relies on in
1464 `MessageData::from<T>()` / `MessageData::as<T>()`.
1465 * Path B — byte-aligned offset and width but a non-standard width
1466 (e.g. `i24`, `i48`). Read each byte directly out of `_bytes` and
1467 OR-shift them together (and the reverse on write — split the
1468 value out one byte at a time). Signed fields read into the
1469 matching unsigned, then sign-extend before returning.
1470 * Path C — sub-byte alignment. Fall back to the
1471 `esi::detail::{read,write}{Un,}signedBits` helpers from
1472 `esi/BitAccess.h`, which loop over the constituent bits.
1473 * Path D — view-class fields. Triggered when `_is_value_class_type`
1474 is true — currently widths above 64 bits, for any of `BitsType`,
1475 signed, or unsigned. Returns a non-owning
1476 `esi::BitVector` / `esi::IntView` / `esi::UIntView` view *into*
1477 the parent struct's `_bytes` -- zero allocation, no copy. The
1478 setter accepts any `esi::BitVector` (so views and owning
1479 subclasses both work) and writes back via `copyBitsOut`. The
1480 returned view dangles when the parent buffer dies; see the
1481 lifetime warning at the top of the generated header.
1484 if field_cpp ==
"void":
1494 byte_offset = bit_offset // 8
1495 sub_bit_index = bit_offset % 8
1500 span_bytes = (sub_bit_index + bit_width + 7) // 8
1501 hdr.write(f
"{indent}{field_cpp} {field_name}() const {{\n")
1502 hdr.write(f
"{indent} return {field_cpp}(\n")
1503 hdr.write(f
"{indent} std::span<const uint8_t>("
1504 f
"{raw_member}.data() + {byte_offset}, {span_bytes}),\n")
1505 hdr.write(f
"{indent} static_cast<std::size_t>({bit_width}),\n")
1506 hdr.write(f
"{indent} static_cast<uint8_t>({sub_bit_index}));\n")
1507 hdr.write(f
"{indent}}}\n")
1508 hdr.write(f
"{indent}{self_type} &{field_name}("
1509 f
"const {field_cpp} &v) {{\n")
1512 hdr.write(f
"{indent} const std::size_t n = "
1513 f
"std::min<std::size_t>(v.width(), {bit_width});\n")
1515 f
"{indent} for (std::size_t b = 0; b < {bit_width}; ++b) {{\n")
1516 hdr.write(f
"{indent} const std::size_t g = {bit_offset} + b;\n")
1517 hdr.write(f
"{indent} const bool val = (b < n) && v.getBit(b);\n")
1518 hdr.write(f
"{indent} if (val)\n")
1519 hdr.write(f
"{indent} {raw_member}[g / 8] |= "
1520 f
"static_cast<uint8_t>(uint8_t{{1}} << (g % 8));\n")
1521 hdr.write(f
"{indent} else\n")
1522 hdr.write(f
"{indent} {raw_member}[g / 8] &= "
1523 f
"static_cast<uint8_t>(~(uint8_t{{1}} << (g % 8)));\n")
1524 hdr.write(f
"{indent} }}\n")
1525 hdr.write(f
"{indent} return *this;\n")
1526 hdr.write(f
"{indent}}}\n")
1531 if bit_width == 1
and field_cpp ==
"bool":
1532 hdr.write(f
"{indent}bool {field_name}() const {{\n")
1533 hdr.write(f
"{indent} return esi::detail::readUnsignedBits<uint8_t, "
1534 f
"{bit_offset}, 1>({raw_member}.data()) != 0;\n")
1535 hdr.write(f
"{indent}}}\n")
1536 hdr.write(f
"{indent}{self_type} &{field_name}(bool v) {{\n")
1537 hdr.write(f
"{indent} esi::detail::writeUnsignedBits<uint8_t, "
1538 f
"{bit_offset}, 1>({raw_member}.data(), "
1539 f
"static_cast<uint8_t>(v) & uint8_t{{1}});\n")
1540 hdr.write(f
"{indent} return *this;\n")
1541 hdr.write(f
"{indent}}}\n")
1545 byte_aligned = (bit_offset % 8 == 0
and bit_width % 8 == 0)
1548 byte_offset = bit_offset // 8
1549 byte_width = bit_width // 8
1556 if bit_width
in (8, 16, 32, 64):
1557 hdr.write(f
"{indent}{field_cpp} {field_name}() const {{\n")
1558 hdr.write(f
"{indent} {field_cpp} out;\n")
1559 hdr.write(f
"{indent} std::memcpy(&out, {raw_member}.data() + "
1560 f
"{byte_offset}, sizeof({field_cpp}));\n")
1561 hdr.write(f
"{indent} return out;\n")
1562 hdr.write(f
"{indent}}}\n")
1563 hdr.write(f
"{indent}{self_type} &{field_name}({field_cpp} v) {{\n")
1564 hdr.write(f
"{indent} std::memcpy({raw_member}.data() + "
1565 f
"{byte_offset}, &v, sizeof({field_cpp}));\n")
1566 hdr.write(f
"{indent} return *this;\n")
1567 hdr.write(f
"{indent}}}\n")
1581 unsigned_cpp =
"u" + field_cpp
1583 unsigned_cpp = field_cpp
1586 f
"static_cast<{unsigned_cpp}>({raw_member}[{byte_offset}])"
1588 for i
in range(1, byte_width):
1590 f
"(static_cast<{unsigned_cpp}>({raw_member}[{byte_offset + i}])"
1592 read_expr =
" |\n ".join(read_terms)
1594 hdr.write(f
"{indent}{field_cpp} {field_name}() const {{\n")
1596 hdr.write(f
"{indent} {unsigned_cpp} u = {read_expr};\n")
1599 f
"{indent} if (u & ({unsigned_cpp}{{1}} << {bit_width - 1}))\n")
1600 hdr.write(f
"{indent} u |= ~(({unsigned_cpp}{{1}} << {bit_width})"
1601 f
" - {unsigned_cpp}{{1}});\n")
1602 hdr.write(f
"{indent} return static_cast<{field_cpp}>(u);\n")
1604 hdr.write(f
"{indent} return {read_expr};\n")
1605 hdr.write(f
"{indent}}}\n")
1607 hdr.write(f
"{indent}{self_type} &{field_name}({field_cpp} v) {{\n")
1609 hdr.write(f
"{indent} {unsigned_cpp} u = "
1610 f
"static_cast<{unsigned_cpp}>(v);\n")
1614 for i
in range(byte_width):
1616 hdr.write(f
"{indent} {raw_member}[{byte_offset}] = "
1617 f
"static_cast<uint8_t>({value_expr});\n")
1619 hdr.write(f
"{indent} {raw_member}[{byte_offset + i}] = "
1620 f
"static_cast<uint8_t>({value_expr} >> {i * 8});\n")
1621 hdr.write(f
"{indent} return *this;\n")
1622 hdr.write(f
"{indent}}}\n")
1627 hdr.write(f
"{indent}{field_cpp} {field_name}() const {{\n")
1628 hdr.write(f
"{indent} return esi::detail::readSignedBits<{field_cpp}, "
1629 f
"{bit_offset}, {bit_width}>({raw_member}.data());\n")
1630 hdr.write(f
"{indent}}}\n")
1631 hdr.write(f
"{indent}{self_type} &{field_name}({field_cpp} v) {{\n")
1632 hdr.write(f
"{indent} esi::detail::writeSignedBits<{field_cpp}, "
1633 f
"{bit_offset}, {bit_width}>({raw_member}.data(), v);\n")
1634 hdr.write(f
"{indent} return *this;\n")
1635 hdr.write(f
"{indent}}}\n")
1637 hdr.write(f
"{indent}{field_cpp} {field_name}() const {{\n")
1639 f
"{indent} return esi::detail::readUnsignedBits<{field_cpp}, "
1640 f
"{bit_offset}, {bit_width}>({raw_member}.data());\n")
1641 hdr.write(f
"{indent}}}\n")
1642 hdr.write(f
"{indent}{self_type} &{field_name}({field_cpp} v) {{\n")
1643 hdr.write(f
"{indent} esi::detail::writeUnsignedBits<{field_cpp}, "
1644 f
"{bit_offset}, {bit_width}>({raw_member}.data(), v);\n")
1645 hdr.write(f
"{indent} return *this;\n")
1646 hdr.write(f
"{indent}}}\n")
1658 assert bit_width >= 0, (
1659 f
"field '{field_name}': unbounded aggregate field reached the "
1660 f
"emitter; the planner should have excluded the parent struct "
1661 f
"via `_contains_unbounded`")
1662 byte_aligned = (bit_offset % 8 == 0
and bit_width % 8 == 0)
1663 byte_offset = bit_offset // 8
1664 byte_width = (bit_width + 7) // 8
1686 packed_elem = (self.
_unwrap_aliases(wrapped.element_type)
if isinstance(
1689 not is_view_array
and
1690 isinstance(packed_elem,
1697 elem_type = wrapped.element_type
1699 elem_width = elem_type.bit_width
1700 elem_count = wrapped.size
1708 hdr.write(f
"{indent}{elem_cpp} {field_name}(std::size_t i) const {{\n")
1709 hdr.write(f
"{indent} {elem_cpp} out{{}};\n")
1710 hdr.write(f
"{indent} auto *dst = reinterpret_cast<uint8_t *>(&out);\n")
1711 hdr.write(f
"{indent} const std::size_t bit_off = "
1712 f
"{bit_offset} + i * {elem_width};\n")
1714 f
"{indent} for (std::size_t b = 0; b < {elem_width}; ++b) {{\n")
1715 hdr.write(f
"{indent} const std::size_t g = bit_off + b;\n")
1716 hdr.write(f
"{indent} if (({raw_member}[g / 8] >> (g % 8)) & 1u)\n")
1717 hdr.write(f
"{indent} dst[b / 8] |= "
1718 f
"static_cast<uint8_t>(uint8_t{{1}} << (b % 8));\n")
1719 hdr.write(f
"{indent} }}\n")
1720 hdr.write(f
"{indent} return out;\n")
1721 hdr.write(f
"{indent}}}\n")
1723 hdr.write(f
"{indent}{self_type} &{field_name}(std::size_t i, "
1724 f
"const {elem_cpp} &v) {{\n")
1725 hdr.write(f
"{indent} const auto *src = "
1726 f
"reinterpret_cast<const uint8_t *>(&v);\n")
1727 hdr.write(f
"{indent} const std::size_t bit_off = "
1728 f
"{bit_offset} + i * {elem_width};\n")
1730 f
"{indent} for (std::size_t b = 0; b < {elem_width}; ++b) {{\n")
1731 hdr.write(f
"{indent} const std::size_t g = bit_off + b;\n")
1732 hdr.write(f
"{indent} const uint8_t bit = static_cast<uint8_t>("
1733 f
"(src[b / 8] >> (b % 8)) & 1u);\n")
1734 hdr.write(f
"{indent} {raw_member}[g / 8] = static_cast<uint8_t>(\n")
1736 f
"{indent} ({raw_member}[g / 8] & static_cast<uint8_t>("
1737 f
"~(uint8_t{{1}} << (g % 8)))) |\n")
1738 hdr.write(f
"{indent} static_cast<uint8_t>(bit << (g % 8)));\n")
1739 hdr.write(f
"{indent} }}\n")
1740 hdr.write(f
"{indent} return *this;\n")
1741 hdr.write(f
"{indent}}}\n")
1744 is_bool = (elem_cpp ==
"bool")
1750 unsigned_cpp =
"uint8_t"
1752 unsigned_cpp =
"u" + elem_cpp
1754 unsigned_cpp = elem_cpp
1758 hdr.write(f
"{indent}{elem_cpp} {field_name}(std::size_t i) const {{\n")
1759 hdr.write(f
"{indent} const std::size_t bit_off = "
1760 f
"{bit_offset} + i * {elem_width};\n")
1762 hdr.write(f
"{indent} return (({raw_member}[bit_off / 8] >> "
1763 f
"(bit_off % 8)) & 1u) != 0;\n")
1765 hdr.write(f
"{indent} {unsigned_cpp} u = 0;\n")
1767 f
"{indent} for (std::size_t b = 0; b < {elem_width}; ++b) {{\n")
1768 hdr.write(f
"{indent} const std::size_t g = bit_off + b;\n")
1769 hdr.write(f
"{indent} u = static_cast<{unsigned_cpp}>(\n")
1770 hdr.write(f
"{indent} u | (static_cast<{unsigned_cpp}>("
1771 f
"({raw_member}[g / 8] >> (g % 8)) & 1u) << b));\n")
1772 hdr.write(f
"{indent} }}\n")
1774 hdr.write(f
"{indent} constexpr {unsigned_cpp} signBit = "
1775 f
"static_cast<{unsigned_cpp}>({unsigned_cpp}{{1}} << "
1776 f
"{elem_width - 1});\n")
1777 hdr.write(f
"{indent} u = static_cast<{unsigned_cpp}>("
1778 f
"(u ^ signBit) - signBit);\n")
1779 hdr.write(f
"{indent} return static_cast<{elem_cpp}>(u);\n")
1780 hdr.write(f
"{indent}}}\n")
1784 hdr.write(f
"{indent}{self_type} &{field_name}(std::size_t i, "
1785 f
"{elem_cpp} v) {{\n")
1786 hdr.write(f
"{indent} const std::size_t bit_off = "
1787 f
"{bit_offset} + i * {elem_width};\n")
1788 hdr.write(f
"{indent} const {unsigned_cpp} u = "
1789 f
"static_cast<{unsigned_cpp}>(v);\n")
1791 f
"{indent} for (std::size_t b = 0; b < {elem_width}; ++b) {{\n")
1792 hdr.write(f
"{indent} const std::size_t g = bit_off + b;\n")
1793 hdr.write(f
"{indent} const uint8_t bit = static_cast<uint8_t>("
1794 f
"(u >> b) & {unsigned_cpp}{{1}});\n")
1795 hdr.write(f
"{indent} {raw_member}[g / 8] = static_cast<uint8_t>(\n")
1797 f
"{indent} ({raw_member}[g / 8] & static_cast<uint8_t>("
1798 f
"~(uint8_t{{1}} << (g % 8)))) |\n")
1799 hdr.write(f
"{indent} static_cast<uint8_t>(bit << (g % 8)));\n")
1800 hdr.write(f
"{indent} }}\n")
1801 hdr.write(f
"{indent} return *this;\n")
1802 hdr.write(f
"{indent}}}\n")
1806 hdr.write(f
"{indent}{field_cpp} {field_name}() const {{\n")
1807 hdr.write(f
"{indent} {field_cpp} out{{}};\n")
1808 hdr.write(f
"{indent} for (std::size_t i = 0; i < {elem_count}; ++i)\n")
1809 hdr.write(f
"{indent} out[i] = {field_name}(i);\n")
1810 hdr.write(f
"{indent} return out;\n")
1811 hdr.write(f
"{indent}}}\n")
1812 hdr.write(f
"{indent}{self_type} &{field_name}(const {field_cpp} &v) {{\n")
1813 hdr.write(f
"{indent} for (std::size_t i = 0; i < {elem_count}; ++i)\n")
1814 hdr.write(f
"{indent} {field_name}(i, v[i]);\n")
1815 hdr.write(f
"{indent} return *this;\n")
1816 hdr.write(f
"{indent}}}\n")
1819 if not is_view_array:
1824 hdr.write(f
"{indent}{field_cpp} {field_name}() const {{\n")
1825 hdr.write(f
"{indent} {field_cpp} out{{}};\n")
1826 hdr.write(f
"{indent} for (std::size_t i = 0; i < {byte_width}; ++i)\n")
1827 hdr.write(f
"{indent} reinterpret_cast<uint8_t *>(&out)[i] = "
1828 f
"{raw_member}[{byte_offset} + i];\n")
1829 hdr.write(f
"{indent} return out;\n")
1830 hdr.write(f
"{indent}}}\n")
1832 f
"{indent}{self_type} &{field_name}(const {field_cpp} &v) {{\n")
1833 hdr.write(f
"{indent} for (std::size_t i = 0; i < {byte_width}; ++i)\n")
1834 hdr.write(f
"{indent} {raw_member}[{byte_offset} + i] = "
1835 f
"reinterpret_cast<const uint8_t *>(&v)[i];\n")
1836 hdr.write(f
"{indent} return *this;\n")
1837 hdr.write(f
"{indent}}}\n")
1842 hdr.write(f
"{indent}{field_cpp} {field_name}() const {{\n")
1843 hdr.write(f
"{indent} {field_cpp} out{{}};\n")
1844 hdr.write(f
"{indent} esi::detail::copyBitsIn<{bit_offset}, "
1845 f
"{bit_width}>({raw_member}.data(), "
1846 f
"reinterpret_cast<uint8_t *>(&out));\n")
1847 hdr.write(f
"{indent} return out;\n")
1848 hdr.write(f
"{indent}}}\n")
1850 f
"{indent}{self_type} &{field_name}(const {field_cpp} &v) {{\n")
1851 hdr.write(f
"{indent} esi::detail::copyBitsOut<{bit_offset}, "
1852 f
"{bit_width}>({raw_member}.data(), "
1853 f
"reinterpret_cast<const uint8_t *>(&v));\n")
1854 hdr.write(f
"{indent} return *this;\n")
1855 hdr.write(f
"{indent}}}\n")
1865 byte_aligned
and wrapped.element_type.bit_width % 8 == 0):
1866 elem_cpp = self.
_cpp_type(wrapped.element_type)
1867 if elem_cpp !=
"void":
1869 hdr.write(f
"{indent}{elem_cpp} {field_name}(std::size_t i) const {{\n")
1870 hdr.write(f
"{indent} {elem_cpp} out{{}};\n")
1871 hdr.write(f
"{indent} for (std::size_t j = 0; j < {elem_bytes}; ++j)\n")
1872 hdr.write(f
"{indent} reinterpret_cast<uint8_t *>(&out)[j] = "
1873 f
"{raw_member}[{byte_offset} + i * {elem_bytes} + j];\n")
1874 hdr.write(f
"{indent} return out;\n")
1875 hdr.write(f
"{indent}}}\n")
1876 hdr.write(f
"{indent}{self_type} &{field_name}(std::size_t i, "
1877 f
"const {elem_cpp} &v) {{\n")
1878 hdr.write(f
"{indent} for (std::size_t j = 0; j < {elem_bytes}; ++j)\n")
1879 hdr.write(f
"{indent} {raw_member}[{byte_offset} + i * {elem_bytes}"
1880 f
" + j] = reinterpret_cast<const uint8_t *>(&v)[j];\n")
1881 hdr.write(f
"{indent} return *this;\n")
1882 hdr.write(f
"{indent}}}\n")
1905 elem_type = wrapped.element_type
1907 elem_width = elem_type.bit_width
1908 elem_count = wrapped.size
1909 hdr.write(f
"{indent}{elem_cpp} {field_name}(std::size_t i) const {{\n")
1910 hdr.write(f
"{indent} const std::size_t bit_off = "
1911 f
"{bit_offset} + i * {elem_width};\n")
1912 hdr.write(f
"{indent} return {elem_cpp}(\n")
1913 hdr.write(f
"{indent} std::span<const uint8_t>("
1914 f
"{raw_member}.data() + bit_off / 8,\n")
1915 hdr.write(f
"{indent} "
1916 f
"(bit_off % 8 + {elem_width} + 7) / 8),\n")
1917 hdr.write(f
"{indent} static_cast<std::size_t>({elem_width}),\n")
1918 hdr.write(f
"{indent} static_cast<uint8_t>(bit_off % 8));\n")
1919 hdr.write(f
"{indent}}}\n")
1920 hdr.write(f
"{indent}{self_type} &{field_name}(std::size_t i, "
1921 f
"const {elem_cpp} &v) {{\n")
1922 hdr.write(f
"{indent} const std::size_t bit_off = "
1923 f
"{bit_offset} + i * {elem_width};\n")
1924 hdr.write(f
"{indent} const std::size_t n = "
1925 f
"std::min<std::size_t>(v.width(), {elem_width});\n")
1927 f
"{indent} for (std::size_t b = 0; b < {elem_width}; ++b) {{\n")
1928 hdr.write(f
"{indent} const std::size_t g = bit_off + b;\n")
1929 hdr.write(f
"{indent} const bool val = (b < n) && v.getBit(b);\n")
1930 hdr.write(f
"{indent} if (val)\n")
1931 hdr.write(f
"{indent} {raw_member}[g / 8] |= "
1932 f
"static_cast<uint8_t>(uint8_t{{1}} << (g % 8));\n")
1933 hdr.write(f
"{indent} else\n")
1934 hdr.write(f
"{indent} {raw_member}[g / 8] &= "
1935 f
"static_cast<uint8_t>(~(uint8_t{{1}} << (g % 8)));\n")
1936 hdr.write(f
"{indent} }}\n")
1937 hdr.write(f
"{indent} return *this;\n")
1938 hdr.write(f
"{indent}}}\n")
1942 hdr.write(f
"{indent}auto {field_name}() const {{\n")
1943 hdr.write(f
"{indent} return std::views::iota("
1944 f
"std::size_t{{0}}, std::size_t{{{elem_count}}}) |\n")
1945 hdr.write(f
"{indent} std::views::transform("
1946 f
"[this](std::size_t i) {{\n")
1947 hdr.write(f
"{indent} return this->{field_name}(i);\n")
1948 hdr.write(f
"{indent} }});\n")
1949 hdr.write(f
"{indent}}}\n")
1952 hdr.write(f
"{indent}{self_type} &{field_name}("
1953 f
"const std::array<{elem_cpp}, {elem_count}> &v) {{\n")
1954 hdr.write(f
"{indent} for (std::size_t i = 0; i < {elem_count}; ++i)\n")
1955 hdr.write(f
"{indent} {field_name}(i, v[i]);\n")
1956 hdr.write(f
"{indent} return *this;\n")
1957 hdr.write(f
"{indent}}}\n")
1960 """Return the C++ constructor-parameter type for a setter call."""
1965 return f
"const {field_cpp} &"
1968 """Emit a packed struct as a raw byte buffer with bit-precise accessors.
1970 The struct's storage is a single `std::array<uint8_t, N>` (where `N`
1971 is the byte width derived from the manifest) whose layout matches the
1972 on-wire bit-packing exactly. Per-field accessors (generated below)
1973 read/write the bits so the wire format does not depend on C++ bit-field
1974 allocation rules, which differ between the Itanium ABI (GCC/Clang) and MSVC.
1978 if struct_type.bit_width == 0:
1982 total_bytes = (total_bits + 7) // 8
1986 logical_fields = [(name, ftype)
1987 for name, ftype
in struct_type.fields
1995 if not logical_fields:
1998 hdr.write(f
"struct {struct_name} {{\n")
2003 hdr.write(
"private:\n")
2004 hdr.write(f
" std::array<uint8_t, {max(total_bytes, 1)}> _bytes{{}};\n\n")
2005 hdr.write(
"public:\n")
2007 hdr.write(f
" {struct_name}() = default;\n")
2008 ctor_params =
", ".join(f
"{self._ctor_param_type(ftype)} {name}"
2009 for name, ftype
in logical_fields)
2010 hdr.write(f
" {struct_name}({ctor_params}) {{\n")
2013 chain =
".".join(f
"{name}({name})" for name, _
in logical_fields)
2014 hdr.write(f
" this->{chain};\n")
2019 for name, ftype
in logical_fields:
2021 bit_offsets[name], ftype.bit_width)
2024 hdr.write(f
" static constexpr std::string_view _ESI_ID = "
2025 f
"{self._cpp_string_literal(struct_type.id)};\n")
2028 if expected_bytes
is not None and expected_bytes > 0:
2033 """Emit a union as a raw byte buffer with per-variant accessors.
2035 The union's storage is a single `std::array<uint8_t, N>` sized to the
2036 widest variant. Each variant lives at the MSB end of the buffer
2037 (matching the existing SV-style packed union layout where padding
2038 occupies the lower bytes), so the byte offset for a variant of size
2039 V is `union_bytes - V`. Sub-byte integer variants are byte-padded to
2040 full bytes within that region, matching the Python runtime's union
2045 if union_type.bit_width == 0:
2050 hdr.write(f
"struct {union_name} {{\n")
2052 hdr.write(
"private:\n")
2053 hdr.write(f
" std::array<uint8_t, {union_bytes}> _bytes{{}};\n\n")
2054 hdr.write(
"public:\n")
2055 hdr.write(f
" {union_name}() = default;\n\n")
2057 for field_name, field_type
in union_type.fields:
2059 if field_cpp ==
"void":
2062 byte_offset = union_bytes - field_bytes
2063 bit_offset = byte_offset * 8
2064 bit_width = field_type.bit_width
2069 field_type, bit_offset, bit_width)
2072 hdr.write(f
" static constexpr std::string_view _ESI_ID = "
2073 f
"{self._cpp_string_literal(union_type.id)};\n")
2080 """Compute (name, type, byte_offset, bit_width) for each window frame field.
2082 Fields are listed in C++ declaration order. They start after `pad_bytes`
2083 bytes of padding and each field consumes `ceil(bit_width/8)` bytes —
2084 matching what the `#pragma pack(1)` layout produced.
2086 The sentinel `(name, None)` count field is materialised as
2087 `count_type_synth` so it can flow through the standard
2088 `_emit_field_accessor` path.
2091 byte_offset = pad_bytes
2092 for name, ftype
in fields:
2093 actual_type = count_type_synth
if ftype
is None else ftype
2094 bit_width = actual_type.bit_width
2095 layout.append((name, actual_type, byte_offset, bit_width))
2096 byte_offset += (bit_width + 7) // 8
2101 """Emit a window header/data frame as a raw-bytes struct with accessors.
2103 Nested inside the window helper class (`indent` = 2 spaces) and uses
2104 the same B3 accessor pattern as top-level structs/unions: a private
2105 `_bytes` array plus per-field getter/setter pairs returning
2106 `frame_name &` to allow chaining.
2108 hdr.write(f
" struct {frame_name} {{\n")
2109 hdr.write(
" private:\n")
2110 hdr.write(f
" std::array<uint8_t, {frame_bytes}> _bytes{{}};\n\n")
2111 hdr.write(
" public:\n")
2112 for name, ftype, byte_offset, bit_width
in layout:
2114 byte_offset * 8, bit_width)
2120 """Emit a SegmentedMessageData helper for a serial list window."""
2124 for name, field_type
in info[
"ctor_params"]
2126 value_ctor_params = list(ctor_params)
2127 value_ctor_params.append(
2128 f
"const std::vector<value_type> &{info['list_field_name']}")
2129 value_ctor_signature =
", ".join(value_ctor_params)
2130 frame_ctor_params = list(ctor_params)
2131 frame_ctor_params.append(
"std::vector<data_frame> frames")
2132 frame_ctor_signature =
", ".join(frame_ctor_params)
2133 helper_args =
", ".join(name
for name, _
in info[
"ctor_params"])
2134 helper_call = f
"{helper_args}, std::move(frames)" if helper_args
else "std::move(frames)"
2139 count_type_synth =
types.UIntType(f
"_count_ui{info['count_width']}",
2140 info[
"count_width"])
2142 info[
"data_pad_bytes"],
2145 info[
"header_pad_bytes"],
2149 f
"struct {info['window_name']} : public esi::SegmentedMessageData {{\n")
2150 hdr.write(
"public:\n")
2151 hdr.write(f
" using value_type = {info['element_cpp']};\n")
2152 hdr.write(f
" using count_type = {info['count_cpp']};\n\n")
2155 hdr.write(
"private:\n")
2159 hdr.write(
" header_frame header{};\n")
2160 hdr.write(
" std::vector<data_frame> data_frames;\n")
2161 hdr.write(
" header_frame footer{};\n\n")
2162 hdr.write(f
" void construct({frame_ctor_signature}) {{\n")
2163 hdr.write(
" if (frames.empty())\n")
2165 f
" throw std::invalid_argument(\"{info['window_name']}: bulk windowed lists cannot be empty\");\n"
2168 " if (frames.size() > std::numeric_limits<count_type>::max())\n")
2170 f
" throw std::invalid_argument(\"{info['window_name']}: list too large for encoded count\");\n"
2173 f
" header.{info['count_field_name']}(static_cast<count_type>(frames.size()));\n"
2175 for name, _
in info[
"ctor_params"]:
2176 hdr.write(f
" header.{name}({name});\n")
2177 hdr.write(f
" footer.{info['count_field_name']}(0);\n")
2178 hdr.write(
" data_frames = std::move(frames);\n")
2180 hdr.write(
"public:\n")
2181 hdr.write(f
" {info['window_name']}({frame_ctor_signature}) {{\n")
2182 hdr.write(f
" construct({helper_call});\n")
2184 hdr.write(f
" {info['window_name']}({value_ctor_signature}) {{\n")
2185 hdr.write(
" std::vector<data_frame> frames;\n")
2186 hdr.write(f
" frames.reserve({info['list_field_name']}.size());\n")
2187 hdr.write(f
" for (const auto &element : {info['list_field_name']}) {{\n")
2188 hdr.write(
" auto &frame = frames.emplace_back();\n")
2189 hdr.write(f
" frame.{info['list_field_name']}(element);\n")
2191 hdr.write(f
" construct({helper_call});\n")
2193 hdr.write(
" size_t numSegments() const override { return 3; }\n")
2194 hdr.write(
" esi::Segment segment(size_t idx) const override {\n")
2195 hdr.write(
" if (idx == 0)\n")
2197 " return {reinterpret_cast<const uint8_t *>(&header), sizeof(header)};\n"
2199 hdr.write(
" if (idx == 1)\n")
2201 " return {reinterpret_cast<const uint8_t *>(data_frames.data()),\n"
2203 hdr.write(
" data_frames.size() * sizeof(data_frame)};\n")
2204 hdr.write(
" if (idx == 2)\n")
2206 " return {reinterpret_cast<const uint8_t *>(&footer), sizeof(footer)};\n"
2209 f
" throw std::out_of_range(\"{info['window_name']}: invalid segment index\");\n"
2213 f
" static constexpr std::string_view _ESI_ID = {self._cpp_string_literal(self._unwrap_aliases(window_type.into_type).id)};\n"
2219 f
" static constexpr std::string_view _ESI_WINDOW_ID = {self._cpp_string_literal(window_type.id)};\n"
2226 """Emit accessors for the header and data fields of a window helper.
2228 Exposes each static header field as a scalar accessor, the count of data
2229 frames, and one vector-valued accessor per data field so decoded values
2230 are easy to inspect on the read side.
2232 list_field_name = info[
"list_field_name"]
2234 for field_name, field_type
in info[
"header_fields"]:
2237 if field_type
is None:
2244 f
" {cpp} {field_name}() const {{ return header.{field_name}(); }}\n")
2246 f
" size_t {list_field_name}_count() const {{ return data_frames.size(); }}\n"
2248 for field_name, field_type
in info[
"data_fields"]:
2249 if field_name == list_field_name:
2250 elem_cpp =
"value_type"
2266 projection = (f
"[](const data_frame &f) -> {elem_cpp} "
2267 f
"{{ return f.{field_name}(); }}")
2269 f
" auto {field_name}() const {{\n"
2270 f
" return std::views::transform(data_frames, {projection});\n"
2272 hdr.write(f
" std::vector<{elem_cpp}> {field_name}_vector() const {{\n"
2273 f
" std::vector<{elem_cpp}> out;\n"
2274 f
" out.reserve(data_frames.size());\n"
2275 f
" for (const auto &frame : data_frames)\n"
2276 f
" out.push_back(frame.{field_name}());\n"
2281 """Emit a few bridge helpers + a `TypeDeserializer` alias.
2283 The actual decoder lives in `esi::SerialListTypeDeserializer<T>`, which
2284 walks the header/data/footer burst protocol generically. Each window
2285 helper only has to expose:
2287 - `_headerCount(const header_frame &)` -> `count_type`
2288 - `_fromFrames(const header_frame &, std::vector<data_frame> &&)`
2289 -> `std::unique_ptr<T>`
2291 plus a `friend class esi::SerialListTypeDeserializer<T>;` so the template
2292 can reach the (private) `header_frame` definition.
2294 window_name = info[
"window_name"]
2295 count_field_name = info[
"count_field_name"]
2296 ctor_args =
", ".join(f
"h.{name}()" for name, _
in info[
"ctor_params"])
2298 ctor_args = f
"{ctor_args}, std::move(frames)"
2300 ctor_args =
"std::move(frames)"
2303 hdr.write(
"private:\n")
2305 " // Bridge helpers used by esi::SerialListTypeDeserializer<T>; the\n")
2307 " // template walks the serial-list burst protocol generically and\n")
2309 " // reaches into `header_frame` via the friend declaration below.\n")
2310 hdr.write(
" static count_type _headerCount(const header_frame &h) {\n")
2311 hdr.write(f
" return h.{count_field_name}();\n")
2313 hdr.write(f
" static std::unique_ptr<{window_name}> _fromFrames(\n")
2315 " const header_frame &h, std::vector<data_frame> &&frames) {\n")
2316 hdr.write(f
" return std::make_unique<{window_name}>({ctor_args});\n")
2319 f
" friend class esi::SerialListTypeDeserializer<{window_name}>;\n\n")
2320 hdr.write(
"public:\n")
2322 f
" using TypeDeserializer = esi::SerialListTypeDeserializer<{window_name}>;\n"
2326 """Emit a using alias when the alias targets a different C++ type."""
2327 inner_wrapped = alias_type.inner_type
2330 if inner_wrapped
is not None:
2331 inner_cpp = self.
_cpp_type(inner_wrapped)
2332 if inner_cpp
is None:
2334 if inner_cpp != alias_name:
2335 hdr.write(f
"using {alias_name} = {inner_cpp};\n\n")
2338 """Emit the fully ordered types.h header into the output directory."""
2339 hdr_file = output_dir /
"types.h"
2340 with open(hdr_file,
"w", encoding=
"utf-8")
as hdr:
2342 textwrap.dedent(f
"""
2343 // Generated header for {system_name} types.
2345 // Lifetime note: accessors that return `esi::BitVector` (for
2346 // `Bits<N>` fields), `esi::IntView` (for `Int<N>`, N > 64), or
2347 // `esi::UIntView` (for `UInt<N>`, N > 64) hand back non-owning
2348 // views into the parent struct's bytes. The view is only valid
2349 // while the parent is alive. Bind the parent to a named local
2350 // first, or construct an owning `esi::Int` / `esi::UInt` /
2351 // `esi::MutableBitVector` from the view if you need the value
2352 // to outlive its source. See `esi/Values.h` for details.
2358 #include <algorithm>
2363 #include <stdexcept>
2364 #include <string_view>
2368 #include "esi/BitAccess.h"
2369 #include "esi/Common.h"
2370 #include "esi/TypedPorts.h"
2371 #include "esi/Values.h"
2373 namespace {system_name} {{
2377 sys.stderr.write(
"Warning: cyclic type dependencies detected.\n")
2378 sys.stderr.write(
" Logically this should not be possible.\n")
2380 " Emitted code may fail to compile due to ordering issues.\n")
2383 hdr.write(f
"// Unsupported type '{skipped_type}': {reason}\n\n")
2399 hdr.write(textwrap.dedent(f
"""
2400 }} // namespace {system_name}
2404def run(generator: Type[Generator] = CppGenerator,
2405 cmdline_args=sys.argv) -> int:
2406 """Create and run a generator reading options from the command line."""
2408 argparser = argparse.ArgumentParser(
2409 description=f
"Generate {generator.language} headers from an ESI manifest",
2410 formatter_class=argparse.RawDescriptionHelpFormatter,
2411 epilog=textwrap.dedent(
"""
2412 Can read the manifest from either a file OR a running accelerator.
2415 # To read the manifest from a file:
2416 esi-cppgen --file /path/to/manifest.json
2418 # To read the manifest from a running accelerator:
2419 esi-cppgen --platform cosim --connection localhost:1234
2422 argparser.add_argument(
"--file",
2425 help=
"Path to the manifest file.")
2426 argparser.add_argument(
2429 help=
"Name of platform for live accelerator connection.")
2430 argparser.add_argument(
2433 help=
"Connection string for live accelerator connection.")
2434 argparser.add_argument(
2438 help=
"Output directory for generated files. Recommend adding either `esi`"
2439 " or the system name to the end of the path so as to avoid header name"
2440 "conflicts. Defaults to `esi`")
2441 argparser.add_argument(
2444 default=
"esi_system",
2445 help=
"Name of the ESI system. For C++, this will be the namespace.")
2447 if (len(cmdline_args) <= 1):
2448 argparser.print_help()
2450 args = argparser.parse_args(cmdline_args[1:])
2452 if args.file
is not None and args.platform
is not None:
2453 print(
"Cannot specify both --file and --platform")
2456 conn: AcceleratorConnection
2457 if args.file
is not None:
2460 conn = Context.default().
connect(
"trace", f
"-{os.pathsep}{args.file}")
2461 elif args.platform
is not None:
2462 if args.connection
is None:
2463 print(
"Must specify --connection with --platform")
2465 conn = Context.default().
connect(args.platform, args.connection)
2467 print(
"Must specify either --file or --platform")
2470 output_dir = Path(args.output_dir)
2471 if output_dir.exists()
and not output_dir.is_dir():
2472 print(f
"Output directory {output_dir} is not a directory")
2474 if not output_dir.exists():
2475 output_dir.mkdir(parents=
True)
2477 gen = generator(conn)
2478 gen.generate(output_dir, args.system_name)
2482if __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)
bool _is_value_class_type(self, types.ESIType field_type)
bool _is_signed_int_field(self, 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 _ctor_param_type(self, types.ESIType field_type)
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)
Tuple[Dict[str, int], int] _compute_field_bit_offsets(self, types.StructType struct_type)
int _field_byte_width(self, types.ESIType field_type)
_compute_window_frame_layout(self, fields, pad_bytes, count_type_synth)
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_field_accessor(self, TextIO hdr, str indent, str raw_member, str self_type, str field_name, types.ESIType field_type, int bit_offset, int bit_width)
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_frame(self, TextIO hdr, str frame_name, int frame_bytes, layout)
bool _is_byte_packable(self, types.ESIType esi_type)
str _cpp_type(self, types.ESIType wrapped)
str _auto_window_name(self, types.WindowType window_type)
bool _contains_unbounded(self, types.ESIType esi_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)