[pypy-svn] pypy refactor-rerase: (cfbolz, arigo)

arigo commits-noreply at bitbucket.org
Fri Feb 25 15:42:56 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: refactor-rerase
Changeset: r42283:2aae7e82f9d8
Date: 2011-02-25 15:40 +0100
http://bitbucket.org/pypy/pypy/changeset/2aae7e82f9d8/

Log:	(cfbolz, arigo)

	We only support the pair mechanism. Make it rtype.

diff --git a/pypy/rlib/rerased.py b/pypy/rlib/rerased.py
--- a/pypy/rlib/rerased.py
+++ b/pypy/rlib/rerased.py
@@ -1,6 +1,18 @@
 """ Contains a mechanism for turning any class instance and any integer into a
 pointer-like thing. Gives full control over pointer tagging, i.e. there won't
-be tag checks everywhere in the C code. """
+be tag checks everywhere in the C code.
+
+Usage:  erasestuff, unerasestuff = new_erasing_pair('stuff')
+
+An erasestuff(x) object contains a reference to 'x'.  Nothing can be done with
+this object, except calling unerasestuff(), which returns 'x' again.  The point
+is that all erased objects can be mixed together, whether they are instances,
+lists, strings, etc.  As a special case, an erased object can also be an
+integer fitting into 31/63 bits, with erase_int() and unerase_int().
+
+Warning: some care is needed to make sure that you call the unerase function
+corresponding to the original creator's erase function.  Otherwise, segfault.
+"""
 
 import sys
 from pypy.annotation import model as annmodel
@@ -15,49 +27,59 @@
 
 
 
-def erase(x):
-    """Creates an 'erased' object that contains a reference to 'x'. Nothing can
-    be done with this object, except calling unerase(y, <type>) on it.
-    x needs to be either an instance or an integer fitting into 31/63 bits."""
-    if isinstance(x, int):
-        res = 2 * x + 1
-        if res > sys.maxint or res < -sys.maxint - 1:
-            raise OverflowError
-    assert not isinstance(x, list)
-    return Erased(x)
+def erase_int(x):
+    assert isinstance(x, int)
+    res = 2 * x + 1
+    if res > sys.maxint or res < -sys.maxint - 1:
+        raise OverflowError
+    return Erased(x, _identity_for_ints)
 
-def erase_fixedsizelist(l, type):
-    assert isinstance(l, list)
-    result = Erased(l)
-    result._list_item_type = type
-    return result
-
-def unerase(y, type):
-    """Turn an erased object back into an object of type 'type'."""
-    if y._x is None:
-        return None
-    assert isinstance(y._x, type)
+def unerase_int(y):
+    assert y._identity is _identity_for_ints
+    assert isinstance(y._x, int)
     return y._x
 
-def unerase_fixedsizelist(y, type):
-    if y._x is None:
-        return None
-    assert isinstance(y._x, list)
-    if y._x:
-        assert isinstance(y._x[0], type)
-    return y._x
-
-def is_integer(e):
-    """Gives information whether the erased argument is a tagged integer or not."""
-    return isinstance(e._x, int)
-
 
 class ErasingPairIdentity(object):
+
     def __init__(self, name):
         self.name = name
+
     def __repr__(self):
         return 'ErasingPairIdentity(%r)' % self.name
 
+    def _getdict(self, bk):
+        try:
+            dict = bk._erasing_pairs_tunnel
+        except AttributeError:
+            dict = bk._erasing_pairs_tunnel = {}
+        return dict
+
+    def enter_tunnel(self, bookkeeper, s_obj):
+        dict = self._getdict(bookkeeper)
+        s_previousobj, reflowpositions = dict.setdefault(
+            self, (annmodel.s_ImpossibleValue, {}))
+        s_obj = annmodel.unionof(s_previousobj, s_obj)
+        if s_obj != s_previousobj:
+            dict[self] = (s_obj, reflowpositions)
+            for position in reflowpositions:
+                bookkeeper.annotator.reflowfromposition(position)
+
+    def leave_tunnel(self, bookkeeper):
+        dict = self._getdict(bookkeeper)
+        s_obj, reflowpositions = dict.setdefault(
+            self, (annmodel.s_ImpossibleValue, {}))
+        reflowpositions[bookkeeper.position_key] = True
+        return s_obj
+
+    def get_input_annotation(self, bookkeeper):
+        dict = self._getdict(bookkeeper)
+        s_obj, _ = dict[self]
+        return s_obj
+
+_identity_for_ints = ErasingPairIdentity("int")
+
+
 def new_erasing_pair(name):
     identity = ErasingPairIdentity(name)
 
@@ -68,45 +90,29 @@
         assert y._identity is identity
         return y._x
 
-    def _getdict(bk):
-        try:
-            dict = bk._erasing_pairs_tunnel
-        except AttributeError:
-            dict = bk._erasing_pairs_tunnel = {}
-        return dict
-
     class Entry(ExtRegistryEntry):
         _about_ = erase
 
         def compute_result_annotation(self, s_obj):
-            bk = self.bookkeeper
-            dict = _getdict(bk)
-            s_previousobj, reflowpositions = dict.setdefault(
-                identity, (annmodel.s_ImpossibleValue, {}))
-            s_obj = annmodel.unionof(s_previousobj, s_obj)
-            if s_obj != s_previousobj:
-                dict[identity] = (s_obj, reflowpositions)
-                for position in reflowpositions:
-                    bk.annotator.reflowfromposition(position)
+            identity.enter_tunnel(self.bookkeeper, s_obj)
             return SomeErased()
 
         def specialize_call(self, hop):
-            return hop.r_result.specialize_call(hop)
+            bk = hop.rtyper.annotator.bookkeeper
+            s_obj = identity.get_input_annotation(bk)
+            return hop.r_result.rtype_erase(hop, s_obj)
 
     class Entry(ExtRegistryEntry):
         _about_ = unerase
 
         def compute_result_annotation(self, s_obj):
             assert SomeErased().contains(s_obj)
-            bk = self.bookkeeper
-            dict = _getdict(bk)
-            s_obj, reflowpositions = dict.setdefault(
-                identity, (annmodel.s_ImpossibleValue, []))
-            reflowpositions[bk.position_key] = True
-            return s_obj
+            return identity.leave_tunnel(self.bookkeeper)
 
         def specialize_call(self, hop):
-            v, t = hop.inputargs(hop.args_r[0], lltype.Void)
+            if hop.r_result.lowleveltype is lltype.Void:
+                return hop.inputconst(lltype.Void, None)
+            [v] = hop.inputargs(hop.args_r[0])
             return hop.genop('cast_opaque_ptr', [v], resulttype = hop.r_result)
 
     return erase, unerase
@@ -115,103 +121,55 @@
 # ---------- implementation-specific ----------
 
 class Erased(object):
-    _list_item_type = None
-    def __init__(self, x, identity=None):
+    def __init__(self, x, identity):
         self._x = x
         self._identity = identity
     def __repr__(self):
         return "Erased(%r, %r)" % (self._x, self._identity)
 
 class Entry(ExtRegistryEntry):
-    _about_ = erase
+    _about_ = erase_int
 
     def compute_result_annotation(self, s_obj):
+        assert annmodel.SomeInteger().contains(s_obj)
         return SomeErased()
 
     def specialize_call(self, hop):
-        return hop.r_result.specialize_call(hop)
+        return hop.r_result.rtype_erase_int(hop)
 
 class Entry(ExtRegistryEntry):
-    _about_ = erase_fixedsizelist
+    _about_ = unerase_int
 
-    def compute_result_annotation(self, s_arg, s_type):
-        # s_type ignored: only for prebuilt erased lists
-        assert isinstance(s_arg, annmodel.SomeList)
-        s_arg.listdef.never_resize()
-        return SomeErased()
+    def compute_result_annotation(self, s_obj):
+        assert SomeErased().contains(s_obj)
+        return annmodel.SomeInteger()
 
     def specialize_call(self, hop):
-        return hop.r_result.specialize_call(hop)
+        [v] = hop.inputargs(hop.args_r[0])
+        assert isinstance(hop.s_result, annmodel.SomeInteger)
+        return hop.gendirectcall(ll_unerase_int, v)
 
-class Entry(ExtRegistryEntry):
-    _about_ = unerase
-
-    def compute_result_annotation(self, s_obj, s_type):
-        assert s_type.is_constant()
-        if s_type.const is int:
-            return annmodel.SomeInteger()
-        assert isinstance(s_type, annmodel.SomePBC)
-        assert len(s_type.descriptions) == 1
-        clsdef = s_type.any_description().getuniqueclassdef()
-        return annmodel.SomeInstance(clsdef)
-
-    def specialize_call(self, hop):
-        v, t = hop.inputargs(hop.args_r[0], lltype.Void)
-        if isinstance(hop.s_result, annmodel.SomeInteger):
-            c_one = hop.inputconst(lltype.Signed, 1)
-            vi = hop.genop('cast_ptr_to_int', [v], resulttype=lltype.Signed)
-            return hop.genop('int_rshift', [vi, c_one], resulttype=lltype.Signed)
-        return hop.genop('cast_opaque_ptr', [v], resulttype = hop.r_result)
-
-class Entry(ExtRegistryEntry):
-    _about_ = unerase_fixedsizelist
-
-    def compute_result_annotation(self, s_obj, s_type):
-        assert isinstance(s_type, annmodel.SomePBC)
-        assert len(s_type.descriptions) == 1
-        clsdef = s_type.any_description().getuniqueclassdef()
-        s_item = annmodel.SomeInstance(clsdef)
-        return self.bookkeeper.newlist(s_item)
-
-    def specialize_call(self, hop):
-        v, t = hop.inputargs(hop.args_r[0], lltype.Void)
-        return hop.genop('cast_opaque_ptr', [v], resulttype = hop.r_result)
-
-
-class Entry(ExtRegistryEntry):
-    _about_ = is_integer
-
-    def compute_result_annotation(self, s_obj):
-        return annmodel.SomeBool()
-
-    def specialize_call(self, hop):
-        v, = hop.inputargs(hop.args_r[0])
-        c_one = hop.inputconst(lltype.Signed, 1)
-        vi = hop.genop('cast_ptr_to_int', [v], resulttype=lltype.Signed)
-        vb = hop.genop('int_and', [vi, c_one], resulttype=lltype.Signed)
-        return hop.genop('int_is_true', [vb], resulttype=lltype.Bool)
+def ll_unerase_int(gcref):
+    from pypy.rpython.lltypesystem.lloperation import llop
+    from pypy.rlib.debug import ll_assert
+    x = llop.cast_ptr_to_int(lltype.Signed, gcref)
+    ll_assert((x&1) != 0, "unerased_int(): not an integer")
+    return x >> 1
 
 
 class Entry(ExtRegistryEntry):
     _type_ = Erased
 
     def compute_annotation(self):
+        identity = self.instance._identity
         s_obj = self.bookkeeper.immutablevalue(self.instance._x)
-        if self.instance._list_item_type is not None:
-            # only non-resizable lists of instances for now
-            clsdef = self.bookkeeper.getuniqueclassdef(self.instance._list_item_type)
-            s_item = annmodel.SomeInstance(clsdef)
-            s_obj.listdef.generalize(s_item)
-            self.instance._s_list = s_obj
+        identity.enter_tunnel(self.bookkeeper, s_obj)
         return SomeErased()
 
 # annotation and rtyping support
 
 class SomeErased(annmodel.SomeObject):
 
-    def __init__(self, s_obj=None):
-        self.s_obj = s_obj # only non-None for constants
-
     def can_be_none(self):
         return False # cannot be None, but can contain a None
 
@@ -232,48 +190,35 @@
     def __init__(self, rtyper):
         self.rtyper = rtyper
 
-    def specialize_call(self, hop):
-        s_arg = hop.args_s[0]
-        r_generic_object = getinstancerepr(hop.rtyper, None)
-        if (isinstance(s_arg, annmodel.SomeInstance) or
-                (s_arg.is_constant() and s_arg.const is None)):
-            hop.exception_cannot_occur()
-            [v_instance] = hop.inputargs(r_generic_object)   # might generate a cast_pointer
-            v = hop.genop('cast_opaque_ptr', [v_instance],
-                          resulttype=self.lowleveltype)
-            return v
-        elif isinstance(s_arg, annmodel.SomeList):
-            hop.exception_cannot_occur()
-            r_list = self.rtyper.getrepr(s_arg)
-            v_list = hop.inputarg(r_list, 0)
-            v = hop.genop('cast_opaque_ptr', [v_list],
-                          resulttype=self.lowleveltype)
-            return v
-        else:
-            assert isinstance(s_arg, annmodel.SomeInteger)
-            v_value = hop.inputarg(lltype.Signed, arg=0)
-            c_one = hop.inputconst(lltype.Signed, 1)
-            hop.exception_is_here()
-            v2 = hop.genop('int_lshift_ovf', [v_value, c_one],
-                           resulttype = lltype.Signed)
-            v2p1 = hop.genop('int_add', [v2, c_one],
-                             resulttype = lltype.Signed)
-            v_instance = hop.genop('cast_int_to_ptr', [v2p1],
-                                   resulttype=self.lowleveltype)
-            v = hop.genop('cast_opaque_ptr', [v_instance],
-                          resulttype=self.lowleveltype)
-            return v
+    def rtype_erase(self, hop, s_obj):
+        hop.exception_cannot_occur()
+        r_obj = self.rtyper.getrepr(s_obj)
+        if r_obj.lowleveltype is lltype.Void:
+            return hop.inputconst(self.lowleveltype,
+                                  lltype.nullptr(self.lowleveltype.TO))
+        [v_obj] = hop.inputargs(r_obj)
+        return hop.genop('cast_opaque_ptr', [v_obj],
+                         resulttype=self.lowleveltype)
 
+    def rtype_erase_int(self, hop):
+        [v_value] = hop.inputargs(lltype.Signed)
+        c_one = hop.inputconst(lltype.Signed, 1)
+        hop.exception_is_here()
+        v2 = hop.genop('int_lshift_ovf', [v_value, c_one],
+                       resulttype = lltype.Signed)
+        v2p1 = hop.genop('int_add', [v2, c_one],
+                         resulttype = lltype.Signed)
+        v_instance = hop.genop('cast_int_to_ptr', [v2p1],
+                               resulttype=self.lowleveltype)
+        v = hop.genop('cast_opaque_ptr', [v_instance],
+                      resulttype=self.lowleveltype)
+        return v
 
     def convert_const(self, value):
-        if isinstance(value._x, int):
+        if value._identity is _identity_for_ints:
             return lltype.cast_int_to_ptr(self.lowleveltype, value._x * 2 + 1)
-        if isinstance(value._x, list):
-            r_list = self.rtyper.getrepr(value._s_list)
-            v = r_list.convert_const(value._x)
-            return lltype.cast_opaque_ptr(self.lowleveltype, v)
-        else:
-            r_generic_object = getinstancerepr(self.rtyper, None)
-            v = r_generic_object.convert_const(value._x)
-            return lltype.cast_opaque_ptr(self.lowleveltype, v)
-
+        bk = self.rtyper.annotator.bookkeeper
+        s_obj = value._identity.get_input_annotation(bk)
+        r_obj = self.rtyper.getrepr(s_obj)
+        v = r_obj.convert_const(value._x)
+        return lltype.cast_opaque_ptr(self.lowleveltype, v)

diff --git a/pypy/rlib/test/test_rerased.py b/pypy/rlib/test/test_rerased.py
--- a/pypy/rlib/test/test_rerased.py
+++ b/pypy/rlib/test/test_rerased.py
@@ -17,37 +17,42 @@
 class Z(X):
     pass
 
+eraseX, uneraseX = new_erasing_pair("X")
+erase_list_X, unerase_list_X = new_erasing_pair("list of X")
+
 
 def test_simple():
     x1 = X()
-    e = erase(x1)
-    assert is_integer(e) is False
-    assert unerase(e, X) is x1
+    e = eraseX(x1)
+    #assert is_integer(e) is False
+    assert uneraseX(e) is x1
 
 def test_simple_none():
-    e = erase(None)
-    assert unerase(e, X) is None
+    e = eraseX(None)
+    assert uneraseX(e) is None
 
 def test_simple_int():
-    e = erase(15)
-    assert is_integer(e) is True
-    assert unerase(e, int) == 15
+    e = erase_int(15)
+    #assert is_integer(e) is True
+    assert unerase_int(e) == 15
 
 def test_simple_int_overflow():
-    py.test.raises(OverflowError, erase, sys.maxint)
-    py.test.raises(OverflowError, erase, sys.maxint-1)
-    py.test.raises(OverflowError, erase, -sys.maxint)
-    py.test.raises(OverflowError, erase, -sys.maxint-1)
+    erase_int(sys.maxint//2)
+    py.test.raises(OverflowError, erase_int, sys.maxint//2 + 1)
+    py.test.raises(OverflowError, erase_int, sys.maxint)
+    py.test.raises(OverflowError, erase_int, sys.maxint-1)
+    py.test.raises(OverflowError, erase_int, -sys.maxint)
+    py.test.raises(OverflowError, erase_int, -sys.maxint-1)
 
 def test_list():
     l = [X()]
-    e = erase_fixedsizelist(l, X)
-    assert is_integer(e) is False
-    assert unerase_fixedsizelist(e, X) is l
+    e = erase_list_X(l)
+    #assert is_integer(e) is False
+    assert unerase_list_X(e) is l
 
 def test_annotate_1():
     def f():
-        return erase(X())
+        return eraseX(X())
     a = RPythonAnnotator()
     s = a.build_types(f, [])
     assert isinstance(s, SomeErased)
@@ -55,9 +60,9 @@
 def test_annotate_2():
     def f():
         x1 = X()
-        e = erase(x1)
-        assert not is_integer(e)
-        x2 = unerase(e, X)
+        e = eraseX(x1)
+        #assert not is_integer(e)
+        x2 = uneraseX(e)
         return x2
     a = RPythonAnnotator()
     s = a.build_types(f, [])
@@ -66,9 +71,9 @@
 
 def test_annotate_3():
     def f():
-        e = erase(16)
-        assert is_integer(e)
-        x2 = unerase(e, int)
+        e = erase_int(16)
+        #assert is_integer(e)
+        x2 = unerase_int(e)
         return x2
     a = RPythonAnnotator()
     s = a.build_types(f, [])
@@ -76,51 +81,53 @@
 
 def test_rtype_1():
     def f():
-        return erase(X())
+        return eraseX(X())
     x = interpret(f, [])
     assert lltype.typeOf(x) == llmemory.GCREF
 
 def test_rtype_2():
     def f():
         x1 = X()
-        e = erase(x1)
-        assert not is_integer(e)
-        x2 = unerase(e, X)
+        e = eraseX(x1)
+        #assert not is_integer(e)
+        x2 = uneraseX(e)
         return x2
     x = interpret(f, [])
     assert lltype.castable(OBJECTPTR, lltype.typeOf(x)) > 0
 
 def test_rtype_3():
     def f():
-        e = erase(16)
-        assert is_integer(e)
-        x2 = unerase(e, int)
+        e = erase_int(16)
+        #assert is_integer(e)
+        x2 = unerase_int(e)
         return x2
     x = interpret(f, [])
     assert x == 16
 
 
 def test_prebuilt_erased():
-    e1 = erase(16)
+    e1 = erase_int(16)
     x1 = X()
-    e2 = erase(x1)
+    x1.foobar = 42
+    e2 = eraseX(x1)
 
     def f():
-        assert is_integer(e1)
-        assert not is_integer(e2)
-        x2 = unerase(e1, int)
+        #assert is_integer(e1)
+        #assert not is_integer(e2)
+        x1.foobar += 1
+        x2 = unerase_int(e1) + uneraseX(e2).foobar
         return x2
     x = interpret(f, [])
-    assert x == 16
+    assert x == 16 + 42 + 1
 
 def test_overflow():
     def f(i):
         try:
-            e = erase(i)
+            e = erase_int(i)
         except OverflowError:
             return -1
-        assert is_integer(e)
-        return unerase(e, int)
+        #assert is_integer(e)
+        return unerase_int(e)
     x = interpret(f, [16])
     assert x == 16
     x = interpret(f, [sys.maxint])
@@ -128,7 +135,14 @@
 
 def test_none():
     def foo():
-        return unerase(erase(None), X)
+        return uneraseX(eraseX(None))
+    assert foo() is None
+    res = interpret(foo, [])
+    assert not res
+    #
+    def foo():
+        eraseX(X())
+        return uneraseX(eraseX(None))
     assert foo() is None
     res = interpret(foo, [])
     assert not res
@@ -143,19 +157,19 @@
 
 def test_rtype_list():
     prebuilt_l = [X()]
-    prebuilt_e = erase_fixedsizelist(prebuilt_l, X)
+    prebuilt_e = erase_list_X(prebuilt_l)
     def l(flag):
         if flag == 1:
             l = [X()]
-            e = erase_fixedsizelist(l, X)
+            e = erase_list_X(l)
         elif flag == 2:
             l = prebuilt_l
-            e = erase_fixedsizelist(l, X)
+            e = erase_list_X(l)
         else:
             l = prebuilt_l
             e = prebuilt_e
-        assert is_integer(e) is False
-        assert unerase_fixedsizelist(e, X) is l
+        #assert is_integer(e) is False
+        assert unerase_list_X(e) is l
     interpret(l, [0])
     interpret(l, [1])
     interpret(l, [2])
@@ -170,8 +184,6 @@
     erased = erase(x)
     assert unerase(erased) is x
     #
-    assert not is_integer(erased)
-    #
     erase2, unerase2 = new_erasing_pair("test2")
     py.test.raises(AssertionError, unerase2, erased)
 
@@ -233,3 +245,32 @@
     s = a.build_types(f, [])
     assert isinstance(s, annmodel.SomeInstance)
     assert s.classdef == a.bookkeeper.getuniqueclassdef(A)
+
+def test_annotate_prebuilt():
+    erase, unerase = new_erasing_pair("test1")
+    class X(object):
+        pass
+    x1 = X()
+    e1 = erase(x1)
+    e2 = erase(None)
+
+    def f(i):
+        if i:
+            e = e1
+        else:
+            e = e2
+        return unerase(e)
+    #
+    a = RPythonAnnotator()
+    s = a.build_types(f, [int])
+    assert isinstance(s, annmodel.SomeInstance)
+    assert s.classdef == a.bookkeeper.getuniqueclassdef(X)
+    assert s.can_be_none()
+
+def test_annotate_prebuilt_int():
+    e1 = erase_int(42)
+    def f(i):
+        return unerase_int(e1)
+    a = RPythonAnnotator()
+    s = a.build_types(f, [int])
+    assert isinstance(s, annmodel.SomeInteger)


More information about the Pypy-commit mailing list