Skip to content

Commit 569fc68

Browse files
gh-134584: Specialize POP_TOP by reference and type in JIT (GH-135761)
1 parent 99712c4 commit 569fc68

File tree

12 files changed

+239
-62
lines changed

12 files changed

+239
-62
lines changed

Include/internal/pycore_uop_ids.h

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

Include/internal/pycore_uop_metadata.h

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

Lib/test/test_capi/test_opt.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2392,6 +2392,46 @@ def testfunc(n):
23922392
assert ex is not None
23932393
"""))
23942394

2395+
def test_pop_top_specialize_none(self):
2396+
def testfunc(n):
2397+
for _ in range(n):
2398+
global_identity(None)
2399+
2400+
testfunc(TIER2_THRESHOLD)
2401+
2402+
ex = get_first_executor(testfunc)
2403+
self.assertIsNotNone(ex)
2404+
uops = get_opnames(ex)
2405+
2406+
self.assertIn("_POP_TOP_NOP", uops)
2407+
2408+
def test_pop_top_specialize_int(self):
2409+
def testfunc(n):
2410+
for _ in range(n):
2411+
global_identity(100000)
2412+
2413+
testfunc(TIER2_THRESHOLD)
2414+
2415+
ex = get_first_executor(testfunc)
2416+
self.assertIsNotNone(ex)
2417+
uops = get_opnames(ex)
2418+
2419+
self.assertIn("_POP_TOP_INT", uops)
2420+
2421+
def test_pop_top_specialize_float(self):
2422+
def testfunc(n):
2423+
for _ in range(n):
2424+
global_identity(1e6)
2425+
2426+
testfunc(TIER2_THRESHOLD)
2427+
2428+
ex = get_first_executor(testfunc)
2429+
self.assertIsNotNone(ex)
2430+
uops = get_opnames(ex)
2431+
2432+
self.assertIn("_POP_TOP_FLOAT", uops)
2433+
2434+
23952435

23962436
def global_identity(x):
23972437
return x
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Specialize :opcode:`POP_TOP` in the JIT compiler by specializing for reference lifetime and type. This will also enable easier top of stack caching in the JIT compiler.

Python/bytecodes.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,27 @@ dummy_func(
344344
PyStackRef_XCLOSE(value);
345345
}
346346

347+
op(_POP_TOP_NOP, (value --)) {
348+
assert(PyStackRef_IsNull(value) || (!PyStackRef_RefcountOnObject(value)) ||
349+
_Py_IsImmortal((PyStackRef_AsPyObjectBorrow(value))));
350+
DEAD(value);
351+
}
352+
353+
op(_POP_TOP_INT, (value --)) {
354+
assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value)));
355+
PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc);
356+
}
357+
358+
op(_POP_TOP_FLOAT, (value --)) {
359+
assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value)));
360+
PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc);
361+
}
362+
363+
op(_POP_TOP_UNICODE, (value --)) {
364+
assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value)));
365+
PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc);
366+
}
367+
347368
tier2 op(_POP_TWO, (nos, tos --)) {
348369
PyStackRef_CLOSE(tos);
349370
PyStackRef_CLOSE(nos);

Python/executor_cases.c.h

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

Python/optimizer_analysis.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
345345
#define sym_new_tuple _Py_uop_sym_new_tuple
346346
#define sym_tuple_getitem _Py_uop_sym_tuple_getitem
347347
#define sym_tuple_length _Py_uop_sym_tuple_length
348-
#define sym_is_immortal _Py_uop_sym_is_immortal
348+
#define sym_is_immortal _Py_uop_symbol_is_immortal
349349
#define sym_is_compact_int _Py_uop_sym_is_compact_int
350350
#define sym_new_compact_int _Py_uop_sym_new_compact_int
351351
#define sym_new_truthiness _Py_uop_sym_new_truthiness

Python/optimizer_bytecodes.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
3434
#define sym_new_tuple _Py_uop_sym_new_tuple
3535
#define sym_tuple_getitem _Py_uop_sym_tuple_getitem
3636
#define sym_tuple_length _Py_uop_sym_tuple_length
37-
#define sym_is_immortal _Py_uop_sym_is_immortal
37+
#define sym_is_immortal _Py_uop_symbol_is_immortal
3838
#define sym_new_compact_int _Py_uop_sym_new_compact_int
3939
#define sym_is_compact_int _Py_uop_sym_is_compact_int
4040
#define sym_new_truthiness _Py_uop_sym_new_truthiness
@@ -534,15 +534,15 @@ dummy_func(void) {
534534
}
535535

536536
op(_LOAD_CONST_INLINE, (ptr/4 -- value)) {
537-
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
537+
value = sym_new_const(ctx, ptr);
538538
}
539539

540540
op(_LOAD_CONST_INLINE_BORROW, (ptr/4 -- value)) {
541541
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
542542
}
543543

544544
op(_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) {
545-
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
545+
value = sym_new_const(ctx, ptr);
546546
}
547547

548548
op(_POP_TOP_LOAD_CONST_INLINE_BORROW, (ptr/4, pop -- value)) {
@@ -561,6 +561,24 @@ dummy_func(void) {
561561
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
562562
}
563563

564+
op(_POP_TOP, (value -- )) {
565+
PyTypeObject *typ = sym_get_type(value);
566+
if (PyJitRef_IsBorrowed(value) ||
567+
sym_is_immortal(PyJitRef_Unwrap(value)) ||
568+
sym_is_null(value)) {
569+
REPLACE_OP(this_instr, _POP_TOP_NOP, 0, 0);
570+
}
571+
else if (typ == &PyLong_Type) {
572+
REPLACE_OP(this_instr, _POP_TOP_INT, 0, 0);
573+
}
574+
else if (typ == &PyFloat_Type) {
575+
REPLACE_OP(this_instr, _POP_TOP_FLOAT, 0, 0);
576+
}
577+
else if (typ == &PyUnicode_Type) {
578+
REPLACE_OP(this_instr, _POP_TOP_UNICODE, 0, 0);
579+
}
580+
}
581+
564582
op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) {
565583
assert(oparg > 0);
566584
top = bottom;
@@ -803,7 +821,9 @@ dummy_func(void) {
803821
}
804822

805823
op(_RETURN_VALUE, (retval -- res)) {
806-
JitOptRef temp = retval;
824+
// We wrap and unwrap the value to mimic PyStackRef_MakeHeapSafe
825+
// in bytecodes.c
826+
JitOptRef temp = PyJitRef_Wrap(PyJitRef_Unwrap(retval));
807827
DEAD(retval);
808828
SAVE_STACK();
809829
ctx->frame->stack_pointer = stack_pointer;

0 commit comments

Comments
 (0)