| 
									
										
										
										
											2023-12-21 12:46:28 +00:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2024-05-29 10:47:56 +01:00
										 |  |  | from analyzer import StackItem, StackEffect, Instruction, Uop, PseudoInstruction | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  | from dataclasses import dataclass | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  | from cwriter import CWriter | 
					
						
							| 
									
										
										
										
											2025-03-13 10:59:51 +01:00
										 |  |  | from typing import Iterator | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-13 01:30:27 +08:00
										 |  |  | UNUSED = {"unused"} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-21 12:46:28 +00:00
										 |  |  | def maybe_parenthesize(sym: str) -> str: | 
					
						
							|  |  |  |     """Add parentheses around a string if it contains an operator
 | 
					
						
							|  |  |  |        and is not already parenthesized. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     An exception is made for '*' which is common and harmless | 
					
						
							|  |  |  |     in the context where the symbolic size is used. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if sym.startswith("(") and sym.endswith(")"): | 
					
						
							|  |  |  |         return sym | 
					
						
							|  |  |  |     if re.match(r"^[\s\w*]+$", sym): | 
					
						
							|  |  |  |         return sym | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return f"({sym})" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  | def var_size(var: StackItem) -> str: | 
					
						
							| 
									
										
										
										
											2025-03-20 15:39:38 +00:00
										 |  |  |     if var.size: | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  |         return var.size | 
					
						
							| 
									
										
										
										
											2024-07-09 11:33:56 +01:00
										 |  |  |     else: | 
					
						
							|  |  |  |         return "1" | 
					
						
							| 
									
										
										
										
											2024-01-13 01:30:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-20 14:27:25 +00:00
										 |  |  | @dataclass | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  | class PointerOffset: | 
					
						
							|  |  |  |     """The offset of a pointer from the reference pointer
 | 
					
						
							|  |  |  |         The 'reference pointer' is the address of the physical stack pointer | 
					
						
							|  |  |  |         at the start of the code section, as if each code section started with | 
					
						
							|  |  |  |         `const PyStackRef *reference = stack_pointer` | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     numeric: int | 
					
						
							|  |  |  |     positive: tuple[str, ...] | 
					
						
							|  |  |  |     negative: tuple[str, ...] | 
					
						
							| 
									
										
										
										
											2023-12-20 14:27:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def zero() -> "PointerOffset": | 
					
						
							|  |  |  |         return PointerOffset(0, (), ()) | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def pop(self, item: StackItem) -> "PointerOffset": | 
					
						
							|  |  |  |         return self - PointerOffset.from_item(item) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:12:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def push(self, item: StackItem) -> "PointerOffset": | 
					
						
							|  |  |  |         return self + PointerOffset.from_item(item) | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def from_item(item: StackItem) -> "PointerOffset": | 
					
						
							|  |  |  |         if not item.size: | 
					
						
							|  |  |  |             return PointerOffset(1, (), ()) | 
					
						
							|  |  |  |         txt = item.size.strip() | 
					
						
							|  |  |  |         n: tuple[str, ...] = () | 
					
						
							|  |  |  |         p: tuple[str, ...] = () | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             i = int(txt) | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             i = 0 | 
					
						
							|  |  |  |             if txt[0] == "+": | 
					
						
							|  |  |  |                 txt = txt[1:] | 
					
						
							|  |  |  |             if txt[0] == "-": | 
					
						
							|  |  |  |                 n = (txt[1:],) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 p = (txt,) | 
					
						
							|  |  |  |         return PointerOffset(i, p, n) | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def create(numeric: int, positive: tuple[str, ...], negative: tuple[str, ...]) -> "PointerOffset": | 
					
						
							|  |  |  |         positive, negative = PointerOffset._simplify(positive, negative) | 
					
						
							|  |  |  |         return PointerOffset(numeric, positive, negative) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __sub__(self, other: "PointerOffset") -> "PointerOffset": | 
					
						
							|  |  |  |         return PointerOffset.create( | 
					
						
							|  |  |  |             self.numeric - other.numeric, | 
					
						
							|  |  |  |             self.positive + other.negative, | 
					
						
							|  |  |  |             self.negative + other.positive | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-12-20 14:27:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def __add__(self, other: "PointerOffset") -> "PointerOffset": | 
					
						
							|  |  |  |         return PointerOffset.create( | 
					
						
							|  |  |  |             self.numeric + other.numeric, | 
					
						
							|  |  |  |             self.positive + other.positive, | 
					
						
							|  |  |  |             self.negative + other.negative | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-12-20 14:27:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def __neg__(self) -> "PointerOffset": | 
					
						
							|  |  |  |         return PointerOffset(-self.numeric, self.negative, self.positive) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _simplify(positive: tuple[str, ...], negative: tuple[str, ...]) -> tuple[tuple[str, ...], tuple[str, ...]]: | 
					
						
							|  |  |  |         p_orig: list[str] = sorted(positive) | 
					
						
							|  |  |  |         n_orig: list[str] = sorted(negative) | 
					
						
							|  |  |  |         p_uniq: list[str] = [] | 
					
						
							|  |  |  |         n_uniq: list[str] = [] | 
					
						
							|  |  |  |         while p_orig and n_orig: | 
					
						
							|  |  |  |             p_item = p_orig.pop() | 
					
						
							|  |  |  |             n_item = n_orig.pop() | 
					
						
							|  |  |  |             if p_item > n_item: | 
					
						
							|  |  |  |                 # if p_item > n_item, there can be no element in n matching p_item. | 
					
						
							|  |  |  |                 p_uniq.append(p_item) | 
					
						
							|  |  |  |                 n_orig.append(n_item) | 
					
						
							|  |  |  |             elif p_item < n_item: | 
					
						
							|  |  |  |                 n_uniq.append(n_item) | 
					
						
							|  |  |  |                 p_orig.append(p_item) | 
					
						
							|  |  |  |             # Otherwise they are the same and cancel each other out | 
					
						
							|  |  |  |         return tuple(p_orig + p_uniq), tuple(n_orig + n_uniq) | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def to_c(self) -> str: | 
					
						
							|  |  |  |         symbol_offset = "" | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         for item in self.negative: | 
					
						
							|  |  |  |             symbol_offset += f" - {maybe_parenthesize(item)}" | 
					
						
							|  |  |  |         for item in self.positive: | 
					
						
							|  |  |  |             symbol_offset += f" + {maybe_parenthesize(item)}" | 
					
						
							|  |  |  |         if symbol_offset and self.numeric == 0: | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  |             res = symbol_offset | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             res = f"{self.numeric}{symbol_offset}" | 
					
						
							| 
									
										
										
										
											2023-12-07 12:49:40 +00:00
										 |  |  |         if res.startswith(" + "): | 
					
						
							|  |  |  |             res = res[3:] | 
					
						
							|  |  |  |         if res.startswith(" - "): | 
					
						
							|  |  |  |             res = "-" + res[3:] | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |     def as_int(self) -> int | None: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         if self.positive or self.negative: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  |         return self.numeric | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |     def __bool__(self) -> bool: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         return self.numeric != 0 or bool(self.positive) or bool(self.negative) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def __str__(self) -> str: | 
					
						
							|  |  |  |         return self.to_c() | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def __repr__(self) -> str: | 
					
						
							|  |  |  |         return f"PointerOffset({self.to_c()})" | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  | @dataclass | 
					
						
							|  |  |  | class Local: | 
					
						
							|  |  |  |     item: StackItem | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     memory_offset: PointerOffset | None | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |     in_local: bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self) -> str: | 
					
						
							|  |  |  |         return f"Local('{self.item.name}', mem={self.memory_offset}, local={self.in_local}, array={self.is_array()})" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def compact_str(self) -> str: | 
					
						
							|  |  |  |         mtag = "M" if self.memory_offset else "" | 
					
						
							|  |  |  |         dtag = "D" if self.in_local else "" | 
					
						
							|  |  |  |         atag = "A" if self.is_array() else "" | 
					
						
							|  |  |  |         return f"'{self.item.name}'{mtag}{dtag}{atag}" | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def unused(defn: StackItem, offset: PointerOffset | None) -> "Local": | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         return Local(defn, offset, False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def undefined(defn: StackItem) -> "Local": | 
					
						
							|  |  |  |         return Local(defn, None, False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def from_memory(defn: StackItem, offset: PointerOffset) -> "Local": | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         return Local(defn, offset, True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def kill(self) -> None: | 
					
						
							|  |  |  |         self.in_local = False | 
					
						
							|  |  |  |         self.memory_offset = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def in_memory(self) -> bool: | 
					
						
							|  |  |  |         return self.memory_offset is not None or self.is_array() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_dead(self) -> bool: | 
					
						
							|  |  |  |         return not self.in_local and self.memory_offset is None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def copy(self) -> "Local": | 
					
						
							|  |  |  |         return Local( | 
					
						
							|  |  |  |             self.item, | 
					
						
							|  |  |  |             self.memory_offset, | 
					
						
							|  |  |  |             self.in_local | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def size(self) -> str: | 
					
						
							|  |  |  |         return self.item.size | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def name(self) -> str: | 
					
						
							|  |  |  |         return self.item.name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_array(self) -> bool: | 
					
						
							|  |  |  |         return self.item.is_array() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other: object) -> bool: | 
					
						
							|  |  |  |         if not isinstance(other, Local): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return ( | 
					
						
							|  |  |  |             self.item is other.item | 
					
						
							|  |  |  |             and self.memory_offset == other.memory_offset | 
					
						
							|  |  |  |             and self.in_local == other.in_local | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-18 12:49:24 +01:00
										 |  |  | class StackError(Exception): | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-12 15:32:45 +01:00
										 |  |  | def array_or_scalar(var: StackItem | Local) -> str: | 
					
						
							|  |  |  |     return "array" if var.is_array() else "scalar" | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Stack: | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |     def __init__(self, extract_bits: bool=True, cast_type: str = "uintptr_t") -> None: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         self.base_offset = PointerOffset.zero() | 
					
						
							|  |  |  |         self.physical_sp = PointerOffset.zero() | 
					
						
							|  |  |  |         self.logical_sp = PointerOffset.zero() | 
					
						
							| 
									
										
										
										
											2024-08-01 09:27:26 +01:00
										 |  |  |         self.variables: list[Local] = [] | 
					
						
							| 
									
										
										
										
											2025-01-17 16:59:30 +00:00
										 |  |  |         self.extract_bits = extract_bits | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         self.cast_type = cast_type | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |     def drop(self, var: StackItem, check_liveness: bool) -> None: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         self.logical_sp = self.logical_sp.pop(var) | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         if self.variables: | 
					
						
							|  |  |  |             popped = self.variables.pop() | 
					
						
							|  |  |  |             if popped.is_dead() or not var.used: | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |         if check_liveness: | 
					
						
							|  |  |  |             raise StackError(f"Dropping live value '{var.name}'") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def pop(self, var: StackItem, out: CWriter) -> Local: | 
					
						
							|  |  |  |         self.logical_sp = self.logical_sp.pop(var) | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  |         indirect = "&" if var.is_array() else "" | 
					
						
							|  |  |  |         if self.variables: | 
					
						
							|  |  |  |             popped = self.variables.pop() | 
					
						
							| 
									
										
										
										
											2024-09-12 15:32:45 +01:00
										 |  |  |             if var.is_array() ^ popped.is_array(): | 
					
						
							|  |  |  |                 raise StackError( | 
					
						
							|  |  |  |                     f"Array mismatch when popping '{popped.name}' from stack to assign to '{var.name}'. " | 
					
						
							|  |  |  |                     f"Expected {array_or_scalar(var)} got {array_or_scalar(popped)}" | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  |             if popped.size != var.size: | 
					
						
							| 
									
										
										
										
											2024-07-18 12:49:24 +01:00
										 |  |  |                 raise StackError( | 
					
						
							| 
									
										
										
										
											2024-09-12 15:32:45 +01:00
										 |  |  |                     f"Size mismatch when popping '{popped.name}' from stack to assign to '{var.name}'. " | 
					
						
							|  |  |  |                     f"Expected {var_size(var)} got {var_size(popped.item)}" | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2024-08-01 09:27:26 +01:00
										 |  |  |             if not var.used: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |                 return popped | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if popped.name != var.name: | 
					
						
							|  |  |  |                 rename = f"{var.name} = {popped.name};\n" | 
					
						
							|  |  |  |                 popped.item = var | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |                 rename = "" | 
					
						
							|  |  |  |             if not popped.in_local: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |                 if popped.memory_offset is None: | 
					
						
							|  |  |  |                     popped.memory_offset = self.logical_sp | 
					
						
							|  |  |  |                 assert popped.memory_offset == self.logical_sp, (popped, self.as_comment()) | 
					
						
							|  |  |  |                 offset = popped.memory_offset.to_c() | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  |                 if var.is_array(): | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |                     defn = f"{var.name} = &stack_pointer[{offset}];\n" | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |                     defn = f"{var.name} = stack_pointer[{offset}];\n" | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |                     popped.in_local = True | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 defn = rename | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             out.emit(defn) | 
					
						
							|  |  |  |             return popped | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         self.base_offset = self.logical_sp | 
					
						
							| 
									
										
										
										
											2024-07-18 12:49:24 +01:00
										 |  |  |         if var.name in UNUSED or not var.used: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             return Local.unused(var, self.base_offset) | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  |         cast = f"({var.type})" if (not indirect and var.type) else "" | 
					
						
							| 
									
										
										
										
											2025-01-17 16:59:30 +00:00
										 |  |  |         bits = ".bits" if cast and self.extract_bits else "" | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         offset = (self.base_offset - self.physical_sp).to_c() | 
					
						
							|  |  |  |         assign = f"{var.name} = {cast}{indirect}stack_pointer[{offset}]{bits};\n" | 
					
						
							|  |  |  |         out.emit(assign) | 
					
						
							|  |  |  |         return Local.from_memory(var, self.base_offset) | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  |     def push(self, var: Local) -> None: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         assert(var not in self.variables) | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  |         self.variables.append(var) | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         self.logical_sp = self.logical_sp.push(var.item) | 
					
						
							| 
									
										
										
										
											2024-08-01 09:27:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-07 13:23:53 -04:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  |     def _do_emit( | 
					
						
							|  |  |  |         out: CWriter, | 
					
						
							|  |  |  |         var: StackItem, | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         stack_offset: PointerOffset, | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         cast_type: str, | 
					
						
							|  |  |  |         extract_bits: bool, | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  |     ) -> None: | 
					
						
							| 
									
										
										
										
											2024-08-07 13:23:53 -04:00
										 |  |  |         cast = f"({cast_type})" if var.type else "" | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         bits = ".bits" if cast and extract_bits else "" | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         out.emit(f"stack_pointer[{stack_offset.to_c()}]{bits} = {cast}{var.name};\n") | 
					
						
							| 
									
										
										
										
											2024-08-07 13:23:53 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def _save_physical_sp(self, out: CWriter) -> None: | 
					
						
							|  |  |  |         if self.physical_sp != self.logical_sp: | 
					
						
							|  |  |  |             diff = self.logical_sp - self.physical_sp | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |             out.start_line() | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             out.emit(f"stack_pointer += {diff.to_c()};\n") | 
					
						
							| 
									
										
										
										
											2024-06-25 16:42:29 +01:00
										 |  |  |             out.emit("assert(WITHIN_STACK_BOUNDS());\n") | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             self.physical_sp = self.logical_sp | 
					
						
							| 
									
										
										
										
											2024-08-01 09:27:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |     def flush(self, out: CWriter) -> None: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         out.start_line() | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         var_offset = self.base_offset | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         for var in self.variables: | 
					
						
							|  |  |  |             if ( | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |                 var.in_local and | 
					
						
							|  |  |  |                 not var.memory_offset and | 
					
						
							|  |  |  |                 not var.is_array() | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |             ): | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |                 var.memory_offset = var_offset | 
					
						
							|  |  |  |                 stack_offset = var_offset - self.physical_sp | 
					
						
							|  |  |  |                 Stack._do_emit(out, var.item, stack_offset, self.cast_type, self.extract_bits) | 
					
						
							|  |  |  |             var_offset = var_offset.push(var.item) | 
					
						
							|  |  |  |         self._save_physical_sp(out) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         out.start_line() | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |     def is_flushed(self) -> bool: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         for var in self.variables: | 
					
						
							|  |  |  |             if not var.in_memory(): | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |         return self.physical_sp == self.logical_sp | 
					
						
							| 
									
										
										
										
											2024-08-07 13:23:53 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def sp_offset(self) -> str: | 
					
						
							|  |  |  |         return (self.physical_sp - self.logical_sp).to_c() | 
					
						
							| 
									
										
										
										
											2024-07-23 14:12:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 12:12:17 +00:00
										 |  |  |     def as_comment(self) -> str: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         variables = ", ".join([v.compact_str() for v in self.variables]) | 
					
						
							|  |  |  |         return ( | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             f"/* Variables: {variables}. base: {self.base_offset.to_c()}. sp: {self.physical_sp.to_c()}. logical_sp: {self.logical_sp.to_c()} */" | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def copy(self) -> "Stack": | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         other = Stack(self.extract_bits, self.cast_type) | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         other.base_offset = self.base_offset | 
					
						
							|  |  |  |         other.physical_sp = self.physical_sp | 
					
						
							|  |  |  |         other.logical_sp = self.logical_sp | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         other.variables = [var.copy() for var in self.variables] | 
					
						
							|  |  |  |         return other | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other: object) -> bool: | 
					
						
							|  |  |  |         if not isinstance(other, Stack): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return ( | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             self.physical_sp == other.physical_sp | 
					
						
							|  |  |  |             and self.logical_sp == other.logical_sp | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |             and self.base_offset == other.base_offset | 
					
						
							|  |  |  |             and self.variables == other.variables | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def align(self, other: "Stack", out: CWriter) -> None: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         if self.logical_sp != other.logical_sp: | 
					
						
							|  |  |  |             raise StackError("Cannot align stacks: differing logical top") | 
					
						
							|  |  |  |         if self.physical_sp == other.physical_sp: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         diff = other.physical_sp - self.physical_sp | 
					
						
							|  |  |  |         out.start_line() | 
					
						
							|  |  |  |         out.emit(f"stack_pointer += {diff.to_c()};\n") | 
					
						
							|  |  |  |         out.emit("assert(WITHIN_STACK_BOUNDS());\n") | 
					
						
							|  |  |  |         self.physical_sp = other.physical_sp | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def merge(self, other: "Stack", out: CWriter) -> None: | 
					
						
							|  |  |  |         if len(self.variables) != len(other.variables): | 
					
						
							|  |  |  |             raise StackError("Cannot merge stacks: differing variables") | 
					
						
							|  |  |  |         for self_var, other_var in zip(self.variables, other.variables): | 
					
						
							|  |  |  |             if self_var.name != other_var.name: | 
					
						
							|  |  |  |                 raise StackError(f"Mismatched variables on stack: {self_var.name} and {other_var.name}") | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             self_var.in_local = self_var.in_local and other_var.in_local | 
					
						
							|  |  |  |             if other_var.memory_offset is None: | 
					
						
							|  |  |  |                 self_var.memory_offset = None | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         self.align(other, out) | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         for self_var, other_var in zip(self.variables, other.variables): | 
					
						
							|  |  |  |             if self_var.memory_offset is not None: | 
					
						
							|  |  |  |                 if self_var.memory_offset != other_var.memory_offset: | 
					
						
							|  |  |  |                     raise StackError(f"Mismatched stack depths for {self_var.name}: {self_var.memory_offset} and {other_var.memory_offset}") | 
					
						
							| 
									
										
										
										
											2025-03-27 08:32:45 +00:00
										 |  |  |             elif other_var.memory_offset is None: | 
					
						
							|  |  |  |                 self_var.memory_offset = None | 
					
						
							| 
									
										
										
										
											2023-12-20 14:27:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-25 16:53:49 -08:00
										 |  |  | def stacks(inst: Instruction | PseudoInstruction) -> Iterator[StackEffect]: | 
					
						
							|  |  |  |     if isinstance(inst, Instruction): | 
					
						
							|  |  |  |         for uop in inst.parts: | 
					
						
							|  |  |  |             if isinstance(uop, Uop): | 
					
						
							|  |  |  |                 yield uop.stack | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         assert isinstance(inst, PseudoInstruction) | 
					
						
							|  |  |  |         yield inst.stack | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def apply_stack_effect(stack: Stack, effect: StackEffect) -> None: | 
					
						
							|  |  |  |     locals: dict[str, Local] = {} | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     null = CWriter.null() | 
					
						
							| 
									
										
										
										
											2024-11-25 16:53:49 -08:00
										 |  |  |     for var in reversed(effect.inputs): | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         local = stack.pop(var, null) | 
					
						
							| 
									
										
										
										
											2024-11-25 16:53:49 -08:00
										 |  |  |         if var.name != "unused": | 
					
						
							|  |  |  |             locals[local.name] = local | 
					
						
							|  |  |  |     for var in effect.outputs: | 
					
						
							|  |  |  |         if var.name in locals: | 
					
						
							|  |  |  |             local = locals[var.name] | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             local = Local.unused(var, None) | 
					
						
							| 
									
										
										
										
											2024-11-25 16:53:49 -08:00
										 |  |  |         stack.push(local) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-29 10:47:56 +01:00
										 |  |  | def get_stack_effect(inst: Instruction | PseudoInstruction) -> Stack: | 
					
						
							| 
									
										
										
										
											2023-12-20 14:27:25 +00:00
										 |  |  |     stack = Stack() | 
					
						
							| 
									
										
										
										
											2024-11-25 16:53:49 -08:00
										 |  |  |     for s in stacks(inst): | 
					
						
							|  |  |  |         apply_stack_effect(stack, s) | 
					
						
							|  |  |  |     return stack | 
					
						
							| 
									
										
										
										
											2024-08-08 10:57:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-29 10:47:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  | @dataclass | 
					
						
							|  |  |  | class Storage: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stack: Stack | 
					
						
							|  |  |  |     inputs: list[Local] | 
					
						
							|  |  |  |     outputs: list[Local] | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |     check_liveness: bool | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |     spilled: int = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def needs_defining(var: Local) -> bool: | 
					
						
							|  |  |  |         return ( | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             not var.in_local and | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |             not var.is_array() and | 
					
						
							|  |  |  |             var.name != "unused" | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def is_live(var: Local) -> bool: | 
					
						
							|  |  |  |         return ( | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             var.name != "unused" and | 
					
						
							|  |  |  |             ( | 
					
						
							|  |  |  |                 var.in_local or | 
					
						
							|  |  |  |                 var.memory_offset is not None | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def clear_inputs(self, reason:str) -> None: | 
					
						
							|  |  |  |         while self.inputs: | 
					
						
							|  |  |  |             tos = self.inputs.pop() | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if self.is_live(tos) and self.check_liveness: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |                 raise StackError( | 
					
						
							|  |  |  |                     f"Input '{tos.name}' is still live {reason}" | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             self.stack.drop(tos.item, self.check_liveness) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def clear_dead_inputs(self) -> None: | 
					
						
							|  |  |  |         live = "" | 
					
						
							|  |  |  |         while self.inputs: | 
					
						
							|  |  |  |             tos = self.inputs[-1] | 
					
						
							|  |  |  |             if self.is_live(tos): | 
					
						
							|  |  |  |                 live = tos.name | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             self.inputs.pop() | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             self.stack.drop(tos.item, self.check_liveness) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         for var in self.inputs: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if not self.is_live(var): | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |                 raise StackError( | 
					
						
							|  |  |  |                     f"Input '{var.name}' is not live, but '{live}' is" | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _push_defined_outputs(self) -> None: | 
					
						
							|  |  |  |         defined_output = "" | 
					
						
							|  |  |  |         for output in self.outputs: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if output.in_local and not output.memory_offset: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |                 defined_output = output.name | 
					
						
							|  |  |  |         if not defined_output: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.clear_inputs(f"when output '{defined_output}' is defined") | 
					
						
							|  |  |  |         undefined = "" | 
					
						
							|  |  |  |         for out in self.outputs: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if out.in_local: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |                 if undefined: | 
					
						
							|  |  |  |                     f"Locals not defined in stack order. " | 
					
						
							|  |  |  |                     f"Expected '{undefined}' to be defined before '{out.name}'" | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 undefined = out.name | 
					
						
							|  |  |  |         while self.outputs and not self.needs_defining(self.outputs[0]): | 
					
						
							|  |  |  |             out = self.outputs.pop(0) | 
					
						
							|  |  |  |             self.stack.push(out) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def locals_cached(self) -> bool: | 
					
						
							|  |  |  |         for out in self.outputs: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if out.in_local: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |                 return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |     def flush(self, out: CWriter) -> None: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         self.clear_dead_inputs() | 
					
						
							|  |  |  |         self._push_defined_outputs() | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         self.stack.flush(out) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def save(self, out: CWriter) -> None: | 
					
						
							|  |  |  |         assert self.spilled >= 0 | 
					
						
							|  |  |  |         if self.spilled == 0: | 
					
						
							|  |  |  |             out.start_line() | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |             out.emit_spill() | 
					
						
							|  |  |  |         self.spilled += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def save_inputs(self, out: CWriter) -> None: | 
					
						
							|  |  |  |         assert self.spilled >= 0 | 
					
						
							|  |  |  |         if self.spilled == 0: | 
					
						
							|  |  |  |             self.clear_dead_inputs() | 
					
						
							|  |  |  |             self.stack.flush(out) | 
					
						
							|  |  |  |             out.start_line() | 
					
						
							|  |  |  |             out.emit_spill() | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         self.spilled += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def reload(self, out: CWriter) -> None: | 
					
						
							|  |  |  |         if self.spilled == 0: | 
					
						
							|  |  |  |             raise StackError("Cannot reload stack as it hasn't been saved") | 
					
						
							|  |  |  |         assert self.spilled > 0 | 
					
						
							|  |  |  |         self.spilled -= 1 | 
					
						
							|  |  |  |         if self.spilled == 0: | 
					
						
							|  |  |  |             out.start_line() | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |             out.emit_reload() | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |     def for_uop(stack: Stack, uop: Uop, out: CWriter, check_liveness: bool = True) -> "Storage": | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         inputs: list[Local] = [] | 
					
						
							|  |  |  |         peeks: list[Local] = [] | 
					
						
							|  |  |  |         for input in reversed(uop.stack.inputs): | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             local = stack.pop(input, out) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |             if input.peek: | 
					
						
							|  |  |  |                 peeks.append(local) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 inputs.append(local) | 
					
						
							|  |  |  |         inputs.reverse() | 
					
						
							|  |  |  |         peeks.reverse() | 
					
						
							|  |  |  |         for peek in peeks: | 
					
						
							|  |  |  |             stack.push(peek) | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         offset = stack.logical_sp - stack.physical_sp | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         for ouput in uop.stack.outputs: | 
					
						
							|  |  |  |             if ouput.is_array() and ouput.used and not ouput.peek: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |                 c_offset = offset.to_c() | 
					
						
							|  |  |  |                 out.emit(f"{ouput.name} = &stack_pointer[{c_offset}];\n") | 
					
						
							|  |  |  |             offset = offset.push(ouput) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         for var in inputs: | 
					
						
							|  |  |  |             stack.push(var) | 
					
						
							|  |  |  |         outputs = [ Local.undefined(var) for var in uop.stack.outputs if not var.peek ] | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |         return Storage(stack, inputs, outputs, check_liveness) | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def copy_list(arg: list[Local]) -> list[Local]: | 
					
						
							|  |  |  |         return [ l.copy() for l in arg ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def copy(self) -> "Storage": | 
					
						
							|  |  |  |         new_stack = self.stack.copy() | 
					
						
							|  |  |  |         variables = { var.name: var for var in new_stack.variables } | 
					
						
							|  |  |  |         inputs = [ variables[var.name] for var in self.inputs] | 
					
						
							|  |  |  |         assert [v.name for v in inputs] == [v.name for v in self.inputs], (inputs, self.inputs) | 
					
						
							|  |  |  |         return Storage( | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             new_stack, inputs, self.copy_list(self.outputs), | 
					
						
							|  |  |  |             self.check_liveness, self.spilled | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def sanity_check(self) -> None: | 
					
						
							|  |  |  |         names: set[str] = set() | 
					
						
							|  |  |  |         for var in self.inputs: | 
					
						
							|  |  |  |             if var.name in names: | 
					
						
							|  |  |  |                 raise StackError(f"Duplicate name {var.name}") | 
					
						
							|  |  |  |             names.add(var.name) | 
					
						
							|  |  |  |         names = set() | 
					
						
							|  |  |  |         for var in self.outputs: | 
					
						
							|  |  |  |             if var.name in names: | 
					
						
							|  |  |  |                 raise StackError(f"Duplicate name {var.name}") | 
					
						
							|  |  |  |             names.add(var.name) | 
					
						
							|  |  |  |         names = set() | 
					
						
							|  |  |  |         for var in self.stack.variables: | 
					
						
							|  |  |  |             if var.name in names: | 
					
						
							|  |  |  |                 raise StackError(f"Duplicate name {var.name}") | 
					
						
							|  |  |  |             names.add(var.name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_flushed(self) -> bool: | 
					
						
							|  |  |  |         for var in self.outputs: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if var.in_local and not var.memory_offset: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |                 return False | 
					
						
							|  |  |  |         return self.stack.is_flushed() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def merge(self, other: "Storage", out: CWriter) -> None: | 
					
						
							|  |  |  |         self.sanity_check() | 
					
						
							|  |  |  |         if len(self.inputs) != len(other.inputs): | 
					
						
							|  |  |  |             self.clear_dead_inputs() | 
					
						
							|  |  |  |             other.clear_dead_inputs() | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         if len(self.inputs) != len(other.inputs) and self.check_liveness: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |             diff = self.inputs[-1] if len(self.inputs) > len(other.inputs) else other.inputs[-1] | 
					
						
							|  |  |  |             raise StackError(f"Unmergeable inputs. Differing state of '{diff.name}'") | 
					
						
							|  |  |  |         for var, other_var in zip(self.inputs, other.inputs): | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if var.in_local != other_var.in_local: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |                 raise StackError(f"'{var.name}' is cleared on some paths, but not all") | 
					
						
							|  |  |  |         if len(self.outputs) != len(other.outputs): | 
					
						
							|  |  |  |             self._push_defined_outputs() | 
					
						
							|  |  |  |             other._push_defined_outputs() | 
					
						
							|  |  |  |         if len(self.outputs) != len(other.outputs): | 
					
						
							|  |  |  |             var = self.outputs[0] if len(self.outputs) > len(other.outputs) else other.outputs[0] | 
					
						
							|  |  |  |             raise StackError(f"'{var.name}' is set on some paths, but not all") | 
					
						
							| 
									
										
										
										
											2025-03-27 08:32:45 +00:00
										 |  |  |         for var, other_var in zip(self.outputs, other.outputs): | 
					
						
							|  |  |  |             if var.memory_offset is None: | 
					
						
							|  |  |  |                 other_var.memory_offset = None | 
					
						
							|  |  |  |             elif other_var.memory_offset is None: | 
					
						
							|  |  |  |                 var.memory_offset = None | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |         self.stack.merge(other.stack, out) | 
					
						
							|  |  |  |         self.sanity_check() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def push_outputs(self) -> None: | 
					
						
							|  |  |  |         if self.spilled: | 
					
						
							|  |  |  |             raise StackError(f"Unbalanced stack spills") | 
					
						
							|  |  |  |         self.clear_inputs("at the end of the micro-op") | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         if self.inputs and self.check_liveness: | 
					
						
							| 
									
										
										
										
											2024-10-07 14:56:39 +01:00
										 |  |  |             raise StackError(f"Input variable '{self.inputs[-1].name}' is still live") | 
					
						
							|  |  |  |         self._push_defined_outputs() | 
					
						
							|  |  |  |         if self.outputs: | 
					
						
							|  |  |  |             for out in self.outputs: | 
					
						
							|  |  |  |                 if self.needs_defining(out): | 
					
						
							|  |  |  |                     raise StackError(f"Output variable '{self.outputs[0].name}' is not defined") | 
					
						
							|  |  |  |                 self.stack.push(out) | 
					
						
							|  |  |  |             self.outputs = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def as_comment(self) -> str: | 
					
						
							|  |  |  |         stack_comment = self.stack.as_comment() | 
					
						
							|  |  |  |         next_line = "\n               " | 
					
						
							|  |  |  |         inputs = ", ".join([var.compact_str() for var in self.inputs]) | 
					
						
							|  |  |  |         outputs = ", ".join([var.compact_str() for var in self.outputs]) | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         return f"{stack_comment[:-2]}{next_line}inputs: {inputs}{next_line}outputs: {outputs}*/" | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def close_inputs(self, out: CWriter) -> None: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         tmp_defined = False | 
					
						
							|  |  |  |         def close_named(close: str, name: str, overwrite: str) -> None: | 
					
						
							|  |  |  |             nonlocal tmp_defined | 
					
						
							|  |  |  |             if overwrite: | 
					
						
							|  |  |  |                 if not tmp_defined: | 
					
						
							|  |  |  |                     out.emit("_PyStackRef ") | 
					
						
							|  |  |  |                     tmp_defined = True | 
					
						
							|  |  |  |                 out.emit(f"tmp = {name};\n") | 
					
						
							|  |  |  |                 out.emit(f"{name} = {overwrite};\n") | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |                 self.stack.flush(out) | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |                 out.emit(f"{close}(tmp);\n") | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 out.emit(f"{close}({name});\n") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def close_variable(var: Local, overwrite: str) -> None: | 
					
						
							|  |  |  |             nonlocal tmp_defined | 
					
						
							|  |  |  |             close = "PyStackRef_CLOSE" | 
					
						
							| 
									
										
										
										
											2025-03-20 15:39:38 +00:00
										 |  |  |             if "null" in var.name: | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |                 close = "PyStackRef_XCLOSE" | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             var.memory_offset = None | 
					
						
							|  |  |  |             self.save(out) | 
					
						
							|  |  |  |             out.start_line() | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |             if var.size: | 
					
						
							|  |  |  |                 if var.size == "1": | 
					
						
							|  |  |  |                     close_named(close, f"{var.name}[0]", overwrite) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     if overwrite and not tmp_defined: | 
					
						
							|  |  |  |                         out.emit("_PyStackRef tmp;\n") | 
					
						
							|  |  |  |                         tmp_defined = True | 
					
						
							|  |  |  |                     out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n") | 
					
						
							|  |  |  |                     close_named(close, f"{var.name}[_i]", overwrite) | 
					
						
							|  |  |  |                     out.emit("}\n") | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 close_named(close, var.name, overwrite) | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             self.reload(out) | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self.clear_dead_inputs() | 
					
						
							|  |  |  |         if not self.inputs: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         lowest = self.inputs[0] | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         output: Local | None = None | 
					
						
							|  |  |  |         for var in self.outputs: | 
					
						
							|  |  |  |             if var.is_array(): | 
					
						
							|  |  |  |                 if len(self.inputs) > 1: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |                     raise StackError("Cannot call DECREF_INPUTS with array output and more than one input") | 
					
						
							|  |  |  |                 output = var | 
					
						
							|  |  |  |             elif var.in_local: | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |                 if output is not None: | 
					
						
							|  |  |  |                     raise StackError("Cannot call DECREF_INPUTS with more than one live output") | 
					
						
							|  |  |  |                 output = var | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |         self.stack.flush(out) | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         if output is not None: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             if output.is_array(): | 
					
						
							|  |  |  |                 assert len(self.inputs) == 1 | 
					
						
							|  |  |  |                 self.stack.drop(self.inputs[0].item, False) | 
					
						
							|  |  |  |                 self.stack.push(output) | 
					
						
							|  |  |  |                 self.stack.flush(out) | 
					
						
							|  |  |  |                 close_variable(self.inputs[0], "") | 
					
						
							|  |  |  |                 self.stack.drop(output.item, self.check_liveness) | 
					
						
							|  |  |  |                 self.inputs = [] | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             if var_size(lowest.item) != var_size(output.item): | 
					
						
							|  |  |  |                 raise StackError("Cannot call DECREF_INPUTS with live output not matching first input size") | 
					
						
							|  |  |  |             lowest.in_local = True | 
					
						
							|  |  |  |             close_variable(lowest, output.name) | 
					
						
							|  |  |  |             assert lowest.memory_offset is not None | 
					
						
							|  |  |  |         for input in reversed(self.inputs[1:]): | 
					
						
							|  |  |  |             close_variable(input, "PyStackRef_NULL") | 
					
						
							|  |  |  |         if output is None: | 
					
						
							|  |  |  |             close_variable(self.inputs[0], "PyStackRef_NULL") | 
					
						
							|  |  |  |         for input in reversed(self.inputs[1:]): | 
					
						
							|  |  |  |             input.kill() | 
					
						
							|  |  |  |             self.stack.drop(input.item, self.check_liveness) | 
					
						
							|  |  |  |         if output is None: | 
					
						
							|  |  |  |             self.inputs[0].kill() | 
					
						
							|  |  |  |         self.stack.drop(self.inputs[0].item, False) | 
					
						
							|  |  |  |         output_in_place = self.outputs and output is self.outputs[0] and lowest.memory_offset is not None | 
					
						
							|  |  |  |         if output_in_place: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             output.memory_offset = lowest.memory_offset  # type: ignore[union-attr] | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2025-03-26 15:21:35 +00:00
										 |  |  |             self.stack.flush(out) | 
					
						
							|  |  |  |         if output is not None: | 
					
						
							|  |  |  |             self.stack.push(output) | 
					
						
							|  |  |  |         self.inputs = [] | 
					
						
							|  |  |  |         if output_in_place: | 
					
						
							|  |  |  |             self.stack.flush(out) | 
					
						
							| 
									
										
										
										
											2025-02-12 17:44:59 +00:00
										 |  |  |         if output is not None: | 
					
						
							| 
									
										
										
										
											2025-03-31 13:52:48 +01:00
										 |  |  |             output = self.stack.pop(output.item, out) |