10000 gh-104909: Implement conditional stack effects for macros (#105748) · python/cpython@4caa728 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4caa728

Browse files
authored
gh-104909: Implement conditional stack effects for macros (#105748)
1 parent 820febc commit 4caa728

File tree

2 files changed

+80
-13
lines changed

2 files changed

+80
-13
lines changed

Tools/cases_generator/generate_cases.py

+40-13
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,12 @@ def block(self, head: str):
179179

180180
def stack_adjust(
181181
self,
182-
diff: int,
183182
input_effects: list[StackEffect],
184183
output_effects: list[StackEffect],
185184
):
186-
# TODO: Get rid of 'diff' parameter
187185
shrink, isym = list_effect_size(input_effects)
188186
grow, osym = list_effect_size(output_effects)
189-
diff += grow - shrink
187+
diff = grow - shrink
190188
if isym and isym != osym:
191189
self.emit(f"STACK_SHRINK({isym});")
192190
if diff < 0:
@@ -355,7 +353,6 @@ def write(self, out: Formatter) -> None:
355353

356354
# Write net stack growth/shrinkage
357355
out.stack_adjust(
358-
0,
359356
[ieff for ieff in self.input_effects],
360357
[oeff for oeff in self.output_effects],
361358
)
@@ -848,9 +845,14 @@ def stack_analysis(
848845
849846
Ignore cache effects.
850847
851-
Return the list of variable names and the initial stack pointer.
848+
Return the list of variables (as StackEffects) and the initial stack pointer.
852849
"""
853850
lowest = current = highest = 0
851+
conditions: dict[int, str] = {} # Indexed by 'current'.
852+
last_instr: Instruction | None = None
853+
for thing in components:
854+
if isinstance(thing, Instruction):
855+
last_instr = thing
854856
for thing in components:
855857
match thing:
856858
case Instruction() as instr:
@@ -863,9 +865,24 @@ def stack_analysis(
863865
"which are not supported in macro instructions",
864866
instr.inst, # TODO: Pass name+location of macro
865867
)
868+
if any(eff.cond for eff in instr.input_effects):
869+
self.error(
870+
f"Instruction {instr.name!r} has conditional input stack effect, "
871+
"which are not supported in macro instructions",
872+
instr.inst, # TODO: Pass name+location of macro
873+
)
874+
if any(eff.cond for eff in instr.output_effects) and instr is not last_instr:
875+
self.error(
876+
f"Instruction {instr.name!r} has conditional output stack effect, "
877+
"but is not the last instruction in a macro",
878+
instr.inst, # TODO: Pass name+location of macro
879+
)
866880
current -= len(instr.input_effects)
867881
lowest = min(lowest, current)
868-
current += len(instr.output_effects)
882+
for eff in instr.output_effects:
883+
if eff.cond:
884+
conditions[current] = eff.cond
885+
current += 1
869886
highest = max(highest, current)
870887
case parser.CacheEffect():
871888
pass
@@ -874,9 +891,9 @@ def stack_analysis(
874891
# At this point, 'current' is the net stack effect,
875892
# and 'lowest' and 'highest' are the extremes.
876893
# Note that 'lowest' may be negative.
877-
# TODO: Reverse the numbering.
878894
stack = [
879-
StackEffect(f"_tmp_{i+1}", "") for i in reversed(range(highest - lowest))
895+
StackEffect(f"_tmp_{i}", "", conditions.get(highest - i, ""))
896+
for i in reversed(range(1, highest - lowest + 1))
880897
]
881898
return stack, -lowest
882899

@@ -908,15 +925,17 @@ def effect_str(effects: list[StackEffect]) -> str:
908925
low = 0
909926
sp = 0
910927
high = 0
928+
pushed_symbolic: list[str] = []
911929
for comp in parts:
912930
for effect in comp.instr.input_effects:
913931
assert not effect.cond, effect
914932
assert not effect.size, effect
915933
sp -= 1
916934
low = min(low, sp)
917935
for effect in comp.instr.output_effects:
918-
assert not effect.cond, effect
919936
assert not effect.size, effect
937+
if effect.cond:
938+
pushed_symbolic.append(maybe_parenthesize(f"{maybe_parenthesize(effect.cond)} ? 1 : 0"))
920939
sp += 1
921940
high = max(sp, high)
922941
if high != max(0, sp):
@@ -926,7 +945,8 @@ def effect_str(effects: list[StackEffect]) -> str:
926945
# calculations to use the micro ops.
927946
self.error("Macro has virtual stack growth", thing)
928947
popped = str(-low)
929-
pushed = str(sp - low)
948+
pushed_symbolic.append(str(sp - low - len(pushed_symbolic)))
949+
pushed = " + ".join(pushed_symbolic)
930950
case parser.Pseudo():
931951
instr = self.pseudo_instrs[thing.name]
932952
popped = pushed = None
@@ -1203,16 +1223,23 @@ def wrap_macro(self, mac: MacroInstruction):
12031223
with self.out.block(f"TARGET({mac.name})"):
12041224
if mac.predicted:
12051225
self.out.emit(f"PREDICTED({mac.name});")
1206-
for i, var in reversed(list(enumerate(mac.stack))):
1226+
1227+
# The input effects should have no conditionals.
1228+
# Only the output effects do (for now).
1229+
ieffects = [
1230+
StackEffect(eff.name, eff.type) if eff.cond else eff
1231+
for eff in mac.stack
1232+
]
1233+
1234+
for i, var in reversed(list(enumerate(ieffects))):
12071235
src = None
12081236
if i < mac.initial_sp:
12091237
src = StackEffect(f"stack_pointer[-{mac.initial_sp - i}]", "")
12101238
self.out.declare(var, src)
12111239

12121240
yield
12131241

1214-
# TODO: Use slices of mac.stack instead of numeric values
1215-
self.out.stack_adjust(mac.final_sp - mac.initial_sp, [], [])
1242+
self.out.stack_adjust(ieffects[:mac.initial_sp], mac.stack[:mac.final_sp])
12161243

12171244
for i, var in enumerate(reversed(mac.stack[: mac.final_sp]), 1):
12181245
dst = StackEffect(f"stack_pointer[-{i}]", "")

Tools/cases_generator/test_generator.py

+40
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,43 @@ def test_cond_effect():
432432
}
433433
"""
434434
run_cases_test(input, output)
435+
436+
def test_macro_cond_effect():
437+
input = """
438+
op(A, (left, middle, right --)) {
439+
# Body of A
440+
}
441+
op(B, (-- deep, extra if (oparg), res)) {
442+
# Body of B
443+
}
444+
macro(M) = A + B;
445+
"""
446+
output = """
447+
TARGET(M) {
448+
PyObject *_tmp_1 = stack_pointer[-1];
449+
PyObject *_tmp_2 = stack_pointer[-2];
450+
PyObject *_tmp_3 = stack_pointer[-3];
451+
{
452+
PyObject *right = _tmp_1;
453+
PyObject *middle = _tmp_2;
454+
PyObject *left = _tmp_3;
455+
# Body of A
456+
}
457+
{
458+
PyObject *deep;
459+
PyObject *extra = NULL;
460+
PyObject *res;
461+
# Body of B
462+
_tmp_3 = deep;
463+
if (oparg) { _tmp_2 = extra; }
464+
_tmp_1 = res;
465+
}
466+
STACK_SHRINK(1);
467+
STACK_GROW((oparg ? 1 : 0));
468+
stack_pointer[-1] = _tmp_1;
469+
if (oparg) { stack_pointer[-2] = _tmp_2; }
470+
stack_pointer[-3] = _tmp_3;
471+
DISPATCH();
472+
}
473+
"""
474+
run_cases_test(input, output)

0 commit comments

Comments
 (0)
0