Skip to content

Commit 8810e28

Browse files
Fidget-Spinnerpicnixzcolesbury
authored
pythongh-121459: Deferred LOAD_GLOBAL (pythonGH-123128)
Co-authored-by: Bénédikt Tran <[email protected]> Co-authored-by: Sam Gross <[email protected]>
1 parent 74330d9 commit 8810e28

File tree

8 files changed

+108
-29
lines changed

8 files changed

+108
-29
lines changed

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_
271271
PyAPI_FUNC(void) _PyObjectArray_Free(PyObject **array, PyObject **scratch);
272272

273273
PyAPI_FUNC(PyObject *) _PyEval_GetANext(PyObject *aiter);
274-
PyAPI_FUNC(PyObject *) _PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name);
274+
PyAPI_FUNC(void) _PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name, _PyStackRef *writeto);
275275
PyAPI_FUNC(PyObject *) _PyEval_GetAwaitable(PyObject *iterable, int oparg);
276276
PyAPI_FUNC(PyObject *) _PyEval_LoadName(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *name);
277277

Include/internal/pycore_dict.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010

1111
#include "pycore_object.h" // PyManagedDictPointer
1212
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_ACQUIRE
13+
#include "pycore_stackref.h" // _PyStackRef
1314

1415
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
1516
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
@@ -100,10 +101,12 @@ extern void _PyDictKeys_DecRef(PyDictKeysObject *keys);
100101
*/
101102
extern Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
102103
extern Py_ssize_t _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);
104+
extern Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr);
103105

104106
extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *);
105107
extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key);
106108
PyAPI_FUNC(PyObject *)_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
109+
PyAPI_FUNC(void) _PyDict_LoadGlobalStackRef(PyDictObject *, PyDictObject *, PyObject *, _PyStackRef *);
107110

108111
/* Consumes references to key and value */
109112
PyAPI_FUNC(int) _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value);

Objects/dictobject.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,45 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
14961496
return ix;
14971497
}
14981498

1499+
Py_ssize_t
1500+
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
1501+
{
1502+
PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys);
1503+
if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) {
1504+
Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
1505+
if (ix == DKIX_EMPTY) {
1506+
*value_addr = PyStackRef_NULL;
1507+
return ix;
1508+
}
1509+
else if (ix >= 0) {
1510+
PyObject **addr_of_value = &DK_UNICODE_ENTRIES(dk)[ix].me_value;
1511+
PyObject *value = _Py_atomic_load_ptr(addr_of_value);
1512+
if (value == NULL) {
1513+
*value_addr = PyStackRef_NULL;
1514+
return DKIX_EMPTY;
1515+
}
1516+
if (_Py_IsImmortal(value) || _PyObject_HasDeferredRefcount(value)) {
1517+
*value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_DEFERRED };
1518+
return ix;
1519+
}
1520+
if (_Py_TryIncrefCompare(addr_of_value, value)) {
1521+
*value_addr = PyStackRef_FromPyObjectSteal(value);
1522+
return ix;
1523+
}
1524+
}
1525+
}
1526+
1527+
PyObject *obj;
1528+
Py_ssize_t ix = _Py_dict_lookup_threadsafe(mp, key, hash, &obj);
1529+
if (ix >= 0 && obj != NULL) {
1530+
*value_addr = PyStackRef_FromPyObjectSteal(obj);
1531+
}
1532+
else {
1533+
*value_addr = PyStackRef_NULL;
1534+
}
1535+
return ix;
1536+
}
1537+
14991538
#else // Py_GIL_DISABLED
15001539

15011540
Py_ssize_t
@@ -1506,6 +1545,15 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
15061545
return ix;
15071546
}
15081547

1548+
Py_ssize_t
1549+
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
1550+
{
1551+
PyObject *val;
1552+
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &val);
1553+
*value_addr = val == NULL ? PyStackRef_NULL : PyStackRef_FromPyObjectNew(val);
1554+
return ix;
1555+
}
1556+
15091557
#endif
15101558

15111559
int
@@ -2420,6 +2468,32 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
24202468
return value;
24212469
}
24222470

2471+
void
2472+
_PyDict_LoadGlobalStackRef(PyDictObject *globals, PyDictObject *builtins, PyObject *key, _PyStackRef *res)
2473+
{
2474+
Py_ssize_t ix;
2475+
Py_hash_t hash;
2476+
2477+
hash = _PyObject_HashFast(key);
2478+
if (hash == -1) {
2479+
*res = PyStackRef_NULL;
2480+
return;
2481+
}
2482+
2483+
/* namespace 1: globals */
2484+
ix = _Py_dict_lookup_threadsafe_stackref(globals, key, hash, res);
2485+
if (ix == DKIX_ERROR) {
2486+
*res = PyStackRef_NULL;
2487+
}
2488+
if (ix != DKIX_EMPTY && !PyStackRef_IsNull(*res)) {
2489+
return;
2490+
}
2491+
2492+
/* namespace 2: builtins */
2493+
ix = _Py_dict_lookup_threadsafe_stackref(builtins, key, hash, res);
2494+
assert(ix >= 0 || PyStackRef_IsNull(*res));
2495+
}
2496+
24232497
/* Consumes references to key and value */
24242498
static int
24252499
setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value)

Python/bytecodes.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,8 +1469,8 @@ dummy_func(
14691469
&& PyDict_CheckExact(BUILTINS()))
14701470
{
14711471
v_o = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(),
1472-
(PyDictObject *)BUILTINS(),
1473-
name);
1472+
(PyDictObject *)BUILTINS(),
1473+
name);
14741474
if (v_o == NULL) {
14751475
if (!_PyErr_Occurred(tstate)) {
14761476
/* _PyDict_LoadGlobal() returns NULL without raising
@@ -1526,12 +1526,12 @@ dummy_func(
15261526
#endif /* ENABLE_SPECIALIZATION */
15271527
}
15281528

1529-
op(_LOAD_GLOBAL, ( -- res, null if (oparg & 1))) {
1529+
// res[1] because we need a pointer to res to pass it to _PyEval_LoadGlobalStackRef
1530+
op(_LOAD_GLOBAL, ( -- res[1], null if (oparg & 1))) {
15301531
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
1531-
PyObject *res_o = _PyEval_LoadGlobal(GLOBALS(), BUILTINS(), name);
1532-
ERROR_IF(res_o == NULL, error);
1532+
_PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res);
1533+
ERROR_IF(PyStackRef_IsNull(*res), error);
15331534
null = PyStackRef_NULL;
1534-
res = PyStackRef_FromPyObjectSteal(res_o);
15351535
}
15361536

15371537
macro(LOAD_GLOBAL) =

Python/ceval.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3110,15 +3110,14 @@ _PyEval_GetANext(PyObject *aiter)
31103110
return awaitable;
31113111
}
31123112

3113-
PyObject *
3114-
_PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name)
3113+
void
3114+
_PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name, _PyStackRef *writeto)
31153115
{
3116-
PyObject *res;
31173116
if (PyDict_CheckExact(globals) && PyDict_CheckExact(builtins)) {
3118-
res = _PyDict_LoadGlobal((PyDictObject *)globals,
3117+
_PyDict_LoadGlobalStackRef((PyDictObject *)globals,
31193118
(PyDictObject *)builtins,
3120-
name);
3121-
if (res == NULL && !PyErr_Occurred()) {
3119+
name, writeto);
3120+
if (PyStackRef_IsNull(*writeto) && !PyErr_Occurred()) {
31223121
/* _PyDict_LoadGlobal() returns NULL without raising
31233122
* an exception if the key doesn't exist */
31243123
_PyEval_FormatExcCheckArg(PyThreadState_GET(), PyExc_NameError,
@@ -3128,22 +3127,25 @@ _PyEval_LoadGlobal(PyObject *globals, PyObject *builtins, PyObject *name)
31283127
else {
31293128
/* Slow-path if globals or builtins is not a dict */
31303129
/* namespace 1: globals */
3130+
PyObject *res;
31313131
if (PyMapping_GetOptionalItem(globals, name, &res) < 0) {
3132-
return NULL;
3132+
*writeto = PyStackRef_NULL;
3133+
return;
31333134
}
31343135
if (res == NULL) {
31353136
/* namespace 2: builtins */
31363137
if (PyMapping_GetOptionalItem(builtins, name, &res) < 0) {
3137-
return NULL;
3138+
*writeto = PyStackRef_NULL;
3139+
return;
31383140
}
31393141
if (res == NULL) {
31403142
_PyEval_FormatExcCheckArg(
31413143
PyThreadState_GET(), PyExc_NameError,
31423144
NAME_ERROR_MSG, name);
31433145
}
31443146
}
3147+
*writeto = PyStackRef_FromPyObjectSteal(res);
31453148
}
3146-
return res;
31473149
}
31483150

31493151
PyObject *

Python/executor_cases.c.h

Lines changed: 4 additions & 5 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: 4 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer_cases.c.h

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

0 commit comments

Comments
 (0)