gh-106581: Split CALL_PY_EXACT_ARGS into uops (#107760)

* Split `CALL_PY_EXACT_ARGS` into uops

This is only the first step for doing `CALL` in Tier 2.
The next step involves tracing into the called code object and back.
After that we'll have to do the remaining `CALL` specialization.
Finally we'll have to deal with `KW_NAMES`.

Note: this moves setting `frame->return_offset` directly in front of
`DISPATCH_INLINED()`, to make it easier to move it into `_PUSH_FRAME`.
This commit is contained in:
Guido van Rossum 2023-08-16 16:26:43 -07:00 committed by GitHub
parent 665a4391e1
commit dc8fdf5fd5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 412 additions and 116 deletions

View file

@ -59,7 +59,7 @@ class Instruction:
block_line: int # First line of block in original code
# Computed by constructor
always_exits: bool
always_exits: str # If the block always exits, its last line; else ""
has_deopt: bool
cache_offset: int
cache_effects: list[parsing.CacheEffect]
@ -120,13 +120,13 @@ class Instruction:
def is_viable_uop(self) -> bool:
"""Whether this instruction is viable as a uop."""
dprint: typing.Callable[..., None] = lambda *args, **kwargs: None
# if self.name.startswith("CALL"):
# dprint = print
if "FRAME" in self.name:
dprint = print
if self.name == "EXIT_TRACE":
return True # This has 'return frame' but it's okay
if self.always_exits:
dprint(f"Skipping {self.name} because it always exits")
dprint(f"Skipping {self.name} because it always exits: {self.always_exits}")
return False
if len(self.active_caches) > 1:
# print(f"Skipping {self.name} because it has >1 cache entries")
@ -140,23 +140,6 @@ class Instruction:
res = False
return res
def write(self, out: Formatter, tier: Tiers = TIER_ONE) -> None:
"""Write one instruction, sans prologue and epilogue."""
# Write a static assertion that a family's cache size is correct
out.static_assert_family_size(self.name, self.family, self.cache_offset)
# Write input stack effect variable declarations and initializations
stacking.write_single_instr(self, out, tier)
# Skip the rest if the block always exits
if self.always_exits:
return
# Write cache effect
if tier == TIER_ONE and self.cache_offset:
out.emit(f"next_instr += {self.cache_offset};")
def write_body(
self,
out: Formatter,
@ -341,16 +324,16 @@ def extract_block_text(block: parsing.Block) -> tuple[list[str], bool, int]:
return blocklines, check_eval_breaker, block_line
def always_exits(lines: list[str]) -> bool:
def always_exits(lines: list[str]) -> str:
"""Determine whether a block always ends in a return/goto/etc."""
if not lines:
return False
return ""
line = lines[-1].rstrip()
# Indent must match exactly (TODO: Do something better)
if line[:12] != " " * 12:
return False
return ""
line = line[12:]
return line.startswith(
if line.startswith(
(
"goto ",
"return ",
@ -359,4 +342,6 @@ def always_exits(lines: list[str]) -> bool:
"Py_UNREACHABLE()",
"ERROR_IF(true, ",
)
)
):
return line
return ""