gh-124153: Simplify PyType_GetBaseByToken (GH-124488)

This commit is contained in:
neonene 2024-10-10 21:57:13 +09:00 committed by GitHub
parent 87d7315ac5
commit 120b891e4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 50 additions and 87 deletions

View File

@ -5269,11 +5269,10 @@ PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)
static PyTypeObject * static PyTypeObject *
get_base_by_token_recursive(PyTypeObject *type, void *token) get_base_by_token_recursive(PyObject *bases, void *token)
{ {
assert(PyType_GetSlot(type, Py_tp_token) != token);
PyObject *bases = lookup_tp_bases(type);
assert(bases != NULL); assert(bases != NULL);
PyTypeObject *res = NULL;
Py_ssize_t n = PyTuple_GET_SIZE(bases); Py_ssize_t n = PyTuple_GET_SIZE(bases);
for (Py_ssize_t i = 0; i < n; i++) { for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i)); PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
@ -5281,112 +5280,76 @@ get_base_by_token_recursive(PyTypeObject *type, void *token)
continue; continue;
} }
if (((PyHeapTypeObject*)base)->ht_token == token) { if (((PyHeapTypeObject*)base)->ht_token == token) {
return base; res = base;
break;
} }
base = get_base_by_token_recursive(base, token); base = get_base_by_token_recursive(lookup_tp_bases(base), token);
if (base != NULL) { if (base != NULL) {
return base; res = base;
break;
} }
} }
return NULL; return res; // Prefer to return recursively from one place
}
static inline PyTypeObject *
get_base_by_token_from_mro(PyTypeObject *type, void *token)
{
// Bypass lookup_tp_mro() as PyType_IsSubtype() does
PyObject *mro = type->tp_mro;
assert(mro != NULL);
assert(PyTuple_Check(mro));
// mro_invoke() ensures that the type MRO cannot be empty.
assert(PyTuple_GET_SIZE(mro) >= 1);
// Also, the first item in the MRO is the type itself, which is supposed
// to be already checked by the caller. We skip it in the loop.
assert(PyTuple_GET_ITEM(mro, 0) == (PyObject *)type);
assert(PyType_GetSlot(type, Py_tp_token) != token);
Py_ssize_t n = PyTuple_GET_SIZE(mro);
for (Py_ssize_t i = 1; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(mro, i));
if (!_PyType_HasFeature(base, Py_TPFLAGS_HEAPTYPE)) {
continue;
}
if (((PyHeapTypeObject*)base)->ht_token == token) {
return base;
}
}
return NULL;
}
static int
check_base_by_token(PyTypeObject *type, void *token) {
// Chain the branches, which will be optimized exclusive here
if (token == NULL) {
PyErr_Format(PyExc_SystemError,
"PyType_GetBaseByToken called with token=NULL");
return -1;
}
else if (!PyType_Check(type)) {
PyErr_Format(PyExc_TypeError,
"expected a type, got a '%T' object", type);
return -1;
}
else if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
return 0;
}
else if (((PyHeapTypeObject*)type)->ht_token == token) {
return 1;
}
else if (type->tp_mro != NULL) {
// This will not be inlined
return get_base_by_token_from_mro(type, token) ? 1 : 0;
}
else {
return get_base_by_token_recursive(type, token) ? 1 : 0;
}
} }
int int
PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result) PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)
{ {
if (result == NULL) { if (result != NULL) {
// If the `result` is checked only once here, the subsequent
// branches will become trivial to optimize.
return check_base_by_token(type, token);
}
if (token == NULL || !PyType_Check(type)) {
*result = NULL; *result = NULL;
return check_base_by_token(type, token);
} }
// Chain the branches, which will be optimized exclusive here if (token == NULL) {
PyTypeObject *base; PyErr_Format(PyExc_SystemError,
"PyType_GetBaseByToken called with token=NULL");
return -1;
}
if (!PyType_Check(type)) {
PyErr_Format(PyExc_TypeError,
"expected a type, got a '%T' object", type);
return -1;
}
if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
// No static type has a heaptype superclass, // No static type has a heaptype superclass,
// which is ensured by type_ready_mro(). // which is ensured by type_ready_mro().
*result = NULL;
return 0; return 0;
} }
else if (((PyHeapTypeObject*)type)->ht_token == token) { if (((PyHeapTypeObject*)type)->ht_token == token) {
*result = (PyTypeObject *)Py_NewRef(type); found:
if (result != NULL) {
*result = (PyTypeObject *)Py_NewRef(type);
}
return 1; return 1;
} }
else if (type->tp_mro != NULL) {
// Expect this to be inlined
base = get_base_by_token_from_mro(type, token);
}
else {
base = get_base_by_token_recursive(type, token);
}
if (base != NULL) { PyObject *mro = type->tp_mro; // No lookup, following PyType_IsSubtype()
*result = (PyTypeObject *)Py_NewRef(base); if (mro == NULL) {
return 1; PyTypeObject *base;
} base = get_base_by_token_recursive(lookup_tp_bases(type), token);
else { if (base != NULL) {
*result = NULL; // Copying the given type can cause a slowdown,
// unlike the overwrite below.
type = base;
goto found;
}
return 0; return 0;
} }
// mro_invoke() ensures that the type MRO cannot be empty.
assert(PyTuple_GET_SIZE(mro) >= 1);
// Also, the first item in the MRO is the type itself, which
// we already checked above. We skip it in the loop.
assert(PyTuple_GET_ITEM(mro, 0) == (PyObject *)type);
Py_ssize_t n = PyTuple_GET_SIZE(mro);
for (Py_ssize_t i = 1; i < n; i++) {
PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(mro, i);
if (_PyType_HasFeature(base, Py_TPFLAGS_HEAPTYPE)
&& ((PyHeapTypeObject*)base)->ht_token == token) {
type = base;
goto found;
}
}
return 0;
} }