Skip to content

Commit b348313

Browse files
authored
GH-115651: Convert LOAD_MODULE_ATTR into LOAD_INLINE_CONST when the module is itself a constant. (GH-115711)
1 parent c6a47de commit b348313

6 files changed

+182
-95
lines changed

Python/bytecodes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,11 +1932,11 @@ dummy_func(
19321932
_LOAD_ATTR_INSTANCE_VALUE +
19331933
unused/5; // Skip over rest of cache
19341934

1935-
op(_CHECK_ATTR_MODULE, (type_version/2, owner -- owner)) {
1935+
op(_CHECK_ATTR_MODULE, (dict_version/2, owner -- owner)) {
19361936
DEOPT_IF(!PyModule_CheckExact(owner));
19371937
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict;
19381938
assert(dict != NULL);
1939-
DEOPT_IF(dict->ma_keys->dk_version != type_version);
1939+
DEOPT_IF(dict->ma_keys->dk_version != dict_version);
19401940
}
19411941

19421942
op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) {

Python/executor_cases.c.h

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

Python/generated_cases.c.h

Lines changed: 2 additions & 2 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: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,20 @@ sym_is_null(_Py_UOpsSymType *sym)
271271
return (sym->flags & (IS_NULL | NOT_NULL)) == IS_NULL;
272272
}
273273

274+
static inline bool
275+
sym_is_const(_Py_UOpsSymType *sym)
276+
{
277+
return (sym->flags & TRUE_CONST) != 0;
278+
}
279+
280+
static inline PyObject *
281+
sym_get_const(_Py_UOpsSymType *sym)
282+
{
283+
assert(sym_is_const(sym));
284+
assert(sym->const_val);
285+
return sym->const_val;
286+
}
287+
274288
static inline void
275289
sym_set_type(_Py_UOpsSymType *sym, PyTypeObject *tp)
276290
{
@@ -336,18 +350,6 @@ sym_new_const(_Py_UOpsAbstractInterpContext *ctx, PyObject *const_val)
336350
return temp;
337351
}
338352

339-
static inline bool
340-
is_const(_Py_UOpsSymType *sym)
341-
{
342-
return sym->const_val != NULL;
343-
}
344-
345-
static inline PyObject *
346-
get_const(_Py_UOpsSymType *sym)
347-
{
348-
return sym->const_val;
349-
}
350-
351353
static _Py_UOpsSymType*
352354
sym_new_null(_Py_UOpsAbstractInterpContext *ctx)
353355
{
@@ -408,18 +410,21 @@ globals_watcher_callback(PyDict_WatchEvent event, PyObject* dict,
408410
return 0;
409411
}
410412

411-
static void
412-
global_to_const(_PyUOpInstruction *inst, PyObject *obj)
413+
static PyObject *
414+
convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj)
413415
{
414-
assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS);
416+
assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE);
415417
assert(PyDict_CheckExact(obj));
416418
PyDictObject *dict = (PyDictObject *)obj;
417419
assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
418420
PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
419421
assert(inst->operand <= UINT16_MAX);
422+
if ((int)inst->operand >= dict->ma_keys->dk_nentries) {
423+
return NULL;
424+
}
420425
PyObject *res = entries[inst->operand].me_value;
421426
if (res == NULL) {
422-
return;
427+
return NULL;
423428
}
424429
if (_Py_IsImmortal(res)) {
425430
inst->opcode = (inst->oparg & 1) ? _LOAD_CONST_INLINE_BORROW_WITH_NULL : _LOAD_CONST_INLINE_BORROW;
@@ -428,6 +433,7 @@ global_to_const(_PyUOpInstruction *inst, PyObject *obj)
428433
inst->opcode = (inst->oparg & 1) ? _LOAD_CONST_INLINE_WITH_NULL : _LOAD_CONST_INLINE;
429434
}
430435
inst->operand = (uint64_t)res;
436+
return res;
431437
}
432438

433439
static int
@@ -524,12 +530,12 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
524530
break;
525531
case _LOAD_GLOBAL_BUILTINS:
526532
if (globals_checked & builtins_checked & globals_watched & builtins_watched & 1) {
527-
global_to_const(inst, builtins);
533+
convert_global_to_const(inst, builtins);
528534
}
529535
break;
530536
case _LOAD_GLOBAL_MODULE:
531537
if (globals_checked & globals_watched & 1) {
532-
global_to_const(inst, globals);
538+
convert_global_to_const(inst, globals);
533539
}
534540
break;
535541
case _PUSH_FRAME:
@@ -603,7 +609,8 @@ uop_redundancy_eliminator(
603609
PyCodeObject *co,
604610
_PyUOpInstruction *trace,
605611
int trace_len,
606-
int curr_stacklen
612+
int curr_stacklen,
613+
_PyBloomFilter *dependencies
607614
)
608615
{
609616

@@ -665,7 +672,7 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size)
665672
* could error. _CHECK_VALIDITY is needed if the previous
666673
* instruction could have escaped. */
667674
int last_set_ip = -1;
668-
bool may_have_escaped = false;
675+
bool may_have_escaped = true;
669676
for (int pc = 0; pc < buffer_size; pc++) {
670677
int opcode = buffer[pc].opcode;
671678
switch (opcode) {
@@ -691,6 +698,22 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size)
691698
}
692699
last_set_ip = pc;
693700
break;
701+
case _POP_TOP:
702+
{
703+
_PyUOpInstruction *last = &buffer[pc-1];
704+
while (last->opcode == _NOP) {
705+
last--;
706+
}
707+
if (last->opcode == _LOAD_CONST_INLINE ||
708+
last->opcode == _LOAD_CONST_INLINE_BORROW ||
709+
last->opcode == _LOAD_FAST ||
710+
last->opcode == _COPY
711+
) {
712+
last->opcode = _NOP;
713+
buffer[pc].opcode = NOP;
714+
}
715+
break;
716+
}
694717
case _JUMP_TO_TOP:
695718
case _EXIT_TRACE:
696719
return;
@@ -704,9 +727,6 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size)
704727
if (_PyUop_Flags[opcode] & HAS_ERROR_FLAG) {
705728
needs_ip = true;
706729
}
707-
if (opcode == _PUSH_FRAME) {
708-
needs_ip = true;
709-
}
710730
if (needs_ip && last_set_ip >= 0) {
711731
if (buffer[last_set_ip].opcode == _CHECK_VALIDITY) {
712732
buffer[last_set_ip].opcode = _CHECK_VALIDITY_AND_SET_IP;
@@ -791,7 +811,7 @@ _Py_uop_analyze_and_optimize(
791811

792812
err = uop_redundancy_eliminator(
793813
(PyCodeObject *)frame->f_executable, buffer,
794-
buffer_size, curr_stacklen);
814+
buffer_size, curr_stacklen, dependencies);
795815

796816
if (err == 0) {
797817
goto not_ready;

Python/tier2_redundancy_eliminator_bytecodes.c

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "Python.h"
22
#include "pycore_uops.h"
33
#include "pycore_uop_ids.h"
4+
#include "internal/pycore_moduleobject.h"
45

56
#define op(name, ...) /* NAME is ignored */
67

@@ -87,11 +88,11 @@ dummy_func(void) {
8788
}
8889

8990
op(_BINARY_OP_ADD_INT, (left, right -- res)) {
90-
if (is_const(left) && is_const(right)) {
91-
assert(PyLong_CheckExact(get_const(left)));
92-
assert(PyLong_CheckExact(get_const(right)));
93-
PyObject *temp = _PyLong_Add((PyLongObject *)get_const(left),
94-
(PyLongObject *)get_const(right));
91+
if (sym_is_const(left) && sym_is_const(right)) {
92+
assert(PyLong_CheckExact(sym_get_const(left)));
93+
assert(PyLong_CheckExact(sym_get_const(right)));
94+
PyObject *temp = _PyLong_Add((PyLongObject *)sym_get_const(left),
95+
(PyLongObject *)sym_get_const(right));
9596
if (temp == NULL) {
9697
goto error;
9798
}
@@ -105,11 +106,11 @@ dummy_func(void) {
105106
}
106107

107108
op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) {
108-
if (is_const(left) && is_const(right)) {
109-
assert(PyLong_CheckExact(get_const(left)));
110-
assert(PyLong_CheckExact(get_const(right)));
111-
PyObject *temp = _PyLong_Subtract((PyLongObject *)get_const(left),
112-
(PyLongObject *)get_const(right));
109+
if (sym_is_const(left) && sym_is_const(right)) {
110+
assert(PyLong_CheckExact(sym_get_const(left)));
111+
assert(PyLong_CheckExact(sym_get_const(right)));
112+
PyObject *temp = _PyLong_Subtract((PyLongObject *)sym_get_const(left),
113+
(PyLongObject *)sym_get_const(right));
113114
if (temp == NULL) {
114115
goto error;
115116
}
@@ -123,11 +124,11 @@ dummy_func(void) {
123124
}
124125

125126
op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) {
126-
if (is_const(left) && is_const(right)) {
127-
assert(PyLong_CheckExact(get_const(left)));
128-
assert(PyLong_CheckExact(get_const(right)));
129-
PyObject *temp = _PyLong_Multiply((PyLongObject *)get_const(left),
130-
(PyLongObject *)get_const(right));
127+
if (sym_is_const(left) && sym_is_const(right)) {
128+
assert(PyLong_CheckExact(sym_get_const(left)));
129+
assert(PyLong_CheckExact(sym_get_const(right)));
130+
PyObject *temp = _PyLong_Multiply((PyLongObject *)sym_get_const(left),
131+
(PyLongObject *)sym_get_const(right));
131132
if (temp == NULL) {
132133
goto error;
133134
}
@@ -141,12 +142,12 @@ dummy_func(void) {
141142
}
142143

143144
op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) {
144-
if (is_const(left) && is_const(right)) {
145-
assert(PyFloat_CheckExact(get_const(left)));
146-
assert(PyFloat_CheckExact(get_const(right)));
145+
if (sym_is_const(left) && sym_is_const(right)) {
146+
assert(PyFloat_CheckExact(sym_get_const(left)));
147+
assert(PyFloat_CheckExact(sym_get_const(right)));
147148
PyObject *temp = PyFloat_FromDouble(
148-
PyFloat_AS_DOUBLE(get_const(left)) +
149-
PyFloat_AS_DOUBLE(get_const(right)));
149+
PyFloat_AS_DOUBLE(sym_get_const(left)) +
150+
PyFloat_AS_DOUBLE(sym_get_const(right)));
150151
if (temp == NULL) {
151152
goto error;
152153
}
@@ -160,12 +161,12 @@ dummy_func(void) {
160161
}
161162

162163
op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) {
163-
if (is_const(left) && is_const(right)) {
164-
assert(PyFloat_CheckExact(get_const(left)));
165-
assert(PyFloat_CheckExact(get_const(right)));
164+
if (sym_is_const(left) && sym_is_const(right)) {
165+
assert(PyFloat_CheckExact(sym_get_const(left)));
166+
assert(PyFloat_CheckExact(sym_get_const(right)));
166167
PyObject *temp = PyFloat_FromDouble(
167-
PyFloat_AS_DOUBLE(get_const(left)) -
168-
PyFloat_AS_DOUBLE(get_const(right)));
168+
PyFloat_AS_DOUBLE(sym_get_const(left)) -
169+
PyFloat_AS_DOUBLE(sym_get_const(right)));
169170
if (temp == NULL) {
170171
goto error;
171172
}
@@ -179,12 +180,12 @@ dummy_func(void) {
179180
}
180181

181182
op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) {
182-
if (is_const(left) && is_const(right)) {
183-
assert(PyFloat_CheckExact(get_const(left)));
184-
assert(PyFloat_CheckExact(get_const(right)));
183+
if (sym_is_const(left) && sym_is_const(right)) {
184+
assert(PyFloat_CheckExact(sym_get_const(left)));
185+
assert(PyFloat_CheckExact(sym_get_const(right)));
185186
PyObject *temp = PyFloat_FromDouble(
186-
PyFloat_AS_DOUBLE(get_const(left)) *
187-
PyFloat_AS_DOUBLE(get_const(right)));
187+
PyFloat_AS_DOUBLE(sym_get_const(left)) *
188+
PyFloat_AS_DOUBLE(sym_get_const(right)));
188189
if (temp == NULL) {
189190
goto error;
190191
}
@@ -237,10 +238,43 @@ dummy_func(void) {
237238
(void)owner;
238239
}
239240

241+
op(_CHECK_ATTR_MODULE, (dict_version/2, owner -- owner)) {
242+
(void)dict_version;
243+
if (sym_is_const(owner)) {
244+
PyObject *cnst = sym_get_const(owner);
245+
if (PyModule_CheckExact(cnst)) {
246+
PyModuleObject *mod = (PyModuleObject *)cnst;
247+
PyObject *dict = mod->md_dict;
248+
uint64_t watched_mutations = get_mutations(dict);
249+
if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
250+
PyDict_Watch(GLOBALS_WATCHER_ID, dict);
251+
_Py_BloomFilter_Add(dependencies, dict);
252+
this_instr->opcode = _NOP;
253+
}
254+
}
255+
}
256+
}
257+
240258
op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) {
241-
_LOAD_ATTR_NOT_NULL
242259
(void)index;
243-
(void)owner;
260+
OUT_OF_SPACE_IF_NULL(null = sym_new_null(ctx));
261+
attr = NULL;
262+
if (this_instr[-1].opcode == _NOP) {
263+
// Preceding _CHECK_ATTR_MODULE was removed: mod is const and dict is watched.
264+
assert(sym_is_const(owner));
265+
PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
266+
assert(PyModule_CheckExact(mod));
267+
PyObject *dict = mod->md_dict;
268+
PyObject *res = convert_global_to_const(this_instr, dict);
269+
if (res != NULL) {
270+
this_instr[-1].opcode = _POP_TOP;
271+
OUT_OF_SPACE_IF_NULL(attr = sym_new_const(ctx, res));
272+
}
273+
}
274+
if (attr == NULL) {
275+
/* No conversion made. We don't know what `attr` is. */
276+
OUT_OF_SPACE_IF_NULL(attr = sym_new_known_notnull(ctx));
277+
}
244278
}
245279

246280
op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, null if (oparg & 1))) {
@@ -347,4 +381,4 @@ dummy_func(void) {
347381

348382
// END BYTECODES //
349383

350-
}
384+
}

0 commit comments

Comments
 (0)