PageRenderTime 39ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rtyper/normalizecalls.py

https://bitbucket.org/pypy/pypy/
Python | 411 lines | 328 code | 38 blank | 45 comment | 90 complexity | 0b39ee7dbc7a228fc31cbe15b5dd8bdc MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from rpython.annotator import model as annmodel, description
  2. from rpython.flowspace.argument import Signature
  3. from rpython.flowspace.model import (Variable, Constant, Block, Link,
  4. checkgraph, FunctionGraph, SpaceOperation)
  5. from rpython.rlib.objectmodel import ComputedIntSymbolic
  6. from rpython.rtyper.error import TyperError
  7. from rpython.rtyper.rmodel import getgcflavor
  8. from rpython.tool.sourcetools import valid_identifier
  9. from rpython.annotator.classdesc import ClassDesc
  10. def normalize_call_familes(annotator):
  11. for callfamily in annotator.bookkeeper.pbc_maximal_call_families.infos():
  12. if not callfamily.modified:
  13. assert callfamily.normalized
  14. continue
  15. normalize_calltable(annotator, callfamily)
  16. callfamily.normalized = True
  17. callfamily.modified = False
  18. def normalize_calltable(annotator, callfamily):
  19. """Try to normalize all rows of a table."""
  20. nshapes = len(callfamily.calltables)
  21. for shape, table in callfamily.calltables.items():
  22. for row in table:
  23. did_something = normalize_calltable_row_signature(annotator, shape,
  24. row)
  25. if did_something:
  26. assert not callfamily.normalized, "change in call family normalisation"
  27. if nshapes != 1:
  28. raise_call_table_too_complex_error(callfamily, annotator)
  29. while True:
  30. progress = False
  31. for shape, table in callfamily.calltables.items():
  32. for row in table:
  33. progress |= normalize_calltable_row_annotation(annotator,
  34. row.values())
  35. if not progress:
  36. return # done
  37. assert not callfamily.normalized, "change in call family normalisation"
  38. def raise_call_table_too_complex_error(callfamily, annotator):
  39. msg = []
  40. items = callfamily.calltables.items()
  41. for i, (shape1, table1) in enumerate(items):
  42. for shape2, table2 in items[i + 1:]:
  43. if shape1 == shape2:
  44. continue
  45. row1 = table1[0]
  46. row2 = table2[0]
  47. problematic_function_graphs = set(row1.values()).union(set(row2.values()))
  48. pfg = [str(graph) for graph in problematic_function_graphs]
  49. pfg.sort()
  50. msg.append("the following functions:")
  51. msg.append(" %s" % ("\n ".join(pfg), ))
  52. msg.append("are called with inconsistent numbers of arguments")
  53. msg.append("(and/or the argument names are different, which is"
  54. " not supported in this case)")
  55. if shape1[0] != shape2[0]:
  56. msg.append("sometimes with %s arguments, sometimes with %s" % (shape1[0], shape2[0]))
  57. else:
  58. pass # XXX better message in this case
  59. callers = []
  60. msg.append("the callers of these functions are:")
  61. for tag, (caller, callee) in annotator.translator.callgraph.iteritems():
  62. if callee not in problematic_function_graphs:
  63. continue
  64. if str(caller) in callers:
  65. continue
  66. callers.append(str(caller))
  67. callers.sort()
  68. for caller in callers:
  69. msg.append(" %s" % (caller, ))
  70. raise TyperError("\n".join(msg))
  71. def normalize_calltable_row_signature(annotator, shape, row):
  72. graphs = row.values()
  73. assert graphs, "no graph??"
  74. sig0 = graphs[0].signature
  75. defaults0 = graphs[0].defaults
  76. for graph in graphs[1:]:
  77. if graph.signature != sig0:
  78. break
  79. if graph.defaults != defaults0:
  80. break
  81. else:
  82. return False # nothing to do, all signatures already match
  83. shape_cnt, shape_keys, shape_star = shape
  84. assert not shape_star, "should have been removed at this stage"
  85. # for the first 'shape_cnt' arguments we need to generalize to
  86. # a common type
  87. call_nbargs = shape_cnt + len(shape_keys)
  88. did_something = False
  89. for graph in graphs:
  90. argnames, varargname, kwargname = graph.signature
  91. assert not varargname, "XXX not implemented"
  92. assert not kwargname, "XXX not implemented" # ?
  93. inputargs_s = [annotator.binding(v) for v in graph.getargs()]
  94. argorder = range(shape_cnt)
  95. for key in shape_keys:
  96. i = list(argnames).index(key)
  97. assert i not in argorder
  98. argorder.append(i)
  99. need_reordering = (argorder != range(call_nbargs))
  100. if need_reordering or len(graph.getargs()) != call_nbargs:
  101. oldblock = graph.startblock
  102. inlist = []
  103. defaults = graph.defaults or ()
  104. num_nondefaults = len(inputargs_s) - len(defaults)
  105. defaults = [description.NODEFAULT] * num_nondefaults + list(defaults)
  106. newdefaults = []
  107. for j in argorder:
  108. v = Variable(graph.getargs()[j])
  109. annotator.setbinding(v, inputargs_s[j])
  110. inlist.append(v)
  111. newdefaults.append(defaults[j])
  112. newblock = Block(inlist)
  113. # prepare the output args of newblock:
  114. # 1. collect the positional arguments
  115. outlist = inlist[:shape_cnt]
  116. # 2. add defaults and keywords
  117. for j in range(shape_cnt, len(inputargs_s)):
  118. try:
  119. i = argorder.index(j)
  120. v = inlist[i]
  121. except ValueError:
  122. default = defaults[j]
  123. if default is description.NODEFAULT:
  124. raise TyperError(
  125. "call pattern has %d positional arguments, "
  126. "but %r takes at least %d arguments" % (
  127. shape_cnt, graph.name, num_nondefaults))
  128. v = Constant(default)
  129. outlist.append(v)
  130. newblock.closeblock(Link(outlist, oldblock))
  131. graph.startblock = newblock
  132. for i in range(len(newdefaults)-1,-1,-1):
  133. if newdefaults[i] is description.NODEFAULT:
  134. newdefaults = newdefaults[i:]
  135. break
  136. graph.defaults = tuple(newdefaults)
  137. graph.signature = Signature([argnames[j] for j in argorder],
  138. None, None)
  139. # finished
  140. checkgraph(graph)
  141. annotator.annotated[newblock] = annotator.annotated[oldblock]
  142. did_something = True
  143. return did_something
  144. def normalize_calltable_row_annotation(annotator, graphs):
  145. if len(graphs) <= 1:
  146. return False # nothing to do
  147. graph_bindings = {}
  148. for graph in graphs:
  149. graph_bindings[graph] = [annotator.binding(v)
  150. for v in graph.getargs()]
  151. iterbindings = graph_bindings.itervalues()
  152. nbargs = len(iterbindings.next())
  153. for binding in iterbindings:
  154. assert len(binding) == nbargs
  155. generalizedargs = []
  156. for i in range(nbargs):
  157. args_s = []
  158. for graph, bindings in graph_bindings.items():
  159. args_s.append(bindings[i])
  160. s_value = annmodel.unionof(*args_s)
  161. generalizedargs.append(s_value)
  162. result_s = [annotator.binding(graph.getreturnvar())
  163. for graph in graph_bindings]
  164. generalizedresult = annmodel.unionof(*result_s)
  165. conversion = False
  166. for graph in graphs:
  167. bindings = graph_bindings[graph]
  168. need_conversion = (generalizedargs != bindings)
  169. if need_conversion:
  170. conversion = True
  171. oldblock = graph.startblock
  172. inlist = []
  173. for j, s_value in enumerate(generalizedargs):
  174. v = Variable(graph.getargs()[j])
  175. annotator.setbinding(v, s_value)
  176. inlist.append(v)
  177. newblock = Block(inlist)
  178. # prepare the output args of newblock and link
  179. outlist = inlist[:]
  180. newblock.closeblock(Link(outlist, oldblock))
  181. graph.startblock = newblock
  182. # finished
  183. checkgraph(graph)
  184. annotator.annotated[newblock] = annotator.annotated[oldblock]
  185. # convert the return value too
  186. if annotator.binding(graph.getreturnvar()) != generalizedresult:
  187. conversion = True
  188. annotator.setbinding(graph.getreturnvar(), generalizedresult)
  189. return conversion
  190. # ____________________________________________________________
  191. def merge_classpbc_getattr_into_classdef(annotator):
  192. # code like 'some_class.attr' will record an attribute access in the
  193. # PBC access set of the family of classes of 'some_class'. If the classes
  194. # have corresponding ClassDefs, they are not updated by the annotator.
  195. # We have to do it now.
  196. all_families = annotator.bookkeeper.classpbc_attr_families
  197. for attrname, access_sets in all_families.items():
  198. for access_set in access_sets.infos():
  199. descs = access_set.descs
  200. if len(descs) <= 1:
  201. continue
  202. if not isinstance(descs.iterkeys().next(), ClassDesc):
  203. continue
  204. classdefs = [desc.getuniqueclassdef() for desc in descs]
  205. commonbase = classdefs[0]
  206. for cdef in classdefs[1:]:
  207. commonbase = commonbase.commonbase(cdef)
  208. if commonbase is None:
  209. raise TyperError("reading attribute %r: no common base "
  210. "class for %r" % (attrname, descs.keys()))
  211. extra_access_sets = commonbase.extra_access_sets
  212. if commonbase.repr is not None:
  213. assert access_set in extra_access_sets # minimal sanity check
  214. continue
  215. access_set.commonbase = commonbase
  216. if access_set not in extra_access_sets:
  217. counter = len(extra_access_sets)
  218. extra_access_sets[access_set] = attrname, counter
  219. # ____________________________________________________________
  220. def create_class_constructors(annotator):
  221. bk = annotator.bookkeeper
  222. call_families = bk.pbc_maximal_call_families
  223. for family in call_families.infos():
  224. if len(family.descs) <= 1:
  225. continue
  226. descs = family.descs.keys()
  227. if not isinstance(descs[0], ClassDesc):
  228. continue
  229. # Note that if classes are in the same callfamily, their __init__
  230. # attribute must be in the same attrfamily as well.
  231. change = descs[0].mergeattrfamilies(descs[1:], '__init__')
  232. if hasattr(descs[0].getuniqueclassdef(), 'my_instantiate_graph'):
  233. assert not change, "after the fact change to a family of classes" # minimal sanity check
  234. continue
  235. # Put __init__ into the attr family, for ClassesPBCRepr.call()
  236. attrfamily = descs[0].getattrfamily('__init__')
  237. inits_s = [desc.s_read_attribute('__init__') for desc in descs]
  238. s_value = annmodel.unionof(attrfamily.s_value, *inits_s)
  239. attrfamily.s_value = s_value
  240. # ClassesPBCRepr.call() will also need instantiate() support
  241. for desc in descs:
  242. bk.needs_generic_instantiate[desc.getuniqueclassdef()] = True
  243. # ____________________________________________________________
  244. def create_instantiate_functions(annotator):
  245. # build the 'instantiate() -> instance of C' functions for the vtables
  246. needs_generic_instantiate = annotator.bookkeeper.needs_generic_instantiate
  247. for classdef in needs_generic_instantiate:
  248. assert getgcflavor(classdef) == 'gc' # only gc-case
  249. create_instantiate_function(annotator, classdef)
  250. def create_instantiate_function(annotator, classdef):
  251. # build the graph of a function that looks like
  252. #
  253. # def my_instantiate():
  254. # return instantiate(cls)
  255. #
  256. if hasattr(classdef, 'my_instantiate_graph'):
  257. return
  258. v = Variable()
  259. block = Block([])
  260. block.operations.append(SpaceOperation('instantiate1', [], v))
  261. name = valid_identifier('instantiate_' + classdef.name)
  262. graph = FunctionGraph(name, block)
  263. block.closeblock(Link([v], graph.returnblock))
  264. annotator.setbinding(v, annmodel.SomeInstance(classdef))
  265. annotator.annotated[block] = graph
  266. # force the result to be converted to a generic OBJECTPTR
  267. generalizedresult = annmodel.SomeInstance(classdef=None)
  268. annotator.setbinding(graph.getreturnvar(), generalizedresult)
  269. classdef.my_instantiate_graph = graph
  270. annotator.translator.graphs.append(graph)
  271. # ____________________________________________________________
  272. class TooLateForNewSubclass(Exception):
  273. pass
  274. class TotalOrderSymbolic(ComputedIntSymbolic):
  275. def __init__(self, orderwitness, peers):
  276. self.orderwitness = orderwitness
  277. self.peers = peers
  278. self.value = None
  279. self._with_subclasses = None # unknown
  280. peers.append(self)
  281. def __cmp__(self, other):
  282. if not isinstance(other, TotalOrderSymbolic):
  283. return cmp(self.compute_fn(), other)
  284. else:
  285. return cmp(self.orderwitness, other.orderwitness)
  286. # support for implementing int_between: (a<=b<c) with (b-a<c-a)
  287. # see rpython.jit.metainterp.pyjitpl.opimpl_int_between
  288. def __sub__(self, other):
  289. return self.compute_fn() - other
  290. def __rsub__(self, other):
  291. return other - self.compute_fn()
  292. def check_any_subclass_in_peer_list(self, i):
  293. # check if the next peer, in order, is or not the end
  294. # marker for this start marker
  295. assert self.peers[i] is self
  296. return self.peers[i + 1].orderwitness != self.orderwitness + [MAX]
  297. def number_with_subclasses(self):
  298. # Return True or False depending on whether this is the
  299. # subclassrange_min corresponding to a class which has subclasses
  300. # or not. If this is called and returns False, then adding later
  301. # new subclasses will crash in compute_fn().
  302. if self._with_subclasses is None: # unknown so far
  303. self.peers.sort()
  304. i = self.peers.index(self)
  305. self._with_subclasses = self.check_any_subclass_in_peer_list(i)
  306. return self._with_subclasses
  307. def compute_fn(self):
  308. if self.value is None:
  309. self.peers.sort()
  310. for i, peer in enumerate(self.peers):
  311. assert peer.value is None or peer.value == i
  312. peer.value = i
  313. #
  314. if peer._with_subclasses is False:
  315. if peer.check_any_subclass_in_peer_list(i):
  316. raise TooLateForNewSubclass
  317. #
  318. assert self.value is not None
  319. return self.value
  320. def dump(self, annotator): # for debugging
  321. self.peers.sort()
  322. mapping = {}
  323. for classdef in annotator.bookkeeper.classdefs:
  324. if hasattr(classdef, '_unique_cdef_id'):
  325. mapping[classdef._unique_cdef_id] = classdef
  326. for peer in self.peers:
  327. if peer is self:
  328. print '==>',
  329. else:
  330. print ' ',
  331. print 'value %4s --' % (peer.value,), peer.orderwitness,
  332. if peer.orderwitness[-1] in mapping:
  333. print mapping[peer.orderwitness[-1]]
  334. else:
  335. print
  336. def assign_inheritance_ids(annotator):
  337. # we sort the classes by lexicographic order of reversed(mro),
  338. # which gives a nice depth-first order. The classes are turned
  339. # into numbers in order to (1) help determinism, (2) ensure that
  340. # new hierarchies of classes with no common base classes can be
  341. # added later and get higher numbers.
  342. bk = annotator.bookkeeper
  343. try:
  344. lst = bk._inheritance_id_symbolics
  345. except AttributeError:
  346. lst = bk._inheritance_id_symbolics = []
  347. for classdef in annotator.bookkeeper.classdefs:
  348. if not hasattr(classdef, 'minid'):
  349. witness = [get_unique_cdef_id(cdef) for cdef in classdef.getmro()]
  350. witness.reverse()
  351. classdef.minid = TotalOrderSymbolic(witness, lst)
  352. classdef.maxid = TotalOrderSymbolic(witness + [MAX], lst)
  353. MAX = 1E100
  354. _cdef_id_counter = 0
  355. def get_unique_cdef_id(cdef):
  356. global _cdef_id_counter
  357. try:
  358. return cdef._unique_cdef_id
  359. except AttributeError:
  360. cdef._unique_cdef_id = _cdef_id_counter
  361. _cdef_id_counter += 1
  362. return cdef._unique_cdef_id
  363. # ____________________________________________________________
  364. def perform_normalizations(annotator):
  365. create_class_constructors(annotator)
  366. annotator.frozen += 1
  367. try:
  368. normalize_call_familes(annotator)
  369. merge_classpbc_getattr_into_classdef(annotator)
  370. assign_inheritance_ids(annotator)
  371. finally:
  372. annotator.frozen -= 1
  373. create_instantiate_functions(annotator)