1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
|
""" 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.
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 rpython.annotator import model as annmodel
from rpython.rtyper.extregistry import ExtRegistryEntry
from rpython.rtyper.llannotation import lltype_to_annotation
from rpython.rtyper.lltypesystem import lltype, llmemory
from rpython.rtyper.lltypesystem.lloperation import llop
from rpython.rlib.rarithmetic import is_valid_int
from rpython.rlib.debug import ll_assert
def erase_int(x):
assert is_valid_int(x)
res = 2 * x + 1
if res > sys.maxint or res < -sys.maxint - 1:
raise OverflowError
return Erased(x, _identity_for_ints)
def unerase_int(y):
assert y._identity is _identity_for_ints
assert is_valid_int(y._x)
return y._x
class ErasingPairIdentity(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return 'ErasingPairIdentity(%r)' % self.name
def __deepcopy__(self, memo):
return self
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)
def erase(x):
return Erased(x, identity)
def unerase(y):
assert y._identity is identity
return y._x
class Entry(ExtRegistryEntry):
_about_ = erase
def compute_result_annotation(self, s_obj):
identity.enter_tunnel(self.bookkeeper, s_obj)
return _some_erased()
def specialize_call(self, hop):
bk = hop.rtyper.annotator.bookkeeper
s_obj = identity.get_input_annotation(bk)
hop.exception_cannot_occur()
return _rtype_erase(hop, s_obj)
class Entry(ExtRegistryEntry):
_about_ = unerase
def compute_result_annotation(self, s_obj):
assert _some_erased().contains(s_obj)
return identity.leave_tunnel(self.bookkeeper)
def specialize_call(self, hop):
hop.exception_cannot_occur()
if hop.r_result.lowleveltype is lltype.Void:
return hop.inputconst(lltype.Void, None)
[v] = hop.inputargs(hop.args_r[0])
return _rtype_unerase(hop, v)
return erase, unerase
def new_static_erasing_pair(name):
erase, unerase = new_erasing_pair(name)
return staticmethod(erase), staticmethod(unerase)
# ---------- implementation-specific ----------
class Erased(object):
def __init__(self, x, identity):
self._x = x
self._identity = identity
def __repr__(self):
return "Erased(%r, %r)" % (self._x, self._identity)
def _convert_const_ptr(self, r_self):
value = self
if value._identity is _identity_for_ints:
config = r_self.rtyper.annotator.translator.config
assert config.translation.taggedpointers, "need to enable tagged pointers to use erase_int"
return lltype.cast_int_to_ptr(r_self.lowleveltype, value._x * 2 + 1)
bk = r_self.rtyper.annotator.bookkeeper
s_obj = value._identity.get_input_annotation(bk)
r_obj = r_self.rtyper.getrepr(s_obj)
if r_obj.lowleveltype is lltype.Void:
return lltype.nullptr(r_self.lowleveltype.TO)
v = r_obj.convert_const(value._x)
return lltype.cast_opaque_ptr(r_self.lowleveltype, v)
class Entry(ExtRegistryEntry):
_about_ = erase_int
def compute_result_annotation(self, s_obj):
config = self.bookkeeper.annotator.translator.config
assert config.translation.taggedpointers, "need to enable tagged pointers to use erase_int"
assert annmodel.SomeInteger().contains(s_obj)
return _some_erased()
def specialize_call(self, hop):
return _rtype_erase_int(hop)
class Entry(ExtRegistryEntry):
_about_ = unerase_int
def compute_result_annotation(self, s_obj):
assert _some_erased().contains(s_obj)
return annmodel.SomeInteger()
def specialize_call(self, hop):
[v] = hop.inputargs(hop.args_r[0])
assert isinstance(hop.s_result, annmodel.SomeInteger)
return _rtype_unerase_int(hop, v)
def ll_unerase_int(gcref):
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)
identity.enter_tunnel(self.bookkeeper, s_obj)
return _some_erased()
# annotation and rtyping support
def _some_erased():
return lltype_to_annotation(llmemory.GCREF)
def _rtype_erase(hop, s_obj):
hop.exception_cannot_occur()
r_obj = hop.rtyper.getrepr(s_obj)
if r_obj.lowleveltype is lltype.Void:
return hop.inputconst(llmemory.GCREF,
lltype.nullptr(llmemory.GCREF.TO))
[v_obj] = hop.inputargs(r_obj)
return hop.genop('cast_opaque_ptr', [v_obj],
resulttype=llmemory.GCREF)
def _rtype_unerase(hop, s_obj):
[v] = hop.inputargs(hop.args_r[0])
return hop.genop('cast_opaque_ptr', [v], resulttype=hop.r_result)
def _rtype_unerase_int(hop, v):
hop.exception_cannot_occur()
return hop.gendirectcall(ll_unerase_int, v)
def _rtype_erase_int(hop):
[v_value] = hop.inputargs(lltype.Signed)
c_one = hop.inputconst(lltype.Signed, 1)
hop.exception_is_here()
v2 = hop.genop('int_add_ovf', [v_value, v_value],
resulttype = lltype.Signed)
v2p1 = hop.genop('int_add', [v2, c_one],
resulttype = lltype.Signed)
v_instance = hop.genop('cast_int_to_ptr', [v2p1],
resulttype=llmemory.GCREF)
return v_instance
|