mirror of https://github.com/python/cpython
gh-121459: Deferred LOAD_GLOBAL (GH-123128)
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Sam Gross <655866+colesbury@users.noreply.github.com>
This commit is contained in:
parent
74330d992b
commit
8810e286fa
|
@ -271,7 +271,7 @@ PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_
|
||||||
PyAPI_FUNC(void) _PyObjectArray_Free(PyObject **array, PyObject **scratch);
|
PyAPI_FUNC(void) _PyObjectArray_Free(PyObject **array, PyObject **scratch);
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) _PyEval_GetANext(PyObject *aiter);
|
PyAPI_FUNC(PyObject *) _PyEval_GetANext(PyObject *aiter);
|
||||||
PyAPI_FUNC(PyObject *) _PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name);
|
PyAPI_FUNC(void) _PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name, _PyStackRef *writeto);
|
||||||
PyAPI_FUNC(PyObject *) _PyEval_GetAwaitable(PyObject *iterable, int oparg);
|
PyAPI_FUNC(PyObject *) _PyEval_GetAwaitable(PyObject *iterable, int oparg);
|
||||||
PyAPI_FUNC(PyObject *) _PyEval_LoadName(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *name);
|
PyAPI_FUNC(PyObject *) _PyEval_LoadName(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *name);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ extern "C" {
|
||||||
|
|
||||||
#include "pycore_object.h" // PyManagedDictPointer
|
#include "pycore_object.h" // PyManagedDictPointer
|
||||||
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_ACQUIRE
|
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_ACQUIRE
|
||||||
|
#include "pycore_stackref.h" // _PyStackRef
|
||||||
|
|
||||||
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
|
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
|
||||||
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
|
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
|
||||||
|
@ -100,10 +101,12 @@ extern void _PyDictKeys_DecRef(PyDictKeysObject *keys);
|
||||||
*/
|
*/
|
||||||
extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
|
extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
|
||||||
extern Py_ssize_t _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
|
extern Py_ssize_t _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
|
||||||
|
extern Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr);
|
||||||
|
|
||||||
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
|
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
|
||||||
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);
|
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);
|
||||||
PyAPI_FUNC(PyObject *)_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
|
PyAPI_FUNC(PyObject *)_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
|
||||||
|
PyAPI_FUNC(void) _PyDict_LoadGlobalStackRef(PyDictObject *, PyDictObject *, PyObject *, _PyStackRef *);
|
||||||
|
|
||||||
/* Consumes references to key and value */
|
/* Consumes references to key and value */
|
||||||
PyAPI_FUNC(int) _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value);
|
PyAPI_FUNC(int) _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value);
|
||||||
|
|
|
@ -1496,6 +1496,45 @@ read_failed:
|
||||||
return ix;
|
return ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_ssize_t
|
||||||
|
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
|
||||||
|
{
|
||||||
|
PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys);
|
||||||
|
if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) {
|
||||||
|
Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
|
||||||
|
if (ix == DKIX_EMPTY) {
|
||||||
|
*value_addr = PyStackRef_NULL;
|
||||||
|
return ix;
|
||||||
|
}
|
||||||
|
else if (ix >= 0) {
|
||||||
|
PyObject **addr_of_value = &DK_UNICODE_ENTRIES(dk)[ix].me_value;
|
||||||
|
PyObject *value = _Py_atomic_load_ptr(addr_of_value);
|
||||||
|
if (value == NULL) {
|
||||||
|
*value_addr = PyStackRef_NULL;
|
||||||
|
return DKIX_EMPTY;
|
||||||
|
}
|
||||||
|
if (_Py_IsImmortal(value) || _PyObject_HasDeferredRefcount(value)) {
|
||||||
|
*value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_DEFERRED };
|
||||||
|
return ix;
|
||||||
|
}
|
||||||
|
if (_Py_TryIncrefCompare(addr_of_value, value)) {
|
||||||
|
*value_addr = PyStackRef_FromPyObjectSteal(value);
|
||||||
|
return ix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *obj;
|
||||||
|
Py_ssize_t ix = _Py_dict_lookup_threadsafe(mp, key, hash, &obj);
|
||||||
|
if (ix >= 0 && obj != NULL) {
|
||||||
|
*value_addr = PyStackRef_FromPyObjectSteal(obj);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*value_addr = PyStackRef_NULL;
|
||||||
|
}
|
||||||
|
return ix;
|
||||||
|
}
|
||||||
|
|
||||||
#else // Py_GIL_DISABLED
|
#else // Py_GIL_DISABLED
|
||||||
|
|
||||||
Py_ssize_t
|
Py_ssize_t
|
||||||
|
@ -1506,6 +1545,15 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
|
||||||
return ix;
|
return ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_ssize_t
|
||||||
|
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
|
||||||
|
{
|
||||||
|
PyObject *val;
|
||||||
|
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &val);
|
||||||
|
*value_addr = val == NULL ? PyStackRef_NULL : PyStackRef_FromPyObjectNew(val);
|
||||||
|
return ix;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -2420,6 +2468,32 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyDict_LoadGlobalStackRef(PyDictObject *globals, PyDictObject *builtins, PyObject *key, _PyStackRef *res)
|
||||||
|
{
|
||||||
|
Py_ssize_t ix;
|
||||||
|
Py_hash_t hash;
|
||||||
|
|
||||||
|
hash = _PyObject_HashFast(key);
|
||||||
|
if (hash == -1) {
|
||||||
|
*res = PyStackRef_NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* namespace 1: globals */
|
||||||
|
ix = _Py_dict_lookup_threadsafe_stackref(globals, key, hash, res);
|
||||||
|
if (ix == DKIX_ERROR) {
|
||||||
|
*res = PyStackRef_NULL;
|
||||||
|
}
|
||||||
|
if (ix != DKIX_EMPTY && !PyStackRef_IsNull(*res)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* namespace 2: builtins */
|
||||||
|
ix = _Py_dict_lookup_threadsafe_stackref(builtins, key, hash, res);
|
||||||
|
assert(ix >= 0 || PyStackRef_IsNull(*res));
|
||||||
|
}
|
||||||
|
|
||||||
/* Consumes references to key and value */
|
/* Consumes references to key and value */
|
||||||
static int
|
static int
|
||||||
setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value)
|
setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value)
|
||||||
|
|
|
@ -1526,12 +1526,12 @@ dummy_func(
|
||||||
#endif /* ENABLE_SPECIALIZATION */
|
#endif /* ENABLE_SPECIALIZATION */
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_LOAD_GLOBAL, ( -- res, null if (oparg & 1))) {
|
// res[1] because we need a pointer to res to pass it to _PyEval_LoadGlobalStackRef
|
||||||
|
op(_LOAD_GLOBAL, ( -- res[1], null if (oparg & 1))) {
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
||||||
PyObject *res_o = _PyEval_LoadGlobal(GLOBALS(), BUILTINS(), name);
|
_PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res);
|
||||||
ERROR_IF(res_o == NULL, error);
|
ERROR_IF(PyStackRef_IsNull(*res), error);
|
||||||
null = PyStackRef_NULL;
|
null = PyStackRef_NULL;
|
||||||
res = PyStackRef_FromPyObjectSteal(res_o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro(LOAD_GLOBAL) =
|
macro(LOAD_GLOBAL) =
|
||||||
|
|
|
@ -3110,15 +3110,14 @@ _PyEval_GetANext(PyObject *aiter)
|
||||||
return awaitable;
|
return awaitable;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
void
|
||||||
_PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name)
|
_PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name, _PyStackRef *writeto)
|
||||||
{
|
{
|
||||||
PyObject *res;
|
|
||||||
if (PyDict_CheckExact(globals) && PyDict_CheckExact(builtins)) {
|
if (PyDict_CheckExact(globals) && PyDict_CheckExact(builtins)) {
|
||||||
res = _PyDict_LoadGlobal((PyDictObject *)globals,
|
_PyDict_LoadGlobalStackRef((PyDictObject *)globals,
|
||||||
(PyDictObject *)builtins,
|
(PyDictObject *)builtins,
|
||||||
name);
|
name, writeto);
|
||||||
if (res == NULL && !PyErr_Occurred()) {
|
if (PyStackRef_IsNull(*writeto) && !PyErr_Occurred()) {
|
||||||
/* _PyDict_LoadGlobal() returns NULL without raising
|
/* _PyDict_LoadGlobal() returns NULL without raising
|
||||||
* an exception if the key doesn't exist */
|
* an exception if the key doesn't exist */
|
||||||
_PyEval_FormatExcCheckArg(PyThreadState_GET(), PyExc_NameError,
|
_PyEval_FormatExcCheckArg(PyThreadState_GET(), PyExc_NameError,
|
||||||
|
@ -3128,13 +3127,16 @@ _PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name)
|
||||||
else {
|
else {
|
||||||
/* Slow-path if globals or builtins is not a dict */
|
/* Slow-path if globals or builtins is not a dict */
|
||||||
/* namespace 1: globals */
|
/* namespace 1: globals */
|
||||||
|
PyObject *res;
|
||||||
if (PyMapping_GetOptionalItem(globals, name, &res) < 0) {
|
if (PyMapping_GetOptionalItem(globals, name, &res) < 0) {
|
||||||
return NULL;
|
*writeto = PyStackRef_NULL;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
/* namespace 2: builtins */
|
/* namespace 2: builtins */
|
||||||
if (PyMapping_GetOptionalItem(builtins, name, &res) < 0) {
|
if (PyMapping_GetOptionalItem(builtins, name, &res) < 0) {
|
||||||
return NULL;
|
*writeto = PyStackRef_NULL;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
_PyEval_FormatExcCheckArg(
|
_PyEval_FormatExcCheckArg(
|
||||||
|
@ -3142,8 +3144,8 @@ _PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name)
|
||||||
NAME_ERROR_MSG, name);
|
NAME_ERROR_MSG, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*writeto = PyStackRef_FromPyObjectSteal(res);
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
|
@ -1661,15 +1661,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
case _LOAD_GLOBAL: {
|
case _LOAD_GLOBAL: {
|
||||||
_PyStackRef res;
|
_PyStackRef *res;
|
||||||
_PyStackRef null = PyStackRef_NULL;
|
_PyStackRef null = PyStackRef_NULL;
|
||||||
oparg = CURRENT_OPARG();
|
oparg = CURRENT_OPARG();
|
||||||
|
res = &stack_pointer[0];
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
||||||
PyObject *res_o = _PyEval_LoadGlobal(GLOBALS(), BUILTINS(), name);
|
_PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res);
|
||||||
if (res_o == NULL) JUMP_TO_ERROR();
|
if (PyStackRef_IsNull(*res)) JUMP_TO_ERROR();
|
||||||
null = PyStackRef_NULL;
|
null = PyStackRef_NULL;
|
||||||
res = PyStackRef_FromPyObjectSteal(res_o);
|
|
||||||
stack_pointer[0] = res;
|
|
||||||
if (oparg & 1) stack_pointer[1] = null;
|
if (oparg & 1) stack_pointer[1] = null;
|
||||||
stack_pointer += 1 + (oparg & 1);
|
stack_pointer += 1 + (oparg & 1);
|
||||||
assert(WITHIN_STACK_BOUNDS());
|
assert(WITHIN_STACK_BOUNDS());
|
||||||
|
|
|
@ -5674,7 +5674,7 @@
|
||||||
PREDICTED(LOAD_GLOBAL);
|
PREDICTED(LOAD_GLOBAL);
|
||||||
_Py_CODEUNIT *this_instr = next_instr - 5;
|
_Py_CODEUNIT *this_instr = next_instr - 5;
|
||||||
(void)this_instr;
|
(void)this_instr;
|
||||||
_PyStackRef res;
|
_PyStackRef *res;
|
||||||
_PyStackRef null = PyStackRef_NULL;
|
_PyStackRef null = PyStackRef_NULL;
|
||||||
// _SPECIALIZE_LOAD_GLOBAL
|
// _SPECIALIZE_LOAD_GLOBAL
|
||||||
{
|
{
|
||||||
|
@ -5696,13 +5696,12 @@
|
||||||
/* Skip 1 cache entry */
|
/* Skip 1 cache entry */
|
||||||
// _LOAD_GLOBAL
|
// _LOAD_GLOBAL
|
||||||
{
|
{
|
||||||
|
res = &stack_pointer[0];
|
||||||
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
|
||||||
PyObject *res_o = _PyEval_LoadGlobal(GLOBALS(), BUILTINS(), name);
|
_PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res);
|
||||||
if (res_o == NULL) goto error;
|
if (PyStackRef_IsNull(*res)) goto error;
|
||||||
null = PyStackRef_NULL;
|
null = PyStackRef_NULL;
|
||||||
res = PyStackRef_FromPyObjectSteal(res_o);
|
|
||||||
}
|
}
|
||||||
stack_pointer[0] = res;
|
|
||||||
if (oparg & 1) stack_pointer[1] = null;
|
if (oparg & 1) stack_pointer[1] = null;
|
||||||
stack_pointer += 1 + (oparg & 1);
|
stack_pointer += 1 + (oparg & 1);
|
||||||
assert(WITHIN_STACK_BOUNDS());
|
assert(WITHIN_STACK_BOUNDS());
|
||||||
|
|
|
@ -829,11 +829,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
case _LOAD_GLOBAL: {
|
case _LOAD_GLOBAL: {
|
||||||
_Py_UopsSymbol *res;
|
_Py_UopsSymbol **res;
|
||||||
_Py_UopsSymbol *null = NULL;
|
_Py_UopsSymbol *null = NULL;
|
||||||
res = sym_new_not_null(ctx);
|
res = &stack_pointer[0];
|
||||||
|
for (int _i = 1; --_i >= 0;) {
|
||||||
|
res[_i] = sym_new_not_null(ctx);
|
||||||
|
}
|
||||||
null = sym_new_null(ctx);
|
null = sym_new_null(ctx);
|
||||||
stack_pointer[0] = res;
|
|
||||||
if (oparg & 1) stack_pointer[1] = null;
|
if (oparg & 1) stack_pointer[1] = null;
|
||||||
stack_pointer += 1 + (oparg & 1);
|
stack_pointer += 1 + (oparg & 1);
|
||||||
assert(WITHIN_STACK_BOUNDS());
|
assert(WITHIN_STACK_BOUNDS());
|
||||||
|
|
Loading…
Reference in New Issue