[pypy-svn] r77376 - in pypy/branch/jit-str/pypy/jit: codewriter codewriter/test metainterp metainterp/optimizeopt metainterp/test

arigo at codespeak.net arigo at codespeak.net
Sun Sep 26 13:56:58 CEST 2010


Author: arigo
Date: Sun Sep 26 13:56:56 2010
New Revision: 77376

Added:
   pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/string.py   (contents, props changed)
Modified:
   pypy/branch/jit-str/pypy/jit/codewriter/effectinfo.py
   pypy/branch/jit-str/pypy/jit/codewriter/jtransform.py
   pypy/branch/jit-str/pypy/jit/codewriter/support.py
   pypy/branch/jit-str/pypy/jit/codewriter/test/test_support.py
   pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/__init__.py
   pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/optimizer.py
   pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/virtualize.py
   pypy/branch/jit-str/pypy/jit/metainterp/resume.py
   pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizefindnode.py
   pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizeopt.py
   pypy/branch/jit-str/pypy/jit/metainterp/test/test_resume.py
   pypy/branch/jit-str/pypy/jit/metainterp/test/test_string.py
   pypy/branch/jit-str/pypy/jit/metainterp/warmstate.py
Log:
- Implement a number of variants of string equality,
  and pick the best one in optimizeopt.

- Refactor the string-optimization code in its own
  file, string.py.

- Change in resume.py: the nice two-steps building of
  objects -- first allocate them all, then fill them
  all -- no longer works with strings, because we need
  to build the strings in a specific order (e.g. a
  string concatenation needs to know the two concatenated
  strings' lengths before it can even allocate the result).
  Rely on recursion instead.


Modified: pypy/branch/jit-str/pypy/jit/codewriter/effectinfo.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/codewriter/effectinfo.py	(original)
+++ pypy/branch/jit-str/pypy/jit/codewriter/effectinfo.py	Sun Sep 26 13:56:56 2010
@@ -19,9 +19,18 @@
     OS_NONE                     = 0    # normal case, no oopspec
     OS_ARRAYCOPY                = 1    # "list.ll_arraycopy"
     OS_STR_CONCAT               = 2    # "stroruni.concat"
-    OS_STR_SLICE                = 3    # "stroruni.slice"
-    OS_UNI_CONCAT               = 4    # "stroruni.concat"
+    OS_UNI_CONCAT               = 3    # "stroruni.concat"
+    OS_STR_SLICE                = 4    # "stroruni.slice"
     OS_UNI_SLICE                = 5    # "stroruni.slice"
+    OS_STR_EQUAL                = 6    # "stroruni.equal"
+    OS_UNI_EQUAL                = 7    # "stroruni.equal"
+    OS_STREQ_SLICE_CHECKNULL    = 8    # s2!=NULL and s1[x:x+length]==s2
+    OS_STREQ_SLICE_NONNULL      = 9    # s1[x:x+length]==s2   (assert s2!=NULL)
+    OS_STREQ_SLICE_CHAR         = 10   # s1[x:x+length]==char
+    OS_STREQ_NONNULL            = 11   # s1 == s2    (assert s1!=NULL,s2!=NULL)
+    OS_STREQ_NONNULL_CHAR       = 12   # s1 == char  (assert s1!=NULL)
+    OS_STREQ_CHECKNULL_CHAR     = 13   # s1!=NULL and s1==char
+    OS_STREQ_LENGTHOK           = 14   # s1 == s2    (assert len(s1)==len(s2))
 
     def __new__(cls, readonly_descrs_fields,
                 write_descrs_fields, write_descrs_arrays,
@@ -135,11 +144,18 @@
     assert calldescr is not None
     return calldescr, func
 
-def funcptr_for_oopspec(oopspecindex):
-    """A memo function that returns a pointer to the function described
-    by OS_XYZ (as a real low-level function pointer)."""
+
+def _funcptr_for_oopspec_memo(oopspecindex):
     from pypy.jit.codewriter import heaptracker
     _, func_as_int = _callinfo_for_oopspec.get(oopspecindex, (None, 0))
     funcadr = heaptracker.int2adr(func_as_int)
     return funcadr.ptr
-funcptr_for_oopspec._annspecialcase_ = 'specialize:memo'
+_funcptr_for_oopspec_memo._annspecialcase_ = 'specialize:memo'
+
+def funcptr_for_oopspec(oopspecindex):
+    """A memo function that returns a pointer to the function described
+    by OS_XYZ (as a real low-level function pointer)."""
+    funcptr = _funcptr_for_oopspec_memo(oopspecindex)
+    assert funcptr
+    return funcptr
+funcptr_for_oopspec._annspecialcase_ = 'specialize:arg(0)'

Modified: pypy/branch/jit-str/pypy/jit/codewriter/jtransform.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/codewriter/jtransform.py	(original)
+++ pypy/branch/jit-str/pypy/jit/codewriter/jtransform.py	Sun Sep 26 13:56:56 2010
@@ -12,6 +12,7 @@
 from pypy.rlib import objectmodel
 from pypy.rlib.jit import _we_are_jitted
 from pypy.translator.simplify import get_funcobj
+from pypy.translator.unsimplify import varoftype
 
 
 def transform_graph(graph, cpu=None, callcontrol=None, portal_jd=None):
@@ -1042,15 +1043,70 @@
             op1 = [op1, SpaceOperation('-live-', [], None)]
         return op1
 
+    def _register_extra_helper(self, oopspecindex, oopspec_name,
+                               argtypes, resulttype):
+        # a bit hackish
+        if oopspecindex in _callinfo_for_oopspec:
+            return
+        c_func, TP = support.builtin_func_for_spec(self.cpu.rtyper,
+                                                   oopspec_name, argtypes,
+                                                   resulttype)
+        op = SpaceOperation('pseudo_call',
+                            [c_func] + [varoftype(T) for T in argtypes],
+                            varoftype(resulttype))
+        calldescr = self.callcontrol.getcalldescr(op, oopspecindex)
+        _callinfo_for_oopspec[oopspecindex] = calldescr, c_func.value
+
     def _handle_stroruni_call(self, op, oopspec_name, args):
         if args[0].concretetype.TO == rstr.STR:
             dict = {"stroruni.concat": EffectInfo.OS_STR_CONCAT,
-                    "stroruni.slice":  EffectInfo.OS_STR_SLICE}
+                    "stroruni.slice":  EffectInfo.OS_STR_SLICE,
+                    "stroruni.equal":  EffectInfo.OS_STR_EQUAL,
+                    }
         elif args[0].concretetype.TO == rstr.UNICODE:
             dict = {"stroruni.concat": EffectInfo.OS_UNI_CONCAT,
-                    "stroruni.slice":  EffectInfo.OS_UNI_SLICE}
+                    "stroruni.slice":  EffectInfo.OS_UNI_SLICE,
+                    "stroruni.equal":  EffectInfo.OS_UNI_EQUAL,
+                    }
         else:
             assert 0, "args[0].concretetype must be STR or UNICODE"
+        #
+        if oopspec_name == "stroruni.equal":
+            SoU = args[0].concretetype     # Ptr(STR) or Ptr(UNICODE)
+            for otherindex, othername, argtypes, resulttype in [
+
+                (EffectInfo.OS_STREQ_SLICE_CHECKNULL,
+                     "str.eq_slice_checknull",
+                     [SoU, lltype.Signed, lltype.Signed, SoU],
+                     lltype.Signed),
+                (EffectInfo.OS_STREQ_SLICE_NONNULL,
+                     "str.eq_slice_nonnull",
+                     [SoU, lltype.Signed, lltype.Signed, SoU],
+                     lltype.Signed),
+                (EffectInfo.OS_STREQ_SLICE_CHAR,
+                     "str.eq_slice_char",
+                     [SoU, lltype.Signed, lltype.Signed, lltype.Char],
+                     lltype.Signed),
+                (EffectInfo.OS_STREQ_NONNULL,
+                     "str.eq_nonnull",
+                     [SoU, SoU],
+                     lltype.Signed),
+                (EffectInfo.OS_STREQ_NONNULL_CHAR,
+                     "str.eq_nonnull_char",
+                     [SoU, lltype.Char],
+                     lltype.Signed),
+                (EffectInfo.OS_STREQ_CHECKNULL_CHAR,
+                     "str.eq_checknull_char",
+                     [SoU, lltype.Char],
+                     lltype.Signed),
+                (EffectInfo.OS_STREQ_LENGTHOK,
+                     "str.eq_lengthok",
+                     [SoU, SoU],
+                     lltype.Signed),
+                ]:
+                self._register_extra_helper(otherindex, othername,
+                                            argtypes, resulttype)
+        #
         return self._handle_oopspec_call(op, args, dict[oopspec_name])
 
     # ----------

Modified: pypy/branch/jit-str/pypy/jit/codewriter/support.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/codewriter/support.py	(original)
+++ pypy/branch/jit-str/pypy/jit/codewriter/support.py	Sun Sep 26 13:56:56 2010
@@ -277,6 +277,85 @@
 
     _ll_1_str_str2unicode = ll_rstr.LLHelpers.ll_str2unicode
 
+    def _ll_4_str_eq_slice_checknull(s1, start, length, s2):
+        """str1[start : start + length] == str2."""
+        if not s2:
+            return 0
+        chars2 = s2.chars
+        if len(chars2) != length:
+            return 0
+        j = 0
+        chars1 = s1.chars
+        while j < length:
+            if chars1[start + j] != chars2[j]:
+                return 0
+            j += 1
+        return 1
+
+    def _ll_4_str_eq_slice_nonnull(s1, start, length, s2):
+        """str1[start : start + length] == str2, assuming str2 != NULL."""
+        chars2 = s2.chars
+        if len(chars2) != length:
+            return 0
+        j = 0
+        chars1 = s1.chars
+        while j < length:
+            if chars1[start + j] != chars2[j]:
+                return 0
+            j += 1
+        return 1
+
+    def _ll_4_str_eq_slice_char(s1, start, length, c2):
+        """str1[start : start + length] == c2."""
+        if length != 1:
+            return 0
+        if s1.chars[start] != c2:
+            return 0
+        return 1
+
+    def _ll_2_str_eq_nonnull(s1, s2):
+        len1 = len(s1.chars)
+        len2 = len(s2.chars)
+        if len1 != len2:
+            return 0
+        j = 0
+        chars1 = s1.chars
+        chars2 = s2.chars
+        while j < len1:
+            if chars1[j] != chars2[j]:
+                return 0
+            j += 1
+        return 1
+
+    def _ll_2_str_eq_nonnull_char(s1, c2):
+        chars = s1.chars
+        if len(chars) != 1:
+            return 0
+        if chars[0] != c2:
+            return 0
+        return 1
+
+    def _ll_2_str_eq_checknull_char(s1, c2):
+        if not s1:
+            return 0
+        chars = s1.chars
+        if len(chars) != 1:
+            return 0
+        if chars[0] != c2:
+            return 0
+        return 1
+
+    def _ll_2_str_eq_lengthok(s1, s2):
+        j = 0
+        chars1 = s1.chars
+        chars2 = s2.chars
+        len1 = len(chars1)
+        while j < len1:
+            if chars1[j] != chars2[j]:
+                return 0
+            j += 1
+        return 1
+
     # ---------- malloc with del ----------
 
     def _ll_2_raw_malloc(TP, size):

Modified: pypy/branch/jit-str/pypy/jit/codewriter/test/test_support.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/codewriter/test/test_support.py	(original)
+++ pypy/branch/jit-str/pypy/jit/codewriter/test/test_support.py	Sun Sep 26 13:56:56 2010
@@ -1,7 +1,8 @@
 import py
 from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.annlowlevel import llstr
 from pypy.objspace.flow.model import Variable, Constant, SpaceOperation
-from pypy.jit.codewriter.support import decode_builtin_call
+from pypy.jit.codewriter.support import decode_builtin_call, LLtypeHelpers
 
 def newconst(x):
     return Constant(x, lltype.typeOf(x))
@@ -65,3 +66,70 @@
     assert opargs == [newconst(myarray), newconst(2), vc, vi]
     #impl = runner.get_oopspec_impl('spam.foobar', lltype.Ptr(A))
     #assert impl(myarray, 2, 'A', 5) == 42 * ord('A')
+
+def test_streq_slice_checknull():
+    p1 = llstr("hello world")
+    p2 = llstr("wor")
+    func = LLtypeHelpers._ll_4_str_eq_slice_checknull.im_func
+    assert func(p1, 6, 3, p2) == True
+    assert func(p1, 6, 2, p2) == False
+    assert func(p1, 5, 3, p2) == False
+    assert func(p1, 2, 1, llstr(None)) == False
+
+def test_streq_slice_nonnull():
+    p1 = llstr("hello world")
+    p2 = llstr("wor")
+    func = LLtypeHelpers._ll_4_str_eq_slice_nonnull.im_func
+    assert func(p1, 6, 3, p2) == True
+    assert func(p1, 6, 2, p2) == False
+    assert func(p1, 5, 3, p2) == False
+    py.test.raises(AttributeError, func, p1, 2, 1, llstr(None))
+
+def test_streq_slice_char():
+    p1 = llstr("hello world")
+    func = LLtypeHelpers._ll_4_str_eq_slice_char.im_func
+    assert func(p1, 6, 3, "w") == False
+    assert func(p1, 6, 0, "w") == False
+    assert func(p1, 6, 1, "w") == True
+    assert func(p1, 6, 1, "x") == False
+
+def test_streq_nonnull():
+    p1 = llstr("wor")
+    p2 = llstr("wor")
+    assert p1 != p2
+    func = LLtypeHelpers._ll_2_str_eq_nonnull.im_func
+    assert func(p1, p1) == True
+    assert func(p1, p2) == True
+    assert func(p1, llstr("wrl")) == False
+    assert func(p1, llstr("world")) == False
+    assert func(p1, llstr("w")) == False
+    py.test.raises(AttributeError, func, p1, llstr(None))
+    py.test.raises(AttributeError, func, llstr(None), p2)
+
+def test_streq_nonnull_char():
+    func = LLtypeHelpers._ll_2_str_eq_nonnull_char.im_func
+    assert func(llstr("wor"), "x") == False
+    assert func(llstr("w"), "x") == False
+    assert func(llstr(""), "x") == False
+    assert func(llstr("x"), "x") == True
+    py.test.raises(AttributeError, func, llstr(None), "x")
+
+def test_streq_checknull_char():
+    func = LLtypeHelpers._ll_2_str_eq_checknull_char.im_func
+    assert func(llstr("wor"), "x") == False
+    assert func(llstr("w"), "x") == False
+    assert func(llstr(""), "x") == False
+    assert func(llstr("x"), "x") == True
+    assert func(llstr(None), "x") == False
+
+def test_streq_lengthok():
+    p1 = llstr("wor")
+    p2 = llstr("wor")
+    assert p1 != p2
+    func = LLtypeHelpers._ll_2_str_eq_lengthok.im_func
+    assert func(p1, p1) == True
+    assert func(p1, p2) == True
+    assert func(p1, llstr("wrl")) == False
+    py.test.raises(IndexError, func, p1, llstr("w"))
+    py.test.raises(AttributeError, func, p1, llstr(None))
+    py.test.raises(AttributeError, func, llstr(None), p2)

Modified: pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/__init__.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/__init__.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/__init__.py	Sun Sep 26 13:56:56 2010
@@ -3,6 +3,7 @@
 from pypy.jit.metainterp.optimizeopt.intbounds import OptIntBounds
 from pypy.jit.metainterp.optimizeopt.virtualize import OptVirtualize
 from pypy.jit.metainterp.optimizeopt.heap import OptHeap
+from pypy.jit.metainterp.optimizeopt.string import OptString
 
 def optimize_loop_1(metainterp_sd, loop, virtuals=True):
     """Optimize loop.operations to make it match the input of loop.specnodes
@@ -13,6 +14,7 @@
     optimizations = [OptIntBounds(),
                      OptRewrite(),
                      OptVirtualize(),
+                     OptString(),
                      OptHeap(),
                     ]
     optimizer = Optimizer(metainterp_sd, loop, optimizations, virtuals)
@@ -23,4 +25,3 @@
     expect 'specnodes' on the bridge.
     """
     optimize_loop_1(metainterp_sd, bridge, False)
-        

Modified: pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/optimizer.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/optimizer.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/optimizer.py	Sun Sep 26 13:56:56 2010
@@ -9,10 +9,10 @@
 from pypy.jit.metainterp.optimizeutil import InvalidLoop, args_dict
 from pypy.jit.metainterp import resume, compile
 from pypy.jit.metainterp.typesystem import llhelper, oohelper
-from pypy.rpython.lltypesystem import lltype, rstr
+from pypy.rpython.lltypesystem import lltype
 from pypy.jit.metainterp.history import AbstractDescr, make_hashable_int
 from pypy.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded
-from pypy.rpython import annlowlevel
+from pypy.tool.pairtype import extendabletype
 
 LEVEL_UNKNOWN    = '\x00'
 LEVEL_NONNULL    = '\x01'
@@ -24,6 +24,7 @@
 MININT = -sys.maxint - 1
         
 class OptValue(object):
+    __metaclass__ = extendabletype
     _attrs_ = ('box', 'known_class', 'last_guard_index', 'level', 'intbound')
     last_guard_index = -1
 
@@ -127,27 +128,6 @@
     def setitem(self, index, value):
         raise NotImplementedError
 
-    def getstrlen(self, newoperations):
-        s = self.get_constant_string()
-        if s is not None:
-            return ConstInt(len(s))
-        else:
-            self.ensure_nonnull()
-            box = self.force_box()
-            lengthbox = BoxInt()
-            newoperations.append(ResOperation(rop.STRLEN, [box], lengthbox))
-            return lengthbox
-
-    def get_constant_string(self):
-        if self.is_constant():
-            s = self.box.getref(lltype.Ptr(rstr.STR))
-            return annlowlevel.hlstr(s)
-        else:
-            return None
-
-    def string_copy_parts(self, *args):
-        from pypy.jit.metainterp.optimizeopt import virtualize
-        return virtualize.default_string_copy_parts(self, *args)
 
 class ConstantValue(OptValue):
     def __init__(self, box):
@@ -273,6 +253,7 @@
         return None
 
     def make_equal_to(self, box, value):
+        assert isinstance(value, OptValue)
         assert box not in self.values
         self.values[box] = value
 

Added: pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/string.py
==============================================================================
--- (empty file)
+++ pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/string.py	Sun Sep 26 13:56:56 2010
@@ -0,0 +1,536 @@
+from pypy.rpython.lltypesystem import lltype, rstr, llmemory
+from pypy.rpython import annlowlevel
+from pypy.jit.metainterp.history import Box, BoxInt, BoxPtr
+from pypy.jit.metainterp.history import Const, ConstInt, ConstPtr
+from pypy.jit.metainterp.history import get_const_ptr_for_string
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.metainterp.optimizeopt import optimizer, virtualize
+from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1
+from pypy.jit.metainterp.optimizeopt.optimizer import llhelper
+from pypy.jit.metainterp.optimizeutil import _findall
+from pypy.jit.codewriter.effectinfo import EffectInfo, callinfo_for_oopspec
+from pypy.jit.codewriter import heaptracker
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib.objectmodel import missing_value
+
+
+class __extend__(optimizer.OptValue):
+    """New methods added to the base class OptValue for this file."""
+
+    def getstrlen(self, newoperations):
+        s = self.get_constant_string()
+        if s is not None:
+            return ConstInt(len(s))
+        else:
+            if newoperations is None:
+                return None
+            self.ensure_nonnull()
+            box = self.force_box()
+            lengthbox = BoxInt()
+            newoperations.append(ResOperation(rop.STRLEN, [box], lengthbox))
+            return lengthbox
+
+    def get_constant_string(self):
+        if self.is_constant():
+            s = self.box.getref(lltype.Ptr(rstr.STR))
+            return annlowlevel.hlstr(s)
+        else:
+            return None
+
+    def string_copy_parts(self, newoperations, targetbox, offsetbox):
+        # Copies the pointer-to-string 'self' into the target string
+        # given by 'targetbox', at the specified offset.  Returns the offset
+        # at the end of the copy.
+        lengthbox = self.getstrlen(newoperations)
+        srcbox = self.force_box()
+        return copy_str_content(newoperations, srcbox, targetbox,
+                                CONST_0, offsetbox, lengthbox)
+
+
+class VAbstractStringValue(virtualize.AbstractVirtualValue):
+
+    def _really_force(self):
+        s = self.get_constant_string()
+        if s is not None:
+            c_s = get_const_ptr_for_string(s)
+            self.make_constant(c_s)
+            return
+        assert self.source_op is not None
+        self.box = box = self.source_op.result
+        newoperations = self.optimizer.newoperations
+        lengthbox = self.getstrlen(newoperations)
+        newoperations.append(ResOperation(rop.NEWSTR, [lengthbox], box))
+        self.string_copy_parts(newoperations, box, CONST_0)
+
+
+class VStringPlainValue(VAbstractStringValue):
+    """A string built with newstr(const)."""
+    _lengthbox = None     # cache only
+
+    def setup(self, size):
+        self._chars = [optimizer.CVAL_UNINITIALIZED_ZERO] * size
+
+    def setup_slice(self, longerlist, start, stop):
+        assert 0 <= start <= stop <= len(longerlist)
+        self._chars = longerlist[start:stop]
+
+    def getstrlen(self, _):
+        if self._lengthbox is None:
+            self._lengthbox = ConstInt(len(self._chars))
+        return self._lengthbox
+
+    def getitem(self, index):
+        return self._chars[index]
+
+    def setitem(self, index, charvalue):
+        assert isinstance(charvalue, optimizer.OptValue)
+        self._chars[index] = charvalue
+
+    def get_constant_string(self):
+        for c in self._chars:
+            if c is optimizer.CVAL_UNINITIALIZED_ZERO or not c.is_constant():
+                return None
+        return ''.join([chr(c.box.getint()) for c in self._chars])
+
+    def string_copy_parts(self, newoperations, targetbox, offsetbox):
+        for i in range(len(self._chars)):
+            charbox = self._chars[i].force_box()
+            newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
+                                                               offsetbox,
+                                                               charbox], None))
+            offsetbox = _int_add(newoperations, offsetbox, CONST_1)
+        return offsetbox
+
+    def get_args_for_fail(self, modifier):
+        if self.box is None and not modifier.already_seen_virtual(self.keybox):
+            charboxes = [value.get_key_box() for value in self._chars]
+            modifier.register_virtual_fields(self.keybox, charboxes)
+            for value in self._chars:
+                value.get_args_for_fail(modifier)
+
+    def _make_virtual(self, modifier):
+        return modifier.make_vstrplain()
+
+
+class VStringConcatValue(VAbstractStringValue):
+    """The concatenation of two other strings."""
+
+    def setup(self, left, right, lengthbox):
+        self.left = left
+        self.right = right
+        self.lengthbox = lengthbox
+
+    def getstrlen(self, _):
+        return self.lengthbox
+
+    def get_constant_string(self):
+        s1 = self.left.get_constant_string()
+        if s1 is None:
+            return None
+        s2 = self.right.get_constant_string()
+        if s2 is None:
+            return None
+        return s1 + s2
+
+    def string_copy_parts(self, newoperations, targetbox, offsetbox):
+        offsetbox = self.left.string_copy_parts(newoperations, targetbox,
+                                                offsetbox)
+        offsetbox = self.right.string_copy_parts(newoperations, targetbox,
+                                                 offsetbox)
+        return offsetbox
+
+    def get_args_for_fail(self, modifier):
+        if self.box is None and not modifier.already_seen_virtual(self.keybox):
+            # we don't store the lengthvalue in guards, because the
+            # guard-failed code starts with a regular STR_CONCAT again
+            leftbox = self.left.get_key_box()
+            rightbox = self.right.get_key_box()
+            modifier.register_virtual_fields(self.keybox, [leftbox, rightbox])
+            self.left.get_args_for_fail(modifier)
+            self.right.get_args_for_fail(modifier)
+
+    def _make_virtual(self, modifier):
+        return modifier.make_vstrconcat()
+
+
+class VStringSliceValue(VAbstractStringValue):
+    """A slice."""
+    vstr = vstart = vlength = missing_value     # annotator fix
+
+    def setup(self, vstr, vstart, vlength):
+        self.vstr = vstr
+        self.vstart = vstart
+        self.vlength = vlength
+
+    def getstrlen(self, _):
+        return self.vlength.force_box()
+
+    def get_constant_string(self):
+        if self.vstart.is_constant() and self.vlength.is_constant():
+            s1 = self.vstr.get_constant_string()
+            if s1 is None:
+                return None
+            start = self.vstart.box.getint()
+            length = self.vlength.box.getint()
+            return s1[start : start + length]
+        return None
+
+    def string_copy_parts(self, newoperations, targetbox, offsetbox):
+        lengthbox = self.getstrlen(newoperations)
+        return copy_str_content(newoperations,
+                                self.vstr.force_box(), targetbox,
+                                self.vstart.force_box(), offsetbox,
+                                lengthbox)
+
+    def get_args_for_fail(self, modifier):
+        if self.box is None and not modifier.already_seen_virtual(self.keybox):
+            boxes = [self.vstr.get_key_box(),
+                     self.vstart.get_key_box(),
+                     self.vlength.get_key_box()]
+            modifier.register_virtual_fields(self.keybox, boxes)
+            self.vstr.get_args_for_fail(modifier)
+            self.vstart.get_args_for_fail(modifier)
+            self.vlength.get_args_for_fail(modifier)
+
+    def _make_virtual(self, modifier):
+        return modifier.make_vstrslice()
+
+
+def copy_str_content(newoperations, srcbox, targetbox,
+                     srcoffsetbox, offsetbox, lengthbox):
+    if isinstance(srcbox, ConstPtr) and isinstance(srcoffsetbox, Const):
+        M = 5
+    else:
+        M = 2
+    if isinstance(lengthbox, ConstInt) and lengthbox.value <= M:
+        # up to M characters are done "inline", i.e. with STRGETITEM/STRSETITEM
+        # instead of just a COPYSTRCONTENT.
+        for i in range(lengthbox.value):
+            charbox = _strgetitem(newoperations, srcbox, srcoffsetbox)
+            srcoffsetbox = _int_add(newoperations, srcoffsetbox, CONST_1)
+            newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
+                                                               offsetbox,
+                                                               charbox], None))
+            offsetbox = _int_add(newoperations, offsetbox, CONST_1)
+    else:
+        nextoffsetbox = _int_add(newoperations, offsetbox, lengthbox)
+        op = ResOperation(rop.COPYSTRCONTENT, [srcbox, targetbox,
+                                               srcoffsetbox, offsetbox,
+                                               lengthbox], None)
+        newoperations.append(op)
+        offsetbox = nextoffsetbox
+    return offsetbox
+
+def _int_add(newoperations, box1, box2):
+    if isinstance(box1, ConstInt):
+        if box1.value == 0:
+            return box2
+        if isinstance(box2, ConstInt):
+            return ConstInt(box1.value + box2.value)
+    elif isinstance(box2, ConstInt) and box2.value == 0:
+        return box1
+    resbox = BoxInt()
+    newoperations.append(ResOperation(rop.INT_ADD, [box1, box2], resbox))
+    return resbox
+
+def _int_sub(newoperations, box1, box2):
+    if isinstance(box2, ConstInt):
+        if box2.value == 0:
+            return box1
+        if isinstance(box1, ConstInt):
+            return ConstInt(box1.value - box2.value)
+    resbox = BoxInt()
+    newoperations.append(ResOperation(rop.INT_SUB, [box1, box2], resbox))
+    return resbox
+
+def _strgetitem(newoperations, strbox, indexbox):
+    if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
+        s = strbox.getref(lltype.Ptr(rstr.STR))
+        return ConstInt(ord(s.chars[indexbox.getint()]))
+    resbox = BoxInt()
+    newoperations.append(ResOperation(rop.STRGETITEM, [strbox, indexbox],
+                                      resbox))
+    return resbox
+
+
+class OptString(optimizer.Optimization):
+    "Handling of strings and unicodes."
+
+    def make_vstring_plain(self, box, source_op=None):
+        vvalue = VStringPlainValue(self.optimizer, box, source_op)
+        self.make_equal_to(box, vvalue)
+        return vvalue
+
+    def make_vstring_concat(self, box, source_op=None):
+        vvalue = VStringConcatValue(self.optimizer, box, source_op)
+        self.make_equal_to(box, vvalue)
+        return vvalue
+
+    def make_vstring_slice(self, box, source_op=None):
+        vvalue = VStringSliceValue(self.optimizer, box, source_op)
+        self.make_equal_to(box, vvalue)
+        return vvalue
+
+    def optimize_CALL(self, op):
+        # dispatch based on 'oopspecindex' to a method that handles
+        # specifically the given oopspec call.  For non-oopspec calls,
+        # oopspecindex is just zero.
+        effectinfo = op.descr.get_extra_info()
+        if effectinfo is not None:
+            oopspecindex = effectinfo.oopspecindex
+            for value, meth in opt_call_oopspec_ops:
+                if oopspecindex == value:
+                    if meth(self, op):
+                        return
+        self.emit_operation(op)
+
+    def opt_call_oopspec_ARRAYCOPY(self, op):
+        source_value = self.getvalue(op.args[1])
+        dest_value = self.getvalue(op.args[2])
+        source_start_box = self.get_constant_box(op.args[3])
+        dest_start_box = self.get_constant_box(op.args[4])
+        length = self.get_constant_box(op.args[5])
+        if (source_value.is_virtual() and source_start_box and dest_start_box
+            and length and dest_value.is_virtual()):
+            # XXX optimize the case where dest value is not virtual,
+            #     but we still can avoid a mess
+            source_start = source_start_box.getint()
+            dest_start = dest_start_box.getint()
+            for index in range(length.getint()):
+                val = source_value.getitem(index + source_start)
+                dest_value.setitem(index + dest_start, val)
+            return True
+        if length and length.getint() == 0:
+            return True # 0-length arraycopy
+        return False
+
+    def optimize_NEWSTR(self, op):
+        length_box = self.get_constant_box(op.args[0])
+        if length_box:
+            # if the original 'op' did not have a ConstInt as argument,
+            # build a new one with the ConstInt argument
+            if not isinstance(op.args[0], ConstInt):
+                op = ResOperation(rop.NEWSTR, [length_box], op.result)
+            vvalue = self.make_vstring_plain(op.result, op)
+            vvalue.setup(length_box.getint())
+        else:
+            self.getvalue(op.result).ensure_nonnull()
+            self.emit_operation(op)
+
+    def optimize_STRSETITEM(self, op):
+        value = self.getvalue(op.args[0])
+        if value.is_virtual() and isinstance(value, VStringPlainValue):
+            indexbox = self.get_constant_box(op.args[1])
+            if indexbox is not None:
+                value.setitem(indexbox.getint(), self.getvalue(op.args[2]))
+                return
+        value.ensure_nonnull()
+        self.emit_operation(op)
+
+    def optimize_STRGETITEM(self, op):
+        value = self.getvalue(op.args[0])
+        vindex = self.getvalue(op.args[1])
+        vresult = self.strgetitem(value, vindex)
+        self.make_equal_to(op.result, vresult)
+
+    def strgetitem(self, value, vindex):
+        value.ensure_nonnull()
+        #
+        if value.is_virtual() and isinstance(value, VStringSliceValue):
+            fullindexbox = _int_add(self.optimizer.newoperations,
+                                    value.vstart.force_box(),
+                                    vindex.force_box())
+            value = value.vstr
+            vindex = self.getvalue(fullindexbox)
+        #
+        if isinstance(value, VStringPlainValue):  # even if no longer virtual
+            if vindex.is_constant():
+                return value.getitem(vindex.box.getint())
+        #
+        resbox = _strgetitem(self.optimizer.newoperations,
+                             value.force_box(),vindex.force_box())
+        return self.getvalue(resbox)
+
+    def optimize_STRLEN(self, op):
+        value = self.getvalue(op.args[0])
+        lengthbox = value.getstrlen(self.optimizer.newoperations)
+        self.make_equal_to(op.result, self.getvalue(lengthbox))
+
+    def opt_call_oopspec_STR_CONCAT(self, op):
+        vleft = self.getvalue(op.args[1])
+        vright = self.getvalue(op.args[2])
+        vleft.ensure_nonnull()
+        vright.ensure_nonnull()
+        newoperations = self.optimizer.newoperations
+        len1box = vleft.getstrlen(newoperations)
+        len2box = vright.getstrlen(newoperations)
+        lengthbox = _int_add(newoperations, len1box, len2box)
+        value = self.make_vstring_concat(op.result, op)
+        value.setup(vleft, vright, lengthbox)
+        return True
+
+    def opt_call_oopspec_STR_SLICE(self, op):
+        newoperations = self.optimizer.newoperations
+        vstr = self.getvalue(op.args[1])
+        vstart = self.getvalue(op.args[2])
+        vstop = self.getvalue(op.args[3])
+        #
+        if (isinstance(vstr, VStringPlainValue) and vstart.is_constant()
+            and vstop.is_constant()):
+            # slicing with constant bounds of a VStringPlainValue
+            value = self.make_vstring_plain(op.result, op)
+            value.setup_slice(vstr._chars, vstart.box.getint(),
+                                           vstop.box.getint())
+            return True
+        #
+        vstr.ensure_nonnull()
+        if isinstance(vstr, VStringSliceValue):
+            # double slicing  s[i:j][k:l]
+            vintermediate = vstr
+            vstr = vintermediate.vstr
+            startbox = _int_add(newoperations,
+                                vintermediate.vstart.force_box(),
+                                vstart.force_box())
+            vstart = self.getvalue(startbox)
+        #
+        lengthbox = _int_sub(newoperations, vstop.force_box(),
+                                            vstart.force_box())
+        value = self.make_vstring_slice(op.result, op)
+        value.setup(vstr, vstart, self.getvalue(lengthbox))
+        return True
+
+    def opt_call_oopspec_STR_EQUAL(self, op):
+        v1 = self.getvalue(op.args[1])
+        v2 = self.getvalue(op.args[2])
+        #
+        l1box = v1.getstrlen(None)
+        l2box = v2.getstrlen(None)
+        if (l1box is not None and l2box is not None and
+            isinstance(l1box, ConstInt) and
+            isinstance(l2box, ConstInt) and
+            l1box.value != l2box.value):
+            # statically known to have a different length
+            self.make_constant(op.result, CONST_0)
+            return True
+        #
+        if self.handle_str_equal_level1(v1, v2, op.result):
+            return True
+        if self.handle_str_equal_level1(v2, v1, op.result):
+            return True
+        if self.handle_str_equal_level2(v1, v2, op.result):
+            return True
+        if self.handle_str_equal_level2(v2, v1, op.result):
+            return True
+        #
+        if v1.is_nonnull() and v2.is_nonnull():
+            if l1box is not None and l2box is not None and (
+                l1box == l2box or (isinstance(l1box, ConstInt) and
+                                   isinstance(l2box, ConstInt) and
+                                   l1box.value == l2box.value)):
+                do = EffectInfo.OS_STREQ_LENGTHOK
+            else:
+                do = EffectInfo.OS_STREQ_NONNULL
+            self.generate_modified_call(do, [v1.force_box(),
+                                             v2.force_box()], op.result)
+            return True
+        return False
+
+    def handle_str_equal_level1(self, v1, v2, resultbox):
+        l2box = v2.getstrlen(None)
+        if isinstance(l2box, ConstInt):
+            if l2box.value == 0:
+                lengthbox = v1.getstrlen(self.optimizer.newoperations)
+                seo = self.optimizer.send_extra_operation
+                seo(ResOperation(rop.INT_EQ, [lengthbox, CONST_0], resultbox))
+                return True
+            if l2box.value == 1:
+                l1box = v1.getstrlen(None)
+                if isinstance(l1box, ConstInt) and l1box.value == 1:
+                    # comparing two single chars
+                    vchar1 = self.strgetitem(v1, optimizer.CVAL_ZERO)
+                    vchar2 = self.strgetitem(v2, optimizer.CVAL_ZERO)
+                    seo = self.optimizer.send_extra_operation
+                    seo(ResOperation(rop.INT_EQ, [vchar1.force_box(),
+                                                  vchar2.force_box()],
+                                     resultbox))
+                    return True
+                if isinstance(v1, VStringSliceValue):
+                    vchar = self.strgetitem(v2, optimizer.CVAL_ZERO)
+                    do = EffectInfo.OS_STREQ_SLICE_CHAR
+                    self.generate_modified_call(do, [v1.vstr.force_box(),
+                                                     v1.vstart.force_box(),
+                                                     v1.vlength.force_box(),
+                                                     vchar.force_box()],
+                                                resultbox)
+                    return True
+        #
+        if v2.is_null():
+            if v1.is_nonnull():
+                self.make_constant(resultbox, CONST_0)
+                return True
+            if v1.is_null():
+                self.make_constant(resultbox, CONST_1)
+                return True
+            op = ResOperation(rop.PTR_EQ, [v1.force_box(),
+                                           llhelper.CONST_NULL],
+                              resultbox)
+            self.optimizer.newoperations.append(op)
+            return True
+        #
+        return False
+
+    def handle_str_equal_level2(self, v1, v2, resultbox):
+        l2box = v2.getstrlen(None)
+        if isinstance(l2box, ConstInt):
+            if l2box.value == 1:
+                vchar = self.strgetitem(v2, optimizer.CVAL_ZERO)
+                if v1.is_nonnull():
+                    do = EffectInfo.OS_STREQ_NONNULL_CHAR
+                else:
+                    do = EffectInfo.OS_STREQ_CHECKNULL_CHAR
+                self.generate_modified_call(do, [v1.force_box(),
+                                                 vchar.force_box()], resultbox)
+                return True
+        #
+        if v1.is_virtual() and isinstance(v1, VStringSliceValue):
+            if v2.is_nonnull():
+                do = EffectInfo.OS_STREQ_SLICE_NONNULL
+            else:
+                do = EffectInfo.OS_STREQ_SLICE_CHECKNULL
+            self.generate_modified_call(do, [v1.vstr.force_box(),
+                                             v1.vstart.force_box(),
+                                             v1.vlength.force_box(),
+                                             v2.force_box()], resultbox)
+            return True
+        return False
+
+    def generate_modified_call(self, oopspecindex, args, result):
+        calldescr, func = callinfo_for_oopspec(oopspecindex)
+        func = llmemory.cast_ptr_to_adr(func)
+        func = heaptracker.adr2int(func)
+        op = ResOperation(rop.CALL, [ConstInt(func)] + args, result,
+                          descr=calldescr)
+        self.optimizer.newoperations.append(op)
+    generate_modified_call._annspecialcase_ = 'specialize:arg(1)'
+
+    def propagate_forward(self, op):
+        opnum = op.opnum
+        for value, func in optimize_ops:
+            if opnum == value:
+                func(self, op)
+                break
+        else:
+            self.emit_operation(op)
+
+optimize_ops = _findall(OptString, 'optimize_')
+
+def _findall_call_oopspec():
+    prefix = 'opt_call_oopspec_'
+    result = []
+    for name in dir(OptString):
+        if name.startswith(prefix):
+            value = getattr(EffectInfo, 'OS_' + name[len(prefix):])
+            assert isinstance(value, int) and value != 0
+            result.append((value, getattr(OptString, name)))
+    return unrolling_iterable(result)
+opt_call_oopspec_ops = _findall_call_oopspec()

Modified: pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/virtualize.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/virtualize.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/virtualize.py	Sun Sep 26 13:56:56 2010
@@ -5,11 +5,8 @@
 from pypy.jit.metainterp.specnode import VirtualStructSpecNode
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.jit.metainterp.optimizeutil import _findall
-from pypy.jit.metainterp.history import get_const_ptr_for_string
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.jit.metainterp.optimizeopt.optimizer import *
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.rlib.unroll import unrolling_iterable
 
 
 class AbstractVirtualValue(OptValue):
@@ -197,217 +194,6 @@
         return modifier.make_varray(self.arraydescr)
 
 
-class VAbstractStringValue(AbstractVirtualValue):
-
-    def _really_force(self):
-        s = self.get_constant_string()
-        if s is not None:
-            c_s = get_const_ptr_for_string(s)
-            self.make_constant(c_s)
-            return
-        assert self.source_op is not None
-        self.box = box = self.source_op.result
-        newoperations = self.optimizer.newoperations
-        lengthbox = self.getstrlen(newoperations)
-        newoperations.append(ResOperation(rop.NEWSTR, [lengthbox], box))
-        self.string_copy_parts(newoperations, box, CONST_0)
-
-
-class VStringPlainValue(VAbstractStringValue):
-    """A string built with newstr(const)."""
-    _lengthbox = None     # cache only
-
-    def setup(self, size):
-        self._chars = [CVAL_UNINITIALIZED_ZERO] * size
-
-    def getstrlen(self, _):
-        if self._lengthbox is None:
-            self._lengthbox = ConstInt(len(self._chars))
-        return self._lengthbox
-
-    def getitem(self, index):
-        return self._chars[index]
-
-    def setitem(self, index, charvalue):
-        assert isinstance(charvalue, OptValue)
-        self._chars[index] = charvalue
-
-    def get_constant_string(self):
-        for c in self._chars:
-            if c is CVAL_UNINITIALIZED_ZERO or not c.is_constant():
-                return None
-        return ''.join([chr(c.box.getint()) for c in self._chars])
-
-    def string_copy_parts(self, newoperations, targetbox, offsetbox):
-        for i in range(len(self._chars)):
-            charbox = self._chars[i].force_box()
-            newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
-                                                               offsetbox,
-                                                               charbox], None))
-            offsetbox = _int_add(newoperations, offsetbox, CONST_1)
-        return offsetbox
-
-    def get_args_for_fail(self, modifier):
-        if self.box is None and not modifier.already_seen_virtual(self.keybox):
-            charboxes = [value.get_key_box() for value in self._chars]
-            modifier.register_virtual_fields(self.keybox, charboxes)
-            for value in self._chars:
-                value.get_args_for_fail(modifier)
-
-    def _make_virtual(self, modifier):
-        return modifier.make_vstrplain()
-
-
-class VStringConcatValue(VAbstractStringValue):
-    """The concatenation of two other strings."""
-
-    def setup(self, left, right, lengthbox):
-        self.left = left
-        self.right = right
-        self.lengthbox = lengthbox
-
-    def getstrlen(self, _):
-        return self.lengthbox
-
-    def get_constant_string(self):
-        s1 = self.left.get_constant_string()
-        if s1 is None:
-            return None
-        s2 = self.right.get_constant_string()
-        if s2 is None:
-            return None
-        return s1 + s2
-
-    def string_copy_parts(self, newoperations, targetbox, offsetbox):
-        offsetbox = self.left.string_copy_parts(newoperations, targetbox,
-                                                offsetbox)
-        offsetbox = self.right.string_copy_parts(newoperations, targetbox,
-                                                 offsetbox)
-        return offsetbox
-
-    def get_args_for_fail(self, modifier):
-        if self.box is None and not modifier.already_seen_virtual(self.keybox):
-            # we don't store the lengthvalue in guards, because the
-            # guard-failed code starts with a regular STR_CONCAT again
-            leftbox = self.left.get_key_box()
-            rightbox = self.right.get_key_box()
-            modifier.register_virtual_fields(self.keybox, [leftbox, rightbox])
-            self.left.get_args_for_fail(modifier)
-            self.right.get_args_for_fail(modifier)
-
-    def _make_virtual(self, modifier):
-        return modifier.make_vstrconcat()
-
-
-class VStringSliceValue(VAbstractStringValue):
-    """A slice."""
-
-    def setup(self, vstr, vstart, vlength):
-        self.vstr = vstr
-        self.vstart = vstart
-        self.vlength = vlength
-
-    def getstrlen(self, newoperations):
-        return self.vlength.force_box()
-
-    def get_constant_string(self):
-        if self.vstart.is_constant() and self.vlength.is_constant():
-            s1 = self.vstr.get_constant_string()
-            if s1 is None:
-                return None
-            start = self.vstart.box.getint()
-            length = self.vlength.box.getint()
-            return s1[start : start + length]
-        return None
-
-    def string_copy_parts(self, newoperations, targetbox, offsetbox):
-        lengthbox = self.getstrlen(newoperations)
-        return copy_str_content(newoperations,
-                                self.vstr.force_box(), targetbox,
-                                self.vstart.force_box(), offsetbox,
-                                lengthbox)
-
-    def get_args_for_fail(self, modifier):
-        if self.box is None and not modifier.already_seen_virtual(self.keybox):
-            boxes = [self.vstr.get_key_box(),
-                     self.vstart.get_key_box(),
-                     self.vlength.get_key_box()]
-            modifier.register_virtual_fields(self.keybox, boxes)
-            self.vstr.get_args_for_fail(modifier)
-            self.vstart.get_args_for_fail(modifier)
-            self.vlength.get_args_for_fail(modifier)
-
-    def _make_virtual(self, modifier):
-        return modifier.make_vstrslice()
-
-
-def default_string_copy_parts(srcvalue, newoperations, targetbox, offsetbox):
-    # Copies the pointer-to-string 'srcvalue' into the target string
-    # given by 'targetbox', at the specified offset.  Returns the offset
-    # at the end of the copy.
-    lengthbox = srcvalue.getstrlen(newoperations)
-    srcbox = srcvalue.force_box()
-    return copy_str_content(newoperations, srcbox, targetbox,
-                            CONST_0, offsetbox, lengthbox)
-
-def copy_str_content(newoperations, srcbox, targetbox,
-                     srcoffsetbox, offsetbox, lengthbox):
-    if isinstance(srcbox, ConstPtr) and isinstance(srcoffsetbox, Const):
-        M = 5
-    else:
-        M = 2
-    if isinstance(lengthbox, ConstInt) and lengthbox.value <= M:
-        # up to M characters are done "inline", i.e. with STRGETITEM/STRSETITEM
-        # instead of just a COPYSTRCONTENT.
-        for i in range(lengthbox.value):
-            charbox = _strgetitem(newoperations, srcbox, srcoffsetbox)
-            srcoffsetbox = _int_add(newoperations, srcoffsetbox, CONST_1)
-            newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
-                                                               offsetbox,
-                                                               charbox], None))
-            offsetbox = _int_add(newoperations, offsetbox, CONST_1)
-    else:
-        nextoffsetbox = _int_add(newoperations, offsetbox, lengthbox)
-        op = ResOperation(rop.COPYSTRCONTENT, [srcbox, targetbox,
-                                               srcoffsetbox, offsetbox,
-                                               lengthbox], None)
-        newoperations.append(op)
-        offsetbox = nextoffsetbox
-    return offsetbox
-
-def _int_add(newoperations, box1, box2):
-    if isinstance(box1, ConstInt):
-        if box1.value == 0:
-            return box2
-        if isinstance(box2, ConstInt):
-            return ConstInt(box1.value + box2.value)
-    elif isinstance(box2, ConstInt) and box2.value == 0:
-        return box1
-    resbox = BoxInt()
-    newoperations.append(ResOperation(rop.INT_ADD, [box1, box2], resbox))
-    return resbox
-
-def _int_sub(newoperations, box1, box2):
-    if isinstance(box2, ConstInt):
-        if box2.value == 0:
-            return box1
-        if isinstance(box1, ConstInt):
-            return ConstInt(box1.value - box2.value)
-    resbox = BoxInt()
-    newoperations.append(ResOperation(rop.INT_SUB, [box1, box2], resbox))
-    return resbox
-
-def _strgetitem(newoperations, strbox, indexbox):
-    # hum, this repetition of the operations is not quite right
-    if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
-        s = strbox.getref(lltype.Ptr(rstr.STR))
-        return ConstInt(ord(s.chars[indexbox.getint()]))
-    resbox = BoxInt()
-    newoperations.append(ResOperation(rop.STRGETITEM, [strbox, indexbox],
-                                      resbox))
-    return resbox
-
-
 class __extend__(SpecNode):
     def setup_virtual_node(self, optimizer, box, newinputargs):
         raise NotImplementedError
@@ -496,21 +282,6 @@
         self.make_equal_to(box, vvalue)
         return vvalue
 
-    def make_vstring_plain(self, box, source_op=None):
-        vvalue = VStringPlainValue(self.optimizer, box, source_op)
-        self.make_equal_to(box, vvalue)
-        return vvalue
-
-    def make_vstring_concat(self, box, source_op=None):
-        vvalue = VStringConcatValue(self.optimizer, box, source_op)
-        self.make_equal_to(box, vvalue)
-        return vvalue
-
-    def make_vstring_slice(self, box, source_op=None):
-        vvalue = VStringSliceValue(self.optimizer, box, source_op)
-        self.make_equal_to(box, vvalue)
-        return vvalue
-
     def optimize_JUMP(self, op):
         orgop = self.optimizer.loop.operations[-1]
         exitargs = []
@@ -649,113 +420,6 @@
         ###self.heap_op_optimizer.optimize_SETARRAYITEM_GC(op, value, fieldvalue)
         self.emit_operation(op)
 
-    def optimize_CALL(self, op):
-        # dispatch based on 'oopspecindex' to a method that handles
-        # specifically the given oopspec call.  For non-oopspec calls,
-        # oopspecindex is just zero.
-        effectinfo = op.descr.get_extra_info()
-        if effectinfo is not None:
-            oopspecindex = effectinfo.oopspecindex
-            for value, meth in opt_call_oopspec_ops:
-                if oopspecindex == value:
-                    if meth(self, op):
-                        return
-        self.emit_operation(op)
-
-    def opt_call_oopspec_ARRAYCOPY(self, op):
-        source_value = self.getvalue(op.args[1])
-        dest_value = self.getvalue(op.args[2])
-        source_start_box = self.get_constant_box(op.args[3])
-        dest_start_box = self.get_constant_box(op.args[4])
-        length = self.get_constant_box(op.args[5])
-        if (source_value.is_virtual() and source_start_box and dest_start_box
-            and length and dest_value.is_virtual()):
-            # XXX optimize the case where dest value is not virtual,
-            #     but we still can avoid a mess
-            source_start = source_start_box.getint()
-            dest_start = dest_start_box.getint()
-            for index in range(length.getint()):
-                val = source_value.getitem(index + source_start)
-                dest_value.setitem(index + dest_start, val)
-            return True
-        if length and length.getint() == 0:
-            return True # 0-length arraycopy
-        return False
-
-    def optimize_NEWSTR(self, op):
-        length_box = self.get_constant_box(op.args[0])
-        if length_box:
-            # if the original 'op' did not have a ConstInt as argument,
-            # build a new one with the ConstInt argument
-            if not isinstance(op.args[0], ConstInt):
-                op = ResOperation(rop.NEWSTR, [length_box], op.result)
-            vvalue = self.make_vstring_plain(op.result, op)
-            vvalue.setup(length_box.getint())
-        else:
-            self.getvalue(op.result).ensure_nonnull()
-            self.emit_operation(op)
-
-    def optimize_STRSETITEM(self, op):
-        value = self.getvalue(op.args[0])
-        if value.is_virtual() and isinstance(value, VStringPlainValue):
-            indexbox = self.get_constant_box(op.args[1])
-            if indexbox is not None:
-                value.setitem(indexbox.getint(), self.getvalue(op.args[2]))
-                return
-        value.ensure_nonnull()
-        self.emit_operation(op)
-
-    def optimize_STRGETITEM(self, op):
-        value = self.getvalue(op.args[0])
-        if isinstance(value, VStringPlainValue):  # even if no longer virtual
-            indexbox = self.get_constant_box(op.args[1])
-            if indexbox is not None:
-                charvalue = value.getitem(indexbox.getint())
-                self.make_equal_to(op.result, charvalue)
-                return
-        value.ensure_nonnull()
-        self.emit_operation(op)
-
-    def optimize_STRLEN(self, op):
-        value = self.getvalue(op.args[0])
-        lengthbox = value.getstrlen(self.optimizer.newoperations)
-        self.make_equal_to(op.result, self.getvalue(lengthbox))
-
-    def opt_call_oopspec_STR_CONCAT(self, op):
-        vleft = self.getvalue(op.args[1])
-        vright = self.getvalue(op.args[2])
-        vleft.ensure_nonnull()
-        vright.ensure_nonnull()
-        newoperations = self.optimizer.newoperations
-        len1box = vleft.getstrlen(newoperations)
-        len2box = vright.getstrlen(newoperations)
-        lengthbox = _int_add(newoperations, len1box, len2box)
-        value = self.make_vstring_concat(op.result, op)
-        value.setup(vleft, vright, lengthbox)
-        return True
-
-    def opt_call_oopspec_STR_SLICE(self, op):
-        newoperations = self.optimizer.newoperations
-        vstr = self.getvalue(op.args[1])
-        vstart = self.getvalue(op.args[2])
-        vstop = self.getvalue(op.args[3])
-        vstr.ensure_nonnull()
-        lengthbox = _int_sub(newoperations, vstop.force_box(),
-                                            vstart.force_box())
-        value = self.make_vstring_slice(op.result, op)
-        #
-        if isinstance(vstr, VStringSliceValue):
-            # double slicing  s[i:j][k:l]
-            vintermediate = vstr
-            vstr = vintermediate.vstr
-            startbox = _int_add(newoperations,
-                                vintermediate.vstart.force_box(),
-                                vstart.force_box())
-            vstart = self.getvalue(startbox)
-        #
-        value.setup(vstr, vstart, self.getvalue(lengthbox))
-        return True
-
     def propagate_forward(self, op):
         opnum = op.opnum
         for value, func in optimize_ops:
@@ -766,14 +430,3 @@
             self.emit_operation(op)
 
 optimize_ops = _findall(OptVirtualize, 'optimize_')
-
-def _findall_call_oopspec():
-    prefix = 'opt_call_oopspec_'
-    result = []
-    for name in dir(OptVirtualize):
-        if name.startswith(prefix):
-            value = getattr(EffectInfo, 'OS_' + name[len(prefix):])
-            assert isinstance(value, int) and value != 0
-            result.append((value, getattr(OptVirtualize, name)))
-    return unrolling_iterable(result)
-opt_call_oopspec_ops = _findall_call_oopspec()

Modified: pypy/branch/jit-str/pypy/jit/metainterp/resume.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/resume.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/resume.py	Sun Sep 26 13:56:56 2010
@@ -9,7 +9,7 @@
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rstr
 from pypy.rlib import rarithmetic
 from pypy.rlib.objectmodel import we_are_translated, specialize
-from pypy.rlib.debug import have_debug_prints
+from pypy.rlib.debug import have_debug_prints, ll_assert
 from pypy.rlib.debug import debug_start, debug_stop, debug_print
 
 # Logic to encode the chain of frames and the state of the boxes at a
@@ -408,9 +408,7 @@
 
 
 class AbstractVirtualInfo(object):
-    #def allocate(self, metainterp):
-    #    raise NotImplementedError
-    #def setfields(self, decoder, struct):
+    #def allocate(self, decoder, index):
     #    raise NotImplementedError
     def equals(self, fieldnums):
         return tagged_list_eq(self.fieldnums, fieldnums)
@@ -430,6 +428,7 @@
         for i in range(len(self.fielddescrs)):
             descr = self.fielddescrs[i]
             decoder.setfield(descr, struct, self.fieldnums[i])
+        return struct
 
     def debug_prints(self):
         assert len(self.fielddescrs) == len(self.fieldnums)
@@ -444,8 +443,10 @@
         self.known_class = known_class
 
     @specialize.argtype(1)
-    def allocate(self, decoder):
-        return decoder.allocate_with_vtable(self.known_class)
+    def allocate(self, decoder, index):
+        struct = decoder.allocate_with_vtable(self.known_class)
+        decoder.virtuals_cache[index] = struct
+        return self.setfields(decoder, struct)
 
     def debug_prints(self):
         debug_print("\tvirtualinfo", self.known_class.repr_rpython())
@@ -457,8 +458,10 @@
         self.typedescr = typedescr
 
     @specialize.argtype(1)
-    def allocate(self, decoder):
-        return decoder.allocate_struct(self.typedescr)
+    def allocate(self, decoder, index):
+        struct = decoder.allocate_struct(self.typedescr)
+        decoder.virtuals_cache[index] = struct
+        return self.setfields(decoder, struct)
 
     def debug_prints(self):
         debug_print("\tvstructinfo", self.typedescr.repr_rpython())
@@ -470,14 +473,11 @@
         #self.fieldnums = ...
 
     @specialize.argtype(1)
-    def allocate(self, decoder):
+    def allocate(self, decoder, index):
         length = len(self.fieldnums)
-        return decoder.allocate_array(self.arraydescr, length)
-
-    @specialize.argtype(1)
-    def setfields(self, decoder, array):
         arraydescr = self.arraydescr
-        length = len(self.fieldnums)
+        array = decoder.allocate_array(arraydescr, length)
+        decoder.virtuals_cache[index] = array
         # NB. the check for the kind of array elements is moved out of the loop
         if arraydescr.is_array_of_pointers():
             for i in range(length):
@@ -491,25 +491,25 @@
             for i in range(length):
                 decoder.setarrayitem_int(arraydescr, array, i,
                                          self.fieldnums[i])
+        return array
 
     def debug_prints(self):
         debug_print("\tvarrayinfo", self.arraydescr)
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
+
 class VStrPlainInfo(AbstractVirtualInfo):
     """Stands for the string made out of the characters of all fieldnums."""
 
     @specialize.argtype(1)
-    def allocate(self, decoder):
-        length = len(self.fieldnums)
-        return decoder.allocate_string(length)
-
-    @specialize.argtype(1)
-    def setfields(self, decoder, string):
+    def allocate(self, decoder, index):
         length = len(self.fieldnums)
+        string = decoder.allocate_string(length)
+        decoder.virtuals_cache[index] = string
         for i in range(length):
-            decoder.strsetitem(string, i, self.fieldnums[i])
+            decoder.string_setitem(string, i, self.fieldnums[i])
+        return string
 
     def debug_prints(self):
         debug_print("\tvstrplaininfo length", len(self.fieldnums))
@@ -520,18 +520,14 @@
     other strings."""
 
     @specialize.argtype(1)
-    def allocate(self, decoder):
+    def allocate(self, decoder, index):
         # xxx for blackhole resuming, this will build all intermediate
         # strings and throw them away immediately, which is a bit sub-
         # efficient.  Not sure we care.
         left, right = self.fieldnums
-        return decoder.concat_strings(left, right)
-
-    @specialize.argtype(1)
-    def setfields(self, decoder, string):
-        # we do everything in allocate(); no risk of circular data structure
-        # with strings.
-        pass
+        string = decoder.concat_strings(left, right)
+        decoder.virtuals_cache[index] = string
+        return string
 
     def debug_prints(self):
         debug_print("\tvstrconcatinfo")
@@ -543,15 +539,11 @@
     """Stands for the string made out of slicing another string."""
 
     @specialize.argtype(1)
-    def allocate(self, decoder):
-        str, start, length = self.fieldnums
-        return decoder.slice_string(str, start, length)
-
-    @specialize.argtype(1)
-    def setfields(self, decoder, string):
-        # we do everything in allocate(); no risk of circular data structure
-        # with strings.
-        pass
+    def allocate(self, decoder, index):
+        largerstr, start, length = self.fieldnums
+        string = decoder.slice_string(largerstr, start, length)
+        decoder.virtuals_cache[index] = string
+        return string
 
     def debug_prints(self):
         debug_print("\tvstrsliceinfo")
@@ -568,7 +560,8 @@
     blackholing and want the best performance.
     """
     _mixin_ = True
-    virtuals = None
+    rd_virtuals = None
+    virtuals_cache = None
     virtual_default = None
 
     def _init(self, cpu, storage):
@@ -580,17 +573,29 @@
         self._prepare_virtuals(storage.rd_virtuals)
         self._prepare_pendingfields(storage.rd_pendingfields)
 
+    def getvirtual(self, index):
+        # Returns the index'th virtual, building it lazily if needed.
+        # Note that this may be called recursively; that's why the
+        # allocate() methods must fill in the cache as soon as they
+        # have the object, before they fill its fields.
+        v = self.virtuals_cache[index]
+        if not v:
+            v = self.rd_virtuals[index].allocate(self, index)
+            ll_assert(v == self.virtuals_cache[index], "resume.py: bad cache")
+        return v
+
+    def force_all_virtuals(self):
+        rd_virtuals = self.rd_virtuals
+        if rd_virtuals:
+            for i in range(len(rd_virtuals)):
+                if rd_virtuals[i] is not None:
+                    self.getvirtual(i)
+        return self.virtuals_cache
+
     def _prepare_virtuals(self, virtuals):
         if virtuals:
-            self.virtuals = [self.virtual_default] * len(virtuals)
-            for i in range(len(virtuals)):
-                vinfo = virtuals[i]
-                if vinfo is not None:
-                    self.virtuals[i] = vinfo.allocate(self)
-            for i in range(len(virtuals)):
-                vinfo = virtuals[i]
-                if vinfo is not None:
-                    vinfo.setfields(self, self.virtuals[i])
+            self.rd_virtuals = virtuals
+            self.virtuals_cache = [self.virtual_default] * len(virtuals)
 
     def _prepare_pendingfields(self, pendingfields):
         if pendingfields is not None:
@@ -698,6 +703,11 @@
         return self.metainterp.execute_and_record(rop.NEWSTR,
                                                   None, ConstInt(length))
 
+    def string_setitem(self, strbox, index, charnum):
+        charbox = self.decode_box(charnum, INT)
+        self.metainterp.execute_and_record(rop.STRSETITEM, None,
+                                           strbox, ConstInt(index), charbox)
+
     def concat_strings(self, str1num, str2num):
         calldescr, func = callinfo_for_oopspec(EffectInfo.OS_STR_CONCAT)
         str1box = self.decode_box(str1num, REF)
@@ -756,9 +766,7 @@
             else:
                 box = self.consts[num]
         elif tag == TAGVIRTUAL:
-            virtuals = self.virtuals
-            assert virtuals is not None
-            box = virtuals[num]
+            box = self.getvirtual(num)
         elif tag == TAGINT:
             box = ConstInt(num)
         else:
@@ -843,7 +851,7 @@
     resumereader.handling_async_forcing()
     vrefinfo = metainterp_sd.virtualref_info
     resumereader.consume_vref_and_vable(vrefinfo, vinfo)
-    return resumereader.virtuals
+    return resumereader.force_all_virtuals()
 
 class ResumeDataDirectReader(AbstractResumeDataReader):
     unique_id = lambda: None
@@ -861,7 +869,9 @@
             # special case for resuming after a GUARD_NOT_FORCED: we already
             # have the virtuals
             self.resume_after_guard_not_forced = 2
-            self.virtuals = all_virtuals
+            self.virtuals_cache = all_virtuals
+            # self.rd_virtuals can remain None, because virtuals_cache is
+            # already filled
 
     def handling_async_forcing(self):
         self.resume_after_guard_not_forced = 1
@@ -935,6 +945,10 @@
     def allocate_string(self, length):
         return self.cpu.bh_newstr(length)
 
+    def string_setitem(self, str, index, charnum):
+        char = self.decode_int(charnum)
+        self.cpu.bh_strsetitem(str, index, char)
+
     def concat_strings(self, str1num, str2num):
         str1 = self.decode_ref(str1num)
         str2 = self.decode_ref(str2num)
@@ -995,9 +1009,7 @@
                 return self.cpu.ts.NULLREF
             return self.consts[num].getref_base()
         elif tag == TAGVIRTUAL:
-            virtuals = self.virtuals
-            assert virtuals is not None
-            return virtuals[num]
+            return self.getvirtual(num)
         else:
             assert tag == TAGBOX
             if num < 0:

Modified: pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizefindnode.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizefindnode.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizefindnode.py	Sun Sep 26 13:56:56 2010
@@ -123,6 +123,27 @@
                  EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR_SLICE))
     strequaldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                  EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR_EQUAL))
+    streq_slice_checknull_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                 EffectInfo([], [], [],
+                     oopspecindex=EffectInfo.OS_STREQ_SLICE_CHECKNULL))
+    streq_slice_nonnull_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                 EffectInfo([], [], [],
+                     oopspecindex=EffectInfo.OS_STREQ_SLICE_NONNULL))
+    streq_slice_char_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                 EffectInfo([], [], [],
+                     oopspecindex=EffectInfo.OS_STREQ_SLICE_CHAR))
+    streq_nonnull_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                 EffectInfo([], [], [],
+                     oopspecindex=EffectInfo.OS_STREQ_NONNULL))
+    streq_nonnull_char_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                 EffectInfo([], [], [],
+                     oopspecindex=EffectInfo.OS_STREQ_NONNULL_CHAR))
+    streq_checknull_char_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                 EffectInfo([], [], [],
+                     oopspecindex=EffectInfo.OS_STREQ_CHECKNULL_CHAR))
+    streq_lengthok_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                 EffectInfo([], [], [],
+                     oopspecindex=EffectInfo.OS_STREQ_LENGTHOK))
 
     class LoopToken(AbstractDescr):
         pass

Modified: pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizeopt.py	Sun Sep 26 13:56:56 2010
@@ -4098,6 +4098,42 @@
         """
         self.optimize_loop(ops, 'Not, Not, Not, Not, Not', expected)
 
+    def test_str_slice_getitem1(self):
+        ops = """
+        [p1, i1, i2, i3]
+        p2 = call(0, p1, i1, i2, descr=slicedescr)
+        i4 = strgetitem(p2, i3)
+        escape(i4)
+        jump(p1, i1, i2, i3)
+        """
+        expected = """
+        [p1, i1, i2, i3]
+        i6 = int_sub(i2, i1)      # killed by the backend
+        i5 = int_add(i1, i3)
+        i4 = strgetitem(p1, i5)
+        escape(i4)
+        jump(p1, i1, i2, i3)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+    def test_str_slice_plain(self):
+        ops = """
+        [i3, i4]
+        p1 = newstr(2)
+        strsetitem(p1, 0, i3)
+        strsetitem(p1, 1, i4)
+        p2 = call(0, p1, 1, 2, descr=slicedescr)
+        i5 = strgetitem(p2, 0)
+        escape(i5)
+        jump(i3, i4)
+        """
+        expected = """
+        [i3, i4]
+        escape(i4)
+        jump(i3, i4)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
     def test_str_slice_concat(self):
         ops = """
         [p1, i1, i2, p2]
@@ -4119,6 +4155,345 @@
         """
         self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
 
+    # ----------
+    def optimize_loop_extradescrs(self, ops, spectext, optops):
+        from pypy.jit.metainterp.optimizeopt import string
+        def my_callinfo_for_oopspec(oopspecindex):
+            calldescrtype = type(LLtypeMixin.strequaldescr)
+            for value in LLtypeMixin.__dict__.values():
+                if isinstance(value, calldescrtype):
+                    if (value.get_extra_info() and
+                        value.get_extra_info().oopspecindex == oopspecindex):
+                        from pypy.rpython.lltypesystem import lltype
+                        func = lltype.nullptr(lltype.FuncType([], lltype.Void))
+                        # returns 0 for 'func' in this test
+                        return value, func
+            raise AssertionError("not found: oopspecindex=%d" % oopspecindex)
+        #
+        saved = string.callinfo_for_oopspec
+        try:
+            string.callinfo_for_oopspec = my_callinfo_for_oopspec
+            self.optimize_loop(ops, spectext, optops)
+        finally:
+            string.callinfo_for_oopspec = saved
+
+    def test_str_equal_noop1(self):
+        ops = """
+        [p1, p2]
+        i0 = call(0, p1, p2, descr=strequaldescr)
+        escape(i0)
+        jump(p1, p2)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not', ops)
+
+    def test_str_equal_noop2(self):
+        ops = """
+        [p1, p2, p3]
+        p4 = call(0, p1, p2, descr=strconcatdescr)
+        i0 = call(0, p3, p4, descr=strequaldescr)
+        escape(i0)
+        jump(p1, p2, p3)
+        """
+        expected = """
+        [p1, p2, p3]
+        i1 = strlen(p1)
+        i2 = strlen(p2)
+        i3 = int_add(i1, i2)
+        p4 = newstr(i3)
+        i4 = strlen(p1)
+        copystrcontent(p1, p4, 0, 0, i4)
+        i5 = strlen(p2)
+        i6 = int_add(i4, i5)      # will be killed by the backend
+        copystrcontent(p2, p4, 0, i4, i5)
+        i0 = call(0, p3, p4, descr=strequaldescr)
+        escape(i0)
+        jump(p1, p2, p3)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not, Not', expected)
+
+    def test_str_equal_slice1(self):
+        ops = """
+        [p1, i1, i2, p3]
+        p4 = call(0, p1, i1, i2, descr=slicedescr)
+        i0 = call(0, p4, p3, descr=strequaldescr)
+        escape(i0)
+        jump(p1, i1, i2, p3)
+        """
+        expected = """
+        [p1, i1, i2, p3]
+        i3 = int_sub(i2, i1)
+        i0 = call(0, p1, i1, i3, p3, descr=streq_slice_checknull_descr)
+        escape(i0)
+        jump(p1, i1, i2, p3)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not, Not, Not', expected)
+
+    def test_str_equal_slice2(self):
+        ops = """
+        [p1, i1, i2, p3]
+        p4 = call(0, p1, i1, i2, descr=slicedescr)
+        i0 = call(0, p3, p4, descr=strequaldescr)
+        escape(i0)
+        jump(p1, i1, i2, p3)
+        """
+        expected = """
+        [p1, i1, i2, p3]
+        i4 = int_sub(i2, i1)
+        i0 = call(0, p1, i1, i4, p3, descr=streq_slice_checknull_descr)
+        escape(i0)
+        jump(p1, i1, i2, p3)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not, Not, Not', expected)
+
+    def test_str_equal_slice3(self):
+        ops = """
+        [p1, i1, i2, p3]
+        guard_nonnull(p3) []
+        p4 = call(0, p1, i1, i2, descr=slicedescr)
+        i0 = call(0, p3, p4, descr=strequaldescr)
+        escape(i0)
+        jump(p1, i1, i2, p3)
+        """
+        expected = """
+        [p1, i1, i2, p3]
+        guard_nonnull(p3) []
+        i4 = int_sub(i2, i1)
+        i0 = call(0, p1, i1, i4, p3, descr=streq_slice_nonnull_descr)
+        escape(i0)
+        jump(p1, i1, i2, p3)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not, Not, Not', expected)
+
+    def test_str_equal_slice4(self):
+        ops = """
+        [p1, i1, i2]
+        p3 = call(0, p1, i1, i2, descr=slicedescr)
+        i0 = call(0, p3, "x", descr=strequaldescr)
+        escape(i0)
+        jump(p1, i1, i2)
+        """
+        expected = """
+        [p1, i1, i2]
+        i3 = int_sub(i2, i1)
+        i0 = call(0, p1, i1, i3, 120, descr=streq_slice_char_descr)
+        escape(i0)
+        jump(p1, i1, i2)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not, Not', expected)
+
+    def test_str_equal_slice5(self):
+        ops = """
+        [p1, i1, i2, i3]
+        p4 = call(0, p1, i1, i2, descr=slicedescr)
+        p5 = newstr(1)
+        strsetitem(p5, 0, i3)
+        i0 = call(0, p5, p4, descr=strequaldescr)
+        escape(i0)
+        jump(p1, i1, i2, i3)
+        """
+        expected = """
+        [p1, i1, i2, i3]
+        i4 = int_sub(i2, i1)
+        i0 = call(0, p1, i1, i4, i3, descr=streq_slice_char_descr)
+        escape(i0)
+        jump(p1, i1, i2, i3)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not, Not, Not', expected)
+
+    def test_str_equal_none1(self):
+        ops = """
+        [p1]
+        i0 = call(0, p1, NULL, descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        i0 = ptr_eq(p1, NULL)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    def test_str_equal_none2(self):
+        ops = """
+        [p1]
+        i0 = call(0, NULL, p1, descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        i0 = ptr_eq(p1, NULL)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    def test_str_equal_nonnull1(self):
+        ops = """
+        [p1]
+        guard_nonnull(p1) []
+        i0 = call(0, p1, "hello world", descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        guard_nonnull(p1) []
+        i0 = call(0, p1, "hello world", descr=streq_nonnull_descr)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    def test_str_equal_nonnull2(self):
+        ops = """
+        [p1]
+        guard_nonnull(p1) []
+        i0 = call(0, p1, "", descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        guard_nonnull(p1) []
+        i1 = strlen(p1)
+        i0 = int_eq(i1, 0)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    def test_str_equal_nonnull3(self):
+        ops = """
+        [p1]
+        guard_nonnull(p1) []
+        i0 = call(0, p1, "x", descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        guard_nonnull(p1) []
+        i0 = call(0, p1, 120, descr=streq_nonnull_char_descr)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    def test_str_equal_nonnull4(self):
+        ops = """
+        [p1, p2]
+        p4 = call(0, p1, p2, descr=strconcatdescr)
+        i0 = call(0, "hello world", p4, descr=strequaldescr)
+        escape(i0)
+        jump(p1, p2)
+        """
+        expected = """
+        [p1, p2]
+        i1 = strlen(p1)
+        i2 = strlen(p2)
+        i3 = int_add(i1, i2)
+        p4 = newstr(i3)
+        i4 = strlen(p1)
+        copystrcontent(p1, p4, 0, 0, i4)
+        i5 = strlen(p2)
+        i6 = int_add(i4, i5)      # will be killed by the backend
+        copystrcontent(p2, p4, 0, i4, i5)
+        i0 = call(0, "hello world", p4, descr=streq_nonnull_descr)
+        escape(i0)
+        jump(p1, p2)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not', expected)
+
+    def test_str_equal_chars0(self):
+        ops = """
+        [i1]
+        p1 = newstr(0)
+        i0 = call(0, p1, "", descr=strequaldescr)
+        escape(i0)
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        escape(1)
+        jump(i1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    def test_str_equal_chars1(self):
+        ops = """
+        [i1]
+        p1 = newstr(1)
+        strsetitem(p1, 0, i1)
+        i0 = call(0, p1, "x", descr=strequaldescr)
+        escape(i0)
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        i0 = int_eq(i1, 120)     # ord('x')
+        escape(i0)
+        jump(i1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    def test_str_equal_chars2(self):
+        ops = """
+        [i1, i2]
+        p1 = newstr(2)
+        strsetitem(p1, 0, i1)
+        strsetitem(p1, 1, i2)
+        i0 = call(0, p1, "xy", descr=strequaldescr)
+        escape(i0)
+        jump(i1, i2)
+        """
+        expected = """
+        [i1, i2]
+        p1 = newstr(2)
+        strsetitem(p1, 0, i1)
+        strsetitem(p1, 1, i2)
+        i0 = call(0, p1, "xy", descr=streq_lengthok_descr)
+        escape(i0)
+        jump(i1, i2)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not, Not', expected)
+
+    def test_str_equal_chars3(self):
+        ops = """
+        [p1]
+        i0 = call(0, "x", p1, descr=strequaldescr)
+        escape(i0)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        i0 = call(0, p1, 120, descr=streq_checknull_char_descr)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    def test_str_equal_lengthmismatch1(self):
+        ops = """
+        [i1]
+        p1 = newstr(1)
+        strsetitem(p1, 0, i1)
+        i0 = call(0, "xy", p1, descr=strequaldescr)
+        escape(i0)
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        escape(0)
+        jump(i1)
+        """
+        self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+    # XXX unicode operations
+    # XXX str2unicode
+
 
 ##class TestOOtype(BaseTestOptimizeOpt, OOtypeMixin):
 

Modified: pypy/branch/jit-str/pypy/jit/metainterp/test/test_resume.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/test/test_resume.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/test/test_resume.py	Sun Sep 26 13:56:56 2010
@@ -199,10 +199,10 @@
 
 def test_prepare_virtuals():
     class FakeVinfo(object):
-        def allocate(self, decoder):
-            return "allocated"
-        def setfields(self, decoder, virtual):
-            assert virtual == "allocated"
+        def allocate(self, decoder, index):
+            s = "allocated"
+            decoder.virtuals_cache[index] = s
+            return s
     class FakeStorage(object):
         rd_virtuals = [FakeVinfo(), None]
         rd_numb = []
@@ -212,7 +212,97 @@
         _already_allocated_resume_virtuals = None
         cpu = None
     reader = ResumeDataDirectReader(None, FakeStorage())
-    assert reader.virtuals == ["allocated", reader.virtual_default]
+    assert reader.force_all_virtuals() == ["allocated", reader.virtual_default]
+
+# ____________________________________________________________
+
+class FakeResumeDataReader(AbstractResumeDataReader):
+    def allocate_with_vtable(self, known_class):
+        return FakeBuiltObject(vtable=known_class)
+    def allocate_struct(self, typedescr):
+        return FakeBuiltObject(typedescr=typedescr)
+    def allocate_array(self, arraydescr, length):
+        return FakeBuiltObject(arraydescr=arraydescr, items=[None]*length)
+    def setfield(self, descr, struct, fieldnum):
+        setattr(struct, descr, fieldnum)
+    def setarrayitem_int(self, arraydescr, array, i, fieldnum):
+        assert 0 <= i < len(array.items)
+        assert arraydescr is array.arraydescr
+        array.items[i] = fieldnum
+    def allocate_string(self, length):
+        return FakeBuiltObject(string=[None]*length)
+    def string_setitem(self, string, i, fieldnum):
+        value, tag = untag(fieldnum)
+        assert tag == TAGINT
+        assert 0 <= i < len(string.string)
+        string.string[i] = value
+    def concat_strings(self, left, right):
+        return FakeBuiltObject(strconcat=[left, right])
+    def slice_string(self, str, start, length):
+        return FakeBuiltObject(strslice=[str, start, length])
+
+class FakeBuiltObject(object):
+    def __init__(self, **kwds):
+        self.__dict__ = kwds
+    def __eq__(self, other):
+        return (self.__class__ == other.__class__ and
+                self.__dict__ == other.__dict__)
+    def __repr__(self):
+        return 'FakeBuiltObject(%s)' % (
+            ', '.join(['%s=%r' % item for item in self.__dict__.items()]))
+
+class FakeArrayDescr(object):
+    def is_array_of_pointers(self): return False
+    def is_array_of_floats(self): return False
+
+def test_virtualinfo():
+    info = VirtualInfo(123, ["fielddescr1"])
+    info.fieldnums = [tag(456, TAGINT)]
+    reader = FakeResumeDataReader()
+    reader._prepare_virtuals([info])
+    assert reader.force_all_virtuals() == [
+        FakeBuiltObject(vtable=123, fielddescr1=tag(456, TAGINT))]
+
+def test_vstructinfo():
+    info = VStructInfo(124, ["fielddescr1"])
+    info.fieldnums = [tag(456, TAGINT)]
+    reader = FakeResumeDataReader()
+    reader._prepare_virtuals([info])
+    assert reader.force_all_virtuals() == [
+        FakeBuiltObject(typedescr=124, fielddescr1=tag(456, TAGINT))]
+
+def test_varrayinfo():
+    arraydescr = FakeArrayDescr()
+    info = VArrayInfo(arraydescr)
+    info.fieldnums = [tag(456, TAGINT)]
+    reader = FakeResumeDataReader()
+    reader._prepare_virtuals([info])
+    assert reader.force_all_virtuals() == [
+        FakeBuiltObject(arraydescr=arraydescr, items=[tag(456, TAGINT)])]
+
+def test_vstrplaininfo():
+    info = VStrPlainInfo()
+    info.fieldnums = [tag(60, TAGINT)]
+    reader = FakeResumeDataReader()
+    reader._prepare_virtuals([info])
+    assert reader.force_all_virtuals() == [
+        FakeBuiltObject(string=[60])]
+
+def test_vstrconcatinfo():
+    info = VStrConcatInfo()
+    info.fieldnums = [tag(10, TAGBOX), tag(20, TAGBOX)]
+    reader = FakeResumeDataReader()
+    reader._prepare_virtuals([info])
+    assert reader.force_all_virtuals() == [
+        FakeBuiltObject(strconcat=info.fieldnums)]
+
+def test_vstrsliceinfo():
+    info = VStrSliceInfo()
+    info.fieldnums = [tag(10, TAGBOX), tag(20, TAGBOX), tag(30, TAGBOX)]
+    reader = FakeResumeDataReader()
+    reader._prepare_virtuals([info])
+    assert reader.force_all_virtuals() == [
+        FakeBuiltObject(strslice=info.fieldnums)]
 
 # ____________________________________________________________
 
@@ -957,7 +1047,7 @@
 
     metainterp = MyMetaInterp()
     reader = ResumeDataFakeReader(storage, newboxes, metainterp)
-    assert len(reader.virtuals) == 2
+    assert len(reader.virtuals_cache) == 2
     b2t = reader.decode_ref(modifier._gettagged(b2s))
     b4t = reader.decode_ref(modifier._gettagged(b4s))
     trace = metainterp.trace
@@ -973,9 +1063,9 @@
              (rop.SETFIELD_GC, [b4t, b3t],     None, LLtypeMixin.valuedescr),
              (rop.SETFIELD_GC, [b4t, b5t],     None, LLtypeMixin.otherdescr)]
     if untag(modifier._gettagged(b2s))[0] == -2:
-        expected = [b2new, b4new] + b2set + b4set
+        expected = [b2new, b4new] + b4set + b2set
     else:
-        expected = [b4new, b2new] + b4set + b2set
+        expected = [b4new, b2new] + b2set + b4set
         
     for x, y in zip(expected, trace):
         assert x == y
@@ -1020,7 +1110,7 @@
     # resume
     metainterp = MyMetaInterp()
     reader = ResumeDataFakeReader(storage, newboxes, metainterp)
-    assert len(reader.virtuals) == 1
+    assert len(reader.virtuals_cache) == 1
     b2t = reader.decode_ref(tag(0, TAGVIRTUAL))
     trace = metainterp.trace
     expected = [
@@ -1065,7 +1155,7 @@
     NULL = ConstPtr.value
     metainterp = MyMetaInterp()
     reader = ResumeDataFakeReader(storage, newboxes, metainterp)
-    assert len(reader.virtuals) == 1
+    assert len(reader.virtuals_cache) == 1
     b2t = reader.decode_ref(tag(0, TAGVIRTUAL))
 
     trace = metainterp.trace
@@ -1112,7 +1202,7 @@
 
     metainterp = MyMetaInterp()
     reader = ResumeDataFakeReader(storage, newboxes, metainterp)
-    assert reader.virtuals is None
+    assert reader.virtuals_cache is None
     trace = metainterp.trace
     b2set = (rop.SETFIELD_GC, [b2t, b4t], None, LLtypeMixin.nextdescr)
     expected = [b2set]

Modified: pypy/branch/jit-str/pypy/jit/metainterp/test/test_string.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/test/test_string.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/test/test_string.py	Sun Sep 26 13:56:56 2010
@@ -281,6 +281,24 @@
                 return 42
             self.meta_interp(f, [10, 10])
 
+    def test_streq_char(self):
+        for somestr in ["?abcdefg", ]: #u"def"]:
+            jitdriver = JitDriver(greens = [], reds = ['m', 'n'])
+            @dont_look_inside
+            def escape(x):
+                pass
+            def f(n, m):
+                assert n >= 0
+                while m >= 0:
+                    jitdriver.can_enter_jit(m=m, n=n)
+                    jitdriver.jit_merge_point(m=m, n=n)
+                    s = somestr[:m]
+                    escape(s == "?")
+                    m -= 1
+                return 42
+            self.meta_interp(f, [6, 7])
+            self.check_loops(newstr=0, newunicode=0)
+
 
 class TestOOtype(StringTests, OOJitMixin):
     CALL = "oosend"

Modified: pypy/branch/jit-str/pypy/jit/metainterp/warmstate.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/warmstate.py	(original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/warmstate.py	Sun Sep 26 13:56:56 2010
@@ -83,7 +83,7 @@
             return history.ConstFloat(value)
         else:
             return history.BoxFloat(value)
-    elif isinstance(value, (str, unicode)):
+    elif isinstance(value, str) or isinstance(value, unicode):
         assert len(value) == 1     # must be a character
         value = ord(value)
     else:



More information about the Pypy-commit mailing list