Skip to content

Commit a4fd7aa

Browse files
authored
GH-115776: Allow any fixed sized object to have inline values (GH-123192)
1 parent 4b7c488 commit a4fd7aa

File tree

13 files changed

+61
-38
lines changed

13 files changed

+61
-38
lines changed

Include/internal/pycore_object.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,10 +803,11 @@ _PyObject_GetManagedDict(PyObject *obj)
803803
static inline PyDictValues *
804804
_PyObject_InlineValues(PyObject *obj)
805805
{
806+
PyTypeObject *tp = Py_TYPE(obj);
807+
assert(tp->tp_basicsize > 0 && tp->tp_basicsize % sizeof(PyObject *) == 0);
806808
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
807809
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
808-
assert(Py_TYPE(obj)->tp_basicsize == sizeof(PyObject));
809-
return (PyDictValues *)((char *)obj + sizeof(PyObject));
810+
return (PyDictValues *)((char *)obj + tp->tp_basicsize);
810811
}
811812

812813
extern PyObject ** _PyObject_ComputedDictPointer(PyObject *);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Enables inline values (Python's equivalent of hidden classes) on any class
2+
who's instances are of a fixed size.

Objects/object_layout.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ So the pre-header is these two fields:
2828
If the object has no physical dictionary, then the ``dict_pointer``
2929
is set to `NULL`.
3030

31+
In 3.13 only objects with no additional data could have inline values.
32+
That is, instances of classes with `tp_basicsize == sizeof(PyObject)`.
33+
In 3.14, any object whose class has `tp_itemsize == 0` can have inline values.
34+
In both versions, the inline values starts `tp_basicsize` bytes after the object.
3135

3236
<details>
3337
<summary> 3.12 </summary>

Objects/typeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8340,7 +8340,7 @@ type_ready_managed_dict(PyTypeObject *type)
83408340
return -1;
83418341
}
83428342
}
8343-
if (type->tp_itemsize == 0 && type->tp_basicsize == sizeof(PyObject)) {
8343+
if (type->tp_itemsize == 0) {
83448344
type->tp_flags |= Py_TPFLAGS_INLINE_VALUES;
83458345
}
83468346
return 0;

Python/bytecodes.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,9 +2012,10 @@ dummy_func(
20122012
DEOPT_IF(!_PyObject_InlineValues(owner_o)->valid);
20132013
}
20142014

2015-
split op(_LOAD_ATTR_INSTANCE_VALUE, (index/1, owner -- attr, null if (oparg & 1))) {
2015+
split op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, null if (oparg & 1))) {
20162016
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
2017-
PyObject *attr_o = _PyObject_InlineValues(owner_o)->values[index];
2017+
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
2018+
PyObject *attr_o = *value_ptr;
20182019
DEOPT_IF(attr_o == NULL);
20192020
STAT_INC(LOAD_ATTR, hit);
20202021
Py_INCREF(attr_o);
@@ -2196,16 +2197,17 @@ dummy_func(
21962197
EXIT_IF(_PyObject_InlineValues(owner_o)->valid == 0);
21972198
}
21982199

2199-
op(_STORE_ATTR_INSTANCE_VALUE, (index/1, value, owner --)) {
2200+
op(_STORE_ATTR_INSTANCE_VALUE, (offset/1, value, owner --)) {
22002201
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
22012202

22022203
STAT_INC(STORE_ATTR, hit);
22032204
assert(_PyObject_GetManagedDict(owner_o) == NULL);
2204-
PyDictValues *values = _PyObject_InlineValues(owner_o);
2205-
2206-
PyObject *old_value = values->values[index];
2207-
values->values[index] = PyStackRef_AsPyObjectSteal(value);
2205+
PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset);
2206+
PyObject *old_value = *value_ptr;
2207+
*value_ptr = PyStackRef_AsPyObjectSteal(value);
22082208
if (old_value == NULL) {
2209+
PyDictValues *values = _PyObject_InlineValues(owner_o);
2210+
int index = value_ptr - values->values;
22092211
_PyDictValues_AddToInsertionOrder(values, index);
22102212
}
22112213
else {

Python/executor_cases.c.h

Lines changed: 12 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/gc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,6 +2055,9 @@ _PyObject_GC_New(PyTypeObject *tp)
20552055
return NULL;
20562056
}
20572057
_PyObject_Init(op, tp);
2058+
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
2059+
_PyObject_InitInlineValues(op, tp);
2060+
}
20582061
return op;
20592062
}
20602063

Python/gc_free_threading.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,9 @@ _PyObject_GC_New(PyTypeObject *tp)
18101810
return NULL;
18111811
}
18121812
_PyObject_Init(op, tp);
1813+
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
1814+
_PyObject_InitInlineValues(op, tp);
1815+
}
18131816
return op;
18141817
}
18151818

Python/generated_cases.c.h

Lines changed: 9 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer_bytecodes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,10 @@ dummy_func(void) {
452452
top, unused[oparg-2], bottom)) {
453453
}
454454

455-
op(_LOAD_ATTR_INSTANCE_VALUE, (index/1, owner -- attr, null if (oparg & 1))) {
455+
op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, null if (oparg & 1))) {
456456
attr = sym_new_not_null(ctx);
457457
null = sym_new_null(ctx);
458-
(void)index;
458+
(void)offset;
459459
(void)owner;
460460
}
461461

0 commit comments

Comments
 (0)