diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 83c878d8fe205b..165123e45a630a 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -1,13 +1,14 @@ """Low-level optimization of textual assembly.""" - import dataclasses import enum import pathlib import re import typing +from collections import defaultdict # Same as saying "not string.startswith('')": _RE_NEVER_MATCH = re.compile(r"(?!)") + # Dictionary mapping branch instructions to their inverted branch instructions. # If a branch cannot be inverted, the value is None: _X86_BRANCH_NAMES = { @@ -37,8 +38,10 @@ "loopnz": None, "loopz": None, } + # Update with all of the inverted branches, too: _X86_BRANCH_NAMES |= {v: k for k, v in _X86_BRANCH_NAMES.items() if v} + # No custom relocations needed _X86_BRANCHES: dict[str, tuple[str | None, str | None]] = { k: (v, None) for k, v in _X86_BRANCH_NAMES.items() @@ -63,6 +66,7 @@ "hi": "ls", "ls": "hi", } + # MyPy doesn't understand that a invariant variable can be initialized by a covariant value CUSTOM_AARCH64_BRANCH19: str | None = "CUSTOM_AARCH64_BRANCH19" @@ -91,7 +95,6 @@ @enum.unique class InstructionKind(enum.Enum): - JUMP = enum.auto() LONG_BRANCH = enum.auto() SHORT_BRANCH = enum.auto() @@ -99,6 +102,9 @@ class InstructionKind(enum.Enum): RETURN = enum.auto() SMALL_CONST_1 = enum.auto() SMALL_CONST_2 = enum.auto() + MOVE = enum.auto() # NEW: For move instructions + ARITHMETIC = enum.auto() # NEW: For arithmetic operations + COMPARE = enum.auto() # NEW: For comparison operations OTHER = enum.auto() @@ -108,16 +114,24 @@ class Instruction: name: str text: str target: str | None - + # NEW: Additional metadata for optimizations + reads: set[str] = dataclasses.field(default_factory=set) # Registers read + writes: set[str] = dataclasses.field(default_factory=set) # Registers written + def is_branch(self) -> bool: return self.kind in (InstructionKind.LONG_BRANCH, InstructionKind.SHORT_BRANCH) - + + def is_nop(self) -> bool: + """Check if instruction is effectively a no-op.""" + return self.name in ("nop", "nopw", "nopl") + def update_target(self, target: str) -> "Instruction": assert self.target is not None return Instruction( - self.kind, self.name, self.text.replace(self.target, target), target + self.kind, self.name, self.text.replace(self.target, target), target, + self.reads, self.writes ) - + def update_name_and_target(self, name: str, target: str) -> "Instruction": assert self.target is not None return Instruction( @@ -125,6 +139,8 @@ def update_name_and_target(self, name: str, target: str) -> "Instruction": name, self.text.replace(self.name, name).replace(self.target, target), target, + self.reads, + self.writes ) @@ -143,7 +159,13 @@ class _Block: fallthrough: bool = True # Whether this block can eventually reach the next uop (_JIT_CONTINUE): hot: bool = False - + # NEW: Live registers at block entry (for liveness analysis) + live_in: set[str] = dataclasses.field(default_factory=set) + # NEW: Live registers at block exit + live_out: set[str] = dataclasses.field(default_factory=set) + # NEW: Available expressions (for common subexpression elimination) + available_in: set[str] = dataclasses.field(default_factory=set) + def resolve(self) -> typing.Self: """Find the first non-empty block reachable from this one.""" block = self @@ -155,7 +177,7 @@ def resolve(self) -> typing.Self: @dataclasses.dataclass class Optimizer: """Several passes of analysis and optimization for textual assembly.""" - + path: pathlib.Path _: dataclasses.KW_ONLY # Prefixes used to mangle local labels and symbols: @@ -165,6 +187,9 @@ class Optimizer: # The first block in the linked list: _root: _Block = dataclasses.field(init=False, default_factory=_Block) _labels: dict[str, _Block] = dataclasses.field(init=False, default_factory=dict) + # NEW: Optimization statistics + _stats: dict[str, int] = dataclasses.field(init=False, default_factory=lambda: defaultdict(int)) + # No groups: _re_noninstructions: typing.ClassVar[re.Pattern[str]] = re.compile( r"\s*(?:\.|#|//|;|$)" @@ -173,6 +198,7 @@ class Optimizer: _re_label: typing.ClassVar[re.Pattern[str]] = re.compile( r'\s*(?P