Skip to content

Commit 2575e1d

Browse files
authored
Merge branch 'main' into gh-149819-fix-pth-subprocess
2 parents 78094f8 + 65f9932 commit 2575e1d

27 files changed

Lines changed: 467 additions & 148270 deletions

Include/internal/pycore_magic_number.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ Known values:
298298
Python 3.15a8 3665 (Add FOR_ITER_VIRTUAL and GET_ITER specializations)
299299
Python 3.15b1 3666 (Add SEND_VIRTUAL and SEND_ASYNC_GEN specializations)
300300
Python 3.16a0 3700 (Initial version)
301+
Python 3.16a0 3701 (Add CONSTANT_EMPTY_TUPLE to LOAD_COMMON_CONSTANT)
301302
302303
303304
Python 3.17 will start with 3750
@@ -311,7 +312,7 @@ PC/launcher.c must also be updated.
311312
312313
*/
313314

314-
#define PYC_MAGIC_NUMBER 3700
315+
#define PYC_MAGIC_NUMBER 3701
315316
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
316317
(little-endian) and then appending b'\r\n'. */
317318
#define PYC_MAGIC_NUMBER_TOKEN \

Include/internal/pycore_opcode_utils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ extern "C" {
8181
#define CONSTANT_FALSE 10
8282
#define CONSTANT_MINUS_ONE 11
8383
#define CONSTANT_BUILTIN_FROZENSET 12
84-
#define NUM_COMMON_CONSTANTS 13
84+
#define CONSTANT_EMPTY_TUPLE 13
85+
#define NUM_COMMON_CONSTANTS 14
8586

8687
/* Values used in the oparg for RESUME */
8788
#define RESUME_AT_FUNC_START 0

Include/internal/pycore_optimizer.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ extern "C" {
3131
* 4. A push followed by a matching return is net-zero on frame-specific
3232
* fitness, excluding per-slot costs.
3333
*/
34-
#define MAX_TARGET_LENGTH (UOP_MAX_TRACE_LENGTH / 2)
3534
#define OPTIMIZER_EFFECTIVENESS 2
36-
#define FITNESS_INITIAL (MAX_TARGET_LENGTH * OPTIMIZER_EFFECTIVENESS)
35+
#define MAX_TARGET_LENGTH (FITNESS_INITIAL / OPTIMIZER_EFFECTIVENESS)
3736

3837
/* Exit quality thresholds: trace stops when fitness < exit_quality.
3938
* Higher = trace is more willing to stop here. */

Include/internal/pycore_uop.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,18 @@ typedef struct _PyUOpInstruction{
3636
#endif
3737
} _PyUOpInstruction;
3838

39-
// This is the length of the trace we translate initially.
39+
// Fitness is the target length of the trace we translate initially. The uop
40+
// buffer has a small amount of extra space for entry/loop-closing overhead.
4041
#if defined(Py_DEBUG) && defined(_Py_JIT)
4142
// With asserts, the stencils are a lot larger
42-
#define UOP_MAX_TRACE_LENGTH 1000
43+
#define FITNESS_INITIAL 1000
4344
#else
44-
#define UOP_MAX_TRACE_LENGTH 2500
45+
#define FITNESS_INITIAL 2500
4546
#endif
4647

48+
#define UOP_TRACE_BUFFER_OVERHEAD 10
49+
#define UOP_MAX_TRACE_LENGTH (FITNESS_INITIAL + UOP_TRACE_BUFFER_OVERHEAD)
50+
4751
/* Bloom filter with m = 256
4852
* https://en.wikipedia.org/wiki/Bloom_filter */
4953
#ifdef HAVE_GCC_UINT128_T

Lib/dis.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,8 +698,8 @@ def _get_const_value(op, arg, co_consts):
698698
if op == LOAD_SMALL_INT:
699699
return arg
700700
if op == LOAD_COMMON_CONSTANT:
701-
# Opargs 0-6 are callables; 7-11 are literal values.
702-
if 7 <= arg <= 11:
701+
# Opargs 0-6 and 12 are callables; 7-11 and 13 are literal values.
702+
if 7 <= arg <= 11 or arg == 13:
703703
return _common_constants[arg]
704704
return UNKNOWN
705705
argval = UNKNOWN

Lib/opcode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
builtins.set,
4545
# Append-only — must match CONSTANT_* in
4646
# Include/internal/pycore_opcode_utils.h.
47-
None, "", True, False, -1, builtins.frozenset]
47+
None, "", True, False, -1, builtins.frozenset, ()]
4848
_nb_ops = _opcode.get_nb_ops()
4949

5050
hascompare = [opmap["COMPARE_OP"]]

Lib/tarfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ def _init_read_gz(self):
498498

499499
if flag & 4:
500500
xlen = ord(self.__read(1)) + 256 * ord(self.__read(1))
501-
self.read(xlen)
501+
self.__read(xlen)
502502
if flag & 8:
503503
while True:
504504
s = self.__read(1)

Lib/test/test_capi/test_opt.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5924,6 +5924,44 @@ def __next__(self):
59245924
"""), PYTHON_JIT="1", PYTHON_JIT_STRESS="1")
59255925
self.assertEqual(result[0].rc, 0, result)
59265926

5927+
def test_149335_trace_buffer_guard(self):
5928+
# https://github.com/python/cpython/issues/149335
5929+
5930+
result = script_helper.run_python_until_end('-c', textwrap.dedent("""
5931+
import sys
5932+
5933+
def f1():
5934+
for i_3178 in 0, 2, 10:
5935+
mv162 = 162
5936+
5937+
mv3 = mv1 = mv_165 = mv16 = \
5938+
mv167 = mv168 = \
5939+
mv169 = \
5940+
mv_1403_170 = \
5941+
169
5942+
5943+
mv_1403_170
5944+
5945+
mv_172 = mv_3 = mv_4 = mv175 = mv176 = mv17 = mv178 = mv179 = mv0 = mv1 = mv182 = (
5946+
mv3
5947+
) = mv4 = mv185 = mv186 = mv187 = mv18 = mv189 = mv0 = mv1 = mv192 = mv3 = mv4 = (
5948+
mv195
5949+
) = mv196 = mv197 = mv_198 = mv19 = mv0 = mv1 = mv2 = mv3 = mv4 = mv05 = mv06 = (
5950+
mv07
5951+
) = mv08 = mv09 = mv0 = mv1 = mv2 = mv3 = mv4 = mv15 = mv16 = mv17 = mv18 = mv19 = (
5952+
mv0
5953+
) = mv1 = mv_2 = mv3 = mv4 = mv_25 = mv_26 = mv_27 = mv_28 = mv_29 = mv0 = mv1 = (
5954+
mv2
5955+
) = mv_1403 = mv4 = mv35 = mv36 = mv37 = mv38 = mv39 = mv0 = -sys.maxsize / 3
5956+
5957+
mv1 = mv_12 = mv3 = mv_14 = mv45 = sys.float_info.epsilon
5958+
mv46 = sys.float_info.epsilon
5959+
5960+
for i in range(15000):
5961+
f1()
5962+
"""), PYTHON_JIT="1")
5963+
self.assertEqual(result[0].rc, 0, result)
5964+
59275965
def test_144068_daemon_thread_jit_cleanup(self):
59285966
result = script_helper.run_python_until_end('-c', textwrap.dedent("""
59295967
import threading

Lib/test/test_crossinterp.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ def ignore_byteswarning():
157157
{},
158158
{1: 7, 2: 8, 3: 9},
159159
{1: [1], 2: (2,), 3: {3: 4}},
160+
# frozendict
161+
frozendict(),
162+
frozendict({1: 7, 2: 8, 3: 9}),
163+
frozendict({1: [1], 2: (2,), 3: {3: 4}, 4: frozendict({5: 6})}),
160164
# set
161165
set(),
162166
{1, 2, 3},

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}}")

0 commit comments

Comments
 (0)