diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index e4af731be0e87f..a97b53028c8f59 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -271,7 +271,7 @@ PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ PyAPI_FUNC(void) _PyObjectArray_Free(PyObject **array, PyObject **scratch); 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_LoadName(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *name); diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 100250928d2064..f9a043b0208c8f 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -10,6 +10,7 @@ extern "C" { #include "pycore_object.h" // PyManagedDictPointer #include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_ACQUIRE +#include "pycore_stackref.h" // _PyStackRef // Unsafe flavor of PyDict_GetItemWithError(): no error checking 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_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 _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key); PyAPI_FUNC(PyObject *)_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *); +PyAPI_FUNC(void) _PyDict_LoadGlobalStackRef(PyDictObject *, PyDictObject *, PyObject *, _PyStackRef *); /* Consumes references to key and value */ PyAPI_FUNC(int) _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value); diff --git a/Objects/dictobject.c b/Objects/dictobject.c index b81ed189456ec1..006bc593c2a754 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1496,6 +1496,45 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb 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 Py_ssize_t @@ -1506,6 +1545,15 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb 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 int @@ -2420,6 +2468,32 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) 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 */ static int setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 078f06d697cc3c..846404e28bb18f 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1469,8 +1469,8 @@ dummy_func( && PyDict_CheckExact(BUILTINS())) { v_o = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(), - (PyDictObject *)BUILTINS(), - name); + (PyDictObject *)BUILTINS(), + name); if (v_o == NULL) { if (!_PyErr_Occurred(tstate)) { /* _PyDict_LoadGlobal() returns NULL without raising @@ -1526,12 +1526,12 @@ dummy_func( #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 *res_o = _PyEval_LoadGlobal(GLOBALS(), BUILTINS(), name); - ERROR_IF(res_o == NULL, error); + _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); + ERROR_IF(PyStackRef_IsNull(*res), error); null = PyStackRef_NULL; - res = PyStackRef_FromPyObjectSteal(res_o); } macro(LOAD_GLOBAL) = diff --git a/Python/ceval.c b/Python/ceval.c index 252833a6243293..6236c668ee65eb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3110,15 +3110,14 @@ _PyEval_GetANext(PyObject *aiter) return awaitable; } -PyObject * -_PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name) +void +_PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name, _PyStackRef *writeto) { - PyObject *res; if (PyDict_CheckExact(globals) && PyDict_CheckExact(builtins)) { - res = _PyDict_LoadGlobal((PyDictObject *)globals, + _PyDict_LoadGlobalStackRef((PyDictObject *)globals, (PyDictObject *)builtins, - name); - if (res == NULL && !PyErr_Occurred()) { + name, writeto); + if (PyStackRef_IsNull(*writeto) && !PyErr_Occurred()) { /* _PyDict_LoadGlobal() returns NULL without raising * an exception if the key doesn't exist */ _PyEval_FormatExcCheckArg(PyThreadState_GET(), PyExc_NameError, @@ -3128,13 +3127,16 @@ _PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name) else { /* Slow-path if globals or builtins is not a dict */ /* namespace 1: globals */ + PyObject *res; if (PyMapping_GetOptionalItem(globals, name, &res) < 0) { - return NULL; + *writeto = PyStackRef_NULL; + return; } if (res == NULL) { /* namespace 2: builtins */ if (PyMapping_GetOptionalItem(builtins, name, &res) < 0) { - return NULL; + *writeto = PyStackRef_NULL; + return; } if (res == NULL) { _PyEval_FormatExcCheckArg( @@ -3142,8 +3144,8 @@ _PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name) NAME_ERROR_MSG, name); } } + *writeto = PyStackRef_FromPyObjectSteal(res); } - return res; } PyObject * diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index b36fff9961febe..93ab068f9de949 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1661,15 +1661,14 @@ } case _LOAD_GLOBAL: { - _PyStackRef res; + _PyStackRef *res; _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); + res = &stack_pointer[0]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - PyObject *res_o = _PyEval_LoadGlobal(GLOBALS(), BUILTINS(), name); - if (res_o == NULL) JUMP_TO_ERROR(); + _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); + if (PyStackRef_IsNull(*res)) JUMP_TO_ERROR(); null = PyStackRef_NULL; - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; if (oparg & 1) stack_pointer[1] = null; stack_pointer += 1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3e9f0396e495b7..6d902e2c1d9ba8 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5674,7 +5674,7 @@ PREDICTED(LOAD_GLOBAL); _Py_CODEUNIT *this_instr = next_instr - 5; (void)this_instr; - _PyStackRef res; + _PyStackRef *res; _PyStackRef null = PyStackRef_NULL; // _SPECIALIZE_LOAD_GLOBAL { @@ -5696,13 +5696,12 @@ /* Skip 1 cache entry */ // _LOAD_GLOBAL { + res = &stack_pointer[0]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - PyObject *res_o = _PyEval_LoadGlobal(GLOBALS(), BUILTINS(), name); - if (res_o == NULL) goto error; + _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); + if (PyStackRef_IsNull(*res)) goto error; null = PyStackRef_NULL; - res = PyStackRef_FromPyObjectSteal(res_o); } - stack_pointer[0] = res; if (oparg & 1) stack_pointer[1] = null; stack_pointer += 1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index e89c969ad0df67..a6cfa271ae6758 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -829,11 +829,13 @@ } case _LOAD_GLOBAL: { - _Py_UopsSymbol *res; + _Py_UopsSymbol **res; _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); - stack_pointer[0] = res; if (oparg & 1) stack_pointer[1] = null; stack_pointer += 1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS());