Skip to content

Commit 198517e

Browse files
committed
Use load bit of LOAD_GLOBAL's oparg to indicate whether it should push an additional NULL.
1 parent 7c77652 commit 198517e

File tree

7 files changed

+212
-196
lines changed

7 files changed

+212
-196
lines changed

Doc/library/dis.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ the following command can be used to display the disassembly of
4343
1 0 RESUME 0
4444

4545
2 2 PUSH_NULL
46-
4 LOAD_GLOBAL 0 (len)
46+
4 LOAD_GLOBAL 1 (NULL + len)
4747
6 LOAD_FAST 0 (alist)
4848
8 PRECALL 1
4949
10 CALL 1
@@ -996,8 +996,11 @@ iterations of the loop.
996996

997997
.. opcode:: LOAD_GLOBAL (namei)
998998

999-
Loads the global named ``co_names[namei]`` onto the stack.
999+
Loads the global named ``co_names[namei>>1]`` onto the stack.
10001000

1001+
.. versionchanged:: 3.11
1002+
If the low bit of ``namei`` is set, then a ``NULL`` is pushed to the
1003+
stack before the global variable.
10011004

10021005
.. opcode:: LOAD_FAST (var_num)
10031006

Lib/dis.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
2929

3030
LOAD_CONST = opmap['LOAD_CONST']
31+
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
3132
BINARY_OP = opmap['BINARY_OP']
3233

3334
CACHE = opmap["CACHE"]
@@ -430,7 +431,12 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
430431
if op in hasconst:
431432
argval, argrepr = _get_const_info(op, arg, co_consts)
432433
elif op in hasname:
433-
argval, argrepr = _get_name_info(arg, get_name)
434+
if op == LOAD_GLOBAL:
435+
argval, argrepr = _get_name_info(arg//2, get_name)
436+
if (arg & 1) and argrepr:
437+
argrepr = "NULL + " + argrepr
438+
else:
439+
argval, argrepr = _get_name_info(arg, get_name)
434440
elif op in hasjabs:
435441
argval = arg*2
436442
argrepr = "to " + repr(argval)

Lib/importlib/_bootstrap_external.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ def _write_atomic(path, data, mode=0o666):
395395
# Python 3.11a5 3485 (Add an oparg to GET_AWAITABLE)
396396
# Python 3.11a6 3486 (Use inline caching for PRECALL and CALL)
397397
# Python 3.11a6 3487 (Remove the adaptive "oparg counter" mechanism)
398+
# Python 3.11a6 3488 (LOAD_GLOBAL can push additional NULL)
398399

399400
# Python 3.12 will start with magic number 3500
400401

@@ -409,7 +410,7 @@ def _write_atomic(path, data, mode=0o666):
409410
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
410411
# in PC/launcher.c must also be updated.
411412

412-
MAGIC_NUMBER = (3487).to_bytes(2, 'little') + b'\r\n'
413+
MAGIC_NUMBER = (3488).to_bytes(2, 'little') + b'\r\n'
413414
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
414415

415416
_PYCACHE = '__pycache__'

Lib/test/test_dis.py

Lines changed: 170 additions & 185 deletions
Large diffs are not rendered by default.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Use low bit of ``LOAD_GLOBAL`` to indicate whether to push a ``NULL`` before
2+
the global. Helps streamline the call sequence a bit.

Python/ceval.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2947,7 +2947,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
29472947

29482948
TARGET(LOAD_GLOBAL) {
29492949
PREDICTED(LOAD_GLOBAL);
2950-
PyObject *name = GETITEM(names, oparg);
2950+
int push_null = oparg & 1;
2951+
PEEK(0) = NULL;
2952+
PyObject *name = GETITEM(names, oparg>>1);
29512953
PyObject *v;
29522954
if (PyDict_CheckExact(GLOBALS())
29532955
&& PyDict_CheckExact(BUILTINS()))
@@ -2970,7 +2972,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
29702972
/* Slow-path if globals or builtins is not a dict */
29712973

29722974
/* namespace 1: globals */
2973-
name = GETITEM(names, oparg);
29742975
v = PyObject_GetItem(GLOBALS(), name);
29752976
if (v == NULL) {
29762977
if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
@@ -2992,6 +2993,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
29922993
}
29932994
/* Skip over inline cache */
29942995
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
2996+
STACK_GROW(push_null);
29952997
PUSH(v);
29962998
DISPATCH();
29972999
}
@@ -3000,7 +3002,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
30003002
assert(cframe.use_tracing == 0);
30013003
_PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
30023004
if (cache->counter == 0) {
3003-
PyObject *name = GETITEM(names, oparg);
3005+
PyObject *name = GETITEM(names, oparg>>1);
30043006
next_instr--;
30053007
if (_Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name) < 0) {
30063008
goto error;
@@ -3025,10 +3027,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
30253027
PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
30263028
PyObject *res = entries[cache->index].me_value;
30273029
DEOPT_IF(res == NULL, LOAD_GLOBAL);
3030+
int push_null = oparg & 1;
3031+
PEEK(0) = NULL;
30283032
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
30293033
STAT_INC(LOAD_GLOBAL, hit);
3034+
STACK_GROW(push_null+1);
30303035
Py_INCREF(res);
3031-
PUSH(res);
3036+
SET_TOP(res);
30323037
NOTRACE_DISPATCH();
30333038
}
30343039

@@ -3047,10 +3052,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
30473052
PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys);
30483053
PyObject *res = entries[cache->index].me_value;
30493054
DEOPT_IF(res == NULL, LOAD_GLOBAL);
3055+
int push_null = oparg & 1;
3056+
PEEK(0) = NULL;
30503057
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
30513058
STAT_INC(LOAD_GLOBAL, hit);
3059+
STACK_GROW(push_null+1);
30523060
Py_INCREF(res);
3053-
PUSH(res);
3061+
SET_TOP(res);
30543062
NOTRACE_DISPATCH();
30553063
}
30563064

Python/compile.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,7 @@ stack_effect(int opcode, int oparg, int jump)
10001000
return -1;
10011001

10021002
case LOAD_GLOBAL:
1003-
return 1;
1003+
return (oparg & 1) + 1;
10041004

10051005
/* Exception handling pseudo-instructions */
10061006
case SETUP_FINALLY:
@@ -4185,8 +4185,12 @@ compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx)
41854185
assert(op);
41864186
arg = compiler_add_o(dict, mangled);
41874187
Py_DECREF(mangled);
4188-
if (arg < 0)
4188+
if (arg < 0) {
41894189
return 0;
4190+
}
4191+
if (op == LOAD_GLOBAL) {
4192+
arg <<= 1;
4193+
}
41904194
return compiler_addop_i(c, op, arg);
41914195
}
41924196

@@ -8812,6 +8816,13 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
88128816
break;
88138817
case KW_NAMES:
88148818
break;
8819+
case PUSH_NULL:
8820+
if (nextop == LOAD_GLOBAL && (inst[1].i_opcode & 1) == 0) {
8821+
inst->i_opcode = NOP;
8822+
inst->i_oparg = 0;
8823+
inst[1].i_oparg |= 1;
8824+
}
8825+
break;
88158826
default:
88168827
/* All HAS_CONST opcodes should be handled with LOAD_CONST */
88178828
assert (!HAS_CONST(inst->i_opcode));

0 commit comments

Comments
 (0)