mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
GH-115776: Allow any fixed sized object to have inline values (GH-123192)
This commit is contained in:
parent
4b7c4880a0
commit
a4fd7aa4a6
13 changed files with 61 additions and 38 deletions
|
@ -803,10 +803,11 @@ _PyObject_GetManagedDict(PyObject *obj)
|
||||||
static inline PyDictValues *
|
static inline PyDictValues *
|
||||||
_PyObject_InlineValues(PyObject *obj)
|
_PyObject_InlineValues(PyObject *obj)
|
||||||
{
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(obj);
|
||||||
|
assert(tp->tp_basicsize > 0 && tp->tp_basicsize % sizeof(PyObject *) == 0);
|
||||||
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
|
||||||
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||||
assert(Py_TYPE(obj)->tp_basicsize == sizeof(PyObject));
|
return (PyDictValues *)((char *)obj + tp->tp_basicsize);
|
||||||
return (PyDictValues *)((char *)obj + sizeof(PyObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern PyObject ** _PyObject_ComputedDictPointer(PyObject *);
|
extern PyObject ** _PyObject_ComputedDictPointer(PyObject *);
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Enables inline values (Python's equivalent of hidden classes) on any class
|
||||||
|
who's instances are of a fixed size.
|
|
@ -28,6 +28,10 @@ So the pre-header is these two fields:
|
||||||
If the object has no physical dictionary, then the ``dict_pointer``
|
If the object has no physical dictionary, then the ``dict_pointer``
|
||||||
is set to `NULL`.
|
is set to `NULL`.
|
||||||
|
|
||||||
|
In 3.13 only objects with no additional data could have inline values.
|
||||||
|
That is, instances of classes with `tp_basicsize == sizeof(PyObject)`.
|
||||||
|
In 3.14, any object whose class has `tp_itemsize == 0` can have inline values.
|
||||||
|
In both versions, the inline values starts `tp_basicsize` bytes after the object.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary> 3.12 </summary>
|
<summary> 3.12 </summary>
|
||||||
|
|
|
@ -8340,7 +8340,7 @@ type_ready_managed_dict(PyTypeObject *type)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type->tp_itemsize == 0 && type->tp_basicsize == sizeof(PyObject)) {
|
if (type->tp_itemsize == 0) {
|
||||||
type->tp_flags |= Py_TPFLAGS_INLINE_VALUES;
|
type->tp_flags |= Py_TPFLAGS_INLINE_VALUES;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -2012,9 +2012,10 @@ dummy_func(
|
||||||
DEOPT_IF(!_PyObject_InlineValues(owner_o)->valid);
|
DEOPT_IF(!_PyObject_InlineValues(owner_o)->valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
split op(_LOAD_ATTR_INSTANCE_VALUE, (index/1, owner -- attr, null if (oparg & 1))) {
|
split op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, null if (oparg & 1))) {
|
||||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||||
PyObject *attr_o = _PyObject_InlineValues(owner_o)->values[index];
|
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
|
||||||
|
PyObject *attr_o = *value_ptr;
|
||||||
DEOPT_IF(attr_o == NULL);
|
DEOPT_IF(attr_o == NULL);
|
||||||
STAT_INC(LOAD_ATTR, hit);
|
STAT_INC(LOAD_ATTR, hit);
|
||||||
Py_INCREF(attr_o);
|
Py_INCREF(attr_o);
|
||||||
|
@ -2196,16 +2197,17 @@ dummy_func(
|
||||||
EXIT_IF(_PyObject_InlineValues(owner_o)->valid == 0);
|
EXIT_IF(_PyObject_InlineValues(owner_o)->valid == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_STORE_ATTR_INSTANCE_VALUE, (index/1, value, owner --)) {
|
op(_STORE_ATTR_INSTANCE_VALUE, (offset/1, value, owner --)) {
|
||||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||||
|
|
||||||
STAT_INC(STORE_ATTR, hit);
|
STAT_INC(STORE_ATTR, hit);
|
||||||
assert(_PyObject_GetManagedDict(owner_o) == NULL);
|
assert(_PyObject_GetManagedDict(owner_o) == NULL);
|
||||||
PyDictValues *values = _PyObject_InlineValues(owner_o);
|
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
|
||||||
|
PyObject *old_value = *value_ptr;
|
||||||
PyObject *old_value = values->values[index];
|
*value_ptr = PyStackRef_AsPyObjectSteal(value);
|
||||||
values->values[index] = PyStackRef_AsPyObjectSteal(value);
|
|
||||||
if (old_value == NULL) {
|
if (old_value == NULL) {
|
||||||
|
PyDictValues *values = _PyObject_InlineValues(owner_o);
|
||||||
|
int index = value_ptr - values->values;
|
||||||
_PyDictValues_AddToInsertionOrder(values, index);
|
_PyDictValues_AddToInsertionOrder(values, index);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
20
Python/executor_cases.c.h
generated
20
Python/executor_cases.c.h
generated
|
@ -2277,9 +2277,10 @@
|
||||||
_PyStackRef null = PyStackRef_NULL;
|
_PyStackRef null = PyStackRef_NULL;
|
||||||
(void)null;
|
(void)null;
|
||||||
owner = stack_pointer[-1];
|
owner = stack_pointer[-1];
|
||||||
uint16_t index = (uint16_t)CURRENT_OPERAND();
|
uint16_t offset = (uint16_t)CURRENT_OPERAND();
|
||||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||||
PyObject *attr_o = _PyObject_InlineValues(owner_o)->values[index];
|
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
|
||||||
|
PyObject *attr_o = *value_ptr;
|
||||||
if (attr_o == NULL) {
|
if (attr_o == NULL) {
|
||||||
UOP_STAT_INC(uopcode, miss);
|
UOP_STAT_INC(uopcode, miss);
|
||||||
JUMP_TO_JUMP_TARGET();
|
JUMP_TO_JUMP_TARGET();
|
||||||
|
@ -2299,9 +2300,10 @@
|
||||||
_PyStackRef null = PyStackRef_NULL;
|
_PyStackRef null = PyStackRef_NULL;
|
||||||
(void)null;
|
(void)null;
|
||||||
owner = stack_pointer[-1];
|
owner = stack_pointer[-1];
|
||||||
uint16_t index = (uint16_t)CURRENT_OPERAND();
|
uint16_t offset = (uint16_t)CURRENT_OPERAND();
|
||||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||||
PyObject *attr_o = _PyObject_InlineValues(owner_o)->values[index];
|
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
|
||||||
|
PyObject *attr_o = *value_ptr;
|
||||||
if (attr_o == NULL) {
|
if (attr_o == NULL) {
|
||||||
UOP_STAT_INC(uopcode, miss);
|
UOP_STAT_INC(uopcode, miss);
|
||||||
JUMP_TO_JUMP_TARGET();
|
JUMP_TO_JUMP_TARGET();
|
||||||
|
@ -2583,14 +2585,16 @@
|
||||||
_PyStackRef value;
|
_PyStackRef value;
|
||||||
owner = stack_pointer[-1];
|
owner = stack_pointer[-1];
|
||||||
value = stack_pointer[-2];
|
value = stack_pointer[-2];
|
||||||
uint16_t index = (uint16_t)CURRENT_OPERAND();
|
uint16_t offset = (uint16_t)CURRENT_OPERAND();
|
||||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||||
STAT_INC(STORE_ATTR, hit);
|
STAT_INC(STORE_ATTR, hit);
|
||||||
assert(_PyObject_GetManagedDict(owner_o) == NULL);
|
assert(_PyObject_GetManagedDict(owner_o) == NULL);
|
||||||
PyDictValues *values = _PyObject_InlineValues(owner_o);
|
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
|
||||||
PyObject *old_value = values->values[index];
|
PyObject *old_value = *value_ptr;
|
||||||
values->values[index] = PyStackRef_AsPyObjectSteal(value);
|
*value_ptr = PyStackRef_AsPyObjectSteal(value);
|
||||||
if (old_value == NULL) {
|
if (old_value == NULL) {
|
||||||
|
PyDictValues *values = _PyObject_InlineValues(owner_o);
|
||||||
|
int index = value_ptr - values->values;
|
||||||
_PyDictValues_AddToInsertionOrder(values, index);
|
_PyDictValues_AddToInsertionOrder(values, index);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -2055,6 +2055,9 @@ _PyObject_GC_New(PyTypeObject *tp)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
_PyObject_Init(op, tp);
|
_PyObject_Init(op, tp);
|
||||||
|
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
||||||
|
_PyObject_InitInlineValues(op, tp);
|
||||||
|
}
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1810,6 +1810,9 @@ _PyObject_GC_New(PyTypeObject *tp)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
_PyObject_Init(op, tp);
|
_PyObject_Init(op, tp);
|
||||||
|
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
|
||||||
|
_PyObject_InitInlineValues(op, tp);
|
||||||
|
}
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
Python/generated_cases.c.h
generated
15
Python/generated_cases.c.h
generated
|
@ -4999,9 +4999,10 @@
|
||||||
}
|
}
|
||||||
// _LOAD_ATTR_INSTANCE_VALUE
|
// _LOAD_ATTR_INSTANCE_VALUE
|
||||||
{
|
{
|
||||||
uint16_t index = read_u16(&this_instr[4].cache);
|
uint16_t offset = read_u16(&this_instr[4].cache);
|
||||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||||
PyObject *attr_o = _PyObject_InlineValues(owner_o)->values[index];
|
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
|
||||||
|
PyObject *attr_o = *value_ptr;
|
||||||
DEOPT_IF(attr_o == NULL, LOAD_ATTR);
|
DEOPT_IF(attr_o == NULL, LOAD_ATTR);
|
||||||
STAT_INC(LOAD_ATTR, hit);
|
STAT_INC(LOAD_ATTR, hit);
|
||||||
Py_INCREF(attr_o);
|
Py_INCREF(attr_o);
|
||||||
|
@ -6829,14 +6830,16 @@
|
||||||
// _STORE_ATTR_INSTANCE_VALUE
|
// _STORE_ATTR_INSTANCE_VALUE
|
||||||
value = stack_pointer[-2];
|
value = stack_pointer[-2];
|
||||||
{
|
{
|
||||||
uint16_t index = read_u16(&this_instr[4].cache);
|
uint16_t offset = read_u16(&this_instr[4].cache);
|
||||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||||
STAT_INC(STORE_ATTR, hit);
|
STAT_INC(STORE_ATTR, hit);
|
||||||
assert(_PyObject_GetManagedDict(owner_o) == NULL);
|
assert(_PyObject_GetManagedDict(owner_o) == NULL);
|
||||||
PyDictValues *values = _PyObject_InlineValues(owner_o);
|
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
|
||||||
PyObject *old_value = values->values[index];
|
PyObject *old_value = *value_ptr;
|
||||||
values->values[index] = PyStackRef_AsPyObjectSteal(value);
|
*value_ptr = PyStackRef_AsPyObjectSteal(value);
|
||||||
if (old_value == NULL) {
|
if (old_value == NULL) {
|
||||||
|
PyDictValues *values = _PyObject_InlineValues(owner_o);
|
||||||
|
int index = value_ptr - values->values;
|
||||||
_PyDictValues_AddToInsertionOrder(values, index);
|
_PyDictValues_AddToInsertionOrder(values, index);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -452,10 +452,10 @@ dummy_func(void) {
|
||||||
top, unused[oparg-2], bottom)) {
|
top, unused[oparg-2], bottom)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_LOAD_ATTR_INSTANCE_VALUE, (index/1, owner -- attr, null if (oparg & 1))) {
|
op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, null if (oparg & 1))) {
|
||||||
attr = sym_new_not_null(ctx);
|
attr = sym_new_not_null(ctx);
|
||||||
null = sym_new_null(ctx);
|
null = sym_new_null(ctx);
|
||||||
(void)index;
|
(void)offset;
|
||||||
(void)owner;
|
(void)owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
Python/optimizer_cases.c.h
generated
4
Python/optimizer_cases.c.h
generated
|
@ -1064,10 +1064,10 @@
|
||||||
_Py_UopsSymbol *attr;
|
_Py_UopsSymbol *attr;
|
||||||
_Py_UopsSymbol *null = NULL;
|
_Py_UopsSymbol *null = NULL;
|
||||||
owner = stack_pointer[-1];
|
owner = stack_pointer[-1];
|
||||||
uint16_t index = (uint16_t)this_instr->operand;
|
uint16_t offset = (uint16_t)this_instr->operand;
|
||||||
attr = sym_new_not_null(ctx);
|
attr = sym_new_not_null(ctx);
|
||||||
null = sym_new_null(ctx);
|
null = sym_new_null(ctx);
|
||||||
(void)index;
|
(void)offset;
|
||||||
(void)owner;
|
(void)owner;
|
||||||
stack_pointer[-1] = attr;
|
stack_pointer[-1] = attr;
|
||||||
if (oparg & 1) stack_pointer[0] = null;
|
if (oparg & 1) stack_pointer[0] = null;
|
||||||
|
|
|
@ -849,15 +849,19 @@ specialize_dict_access(
|
||||||
assert(PyUnicode_CheckExact(name));
|
assert(PyUnicode_CheckExact(name));
|
||||||
Py_ssize_t index = _PyDictKeys_StringLookup(keys, name);
|
Py_ssize_t index = _PyDictKeys_StringLookup(keys, name);
|
||||||
assert (index != DKIX_ERROR);
|
assert (index != DKIX_ERROR);
|
||||||
if (index != (uint16_t)index) {
|
if (index == DKIX_EMPTY) {
|
||||||
SPECIALIZATION_FAIL(base_op,
|
SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_NOT_IN_KEYS);
|
||||||
index == DKIX_EMPTY ?
|
return 0;
|
||||||
SPEC_FAIL_ATTR_NOT_IN_KEYS :
|
}
|
||||||
SPEC_FAIL_OUT_OF_RANGE);
|
assert(index >= 0);
|
||||||
|
char *value_addr = (char *)&_PyObject_InlineValues(owner)->values[index];
|
||||||
|
Py_ssize_t offset = value_addr - (char *)owner;
|
||||||
|
if (offset != (uint16_t)offset) {
|
||||||
|
SPECIALIZATION_FAIL(base_op, SPEC_FAIL_OUT_OF_RANGE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
write_u32(cache->version, type->tp_version_tag);
|
write_u32(cache->version, type->tp_version_tag);
|
||||||
cache->index = (uint16_t)index;
|
cache->index = (uint16_t)offset;
|
||||||
instr->op.code = values_op;
|
instr->op.code = values_op;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -69,9 +69,6 @@ def _type_unsigned_int_ptr():
|
||||||
def _sizeof_void_p():
|
def _sizeof_void_p():
|
||||||
return gdb.lookup_type('void').pointer().sizeof
|
return gdb.lookup_type('void').pointer().sizeof
|
||||||
|
|
||||||
def _sizeof_pyobject():
|
|
||||||
return gdb.lookup_type('PyObject').sizeof
|
|
||||||
|
|
||||||
def _managed_dict_offset():
|
def _managed_dict_offset():
|
||||||
# See pycore_object.h
|
# See pycore_object.h
|
||||||
pyobj = gdb.lookup_type("PyObject")
|
pyobj = gdb.lookup_type("PyObject")
|
||||||
|
@ -505,7 +502,7 @@ class HeapTypeObjectPtr(PyObjectPtr):
|
||||||
dict_ptr = dict_ptr_ptr.cast(_type_char_ptr().pointer()).dereference()
|
dict_ptr = dict_ptr_ptr.cast(_type_char_ptr().pointer()).dereference()
|
||||||
if int(dict_ptr):
|
if int(dict_ptr):
|
||||||
return None
|
return None
|
||||||
char_ptr = obj_ptr + _sizeof_pyobject()
|
char_ptr = obj_ptr + typeobj.field('tp_basicsize')
|
||||||
values_ptr = char_ptr.cast(gdb.lookup_type("PyDictValues").pointer())
|
values_ptr = char_ptr.cast(gdb.lookup_type("PyDictValues").pointer())
|
||||||
values = values_ptr['values']
|
values = values_ptr['values']
|
||||||
return PyKeysValuesPair(self.get_cached_keys(), values)
|
return PyKeysValuesPair(self.get_cached_keys(), values)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue