Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b348313

Browse filesBrowse 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
Copy full SHA for b348313

File tree

Expand file treeCollapse file tree

6 files changed

+182
-95
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+182
-95
lines changed

‎Python/bytecodes.c

Copy file name to clipboardExpand all lines: Python/bytecodes.c
+2-2Lines 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

Copy file name to clipboardExpand all lines: Python/executor_cases.c.h
+2-2Lines 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

Copy file name to clipboardExpand all lines: Python/generated_cases.c.h
+2-2Lines 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

Copy file name to clipboardExpand all lines: Python/optimizer_analysis.c
+44-24Lines 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

Copy file name to clipboardExpand all lines: Python/tier2_redundancy_eliminator_bytecodes.c
+67-33Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.