OR-1 dataflow CPU sketch
1"""Opcode mnemonic mapping and arity classification for OR1 assembly.
2
3This module provides:
4- MNEMONIC_TO_OP: Maps assembly mnemonic strings to ALUOp or MemOp enum values
5- OP_TO_MNEMONIC: Reverse mapping for serialization (handles IntEnum value collisions)
6- MONADIC_OPS: Set of opcodes that are always monadic
7- is_monadic() and is_dyadic(): Functions to check operand arity
8"""
9
10from typing import Optional, Union
11from cm_inst import ArithOp, LogicOp, MemOp, RoutingOp, is_monadic_alu
12
13
14# Build mnemonic to opcode mapping
15MNEMONIC_TO_OP: dict[str, Union[ArithOp, LogicOp, RoutingOp, MemOp]] = {
16 # Arithmetic operations
17 "add": ArithOp.ADD,
18 "sub": ArithOp.SUB,
19 "inc": ArithOp.INC,
20 "dec": ArithOp.DEC,
21 "shl": ArithOp.SHL,
22 "shr": ArithOp.SHR,
23 "asr": ArithOp.ASR,
24 # Logic operations
25 "and": LogicOp.AND,
26 "or": LogicOp.OR,
27 "xor": LogicOp.XOR,
28 "not": LogicOp.NOT,
29 "eq": LogicOp.EQ,
30 "lt": LogicOp.LT,
31 "lte": LogicOp.LTE,
32 "gt": LogicOp.GT,
33 "gte": LogicOp.GTE,
34 # Routing/branch operations
35 "breq": RoutingOp.BREQ,
36 "brgt": RoutingOp.BRGT,
37 "brge": RoutingOp.BRGE,
38 "brof": RoutingOp.BROF,
39 "sweq": RoutingOp.SWEQ,
40 "swgt": RoutingOp.SWGT,
41 "swge": RoutingOp.SWGE,
42 "swof": RoutingOp.SWOF,
43 "gate": RoutingOp.GATE,
44 "sel": RoutingOp.SEL,
45 "merge": RoutingOp.MRGE,
46 "pass": RoutingOp.PASS,
47 "const": RoutingOp.CONST,
48 "free_frame": RoutingOp.FREE_FRAME, # ALU free (deallocate frame slot)
49 "extract_tag": RoutingOp.EXTRACT_TAG,
50 "alloc_remote": RoutingOp.ALLOC_REMOTE,
51 # Memory operations
52 "read": MemOp.READ,
53 "write": MemOp.WRITE,
54 "clear": MemOp.CLEAR,
55 "alloc": MemOp.ALLOC,
56 "free": MemOp.FREE, # SM free
57 "rd_inc": MemOp.RD_INC,
58 "rd_dec": MemOp.RD_DEC,
59 "cmp_sw": MemOp.CMP_SW,
60 "exec": MemOp.EXEC,
61 "raw_read": MemOp.RAW_READ,
62 "set_page": MemOp.SET_PAGE,
63 "write_imm": MemOp.WRITE_IMM,
64 "ext": MemOp.EXT,
65}
66
67
68# Build reverse mapping with type information to avoid IntEnum collisions
69_reverse_mapping: dict[tuple[type, int], str] = {}
70for mnemonic, op in MNEMONIC_TO_OP.items():
71 _reverse_mapping[(type(op), int(op))] = mnemonic
72
73
74class TypeAwareOpToMnemonicDict:
75 """Collision-free reverse mapping from opcodes to mnemonics.
76
77 Handles IntEnum cross-type equality by using (type, value) tuples internally.
78 Supports dict-like access: OP_TO_MNEMONIC[ArithOp.ADD] returns "add",
79 OP_TO_MNEMONIC[MemOp.READ] returns "read", etc.
80 """
81
82 def __init__(self, mapping: dict[tuple[type, int], str]):
83 """Initialize with a type-indexed mapping.
84
85 Args:
86 mapping: dict from (type, value) tuples to mnemonic strings
87 """
88 self._mapping = mapping
89
90 def __getitem__(self, op: Union[ArithOp, LogicOp, RoutingOp, MemOp]) -> str:
91 """Get the mnemonic for an opcode.
92
93 Args:
94 op: The opcode enum value
95
96 Returns:
97 The mnemonic string
98
99 Raises:
100 KeyError: If the opcode is not in the mapping
101 """
102 key = (type(op), int(op))
103 if key not in self._mapping:
104 raise KeyError(f"Opcode {op} ({type(op).__name__}) not found in mapping")
105 return self._mapping[key]
106
107 def __contains__(self, op: Union[ArithOp, LogicOp, RoutingOp, MemOp]) -> bool:
108 """Check if an opcode is in the mapping."""
109 return (type(op), int(op)) in self._mapping
110
111 def __iter__(self):
112 """Iterate over mnemonic strings."""
113 return iter(self._mapping.values())
114
115 def __len__(self) -> int:
116 """Return the number of opcode-mnemonic pairs."""
117 return len(self._mapping)
118
119 def items(self):
120 """Return an iterator of (opcode_type, mnemonic) pairs for testing.
121
122 This reconstructs the original enum instances from the stored types/values.
123 """
124 result = []
125 for (op_type, op_val), mnemonic in self._mapping.items():
126 result.append((op_type(op_val), mnemonic))
127 return result
128
129
130OP_TO_MNEMONIC: TypeAwareOpToMnemonicDict = TypeAwareOpToMnemonicDict(_reverse_mapping)
131
132
133# Set of opcodes that are always monadic (single input operand).
134# We use a frozenset of (type, value) tuples to avoid IntEnum collisions.
135_MONADIC_OPS_TUPLES: frozenset[tuple[type, int]] = frozenset([
136 # ALU ops: duplicated from cm_inst.is_monadic_alu() for MONADIC_OPS membership testing.
137 # is_monadic() short-circuits to is_monadic_alu() for ALU ops, so these entries
138 # are only reached via `op in MONADIC_OPS` (TypeAwareMonadicOpsSet).
139 (ArithOp, int(ArithOp.INC)),
140 (ArithOp, int(ArithOp.DEC)),
141 (ArithOp, int(ArithOp.SHL)),
142 (ArithOp, int(ArithOp.SHR)),
143 (ArithOp, int(ArithOp.ASR)),
144 # Logic: single input
145 (LogicOp, int(LogicOp.NOT)),
146 # Routing: single input or no ALU involvement
147 (RoutingOp, int(RoutingOp.PASS)),
148 (RoutingOp, int(RoutingOp.CONST)),
149 (RoutingOp, int(RoutingOp.FREE_FRAME)),
150 (RoutingOp, int(RoutingOp.EXTRACT_TAG)),
151 (RoutingOp, int(RoutingOp.ALLOC_REMOTE)),
152 # Memory: single input (monadic SM operations)
153 (MemOp, int(MemOp.READ)),
154 (MemOp, int(MemOp.ALLOC)),
155 (MemOp, int(MemOp.FREE)),
156 (MemOp, int(MemOp.CLEAR)),
157 (MemOp, int(MemOp.RD_INC)),
158 (MemOp, int(MemOp.RD_DEC)),
159 (MemOp, int(MemOp.EXEC)),
160 (MemOp, int(MemOp.RAW_READ)),
161 (MemOp, int(MemOp.SET_PAGE)),
162 (MemOp, int(MemOp.WRITE_IMM)),
163 (MemOp, int(MemOp.EXT)),
164])
165
166
167class TypeAwareMonadicOpsSet:
168 """Collision-free set of monadic opcodes.
169
170 Handles IntEnum cross-type equality by using (type, value) tuples internally.
171 Supports membership testing: ArithOp.INC in MONADIC_OPS returns True,
172 but ArithOp.ADD in MONADIC_OPS returns False (collision-free).
173 """
174
175 def __init__(self, tuples: frozenset[tuple[type, int]]):
176 """Initialize with type-indexed tuples.
177
178 Args:
179 tuples: frozenset of (type, value) tuples
180 """
181 self._tuples = tuples
182
183 def __contains__(self, op: Union[ArithOp, LogicOp, RoutingOp, MemOp]) -> bool:
184 """Check if an opcode is in the set, handling IntEnum collisions.
185
186 Args:
187 op: The opcode enum value
188
189 Returns:
190 True if the opcode is monadic, False otherwise
191 """
192 return (type(op), int(op)) in self._tuples
193
194 def __iter__(self):
195 """Iterate over opcode instances in the set."""
196 for op_type, op_val in self._tuples:
197 yield op_type(op_val)
198
199 def __len__(self) -> int:
200 """Return the number of monadic opcodes."""
201 return len(self._tuples)
202
203 def __repr__(self) -> str:
204 """Return a string representation of the set."""
205 ops = list(self)
206 return f"TypeAwareMonadicOpsSet({ops})"
207
208
209MONADIC_OPS: TypeAwareMonadicOpsSet = TypeAwareMonadicOpsSet(_MONADIC_OPS_TUPLES)
210
211
212def is_monadic(op: Union[ArithOp, LogicOp, RoutingOp, MemOp], const: Optional[int] = None) -> bool:
213 """Check if an opcode is monadic (single input operand).
214
215 Args:
216 op: The ALUOp or MemOp enum value
217 const: Optional const value. Used to determine monadic form of WRITE.
218 If const is not None, WRITE is monadic (cell_addr from const).
219 If const is None, WRITE is dyadic (cell_addr from left operand).
220
221 Returns:
222 True if the opcode is always monadic, or if it's WRITE with const set.
223 False for CMP_SW (always dyadic) and WRITE with const=None (dyadic).
224 """
225 # Use canonical is_monadic_alu for ALU operations
226 if isinstance(op, (ArithOp, LogicOp, RoutingOp)):
227 return is_monadic_alu(op)
228
229 # Handle MemOp operations
230 op_tuple = (type(op), int(op))
231 if op_tuple in _MONADIC_OPS_TUPLES:
232 return True
233
234 # Special case: WRITE can be monadic (const given) or dyadic (const not given)
235 if type(op) is MemOp and op == MemOp.WRITE:
236 return const is not None
237
238 return False
239
240
241def is_dyadic(op: Union[ArithOp, LogicOp, RoutingOp, MemOp], const: Optional[int] = None) -> bool:
242 """Check if an opcode is dyadic (two input operands).
243
244 Args:
245 op: The ALUOp or MemOp enum value
246 const: Optional const value. Used for context-dependent operations like WRITE.
247
248 Returns:
249 True if the opcode is dyadic, False otherwise.
250 """
251 return not is_monadic(op, const)