https://github.com/python/cpython/commit/d2fa4b2b13f3c78649599d71f68df7390c1d9261
commit: d2fa4b2b13f3c78649599d71f68df7390c1d9261
branch: main
author: Neko Asakura <[email protected]>
committer: Fidget-Spinner <[email protected]>
date: 2026-04-08T23:20:31+08:00
summary:
gh-148211: decompose `_POP_TOP_LOAD_CONST_INLINE(_BORROW)` in JIT (GH-148230)
files:
M Lib/test/test_capi/test_opt.py
M Python/optimizer_analysis.c
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
M Tools/cases_generator/optimizer_generator.py
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 124a2c0115769c..5d512f19b96933 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -1470,8 +1470,8 @@ class Bar:
opnames = list(iter_opnames(ex))
self.assertEqual(res, TIER2_THRESHOLD * 2 + 2)
call = opnames.index("_CALL_BUILTIN_FAST")
- load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0,
call)
- load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW",
call)
+ load_attr_top = opnames.index("_LOAD_CONST_INLINE_BORROW", 0, call)
+ load_attr_bottom = opnames.index("_LOAD_CONST_INLINE_BORROW", call)
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"),
1)
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2)
@@ -1493,8 +1493,8 @@ class Foo:
self.assertIsNotNone(ex)
self.assertEqual(res, TIER2_THRESHOLD * 2)
call = opnames.index("_CALL_BUILTIN_FAST_WITH_KEYWORDS")
- load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0,
call)
- load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW",
call)
+ load_attr_top = opnames.index("_LOAD_CONST_INLINE_BORROW", 0, call)
+ load_attr_bottom = opnames.index("_LOAD_CONST_INLINE_BORROW", call)
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"),
1)
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2)
diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c
index 3f0aabed10e1db..ed1619df2a188b 100644
--- a/Python/optimizer_analysis.c
+++ b/Python/optimizer_analysis.c
@@ -155,7 +155,7 @@ type_watcher_callback(PyTypeObject* type)
}
static PyObject *
-convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop, bool
insert)
+convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool insert)
{
assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode ==
_LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE);
assert(PyDict_CheckExact(obj));
@@ -183,9 +183,9 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject
*obj, bool pop, bool i
}
} else {
if (_Py_IsImmortal(res)) {
- inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE_BORROW :
_LOAD_CONST_INLINE_BORROW;
+ inst->opcode = _LOAD_CONST_INLINE_BORROW;
} else {
- inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE :
_LOAD_CONST_INLINE;
+ inst->opcode = _LOAD_CONST_INLINE;
}
if (inst->oparg & 1) {
assert(inst[1].opcode == _PUSH_NULL_CONDITIONAL);
@@ -340,10 +340,12 @@ optimize_to_bool(
int truthiness = sym_truthiness(ctx, value);
if (truthiness >= 0) {
PyObject *load = truthiness ? Py_True : Py_False;
- int opcode = insert_mode ?
- _INSERT_1_LOAD_CONST_INLINE_BORROW :
- _POP_TOP_LOAD_CONST_INLINE_BORROW;
- ADD_OP(opcode, 0, (uintptr_t)load);
+ if (insert_mode) {
+ ADD_OP(_INSERT_1_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
+ } else {
+ ADD_OP(_POP_TOP, 0, 0);
+ ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
+ }
*result_ptr = sym_new_const(ctx, load);
return 1;
}
@@ -389,19 +391,22 @@ eliminate_pop_guard(_PyUOpInstruction *this_instr,
JitOptContext *ctx, bool exit
static JitOptRef
lookup_attr(JitOptContext *ctx, _PyBloomFilter *dependencies,
_PyUOpInstruction *this_instr,
- PyTypeObject *type, PyObject *name, uint16_t immortal,
- uint16_t mortal)
+ PyTypeObject *type, PyObject *name, bool pop)
{
// The cached value may be dead, so we need to do the lookup again... :(
if (type && PyType_Check(type)) {
PyObject *lookup = _PyType_Lookup(type, name);
if (lookup) {
- int opcode = mortal;
- // if the object is immortal or the type is immutable, borrowing
is safe
- if (_Py_IsImmortal(lookup) || (type->tp_flags &
Py_TPFLAGS_IMMUTABLETYPE)) {
- opcode = immortal;
+ bool immortal = _Py_IsImmortal(lookup) || (type->tp_flags &
Py_TPFLAGS_IMMUTABLETYPE);
+ if (pop) {
+ ADD_OP(_POP_TOP, 0, 0);
+ ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW :
_LOAD_CONST_INLINE,
+ 0, (uintptr_t)lookup);
+ }
+ else {
+ ADD_OP(immortal ? _INSERT_1_LOAD_CONST_INLINE_BORROW :
_INSERT_1_LOAD_CONST_INLINE,
+ 0, (uintptr_t)lookup);
}
- ADD_OP(opcode, 0, (uintptr_t)lookup);
PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
_Py_BloomFilter_Add(dependencies, type);
return sym_new_const(ctx, lookup);
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 7a0aac11890306..e10891bf9697af 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -757,14 +757,6 @@ dummy_func(void) {
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
}
- op(_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) {
- value = sym_new_const(ctx, ptr);
- }
-
- op(_POP_TOP_LOAD_CONST_INLINE_BORROW, (ptr/4, pop -- value)) {
- value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
- }
-
op(_POP_CALL_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused -- value)) {
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
}
@@ -845,7 +837,7 @@ dummy_func(void) {
if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS)
{
PyDict_Watch(GLOBALS_WATCHER_ID, dict);
_Py_BloomFilter_Add(dependencies, dict);
- PyObject *res = convert_global_to_const(this_instr, dict,
false, true);
+ PyObject *res = convert_global_to_const(this_instr, dict,
true);
if (res == NULL) {
attr = sym_new_not_null(ctx);
}
@@ -898,8 +890,7 @@ dummy_func(void) {
PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _POP_TOP_LOAD_CONST_INLINE_BORROW,
- _POP_TOP_LOAD_CONST_INLINE);
+ true);
}
op(_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, (descr/4, owner -- attr)) {
@@ -907,8 +898,7 @@ dummy_func(void) {
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _POP_TOP_LOAD_CONST_INLINE_BORROW,
- _POP_TOP_LOAD_CONST_INLINE);
+ true);
}
op(_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, (descr/4, owner -- attr)) {
@@ -916,8 +906,7 @@ dummy_func(void) {
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _POP_TOP_LOAD_CONST_INLINE_BORROW,
- _POP_TOP_LOAD_CONST_INLINE);
+ true);
}
op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self)) {
@@ -925,8 +914,7 @@ dummy_func(void) {
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _INSERT_1_LOAD_CONST_INLINE_BORROW,
- _INSERT_1_LOAD_CONST_INLINE);
+ false);
self = owner;
}
@@ -935,8 +923,7 @@ dummy_func(void) {
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _INSERT_1_LOAD_CONST_INLINE_BORROW,
- _INSERT_1_LOAD_CONST_INLINE);
+ false);
self = owner;
}
@@ -945,8 +932,7 @@ dummy_func(void) {
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _INSERT_1_LOAD_CONST_INLINE_BORROW,
- _INSERT_1_LOAD_CONST_INLINE);
+ false);
self = owner;
}
@@ -2010,7 +1996,7 @@ dummy_func(void) {
ctx->builtins_watched = true;
}
if (ctx->frame->globals_checked_version != 0 &&
ctx->frame->globals_watched) {
- cnst = convert_global_to_const(this_instr, builtins, false,
false);
+ cnst = convert_global_to_const(this_instr, builtins, false);
}
}
if (cnst == NULL) {
@@ -2049,7 +2035,7 @@ dummy_func(void) {
ctx->frame->globals_checked_version = version;
}
if (ctx->frame->globals_checked_version == version) {
- cnst = convert_global_to_const(this_instr, globals, false,
false);
+ cnst = convert_global_to_const(this_instr, globals, false);
}
}
}
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 82ac3c84f02880..5c56a9babd889c 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -314,8 +314,9 @@
if (sym_is_const(ctx, res)) {
PyObject *result = sym_get_const(ctx, res);
if (_Py_IsImmortal(result)) {
- // Replace with _POP_TOP_LOAD_CONST_INLINE_BORROW
since we have one input and an immortal result
- ADD_OP(_POP_TOP_LOAD_CONST_INLINE_BORROW, 0,
(uintptr_t)result);
+ // Replace with _POP_TOP + _LOAD_CONST_INLINE_BORROW
since we have one input and an immortal result
+ ADD_OP(_POP_TOP, 0, 0);
+ ADD_OP(_LOAD_CONST_INLINE_BORROW, 0,
(uintptr_t)result);
}
}
stack_pointer[-1] = res;
@@ -2085,7 +2086,7 @@
ctx->frame->globals_checked_version = version;
}
if (ctx->frame->globals_checked_version == version) {
- cnst = convert_global_to_const(this_instr, globals,
false, false);
+ cnst = convert_global_to_const(this_instr, globals,
false);
}
}
}
@@ -2128,7 +2129,7 @@
ctx->builtins_watched = true;
}
if (ctx->frame->globals_checked_version != 0 &&
ctx->frame->globals_watched) {
- cnst = convert_global_to_const(this_instr, builtins,
false, false);
+ cnst = convert_global_to_const(this_instr, builtins,
false);
}
}
if (cnst == NULL) {
@@ -2438,7 +2439,7 @@
if (watched_mutations <
_Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
PyDict_Watch(GLOBALS_WATCHER_ID, dict);
_Py_BloomFilter_Add(dependencies, dict);
- PyObject *res = convert_global_to_const(this_instr,
dict, false, true);
+ PyObject *res = convert_global_to_const(this_instr,
dict, true);
if (res == NULL) {
attr = sym_new_not_null(ctx);
}
@@ -2519,8 +2520,7 @@
PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _POP_TOP_LOAD_CONST_INLINE_BORROW,
- _POP_TOP_LOAD_CONST_INLINE);
+ true);
stack_pointer[-1] = attr;
break;
}
@@ -3413,8 +3413,7 @@
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _INSERT_1_LOAD_CONST_INLINE_BORROW,
- _INSERT_1_LOAD_CONST_INLINE);
+ false);
self = owner;
CHECK_STACK_BOUNDS(1);
stack_pointer[-1] = attr;
@@ -3434,8 +3433,7 @@
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _INSERT_1_LOAD_CONST_INLINE_BORROW,
- _INSERT_1_LOAD_CONST_INLINE);
+ false);
self = owner;
CHECK_STACK_BOUNDS(1);
stack_pointer[-1] = attr;
@@ -3454,8 +3452,7 @@
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _POP_TOP_LOAD_CONST_INLINE_BORROW,
- _POP_TOP_LOAD_CONST_INLINE);
+ true);
stack_pointer[-1] = attr;
break;
}
@@ -3469,8 +3466,7 @@
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _POP_TOP_LOAD_CONST_INLINE_BORROW,
- _POP_TOP_LOAD_CONST_INLINE);
+ true);
stack_pointer[-1] = attr;
break;
}
@@ -3489,8 +3485,7 @@
PyTypeObject *type = sym_get_type(owner);
PyObject *name = get_co_name(ctx, oparg >> 1);
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
- _INSERT_1_LOAD_CONST_INLINE_BORROW,
- _INSERT_1_LOAD_CONST_INLINE);
+ false);
self = owner;
CHECK_STACK_BOUNDS(1);
stack_pointer[-1] = attr;
@@ -4857,8 +4852,7 @@
case _POP_TOP_LOAD_CONST_INLINE: {
JitOptRef value;
- PyObject *ptr = (PyObject *)this_instr->operand0;
- value = sym_new_const(ctx, ptr);
+ value = sym_new_not_null(ctx);
stack_pointer[-1] = value;
break;
}
@@ -4897,8 +4891,7 @@
case _POP_TOP_LOAD_CONST_INLINE_BORROW: {
JitOptRef value;
- PyObject *ptr = (PyObject *)this_instr->operand0;
- value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
+ value = sym_new_not_null(ctx);
stack_pointer[-1] = value;
break;
}
diff --git a/Tools/cases_generator/optimizer_generator.py
b/Tools/cases_generator/optimizer_generator.py
index 65896221ba7a05..495462b6bcd906 100644
--- a/Tools/cases_generator/optimizer_generator.py
+++ b/Tools/cases_generator/optimizer_generator.py
@@ -238,18 +238,23 @@ def replace_opcode_if_evaluates_pure(
# Map input count to output index (from TOS) and the appropriate
constant-loading uop
input_count_to_uop = {
1: {
- # (a -- a), usually for unary ops
- 0: "_POP_TOP_LOAD_CONST_INLINE_BORROW",
+ # (a -- res), usually for unary ops
+ 0: [("_POP_TOP", "0, 0"),
+ ("_LOAD_CONST_INLINE_BORROW",
+ "0, (uintptr_t)result")],
# (left -- res, left)
# usually for unary ops with passthrough references
- 1: "_INSERT_1_LOAD_CONST_INLINE_BORROW",
+ 1: [("_INSERT_1_LOAD_CONST_INLINE_BORROW",
+ "0, (uintptr_t)result")],
},
2: {
- # (a. b -- res), usually for binary ops
- 0: "_POP_TWO_LOAD_CONST_INLINE_BORROW",
+ # (a, b -- res), usually for binary ops
+ 0: [("_POP_TWO_LOAD_CONST_INLINE_BORROW",
+ "0, (uintptr_t)result")],
# (left, right -- res, left, right)
# usually for binary ops with passthrough references
- 2: "_INSERT_2_LOAD_CONST_INLINE_BORROW",
+ 2: [("_INSERT_2_LOAD_CONST_INLINE_BORROW",
+ "0, (uintptr_t)result")],
},
}
@@ -263,14 +268,16 @@ def replace_opcode_if_evaluates_pure(
assert output_index >= 0
input_count = len(used_stack_inputs)
if input_count in input_count_to_uop and output_index in
input_count_to_uop[input_count]:
- replacement_uop = input_count_to_uop[input_count][output_index]
+ ops = input_count_to_uop[input_count][output_index]
input_desc = "one input" if input_count == 1 else "two inputs"
+ ops_desc = " + ".join(op for op, _ in ops)
emitter.emit(f"if (sym_is_const(ctx,
{output_identifier.text})) {{\n")
emitter.emit(f"PyObject *result = sym_get_const(ctx,
{output_identifier.text});\n")
emitter.emit(f"if (_Py_IsImmortal(result)) {{\n")
- emitter.emit(f"// Replace with {replacement_uop} since we have
{input_desc} and an immortal result\n")
- emitter.emit(f"ADD_OP({replacement_uop}, 0,
(uintptr_t)result);\n")
+ emitter.emit(f"// Replace with {ops_desc} since we have
{input_desc} and an immortal result\n")
+ for op, args in ops:
+ emitter.emit(f"ADD_OP({op}, {args});\n")
emitter.emit("}\n")
emitter.emit("}\n")
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]