OR-1 dataflow CPU sketch
at main 206 lines 6.8 kB view raw
1"""Serialize IRGraph back to dfasm source text. 2 3This module provides the serialize() function that converts an IRGraph (at any 4pipeline stage) back to valid dfasm source, enabling inspection of IR after 5lowering, resolution, placement, or allocation. 6 7The serializer emits: 8- inst_def lines for nodes: {qualified_ref}|pe{N} <| {mnemonic}[, {const}] 9- plain_edge lines for edges: {source} |> {dest}:{port} 10- data_def lines: {name}|sm{id}:{cell} = {value} 11- FUNCTION regions: $name |> { ...body... } 12- LOCATION regions: bare directive tag followed by body 13 14Names inside FUNCTION regions are unqualified (prefix stripped). 15Anonymous nodes (__anon_*) are always emitted as inst_def + plain_edge, 16never as inline syntax. 17""" 18 19from asm.ir import ( 20 IRGraph, IRNode, IREdge, IRRegion, RegionKind, IRDataDef 21) 22from asm.opcodes import OP_TO_MNEMONIC 23from cm_inst import Port 24 25 26def serialize(graph: IRGraph) -> str: 27 """Serialize an IRGraph to dfasm source text. 28 29 Converts an IRGraph back to valid dfasm source at any pipeline stage. 30 Useful for inspecting IR after lowering, resolution, placement, or allocation. 31 32 Args: 33 graph: The IRGraph to serialize 34 35 Returns: 36 A string containing valid dfasm source text 37 """ 38 if not graph.nodes and not graph.regions and not graph.data_defs and not graph.edges: 39 return "" 40 41 lines = [] 42 43 # Collect all nodes that are inside regions for later exclusion from top-level output 44 nodes_in_regions: set[str] = set() 45 edges_in_regions: set[tuple[str, str, Port]] = set() # Track edges by (source, dest, port) tuple 46 data_defs_in_regions: set[str] = set() 47 48 # First pass: collect what's inside regions 49 for region in graph.regions: 50 for node_name in region.body.nodes.keys(): 51 nodes_in_regions.add(node_name) 52 for edge in region.body.edges: 53 edges_in_regions.add((edge.source, edge.dest, edge.port)) 54 for data_def in region.body.data_defs: 55 data_defs_in_regions.add(data_def.name) 56 57 # Emit regions in order 58 for region in graph.regions: 59 lines.append(_serialize_region(region, graph)) 60 61 # Emit top-level nodes (not inside any region) 62 for name, node in graph.nodes.items(): 63 if name not in nodes_in_regions: 64 lines.append(_serialize_node(name, node, func_scope=None)) 65 66 # Emit top-level edges (not inside any region) 67 for edge in graph.edges: 68 edge_key = (edge.source, edge.dest, edge.port) 69 if edge_key not in edges_in_regions: 70 lines.append(_serialize_edge(edge)) 71 72 # Emit top-level data_defs (not inside any region) 73 for data_def in graph.data_defs: 74 if data_def.name not in data_defs_in_regions: 75 lines.append(_serialize_data_def(data_def)) 76 77 # Filter out empty lines and join 78 output = '\n'.join(line for line in lines if line.strip()) 79 return output + '\n' if output else "" 80 81 82def _serialize_region(region: IRRegion, parent_graph: IRGraph) -> str: 83 """Serialize a single region (FUNCTION or LOCATION). 84 85 Args: 86 region: The IRRegion to serialize 87 parent_graph: Parent IRGraph (for context) 88 89 Returns: 90 String containing the serialized region 91 """ 92 lines = [] 93 94 if region.kind == RegionKind.FUNCTION: 95 # FUNCTION regions: $name |> { ...body... } 96 lines.append(f"{region.tag} |> {{") 97 98 # Serialize body with function scope for name unqualification 99 func_scope = region.tag 100 for name, node in region.body.nodes.items(): 101 lines.append(_serialize_node(name, node, func_scope=func_scope)) 102 103 # Edges inside function 104 for edge in region.body.edges: 105 lines.append(_serialize_edge(edge, func_scope=func_scope)) 106 107 # Data defs inside function 108 for data_def in region.body.data_defs: 109 lines.append(_serialize_data_def(data_def)) 110 111 lines.append("}") 112 113 elif region.kind == RegionKind.LOCATION: 114 # LOCATION regions: bare directive tag with trailing colon, then body 115 lines.append(f"{region.tag}:") 116 117 # Serialize body (no function scope for locations) 118 for name, node in region.body.nodes.items(): 119 lines.append(_serialize_node(name, node, func_scope=None)) 120 121 for edge in region.body.edges: 122 lines.append(_serialize_edge(edge)) 123 124 for data_def in region.body.data_defs: 125 lines.append(_serialize_data_def(data_def)) 126 127 return '\n'.join(lines) 128 129 130def _serialize_node(name: str, node: IRNode, func_scope: str | None) -> str: 131 """Serialize a single IR node as an inst_def line. 132 133 Args: 134 name: The node name 135 node: The IRNode 136 func_scope: If provided, unqualify the name by stripping this prefix 137 138 Returns: 139 String containing the inst_def line 140 """ 141 # Unqualify the name if inside a function 142 if func_scope and name.startswith(f"{func_scope}."): 143 display_name = name[len(func_scope) + 1:] 144 else: 145 display_name = name 146 147 # Get mnemonic 148 try: 149 mnemonic = OP_TO_MNEMONIC[node.opcode] 150 except (KeyError, TypeError): 151 # Fallback if mnemonic not found 152 mnemonic = str(node.opcode).lower() 153 154 # Build inst_def line: {ref}|pe{N} <| {mnemonic}[, {const}] 155 pe_part = f"|pe{node.pe}" if node.pe is not None else "" 156 line = f"{display_name}{pe_part} <| {mnemonic}" 157 158 # Add const if present 159 if node.const is not None: 160 line += f", {node.const}" 161 162 return line 163 164 165def _serialize_edge(edge: IREdge, func_scope: str | None = None) -> str: 166 """Serialize a single edge as a plain_edge line. 167 168 Args: 169 edge: The IREdge 170 func_scope: If provided, unqualify names by stripping this prefix 171 172 Returns: 173 String containing the plain_edge line 174 """ 175 # Unqualify names if inside a function scope 176 source = edge.source 177 dest = edge.dest 178 179 if func_scope: 180 if source.startswith(f"{func_scope}."): 181 source = source[len(func_scope) + 1:] 182 if dest.startswith(f"{func_scope}."): 183 dest = dest[len(func_scope) + 1:] 184 185 # Format port as :L or :R 186 port_str = ":L" if edge.port == Port.L else ":R" 187 188 return f"{source} |> {dest}{port_str}" 189 190 191def _serialize_data_def(data_def: IRDataDef) -> str: 192 """Serialize a single data definition. 193 194 Args: 195 data_def: The IRDataDef 196 197 Returns: 198 String containing the data_def line 199 """ 200 sm_part = f"|sm{data_def.sm_id}" if data_def.sm_id is not None else "" 201 cell_part = f":{data_def.cell_addr}" if data_def.cell_addr is not None else "" 202 203 # Format value as hex if larger than a byte, decimal otherwise 204 value_str = f"0x{data_def.value:x}" if data_def.value > 255 else str(data_def.value) 205 206 return f"{data_def.name}{sm_part}{cell_part} = {value_str}"