[pypy-commit] pypy gc_no_cleanup_nursery: merge default

fijal noreply at buildbot.pypy.org
Mon Sep 1 21:16:22 CEST 2014


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: gc_no_cleanup_nursery
Changeset: r73277:a9dc0d3a9f93
Date: 2014-09-01 13:15 -0600
http://bitbucket.org/pypy/pypy/changeset/a9dc0d3a9f93/

Log:	merge default

diff --git a/_pytest/README-BEFORE-UPDATING b/_pytest/README-BEFORE-UPDATING
new file mode 100644
--- /dev/null
+++ b/_pytest/README-BEFORE-UPDATING
@@ -0,0 +1,17 @@
+This is PyPy's code of the pytest lib.  We don't expect to upgrade it
+very often, but once we do:
+
+    WARNING!
+
+    WE HAVE MADE A FEW TWEAKS HERE!
+
+Please be sure that you don't just copy the newer version from
+upstream without checking the few changes that we did.  This
+can be done like this:
+
+    cd <this directory>
+    hg log . -v | less
+
+then search for all " _pytest/" in that list to know which are the
+relevant checkins.  (Look for the checkins that only edit one
+or two files in this directory.)
diff --git a/_pytest/resultlog.py b/_pytest/resultlog.py
--- a/_pytest/resultlog.py
+++ b/_pytest/resultlog.py
@@ -53,16 +53,23 @@
         self.config = config
         self.logfile = logfile # preferably line buffered
 
-    def write_log_entry(self, testpath, lettercode, longrepr):
+    def write_log_entry(self, testpath, lettercode, longrepr, sections=None):
         py.builtin.print_("%s %s" % (lettercode, testpath), file=self.logfile)
         for line in longrepr.splitlines():
             py.builtin.print_(" %s" % line, file=self.logfile)
+        if sections is not None:
+            for title, content in sections:
+                py.builtin.print_(" ---------- %s ----------" % (title,),
+                                  file=self.logfile)
+                for line in content.splitlines():
+                    py.builtin.print_(" %s" % line, file=self.logfile)
 
     def log_outcome(self, report, lettercode, longrepr):
         testpath = getattr(report, 'nodeid', None)
         if testpath is None:
             testpath = report.fspath
-        self.write_log_entry(testpath, lettercode, longrepr)
+        self.write_log_entry(testpath, lettercode, longrepr,
+                             getattr(report, 'sections', None))
 
     def pytest_runtest_logreport(self, report):
         if report.when != "call" and report.passed:
diff --git a/py/README-BEFORE-UPDATING b/py/README-BEFORE-UPDATING
new file mode 100644
--- /dev/null
+++ b/py/README-BEFORE-UPDATING
@@ -0,0 +1,17 @@
+This is PyPy's code of the py lib.  We don't expect to upgrade it
+very often, but once we do:
+
+    WARNING!
+
+    WE HAVE MADE A FEW TWEAKS HERE!
+
+Please be sure that you don't just copy the newer version from
+upstream without checking the few changes that we did.  This
+can be done like this:
+
+    cd <this directory>
+    hg log . -v | less
+
+then search for all " py/" in that list to know which are the
+relevant checkins.  (Look for the checkins that only edit one
+or two files in this directory.)
diff --git a/py/_path/local.py b/py/_path/local.py
--- a/py/_path/local.py
+++ b/py/_path/local.py
@@ -750,7 +750,8 @@
     mkdtemp = classmethod(mkdtemp)
 
     def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3,
-                          lock_timeout = 172800):   # two days
+                          lock_timeout = 172800,   # two days
+                          min_timeout = 300):      # five minutes
         """ return unique directory with a number greater than the current
             maximum one.  The number is assumed to start directly after prefix.
             if keep is true directories with a number less than (maxnum-keep)
@@ -818,6 +819,20 @@
             for path in rootdir.listdir():
                 num = parse_num(path)
                 if num is not None and num <= (maxnum - keep):
+                    if min_timeout:
+                        # NB: doing this is needed to prevent (or reduce
+                        # a lot the chance of) the following situation:
+                        # 'keep+1' processes call make_numbered_dir() at
+                        # the same time, they create dirs, but then the
+                        # last process notices the first dir doesn't have
+                        # (yet) a .lock in it and kills it.
+                        try:
+                            t1 = path.lstat().mtime
+                            t2 = lockfile.lstat().mtime
+                            if abs(t2-t1) < min_timeout:
+                                continue   # skip directories too recent
+                        except py.error.Error:
+                            continue   # failure to get a time, better skip
                     lf = path.join('.lock')
                     try:
                         t1 = lf.lstat().mtime
diff --git a/pypy/module/_pypyjson/interp_encoder.py b/pypy/module/_pypyjson/interp_encoder.py
--- a/pypy/module/_pypyjson/interp_encoder.py
+++ b/pypy/module/_pypyjson/interp_encoder.py
@@ -37,16 +37,14 @@
         sb = StringBuilder(len(u))
         sb.append_slice(s, 0, first)
     else:
+        # We used to check if 'u' contains only safe characters, and return
+        # 'w_string' directly.  But this requires an extra pass over all
+        # characters, and the expected use case of this function, from
+        # json.encoder, will anyway re-encode a unicode result back to
+        # a string (with the ascii encoding).  This requires two passes
+        # over the characters.  So we may as well directly turn it into a
+        # string here --- only one pass.
         u = space.unicode_w(w_string)
-        for i in range(len(u)):
-            c = u[i]
-            if c >= u' ' and c <= u'~' and c != u'"' and c != u'\\':
-                pass
-            else:
-                break
-        else:
-            # the input is a unicode with only non-special ascii chars
-            return w_string
         sb = StringBuilder(len(u))
         first = 0
 
diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py
--- a/pypy/module/_pypyjson/test/test__pypyjson.py
+++ b/pypy/module/_pypyjson/test/test__pypyjson.py
@@ -192,14 +192,14 @@
 
     def test_raw_encode_basestring_ascii(self):
         import _pypyjson
-        def check(s, expected_type=str):
+        def check(s):
             s = _pypyjson.raw_encode_basestring_ascii(s)
-            assert type(s) is expected_type
+            assert type(s) is str
             return s
         assert check("") == ""
-        assert check(u"", expected_type=unicode) == u""
+        assert check(u"") == ""
         assert check("abc ") == "abc "
-        assert check(u"abc ", expected_type=unicode) == u"abc "
+        assert check(u"abc ") == "abc "
         raises(UnicodeDecodeError, check, "\xc0")
         assert check("\xc2\x84") == "\\u0084"
         assert check("\xf0\x92\x8d\x85") == "\\ud808\\udf45"
diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py
--- a/pypy/module/pypyjit/test_pypy_c/test_call.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_call.py
@@ -17,13 +17,18 @@
             # now we can inline it as call assembler
             i = 0
             j = 0
-            while i < 20:
+            while i < 25:
                 i += 1
                 j += rec(100) # ID: call_rec
             return j
         #
-        log = self.run(fn, [], threshold=18)
-        loop, = log.loops_by_filename(self.filepath)
+        # NB. the parameters below are a bit ad-hoc.  After 16 iterations,
+        # the we trace from the "while" and reach a "trace too long".  Then
+        # in the next execution, we trace the "rec" function from start;
+        # that's "functrace" below.  Then after one or two extra iterations
+        # we try again from "while", and this time we succeed.
+        log = self.run(fn, [], threshold=20)
+        functrace, loop = log.loops_by_filename(self.filepath)
         assert loop.match_by_id('call_rec', """
             ...
             p53 = call_assembler(..., descr=...)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_ffi.py b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
--- a/pypy/module/pypyjit/test_pypy_c/test_ffi.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
@@ -340,30 +340,19 @@
         guard_value(p166, ConstPtr(ptr72), descr=...)
         p167 = call(ConstClass(_ll_0_alloc_with_del___), descr=<Callr . EF=4>)
         guard_no_exception(descr=...)
-        i168 = call(ConstClass(_ll_1_raw_malloc_varsize__Signed), 6, descr=<Calli . i EF=4 OS=110>)
-        i169 = int_add(i168, i97)
-        i170 = int_sub(i160, i106)
-        setfield_gc(p167, i168, descr=<FieldU pypy.module._cffi_backend.cdataobj.W_CData.inst__cdata .>)
+        i112 = int_sub(i160, -32768)
         setfield_gc(p167, ConstPtr(null), descr=<FieldP pypy.module._cffi_backend.cdataobj.W_CData.inst__lifeline_ .+>)
-        setfield_gc(p167, ConstPtr(ptr89), descr=<FieldP pypy.module._cffi_backend.cdataobj.W_CData.inst_ctype .+>)
-        i171 = uint_gt(i170, i108)
-        guard_false(i171, descr=...)
-        i172 = int_sub(i160, -32768)
-        i173 = int_and(i172, 65535)
-        i174 = int_add(i173, -32768)
-        setarrayitem_raw(i169, 0, i174, descr=<ArrayS 2>)
-        i175 = int_add(i168, i121)
-        i176 = int_sub(i160, i130)
-        i177 = uint_gt(i176, i132)
-        guard_false(i177, descr=...)
-        setarrayitem_raw(i175, 0, i174, descr=<ArrayS 2>)
-        i178 = int_add(i168, i140)
-        i179 = int_sub(i160, i149)
-        i180 = uint_gt(i179, i151)
-        guard_false(i180, descr=...)
-        setarrayitem_raw(i178, 0, i174, descr=<ArrayS 2>)
+        setfield_gc(p167, ConstPtr(ptr85), descr=<FieldP pypy.module._cffi_backend.cdataobj.W_CData.inst_ctype .+>)
+        i114 = uint_gt(i112, 65535)
+        guard_false(i114, descr=...)
+        i115 = int_and(i112, 65535)
+        i116 = int_add(i115, -32768)
         --TICK--
-        i183 = arraylen_gc(p67, descr=<ArrayP .>)
-        i184 = arraylen_gc(p92, descr=<ArrayP .>)
+        i119 = call(ConstClass(_ll_1_raw_malloc_varsize__Signed), 6, descr=<Calli . i EF=4 OS=110>)
+        raw_store(i119, 0, i116, descr=<ArrayS 2>)
+        raw_store(i119, 2, i116, descr=<ArrayS 2>)
+        raw_store(i119, 4, i116, descr=<ArrayS 2>)
+        setfield_gc(p167, i119, descr=<FieldU pypy.module._cffi_backend.cdataobj.W_CData.inst__cdata .+>)
+        i123 = arraylen_gc(p67, descr=<ArrayP .>)
         jump(..., descr=...)
         """)
diff --git a/pypy/module/sys/initpath.py b/pypy/module/sys/initpath.py
--- a/pypy/module/sys/initpath.py
+++ b/pypy/module/sys/initpath.py
@@ -18,6 +18,13 @@
 _WIN32 = sys.platform == 'win32'
 
 
+def _exists_and_is_executable(fn):
+    # os.access checks using the user's real uid and gid.
+    # Since pypy should not be run setuid/setgid, this
+    # should be sufficient.
+    return os.path.isfile(fn) and os.access(fn, os.X_OK)
+
+
 def find_executable(executable):
     """
     Return the absolute path of the executable, by looking into PATH and
@@ -34,14 +41,14 @@
         if path:
             for dir in path.split(os.pathsep):
                 fn = os.path.join(dir, executable)
-                if os.path.isfile(fn):
+                if _exists_and_is_executable(fn):
                     executable = fn
                     break
     executable = rpath.rabspath(executable)
 
     # 'sys.executable' should not end up being an non-existing file;
     # just use '' in this case. (CPython issue #7774)
-    return executable if os.path.isfile(executable) else ''
+    return executable if _exists_and_is_executable(executable) else ''
 
 
 def _readlink_maybe(filename):
diff --git a/pypy/module/sys/test/test_initpath.py b/pypy/module/sys/test/test_initpath.py
--- a/pypy/module/sys/test/test_initpath.py
+++ b/pypy/module/sys/test/test_initpath.py
@@ -57,6 +57,7 @@
     a.join('pypy').ensure(file=True)
     b.join('pypy').ensure(file=True)
     #
+    monkeypatch.setattr(os, 'access', lambda x, y: True)
     # if there is already a slash, don't do anything
     monkeypatch.chdir(tmpdir)
     assert find_executable('a/pypy') == a.join('pypy')
@@ -82,7 +83,11 @@
     # if pypy is found but it's not a file, ignore it
     c.join('pypy').ensure(dir=True)
     assert find_executable('pypy') == a.join('pypy')
+    # if pypy is found but it's not executable, ignore it
+    monkeypatch.setattr(os, 'access', lambda x, y: False)
+    assert find_executable('pypy') == ''
     #
+    monkeypatch.setattr(os, 'access', lambda x, y: True)
     monkeypatch.setattr(initpath, 'we_are_translated', lambda: True)
     monkeypatch.setattr(initpath, '_WIN32', True)
     monkeypatch.setenv('PATH', str(a))
diff --git a/rpython/jit/backend/arm/callbuilder.py b/rpython/jit/backend/arm/callbuilder.py
--- a/rpython/jit/backend/arm/callbuilder.py
+++ b/rpython/jit/backend/arm/callbuilder.py
@@ -80,15 +80,6 @@
                 self.mc.gen_load_int(r.ip.value, n)
                 self.mc.SUB_rr(r.sp.value, r.sp.value, r.ip.value)
 
-    def _must_remap_fnloc(self):
-        fnloc = self.fnloc
-        if fnloc.is_stack():
-            return True
-        if self.is_call_release_gil:
-            if fnloc is r.r5 or fnloc is r.r6 or fnloc is r.r7:
-                return True
-        return False
-
     def call_releasegil_addr_and_move_real_arguments(self, fastgil):
         assert self.is_call_release_gil
         assert not self.asm._is_asmgcc()
@@ -121,7 +112,7 @@
         self.mc.STREX(r.r3.value, r.ip.value, r.r6.value, c=c.EQ)
                                                  # try to claim the lock
         self.mc.CMP_ri(r.r3.value, 0, cond=c.EQ) # did this succeed?
-        self.mc.DMB(c=c.EQ)
+        self.mc.DMB()
         # the success of the lock acquisition is defined by
         # 'EQ is true', or equivalently by 'r3 == 0'.
         #
@@ -182,6 +173,13 @@
 
 
 class SoftFloatCallBuilder(ARMCallbuilder):
+    # XXX Maybe we could kill this class and unify the remaining two
+    # XXX classes, by carefully checking if all methods here are doing
+    # XXX the exact same thing as the methods from HardFloatCallBuilder,
+    # XXX but simply forcing all BoxFloat arguments to be longlongs
+    # XXX (i.e. ignoring 'f' in favour of 'L'), and the same with
+    # XXX single-float arguments (ignoring 'S' in favour of 'i');
+    # XXX and the same for the return value.
 
     def get_result_locs(self):
         if self.resloc is None:
@@ -268,7 +266,7 @@
         # or on the stack, which we can not access later
         # If this happens to be the case we remap the register to r4 and use r4
         # to call the function
-        if self.fnloc in r.argument_regs or self._must_remap_fnloc():
+        if not self.fnloc.is_imm():
             non_float_locs.append(self.fnloc)
             non_float_regs.append(r.r4)
             self.fnloc = r.r4
@@ -285,29 +283,23 @@
 
     def get_next_vfp(self, tp):
         assert tp in 'fS'
-        if self.next_arg_vfp == -1:
-            return None
-        if tp == 'S':
+        if tp == 'f':
+            # 64bit double
+            i = max(self.next_arg_vfp, (self.next_arg_svfp + 1) >> 1)
+            if i >= len(r.vfp_argument_regs):
+                self.next_arg_svfp = 1000    # stop that sequence too
+                return None
+            self.next_arg_vfp = i + 1
+            return r.vfp_argument_regs[i]
+        else:
+            # 32bit float
             i = self.next_arg_svfp
-            next_vfp = (i >> 1) + 1
-            if not (i + 1) & 1: # i is even
-                self.next_arg_vfp = max(self.next_arg_vfp, next_vfp)
-                self.next_arg_svfp = self.next_arg_vfp << 1
-            else:
-                self.next_arg_svfp += 1
-                self.next_arg_vfp = next_vfp
-            lst = r.svfp_argument_regs
-        else: # 64bit double
-            i = self.next_arg_vfp
-            self.next_arg_vfp += 1
-            if self.next_arg_svfp >> 1 == i:
-                self.next_arg_svfp = self.next_arg_vfp << 1
-            lst = r.vfp_argument_regs
-        try:
-            return lst[i]
-        except IndexError:
-            self.next_arg_vfp = self.next_arg_svfp = -1
-            return None
+            if not (i & 1):     # if i is even
+                i = max(i, self.next_arg_vfp << 1)
+            if i >= len(r.svfp_argument_regs):
+                return None
+            self.next_arg_svfp = i + 1
+            return r.svfp_argument_regs[i]
 
     def prepare_arguments(self):
         non_float_locs = []
@@ -316,34 +308,64 @@
         float_regs = []
         stack_args = []
         singlefloats = None
+        longlong_mask = 0
 
         arglocs = self.arglocs
         argtypes = self.argtypes
 
-        count = 0                      # stack alignment counter
+        r_register_count = 0
         on_stack = 0
+
         for i in range(len(arglocs)):
             argtype = INT
             if i < len(argtypes) and argtypes[i] == 'S':
                 argtype = argtypes[i]
             arg = arglocs[i]
+
             if arg.is_float():
-                argtype = FLOAT
-                reg = self.get_next_vfp(argtype)
-                if reg:
-                    assert len(float_regs) < len(r.vfp_argument_regs)
-                    float_locs.append(arg)
-                    assert reg not in float_regs
-                    float_regs.append(reg)
-                else:  # float argument that needs to go on the stack
-                    if count % 2 != 0:
-                        stack_args.append(None)
-                        count = 0
-                        on_stack += 1
-                    stack_args.append(arg)
-                    on_stack += 2
+                if i < len(argtypes) and argtypes[i] == 'L':
+                    # A longlong argument.  It uses two regular argument
+                    # positions, but aligned to an even number.  This is
+                    # a bit strange, but it is the case even for registers:
+                    # it can be in r0-r1 or in r2-r3 but not in r1-r2.
+                    assert arg.is_float()
+                    if r_register_count == 0:
+                        # will temporarily load the register into d8
+                        float_locs.append(arg)
+                        float_regs.append(r.d8)
+                        longlong_mask |= 1
+                        r_register_count = 2
+                        continue
+                    elif r_register_count <= 2:
+                        # will temporarily load the register into d9
+                        float_locs.append(arg)
+                        float_regs.append(r.d9)
+                        longlong_mask |= 2
+                        r_register_count = 4
+                        continue
+                    elif r_register_count == 3:
+                        r_register_count = 4
+                else:
+                    # A 64-bit float argument.  Goes into the next free v#
+                    # register, or if none, to the stack aligned to an
+                    # even number of words.
+                    argtype = FLOAT
+                    reg = self.get_next_vfp(argtype)
+                    if reg:
+                        float_locs.append(arg)
+                        assert reg not in float_regs
+                        float_regs.append(reg)
+                        continue
+                # float or longlong argument that needs to go on the stack
+                if on_stack & 1:   # odd: realign
+                    stack_args.append(None)
+                    on_stack += 1
+                stack_args.append(arg)
+                on_stack += 2
+
             elif argtype == 'S':
-                # Singlefloat argument
+                # Singlefloat (32-bit) argument.  Goes into the next free
+                # v# register, or if none, to the stack in a single word.
                 if singlefloats is None:
                     singlefloats = []
                 tgt = self.get_next_vfp(argtype)
@@ -351,32 +373,36 @@
                     singlefloats.append((arg, tgt))
                 else:  # Singlefloat argument that needs to go on the stack
                        # treated the same as a regular core register argument
-                    count += 1
+                    stack_args.append(arg)
                     on_stack += 1
-                    stack_args.append(arg)
             else:
-                if len(non_float_regs) < len(r.argument_regs):
-                    reg = r.argument_regs[len(non_float_regs)]
+                # Regular one-word argument.  Goes into the next register
+                # free from the list r0, r1, r2, r3, or to the stack.
+                if r_register_count < len(r.argument_regs):
+                    reg = r.argument_regs[r_register_count]
+                    r_register_count += 1
                     non_float_locs.append(arg)
                     non_float_regs.append(reg)
                 else:  # non-float argument that needs to go on the stack
-                    count += 1
+                    stack_args.append(arg)
                     on_stack += 1
-                    stack_args.append(arg)
+
         # align the stack
-        if count % 2 != 0:
+        if on_stack & 1:    # odd: realign
             stack_args.append(None)
             on_stack += 1
         self._push_stack_args(stack_args, on_stack*WORD)
+
         # Check that the address of the function we want to call is not
         # currently stored in one of the registers used to pass the arguments
         # or on the stack, which we can not access later
         # If this happens to be the case we remap the register to r4 and use r4
         # to call the function
-        if self.fnloc in non_float_regs or self._must_remap_fnloc():
+        if not self.fnloc.is_imm():
             non_float_locs.append(self.fnloc)
             non_float_regs.append(r.r4)
             self.fnloc = r.r4
+
         # remap values stored in vfp registers
         remap_frame_layout(self.asm, float_locs, float_regs, r.vfp_ip)
         if singlefloats:
@@ -392,13 +418,22 @@
                     src = r.ip
                 if src.is_core_reg():
                     self.mc.VMOV_cs(dest.value, src.value)
+
         # remap values stored in core registers
         remap_frame_layout(self.asm, non_float_locs, non_float_regs, r.ip)
+        if longlong_mask & 1:
+            self.mc.FMRRD(r.r0.value, r.r1.value, r.d8.value)
+        if longlong_mask & 2:
+            self.mc.FMRRD(r.r2.value, r.r3.value, r.d9.value)
+
 
     def load_result(self):
         resloc = self.resloc
         if self.restype == 'S':
             self.mc.VMOV_sc(resloc.value, r.s0.value)
+        elif self.restype == 'L':
+            assert resloc.is_vfp_reg()
+            self.mc.FMDRR(resloc.value, r.r0.value, r.r1.value)
         # ensure the result is wellformed and stored in the correct location
         if resloc is not None and resloc.is_core_reg():
             self._ensure_result_bit_extension(resloc,
@@ -408,7 +443,10 @@
         if self.resloc is None:
             return [], []
         if self.resloc.is_vfp_reg():
-            return [], [r.d0]
+            if self.restype == 'L':      # long long
+                return [r.r0, r.r1], []
+            else:
+                return [], [r.d0]
         assert self.resloc.is_core_reg()
         return [r.r0], []
 
diff --git a/rpython/jit/backend/arm/codebuilder.py b/rpython/jit/backend/arm/codebuilder.py
--- a/rpython/jit/backend/arm/codebuilder.py
+++ b/rpython/jit/backend/arm/codebuilder.py
@@ -332,13 +332,17 @@
                     | (rd & 0xF) << 12
                     | (rn & 0xF) << 16)
 
-    def DMB(self, c=cond.AL):
-        self.write32(c << 28 | 0x157ff05f)
+    def DMB(self):
+        # note: 'cond' is only permitted on Thumb here
+        self.write32(0xf57ff05f)
 
     DIV = binary_helper_call('int_div')
     MOD = binary_helper_call('int_mod')
     UDIV = binary_helper_call('uint_div')
 
+    FMDRR = VMOV_cr     # uh, there are synonyms?
+    FMRRD = VMOV_rc
+
     def _encode_reg_list(self, instr, regs):
         for reg in regs:
             instr |= 0x1 << reg
diff --git a/rpython/jit/backend/arm/runner.py b/rpython/jit/backend/arm/runner.py
--- a/rpython/jit/backend/arm/runner.py
+++ b/rpython/jit/backend/arm/runner.py
@@ -20,7 +20,7 @@
     IS_64_BIT = False
 
     supports_floats = True
-    supports_longlong = False     # incomplete, notably in callbuilder.py
+    supports_longlong = True
     supports_singlefloats = True
 
     from rpython.jit.backend.arm.arch import JITFRAME_FIXED_SIZE
diff --git a/rpython/jit/backend/arm/test/test_callbuilder.py b/rpython/jit/backend/arm/test/test_callbuilder.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/arm/test/test_callbuilder.py
@@ -0,0 +1,47 @@
+from rpython.jit.backend.arm.callbuilder import HardFloatCallBuilder
+from rpython.jit.backend.arm import registers as r
+
+
+
+def test_hf_vfp_registers_all_singlefloat():
+    hf = HardFloatCallBuilder.__new__(HardFloatCallBuilder)
+    got = [hf.get_next_vfp('S') for i in range(18)]
+    assert got == [r.s0, r.s1, r.s2, r.s3, r.s4, r.s5, r.s6, r.s7,
+                   r.s8, r.s9, r.s10, r.s11, r.s12, r.s13, r.s14, r.s15,
+                   None, None]
+
+def test_hf_vfp_registers_all_doublefloat():
+    hf = HardFloatCallBuilder.__new__(HardFloatCallBuilder)
+    got = [hf.get_next_vfp('f') for i in range(10)]
+    assert got == [r.d0, r.d1, r.d2, r.d3, r.d4, r.d5, r.d6, r.d7,
+                   None, None]
+
+def test_hf_vfp_registers_mixture():
+    hf = HardFloatCallBuilder.__new__(HardFloatCallBuilder)
+    got = [hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('S'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('S'), hf.get_next_vfp('f')]
+    assert got == [r.s0,  r.d1,
+                   r.s1,  r.d2,
+                   r.s6,  r.d4,
+                   r.s7,  r.d5,
+                   r.s12, r.d7,
+                   r.s13, None,
+                   None,  None]
+
+def test_hf_vfp_registers_mixture_2():
+    hf = HardFloatCallBuilder.__new__(HardFloatCallBuilder)
+    got = [hf.get_next_vfp('f'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('f'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('f'), hf.get_next_vfp('f'),
+           hf.get_next_vfp('f'), hf.get_next_vfp('S'),
+           hf.get_next_vfp('f'), hf.get_next_vfp('S')]
+    assert got == [r.d0, r.d1,
+                   r.d2, r.d3,
+                   r.d4, r.d5,
+                   r.d6, r.s14,
+                   None, None]    # <- and not r.s15 for the last item
diff --git a/rpython/jit/backend/arm/test/test_instr_codebuilder.py b/rpython/jit/backend/arm/test/test_instr_codebuilder.py
--- a/rpython/jit/backend/arm/test/test_instr_codebuilder.py
+++ b/rpython/jit/backend/arm/test/test_instr_codebuilder.py
@@ -199,6 +199,14 @@
         self.cb.DMB()
         self.assert_equal('DMB')
 
+    def test_fmdrr(self):
+        self.cb.FMDRR(r.d11.value, r.r9.value, r.r14.value)
+        self.assert_equal('FMDRR d11, r9, r14')
+
+    def test_fmrrd(self):
+        self.cb.FMRRD(r.r9.value, r.r14.value, r.d11.value)
+        self.assert_equal('FMRRD r9, r14, d11')
+
 
 def test_size_of_gen_load_int():
     for v, n in [(5, 4), (6, 4), (7, 2)]:
diff --git a/rpython/jit/backend/llsupport/assembler.py b/rpython/jit/backend/llsupport/assembler.py
--- a/rpython/jit/backend/llsupport/assembler.py
+++ b/rpython/jit/backend/llsupport/assembler.py
@@ -294,10 +294,16 @@
                 struct = self.loop_run_counters[i]
                 if struct.type == 'l':
                     prefix = 'TargetToken(%d)' % struct.number
-                elif struct.type == 'b':
-                    prefix = 'bridge ' + str(struct.number)
                 else:
-                    prefix = 'entry ' + str(struct.number)
+                    num = struct.number
+                    if num == -1:
+                        num = '-1'
+                    else:
+                        num = str(r_uint(num))
+                    if struct.type == 'b':
+                        prefix = 'bridge %s' % num
+                    else:
+                        prefix = 'entry %s' % num
                 debug_print(prefix + ':' + str(struct.i))
             debug_stop('jit-backend-counts')
 
diff --git a/rpython/jit/backend/test/runner_test.py b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -2718,12 +2718,11 @@
                 assert r == result
 
     def test_call_release_gil_variable_function_and_arguments(self):
-        # NOTE NOTE NOTE
-        # This also works as a test for ctypes and libffi.
-        # On some platforms, one of these is buggy...
+        from rpython.translator.tool.cbuild import ExternalCompilationInfo
         from rpython.rlib.libffi import types
         from rpython.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
         from rpython.rlib.rarithmetic import r_singlefloat
+        from rpython.translator.c import primitive
 
         cpu = self.cpu
         rnd = random.Random(525)
@@ -2752,25 +2751,76 @@
                 (types.float,  rffi.FLOAT),
                 ] * 4
 
-        for k in range(100):
+        NB_TESTS = 100
+        c_source = []
+        all_tests = []
+        export_symbols = []
+
+        def prepare_c_source():
+            """Pick a random choice of argument types and length,
+            and build a C function with these arguments.  The C
+            function will simply copy them all into static global
+            variables.  There are then additional functions to fetch
+            them, one per argument, with a signature 'void(ARG *)'.
+            """
             POSSIBLE_TYPES = [rnd.choice(ALL_TYPES)
                               for i in range(random.randrange(2, 5))]
             load_factor = rnd.random()
             keepalive_factor = rnd.random()
             #
-            def pseudo_c_function(*args):
-                seen.append(list(args))
-            #
             ffitypes = []
             ARGTYPES = []
             for i in range(rnd.randrange(4, 20)):
                 ffitype, TP = rnd.choice(POSSIBLE_TYPES)
                 ffitypes.append(ffitype)
                 ARGTYPES.append(TP)
+            fn_name = 'vartest%d' % k
+            all_tests.append((ARGTYPES, ffitypes, fn_name))
             #
-            FPTR = self.Ptr(self.FuncType(ARGTYPES, lltype.Void))
-            func_ptr = llhelper(FPTR, pseudo_c_function)
-            funcbox = self.get_funcbox(cpu, func_ptr)
+            fn_args = []
+            for i, ARG in enumerate(ARGTYPES):
+                arg_decl = primitive.cdecl(primitive.PrimitiveType[ARG],
+                                           'x%d' % i)
+                fn_args.append(arg_decl)
+                var_name = 'argcopy_%s_x%d' % (fn_name, i)
+                var_decl = primitive.cdecl(primitive.PrimitiveType[ARG],
+                                           var_name)
+                c_source.append('static %s;' % var_decl)
+                getter_name = '%s_get%d' % (fn_name, i)
+                export_symbols.append(getter_name)
+                c_source.append('void %s(%s) { *p = %s; }' % (
+                    getter_name,
+                    primitive.cdecl(primitive.PrimitiveType[ARG], '*p'),
+                    var_name))
+            export_symbols.append(fn_name)
+            c_source.append('')
+            c_source.append('static void real%s(%s)' % (
+                fn_name, ', '.join(fn_args)))
+            c_source.append('{')
+            for i in range(len(ARGTYPES)):
+                c_source.append('    argcopy_%s_x%d = x%d;' % (fn_name, i, i))
+            c_source.append('}')
+            c_source.append('void *%s(void)' % fn_name)
+            c_source.append('{')
+            c_source.append('    return (void *)&real%s;' % fn_name)
+            c_source.append('}')
+            c_source.append('')
+
+        for k in range(NB_TESTS):
+            prepare_c_source()
+
+        eci = ExternalCompilationInfo(
+            separate_module_sources=['\n'.join(c_source)],
+            export_symbols=export_symbols)
+
+        for k in range(NB_TESTS):
+            ARGTYPES, ffitypes, fn_name = all_tests[k]
+            func_getter_ptr = rffi.llexternal(fn_name, [], lltype.Signed,
+                                         compilation_info=eci, _nowrapper=True)
+            load_factor = rnd.random()
+            keepalive_factor = rnd.random()
+            #
+            func_raw = func_getter_ptr()
             calldescr = cpu._calldescr_dynamic_for_tests(ffitypes, types.void)
             faildescr = BasicFailDescr(1)
             #
@@ -2790,7 +2840,7 @@
             print
             print codes
             #
-            argvalues = [funcbox.getint()]
+            argvalues = [func_raw]
             for TP in ARGTYPES:
                 r = (rnd.random() - 0.5) * 999999999999.9
                 r = rffi.cast(TP, r)
@@ -2840,16 +2890,26 @@
             looptoken = JitCellToken()
             self.cpu.compile_loop(argboxes, ops, looptoken)
             #
-            seen = []
             deadframe = self.cpu.execute_token(looptoken, *argvalues_normal)
             fail = self.cpu.get_latest_descr(deadframe)
             assert fail.identifier == 0
             expected = argvalues[1:]
-            [got] = seen
-            different_values = ['%r != %r' % (a, b)
-                                    for a, b in zip(got, expected)
-                                        if a != b]
-            assert got == expected, ', '.join(different_values)
+            got = []
+            for i, ARG in enumerate(ARGTYPES):
+                PARG = rffi.CArrayPtr(ARG)
+                getter_name = '%s_get%d' % (fn_name, i)
+                getter_ptr = rffi.llexternal(getter_name, [PARG], lltype.Void,
+                                             compilation_info=eci,
+                                             _nowrapper=True)
+                my_arg = lltype.malloc(PARG.TO, 1, zero=True, flavor='raw')
+                getter_ptr(my_arg)
+                got.append(my_arg[0])
+                lltype.free(my_arg, flavor='raw')
+            different_values = ['x%d: got %r, expected %r' % (i, a, b)
+                                for i, (a, b) in enumerate(zip(got, expected))
+                                if a != b]
+            assert got == expected, '\n'.join(
+                ['bad args, signature %r' % codes[1:]] + different_values)
 
 
     def test_guard_not_invalidated(self):
diff --git a/rpython/jit/codewriter/effectinfo.py b/rpython/jit/codewriter/effectinfo.py
--- a/rpython/jit/codewriter/effectinfo.py
+++ b/rpython/jit/codewriter/effectinfo.py
@@ -36,6 +36,7 @@
     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_STR_CMP                  = 32   # "stroruni.cmp"
     #
     OS_UNI_CONCAT               = 42   #
     OS_UNI_SLICE                = 43   #
@@ -47,6 +48,7 @@
     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_UNI_CMP                  = 52
     _OS_offset_uni              = OS_UNI_CONCAT - OS_STR_CONCAT
     #
     OS_LIBFFI_CALL              = 62
diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -1767,6 +1767,7 @@
             dict = {"stroruni.concat": EffectInfo.OS_STR_CONCAT,
                     "stroruni.slice":  EffectInfo.OS_STR_SLICE,
                     "stroruni.equal":  EffectInfo.OS_STR_EQUAL,
+                    "stroruni.cmp":    EffectInfo.OS_STR_CMP,
                     "stroruni.copy_string_to_raw": EffectInfo.OS_STR_COPY_TO_RAW,
                     }
             CHR = lltype.Char
@@ -1774,6 +1775,7 @@
             dict = {"stroruni.concat": EffectInfo.OS_UNI_CONCAT,
                     "stroruni.slice":  EffectInfo.OS_UNI_SLICE,
                     "stroruni.equal":  EffectInfo.OS_UNI_EQUAL,
+                    "stroruni.cmp":    EffectInfo.OS_UNI_CMP,
                     "stroruni.copy_string_to_raw": EffectInfo.OS_UNI_COPY_TO_RAW
                     }
             CHR = lltype.UniChar
diff --git a/rpython/jit/metainterp/optimizeopt/vstring.py b/rpython/jit/metainterp/optimizeopt/vstring.py
--- a/rpython/jit/metainterp/optimizeopt/vstring.py
+++ b/rpython/jit/metainterp/optimizeopt/vstring.py
@@ -733,6 +733,25 @@
             return True
         return False
 
+    def opt_call_stroruni_STR_CMP(self, op, mode):
+        v1 = self.getvalue(op.getarg(1))
+        v2 = self.getvalue(op.getarg(2))
+        l1box = v1.getstrlen(None, mode, None)
+        l2box = v2.getstrlen(None, mode, None)
+        if (l1box is not None and l2box is not None and
+            isinstance(l1box, ConstInt) and
+            isinstance(l2box, ConstInt) and
+            l1box.value == l2box.value == 1):
+            # comparing two single chars
+            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_SUB, [vchar1.force_box(self),
+                                           vchar2.force_box(self)],
+                             op.result))
+            return True
+        return False
+
     def opt_call_SHRINK_ARRAY(self, op):
         v1 = self.getvalue(op.getarg(1))
         v2 = self.getvalue(op.getarg(2))
diff --git a/rpython/jit/metainterp/test/test_string.py b/rpython/jit/metainterp/test/test_string.py
--- a/rpython/jit/metainterp/test/test_string.py
+++ b/rpython/jit/metainterp/test/test_string.py
@@ -846,6 +846,27 @@
             'jump': 1, 'guard_true': 2, 'int_ge': 2, 'int_add': 2, 'int_sub': 2
         })
 
+    def test_compare_single_char_for_ordering(self):
+        jitdriver = JitDriver(reds=['result', 'n'], greens=[])
+        _str = self._str
+        constant1 = _str("abcdefghij")
+
+        def cmpstr(x, y):
+            return x > _str(y)
+
+        def f(n):
+            cmpstr(_str("abc"), "def")  # force x and y to be annot as strings
+            result = 0
+            while n >= 0:
+                jitdriver.jit_merge_point(n=n, result=result)
+                c = constant1[n]
+                result += cmpstr(c, "c")
+                n -= 1
+            return result
+
+        res = self.meta_interp(f, [9])
+        assert res == f(9)
+        self.check_resops(newstr=0, newunicode=0, call=0)
 
 
 class TestLLtype(StringTests, LLJitMixin):
diff --git a/rpython/rlib/rStringIO.py b/rpython/rlib/rStringIO.py
--- a/rpython/rlib/rStringIO.py
+++ b/rpython/rlib/rStringIO.py
@@ -125,19 +125,19 @@
         assert result >= 0
         return result
 
-    def read(self, n=-1):
+    def read(self, size=-1):
         p = self.__pos
-        if p == 0 and n < 0:
+        if p == 0 and size < 0:
             self.__pos = AT_END
             return self.getvalue()     # reading everything
-        if p == AT_END or n == 0:
+        if p == AT_END or size == 0:
             return ''
         assert p >= 0
         self.__copy_into_bigbuffer()
         mysize = len(self.__bigbuffer)
         count = mysize - p
-        if n >= 0:
-            count = min(n, count)
+        if size >= 0:
+            count = min(size, count)
         if count <= 0:
             return ''
         if p == 0 and count == mysize:
diff --git a/rpython/rtyper/lltypesystem/rstr.py b/rpython/rtyper/lltypesystem/rstr.py
--- a/rpython/rtyper/lltypesystem/rstr.py
+++ b/rpython/rtyper/lltypesystem/rstr.py
@@ -531,6 +531,7 @@
                 return diff
             i += 1
         return len1 - len2
+    ll_strcmp.oopspec = 'stroruni.cmp(s1, s2)'
 
     @jit.elidable
     def ll_streq(s1, s2):
diff --git a/rpython/rtyper/rstr.py b/rpython/rtyper/rstr.py
--- a/rpython/rtyper/rstr.py
+++ b/rpython/rtyper/rstr.py
@@ -3,6 +3,7 @@
 from rpython.rtyper import rint
 from rpython.rtyper.error import TyperError
 from rpython.rtyper.lltypesystem.lltype import Signed, Bool, Void, UniChar
+from rpython.rtyper.lltypesystem import lltype
 from rpython.rtyper.rmodel import IteratorRepr, inputconst, Repr
 from rpython.rtyper.rint import IntegerRepr
 from rpython.rtyper.rfloat import FloatRepr
@@ -384,10 +385,10 @@
             unicode_encode_utf_8_impl, 'runicode_encode_utf_8')
 
     def rtype_method_upper(self, hop):
-        raise TypeError("Cannot do toupper on unicode string")
+        raise TyperError("Cannot do toupper on unicode string")
 
     def rtype_method_lower(self, hop):
-        raise TypeError("Cannot do tolower on unicode string")
+        raise TyperError("Cannot do tolower on unicode string")
 
     @jit.elidable
     def ll_encode_utf8(self, ll_s):
@@ -711,6 +712,11 @@
                  pairtype(AbstractUniCharRepr, AbstractCharRepr)):
     def rtype_eq(_, hop): return _rtype_unchr_compare_template(hop, 'eq')
     def rtype_ne(_, hop): return _rtype_unchr_compare_template(hop, 'ne')
+    def rtype_lt(_, hop): return _rtype_unchr_compare_template_ord(hop, 'lt')
+    def rtype_le(_, hop): return _rtype_unchr_compare_template_ord(hop, 'le')
+    def rtype_gt(_, hop): return _rtype_unchr_compare_template_ord(hop, 'gt')
+    def rtype_ge(_, hop): return _rtype_unchr_compare_template_ord(hop, 'ge')
+
 
 #Helper functions for comparisons
 
@@ -719,6 +725,18 @@
     vlist = hop.inputargs(unichar_repr, unichar_repr)
     return hop.genop('unichar_' + func, vlist, resulttype=Bool)
 
+def _rtype_unchr_compare_template_ord(hop, func):
+    vlist = hop.inputargs(*hop.args_r)
+    vlist2 = []
+    for v in vlist:
+        if v.concretetype == lltype.Char:
+            v = hop.genop('cast_char_to_int', [v], resulttype=lltype.Signed)
+        elif v.concretetype == lltype.UniChar:
+            v = hop.genop('cast_unichar_to_int', [v], resulttype=lltype.Signed)
+        else:
+            assert 0, v.concretetype
+        vlist2.append(v)
+    return hop.genop('int_' + func, vlist2, resulttype=Bool)
 
 #
 # _________________________ Conversions _________________________
diff --git a/rpython/rtyper/test/test_runicode.py b/rpython/rtyper/test/test_runicode.py
--- a/rpython/rtyper/test/test_runicode.py
+++ b/rpython/rtyper/test/test_runicode.py
@@ -296,3 +296,13 @@
 
         res = self.interpret(f, [5])
         assert res == 0
+
+    def test_unicode_char_comparison(self):
+        const = u'abcdef'
+        def f(n):
+            return const[n] >= u'c'
+
+        res = self.interpret(f, [1])
+        assert res == False
+        res = self.interpret(f, [2])
+        assert res == True


More information about the pypy-commit mailing list