Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-45609: Specialize STORE_SUBSCR (alternate) #29242

Merged
merged 20 commits into from Nov 19, 2021
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
@@ -268,6 +268,7 @@ int _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *nam
int _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache);
int _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache);
int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);
int _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *instr);
int _Py_Specialize_CallFunction(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins);
void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
SpecializedCacheEntry *cache);
@@ -22,6 +22,8 @@ typedef struct {
*/
Py_ssize_t _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr);

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

#define DKIX_EMPTY (-1)
#define DKIX_DUMMY (-2) /* Used internally */

Some generated files are not rendered by default. Learn more.

@@ -238,6 +238,9 @@ def jabs_op(name, op):
"BINARY_SUBSCR_LIST_INT",
"BINARY_SUBSCR_TUPLE_INT",
"BINARY_SUBSCR_DICT",
"STORE_SUBSCR_ADAPTIVE",
"STORE_SUBSCR_LIST_INT",
"STORE_SUBSCR_DICT",
"CALL_FUNCTION_ADAPTIVE",
"CALL_FUNCTION_BUILTIN_O",
"CALL_FUNCTION_BUILTIN_FAST",
@@ -892,6 +892,14 @@ def _tracked(self, t):
gc.collect()
self.assertTrue(gc.is_tracked(t), t)

def test_string_keys_can_track_values(self):
# Test that this doesn't leak.
for i in range(10):
d = {}
for j in range(10):
d[str(j)] = j
d["foo"] = d

@support.cpython_only
def test_track_literals(self):
# Test GC-optimization of dict literals
@@ -0,0 +1 @@
Specialized the ``STORE_SUBSCR`` opcode using the PEP 659 machinery.
@@ -1055,15 +1055,14 @@ insert_into_dictkeys(PyDictKeysObject *keys, PyObject *name)
Internal routine to insert a new item into the table.
Used both by the internal resize routine and by the public insert routine.
Returns -1 if an error occurred, or 0 on success.
Consumes key and value references.
*/
static int
insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
{
PyObject *old_value;
PyDictKeyEntry *ep;

Py_INCREF(key);
Py_INCREF(value);
if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) {
if (insertion_resize(mp) < 0)
goto Fail;
@@ -1138,6 +1137,7 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
}

// Same to insertdict but specialized for ma_keys = Py_EMPTY_KEYS.
// Consumes key and value references.
static int
insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash,
PyObject *value)
@@ -1146,6 +1146,8 @@ insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash,

PyDictKeysObject *newkeys = new_keys_object(PyDict_LOG_MINSIZE);
if (newkeys == NULL) {
Py_DECREF(key);
Py_DECREF(value);
return -1;
}
if (!PyUnicode_CheckExact(key)) {
@@ -1155,8 +1157,6 @@ insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash,
mp->ma_keys = newkeys;
mp->ma_values = NULL;

Py_INCREF(key);
Py_INCREF(value);
MAINTAIN_TRACKING(mp, key, value);

size_t hashpos = (size_t)hash & (PyDict_MINSIZE-1);
@@ -1529,39 +1529,51 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
return value;
}

/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the
* dictionary if it's merely replacing the value for an existing key.
* This means that it's safe to loop over a dictionary with PyDict_Next()
* and occasionally replace a value -- but you can't insert new keys or
* remove them.
*/
/* Consumes references to key and value */
int
PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value)
_PyDict_SetItem_Take2(PyDictObject *mp, PyObject *key, PyObject *value)
{
PyDictObject *mp;
Py_hash_t hash;
if (!PyDict_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
assert(key);
assert(value);
mp = (PyDictObject *)op;
assert(PyDict_Check(mp));
Py_hash_t hash;
if (!PyUnicode_CheckExact(key) ||
(hash = ((PyASCIIObject *) key)->hash) == -1)
{
hash = PyObject_Hash(key);
if (hash == -1)
if (hash == -1) {
Py_DECREF(key);
Py_DECREF(value);
return -1;
}
}

if (mp->ma_keys == Py_EMPTY_KEYS) {
return insert_to_emptydict(mp, key, hash, value);
}
/* insertdict() handles any resizing that might be necessary */
return insertdict(mp, key, hash, value);
}

/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the
* dictionary if it's merely replacing the value for an existing key.
* This means that it's safe to loop over a dictionary with PyDict_Next()
* and occasionally replace a value -- but you can't insert new keys or
* remove them.
*/
int
PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value)
{
if (!PyDict_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
assert(key);
assert(value);
Py_INCREF(key);
Py_INCREF(value);
return _PyDict_SetItem_Take2((PyDictObject *)op, key, value);
}

int
_PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value,
Py_hash_t hash)
@@ -1577,6 +1589,8 @@ _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value,
assert(hash != -1);
mp = (PyDictObject *)op;

Py_INCREF(key);
Py_INCREF(value);
if (mp->ma_keys == Py_EMPTY_KEYS) {
return insert_to_emptydict(mp, key, hash, value);
}
@@ -1917,6 +1931,8 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
}

while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) {
Py_INCREF(key);
Py_INCREF(value);
if (insertdict(mp, key, hash, value)) {
Py_DECREF(d);
return NULL;
@@ -1936,6 +1952,8 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
}

while (_PySet_NextEntry(iterable, &pos, &key, &hash)) {
Py_INCREF(key);
Py_INCREF(value);
if (insertdict(mp, key, hash, value)) {
Py_DECREF(d);
return NULL;
@@ -2562,11 +2580,16 @@ dict_merge(PyObject *a, PyObject *b, int override)
int err = 0;
Py_INCREF(key);
Py_INCREF(value);
if (override == 1)
if (override == 1) {
Py_INCREF(key);
Py_INCREF(value);
err = insertdict(mp, key, hash, value);
}
else {
err = _PyDict_Contains_KnownHash(a, key, hash);
if (err == 0) {
Py_INCREF(key);
Py_INCREF(value);
err = insertdict(mp, key, hash, value);
}
else if (err > 0) {
@@ -2967,7 +2990,10 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
if (hash == -1)
return NULL;
}

if (mp->ma_keys == Py_EMPTY_KEYS) {
Py_INCREF(key);
Py_INCREF(defaultobj);
if (insert_to_emptydict(mp, key, hash, defaultobj) < 0) {
return NULL;
}
Loading
Loading