OR-1 dataflow CPU sketch
at main 525 lines 17 kB view raw
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)