[pypy-commit] pypy ufuncapi: merge default into branch

mattip noreply at buildbot.pypy.org
Fri Oct 17 15:57:51 CEST 2014


Author: mattip <matti.picus at gmail.com>
Branch: ufuncapi
Changeset: r73995:f5247fd471a9
Date: 2014-10-16 08:59 -0500
http://bitbucket.org/pypy/pypy/changeset/f5247fd471a9/

Log:	merge default into branch

diff too long, truncating to 2000 out of 6186 lines

diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -3,8 +3,8 @@
 
 Except when otherwise stated (look for LICENSE files in directories or
 information at the beginning of each file) all software and documentation in
-the 'rpython', 'pypy', 'ctype_configure', 'dotviewer', 'demo', and 'lib_pypy'
-directories is licensed as follows: 
+the 'rpython', 'pypy', 'ctype_configure', 'dotviewer', 'demo', 'lib_pypy',
+'py', and '_pytest' directories is licensed as follows: 
 
     The MIT License
 
diff --git a/pypy/doc/release-pypy3-2.4.0.rst b/pypy/doc/release-pypy3-2.4.0.rst
new file mode 100644
--- /dev/null
+++ b/pypy/doc/release-pypy3-2.4.0.rst
@@ -0,0 +1,126 @@
+=================================================
+PyPy3 2.4 - Snow White
+=================================================
+
+We're pleased to announce PyPy3 2.4, which contains significant performance
+enhancements and bug fixes.
+
+You can download the PyPy3 2.4.0 release here:
+
+    http://pypy.org/download.html
+
+We would like to thank our donors for the continued support of the PyPy
+project, and for those who donate to our three sub-projects.
+We've shown quite a bit of progress, but we're slowly running out of funds.
+Please consider donating more, or even better convince your employer to donate,
+so we can finish those projects! The three sub-projects are:
+
+* `Py3k`_ (supporting Python 3.x): This is a Python 3.2.5 compatible
+   version we call PyPy3 2.4, and we are working toward a Python 3.3
+   compatible version
+
+* `STM`_ (software transactional memory): We have released a first working version,
+  and continue to try out new promising paths of achieving a fast multithreaded Python
+
+* `NumPy`_ which requires installation of our fork of upstream numpy,
+  available `on bitbucket`_
+
+.. _`Py3k`: http://pypy.org/py3donate.html
+.. _`STM`: http://pypy.org/tmdonate2.html
+.. _`NumPy`: http://pypy.org/numpydonate.html
+.. _`on bitbucket`: https://www.bitbucket.org/pypy/numpy
+
+What is PyPy?
+=============
+
+PyPy is a very compliant Python interpreter, almost a drop-in replacement for
+CPython 2.7 or 3.2.5. It's fast (`pypy 2.4 and cpython 2.7.x`_ performance
+comparison) due to its integrated tracing JIT compiler.
+
+This release supports **x86** machines on most common operating systems
+(Linux 32/64, Mac OS X 64, Windows, and OpenBSD),
+as well as newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux.
+
+While we support 32 bit python on Windows, work on the native Windows 64
+bit python is still stalling, we would welcome a volunteer
+to `handle that`_.
+
+.. _`pypy 2.4 and cpython 2.7.x`: http://speed.pypy.org
+.. _`handle that`: http://doc.pypy.org/en/latest/windows.html#what-is-missing-for-a-full-64-bit-translation
+
+PyPy3 Highlights
+================
+
+Issues reported with our previous release were fixed after reports from users on
+our new issue tracker at https://bitbucket.org/pypy/pypy/issues or on IRC at
+#pypy. Here is a summary of the user-facing PyPy3 specific changes:
+
+* Better Windows compatibility, e.g. the nt module functions _getfinalpathname
+  & _getfileinformation are now supported (the former is required for the
+  popular pathlib library for example)
+
+* Various fsencode PEP 383 related fixes to the posix module (readlink, uname,
+  ttyname and ctermid) and improved locale handling
+
+* Switched default binary name os POSIX distributions to 'pypy3' (which
+  symlinks to to 'pypy3.2')
+
+* Fixed a couple different crashes related to parsing Python 3 source code
+
+Further Highlights (shared w/ PyPy2)
+====================================
+
+Benchmarks improved after internal enhancements in string and
+bytearray handling, and a major rewrite of the GIL handling. This means
+that external calls are now a lot faster, especially the CFFI ones. It also
+means better performance in a lot of corner cases with handling strings or
+bytearrays. The main bugfix is handling of many socket objects in your
+program which in the long run used to "leak" memory.
+
+We fixed a memory leak in IO in the sandbox_ code
+
+We welcomed more than 12 new contributors, and conducted two Google
+Summer of Code projects, as well as other student projects not
+directly related to Summer of Code.
+
+* Reduced internal copying of bytearray operations
+
+* Tweak the internal structure of StringBuilder to speed up large string
+  handling, which becomes advantageous on large programs at the cost of slightly
+  slower small *benchmark* type programs.
+
+* Boost performance of thread-local variables in both unjitted and jitted code,
+  this mostly affects errno handling on linux, which makes external calls
+  faster.
+
+* Move to a mixed polling and mutex GIL model that make mutlithreaded jitted
+  code run *much* faster
+
+* Optimize errno handling in linux (x86 and x86-64 only)
+
+* Remove ctypes pythonapi and ctypes.PyDLL, which never worked on PyPy
+
+* Classes in the ast module are now distinct from structures used by
+  the compiler, which simplifies and speeds up translation of our
+  source code to the PyPy binary interpreter
+
+* Win32 now links statically to zlib, expat, bzip, and openssl-1.0.1i.
+  No more missing DLLs
+
+* Many issues were resolved_ since the 2.3.1 release in June
+
+.. _`whats-new`: http://doc.pypy.org/en/latest/whatsnew-2.4.0.html
+.. _resolved: https://bitbucket.org/pypy/pypy/issues?status=resolved
+.. _sandbox: http://doc.pypy.org/en/latest/sandbox.html
+
+We have further improvements on the way: rpython file handling,
+numpy linalg compatibility, as well
+as improved GC and many smaller improvements.
+
+Please try it out and let us know what you think. We especially welcome
+success stories, we know you are using PyPy, please tell us about it!
+
+Cheers
+
+The PyPy Team
+
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,10 @@
 
 .. 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.
+
+.. branch: ClassRepr
+Refactor ClassRepr and make normalizecalls independent of the rtyper.
diff --git a/pypy/doc/whatsnew-pypy3-2.4.0.rst b/pypy/doc/whatsnew-pypy3-2.4.0.rst
new file mode 100644
--- /dev/null
+++ b/pypy/doc/whatsnew-pypy3-2.4.0.rst
@@ -0,0 +1,6 @@
+=========================
+What's new in PyPy3 2.4.0
+=========================
+
+.. this is a revision shortly after pypy3-release-2.4.x
+.. startrev: 12b940544622
diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py
--- a/pypy/goal/targetpypystandalone.py
+++ b/pypy/goal/targetpypystandalone.py
@@ -251,6 +251,7 @@
             enable_translationmodules(config)
 
         config.translation.suggest(check_str_without_nul=True)
+        config.translation.suggest(shared=True)
 
         if config.translation.thread:
             config.objspace.usemodules.thread = True
diff --git a/pypy/interpreter/astcompiler/ast.py b/pypy/interpreter/astcompiler/ast.py
--- a/pypy/interpreter/astcompiler/ast.py
+++ b/pypy/interpreter/astcompiler/ast.py
@@ -22,9 +22,11 @@
 
 def get_field(space, w_node, name, optional):
     w_obj = w_node.getdictvalue(space, name)
-    if w_obj is None and not optional:
-        raise oefmt(space.w_TypeError,
+    if w_obj is None:
+        if not optional:
+            raise oefmt(space.w_TypeError,
                 "required field \"%s\" missing from %T", name, w_node)
+        w_obj = space.w_None
     return w_obj
 
 
diff --git a/pypy/interpreter/astcompiler/tools/asdl_py.py b/pypy/interpreter/astcompiler/tools/asdl_py.py
--- a/pypy/interpreter/astcompiler/tools/asdl_py.py
+++ b/pypy/interpreter/astcompiler/tools/asdl_py.py
@@ -405,9 +405,11 @@
 
 def get_field(space, w_node, name, optional):
     w_obj = w_node.getdictvalue(space, name)
-    if w_obj is None and not optional:
-        raise oefmt(space.w_TypeError,
+    if w_obj is None:
+        if not optional:
+            raise oefmt(space.w_TypeError,
                 "required field \"%s\" missing from %T", name, w_node)
+        w_obj = space.w_None
     return w_obj
 
 
diff --git a/pypy/module/_ast/test/test_ast.py b/pypy/module/_ast/test/test_ast.py
--- a/pypy/module/_ast/test/test_ast.py
+++ b/pypy/module/_ast/test/test_ast.py
@@ -425,3 +425,8 @@
         str_node2 = copy.deepcopy(str_node)
         dict_res = str_node2.__dict__
         assert dict_res == {'n':2, 'lineno':2}
+
+    def test_bug_null_in_objspace_type(self):
+        import ast
+        code = ast.Expression(lineno=1, col_offset=1, body=ast.ListComp(lineno=1, col_offset=1, elt=ast.Call(lineno=1, col_offset=1, func=ast.Name(lineno=1, col_offset=1, id='str', ctx=ast.Load(lineno=1, col_offset=1)), args=[ast.Name(lineno=1, col_offset=1, id='x', ctx=ast.Load(lineno=1, col_offset=1))], keywords=[]), generators=[ast.comprehension(lineno=1, col_offset=1, target=ast.Name(lineno=1, col_offset=1, id='x', ctx=ast.Store(lineno=1, col_offset=1)), iter=ast.List(lineno=1, col_offset=1, elts=[ast.Num(lineno=1, col_offset=1, n=23)], ctx=ast.Load(lineno=1, col_offset=1, )), ifs=[])]))
+        compile(code, '<template>', 'eval')
diff --git a/pypy/module/micronumpy/test/test_arrayops.py b/pypy/module/micronumpy/test/test_arrayops.py
--- a/pypy/module/micronumpy/test/test_arrayops.py
+++ b/pypy/module/micronumpy/test/test_arrayops.py
@@ -211,7 +211,7 @@
         assert np.result_type(1.) is np.dtype('float64')
         assert np.result_type(1+2j) is np.dtype('complex128')
         assert np.result_type(1, 1.) is np.dtype('float64')
-        assert np.result_type(np.array([1, 2])) is np.dtype('int64')
+        assert np.result_type(np.array([1, 2])) is np.dtype('int')
         assert np.result_type(np.array([1, 2]), 1, 1+2j) is np.dtype('complex128')
         assert np.result_type(np.array([1, 2]), 1, 'float64') is np.dtype('float64')
         assert np.result_type(np.array([1, 2]), 1, None) is np.dtype('float64')
diff --git a/pypy/module/pypyjit/interp_resop.py b/pypy/module/pypyjit/interp_resop.py
--- a/pypy/module/pypyjit/interp_resop.py
+++ b/pypy/module/pypyjit/interp_resop.py
@@ -7,7 +7,7 @@
 from pypy.interpreter.error import OperationError
 from rpython.rtyper.lltypesystem import lltype
 from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance, hlstr
-from rpython.rtyper.lltypesystem.rclass import OBJECT
+from rpython.rtyper.rclass import OBJECT
 from rpython.jit.metainterp.resoperation import rop
 from rpython.rlib.nonconst import NonConstant
 from rpython.rlib import jit_hooks
diff --git a/pypy/module/pypyjit/test/test_jit_hook.py b/pypy/module/pypyjit/test/test_jit_hook.py
--- a/pypy/module/pypyjit/test/test_jit_hook.py
+++ b/pypy/module/pypyjit/test/test_jit_hook.py
@@ -9,7 +9,7 @@
 from rpython.rtyper.annlowlevel import (cast_instance_to_base_ptr,
                                       cast_base_ptr_to_instance)
 from rpython.rtyper.lltypesystem import lltype, llmemory
-from rpython.rtyper.lltypesystem.rclass import OBJECT
+from rpython.rtyper.rclass import OBJECT
 from pypy.module.pypyjit.interp_jit import pypyjitdriver
 from pypy.module.pypyjit.policy import pypy_hooks
 from rpython.jit.tool.oparser import parse
diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py
--- a/pypy/objspace/std/bytearrayobject.py
+++ b/pypy/objspace/std/bytearrayobject.py
@@ -4,6 +4,7 @@
     import_from_mixin, newlist_hint, resizelist_hint, specialize)
 from rpython.rlib.buffer import Buffer
 from rpython.rlib.rstring import StringBuilder, ByteListBuilder
+from rpython.rlib.debug import check_list_of_chars
 
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import OperationError, oefmt
@@ -22,6 +23,7 @@
     import_from_mixin(StringMethods)
 
     def __init__(self, data):
+        check_list_of_chars(data)
         self.data = data
 
     def __repr__(self):
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
@@ -29,7 +29,7 @@
 def analyzer_for(func):
     def wrapped(ann_func):
         BUILTIN_ANALYZERS[func] = ann_func
-        return func
+        return ann_func
     return wrapped
 
 class Bookkeeper(object):
@@ -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/classdef.py b/rpython/annotator/classdef.py
--- a/rpython/annotator/classdef.py
+++ b/rpython/annotator/classdef.py
@@ -154,6 +154,8 @@
         self.subdefs = []
         self.attr_sources = {}   # {name: list-of-sources}
         self.read_locations_of__class__ = {}
+        self.repr = None
+        self.extra_access_sets = {}
 
         if classdesc.basedesc:
             self.basedef = classdesc.basedesc.getuniqueclassdef()
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/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py
--- a/rpython/jit/backend/arm/opassembler.py
+++ b/rpython/jit/backend/arm/opassembler.py
@@ -1221,17 +1221,29 @@
                 length_box.getint() <= 14 and     # same limit as GCC
                 itemsize in (4, 2, 1)):
             # Inline a series of STR operations, starting at 'dstaddr_loc'.
-            # XXX we could optimize STRB/STRH into STR, but this needs care:
-            # XXX it only works if startindex_loc is a constant, otherwise
-            # XXX we'd be doing unaligned accesses
+            next_group = -1
+            if itemsize < 4 and startindex >= 0:
+                # we optimize STRB/STRH into STR, but this needs care:
+                # it only works if startindex_loc is a constant, otherwise
+                # we'd be doing unaligned accesses.
+                next_group = (-startindex * itemsize) & 3
+            #
             self.mc.gen_load_int(r.ip.value, 0)
-            for i in range(length_box.getint()):
-                if itemsize == 4:
-                    self.mc.STR_ri(r.ip.value, dstaddr_loc.value, imm=i*4)
-                elif itemsize == 2:
-                    self.mc.STRH_ri(r.ip.value, dstaddr_loc.value, imm=i*2)
+            i = 0
+            total_size = length_box.getint() * itemsize
+            while i < total_size:
+                sz = itemsize
+                if i == next_group:
+                    next_group += 4
+                    if next_group <= total_size:
+                        sz = 4
+                if sz == 4:
+                    self.mc.STR_ri(r.ip.value, dstaddr_loc.value, imm=i)
+                elif sz == 2:
+                    self.mc.STRH_ri(r.ip.value, dstaddr_loc.value, imm=i)
                 else:
-                    self.mc.STRB_ri(r.ip.value, dstaddr_loc.value, imm=i*1)
+                    self.mc.STRB_ri(r.ip.value, dstaddr_loc.value, imm=i)
+                i += sz
 
         else:
             if isinstance(length_box, ConstInt):
diff --git a/rpython/jit/backend/arm/test/test_regalloc.py b/rpython/jit/backend/arm/test/test_regalloc.py
--- a/rpython/jit/backend/arm/test/test_regalloc.py
+++ b/rpython/jit/backend/arm/test/test_regalloc.py
@@ -15,7 +15,8 @@
 from rpython.jit.tool.oparser import parse
 from rpython.rtyper.lltypesystem import lltype, llmemory
 from rpython.rtyper.annlowlevel import llhelper
-from rpython.rtyper.lltypesystem import rclass, rstr
+from rpython.rtyper.lltypesystem import rstr
+from rpython.rtyper import rclass
 from rpython.jit.codewriter.effectinfo import EffectInfo
 from rpython.jit.codewriter import longlong
 from rpython.jit.backend.llsupport.test.test_regalloc_integration import BaseTestRegalloc
@@ -333,7 +334,7 @@
         '''
         self.interpret(ops, [0, 0, 3, 0])
         assert self.getints(3) == [1, -3, 10]
-        
+
     def test_compare_memory_result_survives(self):
         ops = '''
         [i0, i1, i2, i3]
diff --git a/rpython/jit/backend/arm/test/test_runner.py b/rpython/jit/backend/arm/test/test_runner.py
--- a/rpython/jit/backend/arm/test/test_runner.py
+++ b/rpython/jit/backend/arm/test/test_runner.py
@@ -6,7 +6,8 @@
                                             BoxInt)
 from rpython.jit.metainterp.resoperation import ResOperation, rop
 from rpython.jit.tool.oparser import parse
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper import rclass
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.jit.codewriter.effectinfo import EffectInfo
 from rpython.jit.metainterp.history import JitCellToken, TargetToken
diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -11,7 +11,8 @@
 from rpython.jit.codewriter.effectinfo import EffectInfo
 
 from rpython.rtyper.llinterp import LLInterpreter, LLException
-from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rclass, rstr
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
+from rpython.rtyper import rclass
 
 from rpython.rlib.clibffi import FFI_DEFAULT_ABI
 from rpython.rlib.rarithmetic import ovfcheck, r_uint, r_ulonglong
diff --git a/rpython/jit/backend/llgraph/symbolic.py b/rpython/jit/backend/llgraph/symbolic.py
--- a/rpython/jit/backend/llgraph/symbolic.py
+++ b/rpython/jit/backend/llgraph/symbolic.py
@@ -1,4 +1,5 @@
-from rpython.rtyper.lltypesystem import lltype, rffi, rclass
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper import rclass
 
 
 Size2Type = [None] * 100
diff --git a/rpython/jit/backend/llsupport/gc.py b/rpython/jit/backend/llsupport/gc.py
--- a/rpython/jit/backend/llsupport/gc.py
+++ b/rpython/jit/backend/llsupport/gc.py
@@ -2,7 +2,8 @@
 from rpython.rlib import rgc
 from rpython.rlib.objectmodel import we_are_translated, specialize
 from rpython.rlib.rarithmetic import ovfcheck
-from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rclass, rstr
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
+from rpython.rtyper import rclass
 from rpython.rtyper.lltypesystem import llgroup
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rtyper.annlowlevel import llhelper, cast_instance_to_gcref
@@ -548,12 +549,6 @@
             type_id = self.layoutbuilder.get_type_id(A)
             descr.tid = llop.combine_ushort(lltype.Signed, type_id, 0)
 
-    def _set_tid(self, gcptr, tid):
-        hdr_addr = llmemory.cast_ptr_to_adr(gcptr)
-        hdr_addr -= self.gcheaderbuilder.size_gc_header
-        hdr = llmemory.cast_adr_to_ptr(hdr_addr, self.HDRPTR)
-        hdr.tid = tid
-
     def can_use_nursery_malloc(self, size):
         return size < self.max_size_of_young_obj
 
@@ -565,7 +560,7 @@
 
     def get_malloc_slowpath_array_addr(self):
         return self.get_malloc_fn_addr('malloc_array')
-    
+
 # ____________________________________________________________
 
 def get_ll_description(gcdescr, translator=None, rtyper=None):
diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -1,4 +1,5 @@
-from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rclass, rstr
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
+from rpython.rtyper import rclass
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rtyper.llinterp import LLInterpreter
 from rpython.rtyper.annlowlevel import llhelper, MixLevelHelperAnnotator
diff --git a/rpython/jit/backend/llsupport/test/test_regalloc_integration.py b/rpython/jit/backend/llsupport/test/test_regalloc_integration.py
--- a/rpython/jit/backend/llsupport/test/test_regalloc_integration.py
+++ b/rpython/jit/backend/llsupport/test/test_regalloc_integration.py
@@ -12,7 +12,8 @@
 from rpython.jit.tool.oparser import parse
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rtyper.annlowlevel import llhelper
-from rpython.rtyper.lltypesystem import rclass, rstr
+from rpython.rtyper.lltypesystem import rstr
+from rpython.rtyper import rclass
 from rpython.jit.codewriter import longlong
 from rpython.jit.codewriter.effectinfo import EffectInfo
 
diff --git a/rpython/jit/backend/llsupport/test/test_rewrite.py b/rpython/jit/backend/llsupport/test/test_rewrite.py
--- a/rpython/jit/backend/llsupport/test/test_rewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_rewrite.py
@@ -9,7 +9,8 @@
 from rpython.jit.metainterp.optimizeopt.util import equaloplists
 from rpython.jit.codewriter.heaptracker import register_known_gctype
 from rpython.jit.metainterp.history import JitCellToken, FLOAT
-from rpython.rtyper.lltypesystem import lltype, rclass, rffi
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper import rclass
 from rpython.jit.backend.x86.arch import WORD
 
 class Evaluator(object):
diff --git a/rpython/jit/backend/test/runner_test.py b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -11,7 +11,8 @@
 from rpython.jit.metainterp.typesystem import deref
 from rpython.jit.codewriter.effectinfo import EffectInfo
 from rpython.jit.tool.oparser import parse
-from rpython.rtyper.lltypesystem import lltype, llmemory, rstr, rffi, rclass
+from rpython.rtyper.lltypesystem import lltype, llmemory, rstr, rffi
+from rpython.rtyper import rclass
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rtyper.llinterp import LLException
 from rpython.jit.codewriter import heaptracker, longlong
@@ -2296,7 +2297,7 @@
 
         for i in range(5):
             called = []
-        
+
             FUNC = self.FuncType([lltype.Signed] * i, lltype.Void)
             func_ptr = llhelper(lltype.Ptr(FUNC), func_void)
             calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
@@ -4450,7 +4451,7 @@
 
     def test_zero_ptr_field(self):
         from rpython.jit.backend.llsupport.llmodel import AbstractLLCPU
-        
+
         if not isinstance(self.cpu, AbstractLLCPU):
             py.test.skip("llgraph can't do zero_ptr_field")
         T = lltype.GcStruct('T')
@@ -4478,7 +4479,7 @@
 
         if not isinstance(self.cpu, AbstractLLCPU):
             py.test.skip("llgraph does not do zero_ptr_field")
-        
+
         from rpython.jit.backend.llsupport import symbolic
         S = lltype.GcStruct('S', ('x', lltype.Signed),
                                  ('p', llmemory.GCREF),
@@ -4503,7 +4504,7 @@
 
         if not isinstance(self.cpu, AbstractLLCPU):
             py.test.skip("llgraph does not do zero_array")
-        
+
         PAIR = lltype.Struct('PAIR', ('a', lltype.Signed), ('b', lltype.Signed))
         for OF in [lltype.Signed, rffi.INT, rffi.SHORT, rffi.UCHAR, PAIR]:
             A = lltype.GcArray(OF)
diff --git a/rpython/jit/backend/test/test_ll_random.py b/rpython/jit/backend/test/test_ll_random.py
--- a/rpython/jit/backend/test/test_ll_random.py
+++ b/rpython/jit/backend/test/test_ll_random.py
@@ -1,5 +1,6 @@
 import py
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass, rffi, rstr
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
+from rpython.rtyper import rclass
 from rpython.jit.backend.test import test_random
 from rpython.jit.metainterp.resoperation import ResOperation, rop
 from rpython.jit.metainterp.history import ConstInt, ConstPtr
@@ -561,7 +562,7 @@
         subset = builder.subset_of_intvars(r)
         funcargs = ", ".join(['arg_%d' % i for i in range(len(subset))])
         S, v = builder.get_structptr_var(r, must_have_vtable=True)
-        
+
         code = py.code.Source("""
         def f(%s):
             raise LLException(vtable, ptr)
diff --git a/rpython/jit/backend/test/test_random.py b/rpython/jit/backend/test/test_random.py
--- a/rpython/jit/backend/test/test_random.py
+++ b/rpython/jit/backend/test/test_random.py
@@ -10,7 +10,8 @@
 from rpython.jit.metainterp.executor import execute_nonspec
 from rpython.jit.metainterp.resoperation import opname
 from rpython.jit.codewriter import longlong
-from rpython.rtyper.lltypesystem import lltype, rstr, rclass
+from rpython.rtyper.lltypesystem import lltype, rstr
+from rpython.rtyper import rclass
 
 class PleaseRewriteMe(Exception):
     pass
@@ -234,7 +235,7 @@
                 ', '.join([names[v] for v in fail_args]))
         print >>s, '    operations = ['
         for op in self.loop.operations:
-            self.process_operation(s, op, names) 
+            self.process_operation(s, op, names)
         print >>s, '        ]'
         for i, op in enumerate(self.loop.operations):
             if op.is_guard():
diff --git a/rpython/jit/backend/x86/test/test_regalloc2.py b/rpython/jit/backend/x86/test/test_regalloc2.py
--- a/rpython/jit/backend/x86/test/test_regalloc2.py
+++ b/rpython/jit/backend/x86/test/test_regalloc2.py
@@ -5,7 +5,8 @@
 from rpython.jit.backend.detect_cpu import getcpuclass
 from rpython.jit.backend.x86.arch import WORD
 from rpython.jit.tool.oparser import parse
-from rpython.rtyper.lltypesystem import lltype, rffi, rclass, llmemory, rstr
+from rpython.rtyper.lltypesystem import lltype, rffi, llmemory, rstr
+from rpython.rtyper import rclass
 from rpython.rtyper.llinterp import LLException
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.jit.codewriter.effectinfo import EffectInfo
@@ -319,7 +320,7 @@
         raise LLException(vtableptr, xptr)
 
     fptr, funcdescr = getllhelper(cpu, f, [lltype.Signed] * count, lltype.Void)
-    
+
     return heaptracker.adr2int(llmemory.cast_ptr_to_adr(vtableptr)), fptr, funcdescr
 
 def getnoexception(cpu, count):
diff --git a/rpython/jit/codewriter/assembler.py b/rpython/jit/codewriter/assembler.py
--- a/rpython/jit/codewriter/assembler.py
+++ b/rpython/jit/codewriter/assembler.py
@@ -6,7 +6,8 @@
 from rpython.jit.codewriter import heaptracker, longlong
 from rpython.rlib.objectmodel import ComputedIntSymbolic
 from rpython.flowspace.model import Constant
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass, rffi
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rtyper import rclass
 
 
 class AssemblerError(Exception):
diff --git a/rpython/jit/codewriter/effectinfo.py b/rpython/jit/codewriter/effectinfo.py
--- a/rpython/jit/codewriter/effectinfo.py
+++ b/rpython/jit/codewriter/effectinfo.py
@@ -1,5 +1,5 @@
 from rpython.jit.metainterp.typesystem import deref, fieldType, arrayItem
-from rpython.rtyper.lltypesystem.rclass import OBJECT
+from rpython.rtyper.rclass import OBJECT
 from rpython.rtyper.lltypesystem import lltype, llmemory
 from rpython.translator.backendopt.graphanalyze import BoolGraphAnalyzer
 
diff --git a/rpython/jit/codewriter/heaptracker.py b/rpython/jit/codewriter/heaptracker.py
--- a/rpython/jit/codewriter/heaptracker.py
+++ b/rpython/jit/codewriter/heaptracker.py
@@ -1,4 +1,5 @@
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper import rclass
 from rpython.rlib.objectmodel import we_are_translated
 
 
@@ -125,7 +126,7 @@
     vtable = descr.as_vtable_size_descr()._corresponding_vtable
     vtable = llmemory.cast_ptr_to_adr(vtable)
     return adr2int(vtable)
-    
+
 def gc_fielddescrs(gccache, STRUCT, res=None):
     from rpython.jit.backend.llsupport import descr
 
diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -12,8 +12,9 @@
 from rpython.rlib import objectmodel
 from rpython.rlib.jit import _we_are_jitted
 from rpython.rlib.rgc import lltype_is_gc
-from rpython.rtyper.lltypesystem import lltype, llmemory, rstr, rclass, rffi
+from rpython.rtyper.lltypesystem import lltype, llmemory, rstr, rffi
 from rpython.rtyper.lltypesystem import rbytearray
+from rpython.rtyper import rclass
 from rpython.rtyper.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
 from rpython.translator.unsimplify import varoftype
 
@@ -1653,41 +1654,34 @@
         self._get_list_nonneg_canraise_flags(op)
 
     def _get_initial_newlist_length(self, op, args):
-        # normalize number of arguments to the 'newlist' function
-        if len(args) > 1:
-            v_default = args[1]     # initial value: must be 0 or NULL
-            ARRAY = deref(op.result.concretetype)
-            if (not isinstance(v_default, Constant) or
-                v_default.value != arrayItem(ARRAY)._defl()):
-                raise NotSupported("variable or non-null initial value")
-        if len(args) >= 1:
-            return args[0]
+        assert len(args) <= 1
+        if len(args) == 1:
+            v_length = args[0]
+            assert v_length.concretetype is lltype.Signed
+            return v_length
         else:
             return Constant(0, lltype.Signed)     # length: default to 0
 
     # ---------- fixed lists ----------
 
     def do_fixed_newlist(self, op, args, arraydescr):
+        # corresponds to rtyper.lltypesystem.rlist.newlist:
+        # the items may be uninitialized.
         v_length = self._get_initial_newlist_length(op, args)
-        assert v_length.concretetype is lltype.Signed
-        ops = []
-        if isinstance(v_length, Constant):
-            if v_length.value >= 0:
-                v = v_length
-            else:
-                v = Constant(0, lltype.Signed)
-        else:
-            v = Variable('new_length')
-            v.concretetype = lltype.Signed
-            ops.append(SpaceOperation('int_force_ge_zero', [v_length], v))
         ARRAY = op.result.concretetype.TO
         if ((isinstance(ARRAY.OF, lltype.Ptr) and ARRAY.OF._needsgc()) or
                isinstance(ARRAY.OF, lltype.Struct)):
             opname = 'new_array_clear'
         else:
             opname = 'new_array'
-        ops.append(SpaceOperation(opname, [v, arraydescr], op.result))
-        return ops
+        return SpaceOperation(opname, [v_length, arraydescr], op.result)
+
+    def do_fixed_newlist_clear(self, op, args, arraydescr):
+        # corresponds to rtyper.rlist.ll_alloc_and_clear:
+        # needs to clear the items.
+        v_length = self._get_initial_newlist_length(op, args)
+        return SpaceOperation('new_array_clear', [v_length, arraydescr],
+                              op.result)
 
     def do_fixed_list_len(self, op, args, arraydescr):
         if args[0] in self.vable_array_vars:     # virtualizable array
@@ -1757,6 +1751,14 @@
                                arraydescr],
                               op.result)
 
+    def do_resizable_newlist_clear(self, op, args, arraydescr, lengthdescr,
+                                   itemsdescr, structdescr):
+        v_length = self._get_initial_newlist_length(op, args)
+        return SpaceOperation('newlist_clear',
+                              [v_length, structdescr, lengthdescr, itemsdescr,
+                               arraydescr],
+                              op.result)
+
     def do_resizable_newlist_hint(self, op, args, arraydescr, lengthdescr,
                                   itemsdescr, structdescr):
         v_hint = self._get_initial_newlist_length(op, args)
diff --git a/rpython/jit/codewriter/support.py b/rpython/jit/codewriter/support.py
--- a/rpython/jit/codewriter/support.py
+++ b/rpython/jit/codewriter/support.py
@@ -14,7 +14,8 @@
 from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator
 from rpython.rtyper.extregistry import ExtRegistryEntry
 from rpython.rtyper.llinterp import LLInterpreter
-from rpython.rtyper.lltypesystem import lltype, rclass, rffi, llmemory, rstr as ll_rstr, rdict as ll_rdict
+from rpython.rtyper.lltypesystem import lltype, rffi, llmemory, rstr as ll_rstr, rdict as ll_rdict
+from rpython.rtyper import rclass
 from rpython.rtyper.lltypesystem import rordereddict
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rtyper.lltypesystem.module import ll_math
@@ -180,11 +181,11 @@
     return LIST.ll_newlist(0)
 def _ll_1_newlist(LIST, count):
     return LIST.ll_newlist(count)
-def _ll_2_newlist(LIST, count, item):
-    return rlist.ll_alloc_and_set(LIST, count, item)
 _ll_0_newlist.need_result_type = True
 _ll_1_newlist.need_result_type = True
-_ll_2_newlist.need_result_type = True
+
+_ll_1_newlist_clear = rlist._ll_alloc_and_clear
+_ll_1_newlist_clear.need_result_type = True
 
 def _ll_1_newlist_hint(LIST, hint):
     return LIST.ll_newlist_hint(hint)
diff --git a/rpython/jit/codewriter/test/test_codewriter.py b/rpython/jit/codewriter/test/test_codewriter.py
--- a/rpython/jit/codewriter/test/test_codewriter.py
+++ b/rpython/jit/codewriter/test/test_codewriter.py
@@ -232,18 +232,3 @@
     assert 'setarrayitem_raw_i' in s
     assert 'getarrayitem_raw_i' in s
     assert 'residual_call_ir_v $<* fn _ll_1_raw_free__arrayPtr>' in s
-
-def test_newlist_negativ():
-    def f(n):
-        l = [0] * n
-        return len(l)
-
-    rtyper = support.annotate(f, [-1])
-    jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
-    cw = CodeWriter(FakeCPU(rtyper), [jitdriver_sd])
-    graphs = cw.find_all_graphs(FakePolicy())
-    backend_optimizations(rtyper.annotator.translator, graphs=graphs)
-    cw.make_jitcodes(verbose=True)
-    s = jitdriver_sd.mainjitcode.dump()
-    assert 'int_force_ge_zero' in s
-    assert 'new_array' in s
diff --git a/rpython/jit/codewriter/test/test_effectinfo.py b/rpython/jit/codewriter/test/test_effectinfo.py
--- a/rpython/jit/codewriter/test/test_effectinfo.py
+++ b/rpython/jit/codewriter/test/test_effectinfo.py
@@ -4,7 +4,7 @@
     EffectInfo, VirtualizableAnalyzer)
 from rpython.rlib import jit
 from rpython.rtyper.lltypesystem import lltype
-from rpython.rtyper.lltypesystem.rclass import OBJECT
+from rpython.rtyper.rclass import OBJECT
 from rpython.translator.translator import TranslationContext, graphof
 
 
diff --git a/rpython/jit/codewriter/test/test_flatten.py b/rpython/jit/codewriter/test/test_flatten.py
--- a/rpython/jit/codewriter/test/test_flatten.py
+++ b/rpython/jit/codewriter/test/test_flatten.py
@@ -6,7 +6,8 @@
 from rpython.jit.codewriter import longlong
 from rpython.jit.codewriter.effectinfo import EffectInfo
 from rpython.jit.metainterp.history import AbstractDescr
-from rpython.rtyper.lltypesystem import lltype, rclass, rstr, rffi
+from rpython.rtyper.lltypesystem import lltype, rstr, rffi
+from rpython.rtyper import rclass
 from rpython.flowspace.model import SpaceOperation, Variable, Constant
 from rpython.translator.unsimplify import varoftype
 from rpython.rlib.rarithmetic import ovfcheck, r_uint, r_longlong, r_ulonglong
diff --git a/rpython/jit/codewriter/test/test_jtransform.py b/rpython/jit/codewriter/test/test_jtransform.py
--- a/rpython/jit/codewriter/test/test_jtransform.py
+++ b/rpython/jit/codewriter/test/test_jtransform.py
@@ -17,7 +17,8 @@
 
 from rpython.flowspace.model import FunctionGraph, Block, Link
 from rpython.flowspace.model import SpaceOperation, Variable, Constant
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass, rstr, rffi
+from rpython.rtyper.lltypesystem import lltype, llmemory, rstr, rffi
+from rpython.rtyper import rclass
 from rpython.rtyper.lltypesystem.module import ll_math
 from rpython.translator.unsimplify import varoftype
 from rpython.jit.codewriter import heaptracker, effectinfo
@@ -1346,7 +1347,7 @@
     assert op2 is None
 
 def test_threadlocalref_get():
-    from rpython.rtyper.lltypesystem import rclass
+    from rpython.rtyper import rclass
     from rpython.rlib.rthread import ThreadLocalReference
     OS_THREADLOCALREF_GET = effectinfo.EffectInfo.OS_THREADLOCALREF_GET
     class Foo: pass
diff --git a/rpython/jit/codewriter/test/test_list.py b/rpython/jit/codewriter/test/test_list.py
--- a/rpython/jit/codewriter/test/test_list.py
+++ b/rpython/jit/codewriter/test/test_list.py
@@ -87,20 +87,10 @@
                  """new_array $0, <ArrayDescr> -> %r0""")
     builtin_test('newlist', [Constant(5, lltype.Signed)], FIXEDLIST,
                  """new_array $5, <ArrayDescr> -> %r0""")
-    builtin_test('newlist', [Constant(-2, lltype.Signed)], FIXEDLIST,
-                 """new_array $0, <ArrayDescr> -> %r0""")
     builtin_test('newlist', [varoftype(lltype.Signed)], FIXEDLIST,
-                 """int_force_ge_zero %i0 -> %i1\n"""
-                 """new_array %i1, <ArrayDescr> -> %r0""")
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             Constant(0, lltype.Signed)], FIXEDLIST,
-                 """new_array $5, <ArrayDescr> -> %r0""")
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             Constant(1, lltype.Signed)], FIXEDLIST,
-                 NotSupported)
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             varoftype(lltype.Signed)], FIXEDLIST,
-                 NotSupported)
+                 """new_array %i0, <ArrayDescr> -> %r0""")
+    builtin_test('newlist_clear', [Constant(5, lltype.Signed)], FIXEDLIST,
+                 """new_array_clear $5, <ArrayDescr> -> %r0""")
     builtin_test('newlist', [], FIXEDPTRLIST,
                  """new_array_clear $0, <ArrayDescr> -> %r0""")
 
@@ -179,15 +169,8 @@
                  """newlist $5, """+alldescrs+""" -> %r0""")
     builtin_test('newlist', [varoftype(lltype.Signed)], VARLIST,
                  """newlist %i0, """+alldescrs+""" -> %r0""")
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             Constant(0, lltype.Signed)], VARLIST,
-                 """newlist $5, """+alldescrs+""" -> %r0""")
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             Constant(1, lltype.Signed)], VARLIST,
-                 NotSupported)
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             varoftype(lltype.Signed)], VARLIST,
-                 NotSupported)
+    builtin_test('newlist_clear', [Constant(5, lltype.Signed)], VARLIST,
+                 """newlist_clear $5, """+alldescrs+""" -> %r0""")
 
 def test_resizable_getitem():
     builtin_test('list.getitem/NONNEG',
diff --git a/rpython/jit/codewriter/test/test_regalloc.py b/rpython/jit/codewriter/test/test_regalloc.py
--- a/rpython/jit/codewriter/test/test_regalloc.py
+++ b/rpython/jit/codewriter/test/test_regalloc.py
@@ -7,7 +7,8 @@
 from rpython.flowspace.model import Variable, Constant, SpaceOperation
 from rpython.flowspace.model import FunctionGraph, Block, Link
 from rpython.flowspace.model import c_last_exception
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper import rclass
 from rpython.rlib.rarithmetic import ovfcheck
 
 
diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -8,7 +8,8 @@
 from rpython.rlib.objectmodel import we_are_translated
 from rpython.rlib.rarithmetic import intmask, LONG_BIT, r_uint, ovfcheck
 from rpython.rlib.unroll import unrolling_iterable
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass, rffi
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rtyper import rclass
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rlib.jit_libffi import CIF_DESCRIPTION_P
 
@@ -1017,6 +1018,15 @@
         return result
 
     @arguments("cpu", "i", "d", "d", "d", "d", returns="r")
+    def bhimpl_newlist_clear(cpu, length, structdescr, lengthdescr,
+                             itemsdescr, arraydescr):
+        result = cpu.bh_new(structdescr)
+        cpu.bh_setfield_gc_i(result, length, lengthdescr)
+        items = cpu.bh_new_array_clear(length, arraydescr)
+        cpu.bh_setfield_gc_r(result, items, itemsdescr)
+        return result
+
+    @arguments("cpu", "i", "d", "d", "d", "d", returns="r")
     def bhimpl_newlist_hint(cpu, lengthhint, structdescr, lengthdescr,
                             itemsdescr, arraydescr):
         result = cpu.bh_new(structdescr)
@@ -1163,7 +1173,7 @@
 
     @arguments("cpu", "i", "d", returns="r")
     def bhimpl_new_array_clear(cpu, length, arraydescr):
-        return cpu.bh_new_array_clear(length, arraydescr)        
+        return cpu.bh_new_array_clear(length, arraydescr)
 
     @arguments("cpu", "r", "i", "d", returns="i")
     def bhimpl_getarrayitem_gc_i(cpu, array, index, arraydescr):
diff --git a/rpython/jit/metainterp/jitexc.py b/rpython/jit/metainterp/jitexc.py
--- a/rpython/jit/metainterp/jitexc.py
+++ b/rpython/jit/metainterp/jitexc.py
@@ -1,6 +1,7 @@
 from rpython.rtyper.annlowlevel import cast_instance_to_base_ptr
 from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
-from rpython.rtyper.lltypesystem import lltype, rclass
+from rpython.rtyper.lltypesystem import lltype
+from rpython.rtyper import rclass
 from rpython.rtyper.llinterp import LLException
 from rpython.rlib.objectmodel import we_are_translated
 from rpython.jit.codewriter import longlong
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -8293,6 +8293,22 @@
         self.optimize_loop(ops, expected)
 
     def test_licm_boxed_opaque_getitem_unknown_class(self):
+        # Explanation: the getfield_gc(p2) is done on what starts as
+        # an opaque object.  The getfield_gc(p1) is moved out of the
+        # (non-preamble) loop.  It looks like the getfield_gc(p2)
+        # should also move out.  However, moving the getfield_gc(p2)
+        # earlier can be dangerous with opaque pointers: we can't move
+        # it before other guards that indirectly check for which type
+        # of object is in p2.  (In this simple test there are no guard
+        # at all between the start of the loop and the
+        # getfield_gc(p2), but in general there are.)
+        #
+        # There are two cases: (1) moving the getfield_gc(p2) out of
+        # the loop into the preamble: this does not look like a
+        # problem because we already have a getfield_gc(p2) there, on
+        # the same p2.  Case (2) is moving the getfield_gc(p2) into
+        # the short preamble: this is more problematic because the
+        # short preamble can't do the indirect checking on p1.
         ops = """
         [p1]
         p2 = getfield_gc(p1, descr=nextdescr)
@@ -8326,6 +8342,7 @@
         self.optimize_loop(ops, expected)
 
     def test_licm_unboxed_opaque_getitem_unknown_class(self):
+        # see test_licm_boxed_opaque_getitem_unknown_class
         ops = """
         [p2]
         mark_opaque_ptr(p2)
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -1,8 +1,9 @@
 import py, random
 
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass, rffi
-from rpython.rtyper.lltypesystem.rclass import OBJECT, OBJECT_VTABLE
-from rpython.rtyper.rclass import FieldListAccessor, IR_QUASIIMMUTABLE
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rtyper import rclass
+from rpython.rtyper.rclass import (
+    OBJECT, OBJECT_VTABLE, FieldListAccessor, IR_QUASIIMMUTABLE)
 
 from rpython.jit.backend.llgraph import runner
 from rpython.jit.metainterp.history import (BoxInt, BoxPtr, ConstInt, ConstPtr,
diff --git a/rpython/jit/metainterp/optimizeopt/virtualize.py b/rpython/jit/metainterp/optimizeopt/virtualize.py
--- a/rpython/jit/metainterp/optimizeopt/virtualize.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualize.py
@@ -764,9 +764,12 @@
     def optimize_GETARRAYITEM_GC(self, op):
         value = self.getvalue(op.getarg(0))
         if value.is_virtual():
+            assert isinstance(value, VArrayValue)


More information about the pypy-commit mailing list