Skip to content

Commit 0c407bd

Browse files
committed
gh-148211: decompose _POP_TOP_LOAD_CONST_INLINE(_BORROW) in JIT
1 parent 4ff8b07 commit 0c407bd

File tree

5 files changed

+62
-71
lines changed

5 files changed

+62
-71
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,8 +1470,8 @@ class Bar:
14701470
opnames = list(iter_opnames(ex))
14711471
self.assertEqual(res, TIER2_THRESHOLD * 2 + 2)
14721472
call = opnames.index("_CALL_BUILTIN_FAST")
1473-
load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0, call)
1474-
load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", call)
1473+
load_attr_top = opnames.index("_LOAD_CONST_INLINE_BORROW", 0, call)
1474+
load_attr_bottom = opnames.index("_LOAD_CONST_INLINE_BORROW", call)
14751475
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1)
14761476
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2)
14771477

@@ -1493,8 +1493,8 @@ class Foo:
14931493
self.assertIsNotNone(ex)
14941494
self.assertEqual(res, TIER2_THRESHOLD * 2)
14951495
call = opnames.index("_CALL_BUILTIN_FAST_WITH_KEYWORDS")
1496-
load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0, call)
1497-
load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", call)
1496+
load_attr_top = opnames.index("_LOAD_CONST_INLINE_BORROW", 0, call)
1497+
load_attr_bottom = opnames.index("_LOAD_CONST_INLINE_BORROW", call)
14981498
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1)
14991499
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2)
15001500

Python/optimizer_analysis.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ type_watcher_callback(PyTypeObject* type)
155155
}
156156

157157
static PyObject *
158-
convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop, bool insert)
158+
convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool insert)
159159
{
160160
assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE);
161161
assert(PyDict_CheckExact(obj));
@@ -183,9 +183,9 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop, bool i
183183
}
184184
} else {
185185
if (_Py_IsImmortal(res)) {
186-
inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE_BORROW;
186+
inst->opcode = _LOAD_CONST_INLINE_BORROW;
187187
} else {
188-
inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE : _LOAD_CONST_INLINE;
188+
inst->opcode = _LOAD_CONST_INLINE;
189189
}
190190
if (inst->oparg & 1) {
191191
assert(inst[1].opcode == _PUSH_NULL_CONDITIONAL);
@@ -340,10 +340,12 @@ optimize_to_bool(
340340
int truthiness = sym_truthiness(ctx, value);
341341
if (truthiness >= 0) {
342342
PyObject *load = truthiness ? Py_True : Py_False;
343-
int opcode = insert_mode ?
344-
_INSERT_1_LOAD_CONST_INLINE_BORROW :
345-
_POP_TOP_LOAD_CONST_INLINE_BORROW;
346-
ADD_OP(opcode, 0, (uintptr_t)load);
343+
if (insert_mode) {
344+
ADD_OP(_INSERT_1_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
345+
} else {
346+
ADD_OP(_POP_TOP, 0, 0);
347+
ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
348+
}
347349
*result_ptr = sym_new_const(ctx, load);
348350
return 1;
349351
}
@@ -389,19 +391,22 @@ eliminate_pop_guard(_PyUOpInstruction *this_instr, JitOptContext *ctx, bool exit
389391

390392
static JitOptRef
391393
lookup_attr(JitOptContext *ctx, _PyBloomFilter *dependencies, _PyUOpInstruction *this_instr,
392-
PyTypeObject *type, PyObject *name, uint16_t immortal,
393-
uint16_t mortal)
394+
PyTypeObject *type, PyObject *name, bool pop)
394395
{
395396
// The cached value may be dead, so we need to do the lookup again... :(
396397
if (type && PyType_Check(type)) {
397398
PyObject *lookup = _PyType_Lookup(type, name);
398399
if (lookup) {
399-
int opcode = mortal;
400-
// if the object is immortal or the type is immutable, borrowing is safe
401-
if (_Py_IsImmortal(lookup) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE)) {
402-
opcode = immortal;
400+
bool immortal = _Py_IsImmortal(lookup) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE);
401+
if (pop) {
402+
ADD_OP(_POP_TOP, 0, 0);
403+
ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE,
404+
0, (uintptr_t)lookup);
405+
}
406+
else {
407+
ADD_OP(immortal ? _LOAD_CONST_UNDER_INLINE_BORROW : _LOAD_CONST_UNDER_INLINE,
408+
0, (uintptr_t)lookup);
403409
}
404-
ADD_OP(opcode, 0, (uintptr_t)lookup);
405410
PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
406411
_Py_BloomFilter_Add(dependencies, type);
407412
return sym_new_const(ctx, lookup);

Python/optimizer_bytecodes.c

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -749,14 +749,6 @@ dummy_func(void) {
749749
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
750750
}
751751

752-
op(_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) {
753-
value = sym_new_const(ctx, ptr);
754-
}
755-
756-
op(_POP_TOP_LOAD_CONST_INLINE_BORROW, (ptr/4, pop -- value)) {
757-
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
758-
}
759-
760752
op(_POP_CALL_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused -- value)) {
761753
value = PyJitRef_Borrow(sym_new_const(ctx, ptr));
762754
}
@@ -837,7 +829,7 @@ dummy_func(void) {
837829
if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
838830
PyDict_Watch(GLOBALS_WATCHER_ID, dict);
839831
_Py_BloomFilter_Add(dependencies, dict);
840-
PyObject *res = convert_global_to_const(this_instr, dict, false, true);
832+
PyObject *res = convert_global_to_const(this_instr, dict, true);
841833
if (res == NULL) {
842834
attr = sym_new_not_null(ctx);
843835
}
@@ -890,35 +882,31 @@ dummy_func(void) {
890882
PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner);
891883
PyObject *name = get_co_name(ctx, oparg >> 1);
892884
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
893-
_POP_TOP_LOAD_CONST_INLINE_BORROW,
894-
_POP_TOP_LOAD_CONST_INLINE);
885+
true);
895886
}
896887

897888
op(_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, (descr/4, owner -- attr)) {
898889
(void)descr;
899890
PyTypeObject *type = sym_get_type(owner);
900891
PyObject *name = get_co_name(ctx, oparg >> 1);
901892
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
902-
_POP_TOP_LOAD_CONST_INLINE_BORROW,
903-
_POP_TOP_LOAD_CONST_INLINE);
893+
true);
904894
}
905895

906896
op(_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, (descr/4, owner -- attr)) {
907897
(void)descr;
908898
PyTypeObject *type = sym_get_type(owner);
909899
PyObject *name = get_co_name(ctx, oparg >> 1);
910900
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
911-
_POP_TOP_LOAD_CONST_INLINE_BORROW,
912-
_POP_TOP_LOAD_CONST_INLINE);
901+
true);
913902
}
914903

915904
op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self)) {
916905
(void)descr;
917906
PyTypeObject *type = sym_get_type(owner);
918907
PyObject *name = get_co_name(ctx, oparg >> 1);
919908
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
920-
_LOAD_CONST_UNDER_INLINE_BORROW,
921-
_LOAD_CONST_UNDER_INLINE);
909+
false);
922910
self = owner;
923911
}
924912

@@ -927,8 +915,7 @@ dummy_func(void) {
927915
PyTypeObject *type = sym_get_type(owner);
928916
PyObject *name = get_co_name(ctx, oparg >> 1);
929917
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
930-
_LOAD_CONST_UNDER_INLINE_BORROW,
931-
_LOAD_CONST_UNDER_INLINE);
918+
false);
932919
self = owner;
933920
}
934921

@@ -937,8 +924,7 @@ dummy_func(void) {
937924
PyTypeObject *type = sym_get_type(owner);
938925
PyObject *name = get_co_name(ctx, oparg >> 1);
939926
attr = lookup_attr(ctx, dependencies, this_instr, type, name,
940-
_LOAD_CONST_UNDER_INLINE_BORROW,
941-
_LOAD_CONST_UNDER_INLINE);
927+
false);
942928
self = owner;
943929
}
944930

@@ -2002,7 +1988,7 @@ dummy_func(void) {
20021988
ctx->builtins_watched = true;
20031989
}
20041990
if (ctx->frame->globals_checked_version != 0 && ctx->frame->globals_watched) {
2005-
cnst = convert_global_to_const(this_instr, builtins, false, false);
1991+
cnst = convert_global_to_const(this_instr, builtins, false);
20061992
}
20071993
}
20081994
if (cnst == NULL) {
@@ -2041,7 +2027,7 @@ dummy_func(void) {
20412027
ctx->frame->globals_checked_version = version;
20422028
}
20432029
if (ctx->frame->globals_checked_version == version) {
2044-
cnst = convert_global_to_const(this_instr, globals, false, false);
2030+
cnst = convert_global_to_const(this_instr, globals, false);
20452031
}
20462032
}
20472033
}

Python/optimizer_cases.c.h

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

Tools/cases_generator/optimizer_generator.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -238,18 +238,23 @@ def replace_opcode_if_evaluates_pure(
238238
# Map input count to output index (from TOS) and the appropriate constant-loading uop
239239
input_count_to_uop = {
240240
1: {
241-
# (a -- a), usually for unary ops
242-
0: "_POP_TOP_LOAD_CONST_INLINE_BORROW",
241+
# (a -- res), usually for unary ops
242+
0: [("_POP_TOP", "0, 0"),
243+
("_LOAD_CONST_INLINE_BORROW",
244+
"0, (uintptr_t)result")],
243245
# (left -- res, left)
244246
# usually for unary ops with passthrough references
245-
1: "_INSERT_1_LOAD_CONST_INLINE_BORROW",
247+
1: [("_INSERT_1_LOAD_CONST_INLINE_BORROW",
248+
"0, (uintptr_t)result")],
246249
},
247250
2: {
248-
# (a. b -- res), usually for binary ops
249-
0: "_POP_TWO_LOAD_CONST_INLINE_BORROW",
251+
# (a, b -- res), usually for binary ops
252+
0: [("_POP_TWO_LOAD_CONST_INLINE_BORROW",
253+
"0, (uintptr_t)result")],
250254
# (left, right -- res, left, right)
251255
# usually for binary ops with passthrough references
252-
2: "_INSERT_2_LOAD_CONST_INLINE_BORROW",
256+
2: [("_INSERT_2_LOAD_CONST_INLINE_BORROW",
257+
"0, (uintptr_t)result")],
253258
},
254259
}
255260

@@ -263,14 +268,16 @@ def replace_opcode_if_evaluates_pure(
263268
assert output_index >= 0
264269
input_count = len(used_stack_inputs)
265270
if input_count in input_count_to_uop and output_index in input_count_to_uop[input_count]:
266-
replacement_uop = input_count_to_uop[input_count][output_index]
271+
ops = input_count_to_uop[input_count][output_index]
267272
input_desc = "one input" if input_count == 1 else "two inputs"
273+
ops_desc = " + ".join(op for op, _ in ops)
268274

269275
emitter.emit(f"if (sym_is_const(ctx, {output_identifier.text})) {{\n")
270276
emitter.emit(f"PyObject *result = sym_get_const(ctx, {output_identifier.text});\n")
271277
emitter.emit(f"if (_Py_IsImmortal(result)) {{\n")
272-
emitter.emit(f"// Replace with {replacement_uop} since we have {input_desc} and an immortal result\n")
273-
emitter.emit(f"ADD_OP({replacement_uop}, 0, (uintptr_t)result);\n")
278+
emitter.emit(f"// Replace with {ops_desc} since we have {input_desc} and an immortal result\n")
279+
for op, args in ops:
280+
emitter.emit(f"ADD_OP({op}, {args});\n")
274281
emitter.emit("}\n")
275282
emitter.emit("}\n")
276283

0 commit comments

Comments
 (0)