summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst5
-rw-r--r--Objects/typeobject.c40
2 files changed, 18 insertions, 27 deletions
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst b/Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst
new file mode 100644
index 0000000..5e306ff
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst
@@ -0,0 +1,5 @@
+Class version tags are no longer recycled.
+
+This means that a version tag serves as a unique identifier for the state of
+a class. We rely on this for effective specialization of the LOAD_ATTR and
+other instructions.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 7ae50c4..1efb9fc 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -45,7 +45,7 @@ class object "PyObject *" "&PyBaseObject_Type"
// bpo-42745: next_version_tag remains shared by all interpreters because of static types
// Used to set PyTypeObject.tp_version_tag
-static unsigned int next_version_tag = 0;
+static unsigned int next_version_tag = 1;
typedef struct PySlot_Offset {
short subslot_offset;
@@ -233,24 +233,14 @@ get_type_cache(void)
static void
-type_cache_clear(struct type_cache *cache, int use_none)
+type_cache_clear(struct type_cache *cache, PyObject *value)
{
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
struct type_cache_entry *entry = &cache->hashtable[i];
entry->version = 0;
- if (use_none) {
- // Set to None so _PyType_Lookup() can use Py_SETREF(),
- // rather than using slower Py_XSETREF().
- Py_XSETREF(entry->name, Py_NewRef(Py_None));
- }
- else {
- Py_CLEAR(entry->name);
- }
+ Py_XSETREF(entry->name, _Py_XNewRef(value));
entry->value = NULL;
}
-
- // Mark all version tags as invalid
- PyType_Modified(&PyBaseObject_Type);
}
@@ -287,14 +277,11 @@ _PyType_ClearCache(PyInterpreterState *interp)
sizeof(cache->hashtable) / 1024);
#endif
- unsigned int cur_version_tag = next_version_tag - 1;
- if (_Py_IsMainInterpreter(interp)) {
- next_version_tag = 0;
- }
+ // Set to None, rather than NULL, so _PyType_Lookup() can
+ // use Py_SETREF() rather than using slower Py_XSETREF().
+ type_cache_clear(cache, Py_None);
- type_cache_clear(cache, 0);
-
- return cur_version_tag;
+ return next_version_tag - 1;
}
@@ -309,7 +296,8 @@ PyType_ClearCache(void)
void
_PyType_Fini(PyInterpreterState *interp)
{
- _PyType_ClearCache(interp);
+ struct type_cache *cache = &interp->type_cache;
+ type_cache_clear(cache, NULL);
if (_Py_IsMainInterpreter(interp)) {
clear_slotdefs();
}
@@ -426,14 +414,12 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type)
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
return 0;
- type->tp_version_tag = next_version_tag++;
- /* for stress-testing: next_version_tag &= 0xFF; */
-
- if (type->tp_version_tag == 0) {
- // Wrap-around or just starting Python - clear the whole cache
- type_cache_clear(cache, 1);
+ if (next_version_tag == 0) {
+ /* We have run out of version numbers */
return 0;
}
+ type->tp_version_tag = next_version_tag++;
+ assert (type->tp_version_tag != 0);
bases = type->tp_bases;
n = PyTuple_GET_SIZE(bases);