PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/rlib/rerased.py

https://bitbucket.org/pypy/pypy/
Python | 289 lines | 285 code | 3 blank | 1 comment | 3 complexity | 65b43e50ebdf9d88056b11e5638534e3 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """ Contains a mechanism for turning any class instance and any integer into a
  2. pointer-like thing. Gives full control over pointer tagging, i.e. there won't
  3. be tag checks everywhere in the C code.
  4. Usage: erasestuff, unerasestuff = new_erasing_pair('stuff')
  5. An erasestuff(x) object contains a reference to 'x'. Nothing can be done with
  6. this object, except calling unerasestuff(), which returns 'x' again. The point
  7. is that all erased objects can be mixed together, whether they are instances,
  8. lists, strings, etc. As a special case, an erased object can also be an
  9. integer fitting into 31/63 bits, with erase_int() and unerase_int().
  10. Warning: some care is needed to make sure that you call the unerase function
  11. corresponding to the original creator's erase function. Otherwise, segfault.
  12. """
  13. import sys
  14. from pypy.annotation import model as annmodel
  15. from pypy.tool.pairtype import pairtype
  16. from pypy.rpython.extregistry import ExtRegistryEntry
  17. from pypy.rpython.rclass import getinstancerepr
  18. from pypy.rpython.rmodel import Repr
  19. from pypy.rpython.lltypesystem.lloperation import llop
  20. from pypy.rpython.lltypesystem.rclass import OBJECTPTR
  21. from pypy.rpython.lltypesystem import lltype, llmemory
  22. from pypy.rpython.error import TyperError
  23. def erase_int(x):
  24. assert isinstance(x, int)
  25. res = 2 * x + 1
  26. if res > sys.maxint or res < -sys.maxint - 1:
  27. raise OverflowError
  28. return Erased(x, _identity_for_ints)
  29. def unerase_int(y):
  30. assert y._identity is _identity_for_ints
  31. assert isinstance(y._x, int)
  32. return y._x
  33. class ErasingPairIdentity(object):
  34. def __init__(self, name):
  35. self.name = name
  36. def __repr__(self):
  37. return 'ErasingPairIdentity(%r)' % self.name
  38. def _getdict(self, bk):
  39. try:
  40. dict = bk._erasing_pairs_tunnel
  41. except AttributeError:
  42. dict = bk._erasing_pairs_tunnel = {}
  43. return dict
  44. def enter_tunnel(self, bookkeeper, s_obj):
  45. dict = self._getdict(bookkeeper)
  46. s_previousobj, reflowpositions = dict.setdefault(
  47. self, (annmodel.s_ImpossibleValue, {}))
  48. s_obj = annmodel.unionof(s_previousobj, s_obj)
  49. if s_obj != s_previousobj:
  50. dict[self] = (s_obj, reflowpositions)
  51. for position in reflowpositions:
  52. bookkeeper.annotator.reflowfromposition(position)
  53. def leave_tunnel(self, bookkeeper):
  54. dict = self._getdict(bookkeeper)
  55. s_obj, reflowpositions = dict.setdefault(
  56. self, (annmodel.s_ImpossibleValue, {}))
  57. reflowpositions[bookkeeper.position_key] = True
  58. return s_obj
  59. def get_input_annotation(self, bookkeeper):
  60. dict = self._getdict(bookkeeper)
  61. s_obj, _ = dict[self]
  62. return s_obj
  63. _identity_for_ints = ErasingPairIdentity("int")
  64. def new_erasing_pair(name):
  65. identity = ErasingPairIdentity(name)
  66. def erase(x):
  67. return Erased(x, identity)
  68. def unerase(y):
  69. assert y._identity is identity
  70. return y._x
  71. class Entry(ExtRegistryEntry):
  72. _about_ = erase
  73. def compute_result_annotation(self, s_obj):
  74. identity.enter_tunnel(self.bookkeeper, s_obj)
  75. return SomeErased()
  76. def specialize_call(self, hop):
  77. bk = hop.rtyper.annotator.bookkeeper
  78. s_obj = identity.get_input_annotation(bk)
  79. return hop.r_result.rtype_erase(hop, s_obj)
  80. class Entry(ExtRegistryEntry):
  81. _about_ = unerase
  82. def compute_result_annotation(self, s_obj):
  83. assert SomeErased().contains(s_obj)
  84. return identity.leave_tunnel(self.bookkeeper)
  85. def specialize_call(self, hop):
  86. if hop.r_result.lowleveltype is lltype.Void:
  87. return hop.inputconst(lltype.Void, None)
  88. [v] = hop.inputargs(hop.args_r[0])
  89. return hop.args_r[0].rtype_unerase(hop, v)
  90. return erase, unerase
  91. def new_static_erasing_pair(name):
  92. erase, unerase = new_erasing_pair(name)
  93. return staticmethod(erase), staticmethod(unerase)
  94. # ---------- implementation-specific ----------
  95. class Erased(object):
  96. def __init__(self, x, identity):
  97. self._x = x
  98. self._identity = identity
  99. def __repr__(self):
  100. return "Erased(%r, %r)" % (self._x, self._identity)
  101. class Entry(ExtRegistryEntry):
  102. _about_ = erase_int
  103. def compute_result_annotation(self, s_obj):
  104. config = self.bookkeeper.annotator.translator.config
  105. assert config.translation.taggedpointers, "need to enable tagged pointers to use erase_int"
  106. assert annmodel.SomeInteger().contains(s_obj)
  107. return SomeErased()
  108. def specialize_call(self, hop):
  109. return hop.r_result.rtype_erase_int(hop)
  110. class Entry(ExtRegistryEntry):
  111. _about_ = unerase_int
  112. def compute_result_annotation(self, s_obj):
  113. assert SomeErased().contains(s_obj)
  114. return annmodel.SomeInteger()
  115. def specialize_call(self, hop):
  116. [v] = hop.inputargs(hop.args_r[0])
  117. assert isinstance(hop.s_result, annmodel.SomeInteger)
  118. return hop.args_r[0].rtype_unerase_int(hop, v)
  119. def ll_unerase_int(gcref):
  120. from pypy.rpython.lltypesystem.lloperation import llop
  121. from pypy.rlib.debug import ll_assert
  122. x = llop.cast_ptr_to_int(lltype.Signed, gcref)
  123. ll_assert((x&1) != 0, "unerased_int(): not an integer")
  124. return x >> 1
  125. class Entry(ExtRegistryEntry):
  126. _type_ = Erased
  127. def compute_annotation(self):
  128. identity = self.instance._identity
  129. s_obj = self.bookkeeper.immutablevalue(self.instance._x)
  130. identity.enter_tunnel(self.bookkeeper, s_obj)
  131. return SomeErased()
  132. # annotation and rtyping support
  133. class SomeErased(annmodel.SomeObject):
  134. def can_be_none(self):
  135. return False # cannot be None, but can contain a None
  136. def rtyper_makerepr(self, rtyper):
  137. if rtyper.type_system.name == 'lltypesystem':
  138. return ErasedRepr(rtyper)
  139. elif rtyper.type_system.name == 'ootypesystem':
  140. return OOErasedRepr(rtyper)
  141. def rtyper_makekey(self):
  142. return self.__class__,
  143. class __extend__(pairtype(SomeErased, SomeErased)):
  144. def union((serased1, serased2)):
  145. return SomeErased()
  146. class ErasedRepr(Repr):
  147. lowleveltype = llmemory.GCREF
  148. def __init__(self, rtyper):
  149. self.rtyper = rtyper
  150. def rtype_erase(self, hop, s_obj):
  151. hop.exception_cannot_occur()
  152. r_obj = self.rtyper.getrepr(s_obj)
  153. if r_obj.lowleveltype is lltype.Void:
  154. return hop.inputconst(self.lowleveltype,
  155. lltype.nullptr(self.lowleveltype.TO))
  156. [v_obj] = hop.inputargs(r_obj)
  157. return hop.genop('cast_opaque_ptr', [v_obj],
  158. resulttype=self.lowleveltype)
  159. def rtype_unerase(self, hop, s_obj):
  160. [v] = hop.inputargs(hop.args_r[0])
  161. return hop.genop('cast_opaque_ptr', [v], resulttype=hop.r_result)
  162. def rtype_unerase_int(self, hop, v):
  163. return hop.gendirectcall(ll_unerase_int, v)
  164. def rtype_erase_int(self, hop):
  165. [v_value] = hop.inputargs(lltype.Signed)
  166. c_one = hop.inputconst(lltype.Signed, 1)
  167. hop.exception_is_here()
  168. v2 = hop.genop('int_add_ovf', [v_value, v_value],
  169. resulttype = lltype.Signed)
  170. v2p1 = hop.genop('int_add', [v2, c_one],
  171. resulttype = lltype.Signed)
  172. v_instance = hop.genop('cast_int_to_ptr', [v2p1],
  173. resulttype=self.lowleveltype)
  174. return v_instance
  175. def convert_const(self, value):
  176. if value._identity is _identity_for_ints:
  177. config = self.rtyper.annotator.translator.config
  178. assert config.translation.taggedpointers, "need to enable tagged pointers to use erase_int"
  179. return lltype.cast_int_to_ptr(self.lowleveltype, value._x * 2 + 1)
  180. bk = self.rtyper.annotator.bookkeeper
  181. s_obj = value._identity.get_input_annotation(bk)
  182. r_obj = self.rtyper.getrepr(s_obj)
  183. if r_obj.lowleveltype is lltype.Void:
  184. return lltype.nullptr(self.lowleveltype.TO)
  185. v = r_obj.convert_const(value._x)
  186. return lltype.cast_opaque_ptr(self.lowleveltype, v)
  187. from pypy.rpython.ootypesystem import ootype
  188. class OOErasedRepr(Repr):
  189. lowleveltype = ootype.Object
  190. def __init__(self, rtyper):
  191. self.rtyper = rtyper
  192. def rtype_erase(self, hop, s_obj):
  193. hop.exception_cannot_occur()
  194. r_obj = self.rtyper.getrepr(s_obj)
  195. if r_obj.lowleveltype is lltype.Void:
  196. return hop.inputconst(self.lowleveltype,
  197. ootype.NULL)
  198. [v_obj] = hop.inputargs(r_obj)
  199. return hop.genop('cast_to_object', [v_obj],
  200. resulttype=self.lowleveltype)
  201. def rtype_unerase(self, hop, s_obj):
  202. [v] = hop.inputargs(hop.args_r[0])
  203. return hop.genop('cast_from_object', [v], resulttype=hop.r_result)
  204. def rtype_unerase_int(self, hop, v):
  205. c_one = hop.inputconst(lltype.Signed, 1)
  206. v2 = hop.genop('oounbox_int', [v], resulttype=hop.r_result)
  207. return hop.genop('int_rshift', [v2, c_one], resulttype=lltype.Signed)
  208. def rtype_erase_int(self, hop):
  209. [v_value] = hop.inputargs(lltype.Signed)
  210. c_one = hop.inputconst(lltype.Signed, 1)
  211. hop.exception_is_here()
  212. v2 = hop.genop('int_add_ovf', [v_value, v_value],
  213. resulttype = lltype.Signed)
  214. v2p1 = hop.genop('int_add', [v2, c_one],
  215. resulttype = lltype.Signed)
  216. return hop.genop('oobox_int', [v2p1], resulttype=hop.r_result)
  217. def convert_const(self, value):
  218. if value._identity is _identity_for_ints:
  219. return value._x # FIXME: what should we do here?
  220. bk = self.rtyper.annotator.bookkeeper
  221. s_obj = value._identity.get_input_annotation(bk)
  222. r_obj = self.rtyper.getrepr(s_obj)
  223. if r_obj.lowleveltype is lltype.Void:
  224. return ootype.NULL
  225. v = r_obj.convert_const(value._x)
  226. return ootype.cast_to_object(v)