mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-106581: Fix two bugs in the code generator's copy optimization (#108380)
I was comparing the last preceding poke with the *last* peek, rather than the *first* peek. Unfortunately this bug obscured another bug: When the last preceding poke is UNUSED, the first peek disappears, leaving the variable unassigned. This is how I fixed it: - Rename CopyEffect to CopyItem. - Change CopyItem to contain StackItems instead of StackEffects. - Update those StackItems when adjusting the manager higher or lower. - Assert that those StackItems' offsets are equivalent. - Other clever things. --------- Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
This commit is contained in:
parent
c494fb333b
commit
88941d665f
2 changed files with 62 additions and 20 deletions
3
Python/generated_cases.c.h
generated
3
Python/generated_cases.c.h
generated
|
@ -3833,7 +3833,6 @@
|
||||||
DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL);
|
DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL);
|
||||||
}
|
}
|
||||||
// _CHECK_STACK_SPACE
|
// _CHECK_STACK_SPACE
|
||||||
callable = stack_pointer[-2 - oparg];
|
|
||||||
{
|
{
|
||||||
PyFunctionObject *func = (PyFunctionObject *)callable;
|
PyFunctionObject *func = (PyFunctionObject *)callable;
|
||||||
PyCodeObject *code = (PyCodeObject *)func->func_code;
|
PyCodeObject *code = (PyCodeObject *)func->func_code;
|
||||||
|
@ -3841,8 +3840,6 @@
|
||||||
}
|
}
|
||||||
// _INIT_CALL_PY_EXACT_ARGS
|
// _INIT_CALL_PY_EXACT_ARGS
|
||||||
args = stack_pointer - oparg;
|
args = stack_pointer - oparg;
|
||||||
self_or_null = stack_pointer[-1 - oparg];
|
|
||||||
callable = stack_pointer[-2 - oparg];
|
|
||||||
{
|
{
|
||||||
int argcount = oparg;
|
int argcount = oparg;
|
||||||
if (self_or_null != NULL) {
|
if (self_or_null != NULL) {
|
||||||
|
|
|
@ -83,6 +83,27 @@ class StackOffset:
|
||||||
terms = self.as_terms()
|
terms = self.as_terms()
|
||||||
return make_index(terms)
|
return make_index(terms)
|
||||||
|
|
||||||
|
def equivalent_to(self, other: "StackOffset") -> bool:
|
||||||
|
if self.deep == other.deep and self.high == other.high:
|
||||||
|
return True
|
||||||
|
deep = list(self.deep)
|
||||||
|
for x in other.deep:
|
||||||
|
try:
|
||||||
|
deep.remove(x)
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
if deep:
|
||||||
|
return False
|
||||||
|
high = list(self.high)
|
||||||
|
for x in other.high:
|
||||||
|
try:
|
||||||
|
high.remove(x)
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
if high:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def make_index(terms: list[tuple[str, str]]) -> str:
|
def make_index(terms: list[tuple[str, str]]) -> str:
|
||||||
# Produce an index expression from the terms honoring PEP 8,
|
# Produce an index expression from the terms honoring PEP 8,
|
||||||
|
@ -131,9 +152,9 @@ class StackItem:
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class CopyEffect:
|
class CopyItem:
|
||||||
src: StackEffect
|
src: StackItem
|
||||||
dst: StackEffect
|
dst: StackItem
|
||||||
|
|
||||||
|
|
||||||
class EffectManager:
|
class EffectManager:
|
||||||
|
@ -143,7 +164,7 @@ class EffectManager:
|
||||||
active_caches: list[ActiveCacheEffect]
|
active_caches: list[ActiveCacheEffect]
|
||||||
peeks: list[StackItem]
|
peeks: list[StackItem]
|
||||||
pokes: list[StackItem]
|
pokes: list[StackItem]
|
||||||
copies: list[CopyEffect] # See merge()
|
copies: list[CopyItem] # See merge()
|
||||||
# Track offsets from stack pointer
|
# Track offsets from stack pointer
|
||||||
min_offset: StackOffset
|
min_offset: StackOffset
|
||||||
final_offset: StackOffset
|
final_offset: StackOffset
|
||||||
|
@ -179,16 +200,18 @@ class EffectManager:
|
||||||
while (
|
while (
|
||||||
pred.pokes
|
pred.pokes
|
||||||
and self.peeks
|
and self.peeks
|
||||||
and pred.pokes[-1].effect == self.peeks[-1].effect
|
and pred.pokes[-1].effect == self.peeks[0].effect
|
||||||
):
|
):
|
||||||
src = pred.pokes.pop(-1).effect
|
src = pred.pokes.pop(-1)
|
||||||
dst = self.peeks.pop(0).effect
|
dst = self.peeks.pop(0)
|
||||||
pred.final_offset.deeper(src)
|
assert src.offset.equivalent_to(dst.offset), (src, dst)
|
||||||
if dst.name != UNUSED:
|
pred.final_offset.deeper(src.effect)
|
||||||
destinations.add(dst.name)
|
if dst.effect.name != src.effect.name:
|
||||||
if dst.name != src.name:
|
if dst.effect.name != UNUSED:
|
||||||
sources.add(src.name)
|
destinations.add(dst.effect.name)
|
||||||
self.copies.append(CopyEffect(src, dst))
|
if src.effect.name != UNUSED:
|
||||||
|
sources.add(src.effect.name)
|
||||||
|
self.copies.append(CopyItem(src, dst))
|
||||||
# TODO: Turn this into an error (pass an Analyzer instance?)
|
# TODO: Turn this into an error (pass an Analyzer instance?)
|
||||||
assert sources & destinations == set(), (
|
assert sources & destinations == set(), (
|
||||||
pred.instr.name,
|
pred.instr.name,
|
||||||
|
@ -202,11 +225,27 @@ class EffectManager:
|
||||||
else:
|
else:
|
||||||
pred = None # Break
|
pred = None # Break
|
||||||
|
|
||||||
|
# Fix up patterns of copies through UNUSED,
|
||||||
|
# e.g. cp(a, UNUSED) + cp(UNUSED, b) -> cp(a, b).
|
||||||
|
if any(copy.src.effect.name == UNUSED for copy in self.copies):
|
||||||
|
pred = self.pred
|
||||||
|
while pred is not None:
|
||||||
|
for copy in self.copies:
|
||||||
|
if copy.src.effect.name == UNUSED:
|
||||||
|
for pred_copy in pred.copies:
|
||||||
|
if pred_copy.dst == copy.src:
|
||||||
|
copy.src = pred_copy.src
|
||||||
|
break
|
||||||
|
pred = pred.pred
|
||||||
|
|
||||||
def adjust_deeper(self, eff: StackEffect) -> None:
|
def adjust_deeper(self, eff: StackEffect) -> None:
|
||||||
for peek in self.peeks:
|
for peek in self.peeks:
|
||||||
peek.offset.deeper(eff)
|
peek.offset.deeper(eff)
|
||||||
for poke in self.pokes:
|
for poke in self.pokes:
|
||||||
poke.offset.deeper(eff)
|
poke.offset.deeper(eff)
|
||||||
|
for copy in self.copies:
|
||||||
|
copy.src.offset.deeper(eff)
|
||||||
|
copy.dst.offset.deeper(eff)
|
||||||
self.min_offset.deeper(eff)
|
self.min_offset.deeper(eff)
|
||||||
self.final_offset.deeper(eff)
|
self.final_offset.deeper(eff)
|
||||||
|
|
||||||
|
@ -215,6 +254,9 @@ class EffectManager:
|
||||||
peek.offset.higher(eff)
|
peek.offset.higher(eff)
|
||||||
for poke in self.pokes:
|
for poke in self.pokes:
|
||||||
poke.offset.higher(eff)
|
poke.offset.higher(eff)
|
||||||
|
for copy in self.copies:
|
||||||
|
copy.src.offset.higher(eff)
|
||||||
|
copy.dst.offset.higher(eff)
|
||||||
self.min_offset.higher(eff)
|
self.min_offset.higher(eff)
|
||||||
self.final_offset.higher(eff)
|
self.final_offset.higher(eff)
|
||||||
|
|
||||||
|
@ -248,8 +290,8 @@ class EffectManager:
|
||||||
vars[eff.name] = eff
|
vars[eff.name] = eff
|
||||||
|
|
||||||
for copy in self.copies:
|
for copy in self.copies:
|
||||||
add(copy.src)
|
add(copy.src.effect)
|
||||||
add(copy.dst)
|
add(copy.dst.effect)
|
||||||
for peek in self.peeks:
|
for peek in self.peeks:
|
||||||
add(peek.effect)
|
add(peek.effect)
|
||||||
for poke in self.pokes:
|
for poke in self.pokes:
|
||||||
|
@ -365,8 +407,11 @@ def write_components(
|
||||||
out.emit(f"// {mgr.instr.name}")
|
out.emit(f"// {mgr.instr.name}")
|
||||||
|
|
||||||
for copy in mgr.copies:
|
for copy in mgr.copies:
|
||||||
if copy.src.name != copy.dst.name:
|
copy_src_effect = copy.src.effect
|
||||||
out.assign(copy.dst, copy.src)
|
if copy_src_effect.name != copy.dst.effect.name:
|
||||||
|
if copy_src_effect.name == UNUSED:
|
||||||
|
copy_src_effect = copy.src.as_stack_effect()
|
||||||
|
out.assign(copy.dst.effect, copy_src_effect)
|
||||||
for peek in mgr.peeks:
|
for peek in mgr.peeks:
|
||||||
out.assign(
|
out.assign(
|
||||||
peek.effect,
|
peek.effect,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue