Several passes of analysis and optimization for textual assembly.
| 154 | |
| 155 | @dataclasses.dataclass |
| 156 | class Optimizer: |
| 157 | """Several passes of analysis and optimization for textual assembly.""" |
| 158 | |
| 159 | path: pathlib.Path |
| 160 | _: dataclasses.KW_ONLY |
| 161 | # Prefixes used to mangle local labels and symbols: |
| 162 | label_prefix: str |
| 163 | symbol_prefix: str |
| 164 | re_global: re.Pattern[str] |
| 165 | frame_pointers: bool |
| 166 | # The first block in the linked list: |
| 167 | _root: _Block = dataclasses.field(init=False, default_factory=_Block) |
| 168 | _labels: dict[str, _Block] = dataclasses.field(init=False, default_factory=dict) |
| 169 | # No groups: |
| 170 | _re_noninstructions: typing.ClassVar[re.Pattern[str]] = re.compile( |
| 171 | r"\s*(?:\.|#|//|;|$)" |
| 172 | ) |
| 173 | # One group (label): |
| 174 | _re_label: typing.ClassVar[re.Pattern[str]] = re.compile( |
| 175 | r'\s*(?P<label>[\w."$?@]+):' |
| 176 | ) |
| 177 | # Override everything that follows in subclasses: |
| 178 | _supports_external_relocations = True |
| 179 | supports_small_constants = False |
| 180 | _branches: typing.ClassVar[dict[str, tuple[str | None, str | None]]] = {} |
| 181 | # Short branches are instructions that can branch within a micro-op, |
| 182 | # but might not have the reach to branch anywhere within a trace. |
| 183 | _short_branches: typing.ClassVar[dict[str, str]] = {} |
| 184 | # Two groups (instruction and target): |
| 185 | _re_branch: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH |
| 186 | # One group (target): |
| 187 | _re_call: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH |
| 188 | # One group (target): |
| 189 | _re_jump: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH |
| 190 | # No groups: |
| 191 | _re_return: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH |
| 192 | text: str = "" |
| 193 | globals: set[str] = dataclasses.field(default_factory=set) |
| 194 | _re_small_const_1 = _RE_NEVER_MATCH |
| 195 | _re_small_const_2 = _RE_NEVER_MATCH |
| 196 | const_reloc = "<Not supported>" |
| 197 | _frame_pointer_modify: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH |
| 198 | |
| 199 | def __post_init__(self) -> None: |
| 200 | # Split the code into a linked list of basic blocks. A basic block is an |
| 201 | # optional label, followed by zero or more non-instruction lines, |
| 202 | # followed by zero or more instruction lines (only the last of which may |
| 203 | # be a branch, jump, or return): |
| 204 | self.text = self._preprocess(self.path.read_text()) |
| 205 | block = self._root |
| 206 | for line in self.text.splitlines(): |
| 207 | # See if we need to start a new block: |
| 208 | if match := self._re_label.match(line): |
| 209 | # Label. New block: |
| 210 | block.link = block = self._lookup_label(match["label"]) |
| 211 | block.noninstructions.append(line) |
| 212 | continue |
| 213 | if match := self.re_global.match(line): |