summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authormpage <mpage@meta.com>2024-12-13 18:17:16 (GMT)
committerGitHub <noreply@github.com>2024-12-13 18:17:16 (GMT)
commit2de048ce79e621f5ae0574095b9600fe8595f607 (patch)
treede3116284fc2016192787297de7a67ba348e5825 /Python
parent292067fbc9db81896c16ff12d51c21d2b0f233e2 (diff)
downloadcpython-2de048ce79e621f5ae0574095b9600fe8595f607.zip
cpython-2de048ce79e621f5ae0574095b9600fe8595f607.tar.gz
cpython-2de048ce79e621f5ae0574095b9600fe8595f607.tar.bz2
gh-115999: Specialize loading attributes from modules in free-threaded builds (#127711)
We use the same approach that was used for specialization of LOAD_GLOBAL in free-threaded builds: _CHECK_ATTR_MODULE is renamed to _CHECK_ATTR_MODULE_PUSH_KEYS; it pushes the keys object for the following _LOAD_ATTR_MODULE_FROM_KEYS (nee _LOAD_ATTR_MODULE). This arrangement avoids having to recheck the keys version. _LOAD_ATTR_MODULE is renamed to _LOAD_ATTR_MODULE_FROM_KEYS; it loads the value from the keys object pushed by the preceding _CHECK_ATTR_MODULE_PUSH_KEYS at the cached index.
Diffstat (limited to 'Python')
-rw-r--r--Python/bytecodes.c59
-rw-r--r--Python/executor_cases.c.h70
-rw-r--r--Python/generated_cases.c.h33
-rw-r--r--Python/optimizer_analysis.c2
-rw-r--r--Python/optimizer_bytecodes.c10
-rw-r--r--Python/optimizer_cases.c.h47
-rw-r--r--Python/specialize.c92
7 files changed, 222 insertions, 91 deletions
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index f0eb540..772b46d 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2070,7 +2070,7 @@ dummy_func(
};
specializing op(_SPECIALIZE_LOAD_ATTR, (counter/1, owner -- owner)) {
- #if ENABLE_SPECIALIZATION
+ #if ENABLE_SPECIALIZATION_FT
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
next_instr = this_instr;
@@ -2079,7 +2079,7 @@ dummy_func(
}
OPCODE_DEFERRED_INC(LOAD_ATTR);
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
- #endif /* ENABLE_SPECIALIZATION */
+ #endif /* ENABLE_SPECIALIZATION_FT */
}
op(_LOAD_ATTR, (owner -- attr, self_or_null if (oparg & 1))) {
@@ -2158,33 +2158,43 @@ dummy_func(
_LOAD_ATTR_INSTANCE_VALUE +
unused/5; // Skip over rest of cache
- op(_CHECK_ATTR_MODULE, (dict_version/2, owner -- owner)) {
+ op(_CHECK_ATTR_MODULE_PUSH_KEYS, (dict_version/2, owner -- owner, mod_keys: PyDictKeysObject *)) {
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro);
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
assert(dict != NULL);
- DEOPT_IF(dict->ma_keys->dk_version != dict_version);
- }
-
- op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) {
- PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
- PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
- assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(index < dict->ma_keys->dk_nentries);
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index;
- PyObject *attr_o = ep->me_value;
+ PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys);
+ DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version);
+ mod_keys = keys;
+ }
+
+ op(_LOAD_ATTR_MODULE_FROM_KEYS, (index/1, owner, mod_keys: PyDictKeysObject * -- attr, null if (oparg & 1))) {
+ assert(mod_keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries));
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index;
+ PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value);
+ DEAD(mod_keys);
+ // Clear mod_keys from stack in case we need to deopt
+ POP_DEAD_INPUTS();
DEOPT_IF(attr_o == NULL);
- STAT_INC(LOAD_ATTR, hit);
+ #ifdef Py_GIL_DISABLED
+ int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+ if (!increfed) {
+ DEOPT_IF(true);
+ }
+ #else
Py_INCREF(attr_o);
attr = PyStackRef_FromPyObjectSteal(attr_o);
+ #endif
+ STAT_INC(LOAD_ATTR, hit);
null = PyStackRef_NULL;
- DECREF_INPUTS();
+ PyStackRef_CLOSE(owner);
}
macro(LOAD_ATTR_MODULE) =
unused/1 +
- _CHECK_ATTR_MODULE +
- _LOAD_ATTR_MODULE +
+ _CHECK_ATTR_MODULE_PUSH_KEYS +
+ _LOAD_ATTR_MODULE_FROM_KEYS +
unused/5;
op(_CHECK_ATTR_WITH_HINT, (owner -- owner)) {
@@ -4963,6 +4973,21 @@ dummy_func(
null = PyStackRef_NULL;
}
+ tier2 op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) {
+ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
+ PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
+ assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(index < dict->ma_keys->dk_nentries);
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index;
+ PyObject *attr_o = ep->me_value;
+ DEOPT_IF(attr_o == NULL);
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(attr_o);
+ attr = PyStackRef_FromPyObjectSteal(attr_o);
+ null = PyStackRef_NULL;
+ DECREF_INPUTS();
+ }
+
/* Internal -- for testing executors */
op(_INTERNAL_INCREMENT_OPT_COUNTER, (opt --)) {
_PyCounterOptimizerObject *exe = (_PyCounterOptimizerObject *)PyStackRef_AsPyObjectBorrow(opt);
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 19ba67a..55e9c3a 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -2641,8 +2641,9 @@
/* _LOAD_ATTR_INSTANCE_VALUE is split on (oparg & 1) */
- case _CHECK_ATTR_MODULE: {
+ case _CHECK_ATTR_MODULE_PUSH_KEYS: {
_PyStackRef owner;
+ PyDictKeysObject *mod_keys;
owner = stack_pointer[-1];
uint32_t dict_version = (uint32_t)CURRENT_OPERAND0();
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
@@ -2652,33 +2653,51 @@
}
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
assert(dict != NULL);
- if (dict->ma_keys->dk_version != dict_version) {
+ PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys);
+ if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
+ mod_keys = keys;
+ stack_pointer[0].bits = (uintptr_t)mod_keys;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
break;
}
- case _LOAD_ATTR_MODULE: {
+ case _LOAD_ATTR_MODULE_FROM_KEYS: {
+ PyDictKeysObject *mod_keys;
_PyStackRef owner;
_PyStackRef attr;
_PyStackRef null = PyStackRef_NULL;
oparg = CURRENT_OPARG();
- owner = stack_pointer[-1];
+ mod_keys = (PyDictKeysObject *)stack_pointer[-1].bits;
+ owner = stack_pointer[-2];
uint16_t index = (uint16_t)CURRENT_OPERAND0();
- PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
- PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
- assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(index < dict->ma_keys->dk_nentries);
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index;
- PyObject *attr_o = ep->me_value;
+ assert(mod_keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries));
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index;
+ PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value);
+ // Clear mod_keys from stack in case we need to deopt
+ stack_pointer += -1;
+ assert(WITHIN_STACK_BOUNDS());
if (attr_o == NULL) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- STAT_INC(LOAD_ATTR, hit);
+ #ifdef Py_GIL_DISABLED
+ int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+ if (!increfed) {
+ if (true) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ }
+ #else
Py_INCREF(attr_o);
attr = PyStackRef_FromPyObjectSteal(attr_o);
+ #endif
+ STAT_INC(LOAD_ATTR, hit);
null = PyStackRef_NULL;
PyStackRef_CLOSE(owner);
stack_pointer[-1] = attr;
@@ -5928,6 +5947,35 @@
break;
}
+ case _LOAD_ATTR_MODULE: {
+ _PyStackRef owner;
+ _PyStackRef attr;
+ _PyStackRef null = PyStackRef_NULL;
+ oparg = CURRENT_OPARG();
+ owner = stack_pointer[-1];
+ uint16_t index = (uint16_t)CURRENT_OPERAND0();
+ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
+ PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
+ assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(index < dict->ma_keys->dk_nentries);
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index;
+ PyObject *attr_o = ep->me_value;
+ if (attr_o == NULL) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(attr_o);
+ attr = PyStackRef_FromPyObjectSteal(attr_o);
+ null = PyStackRef_NULL;
+ PyStackRef_CLOSE(owner);
+ stack_pointer[-1] = attr;
+ if (oparg & 1) stack_pointer[0] = null;
+ stack_pointer += (oparg & 1);
+ assert(WITHIN_STACK_BOUNDS());
+ break;
+ }
+
case _INTERNAL_INCREMENT_OPT_COUNTER: {
_PyStackRef opt;
opt = stack_pointer[-1];
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 51227c9..94343f9 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -5200,7 +5200,7 @@
owner = stack_pointer[-1];
uint16_t counter = read_u16(&this_instr[1].cache);
(void)counter;
- #if ENABLE_SPECIALIZATION
+ #if ENABLE_SPECIALIZATION_FT
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
next_instr = this_instr;
@@ -5211,7 +5211,7 @@
}
OPCODE_DEFERRED_INC(LOAD_ATTR);
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
- #endif /* ENABLE_SPECIALIZATION */
+ #endif /* ENABLE_SPECIALIZATION_FT */
}
/* Skip 8 cache entries */
// _LOAD_ATTR
@@ -5553,10 +5553,11 @@
INSTRUCTION_STATS(LOAD_ATTR_MODULE);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
_PyStackRef owner;
+ PyDictKeysObject *mod_keys;
_PyStackRef attr;
_PyStackRef null = PyStackRef_NULL;
/* Skip 1 cache entry */
- // _CHECK_ATTR_MODULE
+ // _CHECK_ATTR_MODULE_PUSH_KEYS
{
owner = stack_pointer[-1];
uint32_t dict_version = read_u32(&this_instr[2].cache);
@@ -5564,21 +5565,29 @@
DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro, LOAD_ATTR);
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
assert(dict != NULL);
- DEOPT_IF(dict->ma_keys->dk_version != dict_version, LOAD_ATTR);
+ PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys);
+ DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version, LOAD_ATTR);
+ mod_keys = keys;
}
- // _LOAD_ATTR_MODULE
+ // _LOAD_ATTR_MODULE_FROM_KEYS
{
uint16_t index = read_u16(&this_instr[4].cache);
- PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
- PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
- assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(index < dict->ma_keys->dk_nentries);
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index;
- PyObject *attr_o = ep->me_value;
+ assert(mod_keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries));
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index;
+ PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value);
+ // Clear mod_keys from stack in case we need to deopt
DEOPT_IF(attr_o == NULL, LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
+ #ifdef Py_GIL_DISABLED
+ int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+ if (!increfed) {
+ DEOPT_IF(true, LOAD_ATTR);
+ }
+ #else
Py_INCREF(attr_o);
attr = PyStackRef_FromPyObjectSteal(attr_o);
+ #endif
+ STAT_INC(LOAD_ATTR, hit);
null = PyStackRef_NULL;
PyStackRef_CLOSE(owner);
}
diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c
index a4a0472..0ef15c6 100644
--- a/Python/optimizer_analysis.c
+++ b/Python/optimizer_analysis.c
@@ -95,7 +95,7 @@ type_watcher_callback(PyTypeObject* type)
static PyObject *
convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj)
{
- assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE);
+ assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE_FROM_KEYS);
assert(PyDict_CheckExact(obj));
PyDictObject *dict = (PyDictObject *)obj;
assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 42bdbd9..0b8aff0 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -492,8 +492,9 @@ dummy_func(void) {
(void)owner;
}
- op(_CHECK_ATTR_MODULE, (dict_version/2, owner -- owner)) {
+ op(_CHECK_ATTR_MODULE_PUSH_KEYS, (dict_version/2, owner -- owner, mod_keys)) {
(void)dict_version;
+ mod_keys = sym_new_not_null(ctx);
if (sym_is_const(owner)) {
PyObject *cnst = sym_get_const(owner);
if (PyModule_CheckExact(cnst)) {
@@ -515,12 +516,12 @@ dummy_func(void) {
self_or_null = sym_new_unknown(ctx);
}
- op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) {
+ op(_LOAD_ATTR_MODULE_FROM_KEYS, (index/1, owner, mod_keys -- attr, null if (oparg & 1))) {
(void)index;
null = sym_new_null(ctx);
attr = NULL;
if (this_instr[-1].opcode == _NOP) {
- // Preceding _CHECK_ATTR_MODULE was removed: mod is const and dict is watched.
+ // Preceding _CHECK_ATTR_MODULE_PUSH_KEYS was removed: mod is const and dict is watched.
assert(sym_is_const(owner));
PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
assert(PyModule_CheckExact(mod));
@@ -530,6 +531,9 @@ dummy_func(void) {
this_instr[-1].opcode = _POP_TOP;
attr = sym_new_const(ctx, res);
}
+ else {
+ this_instr->opcode = _LOAD_ATTR_MODULE;
+ }
}
if (attr == NULL) {
/* No conversion made. We don't know what `attr` is. */
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index f77a5aa..f4fbe8c 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1134,61 +1134,74 @@
break;
}
- case _CHECK_ATTR_MODULE: {
+ case _CHECK_ATTR_MODULE_PUSH_KEYS: {
_Py_UopsSymbol *owner;
+ _Py_UopsSymbol *mod_keys;
owner = stack_pointer[-1];
uint32_t dict_version = (uint32_t)this_instr->operand0;
(void)dict_version;
+ mod_keys = sym_new_not_null(ctx);
if (sym_is_const(owner)) {
PyObject *cnst = sym_get_const(owner);
if (PyModule_CheckExact(cnst)) {
PyModuleObject *mod = (PyModuleObject *)cnst;
PyObject *dict = mod->md_dict;
+ stack_pointer[0] = mod_keys;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
uint64_t watched_mutations = get_mutations(dict);
if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
PyDict_Watch(GLOBALS_WATCHER_ID, dict);
_Py_BloomFilter_Add(dependencies, dict);
this_instr->opcode = _NOP;
}
+ stack_pointer += -1;
+ assert(WITHIN_STACK_BOUNDS());
}
}
+ stack_pointer[0] = mod_keys;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
break;
}
- case _LOAD_ATTR_MODULE: {
+ case _LOAD_ATTR_MODULE_FROM_KEYS: {
_Py_UopsSymbol *owner;
_Py_UopsSymbol *attr;
_Py_UopsSymbol *null = NULL;
- owner = stack_pointer[-1];
+ owner = stack_pointer[-2];
uint16_t index = (uint16_t)this_instr->operand0;
(void)index;
null = sym_new_null(ctx);
attr = NULL;
if (this_instr[-1].opcode == _NOP) {
- // Preceding _CHECK_ATTR_MODULE was removed: mod is const and dict is watched.
+ // Preceding _CHECK_ATTR_MODULE_PUSH_KEYS was removed: mod is const and dict is watched.
assert(sym_is_const(owner));
PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
assert(PyModule_CheckExact(mod));
PyObject *dict = mod->md_dict;
- stack_pointer[-1] = attr;
- if (oparg & 1) stack_pointer[0] = null;
- stack_pointer += (oparg & 1);
+ stack_pointer[-2] = attr;
+ if (oparg & 1) stack_pointer[-1] = null;
+ stack_pointer += -1 + (oparg & 1);
assert(WITHIN_STACK_BOUNDS());
PyObject *res = convert_global_to_const(this_instr, dict);
if (res != NULL) {
this_instr[-1].opcode = _POP_TOP;
attr = sym_new_const(ctx, res);
}
- stack_pointer += -(oparg & 1);
+ else {
+ this_instr->opcode = _LOAD_ATTR_MODULE;
+ }
+ stack_pointer += 1 - (oparg & 1);
assert(WITHIN_STACK_BOUNDS());
}
if (attr == NULL) {
/* No conversion made. We don't know what `attr` is. */
attr = sym_new_not_null(ctx);
}
- stack_pointer[-1] = attr;
- if (oparg & 1) stack_pointer[0] = null;
- stack_pointer += (oparg & 1);
+ stack_pointer[-2] = attr;
+ if (oparg & 1) stack_pointer[-1] = null;
+ stack_pointer += -1 + (oparg & 1);
assert(WITHIN_STACK_BOUNDS());
break;
}
@@ -2528,6 +2541,18 @@
break;
}
+ case _LOAD_ATTR_MODULE: {
+ _Py_UopsSymbol *attr;
+ _Py_UopsSymbol *null = NULL;
+ attr = sym_new_not_null(ctx);
+ null = sym_new_null(ctx);
+ stack_pointer[-1] = attr;
+ if (oparg & 1) stack_pointer[0] = null;
+ stack_pointer += (oparg & 1);
+ assert(WITHIN_STACK_BOUNDS());
+ break;
+ }
+
case _INTERNAL_INCREMENT_OPT_COUNTER: {
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
diff --git a/Python/specialize.c b/Python/specialize.c
index fd182e7..6eb2982 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -738,22 +738,16 @@ unspecialize(_Py_CODEUNIT *instr)
}
static int function_kind(PyCodeObject *code);
+#ifndef Py_GIL_DISABLED
static bool function_check_args(PyObject *o, int expected_argcount, int opcode);
static uint32_t function_get_version(PyObject *o, int opcode);
+#endif
static uint32_t type_get_version(PyTypeObject *t, int opcode);
static int
-specialize_module_load_attr(
- PyObject *owner, _Py_CODEUNIT *instr, PyObject *name
-) {
+specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, PyObject *name)
+{
_PyAttrCache *cache = (_PyAttrCache *)(instr + 1);
- PyModuleObject *m = (PyModuleObject *)owner;
- assert((Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0);
- PyDictObject *dict = (PyDictObject *)m->md_dict;
- if (dict == NULL) {
- SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_NO_DICT);
- return -1;
- }
if (dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NON_STRING);
return -1;
@@ -773,19 +767,35 @@ specialize_module_load_attr(
SPEC_FAIL_OUT_OF_RANGE);
return -1;
}
- uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState(
- _PyInterpreterState_GET(), dict->ma_keys);
+ uint32_t keys_version = _PyDict_GetKeysVersionForCurrentState(
+ _PyInterpreterState_GET(), dict);
if (keys_version == 0) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS);
return -1;
}
write_u32(cache->version, keys_version);
cache->index = (uint16_t)index;
- instr->op.code = LOAD_ATTR_MODULE;
+ specialize(instr, LOAD_ATTR_MODULE);
return 0;
}
-
+static int
+specialize_module_load_attr(
+ PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
+{
+ PyModuleObject *m = (PyModuleObject *)owner;
+ assert((Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0);
+ PyDictObject *dict = (PyDictObject *)m->md_dict;
+ if (dict == NULL) {
+ SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_NO_DICT);
+ return -1;
+ }
+ int result;
+ Py_BEGIN_CRITICAL_SECTION(dict);
+ result = specialize_module_load_attr_lock_held(dict, instr, name);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
/* Attribute specialization */
@@ -968,7 +978,7 @@ specialize_dict_access(
}
write_u32(cache->version, type->tp_version_tag);
cache->index = (uint16_t)offset;
- instr->op.code = values_op;
+ specialize(instr, values_op);
}
else {
PyDictObject *dict = _PyObject_GetManagedDict(owner);
@@ -992,11 +1002,12 @@ specialize_dict_access(
}
cache->index = (uint16_t)index;
write_u32(cache->version, type->tp_version_tag);
- instr->op.code = hint_op;
+ specialize(instr, hint_op);
}
return 1;
}
+#ifndef Py_GIL_DISABLED
static int specialize_attr_loadclassattr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name,
PyObject* descr, DescriptorClassification kind, bool is_method);
static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name);
@@ -1093,7 +1104,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
write_u32(lm_cache->type_version, type->tp_version_tag);
/* borrowed */
write_obj(lm_cache->descr, fget);
- instr->op.code = LOAD_ATTR_PROPERTY;
+ specialize(instr, LOAD_ATTR_PROPERTY);
return 0;
}
case OBJECT_SLOT:
@@ -1117,7 +1128,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
assert(offset > 0);
cache->index = (uint16_t)offset;
write_u32(cache->version, type->tp_version_tag);
- instr->op.code = LOAD_ATTR_SLOT;
+ specialize(instr, LOAD_ATTR_SLOT);
return 0;
}
case DUNDER_CLASS:
@@ -1126,7 +1137,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
assert(offset == (uint16_t)offset);
cache->index = (uint16_t)offset;
write_u32(cache->version, type->tp_version_tag);
- instr->op.code = LOAD_ATTR_SLOT;
+ specialize(instr, LOAD_ATTR_SLOT);
return 0;
}
case OTHER_SLOT:
@@ -1162,7 +1173,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
/* borrowed */
write_obj(lm_cache->descr, descr);
write_u32(lm_cache->type_version, type->tp_version_tag);
- instr->op.code = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN;
+ specialize(instr, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN);
return 0;
}
case BUILTIN_CLASSMETHOD:
@@ -1186,6 +1197,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
if (shadow) {
goto try_instance;
}
+ set_counter((_Py_BackoffCounter*)instr + 1, adaptive_counter_cooldown());
return 0;
}
Py_UNREACHABLE();
@@ -1197,14 +1209,14 @@ try_instance:
}
return -1;
}
+#endif // Py_GIL_DISABLED
void
_Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *name)
{
- _PyAttrCache *cache = (_PyAttrCache *)(instr + 1);
PyObject *owner = PyStackRef_AsPyObjectBorrow(owner_st);
- assert(ENABLE_SPECIALIZATION);
+ assert(ENABLE_SPECIALIZATION_FT);
assert(_PyOpcode_Caches[LOAD_ATTR] == INLINE_CACHE_ENTRIES_LOAD_ATTR);
PyTypeObject *type = Py_TYPE(owner);
bool fail;
@@ -1219,22 +1231,24 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam
fail = specialize_module_load_attr(owner, instr, name);
}
else if (PyType_Check(owner)) {
+ #ifdef Py_GIL_DISABLED
+ SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR);
+ fail = true;
+ #else
fail = specialize_class_load_attr(owner, instr, name);
+ #endif
}
else {
+ #ifdef Py_GIL_DISABLED
+ SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR);
+ fail = true;
+ #else
fail = specialize_instance_load_attr(owner, instr, name);
+ #endif
}
if (fail) {
- STAT_INC(LOAD_ATTR, failure);
- assert(!PyErr_Occurred());
- instr->op.code = LOAD_ATTR;
- cache->counter = adaptive_counter_backoff(cache->counter);
- }
- else {
- STAT_INC(LOAD_ATTR, success);
- assert(!PyErr_Occurred());
- cache->counter = adaptive_counter_cooldown();
+ unspecialize(instr);
}
}
@@ -1339,6 +1353,7 @@ success:
cache->counter = adaptive_counter_cooldown();
}
+#ifndef Py_GIL_DISABLED
#ifdef Py_STATS
static int
@@ -1422,10 +1437,10 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr,
write_obj(cache->descr, descr);
if (metaclass_check) {
write_u32(cache->keys_version, Py_TYPE(cls)->tp_version_tag);
- instr->op.code = LOAD_ATTR_CLASS_WITH_METACLASS_CHECK;
+ specialize(instr, LOAD_ATTR_CLASS_WITH_METACLASS_CHECK);
}
else {
- instr->op.code = LOAD_ATTR_CLASS;
+ specialize(instr, LOAD_ATTR_CLASS);
}
return 0;
#ifdef Py_STATS
@@ -1461,7 +1476,7 @@ PyObject *descr, DescriptorClassification kind, bool is_method)
return 0;
}
write_u32(cache->keys_version, keys_version);
- instr->op.code = is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES;
+ specialize(instr, is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES);
}
else {
Py_ssize_t dictoffset;
@@ -1476,7 +1491,7 @@ PyObject *descr, DescriptorClassification kind, bool is_method)
}
}
if (dictoffset == 0) {
- instr->op.code = is_method ? LOAD_ATTR_METHOD_NO_DICT : LOAD_ATTR_NONDESCRIPTOR_NO_DICT;
+ specialize(instr, is_method ? LOAD_ATTR_METHOD_NO_DICT : LOAD_ATTR_NONDESCRIPTOR_NO_DICT);
}
else if (is_method) {
PyObject *dict = *(PyObject **) ((char *)owner + dictoffset);
@@ -1490,7 +1505,7 @@ PyObject *descr, DescriptorClassification kind, bool is_method)
dictoffset -= MANAGED_DICT_OFFSET;
assert(((uint16_t)dictoffset) == dictoffset);
cache->dict_offset = (uint16_t)dictoffset;
- instr->op.code = LOAD_ATTR_METHOD_LAZY_DICT;
+ specialize(instr, LOAD_ATTR_METHOD_LAZY_DICT);
}
else {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_CLASS_ATTR_SIMPLE);
@@ -1516,6 +1531,9 @@ PyObject *descr, DescriptorClassification kind, bool is_method)
return 1;
}
+#endif // Py_GIL_DISABLED
+
+
static void
specialize_load_global_lock_held(
PyObject *globals, PyObject *builtins,
@@ -1661,6 +1679,7 @@ function_kind(PyCodeObject *code) {
return SIMPLE_FUNCTION;
}
+#ifndef Py_GIL_DISABLED
/* Returning false indicates a failure. */
static bool
function_check_args(PyObject *o, int expected_argcount, int opcode)
@@ -1693,6 +1712,7 @@ function_get_version(PyObject *o, int opcode)
}
return version;
}
+#endif // Py_GIL_DISABLED
/* Returning 0 indicates a failure. */
static uint32_t