Skip to content

Commit 5ec6022

Browse files
committed
Commenting and cleanup
1 parent a987be7 commit 5ec6022

File tree

2 files changed

+61
-37
lines changed

2 files changed

+61
-37
lines changed

Tools/jit/_optimizers.py

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
"""Low-level optimization of textual assembly."""
2+
13
import dataclasses
24
import pathlib
35
import re
46
import typing
57

6-
_RE_NEVER_MATCH = re.compile(r"(?!)") # Same as saying "not string.startswith('')".
8+
# Same as saying "not string.startswith('')":
9+
_RE_NEVER_MATCH = re.compile(r"(?!)")
10+
# Dictionary mapping x86 branching instructions to their inverted counterparts.
11+
# If a branch cannot be inverted, the value is None:
712
_X86_BRANCHES = {
813
"ja": "jna",
914
"jae": "jnae",
@@ -29,58 +34,67 @@
2934
"loopnz": None,
3035
"loopz": None,
3136
}
37+
# Update with all of the inverted branches, too:
3238
_X86_BRANCHES |= {v: k for k, v in _X86_BRANCHES.items() if v}
3339

3440

3541
@dataclasses.dataclass
3642
class _Block:
3743
label: str | None = None
38-
noise: list[str] = dataclasses.field(default_factory=list)
44+
header: list[str] = dataclasses.field(default_factory=list)
3945
instructions: list[str] = dataclasses.field(default_factory=list)
4046
target: typing.Self | None = None
4147
link: typing.Self | None = None
4248
fallthrough: bool = True
4349
hot: bool = False
4450

4551
def resolve(self) -> typing.Self:
46-
while self.link and not self.instructions:
47-
self = self.link
48-
return self
52+
"""Find the first non-empty block reachable from this one."""
53+
block = self
54+
while block.link and not block.instructions:
55+
block = block.link
56+
return block
4957

5058

5159
@dataclasses.dataclass
5260
class Optimizer:
61+
"""Several passes of analysis and optimization for textual assembly."""
62+
5363
path: pathlib.Path
5464
_: dataclasses.KW_ONLY
65+
# prefix used to mangle symbols on some platforms:
5566
prefix: str = ""
56-
_graph: _Block = dataclasses.field(init=False)
57-
_labels: dict = dataclasses.field(init=False, default_factory=dict)
58-
_alignment: typing.ClassVar[int] = 1
59-
_branches: typing.ClassVar[dict[str, str | None]] = {}
60-
_re_branch: typing.ClassVar[re.Pattern[str]] = (
61-
_RE_NEVER_MATCH # Two groups: instruction and target.
62-
)
63-
_re_jump: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH # One group: target.
67+
# The first block in the linked list:
68+
_root: _Block = dataclasses.field(init=False, default_factory=_Block)
69+
_labels: dict[str, _Block] = dataclasses.field(init=False, default_factory=dict)
70+
# No groups:
71+
_re_header: typing.ClassVar[re.Pattern[str]] = re.compile(r"\s*(?:\.|#|//|$)")
72+
# One group (label):
6473
_re_label: typing.ClassVar[re.Pattern[str]] = re.compile(
65-
r'\s*(?P<label>[\w."$?@]+):' # One group: label.
66-
)
67-
_re_noise: typing.ClassVar[re.Pattern[str]] = re.compile(
68-
r"\s*(?:\.|#|//|$)" # No groups.
74+
r'\s*(?P<label>[\w."$?@]+):'
6975
)
70-
_re_return: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH # No groups.
76+
# Override everything that follows in subclasses:
77+
_alignment: typing.ClassVar[int] = 1
78+
_branches: typing.ClassVar[dict[str, str | None]] = {}
79+
# Two groups (instruction and target):
80+
_re_branch: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH
81+
# One group (target):
82+
_re_jump: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH
83+
# No groups:
84+
_re_return: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH
7185

7286
def __post_init__(self) -> None:
7387
text = self._preprocess(self.path.read_text())
74-
self._graph = block = _Block()
88+
block = self._root
7589
for line in text.splitlines():
7690
if match := self._re_label.match(line):
7791
block.link = block = self._lookup_label(match["label"])
78-
block.noise.append(line)
92+
block.header.append(line)
7993
continue
80-
if self._re_noise.match(line):
94+
if self._re_header.match(line):
8195
if block.instructions:
8296
block.link = block = _Block()
83-
block.noise.append(line)
97+
block.header.append(line)
8498
continue
8599
if block.target or not block.fallthrough:
86100
block.link = block = _Block()
@@ -95,8 +109,7 @@ def __post_init__(self) -> None:
95109
assert not block.target
96110
block.fallthrough = False
97111

98-
@staticmethod
99-
def _preprocess(text: str) -> str:
112+
def _preprocess(self, text: str) -> str:
100113
return text
101114

102115
@classmethod
@@ -122,7 +135,7 @@ def _lookup_label(self, label: str) -> _Block:
122135
return self._labels[label]
123136

124137
def _blocks(self) -> typing.Generator[_Block, None, None]:
125-
block = self._graph
138+
block: _Block | None = self._root
126139
while block:
127140
yield block
128141
block = block.link
@@ -134,7 +147,7 @@ def _body(self) -> str:
134147
if hot != block.hot:
135148
hot = block.hot
136149
lines.append(f"# JIT: {'HOT' if hot else 'COLD'} ".ljust(80, "#"))
137-
lines.extend(block.noise)
150+
lines.extend(block.header)
138151
lines.extend(block.instructions)
139152
return "\n".join(lines)
140153

@@ -150,10 +163,10 @@ def _insert_continue_label(self) -> None:
150163
if end.instructions:
151164
break
152165
align = _Block()
153-
align.noise.append(f"\t.balign\t{self._alignment}")
166+
align.header.append(f"\t.balign\t{self._alignment}")
154167
continuation = self._lookup_label(f"{self.prefix}_JIT_CONTINUE")
155168
assert continuation.label
156-
continuation.noise.append(f"{continuation.label}:")
169+
continuation.header.append(f"{continuation.label}:")
157170
end.link, align.link, continuation.link = align, continuation, end.link
158171

159172
def _mark_hot_blocks(self) -> None:
@@ -168,10 +181,10 @@ def _mark_hot_blocks(self) -> None:
168181
)
169182

170183
def _invert_hot_branches(self) -> None:
171-
# Turn:
184+
# Before:
172185
# branch <hot>
173186
# jump <cold>
174-
# Into:
187+
# After:
175188
# opposite-branch <cold>
176189
# jump <hot>
177190
for branch in self._blocks():
@@ -217,20 +230,25 @@ def _remove_redundant_jumps(self) -> None:
217230
block.instructions.pop()
218231

219232
def run(self) -> None:
233+
"""Run this optimizer."""
220234
self._insert_continue_label()
221235
self._mark_hot_blocks()
222236
self._invert_hot_branches()
223237
self._remove_redundant_jumps()
224238
self.path.write_text(self._body())
225239

226240

227-
class OptimizerAArch64(Optimizer):
241+
class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods
242+
"""aarch64-apple-darwin/aarch64-pc-windows-msvc/aarch64-unknown-linux-gnu"""
243+
228244
# TODO: @diegorusso
229245
_alignment = 8
230246
_re_jump = re.compile(r"\s*b\s+(?P<target>[\w.]+)")
231247

232248

233-
class OptimizerX86(Optimizer):
249+
class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods
250+
"""i686-pc-windows-msvc/x86_64-apple-darwin/x86_64-unknown-linux-gnu"""
251+
234252
_branches = _X86_BRANCHES
235253
_re_branch = re.compile(
236254
rf"\s*(?P<instruction>{'|'.join(_X86_BRANCHES)})\s+(?P<target>[\w.]+)"
@@ -239,10 +257,15 @@ class OptimizerX86(Optimizer):
239257
_re_return = re.compile(r"\s*ret\b")
240258

241259

242-
class OptimizerX86Windows(OptimizerX86):
260+
class OptimizerX8664Windows(OptimizerX86): # pylint: disable = too-few-public-methods
261+
"""x86_64-pc-windows-msvc"""
262+
243263
def _preprocess(self, text: str) -> str:
244264
text = super()._preprocess(text)
245-
# rex64 jumpq *__imp__JIT_CONTINUE(%rip) -> jmp _JIT_CONTINUE
265+
# Before:
266+
# rex64 jmpq *__imp__JIT_CONTINUE(%rip)
267+
# After:
268+
# jmp _JIT_CONTINUE
246269
far_indirect_jump = (
247270
rf"rex64\s+jmpq\s+\*__imp_(?P<target>{self.prefix}_JIT_\w+)\(%rip\)"
248271
)

Tools/jit/_targets.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class _Target(typing.Generic[_S, _R]):
4242
condition: str
4343
_: dataclasses.KW_ONLY
4444
args: typing.Sequence[str] = ()
45-
optimizer: type[_optimizers.Optimizer]
45+
optimizer: type[_optimizers.Optimizer] = _optimizers.Optimizer
4646
prefix: str = ""
4747
stable: bool = False
4848
debug: bool = False
@@ -520,6 +520,7 @@ def _handle_relocation(
520520

521521
def get_target(host: str) -> _COFF | _ELF | _MachO:
522522
"""Build a _Target for the given host "triple" and options."""
523+
optimizer: type[_optimizers.Optimizer]
523524
target: _COFF | _ELF | _MachO
524525
if re.fullmatch(r"aarch64-apple-darwin.*", host):
525526
condition = "defined(__aarch64__) && defined(__APPLE__)"
@@ -539,7 +540,7 @@ def get_target(host: str) -> _COFF | _ELF | _MachO:
539540
elif re.fullmatch(r"i686-pc-windows-msvc", host):
540541
# -Wno-ignored-attributes: __attribute__((preserve_none)) is not supported here.
541542
args = ["-DPy_NO_ENABLE_SHARED", "-Wno-ignored-attributes"]
542-
optimizer = _optimizers.OptimizerX86Windows
543+
optimizer = _optimizers.OptimizerX86
543544
condition = "defined(_M_IX86)"
544545
target = _COFF(host, condition, args=args, optimizer=optimizer, prefix="_")
545546
elif re.fullmatch(r"x86_64-apple-darwin.*", host):
@@ -549,7 +550,7 @@ def get_target(host: str) -> _COFF | _ELF | _MachO:
549550
elif re.fullmatch(r"x86_64-pc-windows-msvc", host):
550551
args = ["-fms-runtime-lib=dll"]
551552
condition = "defined(_M_X64)"
552-
optimizer = _optimizers.OptimizerX86Windows
553+
optimizer = _optimizers.OptimizerX8664Windows
553554
target = _COFF(host, condition, args=args, optimizer=optimizer)
554555
elif re.fullmatch(r"x86_64-.*-linux-gnu", host):
555556
args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0"]

0 commit comments

Comments
 (0)