Skip to content

Commit 93c2179

Browse files
committed
Clarify hot/cold JIT relocation handling
1 parent 5a395d2 commit 93c2179

2 files changed

Lines changed: 31 additions & 4 deletions

File tree

Tools/jit/_optimizers.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
}
6666
# MyPy doesn't understand that a invariant variable can be initialized by a covariant value
6767
CUSTOM_AARCH64_BRANCH19: str | None = "CUSTOM_AARCH64_BRANCH19"
68-
CUSTOM_AARCH64_BRANCH26: str | None = "ARM64_RELOC_BRANCH26"
68+
ARM64_RELOC_BRANCH26: str | None = "ARM64_RELOC_BRANCH26"
6969

7070
_AARCH64_SHORT_BRANCHES = {
7171
"tbz": "tbnz",
@@ -163,6 +163,13 @@ class _Block:
163163
def resolve(self) -> typing.Self:
164164
"""Find the first non-empty block reachable from this one."""
165165
block = self
166+
# Empty blocks are only transparent when control can fall through them:
167+
# .L1:
168+
# .L2:
169+
# mov x0, x1
170+
# We set block.fallthrough to False for sentinels like _JIT_CONTINUE,
171+
# which may be empty but still must not resolve through to
172+
# _JIT_COLD_START.
166173
while block.link and not block.instructions and block.fallthrough:
167174
block = block.link
168175
return block
@@ -353,6 +360,7 @@ def _continuation(self) -> _Block:
353360
return self._lookup_label(f"{self.label_prefix}_JIT_CONTINUE")
354361

355362
def _cold_start_block(self) -> _Block:
363+
"""Create the marker block where the cold code section begins."""
356364
if self._cold_start is None:
357365
label = f"{self.symbol_prefix}_JIT_COLD_START"
358366
self._cold_start = self._lookup_label(label)
@@ -391,6 +399,7 @@ def _make_cross_section_target(self, block: _Block) -> str:
391399
)
392400

393401
def _relocation_marker(self, reloc: str, block: _Block) -> Instruction:
402+
"""Create a marker symbol that _stencils.py converts to a relocation."""
394403
target = self._make_cross_section_target(block).removeprefix(self.symbol_prefix)
395404
label = f"{self.symbol_prefix}{reloc}_JIT_RELOCATION_{target}:"
396405
return Instruction(InstructionKind.OTHER, "", label, None, None)
@@ -414,6 +423,13 @@ def _make_jump(self, target: _Block, *, hot: bool) -> _Block:
414423
)
415424

416425
def _effective_layout_hot(self, block: _Block) -> bool:
426+
"""Return the hot/cold section where a block should be emitted.
427+
428+
Empty label-only blocks inherit the layout of the block they resolve to.
429+
Explicit layout_hot overrides are also respected, because a block can be
430+
cold in the control-flow graph but still need to be emitted in the hot
431+
section as a bridge.
432+
"""
417433
resolved = block.resolve()
418434
if resolved.layout_hot is not None:
419435
return resolved.layout_hot
@@ -506,6 +522,11 @@ def _layout_units(self) -> list[tuple[bool, list[_Block]]]:
506522
def _layout_units_from(
507523
self, blocks: list[_Block]
508524
) -> list[tuple[bool, list[_Block]]]:
525+
"""Group adjacent blocks that must stay together during layout.
526+
527+
Label-only fallthrough blocks inherit the layout of the instruction
528+
block they resolve to, so partitioning must move them as one unit.
529+
"""
509530
continuation = self._continuation()
510531
cold_start = self._cold_start_block()
511532
units: list[tuple[bool, list[_Block]]] = []
@@ -596,6 +617,7 @@ def _blocks(self) -> typing.Generator[_Block, None, None]:
596617
block = block.link
597618

598619
def _layout_blocks(self) -> typing.Generator[_Block, None, None]:
620+
"""Yield only blocks that participate in executable code layout."""
599621
for block in self._blocks():
600622
if not block.is_metadata:
601623
yield block
@@ -802,7 +824,8 @@ def _remove_unreachable(self) -> None:
802824
assert seen_continuation
803825
assert seen_cold_start
804826

805-
def _fixup_cross_section_branches(self) -> None:
827+
def _insert_cross_section_branch_relocations(self) -> None:
828+
"""Insert relocation markers for branches crossing the hot/cold split."""
806829
layout_blocks = set(self._layout_blocks())
807830
for block in self._layout_blocks():
808831
target = block.target
@@ -876,7 +899,7 @@ def run(self) -> None:
876899
self._remove_redundant_jumps()
877900
self._remove_unreachable()
878901
self._fixup_external_labels()
879-
self._fixup_cross_section_branches()
902+
self._insert_cross_section_branch_relocations()
880903
self._fixup_constants()
881904
self._validate()
882905
self.path.write_text(self._body())
@@ -888,7 +911,7 @@ class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods
888911
_branches = _AARCH64_BRANCHES
889912
_short_branches = _AARCH64_SHORT_BRANCHES
890913
_jump_name = "b"
891-
_jump_reloc = CUSTOM_AARCH64_BRANCH26
914+
_jump_reloc = ARM64_RELOC_BRANCH26
892915
# Mach-O does not support the 19 bit branch locations needed for branch reordering
893916
_supports_external_relocations = False
894917
_branch_patterns = [name.replace(".", r"\.") for name in _AARCH64_BRANCHES]

Tools/jit/_stencils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ class Hole:
177177
void: bool = False
178178

179179
def replace(self, **changes: typing.Any) -> typing.Self:
180+
"""Copy this hole without rerunning __post_init__ and resetting func."""
180181
field_names = {field.name for field in dataclasses.fields(self)}
181182
for name in changes:
182183
if name not in field_names:
@@ -346,6 +347,8 @@ def process_relocations(self, known_symbols: dict[str, int]) -> None:
346347
"""Fix up all GOT and internal relocations for this stencil group."""
347348
for stencil in self._code_stencils():
348349
for hole in stencil.holes.copy():
350+
# Cross-section symbols are optimizer-generated internal
351+
# targets; resolve them before external branch handling.
349352
if self._resolve_cross_section_symbol(hole):
350353
continue
351354
if (
@@ -436,6 +439,7 @@ def process_relocations(self, known_symbols: dict[str, int]) -> None:
436439
self.data.holes.sort(key=lambda hole: hole.offset)
437440

438441
def _resolve_cross_section_symbol(self, hole: Hole) -> bool:
442+
"""Strip hot/cold target wrappers and rewrite holes to CODE/COLD_CODE."""
439443
if hole.value is not HoleValue.ZERO or hole.symbol is None:
440444
return False
441445
symbol = hole.symbol

0 commit comments

Comments
 (0)