Skip to content

Commit 6d483d3

Browse files
committed
_PyObject_VAR_SIZE: always round up to a multiple-of-pointer-size value.
As Guido suggested, this makes the new subclassing code substantially simpler. But the mechanics of doing it w/ C macro semantics are a mess, and _PyObject_VAR_SIZE has a new calling sequence now. Question: The PyObject_NEW_VAR macro appears to be part of the public API. Regardless of what it expands to, the notion that it has to round up the memory it allocates is new, and extensions containing the old PyObject_NEW_VAR macro expansion (which was embedded in the PyObject_NEW_VAR expansion) won't do this rounding. But the rounding isn't actually *needed* except for new-style instances with dict pointers after a variable-length blob of embedded data. So my guess is that we do not need to bump the API version for this (as the rounding isn't needed for anything an extension can do unless it's recompiled anyway). What's your guess?
1 parent 406fe3b commit 6d483d3

File tree

4 files changed

+70
-61
lines changed

4 files changed

+70
-61
lines changed

Include/objimpl.h

+35-11
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@ form of memory management you're using).
5656
Unless you have specific memory management requirements, it is
5757
recommended to use PyObject_{New, NewVar, Del}. */
5858

59-
/*
59+
/*
6060
* Core object memory allocator
6161
* ============================
6262
*/
6363

6464
/* The purpose of the object allocator is to make the distinction
6565
between "object memory" and the rest within the Python heap.
66-
66+
6767
Object memory is the one allocated by PyObject_{New, NewVar}, i.e.
6868
the one that holds the object's representation defined by its C
6969
type structure, *excluding* any object-specific memory buffers that
@@ -172,16 +172,41 @@ extern DL_IMPORT(void) _PyObject_Del(PyObject *);
172172
( (op)->ob_size = (size), PyObject_INIT((op), (typeobj)) )
173173

174174
#define _PyObject_SIZE(typeobj) ( (typeobj)->tp_basicsize )
175-
#define _PyObject_VAR_SIZE(typeobj, n) \
176-
( (typeobj)->tp_basicsize + (n) * (typeobj)->tp_itemsize )
175+
176+
/* _PyObject_VAR_SIZE computes the amount of memory allocated for a vrbl-
177+
size object with nitems items, exclusive of gc overhead (if any). The
178+
value is rounded up to the closest multiple of sizeof(void *), in order
179+
to ensure that pointer fields at the end of the object are correctly
180+
aligned for the platform (this is of special importance for subclasses
181+
of, e.g., str or long, so that pointers can be stored after the embedded
182+
data).
183+
184+
Note that there's no memory wastage in doing this, as malloc has to
185+
return (at worst) pointer-aligned memory anyway
186+
187+
However, writing the macro to *return* the result is clumsy due to the
188+
calculations needed. Instead you must pass the result lvalue as the first
189+
argument, and it should be of type size_t (both because that's the
190+
correct conceptual type, and because using an unsigned type allows the
191+
compiler to generate faster code for the mod computation inside the
192+
macro).
193+
*/
194+
#define _PyObject_VAR_SIZE(result, typeobj, nitems) \
195+
do { \
196+
size_t mod; \
197+
(result) = (size_t) (typeobj)->tp_basicsize; \
198+
(result) += (size_t) ((nitems)*(typeobj)->tp_itemsize); \
199+
mod = (result) % SIZEOF_VOID_P; \
200+
if (mod) \
201+
(result) += SIZEOF_VOID_P - mod; \
202+
} while(0)
177203

178204
#define PyObject_NEW(type, typeobj) \
179205
( (type *) PyObject_Init( \
180206
(PyObject *) PyObject_MALLOC( _PyObject_SIZE(typeobj) ), (typeobj)) )
181-
#define PyObject_NEW_VAR(type, typeobj, n) \
182-
( (type *) PyObject_InitVar( \
183-
(PyVarObject *) PyObject_MALLOC( _PyObject_VAR_SIZE((typeobj),(n)) ),\
184-
(typeobj), (n)) )
207+
208+
#define PyObject_NEW_VAR(type, typeobj, nitems) \
209+
((type *) _PyObject_NewVar(typeobj, nitems))
185210

186211
#define PyObject_DEL(op) PyObject_FREE(op)
187212

@@ -230,8 +255,7 @@ extern DL_IMPORT(void) _PyObject_Del(PyObject *);
230255
#define PyObject_IS_GC(o) (PyType_IS_GC((o)->ob_type) && \
231256
((o)->ob_type->tp_is_gc == NULL || (o)->ob_type->tp_is_gc(o)))
232257

233-
extern DL_IMPORT(PyObject *) _PyObject_GC_Malloc(PyTypeObject *,
234-
int nitems, size_t padding);
258+
extern DL_IMPORT(PyObject *) _PyObject_GC_Malloc(PyTypeObject *, int);
235259
extern DL_IMPORT(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, int);
236260

237261
#define PyObject_GC_Resize(type, op, n) \
@@ -276,7 +300,7 @@ extern PyGC_Head _PyGC_generation0;
276300

277301
#define PyObject_GC_Track(op) _PyObject_GC_Track((PyObject *)op)
278302
#define PyObject_GC_UnTrack(op) _PyObject_GC_UnTrack((PyObject *)op)
279-
303+
280304

281305
#define PyObject_GC_New(type, typeobj) \
282306
( (type *) _PyObject_GC_New(typeobj) )

Modules/gcmodule.c

+21-14
Original file line numberDiff line numberDiff line change
@@ -798,14 +798,17 @@ _PyObject_GC_UnTrack(PyObject *op)
798798
}
799799

800800
PyObject *
801-
_PyObject_GC_Malloc(PyTypeObject *tp, int nitems, size_t padding)
801+
_PyObject_GC_Malloc(PyTypeObject *tp, int nitems)
802802
{
803803
PyObject *op;
804+
size_t basicsize;
804805
#ifdef WITH_CYCLE_GC
805-
const size_t basic = (size_t)_PyObject_VAR_SIZE(tp, nitems);
806-
const size_t nbytes = sizeof(PyGC_Head) + basic + padding;
806+
size_t nbytes;
807+
PyGC_Head *g;
807808

808-
PyGC_Head *g = PyObject_MALLOC(nbytes);
809+
_PyObject_VAR_SIZE(basicsize, tp, nitems);
810+
nbytes = sizeof(PyGC_Head) + basicsize;
811+
g = PyObject_MALLOC(nbytes);
809812
if (g == NULL)
810813
return (PyObject *)PyErr_NoMemory();
811814
g->gc_next = NULL;
@@ -821,7 +824,8 @@ _PyObject_GC_Malloc(PyTypeObject *tp, int nitems, size_t padding)
821824
}
822825
op = FROM_GC(g);
823826
#else
824-
op = PyObject_MALLOC(_PyObject_VAR_SIZE(tp, nitems) + padding);
827+
_PyObject_VAR_SIZE(basicsize, tp, nitems);
828+
op = PyObject_MALLOC(basicsize);
825829
if (op == NULL)
826830
return (PyObject *)PyErr_NoMemory();
827831

@@ -832,33 +836,36 @@ _PyObject_GC_Malloc(PyTypeObject *tp, int nitems, size_t padding)
832836
PyObject *
833837
_PyObject_GC_New(PyTypeObject *tp)
834838
{
835-
PyObject *op = _PyObject_GC_Malloc(tp, 0, 0);
839+
PyObject *op = _PyObject_GC_Malloc(tp, 0);
836840
return PyObject_INIT(op, tp);
837841
}
838842

839843
PyVarObject *
840-
_PyObject_GC_NewVar(PyTypeObject *tp, int size)
844+
_PyObject_GC_NewVar(PyTypeObject *tp, int nitems)
841845
{
842-
PyVarObject *op = (PyVarObject *) _PyObject_GC_Malloc(tp, size, 0);
843-
return PyObject_INIT_VAR(op, tp, size);
846+
PyVarObject *op = (PyVarObject *) _PyObject_GC_Malloc(tp, nitems);
847+
return PyObject_INIT_VAR(op, tp, nitems);
844848
}
845849

846850
PyVarObject *
847-
_PyObject_GC_Resize(PyVarObject *op, int size)
851+
_PyObject_GC_Resize(PyVarObject *op, int nitems)
848852
{
853+
size_t basicsize;
849854
#ifdef WITH_CYCLE_GC
850855
PyGC_Head *g = AS_GC(op);
851-
g = PyObject_REALLOC(g, _PyObject_VAR_SIZE(op->ob_type, size) +
852-
sizeof(PyGC_Head));
856+
857+
_PyObject_VAR_SIZE(basicsize, op->ob_type, nitems);
858+
g = PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize);
853859
if (g == NULL)
854860
return (PyVarObject *)PyErr_NoMemory();
855861
op = (PyVarObject *) FROM_GC(g);
856862
#else
857-
op = PyObject_REALLOC(op, _PyObject_VAR_SIZE(op->ob_type, size));
863+
_PyObject_VAR_SIZE(basicsize, op->ob_type, nitems);
864+
op = PyObject_REALLOC(op, basicsize);
858865
if (op == NULL)
859866
return (PyVarObject *)PyErr_NoMemory();
860867
#endif
861-
op->ob_size = size;
868+
op->ob_size = nitems;
862869
return op;
863870
}
864871

Objects/object.c

+11-18
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,16 @@ _PyObject_New(PyTypeObject *tp)
127127
}
128128

129129
PyVarObject *
130-
_PyObject_NewVar(PyTypeObject *tp, int size)
130+
_PyObject_NewVar(PyTypeObject *tp, int nitems)
131131
{
132132
PyVarObject *op;
133-
op = (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size));
133+
size_t size;
134+
135+
_PyObject_VAR_SIZE(size, tp, nitems);
136+
op = (PyVarObject *) PyObject_MALLOC(size);
134137
if (op == NULL)
135138
return (PyVarObject *)PyErr_NoMemory();
136-
return PyObject_INIT_VAR(op, tp, size);
139+
return PyObject_INIT_VAR(op, tp, nitems);
137140
}
138141

139142
void
@@ -1146,8 +1149,6 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
11461149
PyObject **
11471150
_PyObject_GetDictPtr(PyObject *obj)
11481151
{
1149-
#define PTRSIZE (sizeof(PyObject *))
1150-
11511152
long dictoffset;
11521153
PyTypeObject *tp = obj->ob_type;
11531154

@@ -1157,19 +1158,11 @@ _PyObject_GetDictPtr(PyObject *obj)
11571158
if (dictoffset == 0)
11581159
return NULL;
11591160
if (dictoffset < 0) {
1160-
/* dictoffset is positive by the time we're ready to round
1161-
it, and compilers can generate faster rounding code if
1162-
they know that. */
1163-
unsigned long udo; /* unsigned dictoffset */
1164-
const long nitems = ((PyVarObject *)obj)->ob_size;
1165-
const long size = _PyObject_VAR_SIZE(tp, nitems);
1166-
1167-
dictoffset += size;
1168-
assert(dictoffset > 0); /* Sanity check */
1169-
/* Round up to multiple of PTRSIZE. */
1170-
udo = (unsigned long)dictoffset;
1171-
udo = ((udo + PTRSIZE-1) / PTRSIZE) * PTRSIZE;
1172-
dictoffset = (long)udo;
1161+
size_t size;
1162+
_PyObject_VAR_SIZE(size, tp, ((PyVarObject *)obj)->ob_size);
1163+
dictoffset += (long)size;
1164+
assert(dictoffset > 0);
1165+
assert(dictoffset % SIZEOF_VOID_P == 0);
11731166
}
11741167
return (PyObject **) ((char *)obj + dictoffset);
11751168
}

Objects/typeobject.c

+3-18
Original file line numberDiff line numberDiff line change
@@ -190,28 +190,13 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
190190
PyObject *
191191
PyType_GenericAlloc(PyTypeObject *type, int nitems)
192192
{
193-
#define PTRSIZE (sizeof(PyObject *))
194-
195-
size_t size = (size_t)_PyObject_VAR_SIZE(type, nitems);
196-
size_t padding = 0;
197193
PyObject *obj;
194+
size_t size;
198195

199-
/* Round up size, if necessary, so that the __dict__ pointer
200-
following the variable part is properly aligned for the platform.
201-
This is needed only for types with a vrbl number of items
202-
before the __dict__ pointer == types that record the dict offset
203-
as a negative offset from the end of the object. If tp_dictoffset
204-
is 0, there is no __dict__; if positive, tp_dict was declared in a C
205-
struct so the compiler already took care of aligning it. */
206-
if (type->tp_dictoffset < 0) {
207-
padding = PTRSIZE - size % PTRSIZE;
208-
if (padding == PTRSIZE)
209-
padding = 0;
210-
size += padding;
211-
}
196+
_PyObject_VAR_SIZE(size, type, nitems);
212197

213198
if (PyType_IS_GC(type))
214-
obj = _PyObject_GC_Malloc(type, nitems, padding);
199+
obj = _PyObject_GC_Malloc(type, nitems);
215200
else
216201
obj = PyObject_MALLOC(size);
217202

0 commit comments

Comments
 (0)