PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rtyper/rtuple.py

https://bitbucket.org/pypy/pypy/
Python | 413 lines | 378 code | 23 blank | 12 comment | 27 complexity | ce2c4b7d8f5e31936149a8dcc98eb14a MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import operator
  2. from rpython.annotator import model as annmodel
  3. from rpython.flowspace.model import Constant
  4. from rpython.rlib.rarithmetic import intmask
  5. from rpython.rlib.unroll import unrolling_iterable
  6. from rpython.rtyper.error import TyperError
  7. from rpython.rtyper.lltypesystem.lltype import (
  8. Void, Signed, Bool, Ptr, GcStruct, malloc, typeOf, nullptr)
  9. from rpython.rtyper.lltypesystem.rstr import LLHelpers
  10. from rpython.rtyper.rstr import AbstractStringRepr
  11. from rpython.rtyper.rmodel import (Repr, inputconst, IteratorRepr,
  12. externalvsinternal)
  13. from rpython.rtyper.rint import IntegerRepr
  14. from rpython.tool.pairtype import pairtype
  15. class __extend__(annmodel.SomeTuple):
  16. def rtyper_makerepr(self, rtyper):
  17. return TupleRepr(rtyper, [rtyper.getrepr(s_item) for s_item in self.items])
  18. def rtyper_makekey(self):
  19. keys = [s_item.rtyper_makekey() for s_item in self.items]
  20. return tuple([self.__class__] + keys)
  21. _gen_eq_function_cache = {}
  22. _gen_hash_function_cache = {}
  23. _gen_str_function_cache = {}
  24. def gen_eq_function(items_r):
  25. eq_funcs = [r_item.get_ll_eq_function() or operator.eq for r_item in items_r]
  26. key = tuple(eq_funcs)
  27. try:
  28. return _gen_eq_function_cache[key]
  29. except KeyError:
  30. autounrolling_funclist = unrolling_iterable(enumerate(eq_funcs))
  31. def ll_eq(t1, t2):
  32. equal_so_far = True
  33. for i, eqfn in autounrolling_funclist:
  34. if not equal_so_far:
  35. return False
  36. attrname = 'item%d' % i
  37. item1 = getattr(t1, attrname)
  38. item2 = getattr(t2, attrname)
  39. equal_so_far = eqfn(item1, item2)
  40. return equal_so_far
  41. _gen_eq_function_cache[key] = ll_eq
  42. return ll_eq
  43. def gen_hash_function(items_r):
  44. # based on CPython
  45. hash_funcs = [r_item.get_ll_hash_function() for r_item in items_r]
  46. key = tuple(hash_funcs)
  47. try:
  48. return _gen_hash_function_cache[key]
  49. except KeyError:
  50. autounrolling_funclist = unrolling_iterable(enumerate(hash_funcs))
  51. def ll_hash(t):
  52. """Must be kept in sync with rlib.objectmodel._hash_tuple()."""
  53. x = 0x345678
  54. for i, hash_func in autounrolling_funclist:
  55. attrname = 'item%d' % i
  56. item = getattr(t, attrname)
  57. y = hash_func(item)
  58. x = intmask((1000003 * x) ^ y)
  59. return x
  60. _gen_hash_function_cache[key] = ll_hash
  61. return ll_hash
  62. def gen_str_function(tuplerepr):
  63. items_r = tuplerepr.items_r
  64. key = tuple([r_item.ll_str for r_item in items_r])
  65. try:
  66. return _gen_str_function_cache[key]
  67. except KeyError:
  68. autounrolling_funclist = unrolling_iterable(enumerate(key))
  69. constant = LLHelpers.ll_constant
  70. start = LLHelpers.ll_build_start
  71. push = LLHelpers.ll_build_push
  72. finish = LLHelpers.ll_build_finish
  73. length = len(items_r)
  74. def ll_str(t):
  75. if length == 0:
  76. return constant("()")
  77. buf = start(2 * length + 1)
  78. push(buf, constant("("), 0)
  79. for i, str_func in autounrolling_funclist:
  80. attrname = 'item%d' % i
  81. item = getattr(t, attrname)
  82. if i > 0:
  83. push(buf, constant(", "), 2 * i)
  84. push(buf, str_func(item), 2 * i + 1)
  85. if length == 1:
  86. push(buf, constant(",)"), 2 * length)
  87. else:
  88. push(buf, constant(")"), 2 * length)
  89. return finish(buf)
  90. _gen_str_function_cache[key] = ll_str
  91. return ll_str
  92. # ____________________________________________________________
  93. #
  94. # Concrete implementation of RPython tuples:
  95. #
  96. # struct tuple {
  97. # type0 item0;
  98. # type1 item1;
  99. # type2 item2;
  100. # ...
  101. # }
  102. def TUPLE_TYPE(field_lltypes):
  103. if len(field_lltypes) == 0:
  104. return Void # empty tuple
  105. else:
  106. fields = [('item%d' % i, TYPE) for i, TYPE in enumerate(field_lltypes)]
  107. kwds = {'hints': {'immutable': True,
  108. 'noidentity': True}}
  109. return Ptr(GcStruct('tuple%d' % len(field_lltypes), *fields, **kwds))
  110. class TupleRepr(Repr):
  111. def __init__(self, rtyper, items_r):
  112. self.items_r = []
  113. self.external_items_r = []
  114. for item_r in items_r:
  115. external_repr, internal_repr = externalvsinternal(rtyper, item_r)
  116. self.items_r.append(internal_repr)
  117. self.external_items_r.append(external_repr)
  118. items_r = self.items_r
  119. self.fieldnames = ['item%d' % i for i in range(len(items_r))]
  120. self.lltypes = [r.lowleveltype for r in items_r]
  121. self.tuple_cache = {}
  122. self.lowleveltype = TUPLE_TYPE(self.lltypes)
  123. def getitem(self, llops, v_tuple, index):
  124. """Generate the operations to get the index'th item of v_tuple,
  125. in the external repr external_items_r[index]."""
  126. v = self.getitem_internal(llops, v_tuple, index)
  127. r_item = self.items_r[index]
  128. r_external_item = self.external_items_r[index]
  129. return llops.convertvar(v, r_item, r_external_item)
  130. @classmethod
  131. def newtuple(cls, llops, r_tuple, items_v):
  132. # items_v should have the lowleveltype of the internal reprs
  133. assert len(r_tuple.items_r) == len(items_v)
  134. for r_item, v_item in zip(r_tuple.items_r, items_v):
  135. assert r_item.lowleveltype == v_item.concretetype
  136. #
  137. if len(r_tuple.items_r) == 0:
  138. return inputconst(Void, ()) # a Void empty tuple
  139. c1 = inputconst(Void, r_tuple.lowleveltype.TO)
  140. cflags = inputconst(Void, {'flavor': 'gc'})
  141. v_result = llops.genop('malloc', [c1, cflags],
  142. resulttype = r_tuple.lowleveltype)
  143. for i in range(len(r_tuple.items_r)):
  144. cname = inputconst(Void, r_tuple.fieldnames[i])
  145. llops.genop('setfield', [v_result, cname, items_v[i]])
  146. return v_result
  147. @classmethod
  148. def newtuple_cached(cls, hop, items_v):
  149. r_tuple = hop.r_result
  150. if hop.s_result.is_constant():
  151. return inputconst(r_tuple, hop.s_result.const)
  152. else:
  153. return cls.newtuple(hop.llops, r_tuple, items_v)
  154. @classmethod
  155. def _rtype_newtuple(cls, hop):
  156. r_tuple = hop.r_result
  157. vlist = hop.inputargs(*r_tuple.items_r)
  158. return cls.newtuple_cached(hop, vlist)
  159. def convert_const(self, value):
  160. assert isinstance(value, tuple) and len(value) == len(self.items_r)
  161. key = tuple([Constant(item) for item in value])
  162. try:
  163. return self.tuple_cache[key]
  164. except KeyError:
  165. p = self.instantiate()
  166. self.tuple_cache[key] = p
  167. for obj, r, name in zip(value, self.items_r, self.fieldnames):
  168. if r.lowleveltype is not Void:
  169. setattr(p, name, r.convert_const(obj))
  170. return p
  171. def compact_repr(self):
  172. return "TupleR %s" % ' '.join([llt._short_name() for llt in self.lltypes])
  173. def rtype_len(self, hop):
  174. return hop.inputconst(Signed, len(self.items_r))
  175. def get_ll_eq_function(self):
  176. return gen_eq_function(self.items_r)
  177. def get_ll_hash_function(self):
  178. return gen_hash_function(self.items_r)
  179. ll_str = property(gen_str_function)
  180. def make_iterator_repr(self, variant=None):
  181. if variant is not None:
  182. raise TyperError("unsupported %r iterator over a tuple" %
  183. (variant,))
  184. if len(self.items_r) == 1:
  185. # subclasses are supposed to set the IteratorRepr attribute
  186. return self.IteratorRepr(self)
  187. raise TyperError("can only iterate over tuples of length 1 for now")
  188. def instantiate(self):
  189. if len(self.items_r) == 0:
  190. return dum_empty_tuple # PBC placeholder for an empty tuple
  191. else:
  192. return malloc(self.lowleveltype.TO)
  193. def rtype_bltn_list(self, hop):
  194. from rpython.rtyper.lltypesystem import rlist
  195. nitems = len(self.items_r)
  196. vtup = hop.inputarg(self, 0)
  197. LIST = hop.r_result.lowleveltype.TO
  198. cno = inputconst(Signed, nitems)
  199. hop.exception_is_here()
  200. vlist = hop.gendirectcall(LIST.ll_newlist, cno)
  201. v_func = hop.inputconst(Void, rlist.dum_nocheck)
  202. for index in range(nitems):
  203. name = self.fieldnames[index]
  204. ritem = self.items_r[index]
  205. cname = hop.inputconst(Void, name)
  206. vitem = hop.genop('getfield', [vtup, cname], resulttype = ritem)
  207. vitem = hop.llops.convertvar(vitem, ritem, hop.r_result.item_repr)
  208. cindex = inputconst(Signed, index)
  209. hop.gendirectcall(rlist.ll_setitem_nonneg, v_func, vlist, cindex, vitem)
  210. return vlist
  211. def getitem_internal(self, llops, v_tuple, index):
  212. """Return the index'th item, in internal repr."""
  213. name = self.fieldnames[index]
  214. llresult = self.lltypes[index]
  215. cname = inputconst(Void, name)
  216. return llops.genop('getfield', [v_tuple, cname], resulttype = llresult)
  217. def rtype_newtuple(hop):
  218. return TupleRepr._rtype_newtuple(hop)
  219. newtuple = TupleRepr.newtuple
  220. def dum_empty_tuple(): pass
  221. class __extend__(pairtype(TupleRepr, IntegerRepr)):
  222. def rtype_getitem((r_tup, r_int), hop):
  223. v_tuple, v_index = hop.inputargs(r_tup, Signed)
  224. if not isinstance(v_index, Constant):
  225. raise TyperError("non-constant tuple index")
  226. if hop.has_implicit_exception(IndexError):
  227. hop.exception_cannot_occur()
  228. index = v_index.value
  229. return r_tup.getitem(hop.llops, v_tuple, index)
  230. class __extend__(TupleRepr):
  231. def rtype_getslice(r_tup, hop):
  232. s_start = hop.args_s[1]
  233. s_stop = hop.args_s[2]
  234. assert s_start.is_immutable_constant(),"tuple slicing: needs constants"
  235. assert s_stop.is_immutable_constant(), "tuple slicing: needs constants"
  236. start = s_start.const
  237. stop = s_stop.const
  238. indices = range(len(r_tup.items_r))[start:stop]
  239. assert len(indices) == len(hop.r_result.items_r)
  240. v_tup = hop.inputarg(r_tup, arg=0)
  241. items_v = [r_tup.getitem_internal(hop.llops, v_tup, i)
  242. for i in indices]
  243. return hop.r_result.newtuple(hop.llops, hop.r_result, items_v)
  244. class __extend__(pairtype(TupleRepr, Repr)):
  245. def rtype_contains((r_tup, r_item), hop):
  246. s_tup = hop.args_s[0]
  247. if not s_tup.is_constant():
  248. raise TyperError("contains() on non-const tuple")
  249. t = s_tup.const
  250. s_item = hop.args_s[1]
  251. r_item = hop.args_r[1]
  252. v_arg = hop.inputarg(r_item, arg=1)
  253. ll_eq = r_item.get_ll_eq_function() or _ll_equal
  254. v_result = None
  255. for x in t:
  256. s_const_item = hop.rtyper.annotator.bookkeeper.immutablevalue(x)
  257. if not s_item.contains(s_const_item):
  258. continue # corner case, see test_constant_tuple_contains_bug
  259. c_tuple_item = hop.inputconst(r_item, x)
  260. v_equal = hop.gendirectcall(ll_eq, v_arg, c_tuple_item)
  261. if v_result is None:
  262. v_result = v_equal
  263. else:
  264. v_result = hop.genop("int_or", [v_result, v_equal],
  265. resulttype = Bool)
  266. hop.exception_cannot_occur()
  267. return v_result or hop.inputconst(Bool, False)
  268. class __extend__(pairtype(TupleRepr, TupleRepr)):
  269. def rtype_add((r_tup1, r_tup2), hop):
  270. v_tuple1, v_tuple2 = hop.inputargs(r_tup1, r_tup2)
  271. vlist = []
  272. for i in range(len(r_tup1.items_r)):
  273. vlist.append(r_tup1.getitem_internal(hop.llops, v_tuple1, i))
  274. for i in range(len(r_tup2.items_r)):
  275. vlist.append(r_tup2.getitem_internal(hop.llops, v_tuple2, i))
  276. return r_tup1.newtuple_cached(hop, vlist)
  277. rtype_inplace_add = rtype_add
  278. def rtype_eq((r_tup1, r_tup2), hop):
  279. s_tup = annmodel.unionof(*hop.args_s)
  280. r_tup = hop.rtyper.getrepr(s_tup)
  281. v_tuple1, v_tuple2 = hop.inputargs(r_tup, r_tup)
  282. ll_eq = r_tup.get_ll_eq_function()
  283. return hop.gendirectcall(ll_eq, v_tuple1, v_tuple2)
  284. def rtype_ne(tup1tup2, hop):
  285. v_res = tup1tup2.rtype_eq(hop)
  286. return hop.genop('bool_not', [v_res], resulttype=Bool)
  287. def convert_from_to((r_from, r_to), v, llops):
  288. if len(r_from.items_r) == len(r_to.items_r):
  289. if r_from.lowleveltype == r_to.lowleveltype:
  290. return v
  291. n = len(r_from.items_r)
  292. items_v = []
  293. for i in range(n):
  294. item_v = r_from.getitem_internal(llops, v, i)
  295. item_v = llops.convertvar(item_v,
  296. r_from.items_r[i],
  297. r_to.items_r[i])
  298. items_v.append(item_v)
  299. return r_from.newtuple(llops, r_to, items_v)
  300. return NotImplemented
  301. def rtype_is_((robj1, robj2), hop):
  302. raise TyperError("cannot compare tuples with 'is'")
  303. class __extend__(pairtype(AbstractStringRepr, TupleRepr)):
  304. def rtype_mod((r_str, r_tuple), hop):
  305. r_tuple = hop.args_r[1]
  306. v_tuple = hop.args_v[1]
  307. sourcevars = []
  308. for i, r_arg in enumerate(r_tuple.external_items_r):
  309. v_item = r_tuple.getitem(hop.llops, v_tuple, i)
  310. sourcevars.append((v_item, r_arg))
  311. return r_str.ll.do_stringformat(hop, sourcevars)
  312. # ____________________________________________________________
  313. #
  314. # Iteration.
  315. class AbstractTupleIteratorRepr(IteratorRepr):
  316. def newiter(self, hop):
  317. v_tuple, = hop.inputargs(self.r_tuple)
  318. citerptr = hop.inputconst(Void, self.lowleveltype)
  319. return hop.gendirectcall(self.ll_tupleiter, citerptr, v_tuple)
  320. def rtype_next(self, hop):
  321. v_iter, = hop.inputargs(self)
  322. hop.has_implicit_exception(StopIteration) # record that we know about it
  323. hop.exception_is_here()
  324. v = hop.gendirectcall(self.ll_tuplenext, v_iter)
  325. return hop.llops.convertvar(v, self.r_tuple.items_r[0], self.r_tuple.external_items_r[0])
  326. class Length1TupleIteratorRepr(AbstractTupleIteratorRepr):
  327. def __init__(self, r_tuple):
  328. self.r_tuple = r_tuple
  329. self.lowleveltype = Ptr(GcStruct('tuple1iter',
  330. ('tuple', r_tuple.lowleveltype)))
  331. self.ll_tupleiter = ll_tupleiter
  332. self.ll_tuplenext = ll_tuplenext
  333. TupleRepr.IteratorRepr = Length1TupleIteratorRepr
  334. def ll_tupleiter(ITERPTR, tuple):
  335. iter = malloc(ITERPTR.TO)
  336. iter.tuple = tuple
  337. return iter
  338. def ll_tuplenext(iter):
  339. # for iterating over length 1 tuples only!
  340. t = iter.tuple
  341. if t:
  342. iter.tuple = nullptr(typeOf(t).TO)
  343. return t.item0
  344. else:
  345. raise StopIteration
  346. def _ll_equal(x, y):
  347. return x == y