bpo-33597: Reduce PyGC_Head size (GH-7043) · python/cpython@5ac9e6e

@@ -251,76 +251,88 @@ PyAPI_FUNC(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, Py_ssize_t);

251251252252

/* GC information is stored BEFORE the object structure. */

253253

#ifndef Py_LIMITED_API

254-

typedef union _gc_head {

255-

struct {

256-

union _gc_head *gc_next;

257-

union _gc_head *gc_prev;

258-

Py_ssize_t gc_refs;

259-

} gc;

260-

double dummy; /* force worst-case alignment */

254+

typedef struct {

255+

// Pointer to next object in the list.

256+

// 0 means the object is not tracked

257+

uintptr_t _gc_next;

258+259+

// Pointer to previous object in the list.

260+

// Lowest two bits are used for flags documented later.

261+

uintptr_t _gc_prev;

261262

} PyGC_Head;

262263263264

extern PyGC_Head *_PyGC_generation0;

264265265266

#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)

266267268+

/* Bit flags for _gc_prev */

267269

/* Bit 0 is set when tp_finalize is called */

268-

#define _PyGC_REFS_MASK_FINALIZED (1 << 0)

269-

/* The (N-1) most significant bits contain the gc state / refcount */

270-

#define _PyGC_REFS_SHIFT (1)

271-

#define _PyGC_REFS_MASK (((size_t) -1) << _PyGC_REFS_SHIFT)

272-273-

#define _PyGCHead_REFS(g) ((g)->gc.gc_refs >> _PyGC_REFS_SHIFT)

274-

#define _PyGCHead_SET_REFS(g, v) do { \

275-

(g)->gc.gc_refs = ((g)->gc.gc_refs & ~_PyGC_REFS_MASK) \

276-

| (((size_t)(v)) << _PyGC_REFS_SHIFT); \

270+

#define _PyGC_PREV_MASK_FINALIZED (1)

271+

/* Bit 1 is set when the object is in generation which is GCed currently. */

272+

#define _PyGC_PREV_MASK_COLLECTING (2)

273+

/* The (N-2) most significant bits contain the real address. */

274+

#define _PyGC_PREV_SHIFT (2)

275+

#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT)

276+277+

// Lowest bit of _gc_next is used for flags only in GC.

278+

// But it is always 0 for normal code.

279+

#define _PyGCHead_NEXT(g) ((PyGC_Head*)(g)->_gc_next)

280+

#define _PyGCHead_SET_NEXT(g, p) ((g)->_gc_next = (uintptr_t)(p))

281+282+

// Lowest two bits of _gc_prev is used for _PyGC_PREV_MASK_* flags.

283+

#define _PyGCHead_PREV(g) ((PyGC_Head*)((g)->_gc_prev & _PyGC_PREV_MASK))

284+

#define _PyGCHead_SET_PREV(g, p) do { \

285+

assert(((uintptr_t)p & ~_PyGC_PREV_MASK) == 0); \

286+

(g)->_gc_prev = ((g)->_gc_prev & ~_PyGC_PREV_MASK) \

287+

| ((uintptr_t)(p)); \

277288

} while (0)

278-

#define _PyGCHead_DECREF(g) ((g)->gc.gc_refs -= 1 << _PyGC_REFS_SHIFT)

279289280-

#define _PyGCHead_FINALIZED(g) (((g)->gc.gc_refs & _PyGC_REFS_MASK_FINALIZED) != 0)

281-

#define _PyGCHead_SET_FINALIZED(g, v) do { \

282-

(g)->gc.gc_refs = ((g)->gc.gc_refs & ~_PyGC_REFS_MASK_FINALIZED) \

283-

| (v != 0); \

284-

} while (0)

290+

#define _PyGCHead_FINALIZED(g) (((g)->_gc_prev & _PyGC_PREV_MASK_FINALIZED) != 0)

291+

#define _PyGCHead_SET_FINALIZED(g) ((g)->_gc_prev |= _PyGC_PREV_MASK_FINALIZED)

285292286293

#define _PyGC_FINALIZED(o) _PyGCHead_FINALIZED(_Py_AS_GC(o))

287-

#define _PyGC_SET_FINALIZED(o, v) _PyGCHead_SET_FINALIZED(_Py_AS_GC(o), v)

288-289-

#define _PyGC_REFS(o) _PyGCHead_REFS(_Py_AS_GC(o))

290-291-

#define _PyGC_REFS_UNTRACKED (-2)

292-

#define _PyGC_REFS_REACHABLE (-3)

293-

#define _PyGC_REFS_TENTATIVELY_UNREACHABLE (-4)

294-295-

/* Tell the GC to track this object. NB: While the object is tracked the

296-

* collector it must be safe to call the ob_traverse method. */

294+

#define _PyGC_SET_FINALIZED(o) _PyGCHead_SET_FINALIZED(_Py_AS_GC(o))

295+296+

/* Tell the GC to track this object.

297+

*

298+

* NB: While the object is tracked by the collector, it must be safe to call the

299+

* ob_traverse method.

300+

*

301+

* Internal note: _PyGC_generation0->_gc_prev doesn't have any bit flags

302+

* because it's not object header. So we don't use _PyGCHead_PREV() and

303+

* _PyGCHead_SET_PREV() for it to avoid unnecessary bitwise operations.

304+

*/

297305

#define _PyObject_GC_TRACK(o) do { \

298306

PyGC_Head *g = _Py_AS_GC(o); \

299-

if (_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED) \

307+

if (g->_gc_next != 0) { \

300308

Py_FatalError("GC object already tracked"); \

301-

_PyGCHead_SET_REFS(g, _PyGC_REFS_REACHABLE); \

302-

g->gc.gc_next = _PyGC_generation0; \

303-

g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \

304-

g->gc.gc_prev->gc.gc_next = g; \

305-

_PyGC_generation0->gc.gc_prev = g; \

309+

} \

310+

assert((g->_gc_prev & _PyGC_PREV_MASK_COLLECTING) == 0); \

311+

PyGC_Head *last = (PyGC_Head*)(_PyGC_generation0->_gc_prev); \

312+

_PyGCHead_SET_NEXT(last, g); \

313+

_PyGCHead_SET_PREV(g, last); \

314+

_PyGCHead_SET_NEXT(g, _PyGC_generation0); \

315+

_PyGC_generation0->_gc_prev = (uintptr_t)g; \

306316

} while (0);

307317308318

/* Tell the GC to stop tracking this object.

309-

* gc_next doesn't need to be set to NULL, but doing so is a good

310-

* way to provoke memory errors if calling code is confused.

319+

*

320+

* Internal note: This may be called while GC. So _PyGC_PREV_MASK_COLLECTING must

321+

* be cleared. But _PyGC_PREV_MASK_FINALIZED bit is kept.

311322

*/

312323

#define _PyObject_GC_UNTRACK(o) do { \

313324

PyGC_Head *g = _Py_AS_GC(o); \

314-

assert(_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED); \

315-

_PyGCHead_SET_REFS(g, _PyGC_REFS_UNTRACKED); \

316-

g->gc.gc_prev->gc.gc_next = g->gc.gc_next; \

317-

g->gc.gc_next->gc.gc_prev = g->gc.gc_prev; \

318-

g->gc.gc_next = NULL; \

325+

PyGC_Head *prev = _PyGCHead_PREV(g); \

326+

PyGC_Head *next = _PyGCHead_NEXT(g); \

327+

assert(next != NULL); \

328+

_PyGCHead_SET_NEXT(prev, next); \

329+

_PyGCHead_SET_PREV(next, prev); \

330+

g->_gc_next = 0; \

331+

g->_gc_prev &= _PyGC_PREV_MASK_FINALIZED; \

319332

} while (0);

320333321334

/* True if the object is currently tracked by the GC. */

322-

#define _PyObject_GC_IS_TRACKED(o) \

323-

(_PyGC_REFS(o) != _PyGC_REFS_UNTRACKED)

335+

#define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0)

324336325337

/* True if the object may be tracked by the GC in the future, or already is.

326338

This can be useful to implement some optimizations. */