Author: Matti Picus <matti.pi...@gmail.com>
Branch: 
Changeset: r93562:7b550e9b3cee
Date: 2017-12-25 23:31 +0200
http://bitbucket.org/pypy/pypy/changeset/7b550e9b3cee/

Log:    merge cpyext-avoid-roundtrip which avoids some rpython-c conversions

diff too long, truncating to 2000 out of 3523 lines

diff --git a/pypy/module/_cppyy/converter.py b/pypy/module/_cppyy/converter.py
--- a/pypy/module/_cppyy/converter.py
+++ b/pypy/module/_cppyy/converter.py
@@ -682,8 +682,8 @@
         if hasattr(space, "fake"):
             raise NotImplementedError
         space.getbuiltinmodule("cpyext")
-        from pypy.module.cpyext.pyobject import Py_DecRef, PyObject
-        Py_DecRef(space, rffi.cast(PyObject, rffi.cast(rffi.VOIDPP, arg)[0]))
+        from pypy.module.cpyext.pyobject import decref, PyObject
+        decref(space, rffi.cast(PyObject, rffi.cast(rffi.VOIDPP, arg)[0]))
 
 
 class MacroConverter(TypeConverter):
diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py
--- a/pypy/module/_cppyy/executor.py
+++ b/pypy/module/_cppyy/executor.py
@@ -220,11 +220,11 @@
 
     def wrap_result(self, space, lresult):
         space.getbuiltinmodule("cpyext")
-        from pypy.module.cpyext.pyobject import PyObject, from_ref, make_ref, 
Py_DecRef
+        from pypy.module.cpyext.pyobject import PyObject, from_ref, make_ref, 
decref
         result = rffi.cast(PyObject, lresult)
         w_obj = from_ref(space, result)
         if result:
-            Py_DecRef(space, result)
+            decref(space, result)
         return w_obj
 
     def execute(self, space, cppmethod, cppthis, num_args, args):
diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py
--- a/pypy/module/cpyext/__init__.py
+++ b/pypy/module/cpyext/__init__.py
@@ -17,11 +17,12 @@
         space.fromcache(State).startup(space)
         method = pypy.module.cpyext.typeobject.get_new_method_def(space)
         # the w_self argument here is a dummy, the only thing done with w_obj
-        # is call space.type on it
-        w_obj = pypy.module.cpyext.methodobject.W_PyCFunctionObject(space, 
method, space.w_None)
-        space.appexec([space.type(w_obj)], """(methodtype):
+        # is call type() on it
+        w_obj = pypy.module.cpyext.methodobject.W_PyCFunctionObject(space,
+                                                           method, 
space.w_None)
+        space.appexec([w_obj], """(meth):
             from pickle import Pickler
-            Pickler.dispatch[methodtype] = Pickler.save_global
+            Pickler.dispatch[type(meth)] = Pickler.save_global
         """)
 
     def register_atexit(self, function):
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -204,6 +204,9 @@
 # id.  Invariant: this variable always contain 0 when the PyPy GIL is
 # released.  It should also contain 0 when regular RPython code
 # executes.  In non-cpyext-related code, it will thus always be 0.
+# When cpyext-related C code runs, it contains the thread id (usually)
+# or the value -1 (only for state.C.PyXxx() functions which are short-
+# running and should not themselves release the GIL).
 #
 # **make_generic_cpy_call():** RPython to C, with the GIL held.  Before
 # the call, must assert that the global variable is 0 and set the
@@ -255,14 +258,73 @@
 
 cpyext_namespace = NameManager('cpyext_')
 
-class ApiFunction(object):
-    def __init__(self, argtypes, restype, callable, error=CANNOT_FAIL,
-                 c_name=None, cdecl=None, gil=None,
-                 result_borrowed=False, result_is_ll=False):
+class BaseApiFunction(object):
+    def __init__(self, argtypes, restype, callable):
         self.argtypes = argtypes
         self.restype = restype
         self.functype = lltype.Ptr(lltype.FuncType(argtypes, restype))
         self.callable = callable
+        self.cdecl = None    # default
+        #
+        def get_llhelper(space):
+            return llhelper(self.functype, self.get_wrapper(space))
+        self.get_llhelper = get_llhelper
+
+    def get_api_decl(self, name, c_writer):
+        restype = self.get_c_restype(c_writer)
+        args = self.get_c_args(c_writer)
+        res = self.API_VISIBILITY % (restype,)
+        return "{res} {name}({args});".format(**locals())
+
+    def get_c_restype(self, c_writer):
+        if self.cdecl:
+            return self.cdecl.tp.result.get_c_name()
+        return c_writer.gettype(self.restype).replace('@', '').strip()
+
+    def get_c_args(self, c_writer):
+        if self.cdecl:
+            args = [tp.get_c_name('arg%d' % i) for i, tp in
+                enumerate(self.cdecl.tp.args)]
+            return ', '.join(args) or "void"
+        args = []
+        for i, argtype in enumerate(self.argtypes):
+            if argtype is CONST_STRING:
+                arg = 'const char *@'
+            elif argtype is CONST_STRINGP:
+                arg = 'const char **@'
+            elif argtype is CONST_WSTRING:
+                arg = 'const wchar_t *@'
+            else:
+                arg = c_writer.gettype(argtype)
+            arg = arg.replace('@', 'arg%d' % (i,)).strip()
+            args.append(arg)
+        args = ', '.join(args) or "void"
+        return args
+
+    def get_ptr_decl(self, name, c_writer):
+        restype = self.get_c_restype(c_writer)
+        args = self.get_c_args(c_writer)
+        return "{restype} (*{name})({args});".format(**locals())
+
+    def get_ctypes_impl(self, name, c_writer):
+        restype = self.get_c_restype(c_writer)
+        args = self.get_c_args(c_writer)
+        callargs = ', '.join('arg%d' % (i,)
+                            for i in range(len(self.argtypes)))
+        if self.restype is lltype.Void:
+            body = "{ _pypyAPI.%s(%s); }" % (name, callargs)
+        else:
+            body = "{ return _pypyAPI.%s(%s); }" % (name, callargs)
+        return '%s %s(%s)\n%s' % (restype, name, args, body)
+
+
+class ApiFunction(BaseApiFunction):
+    API_VISIBILITY = "PyAPI_FUNC(%s)"
+
+    def __init__(self, argtypes, restype, callable, error=CANNOT_FAIL,
+                 c_name=None, cdecl=None, gil=None,
+                 result_borrowed=False, result_is_ll=False):
+        BaseApiFunction.__init__(self, argtypes, restype, callable)
         self.error_value = error
         self.c_name = c_name
         self.cdecl = cdecl
@@ -280,10 +342,6 @@
         self.gil = gil
         self.result_borrowed = result_borrowed
         self.result_is_ll = result_is_ll
-        #
-        def get_llhelper(space):
-            return llhelper(self.functype, self.get_wrapper(space))
-        self.get_llhelper = get_llhelper
 
     def _freeze_(self):
         return True
@@ -378,57 +436,17 @@
                     arg = input_arg
                 newargs += (arg, )
             try:
-                return self.callable(space, *newargs)
+                result = self.callable(space, *newargs)
             finally:
                 keepalive_until_here(*keepalives)
+            #
+            # this is just a sanity check to ensure that we don't forget to
+            # specify result_is_ll=True
+            if self.restype == PyObject:
+                assert self.result_is_ll == is_pyobj(result)
+            return result
         return unwrapper
 
-    def get_c_restype(self, c_writer):
-        if self.cdecl:
-            return self.cdecl.tp.result.get_c_name()
-        return c_writer.gettype(self.restype).replace('@', '').strip()
-
-    def get_c_args(self, c_writer):
-        if self.cdecl:
-            args = [tp.get_c_name('arg%d' % i) for i, tp in
-                enumerate(self.cdecl.tp.args)]
-            return ', '.join(args) or "void"
-        args = []
-        for i, argtype in enumerate(self.argtypes):
-            if argtype is CONST_STRING:
-                arg = 'const char *@'
-            elif argtype is CONST_STRINGP:
-                arg = 'const char **@'
-            elif argtype is CONST_WSTRING:
-                arg = 'const wchar_t *@'
-            else:
-                arg = c_writer.gettype(argtype)
-            arg = arg.replace('@', 'arg%d' % (i,)).strip()
-            args.append(arg)
-        args = ', '.join(args) or "void"
-        return args
-
-    def get_api_decl(self, name, c_writer):
-        restype = self.get_c_restype(c_writer)
-        args = self.get_c_args(c_writer)
-        return "PyAPI_FUNC({restype}) {name}({args});".format(**locals())
-
-    def get_ptr_decl(self, name, c_writer):
-        restype = self.get_c_restype(c_writer)
-        args = self.get_c_args(c_writer)
-        return "{restype} (*{name})({args});".format(**locals())
-
-    def get_ctypes_impl(self, name, c_writer):
-        restype = self.get_c_restype(c_writer)
-        args = self.get_c_args(c_writer)
-        callargs = ', '.join('arg%d' % (i,)
-                            for i in range(len(self.argtypes)))
-        if self.restype is lltype.Void:
-            body = "{ _pypyAPI.%s(%s); }" % (name, callargs)
-        else:
-            body = "{ return _pypyAPI.%s(%s); }" % (name, callargs)
-        return '%s %s(%s)\n%s' % (restype, name, args, body)
-
 
 DEFAULT_HEADER = 'pypy_decl.h'
 def cpython_api(argtypes, restype, error=_NOT_SPECIFIED, header=DEFAULT_HEADER,
@@ -466,6 +484,26 @@
         return unwrapper
     return decorate
 
+class COnlyApiFunction(BaseApiFunction):
+    API_VISIBILITY = "extern %s"
+
+    def get_wrapper(self, space):
+        return self.callable
+
+    def __call__(self, *args):
+        raise TypeError("the function %s should not be directly "
+                        "called from RPython, but only from C" % (self.func,))
+
+def c_only(argtypes, restype):
+    def decorate(func):
+        header = DEFAULT_HEADER
+        if func.__name__ in FUNCTIONS_BY_HEADER[header]:
+            raise ValueError("%s already registered" % func.__name__)
+        api_function = COnlyApiFunction(argtypes, restype, func)
+        FUNCTIONS_BY_HEADER[header][func.__name__] = api_function
+        return api_function
+    return decorate
+
 def api_func_from_cdef(func, cdef, cts,
         error=_NOT_SPECIFIED, header=DEFAULT_HEADER,
         result_is_ll=False):
@@ -598,6 +636,11 @@
     'Py_DivisionWarningFlag', 'Py_DontWriteBytecodeFlag', 
'Py_NoUserSiteDirectory',
     '_Py_QnewFlag', 'Py_Py3kWarningFlag', 'Py_HashRandomizationFlag', 
'_Py_PackageContext',
     '_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_GC_NewVar',
+    'PyObject_Init', 'PyObject_InitVar', 'PyInt_FromLong',
+    'PyTuple_New',
 ]
 TYPES = {}
 FORWARD_DECLS = []
@@ -933,8 +976,14 @@
 
         # see "Handling of the GIL" above (careful, we don't have the GIL here)
         tid = rthread.get_or_make_ident()
-        _gil_auto = (gil_auto_workaround and cpyext_glob_tid_ptr[0] != tid)
-        if gil_acquire or _gil_auto:
+        _gil_auto = False
+        if gil_auto_workaround and cpyext_glob_tid_ptr[0] != tid:
+            # replace '-1' with the real tid, now that we have the tid
+            if cpyext_glob_tid_ptr[0] == -1:
+                cpyext_glob_tid_ptr[0] = tid
+            else:
+                _gil_auto = True
+        if _gil_auto or gil_acquire:
             if cpyext_glob_tid_ptr[0] == tid:
                 deadlock_error(pname)
             rgil.acquire()
@@ -1059,6 +1108,7 @@
     setdefenc = rffi.llexternal('_%s_setfilesystemdefaultencoding' % prefix,
                                 [rffi.CCHARP], lltype.Void,
                                 compilation_info=eci, _nowrapper=True)
+    @init_function
     def init_types(space):
         from pypy.module.cpyext.typeobject import py_type_ready
         from pypy.module.sys.interp_encoding import getfilesystemencoding
@@ -1067,7 +1117,7 @@
         py_type_ready(space, get_capsule_type())
         s = space.text_w(getfilesystemencoding(space))
         setdefenc(rffi.str2charp(s, track_allocation=False))  # "leaks"
-    INIT_FUNCTIONS.append(init_types)
+
     from pypy.module.posix.interp_posix import add_fork_hook
     _reinit_tls = rffi.llexternal('%sThread_ReInitTLS' % prefix, [],
                                   lltype.Void, compilation_info=eci)
@@ -1075,6 +1125,50 @@
         _reinit_tls()
     add_fork_hook('child', reinit_tls)
 
+
+def attach_c_functions(space, eci, prefix):
+    state = space.fromcache(State)
+    state.C._Py_Dealloc = rffi.llexternal('_Py_Dealloc',
+                                         [PyObject], lltype.Void,
+                                         compilation_info=eci,
+                                         _nowrapper=True)
+    state.C.PyObject_Free = rffi.llexternal(
+        mangle_name(prefix, 'PyObject_Free'),
+        [rffi.VOIDP], lltype.Void,
+        compilation_info=eci,
+        _nowrapper=True)
+    state.C.PyType_GenericAlloc = rffi.llexternal(
+        mangle_name(prefix, 'PyType_GenericAlloc'),
+        [PyTypeObjectPtr, Py_ssize_t], PyObject,
+        compilation_info=eci,
+        _nowrapper=True)
+    state.C.PyInt_FromLong = rffi.llexternal(
+        mangle_name(prefix, 'PyInt_FromLong'),
+        [rffi.LONG], PyObject,
+        compilation_info=eci,
+        _nowrapper=True)
+    state.C._PyPy_int_dealloc = rffi.llexternal(
+        '_PyPy_int_dealloc', [PyObject], lltype.Void,
+        compilation_info=eci, _nowrapper=True)
+    state.C.PyTuple_New = rffi.llexternal(
+        mangle_name(prefix, 'PyTuple_New'),
+        [Py_ssize_t], PyObject,
+        compilation_info=eci,
+        _nowrapper=True)
+    state.C._PyPy_tuple_dealloc = rffi.llexternal(
+        '_PyPy_tuple_dealloc', [PyObject], lltype.Void,
+        compilation_info=eci, _nowrapper=True)
+    _, 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_subtype_dealloc = rffi.llexternal(
+        '_PyPy_subtype_dealloc', [PyObject], lltype.Void,
+        compilation_info=eci, _nowrapper=True)
+    state.C._PyPy_object_dealloc = rffi.llexternal(
+        '_PyPy_object_dealloc', [PyObject], lltype.Void,
+        compilation_info=eci, _nowrapper=True)
+
+
 def init_function(func):
     INIT_FUNCTIONS.append(func)
     return func
@@ -1143,6 +1237,7 @@
     space.fromcache(State).install_dll(eci)
     modulename = py.path.local(eci.libraries[-1])
 
+    attach_c_functions(space, eci, prefix)
     run_bootstrap_functions(space)
 
     # load the bridge, and init structure
@@ -1183,7 +1278,6 @@
                 in_dll = ll2ctypes.get_ctypes_type(PyObject.TO).in_dll(bridge, 
mname)
                 py_obj = ll2ctypes.ctypes2lltype(PyObject, 
ctypes.pointer(in_dll))
             builder.prepare(py_obj, w_obj)
-    builder.attach_all(space)
 
     pypyAPI = ctypes.POINTER(ctypes.c_void_p).in_dll(bridge, 'pypyAPI')
 
@@ -1194,6 +1288,12 @@
                 ll2ctypes.lltype2ctypes(func.get_llhelper(space)),
                 ctypes.c_void_p)
 
+    # we need to call this *after* the init code above, because it might
+    # indirectly call some functions which are attached to pypyAPI (e.g., we
+    # if do tuple_attach of the prebuilt empty tuple, we need to call
+    # _PyPy_Malloc)
+    builder.attach_all(space)
+    
     setup_init_functions(eci, prefix)
     return modulename.new(ext='')
 
@@ -1386,6 +1486,10 @@
                          source_dir / "pythread.c",
                          source_dir / "missing.c",
                          source_dir / "pymem.c",
+                         source_dir / "object.c",
+                         source_dir / "typeobject.c",
+                         source_dir / "intobject.c",
+                         source_dir / "tupleobject.c",
                          ]
 
 def build_eci(code, use_micronumpy=False, translating=False):
@@ -1480,6 +1584,7 @@
     eci = build_eci(code, use_micronumpy, translating=True)
     space.fromcache(State).install_dll(eci)
 
+    attach_c_functions(space, eci, prefix)
     run_bootstrap_functions(space)
 
     # emit uninitialized static data
@@ -1617,7 +1722,7 @@
 
 @specialize.memo()
 def make_generic_cpy_call(FT, expect_null):
-    from pypy.module.cpyext.pyobject import is_pyobj, as_pyobj
+    from pypy.module.cpyext.pyobject import is_pyobj, make_ref, decref
     from pypy.module.cpyext.pyobject import get_w_obj_and_decref
     from pypy.module.cpyext.pyerrors import PyErr_Occurred
     unrolling_arg_types = unrolling_iterable(enumerate(FT.ARGS))
@@ -1645,15 +1750,17 @@
     @specialize.ll()
     def generic_cpy_call(space, func, *args):
         boxed_args = ()
-        keepalives = ()
+        to_decref = ()
         assert len(args) == len(FT.ARGS)
         for i, ARG in unrolling_arg_types:
             arg = args[i]
+            _pyobj = None
             if is_PyObject(ARG):
                 if not is_pyobj(arg):
-                    keepalives += (arg,)
-                    arg = as_pyobj(space, arg)
+                    arg = make_ref(space, arg)
+                    _pyobj = arg
             boxed_args += (arg,)
+            to_decref += (_pyobj,)
 
         # see "Handling of the GIL" above
         tid = rthread.get_ident()
@@ -1667,7 +1774,11 @@
         finally:
             assert cpyext_glob_tid_ptr[0] == tid
             cpyext_glob_tid_ptr[0] = 0
-            keepalive_until_here(*keepalives)
+            for i, ARG in unrolling_arg_types:
+                # note that this loop is nicely unrolled statically by RPython
+                _pyobj = to_decref[i]
+                if _pyobj is not None:
+                    decref(space, _pyobj)
 
         if is_PyObject(RESULT_TYPE):
             if not is_pyobj(result):
diff --git a/pypy/module/cpyext/bufferobject.py 
b/pypy/module/cpyext/bufferobject.py
--- a/pypy/module/cpyext/bufferobject.py
+++ b/pypy/module/cpyext/bufferobject.py
@@ -4,7 +4,7 @@
 from pypy.module.cpyext.api import (
     cpython_api, Py_ssize_t, cpython_struct, bootstrap_function, slot_function,
     PyObjectFields, PyObject)
-from pypy.module.cpyext.pyobject import make_typedescr, Py_DecRef, make_ref
+from pypy.module.cpyext.pyobject import make_typedescr, decref, make_ref
 from pypy.module.array.interp_array import ArrayBuffer
 from pypy.objspace.std.bufferobject import W_Buffer
 
@@ -76,7 +76,7 @@
 def buffer_dealloc(space, py_obj):
     py_buf = rffi.cast(PyBufferObject, py_obj)
     if py_buf.c_b_base:
-        Py_DecRef(space, py_buf.c_b_base)
+        decref(space, py_buf.c_b_base)
     else:
         rffi.free_charp(rffi.cast(rffi.CCHARP, py_buf.c_b_ptr))
     from pypy.module.cpyext.object import _dealloc
diff --git a/pypy/module/cpyext/bytearrayobject.py 
b/pypy/module/cpyext/bytearrayobject.py
--- a/pypy/module/cpyext/bytearrayobject.py
+++ b/pypy/module/cpyext/bytearrayobject.py
@@ -3,12 +3,12 @@
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.objspace.std.bytearrayobject import new_bytearray
 from pypy.module.cpyext.api import (
-    cpython_api, cpython_struct, bootstrap_function, build_type_checkers,
-    PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL)
+    cpython_api, cpython_struct, build_type_checkers,
+    PyVarObjectFields, Py_ssize_t, CONST_STRING)
 from pypy.module.cpyext.pyerrors import PyErr_BadArgument
 from pypy.module.cpyext.pyobject import (
-    PyObject, PyObjectP, Py_DecRef, make_ref, from_ref,
-    make_typedescr, get_typedescr, Py_IncRef)
+    PyObject, PyObjectP, make_ref, from_ref,
+    make_typedescr, get_typedescr)
 # Type PyByteArrayObject represents a mutable array of bytes.
 # The Python API is that of a sequence;
 # the bytes are mapped to ints in [0, 256).
diff --git a/pypy/module/cpyext/bytesobject.py 
b/pypy/module/cpyext/bytesobject.py
--- a/pypy/module/cpyext/bytesobject.py
+++ b/pypy/module/cpyext/bytesobject.py
@@ -5,8 +5,8 @@
     PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL, slot_function)
 from pypy.module.cpyext.pyerrors import PyErr_BadArgument
 from pypy.module.cpyext.pyobject import (
-    PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, track_reference,
-    make_typedescr, get_typedescr, as_pyobj, Py_IncRef, get_w_obj_and_decref,
+    PyObject, PyObjectP, decref, make_ref, from_ref, track_reference,
+    make_typedescr, get_typedescr, as_pyobj, get_w_obj_and_decref,
     pyobj_has_w_obj)
 from pypy.objspace.std.bytesobject import W_BytesObject
 
@@ -215,7 +215,7 @@
     try:
         py_newstr = new_empty_str(space, newsize)
     except MemoryError:
-        Py_DecRef(space, ref[0])
+        decref(space, ref[0])
         ref[0] = lltype.nullptr(PyObject.TO)
         raise
     to_cp = newsize
@@ -224,7 +224,7 @@
         to_cp = oldsize
     for i in range(to_cp):
         py_newstr.c_ob_sval[i] = py_str.c_ob_sval[i]
-    Py_DecRef(space, ref[0])
+    decref(space, ref[0])
     ref[0] = rffi.cast(PyObject, py_newstr)
     return 0
 
@@ -260,7 +260,7 @@
     try:
         PyString_Concat(space, ref, newpart)
     finally:
-        Py_DecRef(space, newpart)
+        decref(space, newpart)
 
 @cpython_api([PyObject, PyObject], PyObject)
 def PyString_Format(space, w_format, w_args):
@@ -294,7 +294,7 @@
     alias."""
     w_str = from_ref(space, string[0])
     w_str = space.new_interned_w_str(w_str)
-    Py_DecRef(space, string[0])
+    decref(space, string[0])
     string[0] = make_ref(space, w_str)
 
 @cpython_api([PyObject, CONST_STRING, CONST_STRING], PyObject)
diff --git a/pypy/module/cpyext/classobject.py 
b/pypy/module/cpyext/classobject.py
--- a/pypy/module/cpyext/classobject.py
+++ b/pypy/module/cpyext/classobject.py
@@ -1,8 +1,7 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
-from pypy.module.cpyext.api import (
-    PyObjectFields, CANNOT_FAIL,
-    cpython_api, bootstrap_function, build_type_checkers)
-from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref, 
Py_DecRef, make_typedescr
+from pypy.module.cpyext.api import CANNOT_FAIL, cpython_api, 
build_type_checkers
+from pypy.module.cpyext.pyobject import (
+    PyObject, make_ref, from_ref, make_typedescr)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.module.__builtin__.interp_classobj import W_ClassObject, 
W_InstanceObject
 
diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py
--- a/pypy/module/cpyext/dictobject.py
+++ b/pypy/module/cpyext/dictobject.py
@@ -9,7 +9,7 @@
     bootstrap_function, slot_function)
 from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, as_pyobj,
         make_typedescr, track_reference, create_ref, from_ref, decref,
-        Py_IncRef)
+        incref)
 from pypy.module.cpyext.object import _dealloc
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 
@@ -272,7 +272,7 @@
             return 0     # XXX should not call keys() above
         w_keys.switch_to_object_strategy()
         py_dict.c__tmpkeys = create_ref(space, w_keys)
-        Py_IncRef(space, py_dict.c__tmpkeys)
+        incref(space, py_dict.c__tmpkeys)
     else:
         if not py_dict.c__tmpkeys:
             # pos should have been 0, cannot fail so return 0
diff --git a/pypy/module/cpyext/frameobject.py 
b/pypy/module/cpyext/frameobject.py
--- a/pypy/module/cpyext/frameobject.py
+++ b/pypy/module/cpyext/frameobject.py
@@ -3,7 +3,7 @@
     cpython_api, bootstrap_function, PyObjectFields, cpython_struct,
     CANNOT_FAIL, slot_function)
 from pypy.module.cpyext.pyobject import (
-    PyObject, Py_DecRef, make_ref, from_ref, track_reference,
+    PyObject, decref, make_ref, from_ref, track_reference,
     make_typedescr, get_typedescr)
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.pystate import PyThreadState
@@ -43,9 +43,9 @@
 def frame_dealloc(space, py_obj):
     py_frame = rffi.cast(PyFrameObject, py_obj)
     py_code = rffi.cast(PyObject, py_frame.c_f_code)
-    Py_DecRef(space, py_code)
-    Py_DecRef(space, py_frame.c_f_globals)
-    Py_DecRef(space, py_frame.c_f_locals)
+    decref(space, py_code)
+    decref(space, py_frame.c_f_globals)
+    decref(space, py_frame.c_f_locals)
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
 
diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py
--- a/pypy/module/cpyext/funcobject.py
+++ b/pypy/module/cpyext/funcobject.py
@@ -4,7 +4,7 @@
     cpython_api, bootstrap_function, cpython_struct, build_type_checkers,
     slot_function)
 from pypy.module.cpyext.pyobject import (
-    PyObject, make_ref, from_ref, Py_DecRef, make_typedescr)
+    PyObject, make_ref, from_ref, decref, make_typedescr)
 from rpython.rlib.unroll import unrolling_iterable
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.function import Function, Method
@@ -60,7 +60,7 @@
 @slot_function([PyObject], lltype.Void)
 def function_dealloc(space, py_obj):
     py_func = rffi.cast(PyFunctionObject, py_obj)
-    Py_DecRef(space, py_func.c_func_name)
+    decref(space, py_func.c_func_name)
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
 
@@ -79,8 +79,8 @@
 @slot_function([PyObject], lltype.Void)
 def code_dealloc(space, py_obj):
     py_code = rffi.cast(PyCodeObject, py_obj)
-    Py_DecRef(space, py_code.c_co_name)
-    Py_DecRef(space, py_code.c_co_filename)
+    decref(space, py_code.c_co_name)
+    decref(space, py_code.c_co_filename)
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
 
diff --git a/pypy/module/cpyext/include/intobject.h 
b/pypy/module/cpyext/include/intobject.h
--- a/pypy/module/cpyext/include/intobject.h
+++ b/pypy/module/cpyext/include/intobject.h
@@ -16,6 +16,9 @@
                 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
 }
 #endif
diff --git a/pypy/module/cpyext/include/object.h 
b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -47,7 +47,12 @@
 #define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0)
 #endif
 
-#define Py_CLEAR(op)                           \
+PyAPI_FUNC(void) Py_IncRef(PyObject *);
+PyAPI_FUNC(void) Py_DecRef(PyObject *);
+extern Py_ssize_t _pypy_rawrefcount_w_marker_deallocating;
+PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
+
+#define Py_CLEAR(op)                        \
         do {                                   \
                 if (op) {                      \
                         PyObject *_py_tmp = (PyObject *)(op);  \
@@ -60,6 +65,10 @@
 #define Py_TYPE(ob)            (((PyObject*)(ob))->ob_type)
 #define Py_SIZE(ob)            (((PyVarObject*)(ob))->ob_size)
 
+#define _Py_NewReference(op)                                        \
+    ( ((PyObject *)(op))->ob_refcnt = 1,                            \
+      ((PyObject *)(op))->ob_pypy_link = 0 )
+
 #define _Py_ForgetReference(ob) /* nothing */
 
 #define Py_None (&_Py_NoneStruct)
@@ -257,8 +266,16 @@
          ) & ~(SIZEOF_VOID_P - 1)              \
        )
 
-#define PyObject_INIT PyObject_Init
-#define PyObject_INIT_VAR PyObject_InitVar
+        
+#define PyObject_INIT(op, typeobj) \
+    ( Py_TYPE(op) = (typeobj), ((PyObject *)(op))->ob_refcnt = 1,\
+      ((PyObject *)(op))->ob_pypy_link = 0, (op) )
+#define PyObject_INIT_VAR(op, typeobj, size) \
+    ( Py_SIZE(op) = (size), PyObject_INIT((op), (typeobj)) )
+
+
+PyAPI_FUNC(PyObject *) PyType_GenericAlloc(PyTypeObject *, Py_ssize_t);
+
 /*
 #define PyObject_NEW(type, typeobj) \
 ( (type *) PyObject_Init( \
@@ -277,6 +294,9 @@
 #define PyObject_GC_New(type, typeobj) \
                 ( (type *) _PyObject_GC_New(typeobj) )
 
+#define PyObject_GC_NewVar(type, typeobj, n) \
+                ( (type *) _PyObject_GC_NewVar((typeobj), (n)) )
+ 
 /* A dummy PyGC_Head, just to please some tests. Don't use it! */
 typedef union _gc_head {
     char dummy;
@@ -343,18 +363,33 @@
 */
 
 
+/* on CPython, these are in objimpl.h */
+
+PyAPI_FUNC(void) PyObject_Free(void *);
+PyAPI_FUNC(void) PyObject_GC_Del(void *);
+
 #define PyObject_MALLOC         PyObject_Malloc
 #define PyObject_REALLOC        PyObject_Realloc
 #define PyObject_FREE           PyObject_Free
 #define PyObject_Del            PyObject_Free
 #define PyObject_DEL            PyObject_Free
 
+PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *);
+PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t);
+PyAPI_FUNC(PyObject *) _PyObject_GC_New(PyTypeObject *);
+PyAPI_FUNC(PyVarObject *) _PyObject_GC_NewVar(PyTypeObject *, Py_ssize_t);
+
+PyAPI_FUNC(PyObject *) PyObject_Init(PyObject *, PyTypeObject *);
+PyAPI_FUNC(PyVarObject *) PyObject_InitVar(PyVarObject *,
+                                           PyTypeObject *, Py_ssize_t);
 
 
 /* PyPy internal ----------------------------------- */
 PyAPI_FUNC(int) PyPyType_Register(PyTypeObject *);
 #define PyObject_Length PyObject_Size
 #define _PyObject_GC_Del PyObject_GC_Del
+PyAPI_FUNC(void) _PyPy_subtype_dealloc(PyObject *);
+PyAPI_FUNC(void) _PyPy_object_dealloc(PyObject *);
 
 
 #ifdef __cplusplus
diff --git a/pypy/module/cpyext/include/tupleobject.h 
b/pypy/module/cpyext/include/tupleobject.h
--- a/pypy/module/cpyext/include/tupleobject.h
+++ b/pypy/module/cpyext/include/tupleobject.h
@@ -16,9 +16,13 @@
      */
 } PyTupleObject;
 
+PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size);
+PyAPI_FUNC(void) _PyPy_tuple_dealloc(PyObject *);
+
 /* defined in varargswrapper.c */
 PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...);
 
+
 /* Macro, trading safety for speed */
 #define PyTuple_GET_ITEM(op, i) (((PyTupleObject *)(op))->ob_item[i])
 #define PyTuple_GET_SIZE(op)    Py_SIZE(op)
diff --git a/pypy/module/cpyext/intobject.py b/pypy/module/cpyext/intobject.py
--- a/pypy/module/cpyext/intobject.py
+++ b/pypy/module/cpyext/intobject.py
@@ -5,7 +5,8 @@
     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,25 @@
 @bootstrap_function
 def init_intobject(space):
     "Type description of PyIntObject"
+    state = space.fromcache(State)
     make_typedescr(space.w_int.layout.typedef,
                    basestruct=PyIntObject.TO,
                    attach=int_attach,
+                   alloc=int_alloc,
+                   dealloc=state.C._PyPy_int_dealloc,
                    realize=int_realize)
 
+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.ccall("PyInt_FromLong", 0x0DEADBEE)
+    else:
+        return BaseCpyTypedescr.allocate(typedescr, space, w_type, itemcount)
+
 def int_attach(space, py_obj, w_obj, w_userdata=None):
     """
     Fills a newly allocated PyIntObject with the given int object. The
@@ -48,12 +63,6 @@
     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):
diff --git a/pypy/module/cpyext/mapping.py b/pypy/module/cpyext/mapping.py
--- a/pypy/module/cpyext/mapping.py
+++ b/pypy/module/cpyext/mapping.py
@@ -1,7 +1,8 @@
 from rpython.rtyper.lltypesystem import lltype, rffi
 from pypy.module.cpyext.api import (
     cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t)
-from pypy.module.cpyext.pyobject import PyObject
+from pypy.module.cpyext.pyobject import (
+    PyObject, hack_for_result_often_existing_obj)
 
 
 @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
@@ -37,12 +38,13 @@
     the Python expression o.items()."""
     return space.call_method(w_obj, "items")
 
-@cpython_api([PyObject, CONST_STRING], PyObject)
+@cpython_api([PyObject, CONST_STRING], PyObject, result_is_ll=True)
 def PyMapping_GetItemString(space, w_obj, key):
     """Return element of o corresponding to the object key or NULL on failure.
     This is the equivalent of the Python expression o[key]."""
     w_key = space.newtext(rffi.charp2str(key))
-    return space.getitem(w_obj, w_key)
+    w_res = space.getitem(w_obj, w_key)
+    return hack_for_result_often_existing_obj(space, w_res)
 
 @cpython_api([PyObject, CONST_STRING, PyObject], rffi.INT_real, error=-1)
 def PyMapping_SetItemString(space, w_obj, key, w_value):
diff --git a/pypy/module/cpyext/methodobject.py 
b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -14,7 +14,9 @@
     cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts,
     build_type_checkers)
 from pypy.module.cpyext.pyobject import (
-    Py_DecRef, from_ref, make_ref, as_pyobj, make_typedescr)
+    decref, from_ref, make_ref, as_pyobj, make_typedescr)
+from pypy.module.cpyext.state import State
+from pypy.module.cpyext.tupleobject import tuple_from_args_w
 
 PyMethodDef = cts.gettype('PyMethodDef')
 PyCFunction = cts.gettype('PyCFunction')
@@ -38,37 +40,35 @@
 @slot_function([PyObject], lltype.Void)
 def cfunction_dealloc(space, py_obj):
     py_func = rffi.cast(PyCFunctionObject, py_obj)
-    Py_DecRef(space, py_func.c_m_self)
-    Py_DecRef(space, py_func.c_m_module)
+    decref(space, py_func.c_m_self)
+    decref(space, py_func.c_m_module)
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
 
 class W_PyCFunctionObject(W_Root):
-    # TODO create a slightly different class depending on the c_ml_flags
+    _immutable_fields_ = ["flags"]
+
     def __init__(self, space, ml, w_self, w_module=None):
         self.ml = ml
         self.name = rffi.charp2str(rffi.cast(rffi.CCHARP,self.ml.c_ml_name))
+        self.flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags)
         self.w_self = w_self
         self.w_module = w_module
 
-    def call(self, space, w_self, w_args, w_kw):
-        # Call the C function
-        if w_self is None:
-            w_self = self.w_self
-        flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags)
-        flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
-        if not flags & METH_KEYWORDS and space.is_true(w_kw):
+    def descr_call(self, space, __args__):
+        return self.call(space, self.w_self, __args__)
+
+    def call(self, space, w_self, __args__):
+        flags = self.flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST)
+        length = len(__args__.arguments_w)
+        if not flags & METH_KEYWORDS and __args__.keywords:
             raise oefmt(space.w_TypeError,
                         "%s() takes no keyword arguments", self.name)
-
-        func = self.ml.c_ml_meth
-        length = space.int_w(space.len(w_args))
         if flags & METH_KEYWORDS:
-            func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
-            return generic_cpy_call(space, func, w_self, w_args, w_kw)
+            return self.call_keywords(space, w_self, __args__)
         elif flags & METH_NOARGS:
             if length == 0:
-                return generic_cpy_call(space, func, w_self, None)
+                return self.call_noargs(space, w_self, __args__)
             raise oefmt(space.w_TypeError,
                         "%s() takes no arguments", self.name)
         elif flags & METH_O:
@@ -76,19 +76,60 @@
                 raise oefmt(space.w_TypeError,
                             "%s() takes exactly one argument (%d given)",
                             self.name, length)
-            w_arg = space.getitem(w_args, space.newint(0))
-            return generic_cpy_call(space, func, w_self, w_arg)
+            return self.call_o(space, w_self, __args__)
         elif flags & METH_VARARGS:
-            return generic_cpy_call(space, func, w_self, w_args)
-        else: # METH_OLDARGS, the really old style
-            size = length
-            if size == 1:
-                w_arg = space.getitem(w_args, space.newint(0))
-            elif size == 0:
-                w_arg = None
-            else:
-                w_arg = w_args
-            return generic_cpy_call(space, func, w_self, w_arg)
+            return self.call_varargs(space, w_self, __args__)
+        else:
+            return self.call_oldargs(space, w_self, __args__)
+
+    def call_noargs(self, space, w_self, __args__):
+        func = self.ml.c_ml_meth
+        return generic_cpy_call(space, func, w_self, None)
+
+    def call_o(self, space, w_self, __args__):
+        func = self.ml.c_ml_meth
+        w_o = __args__.arguments_w[0]        
+        return generic_cpy_call(space, func, w_self, w_o)
+
+    def call_varargs(self, space, w_self, __args__):
+        state = space.fromcache(State)
+        func = self.ml.c_ml_meth
+        py_args = tuple_from_args_w(space, __args__.arguments_w)
+        try:
+            return generic_cpy_call(space, func, w_self, py_args)
+        finally:
+            decref(space, py_args)
+
+    def call_keywords(self, space, w_self, __args__):
+        func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
+        py_args = tuple_from_args_w(space, __args__.arguments_w)
+        w_kwargs = None
+        if __args__.keywords:
+            # CCC: we should probably have a @jit.look_inside_iff if the
+            # keyword count is constant, as we do in Arguments.unpack
+            w_kwargs = space.newdict()
+            for i in range(len(__args__.keywords)):
+                key = __args__.keywords[i]
+                w_obj = __args__.keywords_w[i]
+                space.setitem(w_kwargs, space.newtext(key), w_obj)
+        try:
+            return generic_cpy_call(space, func, w_self, py_args, w_kwargs)
+        finally:
+            decref(space, py_args)
+
+    def call_oldargs(self, space, w_self, __args__):
+        func = self.ml.c_ml_meth
+        length = len(__args__.arguments_w)
+        if length == 0:
+            py_args = lltype.nullptr(PyObject.TO)
+        elif length == 1:
+            py_args = make_ref(space, __args__.arguments_w[0])
+        else:
+            py_args = tuple_from_args_w(space, __args__.arguments_w)
+        try:
+            return generic_cpy_call(space, func, w_self, py_args)
+        finally:
+            decref(space, py_args)
 
     def get_doc(self, space):
         doc = self.ml.c_ml_doc
@@ -97,27 +138,11 @@
         else:
             return space.w_None
 
-class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject):
-    def call(self, space, w_self, w_args, w_kw):
-        # Call the C function
-        if w_self is None:
-            w_self = self.w_self
-        func = self.ml.c_ml_meth
-        return generic_cpy_call(space, func, w_self, None)
+class W_PyCMethodObject(W_PyCFunctionObject):
 
-class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject):
-    def call(self, space, w_self, w_o, w_kw):
-        if w_self is None:
-            w_self = self.w_self
-        func = self.ml.c_ml_meth
-        return generic_cpy_call(space, func, w_self, w_o)
-
-class W_PyCMethodObject(W_PyCFunctionObject):
-    w_self = None
     def __init__(self, space, ml, w_type):
+        W_PyCFunctionObject.__init__(self, space, ml, w_self=None)
         self.space = space
-        self.ml = ml
-        self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, ml.c_ml_name))
         self.w_objclass = w_type
 
     def __repr__(self):
@@ -130,14 +155,13 @@
             self.name, w_objclass.name))
 
     def descr_call(self, space, __args__):
-        args_w, kw_w = __args__.unpack()
-        if len(args_w) < 1:
+        if len(__args__.arguments_w) == 0:
             w_objclass = self.w_objclass
             assert isinstance(w_objclass, W_TypeObject)
             raise oefmt(space.w_TypeError,
                 "descriptor '%s' of '%s' object needs an argument",
                 self.name, w_objclass.name)
-        w_instance = args_w[0]
+        w_instance = __args__.arguments_w[0]
         # XXX: needs a stricter test
         if not space.isinstance_w(w_instance, self.w_objclass):
             w_objclass = self.w_objclass
@@ -145,12 +169,10 @@
             raise oefmt(space.w_TypeError,
                 "descriptor '%s' requires a '%s' object but received a '%T'",
                 self.name, w_objclass.name, w_instance)
-        w_args = space.newtuple(args_w[1:])
-        w_kw = space.newdict()
-        for key, w_obj in kw_w.items():
-            space.setitem(w_kw, space.newtext(key), w_obj)
-        ret = self.call(space, w_instance, w_args, w_kw)
-        return ret
+        #
+        # CCC: we can surely do better than this
+        __args__ = __args__.replace_arguments(__args__.arguments_w[1:])
+        return self.call(space, w_instance, __args__)
 
 # PyPy addition, for Cython
 _, _ = build_type_checkers("MethodDescr", W_PyCMethodObject)
@@ -166,16 +188,25 @@
     return isinstance(w_obj, BuiltinFunction)
 
 class W_PyCClassMethodObject(W_PyCFunctionObject):
-    w_self = None
+
     def __init__(self, space, ml, w_type):
+        W_PyCFunctionObject.__init__(self, space, ml, w_self=None)
         self.space = space
-        self.ml = ml
-        self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, ml.c_ml_name))
         self.w_objclass = w_type
 
     def __repr__(self):
         return self.space.unwrap(self.descr_method_repr())
 
+    def descr_call(self, space, __args__):
+        if len(__args__.arguments_w) == 0:
+            raise oefmt(space.w_TypeError,
+                "descriptor '%s' of '%s' object needs an argument",
+                self.name, self.w_objclass.getname(space))
+        w_instance = __args__.arguments_w[0] # XXX typecheck missing
+        # CCC: we can surely do better than this
+        __args__ = __args__.replace_arguments(__args__.arguments_w[1:])
+        return self.call(space, w_instance, __args__)
+
     def descr_method_repr(self):
         return self.getrepr(self.space,
                             "built-in method '%s' of '%s' object" %
@@ -241,45 +272,6 @@
         space.setitem(w_kw, space.newtext(key), w_obj)
     return self.call(space, w_self, w_args, w_kw)
 
-def cfunction_descr_call_noargs(space, w_self):
-    # special case for calling with flags METH_NOARGS
-    self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self)
-    return self.call(space, None, None, None)
-
-def cfunction_descr_call_single_object(space, w_self, w_o):
-    # special case for calling with flags METH_O
-    self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self)
-    return self.call(space, None, w_o, None)
-
-@jit.dont_look_inside
-def cfunction_descr_call(space, w_self, __args__):
-    # specialize depending on the W_PyCFunctionObject
-    self = space.interp_w(W_PyCFunctionObject, w_self)
-    args_w, kw_w = __args__.unpack()
-    # XXX __args__.unpack is slow
-    w_args = space.newtuple(args_w)
-    w_kw = space.newdict()
-    for key, w_obj in kw_w.items():
-        space.setitem(w_kw, space.newtext(key), w_obj)
-    ret = self.call(space, None, w_args, w_kw)
-    return ret
-
-@jit.dont_look_inside
-def cclassmethod_descr_call(space, w_self, __args__):
-    self = space.interp_w(W_PyCFunctionObject, w_self)
-    args_w, kw_w = __args__.unpack()
-    if len(args_w) < 1:
-        raise oefmt(space.w_TypeError,
-            "descriptor '%s' of '%s' object needs an argument",
-            self.name, self.w_objclass.getname(space))
-    w_instance = args_w[0] # XXX typecheck missing
-    w_args = space.newtuple(args_w[1:])
-    w_kw = space.newdict()
-    for key, w_obj in kw_w.items():
-        space.setitem(w_kw, space.newtext(key), w_obj)
-    ret = self.call(space, w_instance, w_args, w_kw)
-    return ret
-
 def cmethod_descr_get(space, w_function, w_obj, w_cls=None):
     asking_for_bound = (space.is_none(w_cls) or
                         not space.is_w(w_obj, space.w_None) or
@@ -297,7 +289,7 @@
 
 W_PyCFunctionObject.typedef = TypeDef(
     'builtin_function_or_method',
-    __call__ = interp2app(cfunction_descr_call),
+    __call__ = interp2app(W_PyCFunctionObject.descr_call),
     __doc__ = GetSetProperty(W_PyCFunctionObject.get_doc),
     __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObject),
     __name__ = interp_attrproperty('name', cls=W_PyCFunctionObject,
@@ -305,26 +297,6 @@
     )
 W_PyCFunctionObject.typedef.acceptable_as_base_class = False
 
-W_PyCFunctionObjectNoArgs.typedef = TypeDef(
-    'builtin_function_or_method', W_PyCFunctionObject.typedef,
-    __call__ = interp2app(cfunction_descr_call_noargs),
-    __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc),
-    __module__ = interp_attrproperty_w('w_module', 
cls=W_PyCFunctionObjectNoArgs),
-    __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs,
-        wrapfn="newtext_or_none"),
-    )
-W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False
-
-W_PyCFunctionObjectSingleObject.typedef = TypeDef(
-    'builtin_function_or_method', W_PyCFunctionObject.typedef,
-    __call__ = interp2app(cfunction_descr_call_single_object),
-    __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc),
-    __module__ = interp_attrproperty_w('w_module', 
cls=W_PyCFunctionObjectSingleObject),
-    __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject,
-        wrapfn="newtext_or_none"),
-    )
-W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False
-
 W_PyCMethodObject.typedef = TypeDef(
     'method_descriptor',
     __get__ = interp2app(cmethod_descr_get),
@@ -339,7 +311,7 @@
 W_PyCClassMethodObject.typedef = TypeDef(
     'classmethod',
     __get__ = interp2app(cclassmethod_descr_get),
-    __call__ = interp2app(cclassmethod_descr_call),
+    __call__ = interp2app(W_PyCClassMethodObject.descr_call),
     __name__ = interp_attrproperty('name', cls=W_PyCClassMethodObject,
         wrapfn="newtext_or_none"),
     __objclass__ = interp_attrproperty_w('w_objclass',
diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py
--- a/pypy/module/cpyext/modsupport.py
+++ b/pypy/module/cpyext/modsupport.py
@@ -1,13 +1,12 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import cpython_api, cpython_struct, \
         METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING, \
-        METH_NOARGS, METH_O
+        METH_NOARGS, METH_O, METH_VARARGS
 from pypy.module.cpyext.pyobject import PyObject, as_pyobj
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.methodobject import (
     W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod,
-    PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New,
-    W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject)
+    PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.module.cpyext.state import State
 from pypy.interpreter.error import oefmt
@@ -81,14 +80,6 @@
                       space.newtext(rffi.charp2str(doc)))
     return w_mod   # borrowed result kept alive in PyImport_AddModule()
 
-def _create_pyc_function_object(space, method, w_self, w_name, flags):
-    flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
-    if flags == METH_NOARGS:
-        return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name)
-    if flags == METH_O:
-        return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name)
-    return W_PyCFunctionObject(space, method, w_self, w_name)
-
 def convert_method_defs(space, dict_w, methods, w_type, w_self=None, 
name=None):
     w_name = space.newtext_or_none(name)
     methods = rffi.cast(rffi.CArrayPtr(PyMethodDef), methods)
@@ -107,8 +98,7 @@
                     raise oefmt(space.w_ValueError,
                             "module functions cannot set METH_CLASS or "
                             "METH_STATIC")
-                w_obj = _create_pyc_function_object(space, method, w_self,
-                                                    w_name, flags)
+                w_obj = W_PyCFunctionObject(space, method, w_self, w_name)
             else:
                 if methodname in dict_w and not (flags & METH_COEXIST):
                     continue
diff --git a/pypy/module/cpyext/number.py b/pypy/module/cpyext/number.py
--- a/pypy/module/cpyext/number.py
+++ b/pypy/module/cpyext/number.py
@@ -1,6 +1,6 @@
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.module.cpyext.api import cpython_api, CANNOT_FAIL, Py_ssize_t
-from pypy.module.cpyext.pyobject import PyObject, PyObjectP, from_ref, 
make_ref, Py_DecRef
+from pypy.module.cpyext.pyobject import PyObject, PyObjectP, from_ref, make_ref
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.tool.sourcetools import func_with_new_name
 from pypy.module.cpyext.state import State
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -3,10 +3,10 @@
     cpython_api, generic_cpy_call, CANNOT_FAIL, Py_ssize_t, Py_ssize_tP,
     PyVarObject, size_t, slot_function,
     Py_TPFLAGS_HEAPTYPE, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT,
-    Py_GE, CONST_STRING, FILEP, fwrite)
+    Py_GE, CONST_STRING, FILEP, fwrite, c_only)
 from pypy.module.cpyext.pyobject import (
-    PyObject, PyObjectP, from_ref, Py_IncRef, Py_DecRef,
-    get_typedescr)
+    PyObject, PyObjectP, from_ref, incref, decref,
+    get_typedescr, hack_for_result_often_existing_obj)
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr
 from pypy.module.cpyext.pyerrors import PyErr_NoMemory, PyErr_BadInternalCall
 from pypy.objspace.std.typeobject import W_TypeObject
@@ -32,31 +32,20 @@
     # XXX FIXME
     return realloc(ptr, size)
 
-@cpython_api([rffi.VOIDP], lltype.Void)
-def PyObject_Free(space, ptr):
+@c_only([rffi.VOIDP], lltype.Void)
+def _PyPy_Free(ptr):
     lltype.free(ptr, flavor='raw')
 
-@cpython_api([PyTypeObjectPtr], PyObject, result_is_ll=True)
-def _PyObject_New(space, type):
-    return _PyObject_NewVar(space, type, 0)
+@c_only([Py_ssize_t], rffi.VOIDP)
+def _PyPy_Malloc(size):
+    # XXX: the malloc inside BaseCpyTypedescr.allocate and
+    # typeobject.type_alloc specify zero=True, so this is why we use it also
+    # here. However, CPython does a simple non-initialized malloc, so we
+    # should investigate whether we can remove zero=True as well
+    return lltype.malloc(rffi.VOIDP.TO, size,
+                         flavor='raw', zero=True,
+                         add_memory_pressure=True)
 
-@cpython_api([PyTypeObjectPtr, Py_ssize_t], PyObject, result_is_ll=True)
-def _PyObject_NewVar(space, type, itemcount):
-    w_type = from_ref(space, rffi.cast(PyObject, type))
-    assert isinstance(w_type, W_TypeObject)
-    typedescr = get_typedescr(w_type.layout.typedef)
-    py_obj = typedescr.allocate(space, w_type, itemcount=itemcount)
-    #py_obj.c_ob_refcnt = 0 --- will be set to 1 again by PyObject_Init{Var}
-    if type.c_tp_itemsize == 0:
-        w_obj = PyObject_Init(space, py_obj, type)
-    else:
-        py_objvar = rffi.cast(PyVarObject, py_obj)
-        w_obj = PyObject_InitVar(space, py_objvar, type, itemcount)
-    return py_obj
-
-@slot_function([PyObject], lltype.Void)
-def PyObject_dealloc(space, obj):
-    return _dealloc(space, obj)
 
 def _dealloc(space, obj):
     # This frees an object after its refcount dropped to zero, so we
@@ -66,15 +55,7 @@
     obj_voidp = rffi.cast(rffi.VOIDP, obj)
     generic_cpy_call(space, pto.c_tp_free, obj_voidp)
     if pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE:
-        Py_DecRef(space, rffi.cast(PyObject, pto))
-
-@cpython_api([PyTypeObjectPtr], PyObject, result_is_ll=True)
-def _PyObject_GC_New(space, type):
-    return _PyObject_New(space, type)
-
-@cpython_api([rffi.VOIDP], lltype.Void)
-def PyObject_GC_Del(space, obj):
-    PyObject_Free(space, obj)
+        decref(space, rffi.cast(PyObject, pto))
 
 @cpython_api([PyObject], PyObjectP, error=CANNOT_FAIL)
 def _PyObject_GetDictPtr(space, op):
@@ -88,20 +69,22 @@
 def PyObject_Not(space, w_obj):
     return not space.is_true(w_obj)
 
-@cpython_api([PyObject, PyObject], PyObject)
+@cpython_api([PyObject, PyObject], PyObject, result_is_ll=True)
 def PyObject_GetAttr(space, w_obj, w_name):
     """Retrieve an attribute named attr_name from object o. Returns the 
attribute
     value on success, or NULL on failure.  This is the equivalent of the Python
     expression o.attr_name."""
-    return space.getattr(w_obj, w_name)
+    w_res = space.getattr(w_obj, w_name)
+    return hack_for_result_often_existing_obj(space, w_res)
 
-@cpython_api([PyObject, CONST_STRING], PyObject)
+@cpython_api([PyObject, CONST_STRING], PyObject, result_is_ll=True)
 def PyObject_GetAttrString(space, w_obj, name_ptr):
     """Retrieve an attribute named attr_name from object o. Returns the 
attribute
     value on success, or NULL on failure. This is the equivalent of the Python
     expression o.attr_name."""
     name = rffi.charp2str(name_ptr)
-    return space.getattr(w_obj, space.newtext(name))
+    w_res = space.getattr(w_obj, space.newtext(name))
+    return hack_for_result_often_existing_obj(space, w_res)
 
 @cpython_api([PyObject, PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyObject_HasAttr(space, w_obj, w_name):
@@ -160,11 +143,12 @@
     and 0 otherwise.  This function always succeeds."""
     return int(space.is_true(space.callable(w_obj)))
 
-@cpython_api([PyObject, PyObject], PyObject)
+@cpython_api([PyObject, PyObject], PyObject, result_is_ll=True)
 def PyObject_GetItem(space, w_obj, w_key):
     """Return element of o corresponding to the object key or NULL on failure.
     This is the equivalent of the Python expression o[key]."""
-    return space.getitem(w_obj, w_key)
+    w_res = space.getitem(w_obj, w_key)
+    return hack_for_result_often_existing_obj(space, w_res)
 
 @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1)
 def PyObject_SetItem(space, w_obj, w_key, w_value):
@@ -180,29 +164,6 @@
     space.delitem(w_obj, w_key)
     return 0
 
-@cpython_api([PyObject, PyTypeObjectPtr], PyObject, result_is_ll=True)
-def PyObject_Init(space, obj, type):
-    """Initialize a newly-allocated object op with its type and initial
-    reference.  Returns the initialized object.  If type indicates that the
-    object participates in the cyclic garbage detector, it is added to the
-    detector's set of observed objects. Other fields of the object are not
-    affected."""
-    if not obj:
-        PyErr_NoMemory(space)
-    obj.c_ob_type = type
-    obj.c_ob_pypy_link = 0
-    obj.c_ob_refcnt = 1
-    return obj
-
-@cpython_api([PyVarObject, PyTypeObjectPtr, Py_ssize_t], PyObject, 
result_is_ll=True)
-def PyObject_InitVar(space, py_obj, type, size):
-    """This does everything PyObject_Init() does, and also initializes the
-    length information for a variable-size object."""
-    if not py_obj:
-        PyErr_NoMemory(space)
-    py_obj.c_ob_size = size
-    return PyObject_Init(space, rffi.cast(PyObject, py_obj), type)
-
 @cpython_api([PyObject], PyObject)
 def PyObject_Type(space, w_obj):
     """When o is non-NULL, returns a type object corresponding to the object 
type
@@ -309,7 +270,7 @@
 @cpython_api([PyObject], PyObject, result_is_ll=True)
 def PyObject_SelfIter(space, ref):
     """Undocumented function, this is what CPython does."""
-    Py_IncRef(space, ref)
+    incref(space, ref)
     return ref
 
 @cpython_api([PyObject, PyObject], PyObject)
diff --git a/pypy/module/cpyext/pyerrors.py b/pypy/module/cpyext/pyerrors.py
--- a/pypy/module/cpyext/pyerrors.py
+++ b/pypy/module/cpyext/pyerrors.py
@@ -6,7 +6,7 @@
 from pypy.module.cpyext.api import cpython_api, CANNOT_FAIL, CONST_STRING
 from pypy.module.exceptions.interp_exceptions import W_RuntimeWarning
 from pypy.module.cpyext.pyobject import (
-    PyObject, PyObjectP, make_ref, from_ref, Py_DecRef)
+    PyObject, PyObjectP, make_ref, from_ref, decref, get_w_obj_and_decref)
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.import_ import PyImport_Import
 from rpython.rlib import rposix, jit
@@ -65,7 +65,7 @@
         ptraceback[0] = lltype.nullptr(PyObject.TO)
 
 @cpython_api([PyObject, PyObject, PyObject], lltype.Void)
-def PyErr_Restore(space, w_type, w_value, w_traceback):
+def PyErr_Restore(space, py_type, py_value, py_traceback):
     """Set  the error indicator from the three objects.  If the error 
indicator is
     already set, it is cleared first.  If the objects are NULL, the error
     indicator is cleared.  Do not pass a NULL type and non-NULL value or
@@ -80,13 +80,14 @@
     error indicator temporarily; use PyErr_Fetch() to save the current
     exception state."""
     state = space.fromcache(State)
+    w_type = get_w_obj_and_decref(space, py_type)
+    w_value = get_w_obj_and_decref(space, py_value)
+    w_traceback = get_w_obj_and_decref(space, py_traceback)
+    # XXX do something with w_traceback
     if w_type is None:
         state.clear_exception()
         return
     state.set_exception(OperationError(w_type, w_value))
-    Py_DecRef(space, w_type)
-    Py_DecRef(space, w_value)
-    Py_DecRef(space, w_traceback)
 
 @cpython_api([PyObjectP, PyObjectP, PyObjectP], lltype.Void)
 def PyErr_NormalizeException(space, exc_p, val_p, tb_p):
@@ -108,8 +109,8 @@
         w_evalue = space.w_None
     operr = OperationError(w_etype, w_evalue)
     operr.normalize_exception(space)
-    Py_DecRef(space, exc_p[0])
-    Py_DecRef(space, val_p[0])
+    decref(space, exc_p[0])
+    decref(space, val_p[0])
     exc_p[0] = make_ref(space, operr.w_type)
     val_p[0] = make_ref(space, operr.get_w_value(space))
 
@@ -390,7 +391,7 @@
         ptraceback[0] = lltype.nullptr(PyObject.TO)
 
 @cpython_api([PyObject, PyObject, PyObject], lltype.Void)
-def PyErr_SetExcInfo(space, w_type, w_value, w_traceback):
+def PyErr_SetExcInfo(space, py_type, py_value, py_traceback):
     """---Cython extension---
 
     Set the exception info, as known from ``sys.exc_info()``.  This refers
@@ -406,6 +407,9 @@
        restore the exception state temporarily.  Use
        :c:func:`PyErr_GetExcInfo` to read the exception state.
     """
+    w_type = get_w_obj_and_decref(space, py_type)
+    w_value = get_w_obj_and_decref(space, py_value)
+    w_traceback = get_w_obj_and_decref(space, py_traceback)
     if w_value is None or space.is_w(w_value, space.w_None):
         operror = None
     else:
@@ -419,9 +423,6 @@
     #
     ec = space.getexecutioncontext()
     ec.set_sys_exc_info(operror)
-    Py_DecRef(space, w_type)
-    Py_DecRef(space, w_value)
-    Py_DecRef(space, w_traceback)
 
 @cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
 def PyOS_InterruptOccurred(space):
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -7,13 +7,13 @@
 from pypy.module.cpyext.api import (
     cpython_api, bootstrap_function, PyObject, PyObjectP, ADDR,
     CANNOT_FAIL, Py_TPFLAGS_HEAPTYPE, PyTypeObjectPtr, is_PyObject,
-    PyVarObject)
+    PyVarObject, Py_ssize_t, init_function, cts)
 from pypy.module.cpyext.state import State
 from pypy.objspace.std.typeobject import W_TypeObject
 from pypy.objspace.std.objectobject import W_ObjectObject
-from rpython.rlib.objectmodel import specialize
+from rpython.rlib.objectmodel import specialize, we_are_translated
 from rpython.rlib.objectmodel import keepalive_until_here
-from rpython.rtyper.annlowlevel import llhelper
+from rpython.rtyper.annlowlevel import llhelper, cast_instance_to_base_ptr
 from rpython.rlib import rawrefcount, jit
 from rpython.rlib.debug import ll_assert, fatalerror
 
@@ -26,9 +26,10 @@
     W_BaseObject = W_ObjectObject
 
     def get_dealloc(self, space):
-        from pypy.module.cpyext.typeobject import subtype_dealloc
-        return subtype_dealloc.api_func.get_llhelper(space)
+        state = space.fromcache(State)
+        return state.C._PyPy_subtype_dealloc
 
+    # CCC port to C
     def allocate(self, space, w_type, itemcount=0, immortal=False):
         # typically called from PyType_GenericAlloc via typedescr.allocate
         # this returns a PyObject with ob_refcnt == 1.
@@ -39,7 +40,7 @@
         # Don't increase refcount for non-heaptypes
         flags = rffi.cast(lltype.Signed, pytype.c_tp_flags)
         if flags & Py_TPFLAGS_HEAPTYPE:
-            Py_IncRef(space, w_type)
+            incref(space, pytype)
 
         if pytype:
             size = pytype.c_tp_basicsize
@@ -97,17 +98,21 @@
     assert not kw, "Extra arguments to make_typedescr"
 
     null_dealloc = lltype.nullptr(lltype.FuncType([PyObject], lltype.Void))
+    assert not isinstance(tp_basestruct, lltype.Ptr), "should pass .TO"
 
     class CpyTypedescr(BaseCpyTypedescr):
         basestruct = tp_basestruct
 
         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 tp_dealloc:
+        if hasattr(tp_dealloc, 'api_func'):
             def get_dealloc(self, space):
                 return tp_dealloc.api_func.get_llhelper(space)
+        elif tp_dealloc:
+            def get_dealloc(self, space):
+                return tp_dealloc
 
         if tp_attach:
             def attach(self, space, pyobj, w_obj, w_userdata=None):
@@ -123,10 +128,10 @@
 
 @bootstrap_function
 def init_pyobject(space):
-    from pypy.module.cpyext.object import PyObject_dealloc
     # typedescr for the 'object' type
+    state = space.fromcache(State)
     make_typedescr(space.w_object.layout.typedef,
-                   dealloc=PyObject_dealloc)
+                   dealloc=state.C._PyPy_object_dealloc)
     # almost all types, which should better inherit from object.
     make_typedescr(None)
 
@@ -180,7 +185,7 @@
     """
     Ties together a PyObject and an interpreter object.
     The PyObject's refcnt is increased by REFCNT_FROM_PYPY.
-    The reference in 'py_obj' is not stolen!  Remember to Py_DecRef()
+    The reference in 'py_obj' is not stolen!  Remember to decref()
     it is you need to.
     """
     # XXX looks like a PyObject_GC_TRACK
@@ -238,8 +243,8 @@
     use keepalive_until_here(w_obj) some time later.**  In case of
     doubt, use the safer make_ref().
     """
+    assert not is_pyobj(w_obj)
     if w_obj is not None:
-        assert not is_pyobj(w_obj)
         py_obj = rawrefcount.from_obj(PyObject, w_obj)
         if not py_obj:
             py_obj = create_ref(space, w_obj, w_userdata, immortal=immortal)
@@ -277,37 +282,43 @@
         hop.exception_cannot_occur()
         return hop.inputconst(lltype.Bool, hop.s_result.const)
 
-@specialize.ll()
-def make_ref(space, obj, w_userdata=None, immortal=False):
-    """Increment the reference counter of the PyObject and return it.
-    Can be called with either a PyObject or a W_Root.
-    """
-    if is_pyobj(obj):
-        pyobj = rffi.cast(PyObject, obj)
-        at_least = 1
-    else:
-        pyobj = as_pyobj(space, obj, w_userdata, immortal=immortal)
-        at_least = rawrefcount.REFCNT_FROM_PYPY
-    if pyobj:
-        assert pyobj.c_ob_refcnt >= at_least
+def get_pyobj_and_incref(space, w_obj, w_userdata=None, immortal=False):
+    pyobj = as_pyobj(space, w_obj, w_userdata, immortal=immortal)
+    if pyobj:  # != NULL
+        assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
         pyobj.c_ob_refcnt += 1
-        keepalive_until_here(obj)
+        keepalive_until_here(w_obj)
     return pyobj
 
+def hack_for_result_often_existing_obj(space, w_obj):
+    # Equivalent to get_pyobj_and_incref() and not to make_ref():
+    # it builds a PyObject from a W_Root, but ensures that the result
+    # gets attached to the original W_Root.  This is needed to work around
+    # some obscure abuses: https://github.com/numpy/numpy/issues/9850
+    return get_pyobj_and_incref(space, w_obj)
+
+def make_ref(space, w_obj, w_userdata=None, immortal=False):
+    """Turn the W_Root into a corresponding PyObject.  You should
+    decref the returned PyObject later.  Note that it is often the
+    case, but not guaranteed, that make_ref() returns always the
+    same PyObject for the same W_Root; for example, integers.
+    """
+    assert not is_pyobj(w_obj)
+    if w_obj is not None and space.type(w_obj) is space.w_int:
+        state = space.fromcache(State)
+        intval = space.int_w(w_obj)
+        return state.ccall("PyInt_FromLong", intval)
+    return get_pyobj_and_incref(space, w_obj, w_userdata, immortal=False)
 
 @specialize.ll()
-def get_w_obj_and_decref(space, obj):
+def get_w_obj_and_decref(space, pyobj):
     """Decrement the reference counter of the PyObject and return the
-    corresponding W_Root object (so the reference count is at least
-    REFCNT_FROM_PYPY and cannot be zero).  Can be called with either
-    a PyObject or a W_Root.
+    corresponding W_Root object (so the reference count after the decref
+    is at least REFCNT_FROM_PYPY and cannot be zero).
     """
-    if is_pyobj(obj):
-        pyobj = rffi.cast(PyObject, obj)
-        w_obj = from_ref(space, pyobj)
-    else:
-        w_obj = obj
-        pyobj = as_pyobj(space, w_obj)
+    assert is_pyobj(pyobj)
+    pyobj = rffi.cast(PyObject, pyobj)
+    w_obj = from_ref(space, pyobj)
     if pyobj:
         pyobj.c_ob_refcnt -= 1
         assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
@@ -316,51 +327,37 @@
 
 
 @specialize.ll()
-def incref(space, obj):
-    make_ref(space, obj)
+def incref(space, pyobj):
+    assert is_pyobj(pyobj)
+    pyobj = rffi.cast(PyObject, pyobj)
+    assert pyobj.c_ob_refcnt >= 1
+    pyobj.c_ob_refcnt += 1
 
 @specialize.ll()
-def decref(space, obj):
-    if is_pyobj(obj):
-        obj = rffi.cast(PyObject, obj)
-        if obj:
-            assert obj.c_ob_refcnt > 0
-            assert obj.c_ob_pypy_link == 0 or obj.c_ob_refcnt > 
rawrefcount.REFCNT_FROM_PYPY
-            obj.c_ob_refcnt -= 1
-            if obj.c_ob_refcnt == 0:
-                _Py_Dealloc(space, obj)
-            #else:
-            #    w_obj = rawrefcount.to_obj(W_Root, ref)
-            #    if w_obj is not None:
-            #        assert obj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
-    else:
-        get_w_obj_and_decref(space, obj)
+def decref(space, pyobj):
+    from pypy.module.cpyext.api import generic_cpy_call
+    assert is_pyobj(pyobj)
+    pyobj = rffi.cast(PyObject, pyobj)
+    if pyobj:
+        assert pyobj.c_ob_refcnt > 0
+        assert (pyobj.c_ob_pypy_link == 0 or
+                pyobj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY)
+        pyobj.c_ob_refcnt -= 1
+        if pyobj.c_ob_refcnt == 0:
+            state = space.fromcache(State)
+            generic_cpy_call(space, state.C._Py_Dealloc, pyobj)
+        #else:
+        #    w_obj = rawrefcount.to_obj(W_Root, ref)
+        #    if w_obj is not None:
+        #        assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
 
 
-@cpython_api([PyObject], lltype.Void)
-def Py_IncRef(space, obj):
-    incref(space, obj)
-
-@cpython_api([PyObject], lltype.Void)
-def Py_DecRef(space, obj):
-    decref(space, obj)
-
-@cpython_api([PyObject], lltype.Void)
-def _Py_NewReference(space, obj):
-    obj.c_ob_refcnt = 1
-    # XXX is it always useful to create the W_Root object here?
-    w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
-    assert isinstance(w_type, W_TypeObject)
-    get_typedescr(w_type.layout.typedef).realize(space, obj)
-
-@cpython_api([PyObject], lltype.Void)
-def _Py_Dealloc(space, obj):
-    from pypy.module.cpyext.api import generic_cpy_call
-    pto = obj.c_ob_type
-    #print >>sys.stderr, "Calling dealloc slot", pto.c_tp_dealloc, "of", obj, \
-    #      "'s type which is", rffi.charp2str(pto.c_tp_name)
-    rawrefcount.mark_deallocating(w_marker_deallocating, obj)
-    generic_cpy_call(space, pto.c_tp_dealloc, obj)
+@init_function
+def write_w_marker_deallocating(space):
+    if we_are_translated():
+        llptr = cast_instance_to_base_ptr(w_marker_deallocating)
+        state = space.fromcache(State)
+        state.C.set_marker(rffi.cast(Py_ssize_t, llptr))
 
 @cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL)
 def _Py_HashPointer(space, ptr):
diff --git a/pypy/module/cpyext/pystate.py b/pypy/module/cpyext/pystate.py
--- a/pypy/module/cpyext/pystate.py
+++ b/pypy/module/cpyext/pystate.py
@@ -1,6 +1,6 @@
 from pypy.module.cpyext.api import (
     cpython_api, CANNOT_FAIL, cpython_struct)
-from pypy.module.cpyext.pyobject import PyObject, Py_DecRef, make_ref
+from pypy.module.cpyext.pyobject import PyObject, decref, make_ref
 from pypy.interpreter.error import OperationError
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rlib import rthread
@@ -75,7 +75,7 @@
 
 def ThreadState_dealloc(ts, space):
     assert space is not None
-    Py_DecRef(space, ts.c_dict)
+    decref(space, ts.c_dict)
 ThreadStateCapsule = encapsulator(PyThreadState.TO,
                                   dealloc=ThreadState_dealloc)
 
@@ -319,7 +319,7 @@
     interpreter lock must be held."""
     if not space.config.translation.thread:
         raise NoThreads
-    Py_DecRef(space, tstate.c_dict)
+    decref(space, tstate.c_dict)
     tstate.c_dict = lltype.nullptr(PyObject.TO)
     space.threadlocals.leave_thread(space)
     space.getexecutioncontext().cleanup_cpyext_state()
diff --git a/pypy/module/cpyext/pytraceback.py 
b/pypy/module/cpyext/pytraceback.py
--- a/pypy/module/cpyext/pytraceback.py
+++ b/pypy/module/cpyext/pytraceback.py
@@ -4,7 +4,7 @@
     cpython_api, bootstrap_function, cpython_struct, 
     slot_function)
 from pypy.module.cpyext.pyobject import (
-    PyObject, make_ref, from_ref, Py_DecRef, make_typedescr)
+    PyObject, make_ref, from_ref, decref, make_typedescr)
 from pypy.module.cpyext.frameobject import PyFrameObject
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.pytraceback import PyTraceback
@@ -44,7 +44,7 @@
 @slot_function([PyObject], lltype.Void)
 def traceback_dealloc(space, py_obj):
     py_traceback = rffi.cast(PyTracebackObject, py_obj)
-    Py_DecRef(space, rffi.cast(PyObject, py_traceback.c_tb_next))
-    Py_DecRef(space, rffi.cast(PyObject, py_traceback.c_tb_frame))
+    decref(space, rffi.cast(PyObject, py_traceback.c_tb_next))
+    decref(space, rffi.cast(PyObject, py_traceback.c_tb_frame))
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -1,11 +1,13 @@
 
 from rpython.rlib import rerased, jit
+from rpython.rlib.objectmodel import keepalive_until_here
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.objspace.std.listobject import (
     ListStrategy, UNROLL_CUTOFF, W_ListObject, ObjectListStrategy)
 from pypy.module.cpyext.api import (
     cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t, PyObject, PyObjectP)
 from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
+from pypy.module.cpyext.pyobject import as_pyobj, incref
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.objspace.std import tupleobject
 
@@ -47,6 +49,8 @@
     converted to a sequence, and raises a TypeError, raise a new TypeError with
     m as the message text. If the conversion otherwise, fails, reraise the
     original exception"""
+    if isinstance(w_obj, tupleobject.W_TupleObject):
+        return w_obj   # CCC avoid the double conversion that occurs here
     if isinstance(w_obj, W_ListObject):
         # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM
         w_obj.convert_to_cpy_strategy(space)
@@ -58,6 +62,7 @@
             raise OperationError(space.w_TypeError, 
space.newtext(rffi.charp2str(m)))
         raise e
 
+# CCC this should be written as a C macro
 @cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_borrowed=True)
 def PySequence_Fast_GET_ITEM(space, w_obj, index):
     """Return the ith element of o, assuming that o was returned by
@@ -99,6 +104,11 @@
         cpy_strategy = space.fromcache(CPyListStrategy)
         if w_obj.strategy is cpy_strategy:
             return w_obj.get_raw_items() # asserts it's a cpyext strategy
+    elif isinstance(w_obj, tupleobject.W_TupleObject):
+        from pypy.module.cpyext.tupleobject import PyTupleObject
+        py_obj = as_pyobj(space, w_obj)
+        py_tuple = rffi.cast(PyTupleObject, py_obj)
+        return rffi.cast(PyObjectP, py_tuple.c_ob_item)
     raise oefmt(space.w_TypeError,
                 "PySequence_Fast_ITEMS called but object is not the result of "
                 "PySequence_Fast")
@@ -123,7 +133,7 @@
     space.delslice(w_obj, space.newint(start), space.newint(end))
     return 0
 
-@cpython_api([rffi.VOIDP, Py_ssize_t], PyObject)
+@cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_is_ll=True)
 def PySequence_ITEM(space, w_obj, i):
     """Return the ith element of o or NULL on failure. Macro form of
     PySequence_GetItem() but without checking that
@@ -132,12 +142,32 @@
 
     This function used an int type for i. This might require
     changes in your code for properly supporting 64-bit systems."""
-    return space.getitem(w_obj, space.newint(i))
+    # XXX we should call Py*_GET_ITEM() instead of Py*_GetItem()
+    # from here, but we cannot because we are also called from
+    # PySequence_GetItem()
+    if isinstance(w_obj, tupleobject.W_TupleObject):
+        from pypy.module.cpyext.tupleobject import PyTuple_GetItem
+        py_obj = as_pyobj(space, w_obj)
+        py_res = PyTuple_GetItem(space, py_obj, i)
+        incref(space, py_res)
+        keepalive_until_here(w_obj)
+        return py_res
+    if isinstance(w_obj, W_ListObject):
+        from pypy.module.cpyext.listobject import PyList_GetItem
+        py_obj = as_pyobj(space, w_obj)
+        py_res = PyList_GetItem(space, py_obj, i)
+        incref(space, py_res)
+        keepalive_until_here(w_obj)
+        return py_res
+    return make_ref(space, space.getitem(w_obj, space.newint(i)))
 
-@cpython_api([PyObject, Py_ssize_t], PyObject)
+@cpython_api([PyObject, Py_ssize_t], PyObject, result_is_ll=True)
 def PySequence_GetItem(space, w_obj, i):
     """Return the ith element of o, or NULL on failure. This is the equivalent 
of
     the Python expression o[i]."""
+    if i < 0:
+        l = PySequence_Length(space, w_obj)
+        i += l
     return PySequence_ITEM(space, w_obj, i)
 
 @cpython_api([PyObject], PyObject)
diff --git a/pypy/module/cpyext/setobject.py b/pypy/module/cpyext/setobject.py
--- a/pypy/module/cpyext/setobject.py
+++ b/pypy/module/cpyext/setobject.py
@@ -2,7 +2,7 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL,
                                     build_type_checkers)
-from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
+from pypy.module.cpyext.pyobject import (PyObject, PyObjectP,
     make_ref, from_ref)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.objspace.std.setobject import W_SetObject, W_FrozensetObject, newset
diff --git a/pypy/module/cpyext/sliceobject.py 
b/pypy/module/cpyext/sliceobject.py
--- a/pypy/module/cpyext/sliceobject.py
+++ b/pypy/module/cpyext/sliceobject.py
@@ -3,7 +3,7 @@
     cpython_api, cpython_struct, bootstrap_function, build_type_checkers,
     CANNOT_FAIL, Py_ssize_t, Py_ssize_tP, PyObjectFields, slot_function)
 from pypy.module.cpyext.pyobject import (
-    Py_DecRef, PyObject, make_ref, make_typedescr)
+    decref, PyObject, make_ref, make_typedescr)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.interpreter.error import OperationError
 from pypy.objspace.std.sliceobject import W_SliceObject
@@ -41,9 +41,9 @@
     """Frees allocated PyBytesObject resources.
     """
     py_slice = rffi.cast(PySliceObject, py_obj)
-    Py_DecRef(space, py_slice.c_start)
-    Py_DecRef(space, py_slice.c_stop)
-    Py_DecRef(space, py_slice.c_step)
+    decref(space, py_slice.c_start)
+    decref(space, py_slice.c_stop)
+    decref(space, py_slice.c_step)
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
 
diff --git a/pypy/module/cpyext/src/intobject.c 
b/pypy/module/cpyext/src/intobject.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/src/intobject.c
@@ -0,0 +1,108 @@
+
+/* 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 *
+fill_free_list(void)
+{
+    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];
+    q = p + N_INTOBJECTS;
+    while (--q > p)
+        Py_TYPE(q) = (struct _typeobject *)(q-1);
+    Py_TYPE(q) = NULL;
+    return p + N_INTOBJECTS - 1;
+}
+
+#ifndef NSMALLPOSINTS
+#define NSMALLPOSINTS           257
+#endif
+#ifndef NSMALLNEGINTS
+#define NSMALLNEGINTS           5
+#endif
+#if NSMALLNEGINTS + NSMALLPOSINTS > 0
+/* 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];
+#endif
+
+PyObject *
+PyInt_FromLong(long ival)
+{
+    register PyIntObject *v;
+    /*
+#if NSMALLNEGINTS + NSMALLPOSINTS > 0
+    if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
+        v = small_ints[ival + NSMALLNEGINTS];
+        Py_INCREF(v);
+        return (PyObject *) v;
+    }
+#endif
+    */
+    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 */
+void
+_PyPy_int_dealloc(PyObject *obj)
+{
+    PyIntObject *v = (PyIntObject *)obj;
+    if (PyInt_CheckExact(v)) {
+        Py_TYPE(v) = (struct _typeobject *)free_list;
+        free_list = v;
+    }
+    else
+        Py_TYPE(v)->tp_free((PyObject *)v);
+}
diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/src/object.c
@@ -0,0 +1,142 @@
+/* Generic object operations; and implementation of None (NoObject) */
+
+#include "Python.h"
+
+extern void _PyPy_Free(void *ptr);
+extern void *_PyPy_Malloc(Py_ssize_t size);
+
+void
+Py_IncRef(PyObject *o)
+{
+    Py_XINCREF(o);
+}
+
+void
+Py_DecRef(PyObject *o)
+{
+    Py_XDECREF(o);
+}
+
+/* 
+ * The actual value of this variable will be the address of
+ * pyobject.w_marker_deallocating, and will be set by
+ * pyobject.write_w_marker_deallocating().
+ *
+ * The value set here is used only as a marker by tests (because during the
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to