Skip to content

Commit ac9a33e

Browse files
Merge branch 'main' into fix-150075-tar-addfile-no-offsets
2 parents 1711dd3 + 0828782 commit ac9a33e

5 files changed

Lines changed: 234 additions & 105 deletions

File tree

Lib/test/test_generated_cases.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,75 @@ def test_family_member_needs_transform_only_when_shape_changes(self):
21872187
"[OP_TYPED_SPECIALIZED] = {1, 0, {0}}",
21882188
)
21892189

2190+
def test_record_transform_generated_from_recording_uop(self):
2191+
input = """
2192+
tier2 op(_RECORD_TOS, (tos -- tos)) {
2193+
RECORD_VALUE(PyStackRef_AsPyObjectBorrow(tos));
2194+
}
2195+
tier2 op(_RECORD_TOS_TYPE, (tos -- tos)) {
2196+
RECORD_VALUE(Py_TYPE(PyStackRef_AsPyObjectBorrow(tos)));
2197+
}
2198+
op(_DO_STUFF, (tos -- res)) {
2199+
res = tos;
2200+
}
2201+
macro(OP) = _RECORD_TOS + _DO_STUFF;
2202+
macro(OP_SPECIALIZED) = _RECORD_TOS_TYPE + _DO_STUFF;
2203+
family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED };
2204+
"""
2205+
output = self.generate_tables(input)
2206+
self.assertIn("_PyOpcode_RecordTransform_TOS_TYPE", output)
2207+
self.assertIn("tos = PyStackRef_FromPyObjectBorrow(recorded_value);", output)
2208+
self.assertIn(
2209+
"transformed_value = (PyObject *)Py_TYPE(PyStackRef_AsPyObjectBorrow(tos));",
2210+
output,
2211+
)
2212+
self.assertIn("return _PyOpcode_RecordTransform_TOS_TYPE(value);", output)
2213+
self.assertNotIn("record_trace_transform_to_type", output)
2214+
2215+
def test_record_transform_generated_when_only_specialization_records(self):
2216+
input = """
2217+
tier2 op(_RECORD_TOS_TYPE, (tos -- tos)) {
2218+
RECORD_VALUE(Py_TYPE(PyStackRef_AsPyObjectBorrow(tos)));
2219+
}
2220+
op(_DO_STUFF, (tos -- res)) {
2221+
res = tos;
2222+
}
2223+
macro(OP) = _DO_STUFF;
2224+
macro(OP_SPECIALIZED) = _RECORD_TOS_TYPE + _DO_STUFF;
2225+
family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED };
2226+
"""
2227+
output = self.generate_tables(input)
2228+
# Family head must adopt the specialization's recorder.
2229+
self.assertIn("[OP] = {1, {_RECORD_TOS_TYPE_INDEX}}", output)
2230+
self.assertIn("[OP_SPECIALIZED] = {1, {_RECORD_TOS_TYPE_INDEX}}", output)
2231+
# Specialization consumes the slot directly (mask 0), no transform.
2232+
self.assert_slot_map_lines(output, "[OP_SPECIALIZED] = {1, 0, {0}}")
2233+
self.assertNotIn("_PyOpcode_RecordTransform_TOS_TYPE(", output)
2234+
2235+
def test_no_record_transform_when_only_base_records(self):
2236+
input = """
2237+
tier2 op(_RECORD_TOS, (tos -- tos)) {
2238+
RECORD_VALUE(PyStackRef_AsPyObjectBorrow(tos));
2239+
}
2240+
op(_DO_STUFF, (tos -- res)) {
2241+
res = tos;
2242+
}
2243+
macro(OP) = _RECORD_TOS + _DO_STUFF;
2244+
macro(OP_SPECIALIZED) = _DO_STUFF;
2245+
family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED };
2246+
"""
2247+
output = self.generate_tables(input)
2248+
# Family head records via _RECORD_TOS.
2249+
self.assertIn("[OP] = {1, {_RECORD_TOS_INDEX}}", output)
2250+
self.assertIn("[OP_SPECIALIZED] = {1, {_RECORD_TOS_INDEX}}", output)
2251+
# Specialization has no consumer slot map entry (it doesn't read it).
2252+
self.assertNotIn(
2253+
"[OP_SPECIALIZED] = {1,", self.get_slot_map_section(output)
2254+
)
2255+
# No transform helpers are generated.
2256+
self.assertNotIn("_PyOpcode_RecordTransform_TOS(", output)
2257+
self.assertNotIn("_PyOpcode_RecordTransform_TOS_TYPE", output)
2258+
21902259
def test_family_member_maps_positional_recorders_to_family_slots(self):
21912260
input = """
21922261
tier2 op(_RECORD_TOS, (sub -- sub)) {
@@ -2243,7 +2312,12 @@ def test_family_head_records_union_of_member_recorders(self):
22432312
macro(OP_SPECIALIZED) = _RECORD_TOS + _DO_STUFF;
22442313
family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED };
22452314
"""
2315+
analysis = self.analyze_input(input)
22462316
output = self.generate_tables(input)
2317+
self.assertEqual(
2318+
analysis.families["OP"].get_member_record_names(),
2319+
("_RECORD_TOS",),
2320+
)
22472321
self.assertIn("[OP] = {1, {_RECORD_TOS_INDEX}}", output)
22482322
self.assertIn("[OP_SPECIALIZED] = {1, {_RECORD_TOS_INDEX}}", output)
22492323
self.assert_slot_map_lines(output, "[OP_SPECIALIZED] = {1, 0, {0}}")

Python/optimizer.c

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -663,44 +663,6 @@ is_terminator(const _PyUOpInstruction *uop)
663663
);
664664
}
665665

666-
static PyObject *
667-
record_trace_transform_to_type(PyObject *value)
668-
{
669-
PyObject *tp = Py_NewRef((PyObject *)Py_TYPE(value));
670-
Py_DECREF(value);
671-
return tp;
672-
}
673-
674-
/* _RECORD_NOS_GEN_FUNC and _RECORD_3OS_GEN_FUNC record the raw receiver.
675-
* If it is a generator, return its function object; otherwise return NULL.
676-
*/
677-
static PyObject *
678-
record_trace_transform_gen_func(PyObject *value)
679-
{
680-
PyObject *func = NULL;
681-
if (PyGen_Check(value)) {
682-
_PyStackRef f = ((PyGenObject *)value)->gi_iframe.f_funcobj;
683-
if (!PyStackRef_IsNull(f)) {
684-
func = Py_NewRef(PyStackRef_AsPyObjectBorrow(f));
685-
}
686-
}
687-
Py_DECREF(value);
688-
return func;
689-
}
690-
691-
/* _RECORD_BOUND_METHOD records the raw callable.
692-
* Keep it only for bound methods; otherwise return NULL.
693-
*/
694-
static PyObject *
695-
record_trace_transform_bound_method(PyObject *value)
696-
{
697-
if (Py_TYPE(value) == &PyMethod_Type) {
698-
return value;
699-
}
700-
Py_DECREF(value);
701-
return NULL;
702-
}
703-
704666
/* Returns 1 on success (added to trace), 0 on trace end.
705667
*/
706668
// gh-142543: inlining this function causes stack overflows

Python/record_functions.c.h

Lines changed: 70 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/cases_generator/analyzer.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,16 @@ class Family:
322322
size: str
323323
members: list[Instruction]
324324

325+
def get_member_record_names(self) -> tuple[str, ...]:
326+
seen: set[str] = set()
327+
names: list[str] = []
328+
for member in self.members:
329+
for part in member.parts:
330+
if part.properties.records_value and part.name not in seen:
331+
seen.add(part.name)
332+
names.append(part.name)
333+
return tuple(names)
334+
325335
def dump(self, indent: str) -> None:
326336
print(indent, self.name, "= ", ", ".join([m.name for m in self.members]))
327337

0 commit comments

Comments
 (0)