/pypy/rpython/normalizecalls.py

https://bitbucket.org/pypy/pypy/ · Python · 364 lines · 295 code · 32 blank · 37 comment · 80 complexity · 5a48f23a5407609ca9fc48cd9fd4787a MD5 · raw file

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