summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2023-01-30 19:23:57 (GMT)
committerGitHub <noreply@github.com>2023-01-30 19:23:57 (GMT)
commit7a3752338a2bfea023fcb119c842750fe799262f (patch)
treea6bf179f49de2bf72445638f2dddc7af9e85ef39
parente11fc032a75d067d2167a21037722a770b9dfb51 (diff)
downloadcpython-7a3752338a2bfea023fcb119c842750fe799262f.zip
cpython-7a3752338a2bfea023fcb119c842750fe799262f.tar.gz
cpython-7a3752338a2bfea023fcb119c842750fe799262f.tar.bz2
GH-101369: Allow macros as family members (#101399)
Also check for instructions straddling families (this includes macro parts).
-rw-r--r--Tools/cases_generator/generate_cases.py75
-rw-r--r--Tools/cases_generator/test_generator.py54
2 files changed, 93 insertions, 36 deletions
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 1d703a0..f0c5f96 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -426,10 +426,12 @@ class Component:
def write_body(self, out: Formatter, cache_adjust: int) -> None:
with out.block(""):
+ input_names = {ieffect.name for _, ieffect in self.input_mapping}
for var, ieffect in self.input_mapping:
out.declare(ieffect, var)
for _, oeffect in self.output_mapping:
- out.declare(oeffect, None)
+ if oeffect.name not in input_names:
+ out.declare(oeffect, None)
self.instr.write_body(out, dedent=-4, cache_adjust=cache_adjust)
@@ -565,10 +567,10 @@ class Analyzer:
Raises SystemExit if there is an error.
"""
self.find_predictions()
- self.map_families()
- self.check_families()
self.analyze_register_instrs()
self.analyze_supers_and_macros()
+ self.map_families()
+ self.check_families()
def find_predictions(self) -> None:
"""Find the instructions that need PREDICTED() labels."""
@@ -587,11 +589,30 @@ class Analyzer:
)
def map_families(self) -> None:
- """Make instruction names back to their family, if they have one."""
+ """Link instruction names back to their family, if they have one."""
for family in self.families.values():
for member in family.members:
if member_instr := self.instrs.get(member):
- member_instr.family = family
+ if member_instr.family not in (family, None):
+ self.error(
+ f"Instruction {member} is a member of multiple families "
+ f"({member_instr.family.name}, {family.name}).",
+ family
+ )
+ else:
+ member_instr.family = family
+ elif member_macro := self.macro_instrs.get(member):
+ for part in member_macro.parts:
+ if isinstance(part, Component):
+ if part.instr.family not in (family, None):
+ self.error(
+ f"Component {part.instr.name} of macro {member} "
+ f"is a member of multiple families "
+ f"({part.instr.family.name}, {family.name}).",
+ family
+ )
+ else:
+ part.instr.family = family
else:
self.error(
f"Unknown instruction {member!r} referenced in family {family.name!r}",
@@ -608,7 +629,7 @@ class Analyzer:
for family in self.families.values():
if len(family.members) < 2:
self.error(f"Family {family.name!r} has insufficient members", family)
- members = [member for member in family.members if member in self.instrs]
+ members = [member for member in family.members if member in self.instrs or member in self.macro_instrs]
if members != family.members:
unknown = set(family.members) - set(members)
self.error(
@@ -616,24 +637,42 @@ class Analyzer:
)
if len(members) < 2:
continue
- head = self.instrs[members[0]]
- cache = head.cache_offset
- input = len(head.input_effects)
- output = len(head.output_effects)
+ expected_effects = self.effect_counts(members[0])
for member in members[1:]:
- instr = self.instrs[member]
- c = instr.cache_offset
- i = len(instr.input_effects)
- o = len(instr.output_effects)
- if (c, i, o) != (cache, input, output):
+ member_effects = self.effect_counts(member)
+ if member_effects != expected_effects:
self.error(
f"Family {family.name!r} has inconsistent "
- f"(cache, inputs, outputs) effects:\n"
- f" {family.members[0]} = {(cache, input, output)}; "
- f"{member} = {(c, i, o)}",
+ f"(cache, input, output) effects:\n"
+ f" {family.members[0]} = {expected_effects}; "
+ f"{member} = {member_effects}",
family,
)
+ def effect_counts(self, name: str) -> tuple[int, int, int]:
+ if instr := self.instrs.get(name):
+ cache = instr.cache_offset
+ input = len(instr.input_effects)
+ output = len(instr.output_effects)
+ elif macro := self.macro_instrs.get(name):
+ cache, input, output = 0, 0, 0
+ for part in macro.parts:
+ if isinstance(part, Component):
+ cache += part.instr.cache_offset
+ # A component may pop what the previous component pushed,
+ # so we offset the input/output counts by that.
+ delta_i = len(part.instr.input_effects)
+ delta_o = len(part.instr.output_effects)
+ offset = min(delta_i, output)
+ input += delta_i - offset
+ output += delta_o - offset
+ else:
+ assert isinstance(part, parser.CacheEffect), part
+ cache += part.size
+ else:
+ assert False, f"Unknown instruction {name!r}"
+ return cache, input, output
+
def analyze_register_instrs(self) -> None:
for instr in self.instrs.values():
if instr.register:
diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py
index 6e6c607..49a9937 100644
--- a/Tools/cases_generator/test_generator.py
+++ b/Tools/cases_generator/test_generator.py
@@ -339,21 +339,24 @@ def test_super_instruction():
def test_macro_instruction():
input = """
- inst(OP1, (counter/1, arg1 -- interim)) {
- interim = op1(arg1);
+ inst(OP1, (counter/1, left, right -- left, right)) {
+ op1(left, right);
}
- op(OP2, (extra/2, arg2, interim -- res)) {
- res = op2(arg2, interim);
+ op(OP2, (extra/2, arg2, left, right -- res)) {
+ res = op2(arg2, left, right);
}
macro(OP) = OP1 + cache/2 + OP2;
+ inst(OP3, (unused/5, arg2, left, right -- res)) {
+ res = op3(arg2, left, right);
+ }
+ family(op, INLINE_CACHE_ENTRIES_OP) = { OP, OP3 };
"""
output = """
TARGET(OP1) {
- PyObject *arg1 = PEEK(1);
- PyObject *interim;
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
uint16_t counter = read_u16(&next_instr[0].cache);
- interim = op1(arg1);
- POKE(1, interim);
+ op1(left, right);
JUMPBY(1);
DISPATCH();
}
@@ -361,24 +364,39 @@ def test_macro_instruction():
TARGET(OP) {
PyObject *_tmp_1 = PEEK(1);
PyObject *_tmp_2 = PEEK(2);
+ PyObject *_tmp_3 = PEEK(3);
{
- PyObject *arg1 = _tmp_1;
- PyObject *interim;
+ PyObject *right = _tmp_1;
+ PyObject *left = _tmp_2;
uint16_t counter = read_u16(&next_instr[0].cache);
- interim = op1(arg1);
- _tmp_1 = interim;
+ op1(left, right);
+ _tmp_2 = left;
+ _tmp_1 = right;
}
{
- PyObject *interim = _tmp_1;
- PyObject *arg2 = _tmp_2;
+ PyObject *right = _tmp_1;
+ PyObject *left = _tmp_2;
+ PyObject *arg2 = _tmp_3;
PyObject *res;
uint32_t extra = read_u32(&next_instr[3].cache);
- res = op2(arg2, interim);
- _tmp_2 = res;
+ res = op2(arg2, left, right);
+ _tmp_3 = res;
}
JUMPBY(5);
- STACK_SHRINK(1);
- POKE(1, _tmp_2);
+ STACK_SHRINK(2);
+ POKE(1, _tmp_3);
+ DISPATCH();
+ }
+
+ TARGET(OP3) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *arg2 = PEEK(3);
+ PyObject *res;
+ res = op3(arg2, left, right);
+ STACK_SHRINK(2);
+ POKE(1, res);
+ JUMPBY(5);
DISPATCH();
}
"""