|
9 | 9 | _RE_NEVER_MATCH = re.compile(r"(?!)") |
10 | 10 | # Dictionary mapping branch instructions to their inverted branch instructions. |
11 | 11 | # If a branch cannot be inverted, the value is None: |
12 | | -_X86_BRANCHES = { |
| 12 | +_X86_BRANCH_NAMES = { |
13 | 13 | # https://www.felixcloutier.com/x86/jcc |
14 | 14 | "ja": "jna", |
15 | 15 | "jae": "jnae", |
|
37 | 37 | "loopz": None, |
38 | 38 | } |
39 | 39 | # Update with all of the inverted branches, too: |
40 | | -_X86_BRANCHES |= {v: k for k, v in _X86_BRANCHES.items() if v} |
| 40 | +_X86_BRANCH_NAMES |= {v: k for k, v in _X86_BRANCH_NAMES.items() if v} |
| 41 | +# No custom relocations needed |
| 42 | +_X86_BRANCHES: dict[str, tuple[str | None, str | None]] = { |
| 43 | + k: (v, None) for k, v in _X86_BRANCH_NAMES.items() |
| 44 | +} |
41 | 45 |
|
42 | 46 | _AARCH64_COND_CODES = { |
43 | 47 | # https://developer.arm.com/documentation/dui0801/b/CJAJIHAD?lang=en |
|
58 | 62 | "hi": "ls", |
59 | 63 | "ls": "hi", |
60 | 64 | } |
| 65 | +# MyPy doesn't understand that a invariant variable can be initialized by a covariant value |
| 66 | +CUSTOM_AARCH64_BRANCH19: str | None = "CUSTOM_AARCH64_BRANCH19" |
| 67 | + |
61 | 68 | # Branches are either b.{cond} or bc.{cond} |
62 | | -_AARCH64_BRANCHES = { |
63 | | - "b." + cond: ("b." + inverse if inverse else None) |
| 69 | +_AARCH64_BRANCHES: dict[str, tuple[str | None, str | None]] = { |
| 70 | + "b." + cond: (("b." + inverse if inverse else None), CUSTOM_AARCH64_BRANCH19) |
64 | 71 | for (cond, inverse) in _AARCH64_COND_CODES.items() |
65 | 72 | } | { |
66 | | - "bc." + cond: ("bc." + inverse if inverse else None) |
| 73 | + "bc." + cond: (("bc." + inverse if inverse else None), CUSTOM_AARCH64_BRANCH19) |
67 | 74 | for (cond, inverse) in _AARCH64_COND_CODES.items() |
68 | 75 | } |
69 | 76 |
|
@@ -113,7 +120,8 @@ class Optimizer: |
113 | 120 | r'\s*(?P<label>[\w."$?@]+):' |
114 | 121 | ) |
115 | 122 | # Override everything that follows in subclasses: |
116 | | - _branches: typing.ClassVar[dict[str, str | None]] = {} |
| 123 | + _supports_external_relocations = True |
| 124 | + _branches: typing.ClassVar[dict[str, tuple[str | None, str | None]]] = {} |
117 | 125 | # Two groups (instruction and target): |
118 | 126 | _re_branch: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH |
119 | 127 | # One group (target): |
@@ -170,7 +178,10 @@ def _preprocess(self, text: str) -> str: |
170 | 178 | def _invert_branch(cls, line: str, target: str) -> str | None: |
171 | 179 | match = cls._re_branch.match(line) |
172 | 180 | assert match |
173 | | - inverted = cls._branches.get(match["instruction"]) |
| 181 | + inverted_reloc = cls._branches.get(match["instruction"]) |
| 182 | + if inverted_reloc is None: |
| 183 | + return None |
| 184 | + inverted = inverted_reloc[0] |
174 | 185 | if not inverted: |
175 | 186 | return None |
176 | 187 | (a, b), (c, d) = match.span("instruction"), match.span("target") |
@@ -302,27 +313,45 @@ def _remove_redundant_jumps(self) -> None: |
302 | 313 | block.fallthrough = True |
303 | 314 | block.instructions.pop() |
304 | 315 |
|
| 316 | + def _fixup_external_labels(self) -> None: |
| 317 | + if self._supports_external_relocations: |
| 318 | + # Nothing to fix up |
| 319 | + return |
| 320 | + for block in self._blocks(): |
| 321 | + if block.target and block.fallthrough: |
| 322 | + branch = block.instructions[-1] |
| 323 | + match = self._re_branch.match(branch) |
| 324 | + assert match is not None |
| 325 | + target = match["target"] |
| 326 | + reloc = self._branches[match["instruction"]][1] |
| 327 | + if reloc is not None and not target.startswith(self.label_prefix): |
| 328 | + name = target[len(self.symbol_prefix) :] |
| 329 | + block.instructions[-1] = ( |
| 330 | + f"// target='{target}' prefix='{self.label_prefix}'" |
| 331 | + ) |
| 332 | + block.instructions.append( |
| 333 | + f"{self.symbol_prefix}{reloc}_JIT_RELOCATION_{name}:" |
| 334 | + ) |
| 335 | + a, b = match.span("target") |
| 336 | + branch = "".join([branch[:a], "0", branch[b:]]) |
| 337 | + block.instructions.append(branch) |
| 338 | + |
305 | 339 | def run(self) -> None: |
306 | 340 | """Run this optimizer.""" |
307 | 341 | self._insert_continue_label() |
308 | 342 | self._mark_hot_blocks() |
309 | 343 | self._invert_hot_branches() |
310 | 344 | self._remove_redundant_jumps() |
| 345 | + self._fixup_external_labels() |
311 | 346 | self.path.write_text(self._body()) |
312 | 347 |
|
313 | 348 |
|
314 | | -# Mach-O does not support the 19 bit branch locations needed for branch reordering |
315 | | -class OptimizerAArch64_MachO(Optimizer): # pylint: disable = too-few-public-methods |
316 | | - """aarch64-apple-darwin""" |
317 | | - |
318 | | - # https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch- |
319 | | - _re_jump = re.compile(r"\s*b\s+(?P<target>[\w.]+)") |
320 | | - |
321 | | - |
322 | 349 | class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods |
323 | | - """aarch64-pc-windows-msvc/aarch64-unknown-linux-gnu""" |
| 350 | + """aarch64-pc-windows-msvc/aarch64-apple-darwin/aarch64-unknown-linux-gnu""" |
324 | 351 |
|
325 | 352 | _branches = _AARCH64_BRANCHES |
| 353 | + # Mach-O does not support the 19 bit branch locations needed for branch reordering |
| 354 | + _supports_external_relocations = False |
326 | 355 | _re_branch = re.compile( |
327 | 356 | rf"\s*(?P<instruction>{'|'.join(_AARCH64_BRANCHES)})\s+(.+,\s+)*(?P<target>[\w.]+)" |
328 | 357 | ) |
|
0 commit comments