[pypy-commit] pypy default: Merge branch 'var-in-Some'
rlamy
noreply at buildbot.pypy.org
Fri Oct 10 18:31:30 CEST 2014
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch:
Changeset: r73889:46914a24fcd4
Date: 2014-10-10 17:30 +0100
http://bitbucket.org/pypy/pypy/changeset/46914a24fcd4/
Log: Merge branch 'var-in-Some'
Store annotations on the Variable objects, rather than in a big
dict. Introduce a new framework for double-dispatched annotation
implementations.
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -15,3 +15,7 @@
.. branch: rtyper-stuff
Small internal refactorings in the rtyper.
+
+.. branch: var-in-Some
+Store annotations on the Variable objects, rather than in a big dict.
+Introduce a new framework for double-dispatched annotation implementations.
diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py
--- a/rpython/annotator/annrpython.py
+++ b/rpython/annotator/annrpython.py
@@ -17,7 +17,6 @@
log = py.log.Producer("annrpython")
py.log.setconsumer("annrpython", ansi_log)
-FAIL = object()
class RPythonAnnotator(object):
"""Block annotator for RPython.
@@ -33,7 +32,6 @@
translator.annotator = self
self.translator = translator
self.pendingblocks = {} # map {block: graph-containing-it}
- self.bindings = {} # map Variables to SomeValues
self.annotated = {} # set of blocks already seen
self.added_blocks = None # see processblock() below
self.links_followed = {} # set of links that have ever been followed
@@ -54,7 +52,7 @@
self.bookkeeper = bookkeeper
def __getstate__(self):
- attrs = """translator pendingblocks bindings annotated links_followed
+ attrs = """translator pendingblocks annotated links_followed
notify bookkeeper frozen policy added_blocks""".split()
ret = self.__dict__.copy()
for key, value in ret.items():
@@ -143,7 +141,7 @@
# recursively proceed until no more pending block is left
if complete_now:
self.complete()
- return self.binding(flowgraph.getreturnvar(), None)
+ return self.annotation(flowgraph.getreturnvar())
def gettype(self, variable):
"""Return the known type of a control flow graph variable,
@@ -151,9 +149,9 @@
if isinstance(variable, Constant):
return type(variable.value)
elif isinstance(variable, Variable):
- cell = self.bindings.get(variable)
- if cell:
- return cell.knowntype
+ s_variable = variable.annotation
+ if s_variable:
+ return s_variable.knowntype
else:
return object
else:
@@ -221,37 +219,39 @@
raise annmodel.AnnotatorError(text)
for graph in newgraphs:
v = graph.getreturnvar()
- if v not in self.bindings:
+ if v.annotation is None:
self.setbinding(v, annmodel.s_ImpossibleValue)
# policy-dependent computation
self.bookkeeper.compute_at_fixpoint()
- def binding(self, arg, default=FAIL):
+ def annotation(self, arg):
"Gives the SomeValue corresponding to the given Variable or Constant."
if isinstance(arg, Variable):
- try:
- return self.bindings[arg]
- except KeyError:
- if default is not FAIL:
- return default
- else:
- raise
+ return arg.annotation
elif isinstance(arg, Constant):
return self.bookkeeper.immutablevalue(arg.value)
else:
raise TypeError('Variable or Constant expected, got %r' % (arg,))
+ def binding(self, arg):
+ "Gives the SomeValue corresponding to the given Variable or Constant."
+ s_arg = self.annotation(arg)
+ if s_arg is None:
+ raise KeyError
+ return s_arg
+
def typeannotation(self, t):
return signature.annotation(t, self.bookkeeper)
def setbinding(self, arg, s_value):
- if arg in self.bindings:
- assert s_value.contains(self.bindings[arg])
- self.bindings[arg] = s_value
+ s_old = arg.annotation
+ if s_old is not None:
+ assert s_value.contains(s_old)
+ arg.annotation = s_value
def transfer_binding(self, v_target, v_source):
- assert v_source in self.bindings
- self.bindings[v_target] = self.bindings[v_source]
+ assert v_source.annotation is not None
+ v_target.annotation = v_source.annotation
def warning(self, msg, pos=None):
if pos is None:
@@ -290,7 +290,7 @@
# get the (current) return value
v = graph.getreturnvar()
try:
- return self.bindings[v]
+ return self.binding(v)
except KeyError:
# the function didn't reach any return statement so far.
# (some functions actually never do, they always raise exceptions)
@@ -328,7 +328,7 @@
# * block not in self.annotated:
# never seen the block.
# * self.annotated[block] == False:
- # the input variables of the block are in self.bindings but we
+ # the input variables of the block have bindings but we
# still have to consider all the operations in the block.
# * self.annotated[block] == graph-containing-block:
# analysis done (at least until we find we must generalize the
@@ -443,7 +443,7 @@
# is known
exits = block.exits
if isinstance(block.exitswitch, Variable):
- s_exitswitch = self.bindings[block.exitswitch]
+ s_exitswitch = self.binding(block.exitswitch)
if s_exitswitch.is_constant():
exits = [link for link in exits
if link.exitcase == s_exitswitch.const]
@@ -452,20 +452,7 @@
# occour for this specific, typed operation.
if block.exitswitch == c_last_exception:
op = block.operations[-1]
- if op.dispatch == 2:
- arg1 = self.binding(op.args[0])
- arg2 = self.binding(op.args[1])
- binop = getattr(pair(arg1, arg2), op.opname, None)
- can_only_throw = annmodel.read_can_only_throw(binop, arg1, arg2)
- elif op.dispatch == 1:
- arg1 = self.binding(op.args[0])
- opname = op.opname
- if opname == 'contains': opname = 'op_contains'
- unop = getattr(arg1, opname, None)
- can_only_throw = annmodel.read_can_only_throw(unop, arg1)
- else:
- can_only_throw = None
-
+ can_only_throw = op.get_can_only_throw(self)
if can_only_throw is not None:
candidates = can_only_throw
candidate_exits = exits
@@ -482,8 +469,10 @@
# mapping (exitcase, variable) -> s_annotation
# that can be attached to booleans, exitswitches
- knowntypedata = getattr(self.bindings.get(block.exitswitch),
- "knowntypedata", {})
+ knowntypedata = {}
+ if isinstance(block.exitswitch, Variable):
+ knowntypedata = getattr(self.binding(block.exitswitch),
+ "knowntypedata", {})
for link in exits:
self.follow_link(graph, link, knowntypedata)
if block in self.notify:
@@ -578,22 +567,19 @@
self.links_followed[link] = True
self.addpendingblock(graph, link.target, cells)
-
#___ creating the annotations based on operations ______
def consider_op(self, op):
- argcells = [self.binding(a) for a in op.args]
-
# let's be careful about avoiding propagated SomeImpossibleValues
# to enter an op; the latter can result in violations of the
# more general results invariant: e.g. if SomeImpossibleValue enters is_
# is_(SomeImpossibleValue, None) -> SomeBool
# is_(SomeInstance(not None), None) -> SomeBool(const=False) ...
# boom -- in the assert of setbinding()
- for arg in argcells:
- if isinstance(arg, annmodel.SomeImpossibleValue):
+ for arg in op.args:
+ if isinstance(self.annotation(arg), annmodel.SomeImpossibleValue):
raise BlockedInference(self, op, -1)
- resultcell = op.consider(self, *argcells)
+ resultcell = op.consider(self, *op.args)
if resultcell is None:
resultcell = annmodel.s_ImpossibleValue
elif resultcell == annmodel.s_ImpossibleValue:
diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py
--- a/rpython/annotator/binaryop.py
+++ b/rpython/annotator/binaryop.py
@@ -13,7 +13,7 @@
SomeLongFloat, SomeType, SomeConstantType, unionof, UnionError,
read_can_only_throw, add_knowntypedata,
merge_knowntypedata,)
-from rpython.annotator.bookkeeper import getbookkeeper, immutablevalue
+from rpython.annotator.bookkeeper import immutablevalue
from rpython.flowspace.model import Variable, Constant
from rpython.flowspace.operation import op
from rpython.rlib import rarithmetic
@@ -23,6 +23,54 @@
if oper.dispatch == 2])
+ at op.is_.register(SomeObject, SomeObject)
+def is__default(annotator, obj1, obj2):
+ r = SomeBool()
+ s_obj1 = annotator.annotation(obj1)
+ s_obj2 = annotator.annotation(obj2)
+ if s_obj2.is_constant():
+ if s_obj1.is_constant():
+ r.const = s_obj1.const is s_obj2.const
+ if s_obj2.const is None and not s_obj1.can_be_none():
+ r.const = False
+ elif s_obj1.is_constant():
+ if s_obj1.const is None and not s_obj2.can_be_none():
+ r.const = False
+ knowntypedata = {}
+ bk = annotator.bookkeeper
+
+ def bind(src_obj, tgt_obj):
+ s_src = annotator.annotation(src_obj)
+ s_tgt = annotator.annotation(tgt_obj)
+ if hasattr(s_tgt, 'is_type_of') and s_src.is_constant():
+ add_knowntypedata(
+ knowntypedata, True,
+ s_tgt.is_type_of,
+ bk.valueoftype(s_src.const))
+ add_knowntypedata(knowntypedata, True, [tgt_obj], s_src)
+ s_nonnone = s_tgt
+ if (s_src.is_constant() and s_src.const is None and
+ s_tgt.can_be_none()):
+ s_nonnone = s_tgt.nonnoneify()
+ add_knowntypedata(knowntypedata, False, [tgt_obj], s_nonnone)
+
+ bind(obj2, obj1)
+ bind(obj1, obj2)
+ r.set_knowntypedata(knowntypedata)
+ return r
+
+def _make_cmp_annotator_default(cmp_op):
+ @cmp_op.register(SomeObject, SomeObject)
+ def default_annotate(annotator, obj1, obj2):
+ s_1, s_2 = annotator.annotation(obj1), annotator.annotation(obj2)
+ if s_1.is_immutable_constant() and s_2.is_immutable_constant():
+ return immutablevalue(cmp_op.pyfunc(s_1.const, s_2.const))
+ else:
+ return s_Bool
+
+for cmp_op in [op.lt, op.le, op.eq, op.ne, op.gt, op.ge]:
+ _make_cmp_annotator_default(cmp_op)
+
class __extend__(pairtype(SomeObject, SomeObject)):
def union((obj1, obj2)):
@@ -51,87 +99,12 @@
inplace_floordiv.can_only_throw = [ZeroDivisionError]
inplace_mod.can_only_throw = [ZeroDivisionError]
- def lt((obj1, obj2)):
- if obj1.is_immutable_constant() and obj2.is_immutable_constant():
- return immutablevalue(obj1.const < obj2.const)
- else:
- return s_Bool
-
- def le((obj1, obj2)):
- if obj1.is_immutable_constant() and obj2.is_immutable_constant():
- return immutablevalue(obj1.const <= obj2.const)
- else:
- return s_Bool
-
- def eq((obj1, obj2)):
- if obj1.is_immutable_constant() and obj2.is_immutable_constant():
- return immutablevalue(obj1.const == obj2.const)
- else:
- return s_Bool
-
- def ne((obj1, obj2)):
- if obj1.is_immutable_constant() and obj2.is_immutable_constant():
- return immutablevalue(obj1.const != obj2.const)
- else:
- return s_Bool
-
- def gt((obj1, obj2)):
- if obj1.is_immutable_constant() and obj2.is_immutable_constant():
- return immutablevalue(obj1.const > obj2.const)
- else:
- return s_Bool
-
- def ge((obj1, obj2)):
- if obj1.is_immutable_constant() and obj2.is_immutable_constant():
- return immutablevalue(obj1.const >= obj2.const)
- else:
- return s_Bool
-
def cmp((obj1, obj2)):
if obj1.is_immutable_constant() and obj2.is_immutable_constant():
return immutablevalue(cmp(obj1.const, obj2.const))
else:
return SomeInteger()
- def is_((obj1, obj2)):
- r = SomeBool()
- if obj2.is_constant():
- if obj1.is_constant():
- r.const = obj1.const is obj2.const
- if obj2.const is None and not obj1.can_be_none():
- r.const = False
- elif obj1.is_constant():
- if obj1.const is None and not obj2.can_be_none():
- r.const = False
- # XXX HACK HACK HACK
- # XXX HACK HACK HACK
- # XXX HACK HACK HACK
- bk = getbookkeeper()
- if bk is not None: # for testing
- op = bk._find_current_op("is_", 2)
- knowntypedata = {}
- annotator = bk.annotator
-
- def bind(src_obj, tgt_obj, tgt_arg):
- if hasattr(tgt_obj, 'is_type_of') and src_obj.is_constant():
- add_knowntypedata(knowntypedata, True, tgt_obj.is_type_of,
- bk.valueoftype(src_obj.const))
-
- assert annotator.binding(op.args[tgt_arg]) == tgt_obj
- add_knowntypedata(knowntypedata, True, [op.args[tgt_arg]], src_obj)
-
- nonnone_obj = tgt_obj
- if src_obj.is_constant() and src_obj.const is None and tgt_obj.can_be_none():
- nonnone_obj = tgt_obj.nonnoneify()
-
- add_knowntypedata(knowntypedata, False, [op.args[tgt_arg]], nonnone_obj)
-
- bind(obj2, obj1, 0)
- bind(obj1, obj2, 1)
- r.set_knowntypedata(knowntypedata)
-
- return r
-
def divmod((obj1, obj2)):
return SomeTuple([pair(obj1, obj2).div(), pair(obj1, obj2).mod()])
@@ -271,10 +244,14 @@
return SomeInteger(nonneg=int1.nonneg, knowntype=int1.knowntype)
rshift.can_only_throw = []
- def _compare_helper((int1, int2), opname, operation):
+
+def _make_cmp_annotator_int(cmp_op):
+ @cmp_op.register(SomeInteger, SomeInteger)
+ def _compare_helper(annotator, int1, int2):
r = SomeBool()
- if int1.is_immutable_constant() and int2.is_immutable_constant():
- r.const = operation(int1.const, int2.const)
+ s_int1, s_int2 = annotator.annotation(int1), annotator.annotation(int2)
+ if s_int1.is_immutable_constant() and s_int2.is_immutable_constant():
+ r.const = cmp_op.pyfunc(s_int1.const, s_int2.const)
#
# The rest of the code propagates nonneg information between
# the two arguments.
@@ -286,45 +263,38 @@
# nonneg then "assert x>=y" will let the annotator know that
# x is nonneg too, but it will not work if y is unsigned.
#
- if not (rarithmetic.signedtype(int1.knowntype) and
- rarithmetic.signedtype(int2.knowntype)):
+ if not (rarithmetic.signedtype(s_int1.knowntype) and
+ rarithmetic.signedtype(s_int2.knowntype)):
return r
knowntypedata = {}
- op = getbookkeeper()._find_current_op(opname=opname, arity=2)
- def tointtype(int0):
- if int0.knowntype is bool:
+ def tointtype(s_int0):
+ if s_int0.knowntype is bool:
return int
- return int0.knowntype
- if int1.nonneg and isinstance(op.args[1], Variable):
- case = opname in ('lt', 'le', 'eq')
-
- add_knowntypedata(knowntypedata, case, [op.args[1]],
- SomeInteger(nonneg=True, knowntype=tointtype(int2)))
- if int2.nonneg and isinstance(op.args[0], Variable):
- case = opname in ('gt', 'ge', 'eq')
- add_knowntypedata(knowntypedata, case, [op.args[0]],
- SomeInteger(nonneg=True, knowntype=tointtype(int1)))
+ return s_int0.knowntype
+ if s_int1.nonneg and isinstance(int2, Variable):
+ case = cmp_op.opname in ('lt', 'le', 'eq')
+ add_knowntypedata(knowntypedata, case, [int2],
+ SomeInteger(nonneg=True, knowntype=tointtype(s_int2)))
+ if s_int2.nonneg and isinstance(int1, Variable):
+ case = cmp_op.opname in ('gt', 'ge', 'eq')
+ add_knowntypedata(knowntypedata, case, [int1],
+ SomeInteger(nonneg=True, knowntype=tointtype(s_int1)))
r.set_knowntypedata(knowntypedata)
# a special case for 'x < 0' or 'x >= 0',
# where 0 is a flow graph Constant
# (in this case we are sure that it cannot become a r_uint later)
- if (isinstance(op.args[1], Constant) and
- type(op.args[1].value) is int and # filter out Symbolics
- op.args[1].value == 0):
- if int1.nonneg:
- if opname == 'lt':
+ if (isinstance(int2, Constant) and
+ type(int2.value) is int and # filter out Symbolics
+ int2.value == 0):
+ if s_int1.nonneg:
+ if cmp_op.opname == 'lt':
r.const = False
- if opname == 'ge':
+ if cmp_op.opname == 'ge':
r.const = True
return r
- def lt(intint): return intint._compare_helper('lt', operator.lt)
- def le(intint): return intint._compare_helper('le', operator.le)
- def eq(intint): return intint._compare_helper('eq', operator.eq)
- def ne(intint): return intint._compare_helper('ne', operator.ne)
- def gt(intint): return intint._compare_helper('gt', operator.gt)
- def ge(intint): return intint._compare_helper('ge', operator.ge)
-
+for cmp_op in [op.lt, op.le, op.eq, op.ne, op.gt, op.ge]:
+ _make_cmp_annotator_int(cmp_op)
class __extend__(pairtype(SomeBool, SomeBool)):
@@ -746,25 +716,26 @@
return SomeBuiltinMethod(bltn1.analyser, s_self,
methodname=bltn1.methodname)
+ at op.is_.register(SomePBC, SomePBC)
+def is__PBC_PBC(annotator, pbc1, pbc2):
+ s = is__default(annotator, pbc1, pbc2)
+ if not s.is_constant():
+ s_pbc1 = annotator.annotation(pbc1)
+ s_pbc2 = annotator.annotation(pbc2)
+ if not s_pbc1.can_be_None or not s_pbc2.can_be_None:
+ for desc in s_pbc1.descriptions:
+ if desc in s_pbc2.descriptions:
+ break
+ else:
+ s.const = False # no common desc in the two sets
+ return s
+
class __extend__(pairtype(SomePBC, SomePBC)):
-
def union((pbc1, pbc2)):
d = pbc1.descriptions.copy()
d.update(pbc2.descriptions)
return SomePBC(d, can_be_None = pbc1.can_be_None or pbc2.can_be_None)
- def is_((pbc1, pbc2)):
- thistype = pairtype(SomePBC, SomePBC)
- s = super(thistype, pair(pbc1, pbc2)).is_()
- if not s.is_constant():
- if not pbc1.can_be_None or not pbc2.can_be_None:
- for desc in pbc1.descriptions:
- if desc in pbc2.descriptions:
- break
- else:
- s.const = False # no common desc in the two sets
- return s
-
class __extend__(pairtype(SomeImpossibleValue, SomeObject)):
def union((imp1, obj2)):
return obj2
diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py
--- a/rpython/annotator/bookkeeper.py
+++ b/rpython/annotator/bookkeeper.py
@@ -89,14 +89,14 @@
newblocks = self.annotator.added_blocks
if newblocks is None:
newblocks = self.annotator.annotated # all of them
- binding = self.annotator.binding
+ annotation = self.annotator.annotation
for block in newblocks:
for op in block.operations:
if op.opname in ('simple_call', 'call_args'):
yield op
# some blocks are partially annotated
- if binding(op.result, None) is None:
+ if annotation(op.result) is None:
break # ignore the unannotated part
for call_op in call_sites():
@@ -144,15 +144,17 @@
def consider_call_site(self, call_op):
from rpython.rtyper.llannotation import SomeLLADTMeth, lltype_to_annotation
- binding = self.annotator.binding
- s_callable = binding(call_op.args[0])
- args_s = [binding(arg) for arg in call_op.args[1:]]
+ annotation = self.annotator.annotation
+ s_callable = annotation(call_op.args[0])
+ args_s = [annotation(arg) for arg in call_op.args[1:]]
if isinstance(s_callable, SomeLLADTMeth):
adtmeth = s_callable
s_callable = self.immutablevalue(adtmeth.func)
args_s = [lltype_to_annotation(adtmeth.ll_ptrtype)] + args_s
if isinstance(s_callable, SomePBC):
- s_result = binding(call_op.result, s_ImpossibleValue)
+ s_result = annotation(call_op.result)
+ if s_result is None:
+ s_result = s_ImpossibleValue
args = call_op.build_args(args_s)
self.consider_call_site_for_pbc(s_callable, args,
s_result, call_op)
@@ -500,8 +502,9 @@
# needed by some kinds of specialization.
fn, block, i = self.position_key
op = block.operations[i]
- s_previous_result = self.annotator.binding(op.result,
- s_ImpossibleValue)
+ s_previous_result = self.annotator.annotation(op.result)
+ if s_previous_result is None:
+ s_previous_result = s_ImpossibleValue
else:
if emulated is True:
whence = None
diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py
--- a/rpython/annotator/test/test_annrpython.py
+++ b/rpython/annotator/test/test_annrpython.py
@@ -857,7 +857,11 @@
s = a.build_types(snippet.harmonic, [int])
assert s.knowntype == float
# check that the list produced by range() is not mutated or resized
- for s_value in a.bindings.values():
+ graph = graphof(a, snippet.harmonic)
+ all_vars = set().union(*[block.getvariables() for block in graph.iterblocks()])
+ print all_vars
+ for var in all_vars:
+ s_value = var.annotation
if isinstance(s_value, annmodel.SomeList):
assert not s_value.listdef.listitem.resized
assert not s_value.listdef.listitem.mutated
@@ -2767,8 +2771,8 @@
a = self.RPythonAnnotator()
a.build_types(f, [])
v1, v2 = graphof(a, readout).getargs()
- assert not a.bindings[v1].is_constant()
- assert not a.bindings[v2].is_constant()
+ assert not a.binding(v1).is_constant()
+ assert not a.binding(v2).is_constant()
def test_prebuilt_mutables_dont_use_eq(self):
# test that __eq__ is not called during annotation, at least
diff --git a/rpython/annotator/test/test_model.py b/rpython/annotator/test/test_model.py
--- a/rpython/annotator/test/test_model.py
+++ b/rpython/annotator/test/test_model.py
@@ -20,25 +20,6 @@
class C(object):
pass
-class DummyClassDef:
- def __init__(self, cls=C):
- self.cls = cls
- self.name = cls.__name__
-
-si0 = SomeInstance(DummyClassDef(), True)
-si1 = SomeInstance(DummyClassDef())
-sTrue = SomeBool()
-sTrue.const = True
-sFalse = SomeBool()
-sFalse.const = False
-
-def test_is_None():
- assert pair(s_None, s_None).is_() == sTrue
- assert pair(si1, s_None).is_() == sFalse
- assert pair(si0, s_None).is_() != sTrue
- assert pair(si0, s_None).is_() != sFalse
- assert pair(si0, s_None).is_() == SomeBool()
-
def test_equality():
assert s1 != s2 != s3 != s4 != s5 != s6
assert s1 == SomeType()
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -19,19 +19,41 @@
UNARY_OPERATIONS = set([oper.opname for oper in op.__dict__.values()
if oper.dispatch == 1])
+UNARY_OPERATIONS.remove('contains')
+ at op.type.register(SomeObject)
+def type_SomeObject(annotator, arg):
+ r = SomeType()
+ r.is_type_of = [arg]
+ return r
+
+ at op.bool.register(SomeObject)
+def bool_SomeObject(annotator, obj):
+ r = SomeBool()
+ annotator.annotation(obj).bool_behavior(r)
+ s_nonnone_obj = annotator.annotation(obj)
+ if s_nonnone_obj.can_be_none():
+ s_nonnone_obj = s_nonnone_obj.nonnoneify()
+ knowntypedata = {}
+ add_knowntypedata(knowntypedata, True, [obj], s_nonnone_obj)
+ r.set_knowntypedata(knowntypedata)
+ return r
+
+ at op.contains.register(SomeObject)
+def contains_SomeObject(annotator, obj, element):
+ return s_Bool
+contains_SomeObject.can_only_throw = []
+
+ at op.simple_call.register(SomeObject)
+def simple_call_SomeObject(annotator, func, *args):
+ return annotator.annotation(func).call(simple_args([annotator.annotation(arg) for arg in args]))
+
+ at op.call_args.register(SomeObject)
+def call_args(annotator, func, *args):
+ return annotator.annotation(func).call(complex_args([annotator.annotation(arg) for arg in args]))
class __extend__(SomeObject):
- def type(self, *moreargs):
- if moreargs:
- raise Exception('type() called with more than one argument')
- r = SomeType()
- bk = getbookkeeper()
- op = bk._find_current_op(opname="type", arity=1, pos=0, s_type=self)
- r.is_type_of = [op.args[0]]
- return r
-
def issubtype(self, s_cls):
if hasattr(self, 'is_type_of'):
vars = self.is_type_of
@@ -53,21 +75,6 @@
if s_len.is_immutable_constant():
s.const = s_len.const > 0
- def bool(s_obj):
- r = SomeBool()
- s_obj.bool_behavior(r)
-
- bk = getbookkeeper()
- knowntypedata = {}
- op = bk._find_current_op(opname="bool", arity=1)
- arg = op.args[0]
- s_nonnone_obj = s_obj
- if s_obj.can_be_none():
- s_nonnone_obj = s_obj.nonnoneify()
- add_knowntypedata(knowntypedata, True, [arg], s_nonnone_obj)
- r.set_knowntypedata(knowntypedata)
- return r
-
def hash(self):
raise AnnotatorError("cannot use hash() in RPython")
@@ -133,19 +140,9 @@
def bind_callables_under(self, classdef, name):
return self # default unbound __get__ implementation
- def simple_call(self, *args_s):
- return self.call(simple_args(args_s))
-
- def call_args(self, *args_s):
- return self.call(complex_args(args_s))
-
def call(self, args, implicit_init=False):
raise AnnotatorError("Cannot prove that the object is callable")
- def op_contains(self, s_element):
- return s_Bool
- op_contains.can_only_throw = []
-
def hint(self, *args_s):
return self
@@ -249,6 +246,12 @@
items = self.items[s_start.const:s_stop.const]
return SomeTuple(items)
+ at op.contains.register(SomeList)
+def contains_SomeList(annotator, obj, element):
+ annotator.annotation(obj).listdef.generalize(annotator.annotation(element))
+ return s_Bool
+contains_SomeList.can_only_throw = []
+
class __extend__(SomeList):
@@ -296,11 +299,6 @@
def getanyitem(self):
return self.listdef.read_item()
- def op_contains(self, s_element):
- self.listdef.generalize(s_element)
- return s_Bool
- op_contains.can_only_throw = []
-
def hint(self, *args_s):
hints = args_s[-1].const
if 'maxlength' in hints:
@@ -340,6 +338,21 @@
raise AnnotatorError("%s: not proven to have non-negative stop" % error)
+def _can_only_throw(s_dct, *ignore):
+ if s_dct.dictdef.dictkey.custom_eq_hash:
+ return None # r_dict: can throw anything
+ return [] # else: no possible exception
+
+ at op.contains.register(SomeDict)
+def contains_SomeDict(annotator, dct, element):
+ annotator.annotation(dct).dictdef.generalize_key(annotator.annotation(element))
+ if annotator.annotation(dct)._is_empty():
+ s_bool = SomeBool()
+ s_bool.const = False
+ return s_bool
+ return s_Bool
+contains_SomeDict.can_only_throw = _can_only_throw
+
class __extend__(SomeDict):
def _is_empty(self):
@@ -421,19 +434,19 @@
self.dictdef.generalize_value(s_dfl)
return self.dictdef.read_value()
- def _can_only_throw(self, *ignore):
- if self.dictdef.dictkey.custom_eq_hash:
- return None # r_dict: can throw anything
- return [] # else: no possible exception
-
- def op_contains(self, s_element):
- self.dictdef.generalize_key(s_element)
- if self._is_empty():
- s_bool = SomeBool()
- s_bool.const = False
- return s_bool
- return s_Bool
- op_contains.can_only_throw = _can_only_throw
+ at op.contains.register(SomeString)
+ at op.contains.register(SomeUnicodeString)
+def contains_String(annotator, string, char):
+ if annotator.annotation(char).is_constant() and annotator.annotation(char).const == "\0":
+ r = SomeBool()
+ knowntypedata = {}
+ add_knowntypedata(knowntypedata, False, [string],
+ annotator.annotation(string).nonnulify())
+ r.set_knowntypedata(knowntypedata)
+ return r
+ else:
+ return contains_SomeObject(annotator, string, char)
+contains_String.can_only_throw = []
class __extend__(SomeString,
@@ -508,19 +521,6 @@
result = self.basestringclass(no_nul=self.no_nul)
return result
- def op_contains(self, s_element):
- if s_element.is_constant() and s_element.const == "\0":
- r = SomeBool()
- bk = getbookkeeper()
- op = bk._find_current_op(opname="contains", arity=2, pos=0, s_type=self)
- knowntypedata = {}
- add_knowntypedata(knowntypedata, False, [op.args[0]], self.nonnulify())
- r.set_knowntypedata(knowntypedata)
- return r
- else:
- return SomeObject.op_contains(self, s_element)
- op_contains.can_only_throw = []
-
def method_format(self, *args):
raise AnnotatorError("Method format() is not RPython")
@@ -709,9 +709,6 @@
return self._emulate_call('__setslice__', s_start, s_stop, s_iterable)
class __extend__(SomeBuiltin):
- def simple_call(self, *args):
- return self.analyser(*args)
-
def call(self, args, implicit_init=False):
args_s, kwds = args.unpack()
# prefix keyword arguments with 's_'
diff --git a/rpython/flowspace/model.py b/rpython/flowspace/model.py
--- a/rpython/flowspace/model.py
+++ b/rpython/flowspace/model.py
@@ -10,29 +10,6 @@
from rpython.tool.sourcetools import PY_IDENTIFIER, nice_repr_for_func
-"""
- memory size before and after introduction of __slots__
- using targetpypymain with -no-c
-
- slottified annotation ann+genc
- -------------------------------------------
- nothing 321 MB 442 MB
- Var/Const/SpaceOp 205 MB 325 MB
- + Link 189 MB 311 MB
- + Block 185 MB 304 MB
-
- Dropping Variable.instances and using
- just an instancenames dict brought
- annotation down to 160 MB.
- Computing the Variable.renamed attribute
- and dropping Variable.instancenames
- got annotation down to 109 MB.
- Probably an effect of less fragmentation.
-"""
-
-__metaclass__ = type
-
-
class FunctionGraph(object):
def __init__(self, name, startblock, return_var=None):
self.name = name # function name (possibly mangled already)
@@ -273,7 +250,7 @@
class Variable(object):
- __slots__ = ["_name", "_nr", "concretetype"]
+ __slots__ = ["_name", "_nr", "annotation", "concretetype"]
dummyname = 'v'
namesdict = {dummyname: (dummyname, 0)}
@@ -296,6 +273,7 @@
def __init__(self, name=None):
self._name = self.dummyname
self._nr = -1
+ self.annotation = None
# numbers are bound lazily, when the name is requested
if name is not None:
self.rename(name)
@@ -334,6 +312,15 @@
def foldable(self):
return False
+ def copy(self):
+ """Make a copy of the Variable, preserving annotations and concretetype."""
+ newvar = Variable(self)
+ newvar.annotation = self.annotation
+ if hasattr(self, 'concretetype'):
+ newvar.concretetype = self.concretetype
+ return newvar
+
+
class Constant(Hashable):
__slots__ = ["concretetype"]
diff --git a/rpython/flowspace/operation.py b/rpython/flowspace/operation.py
--- a/rpython/flowspace/operation.py
+++ b/rpython/flowspace/operation.py
@@ -7,13 +7,14 @@
import operator
import sys
import types
-from rpython.tool.pairtype import pair
+from rpython.tool.pairtype import pair, DoubleDispatchRegistry
from rpython.rlib.unroll import unrolling_iterable, _unroller
from rpython.tool.sourcetools import compile2
from rpython.flowspace.model import (Constant, WrapException, const, Variable,
SpaceOperation)
from rpython.flowspace.specialcase import register_flow_sc
-from rpython.annotator.model import SomeTuple
+from rpython.annotator.model import (
+ SomeTuple, AnnotatorError, read_can_only_throw)
from rpython.annotator.argument import ArgumentsForTranslation
from rpython.flowspace.specialcase import SPECIAL_CASES
@@ -54,6 +55,11 @@
type.__init__(cls, name, bases, attrdict)
if hasattr(cls, 'opname'):
setattr(op, cls.opname, cls)
+ if cls.dispatch == 1:
+ cls._registry = {}
+ elif cls.dispatch == 2:
+ cls._registry = DoubleDispatchRegistry()
+
class HLOperation(SpaceOperation):
__metaclass__ = HLOperationMeta
@@ -90,11 +96,13 @@
def constfold(self):
return None
- def consider(self, annotator, *argcells):
- consider_meth = getattr(annotator, 'consider_op_' + self.opname, None)
- if not consider_meth:
- raise Exception("unknown op: %r" % op)
- return consider_meth(*argcells)
+ def consider(self, annotator, *args):
+ args_s = [annotator.annotation(arg) for arg in args]
+ spec = type(self).get_specialization(*args_s)
+ return spec(annotator, *args)
+
+ def get_can_only_throw(self, annotator):
+ return None
class PureOperation(HLOperation):
pure = True
@@ -141,16 +149,72 @@
class SingleDispatchMixin(object):
dispatch = 1
- def consider(self, annotator, arg, *other_args):
- impl = getattr(arg, self.opname)
- return impl(*other_args)
+ @classmethod
+ def register(cls, Some_cls):
+ def decorator(func):
+ cls._registry[Some_cls] = func
+ return func
+ return decorator
+
+ @classmethod
+ def _dispatch(cls, Some_cls):
+ for c in Some_cls.__mro__:
+ try:
+ return cls._registry[c]
+ except KeyError:
+ pass
+ raise AnnotatorError("Unknown operation")
+
+ def get_can_only_throw(self, annotator):
+ args_s = [annotator.annotation(v) for v in self.args]
+ spec = type(self).get_specialization(*args_s)
+ return read_can_only_throw(spec, args_s[0])
+
+ @classmethod
+ def get_specialization(cls, s_arg, *_ignored):
+ try:
+ impl = getattr(s_arg, cls.opname)
+
+ def specialized(annotator, arg, *other_args):
+ return impl(*[annotator.annotation(x) for x in other_args])
+ try:
+ specialized.can_only_throw = impl.can_only_throw
+ except AttributeError:
+ pass
+ return specialized
+ except AttributeError:
+ return cls._dispatch(type(s_arg))
+
class DoubleDispatchMixin(object):
dispatch = 2
- def consider(self, annotator, arg1, arg2, *other_args):
- impl = getattr(pair(arg1, arg2), self.opname)
- return impl(*other_args)
+ @classmethod
+ def register(cls, Some1, Some2):
+ def decorator(func):
+ cls._registry[Some1, Some2] = func
+ return func
+ return decorator
+
+ @classmethod
+ def get_specialization(cls, s_arg1, s_arg2, *_ignored):
+ try:
+ impl = getattr(pair(s_arg1, s_arg2), cls.opname)
+
+ def specialized(annotator, arg1, arg2, *other_args):
+ return impl(*[annotator.annotation(x) for x in other_args])
+ try:
+ specialized.can_only_throw = impl.can_only_throw
+ except AttributeError:
+ pass
+ return specialized
+ except AttributeError:
+ return cls._registry[type(s_arg1), type(s_arg2)]
+
+ def get_can_only_throw(self, annotator):
+ args_s = [annotator.annotation(v) for v in self.args]
+ spec = type(self).get_specialization(*args_s)
+ return read_can_only_throw(spec, args_s[0], args_s[1])
def add_operator(name, arity, dispatch=None, pyfunc=None, pure=False, ovf=False):
@@ -368,14 +432,15 @@
add_operator('newslice', 3)
add_operator('hint', None, dispatch=1)
-class Contains(PureOperation):
+class Contains(SingleDispatchMixin, PureOperation):
opname = 'contains'
arity = 2
pyfunc = staticmethod(operator.contains)
- # XXX "contains" clash with SomeObject method
- def consider(self, annotator, seq, elem):
- return seq.op_contains(elem)
+ # XXX "contains" clashes with SomeObject method
+ @classmethod
+ def get_specialization(cls, s_seq, s_elem):
+ return cls._dispatch(type(s_seq))
class NewDict(HLOperation):
@@ -392,7 +457,7 @@
canraise = []
def consider(self, annotator, *args):
- return SomeTuple(items=args)
+ return SomeTuple(items=[annotator.annotation(arg) for arg in args])
class NewList(HLOperation):
@@ -400,7 +465,7 @@
canraise = []
def consider(self, annotator, *args):
- return annotator.bookkeeper.newlist(*args)
+ return annotator.bookkeeper.newlist(*[annotator.annotation(arg) for arg in args])
class Pow(PureOperation):
diff --git a/rpython/memory/gctransform/asmgcroot.py b/rpython/memory/gctransform/asmgcroot.py
--- a/rpython/memory/gctransform/asmgcroot.py
+++ b/rpython/memory/gctransform/asmgcroot.py
@@ -10,7 +10,7 @@
BaseFrameworkGCTransformer, BaseRootWalker)
from rpython.rtyper.llannotation import SomeAddress
from rpython.rtyper.rbuiltin import gen_cast
-from rpython.translator.unsimplify import copyvar, varoftype
+from rpython.translator.unsimplify import varoftype
from rpython.translator.tool.cbuild import ExternalCompilationInfo
import sys
@@ -140,7 +140,7 @@
block1 = Block([])
reloadedvars = []
for v, c_p in zip(block2.inputargs, sra):
- v = copyvar(None, v)
+ v = v.copy()
if isinstance(v.concretetype, lltype.Ptr):
w = varoftype(llmemory.Address)
else:
diff --git a/rpython/rlib/test/test_signature.py b/rpython/rlib/test/test_signature.py
--- a/rpython/rlib/test/test_signature.py
+++ b/rpython/rlib/test/test_signature.py
@@ -19,7 +19,7 @@
def sigof(a, f):
# returns [param1, param2, ..., ret]
g = graphof(a.translator, f)
- return [a.bindings[v] for v in g.startblock.inputargs] + [a.bindings[g.getreturnvar()]]
+ return [a.binding(v) for v in g.startblock.inputargs] + [a.binding(g.getreturnvar())]
def getsig(f, policy=None):
a = annotate_at(f, policy=policy)
diff --git a/rpython/rtyper/callparse.py b/rpython/rtyper/callparse.py
--- a/rpython/rtyper/callparse.py
+++ b/rpython/rtyper/callparse.py
@@ -19,7 +19,7 @@
def getrresult(rtyper, graph):
"""Return the repr of the result variable of the 'graph'."""
- if graph.getreturnvar() in rtyper.annotator.bindings:
+ if graph.getreturnvar().annotation is not None:
return rtyper.bindingrepr(graph.getreturnvar())
else:
return lltype.Void
diff --git a/rpython/rtyper/rtyper.py b/rpython/rtyper/rtyper.py
--- a/rpython/rtyper/rtyper.py
+++ b/rpython/rtyper/rtyper.py
@@ -17,7 +17,6 @@
from rpython.annotator import model as annmodel, unaryop, binaryop
from rpython.rtyper.llannotation import SomePtr, lltype_to_annotation
-from rpython.annotator.annrpython import FAIL
from rpython.flowspace.model import Variable, Constant, SpaceOperation, c_last_exception
from rpython.rtyper.annlowlevel import annotate_lowlevel_helper, LowLevelAnnotatorPolicy
from rpython.rtyper.error import TyperError
@@ -152,8 +151,12 @@
assert result is not None # recursive getrepr()!
return result
- def binding(self, var, default=FAIL):
- s_obj = self.annotator.binding(var, default)
+ def annotation(self, var):
+ s_obj = self.annotator.annotation(var)
+ return s_obj
+
+ def binding(self, var):
+ s_obj = self.annotator.binding(var)
return s_obj
def bindingrepr(self, var):
@@ -493,7 +496,7 @@
hop.r_result, op.opname, resulttype))
# figure out if the resultvar is a completely fresh Variable or not
if (isinstance(resultvar, Variable) and
- resultvar not in self.annotator.bindings and
+ resultvar.annotation is None and
resultvar not in varmapping):
# fresh Variable: rename it to the previously existing op.result
varmapping[resultvar] = op.result
@@ -635,7 +638,7 @@
ARG_GCSTRUCT = GCSTRUCT
args_s = [SomePtr(Ptr(ARG_GCSTRUCT))]
graph = self.annotate_helper(func, args_s)
- s = self.annotator.binding(graph.getreturnvar())
+ s = self.annotation(graph.getreturnvar())
if (not isinstance(s, SomePtr) or
s.ll_ptrtype != Ptr(RuntimeTypeInfo)):
raise TyperError("runtime type info function %r returns %r, "
@@ -882,7 +885,9 @@
newargs_v = []
for v in args_v:
if v.concretetype is Void:
- s_value = rtyper.binding(v, default=annmodel.s_None)
+ s_value = rtyper.annotation(v)
+ if s_value is None:
+ s_value = annmodel.s_None
if not s_value.is_constant():
raise TyperError("non-constant variable of type Void")
if not isinstance(s_value, (annmodel.SomePBC, annmodel.SomeNone)):
diff --git a/rpython/tool/error.py b/rpython/tool/error.py
--- a/rpython/tool/error.py
+++ b/rpython/tool/error.py
@@ -106,7 +106,7 @@
def format_simple_call(annotator, oper, msg):
msg.append("Occurred processing the following simple_call:")
try:
- descs = annotator.bindings[oper.args[0]].descriptions
+ descs = annotator.binding(oper.args[0]).descriptions
except (KeyError, AttributeError), e:
msg.append(" (%s getting at the binding!)" % (
e.__class__.__name__,))
diff --git a/rpython/tool/pairtype.py b/rpython/tool/pairtype.py
--- a/rpython/tool/pairtype.py
+++ b/rpython/tool/pairtype.py
@@ -61,3 +61,36 @@
bases = tuple(bases1 + bases2) or (tuple,) # 'tuple': ultimate base
pair = pairtypecache[cls1, cls2] = extendabletype(name, bases, {})
return pair
+
+def pairmro(cls1, cls2):
+ """
+ Return the resolution order on pairs of types for double dispatch.
+
+ This order is compatible with the mro of pairtype(cls1, cls2).
+ """
+ for base2 in cls2.__mro__:
+ for base1 in cls1.__mro__:
+ yield (base1, base2)
+
+class DoubleDispatchRegistry(object):
+ """
+ A mapping of pairs of types to arbitrary objects respecting inheritance
+ """
+ def __init__(self):
+ self._registry = {}
+ self._cache = {}
+
+ def __getitem__(self, clspair):
+ try:
+ return self._cache[clspair]
+ except KeyError:
+ cls1, cls2 = clspair
+ for c1, c2 in pairmro(cls1, cls2):
+ if (c1, c2) in self._cache:
+ return self._cache[(c1, c2)]
+ else:
+ raise
+
+ def __setitem__(self, clspair, value):
+ self._registry[clspair] = value
+ self._cache = self._registry.copy()
diff --git a/rpython/tool/test/test_pairtype.py b/rpython/tool/test/test_pairtype.py
--- a/rpython/tool/test/test_pairtype.py
+++ b/rpython/tool/test/test_pairtype.py
@@ -1,7 +1,7 @@
+from rpython.tool.pairtype import (
+ pairtype, pair, extendabletype, pairmro, DoubleDispatchRegistry)
-from rpython.tool.pairtype import pairtype, pair, extendabletype
-
-def test_binop():
+def test_binop():
### Binary operation example
class __extend__(pairtype(int, int)):
def add((x, y)):
@@ -13,16 +13,16 @@
def add((x, y)):
return 'bool: %s+%s' % (x, y)
- assert pair(3,4).add() == 'integer: 3+4'
- assert pair(3,4).sub() == 'integer: 3-4'
- assert pair(3,True).add() == 'integer: 3+True'
- assert pair(3,True).sub() == 'integer: 3-True'
- assert pair(False,4).add() == 'integer: False+4'
- assert pair(False,4).sub() == 'integer: False-4'
- assert pair(False,True).add() == 'bool: False+True'
- assert pair(False,True).sub() == 'integer: False-True'
+ assert pair(3, 4).add() == 'integer: 3+4'
+ assert pair(3, 4).sub() == 'integer: 3-4'
+ assert pair(3, True).add() == 'integer: 3+True'
+ assert pair(3, True).sub() == 'integer: 3-True'
+ assert pair(False, 4).add() == 'integer: False+4'
+ assert pair(False, 4).sub() == 'integer: False-4'
+ assert pair(False, True).add() == 'bool: False+True'
+ assert pair(False, True).sub() == 'integer: False-True'
-def test_somebuiltin():
+def test_somebuiltin():
### Operation on built-in types
class MiniPickler:
def __init__(self):
@@ -48,7 +48,7 @@
pair(p, [1, 2, ['hello', 3]]).write()
assert p.data == ['I1', 'I2', 'Shello', 'I3', 'L2', 'L3']
-def test_some_multimethod():
+def test_some_multimethod():
### Another multimethod example
class Block:
def __init__(self, exit):
@@ -57,7 +57,7 @@
pass
class Switch:
pass
-
+
class C_Generator:
def __init__(self):
self.lines = []
@@ -78,7 +78,7 @@
g = C_Generator()
pair(g, Block(Switch())).emit(['v1', 'v2'])
- assert g.lines == ["C code for block", "switch (v5) { ... }"]
+ assert g.lines == ["C code for block", "switch (v5) { ... }"]
class Lisp_Generator:
def __init__(self):
@@ -95,16 +95,37 @@
def test_multiple_extend():
class A:
__metaclass__ = extendabletype
+
class B:
__metaclass__ = extendabletype
- class __extend__(A,B):
-
+ class __extend__(A, B):
def f(self):
pass
assert hasattr(A, 'f')
assert hasattr(B, 'f')
-
-
+def test_pairmro():
+ class A(object): pass
+ class A2(A): pass
+ class A3(A2): pass
+ class B(object): pass
+ class B2(B): pass
+ parent_pairtypes = pairtype(A3, B2).__mro__[:-2]
+ assert (tuple(pairtype(a, b) for a, b in pairmro(A3, B2)) == parent_pairtypes)
+
+def test_doubledispatch():
+ class A(object): pass
+ class A2(A): pass
+ class A3(A2): pass
+ class B(object): pass
+ class B2(B): pass
+ reg = DoubleDispatchRegistry()
+ reg[object, object] = "default"
+ assert reg[A3, B2] == "default"
+ reg[A2, B2] = "A2-B2"
+ assert reg[A, B2] == "default"
+ assert reg[A3, B2] == "A2-B2"
+ reg[A3, B] = "A3-B"
+ assert reg[A3, B2] == "A2-B2" # note that A2,B2 wins over A3,B
diff --git a/rpython/translator/backendopt/inline.py b/rpython/translator/backendopt/inline.py
--- a/rpython/translator/backendopt/inline.py
+++ b/rpython/translator/backendopt/inline.py
@@ -8,7 +8,7 @@
from rpython.translator.backendopt.canraise import RaiseAnalyzer
from rpython.translator.backendopt.support import log, find_loop_blocks
from rpython.translator.simplify import join_blocks, cleanup_graph, get_graph
-from rpython.translator.unsimplify import copyvar, split_block
+from rpython.translator.unsimplify import split_block
class CannotInline(Exception):
@@ -236,14 +236,13 @@
if isinstance(var, Constant):
return var
if var not in self.varmap:
- self.varmap[var] = copyvar(None, var)
+ self.varmap[var] = var.copy()
return self.varmap[var]
def passon_vars(self, cache_key):
if cache_key in self._passon_vars:
return self._passon_vars[cache_key]
- result = [copyvar(None, var)
- for var in self.original_passon_vars]
+ result = [var.copy() for var in self.original_passon_vars]
self._passon_vars[cache_key] = result
return result
@@ -362,8 +361,8 @@
exc_match.concretetype = typeOf(exc_match.value)
blocks = []
for i, link in enumerate(afterblock.exits[1:]):
- etype = copyvar(None, copiedexceptblock.inputargs[0])
- evalue = copyvar(None, copiedexceptblock.inputargs[1])
+ etype = copiedexceptblock.inputargs[0].copy()
+ evalue = copiedexceptblock.inputargs[1].copy()
passon_vars = self.passon_vars(i)
block = Block([etype, evalue] + passon_vars)
res = Variable()
diff --git a/rpython/translator/backendopt/ssa.py b/rpython/translator/backendopt/ssa.py
--- a/rpython/translator/backendopt/ssa.py
+++ b/rpython/translator/backendopt/ssa.py
@@ -158,8 +158,6 @@
'graph_or_blocks' can be a graph, or just a dict that lists some blocks
from a graph, as follows: {block: reachable-from-outside-flag}.
"""
- from rpython.translator.unsimplify import copyvar
-
entrymap = mkinsideentrymap(graph_or_blocks)
builder = DataFlowFamilyBuilder(graph_or_blocks)
variable_families = builder.get_variable_families()
@@ -203,7 +201,7 @@
except KeyError:
raise Exception("SSA_to_SSI failed: no way to give a value to"
" %r in %r" % (v, block))
- w = copyvar(annotator, v)
+ w = v.copy()
variable_families.union(v, w)
block.renamevariables({v: w})
block.inputargs.append(w)
diff --git a/rpython/translator/exceptiontransform.py b/rpython/translator/exceptiontransform.py
--- a/rpython/translator/exceptiontransform.py
+++ b/rpython/translator/exceptiontransform.py
@@ -1,5 +1,5 @@
from rpython.translator.simplify import join_blocks, cleanup_graph
-from rpython.translator.unsimplify import copyvar, varoftype
+from rpython.translator.unsimplify import varoftype
from rpython.translator.unsimplify import insert_empty_block, split_block
from rpython.translator.backendopt import canraise, inline
from rpython.flowspace.model import Block, Constant, Variable, Link, \
@@ -305,8 +305,7 @@
reraise = self.comes_from_last_exception(entrymap, link)
result = Variable()
result.concretetype = lltype.Void
- block = Block([copyvar(None, v)
- for v in graph.exceptblock.inputargs])
+ block = Block([v.copy() for v in graph.exceptblock.inputargs])
if reraise:
block.operations = [
SpaceOperation("direct_call",
@@ -345,7 +344,7 @@
inlined, the correct exception matching blocks are produced."""
# XXX slightly annoying: construct a graph by hand
# but better than the alternative
- result = copyvar(None, op.result)
+ result = op.result.copy()
opargs = []
inputargs = []
callargs = []
@@ -435,7 +434,7 @@
result_i = l0.args.index(v_result)
v_result_after = normalafterblock.inputargs[result_i]
else:
- v_result_after = copyvar(None, v_result)
+ v_result_after = v_result.copy()
l0.args.append(v_result)
normalafterblock.inputargs.append(v_result_after)
if true_zero:
diff --git a/rpython/translator/goal/query.py b/rpython/translator/goal/query.py
--- a/rpython/translator/goal/query.py
+++ b/rpython/translator/goal/query.py
@@ -33,7 +33,7 @@
try:
for block in g.iterblocks():
for v in block.getvariables():
- s = annotator.binding(v, None)
+ s = annotator.annotation(v)
if s and s.__class__ == annmodel.SomeObject and s.knowntype != type:
raise Found
except Found:
@@ -44,8 +44,8 @@
annotator = translator.annotator
for graph in translator.graphs:
et, ev = graph.exceptblock.inputargs
- s_et = annotator.binding(et, None)
- s_ev = annotator.binding(ev, None)
+ s_et = annotator.annotation(et)
+ s_ev = annotator.annotation(ev)
if s_et:
if s_et.knowntype == type:
if s_et.__class__ == annmodel.SomeType:
diff --git a/rpython/translator/tool/graphpage.py b/rpython/translator/tool/graphpage.py
--- a/rpython/translator/tool/graphpage.py
+++ b/rpython/translator/tool/graphpage.py
@@ -106,12 +106,6 @@
self.source = make_dot_graphs(name, gs, target=None)
# make the dictionary of links -- one per annotated variable
self.current_value = {}
- if self.annotator:
- for var, s_value in self.annotator.bindings.items():
- info = '%s: %s' % (var.name, s_value)
- annotationcolor = getattr(s_value, 'annotationcolor', None)
- self.links[var.name] = info, annotationcolor
- self.current_value[var.name] = s_value
#from rpython.jit.hintannotator.annotator import HintAnnotator
#if isinstance(self.annotator, HintAnnotator):
@@ -128,6 +122,12 @@
for v in link.getextravars():
vars[v] = True
for var in vars:
+ s_value = var.annotation
+ if s_value is not None:
+ info = '%s: %s' % (var.name, s_value)
+ annotationcolor = getattr(s_value, 'annotationcolor', None)
+ self.links[var.name] = info, annotationcolor
+ self.current_value[var.name] = s_value
if hasattr(var, 'concretetype'):
#info = self.links.get(var.name, var.name)
#info = '(%s) %s' % (var.concretetype, info)
diff --git a/rpython/translator/transform.py b/rpython/translator/transform.py
--- a/rpython/translator/transform.py
+++ b/rpython/translator/transform.py
@@ -89,8 +89,8 @@
for i in range(len(block.operations)):
op = block.operations[i]
if op.opname == 'mul':
- s0 = self.binding(op.args[0], None)
- s1 = self.binding(op.args[1], None)
+ s0 = self.annotation(op.args[0])
+ s1 = self.annotation(op.args[1])
if (isinstance(s0, annmodel.SomeChar) and
isinstance(s1, annmodel.SomeInteger)):
mul_sources[op.result] = op.args[0], op.args[1]
@@ -124,14 +124,14 @@
elif op.opname == 'contains' and op.args[0] in newlist_sources:
items = {}
for v in newlist_sources[op.args[0]]:
- s = self.binding(v)
+ s = self.annotation(v)
if not s.is_immutable_constant():
break
items[s.const] = None
else:
# all arguments of the newlist are annotation constants
op.args[0] = Constant(items)
- s_dict = self.binding(op.args[0])
+ s_dict = self.annotation(op.args[0])
s_dict.dictdef.generalize_key(self.binding(op.args[1]))
@@ -168,9 +168,9 @@
"Fix a block whose end can never be reached at run-time."
# search the operation that cannot succeed
can_succeed = [op for op in block.operations
- if op.result in self.bindings]
+ if op.result.annotation is not None]
cannot_succeed = [op for op in block.operations
- if op.result not in self.bindings]
+ if op.result.annotation is None]
n = len(can_succeed)
# check consistency
assert can_succeed == block.operations[:n]
@@ -178,8 +178,7 @@
assert 0 <= n < len(block.operations)
# chop off the unreachable end of the block
del block.operations[n+1:]
- s_impossible = annmodel.SomeImpossibleValue()
- self.bindings[block.operations[n].result] = s_impossible
+ self.setbinding(block.operations[n].result, annmodel.s_ImpossibleValue)
# insert the equivalent of 'raise AssertionError'
graph = self.annotated[block]
msg = "Call to %r should have raised an exception" % (getattr(graph, 'func', None),)
diff --git a/rpython/translator/unsimplify.py b/rpython/translator/unsimplify.py
--- a/rpython/translator/unsimplify.py
+++ b/rpython/translator/unsimplify.py
@@ -2,16 +2,6 @@
SpaceOperation, c_last_exception, checkgraph)
-def copyvar(annotator, v):
- """Make a copy of the Variable v, preserving annotations and concretetype."""
- assert isinstance(v, Variable)
- newvar = Variable(v)
- if annotator is not None and v in annotator.bindings:
- annotator.transfer_binding(newvar, v)
- if hasattr(v, 'concretetype'):
- newvar.concretetype = v.concretetype
- return newvar
-
def varoftype(concretetype, name=None):
var = Variable(name)
var.concretetype = concretetype
@@ -31,7 +21,7 @@
vars = [v for v, keep in vars.items() if keep]
mapping = {}
for v in vars:
- mapping[v] = copyvar(annotator, v)
+ mapping[v] = v.copy()
newblock = Block(vars)
newblock.operations.extend(newops)
newblock.closeblock(Link(link.args, link.target))
@@ -41,7 +31,7 @@
return newblock
def insert_empty_startblock(annotator, graph):
- vars = [copyvar(annotator, v) for v in graph.startblock.inputargs]
+ vars = [v.copy() for v in graph.startblock.inputargs]
newblock = Block(vars)
newblock.closeblock(Link(vars, graph.startblock))
graph.startblock = newblock
@@ -72,7 +62,7 @@
if var in vars_produced_in_new_block:
return var
if var not in varmap:
- varmap[var] = copyvar(annotator, var)
+ varmap[var] = var.copy()
return varmap[var]
moved_operations = block.operations[index:]
new_moved_ops = []
@@ -146,7 +136,7 @@
annhelper.finish()
entry_point = translator.entry_point_graph
- args = [copyvar(translator.annotator, v) for v in entry_point.getargs()]
+ args = [v.copy() for v in entry_point.getargs()]
extrablock = Block(args)
v_none = varoftype(lltype.Void)
newop = SpaceOperation('direct_call', [c_initial_func], v_none)
@@ -169,7 +159,7 @@
annhelper.finish()
entry_point = translator.entry_point_graph
- v = copyvar(translator.annotator, entry_point.getreturnvar())
+ v = entry_point.getreturnvar().copy()
extrablock = Block([v])
v_none = varoftype(lltype.Void)
newop = SpaceOperation('direct_call', [c_final_func], v_none)
More information about the pypy-commit
mailing list