Skip to content

Commit b87263b

Browse files
authored
gh-106581: Support multiple uops pushing new values (#108895)
Also avoid the need for the awkward .clone() call in the argument to mgr.adjust_inverse() and mgr.adjust().
1 parent 2c4c26c commit b87263b

File tree

2 files changed

+83
-17
lines changed

2 files changed

+83
-17
lines changed

Lib/test/test_generated_cases.py

+30
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,36 @@ def test_macro_cond_effect(self):
532532
"""
533533
self.run_cases_test(input, output)
534534

535+
def test_macro_push_push(self):
536+
input = """
537+
op(A, (-- val1)) {
538+
val1 = spam();
539+
}
540+
op(B, (-- val2)) {
541+
val2 = spam();
542+
}
543+
macro(M) = A + B;
544+
"""
545+
output = """
546+
TARGET(M) {
547+
PyObject *val1;
548+
PyObject *val2;
549+
// A
550+
{
551+
val1 = spam();
552+
}
553+
// B
554+
{
555+
val2 = spam();
556+
}
557+
STACK_GROW(2);
558+
stack_pointer[-2] = val1;
559+
stack_pointer[-1] = val2;
560+
DISPATCH();
561+
}
562+
"""
563+
self.run_cases_test(input, output)
564+
535565

536566
if __name__ == "__main__":
537567
unittest.main()

Tools/cases_generator/stacking.py

+53-17
Original file line numberDiff line numberDiff line change
@@ -261,15 +261,19 @@ def adjust_higher(self, eff: StackEffect) -> None:
261261
self.final_offset.higher(eff)
262262

263263
def adjust(self, offset: StackOffset) -> None:
264-
for down in offset.deep:
264+
deep = list(offset.deep)
265+
high = list(offset.high)
266+
for down in deep:
265267
self.adjust_deeper(down)
266-
for up in offset.high:
268+
for up in high:
267269
self.adjust_higher(up)
268270

269271
def adjust_inverse(self, offset: StackOffset) -> None:
270-
for down in offset.deep:
272+
deep = list(offset.deep)
273+
high = list(offset.high)
274+
for down in deep:
271275
self.adjust_higher(down)
272-
for up in offset.high:
276+
for up in high:
273277
self.adjust_deeper(up)
274278

275279
def collect_vars(self) -> dict[str, StackEffect]:
@@ -426,15 +430,26 @@ def write_components(
426430
)
427431

428432
if mgr.instr.name in ("_PUSH_FRAME", "_POP_FRAME"):
429-
# Adjust stack to min_offset (input effects materialized)
433+
# Adjust stack to min_offset.
434+
# This means that all input effects of this instruction
435+
# are materialized, but not its output effects.
436+
# That's as intended, since these two are so special.
430437
out.stack_adjust(mgr.min_offset.deep, mgr.min_offset.high)
431-
# Use clone() since adjust_inverse() mutates final_offset
432-
mgr.adjust_inverse(mgr.final_offset.clone())
438+
# However, for tier 2, pretend the stack is at final offset.
439+
mgr.adjust_inverse(mgr.final_offset)
440+
if tier == TIER_ONE:
441+
# TODO: Check in analyzer that _{PUSH,POP}_FRAME is last.
442+
assert (
443+
mgr is managers[-1]
444+
), f"Expected {mgr.instr.name!r} to be the last uop"
445+
assert_no_pokes(managers)
433446

434447
if mgr.instr.name == "SAVE_CURRENT_IP":
435448
next_instr_is_set = True
436449
if cache_offset:
437450
out.emit(f"next_instr += {cache_offset};")
451+
if tier == TIER_ONE:
452+
assert_no_pokes(managers)
438453

439454
if len(parts) == 1:
440455
mgr.instr.write_body(out, 0, mgr.active_caches, tier)
@@ -443,19 +458,41 @@ def write_components(
443458
mgr.instr.write_body(out, -4, mgr.active_caches, tier)
444459

445460
if mgr is managers[-1] and not next_instr_is_set:
446-
# TODO: Explain why this adjustment is needed.
461+
# Adjust the stack to its final depth, *then* write the
462+
# pokes for all preceding uops.
463+
# Note that for array output effects we may still write
464+
# past the stack top.
447465
out.stack_adjust(mgr.final_offset.deep, mgr.final_offset.high)
448-
# Use clone() since adjust_inverse() mutates final_offset
449-
mgr.adjust_inverse(mgr.final_offset.clone())
466+
write_all_pokes(mgr.final_offset, managers, out)
450467

468+
return next_instr_is_set
469+
470+
471+
def assert_no_pokes(managers: list[EffectManager]) -> None:
472+
for mgr in managers:
451473
for poke in mgr.pokes:
452474
if not poke.effect.size and poke.effect.name not in mgr.instr.unmoved_names:
453-
out.assign(
454-
poke.as_stack_effect(),
455-
poke.effect,
456-
)
475+
assert (
476+
poke.effect.name == UNUSED
477+
), f"Unexpected poke of {poke.effect.name} in {mgr.instr.name!r}"
457478

458-
return next_instr_is_set
479+
480+
def write_all_pokes(
481+
offset: StackOffset, managers: list[EffectManager], out: Formatter
482+
) -> None:
483+
# Emit all remaining pushes (pokes)
484+
for m in managers:
485+
m.adjust_inverse(offset)
486+
write_pokes(m, out)
487+
488+
489+
def write_pokes(mgr: EffectManager, out: Formatter) -> None:
490+
for poke in mgr.pokes:
491+
if not poke.effect.size and poke.effect.name not in mgr.instr.unmoved_names:
492+
out.assign(
493+
poke.as_stack_effect(),
494+
poke.effect,
495+
)
459496

460497

461498
def write_single_instr_for_abstract_interp(instr: Instruction, out: Formatter) -> None:
@@ -478,8 +515,7 @@ def _write_components_for_abstract_interp(
478515
for mgr in managers:
479516
if mgr is managers[-1]:
480517
out.stack_adjust(mgr.final_offset.deep, mgr.final_offset.high)
481-
# Use clone() since adjust_inverse() mutates final_offset
482-
mgr.adjust_inverse(mgr.final_offset.clone())
518+
mgr.adjust_inverse(mgr.final_offset)
483519
# NULL out the output stack effects
484520
for poke in mgr.pokes:
485521
if not poke.effect.size and poke.effect.name not in mgr.instr.unmoved_names:

0 commit comments

Comments
 (0)