Commits

Antonio Cuni committed 1728bcd

merge from trunk: svn merge svn+ssh://codespeak.net/svn/pypy/trunk -r77720:78032

Comments (0)

Files changed (75)

lib-python/modified-2.5.2/ctypes/__init__.py

         _restype_ = c_int # default, can be overridden in instances
 
     def __init__(self, name, mode=DEFAULT_MODE, handle=None):
+        import _ffi
         self._name = name
         if handle is None:
-            self._handle = _dlopen(self._name, mode)
+            #self._handle = _dlopen(self._name, mode)
+            self._handle = _ffi.CDLL(name)
         else:
             self._handle = handle
 

lib_pypy/_ctypes/function.py

                 self.dll = ctypes.CDLL(self.dll)
             # we need to check dll anyway
             ptr = self._getfuncptr([], ctypes.c_int)
-            self._buffer = ptr.byptr()
+            #self._buffer = ptr.byptr()
+            self._buffer = None
 
         elif (sys.platform == 'win32' and
               len(args) >= 2 and isinstance(args[0], (int, long))):
             
         if argtypes is None:
             argtypes = self._guess_argtypes(args)
-        argtypes, argsandobjs = self._wrap_args(argtypes, args)
+
+        # XXX
+        #argtypes, argsandobjs = self._wrap_args(argtypes, args)
         
         restype = self._restype_
         funcptr = self._getfuncptr(argtypes, restype, thisarg)
-        resbuffer = funcptr(*[arg._buffer for _, arg in argsandobjs])
-        return self._build_result(restype, resbuffer, argtypes, argsandobjs)
+        return funcptr(*args)
+        #resbuffer = funcptr(*[arg._buffer for _, arg in argsandobjs])
+        #return self._build_result(restype, resbuffer, argtypes, argsandobjs)
+
+    def _shape_to_ffi_type(self, shape):
+        from _ffi import types
+        if shape == 'l':
+            return types.slong
+        elif shape == 'd':
+            return types.double
+        else:
+            print 'unknown shape %s' % shape
+            import pdb;pdb.set_trace()
 
     def _getfuncptr(self, argtypes, restype, thisarg=None):
         if self._ptr is not None and argtypes is self._argtypes_:
         
         cdll = self.dll._handle
         try:
-            return cdll.ptr(self.name, argshapes, resshape, self._flags_)
+            #return cdll.ptr(self.name, argshapes, resshape, self._flags_)
+            ffi_argtypes = [self._shape_to_ffi_type(shape) for shape in argshapes]
+            ffi_restype = self._shape_to_ffi_type(resshape)
+            self._ptr = cdll.getfunc(self.name, ffi_argtypes, ffi_restype)
+            return self._ptr
         except AttributeError:
             if self._flags_ & _rawffi.FUNCFLAG_CDECL:
                 raise

pypy/config/pypyoption.py

         BoolOption("withstrslice", "use strings optimized for slicing",
                    default=False),
 
+        BoolOption("withstrbuf", "use strings optimized for addition (ver 2)",
+                   default=False),
+
         BoolOption("withprebuiltchar",
                    "use prebuilt single-character string objects",
                    default=False),
         BoolOption("withrope", "use ropes as the string implementation",
                    default=False,
                    requires=[("objspace.std.withstrslice", False),
-                             ("objspace.std.withstrjoin", False)],
+                             ("objspace.std.withstrjoin", False),
+                             ("objspace.std.withstrbuf", False)],
                    suggests=[("objspace.std.withprebuiltchar", True),
                              ("objspace.std.sharesmallstr", True)]),
 
                    default=False,
                    requires=[("objspace.std.withshadowtracking", False)]),
 
+        BoolOption("withmapdict",
+                   "make instances really small but slow without the JIT",
+                   default=False,
+                   requires=[("objspace.std.withshadowtracking", False),
+                             ("objspace.std.withinlineddict", False),
+                             ("objspace.std.withsharingdict", False),
+                             ("objspace.std.getattributeshortcut", True),
+                             ("objspace.std.withtypeversion", True),
+                       ]),
+
         BoolOption("withrangelist",
                    "enable special range list implementation that does not "
                    "actually create the full list until the resulting "
         config.objspace.std.suggest(withprebuiltint=True)
         config.objspace.std.suggest(withrangelist=True)
         config.objspace.std.suggest(withprebuiltchar=True)
-        config.objspace.std.suggest(withinlineddict=True)
+        config.objspace.std.suggest(withmapdict=True)
         config.objspace.std.suggest(withstrslice=True)
         config.objspace.std.suggest(withstrjoin=True)
         # xxx other options? ropes maybe?
     # extra optimizations with the JIT
     if level == 'jit':
         config.objspace.std.suggest(withcelldict=True)
+        #config.objspace.std.suggest(withmapdict=True)
 
 
 def enable_allworkingmodules(config):

pypy/doc/config/objspace.std.withmapdict.txt

+Enable the new version of "sharing dictionaries".
+
+See the section in `Standard Interpreter Optimizations`_ for more details.
+
+.. _`Standard Interpreter Optimizations`: ../interpreter-optimizations.html#sharing-dicts

pypy/doc/config/objspace.std.withstrbuf.txt

+Enable "string buffer" objects.
+
+Similar to "string join" objects, but using a StringBuilder to represent
+a string built by repeated application of ``+=``.

pypy/interpreter/baseobjspace.py

     def _call_builtin_destructor(self):
         pass     # method overridden in typedef.py
 
+    # hooks that the mapdict implementations needs:
+    def _get_mapdict_map(self):
+        return None
+    def _set_mapdict_map(self, map):
+        raise NotImplementedError
+    def _mapdict_read_storage(self, index):
+        raise NotImplementedError
+    def _mapdict_write_storage(self, index, value):
+        raise NotImplementedError
+    def _mapdict_storage_length(self):
+        raise NotImplementedError
+    def _set_mapdict_storage_and_map(self, storage, map):
+        raise NotImplementedError
+
 
 class Wrappable(W_Root):
     """A subclass of Wrappable is an internal, interpreter-level class

pypy/interpreter/pycode.py

 
         self._compute_flatcall()
 
+        if self.space.config.objspace.std.withmapdict:
+            from pypy.objspace.std.mapdict import init_mapdict_cache
+            init_mapdict_cache(self)
+
     def _freeze_(self):
         if (self.magic == cpython_magic and
             '__pypy__' not in sys.builtin_module_names):

pypy/interpreter/pyopcode.py

 
     def LOAD_ATTR(self, nameindex, next_instr):
         "obj.attributename"
-        w_attributename = self.getname_w(nameindex)
         w_obj = self.popvalue()
-        w_value = self.space.getattr(w_obj, w_attributename)
+        if (self.space.config.objspace.std.withmapdict
+            and not jit.we_are_jitted()):
+            from pypy.objspace.std.mapdict import LOAD_ATTR_caching
+            w_value = LOAD_ATTR_caching(self.getcode(), w_obj, nameindex)
+        else:
+            w_attributename = self.getname_w(nameindex)
+            w_value = self.space.getattr(w_obj, w_attributename)
         self.pushvalue(w_value)
     LOAD_ATTR._always_inline_ = True
 

pypy/interpreter/pyparser/genpytokenize.py

     pseudoToken = chain(states,
                         makeWhitespace(),
                         group(states,
+                              newArcPair(states, EMPTY),
                               pseudoExtras, number, funny, contStr, name))
     dfaStates, dfaAccepts = nfaToDfa(states, *pseudoToken)
     return DFA(dfaStates, dfaAccepts)

pypy/interpreter/pyparser/pytokenize.py

 # ______________________________________________________________________
 # Automatically generated DFA's
 
-accepts = [False, True, True, True, True, True, True, True,
+accepts = [True, True, True, True, True, True, True, True,
            True, True, False, True, True, True, False, False,
            False, False, True, True, True, False, True,
            False, True, False, True, False, False, True,

pypy/interpreter/typedef.py

     typedef = cls.typedef
     if wants_dict and typedef.hasdict:
         wants_dict = False
+    if config.objspace.std.withmapdict and not typedef.hasdict:
+        # mapdict only works if the type does not already have a dict
+        if wants_del:
+            parentcls = get_unique_interplevel_subclass(config, cls, True, True,
+                                                        False, True)
+            return _usersubclswithfeature(config, parentcls, "del")
+        return _usersubclswithfeature(config, cls, "user", "dict", "weakref", "slots")
     # Forest of if's - see the comment above.
     if wants_del:
         if wants_dict:
 
     def add(Proto):
         for key, value in Proto.__dict__.items():
-            if not key.startswith('__') or key == '__del__':
+            if (not key.startswith('__') and not key.startswith('_mixin_') 
+                    or key == '__del__'):
+                if hasattr(value, "func_name"):
+                    value = func_with_new_name(value, value.func_name)
                 body[key] = value
 
+    if (config.objspace.std.withmapdict and "dict" in features):
+        from pypy.objspace.std.mapdict import BaseMapdictObject, ObjectMixin
+        add(BaseMapdictObject)
+        add(ObjectMixin)
+        features = ()
+
     if "user" in features:     # generic feature needed by all subcls
+
         class Proto(object):
             user_overridden_class = True
 
             wantdict = False
 
     if wantdict:
+        base_user_setup = supercls.user_setup.im_func
+        if "user_setup" in body:
+            base_user_setup = body["user_setup"]
         class Proto(object):
             def getdict(self):
                 return self.w__dict__
                 self.w__dict__ = check_new_dictionary(space, w_dict)
             
             def user_setup(self, space, w_subtype):
-                self.space = space
-                self.w__class__ = w_subtype
                 self.w__dict__ = space.newdict(
                     instance=True, classofinstance=w_subtype)
-                self.user_setup_slots(w_subtype.nslots)
+                base_user_setup(self, space, w_subtype)
 
             def setclass(self, space, w_subtype):
                 # only used by descr_set___class__

pypy/jit/backend/test/runner_test.py

                                                         length_box], 'void')
                                 assert self.look_string(r_box) == "!??cdef?!"
 
+    def test_copyunicodecontent(self):
+        s_box = self.alloc_unicode(u"abcdef")
+        for s_box in [s_box, s_box.constbox()]:
+            for srcstart_box in [BoxInt(2), ConstInt(2)]:
+                for dststart_box in [BoxInt(3), ConstInt(3)]:
+                    for length_box in [BoxInt(4), ConstInt(4)]:
+                        for r_box_is_const in [False, True]:
+                            r_box = self.alloc_unicode(u"!???????!")
+                            if r_box_is_const:
+                                r_box = r_box.constbox()
+                                self.execute_operation(rop.COPYUNICODECONTENT,
+                                                       [s_box, r_box,
+                                                        srcstart_box,
+                                                        dststart_box,
+                                                        length_box], 'void')
+                                assert self.look_unicode(r_box) == u"!??cdef?!"
+
     def test_do_unicode_basic(self):
         u = self.cpu.bh_newunicode(5)
         self.cpu.bh_unicodesetitem(u, 4, 123)
         u_box = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, u))
         return u_box
 
+    def look_unicode(self, unicode_box):
+        u = unicode_box.getref(lltype.Ptr(rstr.UNICODE))
+        return u''.join(u.chars)
+
 
     def test_casts(self):
         py.test.skip("xxx fix or kill")

pypy/jit/backend/test/test_ll_random.py

     OPERATIONS.append(StrLenOperation(rop.STRLEN))
     OPERATIONS.append(UnicodeLenOperation(rop.UNICODELEN))
     OPERATIONS.append(CopyStrContentOperation(rop.COPYSTRCONTENT))
-    #OPERATIONS.append(CopyUnicodeContentOperation(rop.COPYUNICODECONTENT))
+    OPERATIONS.append(CopyUnicodeContentOperation(rop.COPYUNICODECONTENT))
 
 for i in range(2):
     OPERATIONS.append(GuardClassOperation(rop.GUARD_CLASS))

pypy/jit/backend/x86/regalloc.py

             loc = self.loc(op.getarg(0))
             return self._call(op, [loc])
         # boehm GC (XXX kill the following code at some point)
-        ofs_items, itemsize, ofs = symbolic.get_array_token(rstr.UNICODE, self.translate_support_code)
-        if itemsize == 4:
-            return self._malloc_varsize(ofs_items, ofs, 2, op.getarg(0),
-                                        op.result)
-        elif itemsize == 2:
-            return self._malloc_varsize(ofs_items, ofs, 1, op.getarg(0),
-                                        op.result)
-        else:
-            assert False, itemsize
+        ofs_items, _, ofs = symbolic.get_array_token(rstr.UNICODE,
+                                                   self.translate_support_code)
+        scale = self._get_unicode_item_scale()
+        return self._malloc_varsize(ofs_items, ofs, scale, op.getarg(0),
+                                    op.result)
 
     def _malloc_varsize(self, ofs_items, ofs_length, scale, v, res_v):
         # XXX kill this function at some point
     consider_unicodegetitem = consider_strgetitem
 
     def consider_copystrcontent(self, op):
+        self._consider_copystrcontent(op, is_unicode=False)
+
+    def consider_copyunicodecontent(self, op):
+        self._consider_copystrcontent(op, is_unicode=True)
+
+    def _consider_copystrcontent(self, op, is_unicode):
         # compute the source address
         args = op.getarglist()
         base_loc = self.rm.make_sure_var_in_reg(args[0], args)
         srcaddr_box = TempBox()
         forbidden_vars = [args[1], args[3], args[4], srcaddr_box]
         srcaddr_loc = self.rm.force_allocate_reg(srcaddr_box, forbidden_vars)
-        self._gen_address_inside_string(base_loc, ofs_loc, srcaddr_loc)
+        self._gen_address_inside_string(base_loc, ofs_loc, srcaddr_loc,
+                                        is_unicode=is_unicode)
         # compute the destination address
         base_loc = self.rm.make_sure_var_in_reg(args[1], forbidden_vars)
         ofs_loc = self.rm.make_sure_var_in_reg(args[3], forbidden_vars)
         forbidden_vars = [args[4], srcaddr_box]
         dstaddr_box = TempBox()
         dstaddr_loc = self.rm.force_allocate_reg(dstaddr_box, forbidden_vars)
-        self._gen_address_inside_string(base_loc, ofs_loc, dstaddr_loc)
+        self._gen_address_inside_string(base_loc, ofs_loc, dstaddr_loc,
+                                        is_unicode=is_unicode)
+        # compute the length in bytes
+        length_box = args[4]
+        length_loc = self.loc(length_box)
+        if is_unicode:
+            self.rm.possibly_free_var(length_box)
+            forbidden_vars = [srcaddr_box, dstaddr_box]
+            bytes_box = TempBox()
+            bytes_loc = self.rm.force_allocate_reg(bytes_box, forbidden_vars)
+            scale = self._get_unicode_item_scale()
+            if not (isinstance(length_loc, ImmedLoc) or
+                    isinstance(length_loc, RegLoc)):
+                self.assembler.mov(length_loc, bytes_loc)
+                length_loc = bytes_loc
+            self.assembler.load_effective_addr(length_loc, 0, scale, bytes_loc)
+            length_box = bytes_box
+            length_loc = bytes_loc
         # call memcpy()
-        length_loc = self.loc(args[4])
         self.rm.before_call()
         self.xrm.before_call()
         self.assembler._emit_call(imm(self.assembler.memcpy_addr),
                                   [dstaddr_loc, srcaddr_loc, length_loc])
-        self.rm.possibly_free_var(args[4])
+        self.rm.possibly_free_var(length_box)
         self.rm.possibly_free_var(dstaddr_box)
         self.rm.possibly_free_var(srcaddr_box)
 
-    def _gen_address_inside_string(self, baseloc, ofsloc, resloc):
+    def _gen_address_inside_string(self, baseloc, ofsloc, resloc, is_unicode):
         cpu = self.assembler.cpu
-        ofs_items, itemsize, _ = symbolic.get_array_token(rstr.STR,
+        if is_unicode:
+            ofs_items, _, _ = symbolic.get_array_token(rstr.UNICODE,
                                                   self.translate_support_code)
-        assert itemsize == 1
-        self.assembler.load_effective_addr(ofsloc, ofs_items, 0,
+            scale = self._get_unicode_item_scale()
+        else:
+            ofs_items, itemsize, _ = symbolic.get_array_token(rstr.STR,
+                                                  self.translate_support_code)
+            assert itemsize == 1
+            scale = 0
+        self.assembler.load_effective_addr(ofsloc, ofs_items, scale,
                                            resloc, baseloc)
 
+    def _get_unicode_item_scale(self):
+        _, itemsize, _ = symbolic.get_array_token(rstr.UNICODE,
+                                                  self.translate_support_code)
+        if itemsize == 4:
+            return 2
+        elif itemsize == 2:
+            return 1
+        else:
+            raise AssertionError("bad unicode item size")
+
     def consider_jump(self, op):
         assembler = self.assembler
         assert self.jump_target_descr is None

pypy/jit/backend/x86/test/test_runner.py

         self.cpu.execute_token(ops.token)
         # check debugging info
         name, struct = self.cpu.assembler.loop_run_counters[0]
-        assert name == 'xyz'
+        assert name == 0       # 'xyz'
         assert struct.i == 10
         self.cpu.finish_once()
         lines = py.path.local(self.logfile + ".count").readlines()
-        assert lines[0] == '10      xyz\n'
+        assert lines[0] == '0:10\n'  # '10      xyz\n'

pypy/jit/backend/x86/test/test_string.py

 from pypy.jit.metainterp.test import test_string
 from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
 
-class TestString(Jit386Mixin, test_string.StringTests):
+class TestString(Jit386Mixin, test_string.TestLLtype):
     # for the individual tests see
     # ====> ../../../metainterp/test/test_string.py
-    CALL = 'call'
-    CALL_PURE = 'call_pure'
+    pass
+
+class TestUnicode(Jit386Mixin, test_string.TestLLtypeUnicode):
+    # for the individual tests see
+    # ====> ../../../metainterp/test/test_string.py
+    pass

pypy/jit/codewriter/effectinfo.py

     # the 'oopspecindex' field is one of the following values:
     OS_NONE                     = 0    # normal case, no oopspec
     OS_ARRAYCOPY                = 1    # "list.ll_arraycopy"
-    OS_STR_CONCAT               = 2    # "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))
+    OS_STR2UNICODE              = 2    # "str.str2unicode"
     OS_LIBFFI_PREPARE           = 15
     OS_LIBFFI_PUSH_ARG          = 16
     OS_LIBFFI_CALL              = 17
 
+    OS_STR_CONCAT               = 22   # "stroruni.concat"
+    OS_STR_SLICE                = 23   # "stroruni.slice"
+    OS_STR_EQUAL                = 24   # "stroruni.equal"
+    OS_STREQ_SLICE_CHECKNULL    = 25   # s2!=NULL and s1[x:x+length]==s2
+    OS_STREQ_SLICE_NONNULL      = 26   # s1[x:x+length]==s2   (assert s2!=NULL)
+    OS_STREQ_SLICE_CHAR         = 27   # s1[x:x+length]==char
+    OS_STREQ_NONNULL            = 28   # s1 == s2    (assert s1!=NULL,s2!=NULL)
+    OS_STREQ_NONNULL_CHAR       = 29   # s1 == char  (assert s1!=NULL)
+    OS_STREQ_CHECKNULL_CHAR     = 30   # s1!=NULL and s1==char
+    OS_STREQ_LENGTHOK           = 31   # s1 == s2    (assert len(s1)==len(s2))
+
+    OS_UNI_CONCAT               = 42   #
+    OS_UNI_SLICE                = 43   #
+    OS_UNI_EQUAL                = 44   #
+    OS_UNIEQ_SLICE_CHECKNULL    = 45   #
+    OS_UNIEQ_SLICE_NONNULL      = 46   #
+    OS_UNIEQ_SLICE_CHAR         = 47   #
+    OS_UNIEQ_NONNULL            = 48   #   the same for unicode
+    OS_UNIEQ_NONNULL_CHAR       = 49   #   (must be the same amount as for
+    OS_UNIEQ_CHECKNULL_CHAR     = 50   #   STR, in the same order)
+    OS_UNIEQ_LENGTHOK           = 51   #
+    _OS_offset_uni              = OS_UNI_CONCAT - OS_STR_CONCAT
+
     def __new__(cls, readonly_descrs_fields,
                 write_descrs_fields, write_descrs_arrays,
                 extraeffect=EF_CAN_RAISE,

pypy/jit/codewriter/jtransform.py

             prepare = self._handle_list_call
         elif oopspec_name.startswith('stroruni.'):
             prepare = self._handle_stroruni_call
+        elif oopspec_name == 'str.str2unicode':
+            prepare = self._handle_str2unicode_call
         elif oopspec_name.startswith('virtual_ref'):
             prepare = self._handle_virtual_ref_call
         elif oopspec_name.startswith('libffi_'):
                             [c_func] + [varoftype(T) for T in argtypes],
                             varoftype(resulttype))
         calldescr = self.callcontrol.getcalldescr(op, oopspecindex)
-        func = heaptracker.adr2int(
-            llmemory.cast_ptr_to_adr(c_func.value))
+        if isinstance(c_func.value, str):    # in tests only
+            func = c_func.value
+        else:
+            func = heaptracker.adr2int(
+                llmemory.cast_ptr_to_adr(c_func.value))
         _callinfo_for_oopspec[oopspecindex] = calldescr, func
 
     def _handle_stroruni_call(self, op, oopspec_name, args):
-        if args[0].concretetype.TO == rstr.STR:
+        SoU = args[0].concretetype     # Ptr(STR) or Ptr(UNICODE)
+        if SoU.TO == rstr.STR:
             dict = {"stroruni.concat": EffectInfo.OS_STR_CONCAT,
                     "stroruni.slice":  EffectInfo.OS_STR_SLICE,
                     "stroruni.equal":  EffectInfo.OS_STR_EQUAL,
                     }
-        elif args[0].concretetype.TO == rstr.UNICODE:
+            CHR = lltype.Char
+        elif SoU.TO == rstr.UNICODE:
             dict = {"stroruni.concat": EffectInfo.OS_UNI_CONCAT,
                     "stroruni.slice":  EffectInfo.OS_UNI_SLICE,
                     "stroruni.equal":  EffectInfo.OS_UNI_EQUAL,
                     }
+            CHR = lltype.UniChar
         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_CHAR,
                      "str.eq_slice_char",
-                     [SoU, lltype.Signed, lltype.Signed, lltype.Char],
+                     [SoU, lltype.Signed, lltype.Signed, CHR],
                      lltype.Signed),
                 (EffectInfo.OS_STREQ_NONNULL,
                      "str.eq_nonnull",
                      lltype.Signed),
                 (EffectInfo.OS_STREQ_NONNULL_CHAR,
                      "str.eq_nonnull_char",
-                     [SoU, lltype.Char],
+                     [SoU, CHR],
                      lltype.Signed),
                 (EffectInfo.OS_STREQ_CHECKNULL_CHAR,
                      "str.eq_checknull_char",
-                     [SoU, lltype.Char],
+                     [SoU, CHR],
                      lltype.Signed),
                 (EffectInfo.OS_STREQ_LENGTHOK,
                      "str.eq_lengthok",
                      [SoU, SoU],
                      lltype.Signed),
                 ]:
+                if args[0].concretetype.TO == rstr.UNICODE:
+                    otherindex += EffectInfo._OS_offset_uni
                 self._register_extra_helper(otherindex, othername,
                                             argtypes, resulttype)
         #
         return self._handle_oopspec_call(op, args, dict[oopspec_name])
 
+    def _handle_str2unicode_call(self, op, oopspec_name, args):
+        return self._handle_oopspec_call(op, args, EffectInfo.OS_STR2UNICODE)
+
     # ----------
     # VirtualRefs.
 

pypy/jit/codewriter/test/test_jtransform.py

 class FakeBuiltinCallControl:
     def guess_call_kind(self, op):
         return 'builtin'
-    def getcalldescr(self, op, oopspecindex):
+    def getcalldescr(self, op, oopspecindex=None):
+        assert oopspecindex is not None    # in this test
+        EI = effectinfo.EffectInfo
+        if oopspecindex != EI.OS_ARRAYCOPY:
+            PSTR = lltype.Ptr(rstr.STR)
+            PUNICODE = lltype.Ptr(rstr.UNICODE)
+            INT = lltype.Signed
+            UNICHAR = lltype.UniChar
+            argtypes = {
+             EI.OS_STR2UNICODE:([PSTR], PUNICODE),
+             EI.OS_STR_CONCAT: ([PSTR, PSTR], PSTR),
+             EI.OS_STR_SLICE:  ([PSTR, INT, INT], PSTR),
+             EI.OS_UNI_CONCAT: ([PUNICODE, PUNICODE], PUNICODE),
+             EI.OS_UNI_SLICE:  ([PUNICODE, INT, INT], PUNICODE),
+             EI.OS_UNI_EQUAL:  ([PUNICODE, PUNICODE], lltype.Bool),
+             EI.OS_UNIEQ_SLICE_CHECKNULL:([PUNICODE, INT, INT, PUNICODE], INT),
+             EI.OS_UNIEQ_SLICE_NONNULL:  ([PUNICODE, INT, INT, PUNICODE], INT),
+             EI.OS_UNIEQ_SLICE_CHAR:     ([PUNICODE, INT, INT, UNICHAR], INT),
+             EI.OS_UNIEQ_NONNULL:        ([PUNICODE, PUNICODE], INT),
+             EI.OS_UNIEQ_NONNULL_CHAR:   ([PUNICODE, UNICHAR], INT),
+             EI.OS_UNIEQ_CHECKNULL_CHAR: ([PUNICODE, UNICHAR], INT),
+             EI.OS_UNIEQ_LENGTHOK:       ([PUNICODE, PUNICODE], INT),
+            }
+            argtypes = argtypes[oopspecindex]
+            assert argtypes[0] == [v.concretetype for v in op.args[1:]]
+            assert argtypes[1] == op.result.concretetype
         return 'calldescr-%d' % oopspecindex
     def calldescr_canraise(self, calldescr):
         return False
     assert op1.args[3] == ListOfKind('ref', [v1])
     assert op1.result == v4
 
+def test_str2unicode():
+    # test that the oopspec is present and correctly transformed
+    PSTR = lltype.Ptr(rstr.STR)
+    PUNICODE = lltype.Ptr(rstr.UNICODE)
+    FUNC = lltype.FuncType([PSTR], PUNICODE)
+    func = lltype.functionptr(FUNC, 'll_str2unicode',
+                            _callable=rstr.LLHelpers.ll_str2unicode)
+    v1 = varoftype(PSTR)
+    v2 = varoftype(PUNICODE)
+    op = SpaceOperation('direct_call', [const(func), v1], v2)
+    tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+    op1 = tr.rewrite_operation(op)
+    assert op1.opname == 'residual_call_r_r'
+    assert op1.args[0].value == func
+    assert op1.args[1] == 'calldescr-%d' % effectinfo.EffectInfo.OS_STR2UNICODE
+    assert op1.args[2] == ListOfKind('ref', [v1])
+    assert op1.result == v2
+
+def test_unicode_eq_checknull_char():
+    # test that the oopspec is present and correctly transformed
+    PUNICODE = lltype.Ptr(rstr.UNICODE)
+    FUNC = lltype.FuncType([PUNICODE, PUNICODE], lltype.Bool)
+    func = lltype.functionptr(FUNC, 'll_streq',
+                              _callable=rstr.LLHelpers.ll_streq)
+    v1 = varoftype(PUNICODE)
+    v2 = varoftype(PUNICODE)
+    v3 = varoftype(lltype.Bool)
+    op = SpaceOperation('direct_call', [const(func), v1, v2], v3)
+    tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+    op1 = tr.rewrite_operation(op)
+    assert op1.opname == 'residual_call_r_i'
+    assert op1.args[0].value == func
+    assert op1.args[1] == 'calldescr-%d' % effectinfo.EffectInfo.OS_UNI_EQUAL
+    assert op1.args[2] == ListOfKind('ref', [v1, v2])
+    assert op1.result == v3
+    # test that the OS_UNIEQ_* functions are registered
+    cifo = effectinfo._callinfo_for_oopspec
+    assert effectinfo.EffectInfo.OS_UNIEQ_SLICE_NONNULL in cifo
+    assert effectinfo.EffectInfo.OS_UNIEQ_CHECKNULL_CHAR in cifo
+
 def test_list_ll_arraycopy():
     from pypy.rlib.rgc import ll_arraycopy
     LIST = lltype.GcArray(lltype.Signed)

pypy/jit/metainterp/history.py

     return result
 _const_ptr_for_string = {}
 
+def get_const_ptr_for_unicode(s):
+    from pypy.rpython.annlowlevel import llunicode
+    if not we_are_translated():
+        try:
+            return _const_ptr_for_unicode[s]
+        except KeyError:
+            pass
+    if isinstance(s, str):
+        s = unicode(s)
+    result = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, llunicode(s)))
+    if not we_are_translated():
+        _const_ptr_for_unicode[s] = result
+    return result
+_const_ptr_for_unicode = {}
+
 # ____________________________________________________________
 
 # The TreeLoop class contains a loop or a generalized loop, i.e. a tree

pypy/jit/metainterp/optimize_nopspec.py

 def _optimize_loop(metainterp_sd, old_loop_tokens, loop):
     cpu = metainterp_sd.cpu
     metainterp_sd.logger_noopt.log_loop(loop.inputargs, loop.operations)
+    # XXX the following lines are probably still needed, to discard invalid
+    # loops. bit silly to run a full perfect specialization and throw the
+    # result away.
     finder = PerfectSpecializationFinder(cpu)
     finder.find_nodes_loop(loop, False)
     if old_loop_tokens:
 def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
     cpu = metainterp_sd.cpu    
     metainterp_sd.logger_noopt.log_loop(bridge.inputargs, bridge.operations)
+    # XXX same comment as above applies
     finder = BridgeSpecializationFinder(cpu)
     finder.find_nodes_bridge(bridge)
     if old_loop_tokens:

pypy/jit/metainterp/optimizeopt/intbounds.py

         v1.intbound.make_ge(IntLowerBound(0))
 
     optimize_STRLEN = optimize_ARRAYLEN_GC
+    optimize_UNICODELEN = optimize_ARRAYLEN_GC
 
     def make_int_lt(self, box1, box2):
         v1 = self.getvalue(box1)

pypy/jit/metainterp/optimizeopt/rewrite.py

 from pypy.jit.metainterp.history import ConstInt
 from pypy.jit.metainterp.optimizeutil import _findall
 from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.codewriter.effectinfo import EffectInfo
 
 class OptRewrite(Optimization):
     """Rewrite operations into equivalent, cheaper operations.
 ##            return
 ##        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.getdescr().get_extra_info()
+        if effectinfo is not None:
+            oopspecindex = effectinfo.oopspecindex
+            if oopspecindex == EffectInfo.OS_ARRAYCOPY:
+                if self._optimize_CALL_ARRAYCOPY(op):
+                    return
+        self.emit_operation(op)
+
+    def _optimize_CALL_ARRAYCOPY(self, op):
+        source_value = self.getvalue(op.getarg(1))
+        dest_value = self.getvalue(op.getarg(2))
+        source_start_box = self.get_constant_box(op.getarg(3))
+        dest_start_box = self.get_constant_box(op.getarg(4))
+        length = self.get_constant_box(op.getarg(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
+
 optimize_ops = _findall(OptRewrite, 'optimize_')
-        
 
-
-        

pypy/jit/metainterp/optimizeopt/string.py

 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.history import get_const_ptr_for_unicode
 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.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 specialize
+
+
+class StrOrUnicode(object):
+    def __init__(self, LLTYPE, hlstr, emptystr, chr,
+                 NEWSTR, STRLEN, STRGETITEM, STRSETITEM, COPYSTRCONTENT,
+                 OS_offset):
+        self.LLTYPE = LLTYPE
+        self.hlstr = hlstr
+        self.emptystr = emptystr
+        self.chr = chr
+        self.NEWSTR = NEWSTR
+        self.STRLEN = STRLEN
+        self.STRGETITEM = STRGETITEM
+        self.STRSETITEM = STRSETITEM
+        self.COPYSTRCONTENT = COPYSTRCONTENT
+        self.OS_offset = OS_offset
+
+    def _freeze_(self):
+        return True
+
+mode_string = StrOrUnicode(rstr.STR, annlowlevel.hlstr, '', chr,
+                           rop.NEWSTR, rop.STRLEN, rop.STRGETITEM,
+                           rop.STRSETITEM, rop.COPYSTRCONTENT, 0)
+mode_unicode = StrOrUnicode(rstr.UNICODE, annlowlevel.hlunicode, u'', unichr,
+                            rop.NEWUNICODE, rop.UNICODELEN, rop.UNICODEGETITEM,
+                            rop.UNICODESETITEM, rop.COPYUNICODECONTENT,
+                            EffectInfo._OS_offset_uni)
+
+# ____________________________________________________________
 
 
 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))
+    def getstrlen(self, newoperations, mode):
+        if mode is mode_string:
+            s = self.get_constant_string_spec(mode_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
+            s = self.get_constant_string_spec(mode_unicode)
+            if s is not None:
+                return ConstInt(len(s))
+        if newoperations is None:
+            return None
+        self.ensure_nonnull()
+        box = self.force_box()
+        lengthbox = BoxInt()
+        newoperations.append(ResOperation(mode.STRLEN, [box], lengthbox))
+        return lengthbox
 
-    def get_constant_string(self):
+    @specialize.arg(1)
+    def get_constant_string_spec(self, mode):
         if self.is_constant():
-            s = self.box.getref(lltype.Ptr(rstr.STR))
-            return annlowlevel.hlstr(s)
+            s = self.box.getref(lltype.Ptr(mode.LLTYPE))
+            return mode.hlstr(s)
         else:
             return None
 
-    def string_copy_parts(self, newoperations, targetbox, offsetbox):
+    def string_copy_parts(self, newoperations, targetbox, offsetbox, mode):
         # 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)
+        lengthbox = self.getstrlen(newoperations, mode)
         srcbox = self.force_box()
         return copy_str_content(newoperations, srcbox, targetbox,
-                                CONST_0, offsetbox, lengthbox)
+                                CONST_0, offsetbox, lengthbox, mode)
 
 
 class VAbstractStringValue(virtualize.AbstractVirtualValue):
-    _attrs_ = ()
+    _attrs_ = ('mode',)
+
+    def __init__(self, optimizer, keybox, source_op, mode):
+        virtualize.AbstractVirtualValue.__init__(self, optimizer, keybox,
+                                                 source_op)
+        self.mode = mode
 
     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
+        if self.mode is mode_string:
+            s = self.get_constant_string_spec(mode_string)
+            if s is not None:
+                c_s = get_const_ptr_for_string(s)
+                self.make_constant(c_s)
+                return
+        else:
+            s = self.get_constant_string_spec(mode_unicode)
+            if s is not None:
+                c_s = get_const_ptr_for_unicode(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)
+        lengthbox = self.getstrlen(newoperations, self.mode)
+        newoperations.append(ResOperation(self.mode.NEWSTR, [lengthbox], box))
+        self.string_copy_parts(newoperations, box, CONST_0, self.mode)
 
 
 class VStringPlainValue(VAbstractStringValue):
         assert 0 <= start <= stop <= len(longerlist)
         self._chars = longerlist[start:stop]
 
-    def getstrlen(self, _):
+    def getstrlen(self, _, mode):
         if self._lengthbox is None:
             self._lengthbox = ConstInt(len(self._chars))
         return self._lengthbox
         assert isinstance(charvalue, optimizer.OptValue)
         self._chars[index] = charvalue
 
-    def get_constant_string(self):
+    @specialize.arg(1)
+    def get_constant_string_spec(self, mode):
         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])
+        return mode.emptystr.join([mode.chr(c.box.getint())
+                                   for c in self._chars])
 
-    def string_copy_parts(self, newoperations, targetbox, offsetbox):
+    def string_copy_parts(self, newoperations, targetbox, offsetbox, mode):
         for i in range(len(self._chars)):
             charbox = self._chars[i].force_box()
-            newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
-                                                               offsetbox,
-                                                               charbox], None))
+            newoperations.append(ResOperation(mode.STRSETITEM, [targetbox,
+                                                                offsetbox,
+                                                                charbox],
+                                              None))
             offsetbox = _int_add(newoperations, offsetbox, CONST_1)
         return offsetbox
 
                 value.get_args_for_fail(modifier)
 
     def _make_virtual(self, modifier):
-        return modifier.make_vstrplain()
+        return modifier.make_vstrplain(self.mode is mode_unicode)
 
 
 class VStringConcatValue(VAbstractStringValue):
         self.right = right
         self.lengthbox = lengthbox
 
-    def getstrlen(self, _):
+    def getstrlen(self, _, mode):
         return self.lengthbox
 
-    def get_constant_string(self):
-        s1 = self.left.get_constant_string()
+    @specialize.arg(1)
+    def get_constant_string_spec(self, mode):
+        s1 = self.left.get_constant_string_spec(mode)
         if s1 is None:
             return None
-        s2 = self.right.get_constant_string()
+        s2 = self.right.get_constant_string_spec(mode)
         if s2 is None:
             return None
         return s1 + s2
 
-    def string_copy_parts(self, newoperations, targetbox, offsetbox):
+    def string_copy_parts(self, newoperations, targetbox, offsetbox, mode):
         offsetbox = self.left.string_copy_parts(newoperations, targetbox,
-                                                offsetbox)
+                                                offsetbox, mode)
         offsetbox = self.right.string_copy_parts(newoperations, targetbox,
-                                                 offsetbox)
+                                                 offsetbox, mode)
         return offsetbox
 
     def get_args_for_fail(self, modifier):
             self.right.get_args_for_fail(modifier)
 
     def _make_virtual(self, modifier):
-        return modifier.make_vstrconcat()
+        return modifier.make_vstrconcat(self.mode is mode_unicode)
 
 
 class VStringSliceValue(VAbstractStringValue):
         self.vstart = vstart
         self.vlength = vlength
 
-    def getstrlen(self, _):
+    def getstrlen(self, _, mode):
         return self.vlength.force_box()
 
-    def get_constant_string(self):
+    @specialize.arg(1)
+    def get_constant_string_spec(self, mode):
         if self.vstart.is_constant() and self.vlength.is_constant():
-            s1 = self.vstr.get_constant_string()
+            s1 = self.vstr.get_constant_string_spec(mode)
             if s1 is None:
                 return None
             start = self.vstart.box.getint()
             return s1[start : start + length]
         return None
 
-    def string_copy_parts(self, newoperations, targetbox, offsetbox):
-        lengthbox = self.getstrlen(newoperations)
+    def string_copy_parts(self, newoperations, targetbox, offsetbox, mode):
+        lengthbox = self.getstrlen(newoperations, mode)
         return copy_str_content(newoperations,
                                 self.vstr.force_box(), targetbox,
                                 self.vstart.force_box(), offsetbox,
-                                lengthbox)
+                                lengthbox, mode)
 
     def get_args_for_fail(self, modifier):
         if self.box is None and not modifier.already_seen_virtual(self.keybox):
             self.vlength.get_args_for_fail(modifier)
 
     def _make_virtual(self, modifier):
-        return modifier.make_vstrslice()
+        return modifier.make_vstrslice(self.mode is mode_unicode)
 
 
 def copy_str_content(newoperations, srcbox, targetbox,
-                     srcoffsetbox, offsetbox, lengthbox):
+                     srcoffsetbox, offsetbox, lengthbox, mode):
     if isinstance(srcbox, ConstPtr) and isinstance(srcoffsetbox, Const):
         M = 5
     else:
         # 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)
+            charbox = _strgetitem(newoperations, srcbox, srcoffsetbox, mode)
             srcoffsetbox = _int_add(newoperations, srcoffsetbox, CONST_1)
-            newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
-                                                               offsetbox,
-                                                               charbox], None))
+            newoperations.append(ResOperation(mode.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)
+        op = ResOperation(mode.COPYSTRCONTENT, [srcbox, targetbox,
+                                                srcoffsetbox, offsetbox,
+                                                lengthbox], None)
         newoperations.append(op)
         offsetbox = nextoffsetbox
     return offsetbox
     newoperations.append(ResOperation(rop.INT_SUB, [box1, box2], resbox))
     return resbox
 
-def _strgetitem(newoperations, strbox, indexbox):
+def _strgetitem(newoperations, strbox, indexbox, mode):
     if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
-        s = strbox.getref(lltype.Ptr(rstr.STR))
-        return ConstInt(ord(s.chars[indexbox.getint()]))
+        if mode is mode_string:
+            s = strbox.getref(lltype.Ptr(rstr.STR))
+            return ConstInt(ord(s.chars[indexbox.getint()]))
+        else:
+            s = strbox.getref(lltype.Ptr(rstr.UNICODE))
+            return ConstInt(ord(s.chars[indexbox.getint()]))
     resbox = BoxInt()
-    newoperations.append(ResOperation(rop.STRGETITEM, [strbox, indexbox],
+    newoperations.append(ResOperation(mode.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)
+    def make_vstring_plain(self, box, source_op, mode):
+        vvalue = VStringPlainValue(self.optimizer, box, source_op, mode)
         self.make_equal_to(box, vvalue)
         return vvalue
 
-    def make_vstring_concat(self, box, source_op=None):
-        vvalue = VStringConcatValue(self.optimizer, box, source_op)
+    def make_vstring_concat(self, box, source_op, mode):
+        vvalue = VStringConcatValue(self.optimizer, box, source_op, mode)
         self.make_equal_to(box, vvalue)
         return vvalue
 
-    def make_vstring_slice(self, box, source_op=None):
-        vvalue = VStringSliceValue(self.optimizer, box, source_op)
+    def make_vstring_slice(self, box, source_op, mode):
+        vvalue = VStringSliceValue(self.optimizer, box, source_op, mode)
         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.getdescr().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 optimize_NEWSTR(self, op):
+        self._optimize_NEWSTR(op, mode_string)
+    def optimize_NEWUNICODE(self, op):
+        self._optimize_NEWSTR(op, mode_unicode)
 
-    def opt_call_oopspec_ARRAYCOPY(self, op):
-        source_value = self.getvalue(op.getarg(1))
-        dest_value = self.getvalue(op.getarg(2))
-        source_start_box = self.get_constant_box(op.getarg(3))
-        dest_start_box = self.get_constant_box(op.getarg(4))
-        length = self.get_constant_box(op.getarg(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):
+    def _optimize_NEWSTR(self, op, mode):
         length_box = self.get_constant_box(op.getarg(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.getarg(0), ConstInt):
-                op = ResOperation(rop.NEWSTR, [length_box], op.result)
-            vvalue = self.make_vstring_plain(op.result, op)
+                op = ResOperation(mode.NEWSTR, [length_box], op.result)
+            vvalue = self.make_vstring_plain(op.result, op, mode)
             vvalue.setup(length_box.getint())
         else:
             self.getvalue(op.result).ensure_nonnull()
         value.ensure_nonnull()
         self.emit_operation(op)
 
+    optimize_UNICODESETITEM = optimize_STRSETITEM
+
     def optimize_STRGETITEM(self, op):
+        self._optimize_STRGETITEM(op, mode_string)
+    def optimize_UNICODEGETITEM(self, op):
+        self._optimize_STRGETITEM(op, mode_unicode)
+
+    def _optimize_STRGETITEM(self, op, mode):
         value = self.getvalue(op.getarg(0))
         vindex = self.getvalue(op.getarg(1))
-        vresult = self.strgetitem(value, vindex)
+        vresult = self.strgetitem(value, vindex, mode)
         self.make_equal_to(op.result, vresult)
 
-    def strgetitem(self, value, vindex):
+    def strgetitem(self, value, vindex, mode):
         value.ensure_nonnull()
         #
         if value.is_virtual() and isinstance(value, VStringSliceValue):
                 return value.getitem(vindex.box.getint())
         #
         resbox = _strgetitem(self.optimizer.newoperations,
-                             value.force_box(),vindex.force_box())
+                             value.force_box(),vindex.force_box(), mode)
         return self.getvalue(resbox)
 
     def optimize_STRLEN(self, op):
+        self._optimize_STRLEN(op, mode_string)
+    def optimize_UNICODELEN(self, op):
+        self._optimize_STRLEN(op, mode_unicode)
+
+    def _optimize_STRLEN(self, op, mode):
         value = self.getvalue(op.getarg(0))
-        lengthbox = value.getstrlen(self.optimizer.newoperations)
+        lengthbox = value.getstrlen(self.optimizer.newoperations, mode)
         self.make_equal_to(op.result, self.getvalue(lengthbox))
 
-    def opt_call_oopspec_STR_CONCAT(self, 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.getdescr().get_extra_info()
+        if effectinfo is not None:
+            oopspecindex = effectinfo.oopspecindex
+            for value, meth in opt_call_oopspec_ops:
+                if oopspecindex == value:      # a match with the OS_STR_xxx
+                    if meth(self, op, mode_string):
+                        return
+                    break
+                if oopspecindex == value + EffectInfo._OS_offset_uni:
+                    # a match with the OS_UNI_xxx
+                    if meth(self, op, mode_unicode):
+                        return
+                    break
+            if oopspecindex == EffectInfo.OS_STR2UNICODE:
+                if self.opt_call_str_STR2UNICODE(op):
+                    return
+        self.emit_operation(op)
+
+    def opt_call_str_STR2UNICODE(self, op):
+        # Constant-fold unicode("constant string").
+        # More generally, supporting non-constant but virtual cases is
+        # not obvious, because of the exception UnicodeDecodeError that
+        # can be raised by ll_str2unicode()
+        varg = self.getvalue(op.getarg(1))
+        s = varg.get_constant_string_spec(mode_string)
+        if s is None:
+            return False
+        try:
+            u = unicode(s)
+        except UnicodeDecodeError:
+            return False
+        self.make_constant(op.result, get_const_ptr_for_unicode(u))
+        return True
+
+    def opt_call_stroruni_STR_CONCAT(self, op, mode):
         vleft = self.getvalue(op.getarg(1))
         vright = self.getvalue(op.getarg(2))
         vleft.ensure_nonnull()
         vright.ensure_nonnull()
         newoperations = self.optimizer.newoperations
-        len1box = vleft.getstrlen(newoperations)
-        len2box = vright.getstrlen(newoperations)
+        len1box = vleft.getstrlen(newoperations, mode)
+        len2box = vright.getstrlen(newoperations, mode)
         lengthbox = _int_add(newoperations, len1box, len2box)
-        value = self.make_vstring_concat(op.result, op)
+        value = self.make_vstring_concat(op.result, op, mode)
         value.setup(vleft, vright, lengthbox)
         return True
 
-    def opt_call_oopspec_STR_SLICE(self, op):
+    def opt_call_stroruni_STR_SLICE(self, op, mode):
         newoperations = self.optimizer.newoperations
         vstr = self.getvalue(op.getarg(1))
         vstart = self.getvalue(op.getarg(2))
         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 = self.make_vstring_plain(op.result, op, mode)
             value.setup_slice(vstr._chars, vstart.box.getint(),
                                            vstop.box.getint())
             return True
                                 vstart.force_box())
             vstart = self.getvalue(startbox)
         #
-        value = self.make_vstring_slice(op.result, op)
+        value = self.make_vstring_slice(op.result, op, mode)
         value.setup(vstr, vstart, self.getvalue(lengthbox))
         return True
 
-    def opt_call_oopspec_STR_EQUAL(self, op):
+    def opt_call_stroruni_STR_EQUAL(self, op, mode):
         v1 = self.getvalue(op.getarg(1))
         v2 = self.getvalue(op.getarg(2))
         #
-        l1box = v1.getstrlen(None)
-        l2box = v2.getstrlen(None)
+        l1box = v1.getstrlen(None, mode)
+        l2box = v2.getstrlen(None, mode)
         if (l1box is not None and l2box is not None and
             isinstance(l1box, ConstInt) and
             isinstance(l2box, ConstInt) and
             self.make_constant(op.result, CONST_0)
             return True
         #
-        if self.handle_str_equal_level1(v1, v2, op.result):
+        if self.handle_str_equal_level1(v1, v2, op.result, mode):
             return True
-        if self.handle_str_equal_level1(v2, v1, op.result):
+        if self.handle_str_equal_level1(v2, v1, op.result, mode):
             return True
-        if self.handle_str_equal_level2(v1, v2, op.result):
+        if self.handle_str_equal_level2(v1, v2, op.result, mode):
             return True
-        if self.handle_str_equal_level2(v2, v1, op.result):
+        if self.handle_str_equal_level2(v2, v1, op.result, mode):
             return True
         #
         if v1.is_nonnull() and v2.is_nonnull():
             else:
                 do = EffectInfo.OS_STREQ_NONNULL
             self.generate_modified_call(do, [v1.force_box(),
-                                             v2.force_box()], op.result)
+                                             v2.force_box()], op.result, mode)
             return True
         return False
 
-    def handle_str_equal_level1(self, v1, v2, resultbox):
-        l2box = v2.getstrlen(None)
+    def handle_str_equal_level1(self, v1, v2, resultbox, mode):
+        l2box = v2.getstrlen(None, mode)
         if isinstance(l2box, ConstInt):
             if l2box.value == 0:
-                lengthbox = v1.getstrlen(self.optimizer.newoperations)
+                lengthbox = v1.getstrlen(self.optimizer.newoperations, mode)
                 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)
+                l1box = v1.getstrlen(None, mode)
                 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)
+                    vchar1 = self.strgetitem(v1, optimizer.CVAL_ZERO, mode)
+                    vchar2 = self.strgetitem(v2, optimizer.CVAL_ZERO, mode)
                     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)
+                    vchar = self.strgetitem(v2, optimizer.CVAL_ZERO, mode)
                     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)
+                                                resultbox, mode)
                     return True
         #
         if v2.is_null():
         #
         return False
 
-    def handle_str_equal_level2(self, v1, v2, resultbox):
-        l2box = v2.getstrlen(None)
+    def handle_str_equal_level2(self, v1, v2, resultbox, mode):
+        l2box = v2.getstrlen(None, mode)
         if isinstance(l2box, ConstInt):
             if l2box.value == 1:
-                vchar = self.strgetitem(v2, optimizer.CVAL_ZERO)
+                vchar = self.strgetitem(v2, optimizer.CVAL_ZERO, mode)
                 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)
+                                                 vchar.force_box()], resultbox,
+                                            mode)
                 return True
         #
         if v1.is_virtual() and isinstance(v1, VStringSliceValue):
             self.generate_modified_call(do, [v1.vstr.force_box(),
                                              v1.vstart.force_box(),
                                              v1.vlength.force_box(),
-                                             v2.force_box()], resultbox)
+                                             v2.force_box()], resultbox, mode)
             return True
         return False
 
-    def generate_modified_call(self, oopspecindex, args, result):
+    def generate_modified_call(self, oopspecindex, args, result, mode):
+        oopspecindex += mode.OS_offset
         calldescr, func = callinfo_for_oopspec(oopspecindex)
         op = ResOperation(rop.CALL, [ConstInt(func)] + args, result,
                           descr=calldescr)
 optimize_ops = _findall(OptString, 'optimize_')
 
 def _findall_call_oopspec():
-    prefix = 'opt_call_oopspec_'
+    prefix = 'opt_call_stroruni_'
     result = []
     for name in dir(OptString):
         if name.startswith(prefix):

pypy/jit/metainterp/resume.py

     def make_varray(self, arraydescr):
         return VArrayInfo(arraydescr)
 
-    def make_vstrplain(self):
+    def make_vstrplain(self, is_unicode=False):
+        if is_unicode:
+            return VUniPlainInfo()
         return VStrPlainInfo()
 
-    def make_vstrconcat(self):
+    def make_vstrconcat(self, is_unicode=False):
+        if is_unicode:
+            return VUniConcatInfo()
         return VStrConcatInfo()
 
-    def make_vstrslice(self):
+    def make_vstrslice(self, is_unicode=False):
+        if is_unicode:
+            return VUniSliceInfo()
         return VStrSliceInfo()
 
     def register_virtual_fields(self, virtualbox, fieldboxes):
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
+
+class VUniPlainInfo(AbstractVirtualInfo):
+    """Stands for the unicode string made out of the characters of all
+    fieldnums."""
+
+    @specialize.argtype(1)
+    def allocate(self, decoder, index):
+        length = len(self.fieldnums)
+        string = decoder.allocate_unicode(length)
+        decoder.virtuals_cache[index] = string
+        for i in range(length):
+            decoder.unicode_setitem(string, i, self.fieldnums[i])
+        return string
+
+    def debug_prints(self):
+        debug_print("\tvuniplaininfo length", len(self.fieldnums))
+
+
+class VUniConcatInfo(AbstractVirtualInfo):
+    """Stands for the unicode string made out of the concatenation of two
+    other unicode strings."""
+
+    @specialize.argtype(1)
+    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
+        string = decoder.concat_unicodes(left, right)
+        decoder.virtuals_cache[index] = string
+        return string
+
+    def debug_prints(self):
+        debug_print("\tvuniconcatinfo")
+        for i in self.fieldnums:
+            debug_print("\t\t", str(untag(i)))
+
+
+class VUniSliceInfo(AbstractVirtualInfo):
+    """Stands for the unicode string made out of slicing another
+    unicode string."""
+
+    @specialize.argtype(1)
+    def allocate(self, decoder, index):
+        largerstr, start, length = self.fieldnums
+        string = decoder.slice_unicode(largerstr, start, length)
+        decoder.virtuals_cache[index] = string
+        return string
+
+    def debug_prints(self):
+        debug_print("\tvunisliceinfo")
+        for i in self.fieldnums:
+            debug_print("\t\t", str(untag(i)))
+
 # ____________________________________________________________
 
 class AbstractResumeDataReader(object):
         return self.metainterp.execute_and_record_varargs(
             rop.CALL, [ConstInt(func), strbox, startbox, stopbox], calldescr)
 
+    def allocate_unicode(self, length):
+        return self.metainterp.execute_and_record(rop.NEWUNICODE,
+                                                  None, ConstInt(length))
+
+    def unicode_setitem(self, strbox, index, charnum):
+        charbox = self.decode_box(charnum, INT)
+        self.metainterp.execute_and_record(rop.UNICODESETITEM, None,
+                                           strbox, ConstInt(index), charbox)
+
+    def concat_unicodes(self, str1num, str2num):
+        calldescr, func = callinfo_for_oopspec(EffectInfo.OS_UNI_CONCAT)
+        str1box = self.decode_box(str1num, REF)
+        str2box = self.decode_box(str2num, REF)
+        return self.metainterp.execute_and_record_varargs(
+            rop.CALL, [ConstInt(func), str1box, str2box], calldescr)
+
+    def slice_unicode(self, strnum, startnum, lengthnum):
+        calldescr, func = callinfo_for_oopspec(EffectInfo.OS_UNI_SLICE)
+        strbox = self.decode_box(strnum, REF)
+        startbox = self.decode_box(startnum, INT)
+        lengthbox = self.decode_box(lengthnum, INT)
+        stopbox = self.metainterp.execute_and_record(rop.INT_ADD, None,
+                                                     startbox, lengthbox)
+        return self.metainterp.execute_and_record_varargs(
+            rop.CALL, [ConstInt(func), strbox, startbox, stopbox], calldescr)
+
     def setfield(self, descr, structbox, fieldnum):
         if descr.is_pointer_field():
             kind = REF
         result = funcptr(str, start, start + length)
         return lltype.cast_opaque_ptr(llmemory.GCREF, result)
 
+    def allocate_unicode(self, length):
+        return self.cpu.bh_newunicode(length)
+
+    def unicode_setitem(self, str, index, charnum):
+        char = self.decode_int(charnum)
+        self.cpu.bh_unicodesetitem(str, index, char)
+
+    def concat_unicodes(self, str1num, str2num):
+        str1 = self.decode_ref(str1num)
+        str2 = self.decode_ref(str2num)
+        str1 = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), str1)
+        str2 = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), str2)
+        funcptr = funcptr_for_oopspec(EffectInfo.OS_UNI_CONCAT)
+        result = funcptr(str1, str2)
+        return lltype.cast_opaque_ptr(llmemory.GCREF, result)
+
+    def slice_unicode(self, strnum, startnum, lengthnum):
+        str = self.decode_ref(strnum)
+        start = self.decode_int(startnum)
+        length = self.decode_int(lengthnum)
+        str = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), str)
+        funcptr = funcptr_for_oopspec(EffectInfo.OS_UNI_SLICE)
+        result = funcptr(str, start, start + length)
+        return lltype.cast_opaque_ptr(llmemory.GCREF, result)
+
     def setfield(self, descr, struct, fieldnum):
         if descr.is_pointer_field():
             newvalue = self.decode_ref(fieldnum)

pypy/jit/metainterp/test/oparser.py

 
 from pypy.jit.metainterp.history import TreeLoop, BoxInt, ConstInt,\
      ConstObj, ConstPtr, Box, BasicFailDescr, BoxFloat, ConstFloat,\
-     LoopToken, get_const_ptr_for_string
+     LoopToken, get_const_ptr_for_string, get_const_ptr_for_unicode
 from pypy.jit.metainterp.resoperation import rop, ResOperation, ResOpWithDescr, N_aryOp
 from pypy.jit.metainterp.typesystem import llhelper
 from pypy.jit.codewriter.heaptracker import adr2int
         except ValueError:
             if self.is_float(arg):
                 return ConstFloat(float(arg))
-            if arg.startswith('"') or arg.startswith("'"):
+            if (arg.startswith('"') or arg.startswith("'") or
+                arg.startswith('s"')):
                 # XXX ootype
-                info = arg.strip("'\"")
+                info = arg[1:].strip("'\"")
                 return get_const_ptr_for_string(info)
+            if arg.startswith('u"'):
+                # XXX ootype
+                info = arg[1:].strip("'\"")
+                return get_const_ptr_for_unicode(info)
             if arg.startswith('ConstClass('):
                 name = arg[len('ConstClass('):-1]
                 return self.get_const(name, 'class')

pypy/jit/metainterp/test/test_optimizefindnode.py

                             EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE))
     arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                  EffectInfo([], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY))
-    strconcatdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                 EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR_CONCAT))
-    slicedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                 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))
+
+    for _name, _os in [
+        ('strconcatdescr',               'OS_STR_CONCAT'),
+        ('strslicedescr',                'OS_STR_SLICE'),
+        ('strequaldescr',                'OS_STR_EQUAL'),
+        ('streq_slice_checknull_descr',  'OS_STREQ_SLICE_CHECKNULL'),
+        ('streq_slice_nonnull_descr',    'OS_STREQ_SLICE_NONNULL'),
+        ('streq_slice_char_descr',       'OS_STREQ_SLICE_CHAR'),
+        ('streq_nonnull_descr',          'OS_STREQ_NONNULL'),
+        ('streq_nonnull_char_descr',     'OS_STREQ_NONNULL_CHAR'),
+        ('streq_checknull_char_descr',   'OS_STREQ_CHECKNULL_CHAR'),
+        ('streq_lengthok_descr',         'OS_STREQ_LENGTHOK'),
+        ]:
+        _oopspecindex = getattr(EffectInfo, _os)
+        locals()[_name] = \
+            cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                EffectInfo([], [], [], oopspecindex=_oopspecindex))
+        #
+        _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
+        locals()[_name.replace('str', 'unicode')] = \
+            cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                EffectInfo([], [], [], oopspecindex=_oopspecindex))
+
+    s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
+    #
 
     class LoopToken(AbstractDescr):
         pass

pypy/jit/metainterp/test/test_optimizeopt.py

         i0 = strlen(p0)
         jump(p0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not', expected)
 
     def test_addsub_const(self):
         ops = """
         """
         self.optimize_loop(ops, 'Not, Not', expected)
 
+    # ----------
+    def optimize_strunicode_loop(self, ops, spectext, optops):
+        # check with the arguments passed in
+        self.optimize_loop(ops, spectext, optops)
+        # check with replacing 'str' with 'unicode' everywhere
+        self.optimize_loop(ops.replace('str','unicode').replace('s"', 'u"'),
+                           spectext,
+                           optops.replace('str','unicode').replace('s"', 'u"'))
+
     def test_newstr_1(self):
         ops = """
         [i0]
         [i0]
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not', expected)
 
     def test_newstr_2(self):
         ops = """
         [i0, i1]
         jump(i1, i0)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not, Not', expected)
 
     def test_str_concat_1(self):
         ops = """
         copystrcontent(p2, p3, 0, i4, i5)
         jump(p2, p3)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not, Not', expected)
 
     def test_str_concat_vstr2_str(self):
         ops = """
         copystrcontent(p2, p3, 0, 2, i4)
         jump(i1, i0, p3)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected)
 
     def test_str_concat_str_vstr2(self):
         ops = """
         i6 = int_add(i5, 1)      # will be killed by the backend
         jump(i1, i0, p3)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected)
 
     def test_str_concat_str_str_str(self):
         ops = """
         copystrcontent(p3, p5, 0, i12b, i3b)
         jump(p2, p3, p5)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected)
 
     def test_str_concat_str_cstr1(self):
         ops = """
         [p2]
-        p3 = call(0, p2, "x", descr=strconcatdescr)
+        p3 = call(0, p2, s"x", descr=strconcatdescr)
         jump(p3)
         """
         expected = """
         i5 = int_add(i4, 1)      # will be killed by the backend
         jump(p3)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not', expected)
 
     def test_str_concat_consts(self):
         ops = """
         []
-        p1 = same_as("ab")
-        p2 = same_as("cde")
+        p1 = same_as(s"ab")
+        p2 = same_as(s"cde")
         p3 = call(0, p1, p2, descr=strconcatdescr)
         escape(p3)
         jump()
         """
         expected = """
         []
-        escape("abcde")
+        escape(s"abcde")
         jump()
         """
-        self.optimize_loop(ops, '', expected)
+        self.optimize_strunicode_loop(ops, '', expected)
 
     def test_str_slice_1(self):
         ops = """
         [p1, i1, i2]
-        p2 = call(0, p1, i1, i2, descr=slicedescr)
+        p2 = call(0, p1, i1, i2, descr=strslicedescr)
         jump(p2, i1, i2)
         """
         expected = """
         copystrcontent(p1, p2, i1, 0, i3)
         jump(p2, i1, i2)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected)
 
     def test_str_slice_2(self):
         ops = """
         [p1, i2]
-        p2 = call(0, p1, 0, i2, descr=slicedescr)
+        p2 = call(0, p1, 0, i2, descr=strslicedescr)
         jump(p2, i2)
         """
         expected = """
         copystrcontent(p1, p2, 0, 0, i2)
         jump(p2, i2)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop(ops, 'Not, Not', expected)
 
     def test_str_slice_3(self):
         ops = """
         [p1, i1, i2, i3, i4]
-        p2 = call(0, p1, i1, i2, descr=slicedescr)
-        p3 = call(0, p2, i3, i4, descr=slicedescr)
+        p2 = call(0, p1, i1, i2, descr=strslicedescr)
+        p3 = call(0, p2, i3, i4, descr=strslicedescr)
         jump(p3, i1, i2, i3, i4)
         """