OR-1 dataflow CPU sketch
1"""Intermediate Representation (IR) types for OR1 assembly.
2
3Frozen dataclasses define the IR node types that represent a lowered assembly
4program as a graph with nodes, edges, regions, and data definitions. This module
5follows the patterns established in tokens.py and cm_inst.py.
6"""
7
8from __future__ import annotations
9
10from dataclasses import dataclass, field, replace
11from enum import Enum
12from typing import TYPE_CHECKING, Iterator, Optional, Union
13
14from cm_inst import ALUOp, FrameDest, MemOp, Port
15
16if TYPE_CHECKING:
17 from asm.errors import AssemblyError
18
19# Default configuration values for system parameters
20DEFAULT_IRAM_CAPACITY = 256
21DEFAULT_FRAME_COUNT = 8
22DEFAULT_FRAME_SLOTS = 64
23DEFAULT_MATCHABLE_OFFSETS = 8
24
25
26@dataclass(frozen=True)
27class SourceLoc:
28 """Source location for error reporting.
29
30 Extracted from Lark's meta object during parsing.
31 """
32 line: int
33 column: int
34 end_line: Optional[int] = None
35 end_column: Optional[int] = None
36
37
38@dataclass(frozen=True)
39class NameRef:
40 """Unresolved symbolic reference to a node or label.
41
42 Attributes:
43 name: The symbolic name (e.g., "&label" or "@node")
44 port: Optional port specification (L or R)
45 """
46 name: str
47 port: Optional[Port] = None
48
49
50@dataclass(frozen=True)
51class ResolvedDest:
52 """Fully resolved destination after name resolution.
53
54 Attributes:
55 name: The qualified name
56 addr: Deprecated (set to None in frame-based model)
57 frame_dest: The resolved FrameDest with target PE, offset, act_id, port, and token kind
58 """
59 name: str
60 addr: Optional[object] = None
61 frame_dest: Optional[FrameDest] = None
62
63
64@dataclass(frozen=True)
65class IRNode:
66 """One instruction in the IR graph.
67
68 Represents a single instruction that may be executed by a PE. Can be a dyadic
69 ALU operation, a monadic routing operation, or a memory (SM) operation.
70
71 Attributes:
72 name: Qualified name (e.g., "$main.&add" or "&top_level") or ParamRef for macro templates
73 opcode: ALUOp or MemOp enum value
74 dest_l: Left output destination (before name resolution)
75 dest_r: Right output destination (before name resolution)
76 const: Optional constant operand (int, ParamRef, or ConstExpr)
77 pe: Optional PE placement qualifier
78 iram_offset: Optional offset in PE's IRAM (populated during allocation)
79 act_slot: Optional activation slot (populated during allocation)
80 act_id: Optional activation ID (populated during allocation)
81 mode: Optional output mode tuple (OutputStyle, has_const, dest_count) — set by allocate
82 fref: Optional frame slot base index — set by allocate
83 wide: Wide operation flag
84 frame_layout: Optional frame slot map — set by allocate
85 loc: Source location for error reporting
86 args: Optional named arguments dictionary (e.g., {"dest": 0x45})
87 sm_id: Optional SM ID for MemOp instructions (populated during lowering)
88 """
89 name: Union[str, ParamRef]
90 opcode: Union[ALUOp, MemOp, ParamRef]
91 dest_l: Optional[Union[NameRef, ResolvedDest]] = None
92 dest_r: Optional[Union[NameRef, ResolvedDest]] = None
93 const: Optional[Union[int, ParamRef, ConstExpr]] = None
94 pe: Optional[Union[int, PlacementRef]] = None
95 act_slot: Optional[Union[int, ActSlotRef, ActSlotRange]] = None
96 iram_offset: Optional[int] = None
97 act_id: Optional[int] = None
98 mode: Optional[tuple] = None
99 fref: Optional[int] = None
100 wide: bool = False
101 frame_layout: Optional[FrameLayout] = None
102 loc: SourceLoc = SourceLoc(0, 0)
103 args: Optional[dict[str, int]] = None
104 sm_id: Optional[int] = None
105 seed: bool = False
106
107
108@dataclass(frozen=True)
109class IREdge:
110 """Connection between two IR nodes.
111
112 Attributes:
113 source: Name of the source node (str or ParamRef for macro templates)
114 dest: Name of the destination node (str or ParamRef for macro templates)
115 port: Destination input port (L or R)
116 source_port: Source output slot (L or R); None means allocator infers it
117 port_explicit: Whether the destination port was explicitly specified by the user
118 ctx_override: Whether this edge crosses context boundaries (function calls)
119 loc: Source location for error reporting
120 """
121 source: Union[str, ParamRef]
122 dest: Union[str, ParamRef]
123 port: Union[Port, PortRef]
124 source_port: Optional[Union[Port, PortRef]] = None
125 port_explicit: bool = False
126 ctx_override: bool = False
127 loc: SourceLoc = SourceLoc(0, 0)
128
129
130class RegionKind(Enum):
131 """Kind of IR region (nested scope)."""
132 FUNCTION = "function"
133 LOCATION = "location"
134 MACRO = "macro"
135
136
137@dataclass(frozen=True)
138class IRDataDef:
139 """Data definition (initialization in structure memory).
140
141 Attributes:
142 name: Name of the data (e.g., "@hello")
143 sm_id: Optional SM ID (populated from placement during lowering)
144 cell_addr: Optional cell address (populated from port during lowering)
145 value: 16-bit value to store (big-endian packed for multi-char data)
146 loc: Source location for error reporting
147 """
148 name: str
149 sm_id: Optional[int] = None
150 cell_addr: Optional[int] = None
151 value: int = 0
152 loc: SourceLoc = SourceLoc(0, 0)
153
154
155@dataclass(frozen=True)
156class MacroParam:
157 """Formal parameter in a macro definition.
158
159 Attributes:
160 name: Parameter name (without sigil)
161 variadic: Whether this is a variadic parameter (*name), which collects remaining args
162 """
163 name: str
164 variadic: bool = False
165
166
167@dataclass(frozen=True)
168class ParamRef:
169 """Placeholder for a macro parameter within a template IR.
170
171 Used in macro body templates to mark where actual arguments
172 should be substituted during expansion. Supports token pasting
173 via optional prefix/suffix strings.
174
175 Attributes:
176 param: Formal parameter name this references
177 prefix: Optional string prepended during token pasting
178 suffix: Optional string appended during token pasting
179 """
180 param: str
181 prefix: str = ""
182 suffix: str = ""
183
184
185@dataclass(frozen=True)
186class PlacementRef:
187 """Deferred placement from macro parameter."""
188 param: ParamRef
189
190
191@dataclass(frozen=True)
192class PortRef:
193 """Deferred port from macro parameter."""
194 param: ParamRef
195
196
197@dataclass(frozen=True)
198class ActSlotRef:
199 """Deferred activation slot from macro parameter."""
200 param: ParamRef
201
202
203@dataclass(frozen=True)
204class ActSlotRange:
205 """Explicit activation slot range reservation."""
206 start: int
207 end: int
208
209
210@dataclass(frozen=True)
211class FrameSlotMap:
212 """Slot map for a frame layout.
213
214 Attributes:
215 match_slots: Offsets of match operand slots
216 const_slots: Offsets of constant slots
217 dest_slots: Offsets of destination slots
218 sink_slots: Offsets of sink/SM parameter slots
219 """
220 match_slots: tuple[int, ...]
221 const_slots: tuple[int, ...]
222 dest_slots: tuple[int, ...]
223 sink_slots: tuple[int, ...]
224
225
226@dataclass(frozen=True)
227class FrameLayout:
228 """Frame slot layout for an activation.
229
230 Attributes:
231 slot_map: The frame slot map
232 total_slots: Total number of slots used
233 """
234 slot_map: FrameSlotMap
235 total_slots: int
236
237
238@dataclass(frozen=True)
239class IRRepetitionBlock:
240 """A repetition block within a macro body template.
241
242 The body is expanded once per variadic argument during macro
243 expansion. Each iteration binds the variadic param to the
244 current element and ${_idx} to the iteration index.
245
246 Attributes:
247 body: Template IRGraph for the repeating section
248 variadic_param: Name of the variadic parameter this iterates over
249 loc: Source location for error reporting
250 """
251 body: IRGraph
252 variadic_param: str
253 loc: SourceLoc = SourceLoc(0, 0)
254
255
256@dataclass(frozen=True)
257class ConstExpr:
258 """Arithmetic expression in macro body constant field.
259
260 Evaluated during expansion when parameter values are known.
261 Supports +, -, * on integer-valued parameters and literals.
262
263 Attributes:
264 expression: Expression source string, e.g. "base + 1"
265 params: Parameter names referenced in the expression
266 loc: Source location for error reporting
267 """
268 expression: str
269 params: tuple[str, ...]
270 loc: SourceLoc = SourceLoc(0, 0)
271
272
273@dataclass(frozen=True)
274class MacroDef:
275 """A macro definition: name, parameters, and body template.
276
277 The body IRGraph may contain ParamRef instances in node const
278 fields and edge source/dest fields. These are resolved during
279 macro expansion (Phase 2).
280
281 Attributes:
282 name: Macro name (without # sigil)
283 params: Ordered tuple of formal parameters
284 body: Template IRGraph with ParamRef placeholders
285 repetition_blocks: List of repetition blocks in the body (Phase 6)
286 loc: Source location for error reporting
287 """
288 name: str
289 params: tuple[MacroParam, ...]
290 body: IRGraph
291 repetition_blocks: list[IRRepetitionBlock] = field(default_factory=list)
292 loc: SourceLoc = SourceLoc(0, 0)
293
294
295@dataclass(frozen=True)
296class IRMacroCall:
297 """A macro invocation in the IR.
298
299 Stored in IRGraph.macro_calls. Processed and removed by the
300 expand pass (Phase 2).
301
302 Attributes:
303 name: Macro name being invoked (without # sigil)
304 positional_args: Positional argument values
305 named_args: Named argument key-value pairs
306 loc: Source location for error reporting
307 """
308 name: str
309 positional_args: tuple = ()
310 named_args: tuple[tuple[str, object], ...] = ()
311 output_dests: tuple = ()
312 loc: SourceLoc = SourceLoc(0, 0)
313
314
315@dataclass(frozen=True)
316class CallSiteResult:
317 """Intermediate call site data from lower pass, consumed by expand pass.
318
319 Attributes:
320 func_name: Name of the called function (e.g., "$fib")
321 input_args: Tuple of (param_name, source_ref) pairs
322 output_dests: Tuple of output destinations (positional or named)
323 loc: Source location for error reporting
324 """
325 func_name: str
326 input_args: tuple[tuple[str, str], ...] = ()
327 output_dests: tuple = ()
328 loc: SourceLoc = SourceLoc(0, 0)
329
330
331@dataclass(frozen=True)
332class CallSite:
333 """Metadata for a function call site.
334
335 Generated by the expand pass when processing call_stmt syntax.
336 Used by the allocator for per-call-site context slot assignment.
337
338 Attributes:
339 func_name: Name of the called function (e.g., "$fib")
340 call_id: Unique call site identifier (counter)
341 input_edges: Edge names for cross-context inputs
342 trampoline_nodes: Names of generated trampoline pass nodes
343 free_frame_nodes: Names of generated free_frame nodes
344 loc: Source location of the call
345 """
346 func_name: str
347 call_id: int
348 input_edges: tuple[str, ...] = ()
349 trampoline_nodes: tuple[str, ...] = ()
350 free_frame_nodes: tuple[str, ...] = ()
351 loc: SourceLoc = SourceLoc(0, 0)
352
353
354@dataclass(frozen=True)
355class SystemConfig:
356 """System configuration from @system pragma.
357
358 Attributes:
359 pe_count: Number of processing elements
360 sm_count: Number of structure memory instances
361 iram_capacity: IRAM size per PE (default 256)
362 frame_count: Number of frames per PE (default 8)
363 frame_slots: Total slots per frame (default 64)
364 matchable_offsets: Number of matchable IRAM offsets per frame (default 8)
365 loc: Source location for error reporting
366 """
367 pe_count: int
368 sm_count: int
369 iram_capacity: int = DEFAULT_IRAM_CAPACITY
370 frame_count: int = DEFAULT_FRAME_COUNT
371 frame_slots: int = DEFAULT_FRAME_SLOTS
372 matchable_offsets: int = DEFAULT_MATCHABLE_OFFSETS
373 loc: SourceLoc = SourceLoc(0, 0)
374
375
376@dataclass(frozen=True)
377class IRRegion:
378 """Nested scope (function or location region).
379
380 Attributes:
381 tag: Name of the region (e.g., "$main" or "@data_section")
382 kind: Type of region (FUNCTION or LOCATION)
383 body: IRGraph containing statements within this region
384 loc: Source location for error reporting
385 """
386 tag: str
387 kind: RegionKind
388 body: IRGraph
389 loc: SourceLoc = SourceLoc(0, 0)
390
391
392@dataclass(frozen=True)
393class IRGraph:
394 """Complete IR representation of an assembly program or region.
395
396 This is the primary data structure produced by the Lower pass. It contains
397 all nodes, edges, nested regions, and data definitions. Macro definitions
398 and invocations are stored separately for processing by the expand pass.
399
400 Note: IRGraph is frozen but holds mutable containers. This follows the
401 PEConfig pattern: each pass returns a new IRGraph, and containers are
402 never mutated after construction.
403
404 Attributes:
405 nodes: Dictionary of IRNodes keyed by qualified name
406 edges: List of IREdges connecting nodes
407 regions: List of IRRegions (nested scopes)
408 data_defs: List of IRDataDefs (memory initialization)
409 system: Optional SystemConfig from @system pragma
410 errors: List of AssemblyErrors encountered during lowering
411 macro_defs: List of MacroDefs (macro definitions before expansion)
412 macro_calls: List of IRMacroCalls (macro invocations to be expanded)
413 raw_call_sites: Tuple of CallSiteResults from lower pass
414 call_sites: List of CallSites (processed by expand pass)
415 builtin_line_offset: Number of lines in prepended built-in macros (for error reporting)
416 """
417 nodes: dict[str, IRNode] = field(default_factory=dict)
418 edges: list[IREdge] = field(default_factory=list)
419 regions: list[IRRegion] = field(default_factory=list)
420 data_defs: list[IRDataDef] = field(default_factory=list)
421 system: Optional[SystemConfig] = None
422 errors: list[AssemblyError] = field(default_factory=list)
423 macro_defs: list[MacroDef] = field(default_factory=list)
424 macro_calls: list[IRMacroCall] = field(default_factory=list)
425 raw_call_sites: tuple[CallSiteResult, ...] = ()
426 call_sites: list[CallSite] = field(default_factory=list)
427 builtin_line_offset: int = 0
428
429
430def iter_all_subgraphs(graph: IRGraph) -> Iterator[IRGraph]:
431 """Iterate over a graph and all nested region body graphs recursively.
432
433 Yields the graph itself first, then all graphs in nested regions in depth-first order.
434
435 Args:
436 graph: The root IRGraph
437
438 Yields:
439 IRGraph objects from the hierarchy
440 """
441 yield graph
442
443 def _walk_regions(regions: list[IRRegion]) -> Iterator[IRGraph]:
444 for region in regions:
445 yield region.body
446 yield from _walk_regions(region.body.regions)
447
448 yield from _walk_regions(graph.regions)
449
450
451def collect_all_nodes(graph: IRGraph) -> dict[str, IRNode]:
452 """Collect all nodes from graph and regions recursively.
453
454 Args:
455 graph: The IRGraph
456
457 Returns:
458 Dictionary mapping node names to IRNodes
459 """
460 all_nodes = {}
461 for subgraph in iter_all_subgraphs(graph):
462 all_nodes.update(subgraph.nodes)
463 return all_nodes
464
465
466def collect_all_nodes_and_edges(graph: IRGraph) -> tuple[dict[str, IRNode], list[IREdge]]:
467 """Collect all nodes and edges from graph and regions recursively.
468
469 Args:
470 graph: The IRGraph
471
472 Returns:
473 Tuple of (all_nodes dict, all_edges list)
474 """
475 all_nodes = {}
476 all_edges = []
477 for subgraph in iter_all_subgraphs(graph):
478 all_nodes.update(subgraph.nodes)
479 all_edges.extend(subgraph.edges)
480 return all_nodes, all_edges
481
482
483def collect_all_data_defs(graph: IRGraph) -> list[IRDataDef]:
484 """Collect all data_defs from graph and regions recursively.
485
486 Args:
487 graph: The IRGraph
488
489 Returns:
490 List of all IRDataDef objects
491 """
492 all_defs = []
493 for subgraph in iter_all_subgraphs(graph):
494 all_defs.extend(subgraph.data_defs)
495 return all_defs
496
497
498def update_graph_nodes(
499 graph: IRGraph, updated_nodes: dict[str, IRNode]
500) -> IRGraph:
501 """Recursively update nodes in graph and regions.
502
503 This function traverses the graph structure and replaces nodes with updated
504 versions from the provided dictionary. It preserves the tree structure of
505 regions and regions-within-regions.
506
507 Args:
508 graph: The IRGraph to update
509 updated_nodes: Dictionary mapping node names to updated IRNode instances
510
511 Returns:
512 New IRGraph with updated nodes
513 """
514 # Update top-level nodes
515 new_g_nodes = {}
516 for name, node in graph.nodes.items():
517 new_g_nodes[name] = updated_nodes.get(name, node)
518
519 # Update regions recursively
520 new_regions = []
521 for region in graph.regions:
522 new_body = update_graph_nodes(region.body, updated_nodes)
523 new_regions.append(replace(region, body=new_body))
524
525 return replace(graph, nodes=new_g_nodes, regions=new_regions)