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