[pypy-svn] r13129 - in pypy/dist/pypy: annotation rpython rpython/test tool translator
arigo at codespeak.net
arigo at codespeak.net
Mon Jun 6 23:33:21 CEST 2005
Author: arigo
Date: Mon Jun 6 23:33:14 2005
New Revision: 13129
Added:
pypy/dist/pypy/rpython/annlowlevel.py (contents, props changed)
pypy/dist/pypy/rpython/normalizecalls.py (contents, props changed)
pypy/dist/pypy/rpython/test/test_normalizecalls.py (contents, props changed)
Modified:
pypy/dist/pypy/annotation/bookkeeper.py
pypy/dist/pypy/annotation/policy.py
pypy/dist/pypy/rpython/rclass.py
pypy/dist/pypy/rpython/rpbc.py
pypy/dist/pypy/rpython/rtyper.py
pypy/dist/pypy/rpython/test/test_rpbc.py
pypy/dist/pypy/tool/sourcetools.py
pypy/dist/pypy/tool/unionfind.py
pypy/dist/pypy/translator/annrpython.py
Log:
- normalizecalls:
Make sure that all functions called in a group have exactly
the same signature, by hacking their flow graphs if needed.
Messy but quite needed.
- started a LowLevelAnnotatorPolicy.
- bookkeeper.compute_at_fixpoint() can now be called several times on
the same annotator.
- UnionFind mapping interface; has_varargs() and has_varkeywords() in
sourcetools.
- couple of bug fixes.
(this is a catch-all intermediate check-in; the test we intended to
pass -- virtual method call -- still doesn't pass)
Modified: pypy/dist/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/dist/pypy/annotation/bookkeeper.py (original)
+++ pypy/dist/pypy/annotation/bookkeeper.py Mon Jun 6 23:33:14 2005
@@ -85,14 +85,17 @@
del self.position_key
def compute_at_fixpoint(self):
- self.pbc_maximal_call_families = UnionFind(PBCCallFamily)
- self.pbc_callables = {}
+ if self.pbc_maximal_call_families is None:
+ self.pbc_maximal_call_families = UnionFind(PBCCallFamily)
+ if self.pbc_callables is None:
+ self.pbc_callables = {}
for (fn, block, i), shape in self.pbc_call_sites.iteritems():
spaceop = block.operations[i]
assert spaceop.opname in ('call_args', 'simple_call')
pbc = self.annotator.binding(spaceop.args[0], extquery=True)
self.consider_pbc_call(pbc, shape, spaceop)
+ self.pbc_call_sites = {}
def getclassdef(self, cls):
"""Get the ClassDef associated with the given user cls."""
Modified: pypy/dist/pypy/annotation/policy.py
==============================================================================
--- pypy/dist/pypy/annotation/policy.py (original)
+++ pypy/dist/pypy/annotation/policy.py Mon Jun 6 23:33:14 2005
@@ -1,7 +1,8 @@
# base annotation policy for overrides and specialization
from pypy.annotation.specialize import memo, ctr_location, default_specialize as default
+from pypy.translator.annrpython import BasicAnnotatorPolicy
-class AnnotatorPolicy:
+class AnnotatorPolicy(BasicAnnotatorPolicy):
"""
Possibly subclass and pass an instance to the annotator to control special casing during annotation
"""
Added: pypy/dist/pypy/rpython/annlowlevel.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/annlowlevel.py Mon Jun 6 23:33:14 2005
@@ -0,0 +1,20 @@
+"""
+The code needed to flow and annotate low-level helpers -- the ll_*() functions
+"""
+
+##from pypy.translator.annrpython import BasicAnnotatorPolicy
+
+
+##class LowLevelAnnotatorPolicy(BasicAnnotatorPolicy):
+
+## def compute_at_fixpoint(self, annotator):
+## pass
+
+
+def annotate_lowlevel_helper(annotator, ll_function, args_s):
+## saved = annotator.policy
+## annotator.policy = LowLevelAnnotatorPolicy()
+## try:
+ annotator.build_types(ll_function, args_s)
+## finally:
+## annotator.policy = saved
Added: pypy/dist/pypy/rpython/normalizecalls.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/normalizecalls.py Mon Jun 6 23:33:14 2005
@@ -0,0 +1,100 @@
+from pypy.objspace.flow.model import Variable, Constant, Block, Link
+from pypy.objspace.flow.model import SpaceOperation, checkgraph
+from pypy.annotation.model import *
+from pypy.tool.sourcetools import has_varargs
+from pypy.rpython.rmodel import TyperError
+
+
+def normalize_function_signatures(annotator):
+ # make sure that all functions called in a group have exactly
+ # the same signature, by hacking their flow graphs if needed
+ callables = annotator.getpbccallables()
+ call_families = annotator.getpbccallfamilies()
+ # for methods, we create or complete a corresponding function-only
+ # family with call patterns that have the extra 'self' argument
+ for family in call_families.infos():
+ prevkey = None
+ for classdef, func in family.objects:
+ if classdef is not None:
+ # add (None, func) to the func_family
+ if prevkey is None:
+ prevkey = (None, func)
+ else:
+ call_families.union((None, func), prevkey)
+ if prevkey is not None:
+ # copy the patterns from family to func_family with the
+ # extra self argument
+ _, _, func_family = call_families.find(prevkey)
+ for pattern in family.patterns:
+ argcount = pattern[0]
+ pattern = (argcount+1,) + pattern[1:]
+ func_family.patterns[pattern] = True
+ # find the most general signature of each family
+ for family in call_families.infos():
+ functions = [func for classdef, func in family.objects
+ if classdef is None] # ignore methods now
+ if len(functions) > 1: # otherwise, nothing to do
+ if len(family.patterns) > 1:
+ raise TyperError("don't support multiple call patterns "
+ "to multiple functions for now %r" % (
+ functions))
+ pattern, = family.patterns
+ shape_cnt, shape_keys, shape_star, shape_stst = pattern
+ assert not shape_keys, "XXX not implemented"
+ assert not shape_star, "XXX not implemented"
+ assert not shape_stst, "XXX not implemented"
+ # for the first 'shape_cnt' arguments we need to generalize to
+ # a common type
+ generalizedargs = []
+ graph_bindings = {}
+ default_values = {}
+ for func in functions:
+ assert not has_varargs(func), "XXX not implemented"
+ graph = annotator.translator.getflowgraph(func)
+ graph_bindings[graph] = [annotator.binding(v)
+ for v in graph.getargs()]
+ for i in range(shape_cnt):
+ args_s = []
+ for bindings in graph_bindings.values():
+ args_s.append(bindings[i])
+ s_value = unionof(*args_s)
+ generalizedargs.append(s_value)
+ for func in functions:
+ graph = annotator.translator.getflowgraph(func)
+ bindings = graph_bindings[graph]
+ if generalizedargs != bindings: #NB. bindings can also be longer
+ oldblock = graph.startblock
+ vlist = []
+ for i in range(len(generalizedargs)):
+ v = Variable(graph.getargs()[i])
+ annotator.setbinding(v, generalizedargs[i])
+ vlist.append(v)
+ newblock = Block(vlist)
+ # add the defaults as constants
+ defaults = func.func_defaults or ()
+ for i in range(len(generalizedargs), len(bindings)):
+ try:
+ default = defaults[i-len(bindings)]
+ except IndexError:
+ raise TyperError("call pattern has %d arguments, "
+ "but %r takes at least %d "
+ "arguments" % (
+ len(generalizedargs), func,
+ len(bindings) - len(defaults)))
+ vlist.append(Constant(default))
+ newblock.closeblock(Link(vlist, oldblock))
+ oldblock.isstartblock = False
+ newblock.isstartblock = True
+ graph.startblock = newblock
+ # finished
+ checkgraph(graph)
+ annotator.annotated[newblock] = annotator.annotated[oldblock]
+ # XXX convert the return value too
+
+
+def perform_normalizations(annotator):
+ annotator.frozen += 1
+ try:
+ normalize_function_signatures(annotator)
+ finally:
+ annotator.frozen -= 1
Modified: pypy/dist/pypy/rpython/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/rclass.py (original)
+++ pypy/dist/pypy/rpython/rclass.py Mon Jun 6 23:33:14 2005
@@ -122,7 +122,13 @@
self.initialized = True
def prepare_method(self, name, s_value, allmethods):
- # special-casing for methods
+ # special-casing for methods:
+ # - a class (read-only) attribute that would contain a PBC
+ # with {func: classdef...} is probably meant to be used as a
+ # method, but in corner cases it could be a constant object
+ # of type MethodType that just sits here in the class. But
+ # as MethodType has a custom __get__ too and we don't support
+ # it, it's a very bad idea anyway.
if isinstance(s_value, annmodel.SomePBC):
debound = {}
count = 0
Modified: pypy/dist/pypy/rpython/rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/rpbc.py (original)
+++ pypy/dist/pypy/rpython/rpbc.py Mon Jun 6 23:33:14 2005
@@ -198,7 +198,7 @@
vfunc = hop.inputconst(typeOf(f), f)
else:
vinst = vlist[0]
- vcls = self.getfield(vinst, '__class__', hop.llops)
+ vcls = self.r_instance.getfield(vinst, '__class__', hop.llops)
vfunc = r_class.getclsfield(vcls, self.methodname, hop.llops)
vlist.insert(0, vfunc)
return hop.genop('direct_call', vlist, resulttype = rresult)
Modified: pypy/dist/pypy/rpython/rtyper.py
==============================================================================
--- pypy/dist/pypy/rpython/rtyper.py (original)
+++ pypy/dist/pypy/rpython/rtyper.py Mon Jun 6 23:33:14 2005
@@ -9,6 +9,8 @@
from pypy.tool.sourcetools import func_with_new_name, valid_identifier
from pypy.translator.unsimplify import insert_empty_block
from pypy.rpython.rmodel import Repr, inputconst, TyperError
+from pypy.rpython.normalizecalls import perform_normalizations
+from pypy.rpython.annlowlevel import annotate_lowlevel_helper
debug = False
@@ -71,6 +73,9 @@
def specialize(self):
"""Main entry point: specialize all annotated blocks of the program."""
+ # first make sure that all functions called in a group have exactly
+ # the same signature, by hacking their flow graphs if needed
+ perform_normalizations(self.annotator)
# new blocks can be created as a result of specialize_block(), so
# we need to be careful about the loop here.
already_seen = {}
@@ -340,6 +345,10 @@
if v is NotImplemented:
raise TyperError("don't know how to convert from %r to %r" %
(r_from, r_to))
+ if v.concretetype != r_to.lowleveltype:
+ raise TyperError("bug in convertion from %r to %r: "
+ "returned a %r" % (r_from, r_to,
+ v.concretetype))
return v
def genop(self, opname, args_v, resulttype=None):
@@ -383,8 +392,7 @@
spec_function = func_with_new_name(ll_function, name)
# flow and annotate (the copy of) the low-level function
self.rtyper.call_all_setups() # compute ForwardReferences now
- spec_graph = rtyper.annotator.translator.getflowgraph(spec_function)
- rtyper.annotator.build_types(spec_function, args_s)
+ annotate_lowlevel_helper(rtyper.annotator, spec_function, args_s)
# cache the result
self.rtyper.specialized_ll_functions[spec_key] = spec_function
Added: pypy/dist/pypy/rpython/test/test_normalizecalls.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/test/test_normalizecalls.py Mon Jun 6 23:33:14 2005
@@ -0,0 +1,38 @@
+from pypy.annotation import model as annmodel
+from pypy.translator.translator import Translator
+from pypy.rpython.rtyper import RPythonTyper
+
+
+def rtype(fn, argtypes=[]):
+ t = Translator(fn)
+ t.annotate(argtypes)
+ typer = RPythonTyper(t.annotator)
+ typer.specialize()
+ #t.view()
+ t.checkgraphs()
+ return t
+
+# ____________________________________________________________
+
+def test_normalize_f2_as_taking_string_argument():
+ def f1(l1):
+ pass
+ def f2(l2):
+ pass
+ def g(n):
+ if n > 0:
+ f1("123")
+ f = f1
+ else:
+ f2("b")
+ f = f2
+ f("a")
+
+ translator = rtype(g, [int])
+ f1graph = translator.getflowgraph(f1)
+ f2graph = translator.getflowgraph(f2)
+ s_l1 = translator.annotator.binding(f1graph.getargs()[0])
+ s_l2 = translator.annotator.binding(f2graph.getargs()[0])
+ assert s_l1.__class__ == annmodel.SomeString # and not SomeChar
+ assert s_l2.__class__ == annmodel.SomeString # and not SomeChar
+ #translator.view()
Modified: pypy/dist/pypy/rpython/test/test_rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rpbc.py (original)
+++ pypy/dist/pypy/rpython/test/test_rpbc.py Mon Jun 6 23:33:14 2005
@@ -8,7 +8,6 @@
t.annotate(argtypes)
typer = RPythonTyper(t.annotator)
typer.specialize()
- #t.view()
t.checkgraphs()
return t
@@ -38,9 +37,23 @@
def m(self, x):
return self.z + x
+class MySubclass(MyBase):
+ def m(self, x):
+ return self.z - x
+
def test_method_call():
def f(a, b):
obj = MyBase()
obj.z = a
return obj.m(b)
- rtype(f, [int, int]) #.view()
+ rtype(f, [int, int])
+
+def PROGRESSING_ON_test_virtual_method_call():
+ def f(a, b):
+ if a > 0:
+ obj = MyBase()
+ else:
+ obj = MySubclass()
+ obj.z = a
+ return obj.m(b)
+ rtype(f, [int, int]).view()
Modified: pypy/dist/pypy/tool/sourcetools.py
==============================================================================
--- pypy/dist/pypy/tool/sourcetools.py (original)
+++ pypy/dist/pypy/tool/sourcetools.py Mon Jun 6 23:33:14 2005
@@ -235,3 +235,14 @@
if not stuff or ('0' <= stuff[0] <= '9'):
stuff = '_' + stuff
return stuff
+
+CO_VARARGS = 0x0004
+CO_VARKEYWORDS = 0x0008
+
+def has_varargs(func):
+ func = getattr(func, 'func_code', func)
+ return (func.co_flags & CO_VARARGS) != 0
+
+def has_varkeywords(func):
+ func = getattr(func, 'func_code', func)
+ return (func.co_flags & CO_VARKEYWORDS) != 0
Modified: pypy/dist/pypy/tool/unionfind.py
==============================================================================
--- pypy/dist/pypy/tool/unionfind.py (original)
+++ pypy/dist/pypy/tool/unionfind.py Mon Jun 6 23:33:14 2005
@@ -19,6 +19,18 @@
return access
+ def __contains__(self, obj):
+ return obj in self.link_to_parent
+
+ def __iter__(self):
+ return iter(self.link_to_parent)
+
+ def keys(self):
+ return self.link_to_parent.keys()
+
+ def infos(self):
+ return self.root_info.values()
+
def find(self, obj): # -> new_root, obj, info
if obj not in self.link_to_parent:
info = self.root_info[obj] = self.info_factory(obj)
Modified: pypy/dist/pypy/translator/annrpython.py
==============================================================================
--- pypy/dist/pypy/translator/annrpython.py (original)
+++ pypy/dist/pypy/translator/annrpython.py Mon Jun 6 23:33:14 2005
@@ -12,13 +12,17 @@
class AnnotatorError(Exception):
pass
-class NullAnnotatorPolicy:
+class BasicAnnotatorPolicy:
def override(pol, func, inputcells):
return None
def specialize(pol, bookkeeper, spaceop, func, args, mono):
return None
+ def compute_at_fixpoint(pol, annotator):
+ annotator.bookkeeper.compute_at_fixpoint()
+
+
class RPythonAnnotator:
"""Block annotator for RPython.
See description in doc/translation/annotation.txt."""
@@ -47,9 +51,10 @@
self.return_bindings = {} # map return Variables to functions
# --- end of debugging information ---
self.bookkeeper = Bookkeeper(self)
+ self.frozen = False
# user-supplied annotation logic for functions we don't want to flow into
if policy is None:
- self.policy = NullAnnotatorPolicy()
+ self.policy = BasicAnnotatorPolicy()
else:
self.policy = policy
@@ -156,6 +161,7 @@
def addpendingblock(self, fn, block, cells, called_from=None):
"""Register an entry point into block with the given input cells."""
assert self.translator is None or fn in self.translator.flowgraphs
+ assert not self.frozen
for a in cells:
assert isinstance(a, annmodel.SomeObject)
if block not in self.annotated:
@@ -187,7 +193,7 @@
print "++-" * 20
raise AnnotatorError('%d blocks are still blocked' %
self.annotated.values().count(False))
- self.bookkeeper.compute_at_fixpoint()
+ self.policy.compute_at_fixpoint(self)
def binding(self, arg, extquery=False):
"Gives the SomeValue corresponding to the given Variable or Constant."
@@ -341,6 +347,7 @@
raise
def reflowpendingblock(self, fn, block):
+ assert not self.frozen
self.pendingblocks[block] = fn
assert block in self.annotated
self.annotated[block] = False # must re-flow
More information about the Pypy-commit
mailing list