CIRCT 23.0.0git
Loading...
Searching...
No Matches
ports.py
Go to the documentation of this file.
1# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2# See https://llvm.org/LICENSE.txt for license information.
3# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4"""Port-kind strategy table for the C++ module generator.
5
6Centralises everything that varies between the ESI port kinds (function,
7callback, to-host / from-host channel, MMIO region, telemetry metric, plain
8bundle) into a single `CppPortKind` record per kind plus two pure rendering
9functions, so the generator drives every port kind from one table. The
10renderers are pure functions of a `CppPortKind`, so they are golden-tested
11without a live accelerator.
12"""
13
14from dataclasses import dataclass, field as _dc_field
15from typing import List, Optional, Tuple
16
17from ..types import (BundlePort as _BundlePort, FunctionPort as _FunctionPort,
18 CallbackPort as _CallbackPort, ToHostPort as _ToHostPort,
19 FromHostPort as _FromHostPort, MMIORegion as
20 _MMIORegionPort, MetricPort as _MetricPort)
21
22
23@dataclass
25 """All strings needed to emit one port slot (member + ctor + find) in Connected."""
26 struct_decls: List[str] = _dc_field(default_factory=list)
27 member_decl: str = ""
28 ctor_params: List[str] = _dc_field(default_factory=list)
29 init_entry: str = ""
30 find_code: str = ""
31 make_unique_args: List[str] = _dc_field(default_factory=list)
32 post_connect: str = ""
33 using_aliases: List[Tuple[str, str]] = _dc_field(default_factory=list)
34
35
36@dataclass(frozen=True)
38 """Per-port-kind constants for generating a module's typed port slots.
39
40 Holds everything that varies between port kinds in one record.
41 `member_template` (typed channel/func ports) and `member_ref` (service /
42 bundle ports) are mutually exclusive. `find_style` / `indexed_find` select
43 the `connect()` resolution snippet for scalar and indexed ports
44 respectively; `alias_kind` selects which `using` aliases to emit for the
45 typed template parameters.
46 """
47 service_type: Optional[
48 str] # findPortAs<...> service type (None = plain bundle)
49 find_style: str # 'ptr' | 'read_chan' | 'write_chan' | 'deref' | 'bundle'
50 indexed_find: str # 'as_ptr' | 'chan_read' | 'chan_write' | 'bundle_addr'
51 param_type: str # ctor parameter type
52 param_suffix: str # '_port' | '_chan' | '_svc'
53 member_template: Optional[
54 str] # typed member, e.g. "esi::TypedFunction<{p}Args, {p}Result>"
55 member_ref: Optional[str] # non-typed member / ctor ref type
56 indexed_elem: Optional[
57 str] # IndexedPorts<...> element (None => use member_template)
58 alias_kind: Optional[str] # None | 'func' (Args/Result) | 'chan' (Data)
59 connectable: bool # whether generated connect() calls .connect()
60
61 def member_type(self, alias_prefix: Optional[str] = None) -> str:
62 """Return the C++ member type string for this kind (no member name).
63
64 For typed ports (function/callback/to-host/from-host channels)
65 `alias_prefix` is required; the returned template parameters are written
66 using the alias names (`<prefix>Args`, `<prefix>Result`, `<prefix>Data`)
67 that should be emitted at module-class scope. For non-typed ports (MMIO
68 regions, telemetry metrics, plain bundles) `alias_prefix` is ignored and
69 the runtime reference/pointer type is returned directly.
70 """
71 if self.member_template is not None:
72 assert alias_prefix is not None, (
73 "alias_prefix is required for typed ports to avoid emitting long "
74 "mangled type names inline (which would also collide across "
75 "modules' `using` declarations)")
76 return self.member_template.format(p=alias_prefix)
77 # Every non-typed kind defines a reference member type.
78 assert self.member_ref is not None
79 return self.member_ref
80
81 def indexed_elem_type(self, alias_prefix: Optional[str] = None) -> str:
82 """Return the storage type used inside an `IndexedPorts<T>` for this kind.
83
84 Typed ports use the same `TypedFunction<...>` / `TypedReadPort<...>` etc.
85 that `member_type` produces. MMIO regions, telemetry metrics, and plain
86 bundle ports are stored as raw pointers because `std::map<int, T&>` is
87 ill-formed.
88 """
89 if self.indexed_elem is not None:
90 return self.indexed_elem
91 return self.member_type(alias_prefix=alias_prefix)
92
93 def scalar_find_code(self, v: str, appid_expr: str) -> str:
94 """Render the connect()-body snippet that resolves one scalar port into the
95 local `v`. Pure function of the kind so it can be golden-tested without a
96 live accelerator (see `test_port_find_code_golden`)."""
97 if self.find_stylefind_style == "ptr":
98 return (f"auto *{v} =\n"
99 f" esi::findPortAsOrThrow<{self.service_type}>(\n"
100 f" rawModule, {appid_expr});")
101 if self.find_stylefind_style == "read_chan":
102 return (f"auto &{v} =\n"
103 f" esi::findPortAsOrThrow<{self.service_type}>(\n"
104 f' rawModule, {appid_expr})->getRawRead("data");')
105 if self.find_stylefind_style == "write_chan":
106 return (f"auto &{v} =\n"
107 f" esi::findPortAsOrThrow<{self.service_type}>(\n"
108 f' rawModule, {appid_expr})->getRawWrite("data");')
109 if self.find_stylefind_style == "deref":
110 return (f"auto &{v} =\n"
111 f" *esi::findPortAsOrThrow<{self.service_type}>(\n"
112 f" rawModule, {appid_expr});")
113 if self.find_stylefind_style == "bundle":
114 # plain BundlePort
115 return f"auto &{v} = esi::findPortOrThrow(rawModule, {appid_expr});"
116 raise ValueError(f"unknown find_style: {self.find_style!r}")
117
118 def indexed_find_code(self, map_var: str, appid_expr: str) -> str:
119 """Render the per-index `try_emplace` body for an `IndexedPorts<T>` group.
120 Pure function of the kind (golden-tested alongside `scalar_find_code`)."""
121 if self.indexed_findindexed_find == "as_ptr":
122 return (f" {map_var}.try_emplace(\n"
123 f" static_cast<int>(idx),\n"
124 f" esi::findPortAsOrThrow<{self.service_type}>(\n"
125 f" rawModule, {appid_expr}));")
126 if self.indexed_findindexed_find == "chan_read":
127 return (f" auto *svc =\n"
128 f" esi::findPortAsOrThrow<{self.service_type}>(\n"
129 f" rawModule, {appid_expr});\n"
130 f" {map_var}.try_emplace(\n"
131 f" static_cast<int>(idx),\n"
132 f' svc->getRawRead("data"));')
133 if self.indexed_findindexed_find == "chan_write":
134 return (f" auto *svc =\n"
135 f" esi::findPortAsOrThrow<{self.service_type}>(\n"
136 f" rawModule, {appid_expr});\n"
137 f" {map_var}.try_emplace(\n"
138 f" static_cast<int>(idx),\n"
139 f' svc->getRawWrite("data"));')
140 if self.indexed_findindexed_find == "bundle_addr":
141 return (f" {map_var}.try_emplace(\n"
142 f" static_cast<int>(idx),\n"
143 f" &esi::findPortOrThrow(rawModule, {appid_expr}));")
144 raise ValueError(f"unknown indexed_find: {self.indexed_find!r}")
145
146
147# Ordered list of (runtime port class, kind). `cpp_port_kind` returns the first
148# match, falling back to `CPP_BUNDLE_KIND` for any unrecognised service port.
149CPP_PORT_KINDS: List[Tuple[type, CppPortKind]] = [
150 (_FunctionPort,
151 CppPortKind(service_type="esi::services::FuncService::Function",
152 find_style="ptr",
153 indexed_find="as_ptr",
154 param_type="esi::services::FuncService::Function *",
155 param_suffix="_port",
156 member_template="esi::TypedFunction<{p}Args, {p}Result>",
157 member_ref=None,
158 indexed_elem=None,
159 alias_kind="func",
160 connectable=True)),
161 (_CallbackPort,
162 CppPortKind(service_type="esi::services::CallService::Callback",
163 find_style="ptr",
164 indexed_find="as_ptr",
165 param_type="esi::services::CallService::Callback *",
166 param_suffix="_port",
167 member_template="esi::TypedCallback<{p}Args, {p}Result>",
168 member_ref=None,
169 indexed_elem=None,
170 alias_kind="func",
171 connectable=False)),
172 (_ToHostPort,
173 CppPortKind(service_type="esi::services::ChannelService::ToHost",
174 find_style="read_chan",
175 indexed_find="chan_read",
176 param_type="esi::ReadChannelPort &",
177 param_suffix="_chan",
178 member_template="esi::TypedReadPort<{p}Data>",
179 member_ref=None,
180 indexed_elem=None,
181 alias_kind="chan",
182 connectable=True)),
183 (_FromHostPort,
184 CppPortKind(service_type="esi::services::ChannelService::FromHost",
185 find_style="write_chan",
186 indexed_find="chan_write",
187 param_type="esi::WriteChannelPort &",
188 param_suffix="_chan",
189 member_template="esi::TypedWritePort<{p}Data>",
190 member_ref=None,
191 indexed_elem=None,
192 alias_kind="chan",
193 connectable=True)),
194 (_MMIORegionPort,
195 CppPortKind(service_type="esi::services::MMIO::MMIORegion",
196 find_style="deref",
197 indexed_find="as_ptr",
198 param_type="esi::services::MMIO::MMIORegion &",
199 param_suffix="_svc",
200 member_template=None,
201 member_ref="esi::services::MMIO::MMIORegion &",
202 indexed_elem="esi::services::MMIO::MMIORegion *",
203 alias_kind=None,
204 connectable=False)),
205 (_MetricPort,
206 CppPortKind(service_type="esi::services::TelemetryService::Metric",
207 find_style="deref",
208 indexed_find="as_ptr",
209 param_type="esi::services::TelemetryService::Metric &",
210 param_suffix="_svc",
211 member_template=None,
212 member_ref="esi::services::TelemetryService::Metric &",
213 indexed_elem="esi::services::TelemetryService::Metric *",
214 alias_kind=None,
215 connectable=True)),
216]
217
218# Fallback for any service port that doesn't match a standard specialization
219# (e.g. a custom `@esi.ServiceDecl`-defined service).
220CPP_BUNDLE_KIND = CppPortKind(service_type=None,
221 find_style="bundle",
222 indexed_find="bundle_addr",
223 param_type="esi::BundlePort &",
224 param_suffix="_port",
225 member_template=None,
226 member_ref="esi::BundlePort &",
227 indexed_elem="esi::BundlePort *",
228 alias_kind=None,
229 connectable=False)
230
231
232def cpp_port_kind(port: _BundlePort) -> CppPortKind:
233 """Return the `CppPortKind` describing `port`, or `CPP_BUNDLE_KIND` as fallback."""
234 for cls, kind in CPP_PORT_KINDS:
235 if isinstance(port, cls):
236 return kind
237 return CPP_BUNDLE_KIND
str indexed_elem_type(self, Optional[str] alias_prefix=None)
Definition ports.py:81
str indexed_find_code(self, str map_var, str appid_expr)
Definition ports.py:118
str scalar_find_code(self, str v, str appid_expr)
Definition ports.py:93
str member_type(self, Optional[str] alias_prefix=None)
Definition ports.py:61