aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/executor_cases.c.h
diff options
context:
space:
mode:
authorNeil Schemenauer <nas-github@arctrix.com>2024-12-19 10:21:17 -0800
committerGitHub <noreply@github.com>2024-12-19 10:21:17 -0800
commit1b15c89a17ca3de6b05de5379b8717e9738c51ef (patch)
tree00a4ea63dec7879e81d2ea3d75f43cb8a03ee1b5 /Python/executor_cases.c.h
parentd2f1d917e8b3d2dd8f35495c7632a32688883332 (diff)
downloadcpython-1b15c89a17ca3de6b05de5379b8717e9738c51ef.tar.gz
cpython-1b15c89a17ca3de6b05de5379b8717e9738c51ef.zip
gh-115999: Specialize `STORE_ATTR` in free-threaded builds. (gh-127838)
* Add `_PyDictKeys_StringLookupSplit` which does locking on dict keys and use in place of `_PyDictKeys_StringLookup`. * Change `_PyObject_TryGetInstanceAttribute` to use that function in the case of split keys. * Add `unicodekeys_lookup_split` helper which allows code sharing between `_Py_dict_lookup` and `_PyDictKeys_StringLookupSplit`. * Fix locking for `STORE_ATTR_INSTANCE_VALUE`. Create `_GUARD_TYPE_VERSION_AND_LOCK` uop so that object stays locked and `tp_version_tag` cannot change. * Pass `tp_version_tag` to `specialize_dict_access()`, ensuring the version we store on the cache is the correct one (in case of it changing during the specalize analysis). * Split `analyze_descriptor` into `analyze_descriptor_load` and `analyze_descriptor_store` since those don't share much logic. Add `descriptor_is_class` helper function. * In `specialize_dict_access`, double check `_PyObject_GetManagedDict()` in case we race and dict was materialized before the lock. * Avoid borrowed references in `_Py_Specialize_StoreAttr()`. * Use `specialize()` and `unspecialize()` helpers. * Add unit tests to ensure specializing happens as expected in FT builds. * Add unit tests to attempt to trigger data races (useful for running under TSAN). * Add `has_split_table` function to `_testinternalcapi`.
Diffstat (limited to 'Python/executor_cases.c.h')
-rw-r--r--Python/executor_cases.c.h93
1 files changed, 69 insertions, 24 deletions
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 9bfc3e7f0b1..6e752c57cd7 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -2574,13 +2574,34 @@
uint32_t type_version = (uint32_t)CURRENT_OPERAND0();
PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner));
assert(type_version != 0);
- if (tp->tp_version_tag != type_version) {
+ if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
break;
}
+ case _GUARD_TYPE_VERSION_AND_LOCK: {
+ _PyStackRef owner;
+ owner = stack_pointer[-1];
+ uint32_t type_version = (uint32_t)CURRENT_OPERAND0();
+ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
+ assert(type_version != 0);
+ if (!LOCK_OBJECT(owner_o)) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ PyTypeObject *tp = Py_TYPE(owner_o);
+ if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) {
+ UNLOCK_OBJECT(owner_o);
+ if (true) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ }
+ break;
+ }
+
case _CHECK_MANAGED_OBJECT_HAS_VALUES: {
_PyStackRef owner;
owner = stack_pointer[-1];
@@ -2910,13 +2931,13 @@
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
assert(Py_TYPE(owner_o)->tp_dictoffset < 0);
assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
- if (_PyObject_GetManagedDict(owner_o)) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
- if (_PyObject_InlineValues(owner_o)->valid == 0) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
+ if (_PyObject_GetManagedDict(owner_o) ||
+ !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) {
+ UNLOCK_OBJECT(owner_o);
+ if (true) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
}
break;
}
@@ -2932,15 +2953,14 @@
assert(_PyObject_GetManagedDict(owner_o) == NULL);
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
PyObject *old_value = *value_ptr;
- *value_ptr = PyStackRef_AsPyObjectSteal(value);
+ FT_ATOMIC_STORE_PTR_RELEASE(*value_ptr, PyStackRef_AsPyObjectSteal(value));
if (old_value == NULL) {
PyDictValues *values = _PyObject_InlineValues(owner_o);
Py_ssize_t index = value_ptr - values->values;
_PyDictValues_AddToInsertionOrder(values, index);
}
- else {
- Py_DECREF(old_value);
- }
+ UNLOCK_OBJECT(owner_o);
+ Py_XDECREF(old_value);
PyStackRef_CLOSE(owner);
stack_pointer += -2;
assert(WITHIN_STACK_BOUNDS());
@@ -2961,30 +2981,50 @@
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- assert(PyDict_CheckExact((PyObject *)dict));
- PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
- if (hint >= (size_t)dict->ma_keys->dk_nentries) {
+ if (!LOCK_OBJECT(dict)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- if (!DK_IS_UNICODE(dict->ma_keys)) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
+ #ifdef Py_GIL_DISABLED
+ if (dict != _PyObject_GetManagedDict(owner_o)) {
+ UNLOCK_OBJECT(dict);
+ if (true) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ }
+ #endif
+ assert(PyDict_CheckExact((PyObject *)dict));
+ PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
+ if (hint >= (size_t)dict->ma_keys->dk_nentries ||
+ !DK_IS_UNICODE(dict->ma_keys)) {
+ UNLOCK_OBJECT(dict);
+ if (true) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
}
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
if (ep->me_key != name) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
+ UNLOCK_OBJECT(dict);
+ if (true) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
}
PyObject *old_value = ep->me_value;
if (old_value == NULL) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
+ UNLOCK_OBJECT(dict);
+ if (true) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
}
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value));
stack_pointer = _PyFrame_GetStackPointer(frame);
- ep->me_value = PyStackRef_AsPyObjectSteal(value);
+ FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value));
+ UNLOCK_OBJECT(dict);
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
// when dict only holds the strong reference to value in ep->me_value.
Py_XDECREF(old_value);
@@ -3002,10 +3042,15 @@
value = stack_pointer[-2];
uint16_t index = (uint16_t)CURRENT_OPERAND0();
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
+ if (!LOCK_OBJECT(owner_o)) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
char *addr = (char *)owner_o + index;
STAT_INC(STORE_ATTR, hit);
PyObject *old_value = *(PyObject **)addr;
- *(PyObject **)addr = PyStackRef_AsPyObjectSteal(value);
+ FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value));
+ UNLOCK_OBJECT(owner_o);
Py_XDECREF(old_value);
PyStackRef_CLOSE(owner);
stack_pointer += -2;