Commit d8754ab9 authored by Antonio Cuni's avatar Antonio Cuni
Browse files

(antocuni, arigo): implement PyInt_FromLong in C and use freelists for...

(antocuni, arigo): implement PyInt_FromLong in C and use freelists for allocation, copying the code from CPython; plus, some smaller changes here and there which are needed to implement this.

branch : cpyext-avoid-roundtrip
parent 77686cdbb6fe
......@@ -628,7 +628,7 @@ SYMBOLS_C = [
'_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc',
'Py_IncRef', 'Py_DecRef', 'PyObject_Free', 'PyObject_GC_Del', 'PyType_GenericAlloc',
'_PyObject_New', '_PyObject_NewVar', '_PyObject_GC_New',
'PyObject_Init', 'PyObject_InitVar',
'PyObject_Init', 'PyObject_InitVar', 'PyInt_FromLong',
TYPES = {}
......@@ -1120,9 +1120,17 @@ def attach_c_functions(space, eci, prefix):
[PyTypeObjectPtr, Py_ssize_t], PyObject,
state.C.PyInt_FromLong = rffi.llexternal(
mangle_name(prefix, 'PyInt_FromLong'),
[rffi.LONG], PyObject,
_, state.C.set_marker = rffi.CExternVariable(
Py_ssize_t, '_pypy_rawrefcount_w_marker_deallocating',
eci, _nowrapper=True, c_type='Py_ssize_t')
state.C._PyPy_int_dealloc = rffi.llexternal(
'_PyPy_int_dealloc', [PyObject], lltype.Void,
compilation_info=eci, _nowrapper=True)
state.C._PyPy_subtype_dealloc = rffi.llexternal(
'_PyPy_subtype_dealloc', [PyObject], lltype.Void,
compilation_info=eci, _nowrapper=True)
......@@ -1445,6 +1453,7 @@ separate_module_files = [source_dir / "varargwrapper.c",
source_dir / "pymem.c",
source_dir / "object.c",
source_dir / "typeobject.c",
source_dir / "intobject.c",
def build_eci(code, use_micronumpy=False, translating=False):
......@@ -16,6 +16,9 @@ typedef struct {
PyType_FastSubclass((op)->ob_type, Py_TPFLAGS_INT_SUBCLASS)
#define PyInt_CheckExact(op) ((op)->ob_type == &PyInt_Type)
PyAPI_FUNC(PyObject *) PyInt_FromLong(long);
PyAPI_FUNC(void) _PyPy_int_dealloc(PyObject *);
#ifdef __cplusplus
......@@ -5,7 +5,8 @@ from pypy.module.cpyext.api import (
cpython_api, cpython_struct, build_type_checkers_flags, bootstrap_function,
PyObject, PyObjectFields, CONST_STRING, CANNOT_FAIL, Py_ssize_t)
from pypy.module.cpyext.pyobject import (
make_typedescr, track_reference, from_ref)
make_typedescr, track_reference, from_ref, BaseCpyTypedescr)
from pypy.module.cpyext.state import State
from rpython.rlib.rarithmetic import r_uint, intmask, LONG_TEST, r_ulonglong
from pypy.objspace.std.intobject import W_IntObject
import sys
......@@ -19,11 +20,26 @@ cpython_struct("PyIntObject", PyIntObjectFields, PyIntObjectStruct)
def init_intobject(space):
"Type description of PyIntObject"
state = space.fromcache(State)
def int_alloc(typedescr, space, w_type, itemcount):
state = space.fromcache(State)
if w_type is space.w_int:
# in theory here we just want to allocate, without initializing the
# value. However, it's just easier to call PyInt_FromLong with a dummy
# value; make sure it's big enough to avoid the smallint optimization
# (if it will ever be enabled)
return state.C.PyInt_FromLong(0xDEADBEEF)
return BaseCpyTypedescr.allocate(typedescr, space, w_type, itemcount)
# CCC kill this?
def int_attach(space, py_obj, w_obj, w_userdata=None):
Fills a newly allocated PyIntObject with the given int object. The
......@@ -32,6 +48,7 @@ def int_attach(space, py_obj, w_obj, w_userdata=None):
py_int = rffi.cast(PyIntObject, py_obj)
py_int.c_ob_ival = space.int_w(w_obj)
# CCC kill this?
def int_realize(space, obj):
intval = rffi.cast(lltype.Signed, rffi.cast(PyIntObject, obj).c_ob_ival)
w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
......@@ -48,12 +65,6 @@ def PyInt_GetMax(space):
as defined in the system header files)."""
return sys.maxint
@cpython_api([lltype.Signed], PyObject)
def PyInt_FromLong(space, ival):
"""Create a new integer object with a value of ival.
return space.newint(ival)
@cpython_api([PyObject], lltype.Signed, error=-1)
def PyInt_AsLong(space, w_obj):
......@@ -106,7 +106,7 @@ def make_typedescr(typedef, **kw):
if tp_alloc:
def allocate(self, space, w_type, itemcount=0, immortal=False):
return tp_alloc(space, w_type, itemcount)
return tp_alloc(self, space, w_type, itemcount)
if hasattr(tp_dealloc, 'api_func'):
def get_dealloc(self, space):
/* Integer object implementation -- copied&adapted from CPython */
#include "Python.h"
/* Integers are quite normal objects, to make object handling uniform.
(Using odd pointers to represent integers would save much space
but require extra checks for this special case throughout the code.)
Since a typical Python program spends much of its time allocating
and deallocating integers, these operations should be very fast.
Therefore we use a dedicated allocation scheme with a much lower
overhead (in space and time) than straight malloc(): a simple
dedicated free list, filled when necessary with memory from malloc().
block_list is a singly-linked list of all PyIntBlocks ever allocated,
linked via their next members. PyIntBlocks are never returned to the
system before shutdown (PyInt_Fini).
free_list is a singly-linked list of available PyIntObjects, linked
via abuse of their ob_type members.
#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
typedef struct _intblock PyIntBlock;
static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;
static PyIntObject *
PyIntObject *p, *q;
/* Python's object allocator isn't appropriate for large blocks. */
p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
if (p == NULL)
return (PyIntObject *) PyErr_NoMemory();
((PyIntBlock *)p)->next = block_list;
block_list = (PyIntBlock *)p;
/* Link the int objects together, from rear to front, then return
the address of the last int object in the block. */
p = &((PyIntBlock *)p)->objects[0];
while (--q > p)
Py_TYPE(q) = (struct _typeobject *)(q-1);
Py_TYPE(q) = NULL;
return p + N_INTOBJECTS - 1;
/* References to small integers are saved in this array so that they
can be shared.
The integers that are saved are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
PyObject *
PyInt_FromLong(long ival)
register PyIntObject *v;
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
return (PyObject *) v;
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
/* Inline PyObject_New */
v = free_list;
free_list = (PyIntObject *)Py_TYPE(v);
(void)PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
/* this is CPython's int_dealloc */
_PyPy_int_dealloc(PyObject *obj)
PyIntObject *v = (PyIntObject *)obj;
if (PyInt_CheckExact(v)) {
Py_TYPE(v) = (struct _typeobject *)free_list;
free_list = v;
Py_TYPE(v)->tp_free((PyObject *)v);
from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.module.cpyext.intobject import (
PyInt_Check, PyInt_AsLong, PyInt_AS_LONG, PyInt_FromLong,
PyInt_Check, PyInt_AsLong, PyInt_AS_LONG,
PyInt_AsUnsignedLong, PyInt_AsUnsignedLongMask,
from pypy.module.cpyext.pyobject import (decref, make_ref,
from pypy.module.cpyext.state import State
import sys
class TestIntObject(BaseApiTest):
def test_intobject(self, space):
state = space.fromcache(State)
assert PyInt_Check(space, space.wrap(3))
assert PyInt_Check(space, space.w_True)
assert not PyInt_Check(space, space.wrap((1, 2, 3)))
......@@ -16,7 +20,8 @@ class TestIntObject(BaseApiTest):
y = PyInt_AS_LONG(space, space.wrap(i))
assert x == i
assert y == i
w_x = PyInt_FromLong(space, x + 1)
py_x = state.C.PyInt_FromLong(x + 1)
w_x = get_w_obj_and_decref(space, py_x)
assert space.type(w_x) is space.w_int
assert space.eq_w(w_x, space.wrap(i + 1))
......@@ -40,6 +45,14 @@ class TestIntObject(BaseApiTest):
assert (PyInt_AsUnsignedLongLongMask(space, space.wrap(10 ** 30))
== 10 ** 30 % (2 ** 64))
def test_freelist_direct(self, space):
state = space.fromcache(State)
p_x = state.C.PyInt_FromLong(12345678)
decref(space, p_x)
p_y = state.C.PyInt_FromLong(87654321)
assert p_x == p_y
decref(space, p_y)
def test_coerce(self, space):
w_obj = space.appexec([], """():
class Coerce(object):
......@@ -658,7 +658,7 @@ def type_dealloc(space, obj):
# CCC port it to C
def type_alloc(space, w_metatype, itemsize=0):
def type_alloc(typedescr, space, w_metatype, itemsize=0):
metatype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_metatype))
# Don't increase refcount for non-heaptypes
if metatype:
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment