Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Include/internal/pycore_floatobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ struct _Py_float_state {
#endif
};

void _PyFloat_ExactDealloc(PyObject *op);

/* _PyFloat_{Pack,Unpack}{4,8}
*
* The struct and pickle (at least) modules need an efficient platform-
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ PyAPI_FUNC(char*) _PyLong_FormatBytesWriter(
int base,
int alternate);

void _PyLong_ExactDealloc(PyObject *op);

#ifdef __cplusplus
}
#endif
Expand Down
31 changes: 31 additions & 0 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,37 @@ extern "C" {
.ob_size = size, \
}

static inline void
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
{
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
#endif
if (--op->ob_refcnt != 0) {
assert(op->ob_refcnt > 0);
}
else {
#ifdef Py_TRACE_REFS
_Py_ForgetReference(op);
#endif
destruct(op);
}
}

static inline void
_Py_DECREF_IMMORTAL(PyObject *op)
{
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
#endif
op->ob_refcnt--;
#ifdef Py_DEBUG
if (op->ob_refcnt <= 0) {
// Calls _Py_FatalRefcountError for None, True, and False
_Py_Dealloc(op);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change this to call _Py_FatalRefcountError directly?
We might want to use _Py_DECREF_IMMORTAL for other "immortal" objects like 0 or () and there is no guarantee that their de-allocation functions will call _Py_FatalRefcountError.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I moved _Py_FatalRefcountError from pycore_pyerrors to pycore_object as well, rather than having an #include-dependence between the pycore_files.

}
#endif
}

PyAPI_FUNC(int) _PyType_CheckConsistency(PyTypeObject *type);
PyAPI_FUNC(int) _PyDict_CheckConsistency(PyObject *mp, int check_content);
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extern "C" {

#include "pycore_fileutils.h" // _Py_error_handler

void _PyUnicode_ExactDealloc(PyObject *op);

/* runtime lifecycle */

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add type-specialized versions of the ``Py_DECREF()``, and use them for ``float``, ``int``, ``str``, ``bool``, and ``None`` to avoid pointer-chasing at runtime where types are known at C compile time.
41 changes: 27 additions & 14 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,28 +238,41 @@ PyFloat_FromString(PyObject *v)
return result;
}

static void
float_dealloc(PyFloatObject *op)
void
_PyFloat_ExactDealloc(PyObject *obj)
{
assert(PyFloat_CheckExact(obj));
PyFloatObject *op = (PyFloatObject *)obj;
#if PyFloat_MAXFREELIST > 0
if (PyFloat_CheckExact(op)) {
struct _Py_float_state *state = get_float_state();
struct _Py_float_state *state = get_float_state();
#ifdef Py_DEBUG
// float_dealloc() must not be called after _PyFloat_Fini()
assert(state->numfree != -1);
// float_dealloc() must not be called after _PyFloat_Fini()
assert(state->numfree != -1);
#endif
if (state->numfree >= PyFloat_MAXFREELIST) {
PyObject_Free(op);
return;
}
state->numfree++;
Py_SET_TYPE(op, (PyTypeObject *)state->free_list);
state->free_list = op;
if (state->numfree >= PyFloat_MAXFREELIST) {
PyObject_Free(op);
return;
}
state->numfree++;
Py_SET_TYPE(op, (PyTypeObject *)state->free_list);
state->free_list = op;
#else
PyObject_Free(op);
#endif
}

static void
float_dealloc(PyObject *op)
{
assert(PyFloat_Check(op));
#if PyFloat_MAXFREELIST > 0
if (PyFloat_CheckExact(op)) {
_PyFloat_ExactDealloc(op);
}
else
#endif
{
Py_TYPE(op)->tp_free((PyObject *)op);
Py_TYPE(op)->tp_free(op);
}
}

Expand Down
44 changes: 30 additions & 14 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,16 @@ medium_value(PyLongObject *x)
#define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS)
#define IS_SMALL_UINT(ival) ((ival) < _PY_NSMALLPOSINTS)

static inline int is_medium_int(stwodigits x)
static inline void
_Py_DECREF_INT(PyLongObject *op)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this ultimately equivalent to

#define _Py_DECREF_INT(op) _Py_DECREF_SPECIALIZED(op, PyObject_Free)

?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed _PyLongExactDealloc, but I'd like to keep the assertion around to make sure we're never accidentally calling this on int subclasses.

{
assert(PyLong_CheckExact(op));
_Py_DECREF_SPECIALIZED((PyObject *)op,
(destructor)_PyLong_ExactDealloc);
}

static inline int
is_medium_int(stwodigits x)
{
/* Take care that we are comparing unsigned values. */
twodigits x_plus_mask = ((twodigits)x) + PyLong_MASK;
Expand All @@ -61,7 +70,7 @@ maybe_small_long(PyLongObject *v)
if (v && IS_MEDIUM_VALUE(v)) {
stwodigits ival = medium_value(v);
if (IS_SMALL_INT(ival)) {
Py_DECREF(v);
_Py_DECREF_INT(v);
return (PyLongObject *)get_small_int((sdigit)ival);
}
}
Expand Down Expand Up @@ -1824,7 +1833,7 @@ long_to_decimal_string_internal(PyObject *aa,
#undef WRITE_DIGITS
#undef WRITE_UNICODE_DIGITS

Py_DECREF(scratch);
_Py_DECREF_INT(scratch);
if (writer) {
writer->pos += strlen;
}
Expand Down Expand Up @@ -3479,15 +3488,15 @@ k_mul(PyLongObject *a, PyLongObject *b)
*/
i = Py_SIZE(ret) - shift; /* # digits after shift */
(void)v_isub(ret->ob_digit + shift, i, t2->ob_digit, Py_SIZE(t2));
Py_DECREF(t2);
_Py_DECREF_INT(t2);

(void)v_isub(ret->ob_digit + shift, i, t1->ob_digit, Py_SIZE(t1));
Py_DECREF(t1);
_Py_DECREF_INT(t1);

/* 6. t3 <- (ah+al)(bh+bl), and add into result. */
if ((t1 = x_add(ah, al)) == NULL) goto fail;
Py_DECREF(ah);
Py_DECREF(al);
_Py_DECREF_INT(ah);
_Py_DECREF_INT(al);
ah = al = NULL;

if (a == b) {
Expand All @@ -3498,21 +3507,21 @@ k_mul(PyLongObject *a, PyLongObject *b)
Py_DECREF(t1);
goto fail;
}
Py_DECREF(bh);
Py_DECREF(bl);
_Py_DECREF_INT(bh);
_Py_DECREF_INT(bl);
bh = bl = NULL;

t3 = k_mul(t1, t2);
Py_DECREF(t1);
Py_DECREF(t2);
_Py_DECREF_INT(t1);
_Py_DECREF_INT(t2);
if (t3 == NULL) goto fail;
assert(Py_SIZE(t3) >= 0);

/* Add t3. It's not obvious why we can't run out of room here.
* See the (*) comment after this function.
*/
(void)v_iadd(ret->ob_digit + shift, i, t3->ob_digit, Py_SIZE(t3));
Py_DECREF(t3);
_Py_DECREF_INT(t3);

return long_normalize(ret);

Expand Down Expand Up @@ -3617,13 +3626,13 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b)
/* Add into result. */
(void)v_iadd(ret->ob_digit + nbdone, Py_SIZE(ret) - nbdone,
product->ob_digit, Py_SIZE(product));
Py_DECREF(product);
_Py_DECREF_INT(product);

bsize -= nbtouse;
nbdone += nbtouse;
}

Py_DECREF(bslice);
_Py_DECREF_INT(bslice);
return long_normalize(ret);

fail:
Expand Down Expand Up @@ -5743,6 +5752,13 @@ int_from_bytes_impl(PyTypeObject *type, PyObject *bytes_obj,
return long_obj;
}

void
_PyLong_ExactDealloc(PyObject *op)
Comment thread
sweeneyde marked this conversation as resolved.
Outdated
{
assert(PyLong_CheckExact(op));
PyObject_Del(op);
}

static PyObject *
long_long_meth(PyObject *self, PyObject *Py_UNUSED(ignored))
{
Expand Down
7 changes: 7 additions & 0 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -15463,6 +15463,13 @@ unicode_subtype_new(PyTypeObject *type, PyObject *unicode)
return NULL;
}

void
_PyUnicode_ExactDealloc(PyObject *op)
{
assert(PyUnicode_CheckExact(op));
unicode_dealloc(op);
}

PyDoc_STRVAR(unicode_doc,
"str(object='') -> str\n\
str(bytes_or_buffer[, encoding[, errors]]) -> str\n\
Expand Down
2 changes: 1 addition & 1 deletion Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2520,7 +2520,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
}
if (PyFloat_CheckExact(item)) {
f_result += PyFloat_AS_DOUBLE(item);
Py_DECREF(item);
_Py_DECREF_SPECIALIZED(item, (destructor)_PyFloat_ExactDealloc);
Comment thread
sweeneyde marked this conversation as resolved.
Outdated
continue;
}
if (PyLong_Check(item)) {
Expand Down
Loading