[pypy-commit] pypy default: hg merge jit-get-errno

arigo noreply at buildbot.pypy.org
Thu Jun 26 12:38:42 CEST 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r72243:37c876808e80
Date: 2014-06-26 12:36 +0200
http://bitbucket.org/pypy/pypy/changeset/37c876808e80/

Log:	hg merge jit-get-errno

	Optimize the errno handling in the JIT, notably around external
	function calls. Linux-only.

diff --git a/rpython/jit/backend/llsupport/test/ztranslation_test.py b/rpython/jit/backend/llsupport/test/ztranslation_test.py
--- a/rpython/jit/backend/llsupport/test/ztranslation_test.py
+++ b/rpython/jit/backend/llsupport/test/ztranslation_test.py
@@ -3,7 +3,7 @@
 from rpython.rlib.jit import JitDriver, unroll_parameters, set_param
 from rpython.rlib.jit import PARAMETERS, dont_look_inside
 from rpython.rlib.jit import promote
-from rpython.rlib import jit_hooks
+from rpython.rlib import jit_hooks, rposix
 from rpython.rlib.objectmodel import keepalive_until_here
 from rpython.rlib.rthread import ThreadLocalReference
 from rpython.jit.backend.detect_cpu import getcpuclass
@@ -24,6 +24,7 @@
         # - full optimizer
         # - floats neg and abs
         # - threadlocalref_get
+        # - get_errno, set_errno
 
         class Frame(object):
             _virtualizable_ = ['i']
@@ -64,6 +65,8 @@
                 if k - abs(j):  raise ValueError
                 if k - abs(-j): raise ValueError
                 if t.get().nine != 9: raise ValueError
+                rposix.set_errno(total)
+                if rposix.get_errno() != total: raise ValueError
             return chr(total % 253)
         #
         from rpython.rtyper.lltypesystem import lltype, rffi
diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -2325,12 +2325,38 @@
         ed = effectinfo.extradescrs[0]
         assert isinstance(ed, ThreadLocalRefDescr)
         addr1 = rffi.cast(lltype.Signed, ed.get_tlref_addr())
+        # 'addr1' is the address is the current thread, but we assume that
+        # it is a thread-local at a constant offset from %fs/%gs.
         addr0 = stmtlocal.threadlocal_base()
         addr = addr1 - addr0
         assert rx86.fits_in_32bits(addr)
         mc = self.mc
-        mc.writechar(stmtlocal.SEGMENT_TL)     # prefix
-        mc.MOV_rj(resloc.value, addr)
+        mc.writechar(stmtlocal.SEGMENT_TL)     # prefix: %fs or %gs
+        mc.MOV_rj(resloc.value, addr)          # memory read
+
+    def get_set_errno(self, op, loc, issue_a_write):
+        # this function is only called on Linux
+        from rpython.jit.backend.x86 import stmtlocal
+        addr = stmtlocal.get_errno_tl()
+        assert rx86.fits_in_32bits(addr)
+        mc = self.mc
+        mc.writechar(stmtlocal.SEGMENT_TL)     # prefix: %fs or %gs
+        # !!important: the *next* instruction must be the one using 'addr'!!
+        if issue_a_write:
+            if isinstance(loc, RegLoc):
+                mc.MOV32_jr(addr, loc.value)       # memory write from reg
+            else:
+                assert isinstance(loc, ImmedLoc)
+                newvalue = loc.value
+                newvalue = rffi.cast(rffi.INT, newvalue)
+                newvalue = rffi.cast(lltype.Signed, newvalue)
+                mc.MOV32_ji(addr, newvalue)        # memory write immediate
+        else:
+            assert isinstance(loc, RegLoc)
+            if IS_X86_32:
+                mc.MOV_rj(loc.value, addr)         # memory read
+            elif IS_X86_64:
+                mc.MOVSX32_rj(loc.value, addr)     # memory read, sign-extend
 
 
 genop_discard_list = [Assembler386.not_implemented_op_discard] * rop._LAST
diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -693,6 +693,7 @@
         self.perform_math(op, [loc0], loc0)
 
     TLREF_SUPPORT = sys.platform.startswith('linux')
+    ERRNO_SUPPORT = sys.platform.startswith('linux')
 
     def _consider_threadlocalref_get(self, op):
         if self.TLREF_SUPPORT:
@@ -701,6 +702,22 @@
         else:
             self._consider_call(op)
 
+    def _consider_get_errno(self, op):
+        if self.ERRNO_SUPPORT:
+            resloc = self.force_allocate_reg(op.result)
+            self.assembler.get_set_errno(op, resloc, issue_a_write=False)
+        else:
+            self._consider_call(op)
+
+    def _consider_set_errno(self, op):
+        if self.ERRNO_SUPPORT:
+            # op.getarg(0) is the function set_errno; op.getarg(1) is
+            # the new errno value
+            loc0 = self.rm.make_sure_var_in_reg(op.getarg(1))
+            self.assembler.get_set_errno(op, loc0, issue_a_write=True)
+        else:
+            self._consider_call(op)
+
     def _call(self, op, arglocs, force_store=[], guard_not_forced_op=None):
         # we need to save registers on the stack:
         #
@@ -780,6 +797,10 @@
                 return self._consider_math_sqrt(op)
             if oopspecindex == EffectInfo.OS_THREADLOCALREF_GET:
                 return self._consider_threadlocalref_get(op)
+            if oopspecindex == EffectInfo.OS_GET_ERRNO:
+                return self._consider_get_errno(op)
+            if oopspecindex == EffectInfo.OS_SET_ERRNO:
+                return self._consider_set_errno(op)
         self._consider_call(op)
 
     def consider_call_may_force(self, op, guard_op):
diff --git a/rpython/jit/backend/x86/stmtlocal.py b/rpython/jit/backend/x86/stmtlocal.py
--- a/rpython/jit/backend/x86/stmtlocal.py
+++ b/rpython/jit/backend/x86/stmtlocal.py
@@ -21,6 +21,10 @@
     asm("%s" : "=r"(result));
     return result;
 }
+static long pypy__get_errno_tl(void)
+{
+    return ((long)&errno) - pypy__threadlocal_base();
+}
 ''' % _instruction])
 
 
@@ -30,3 +34,10 @@
     compilation_info=eci,
     _nowrapper=True,
     ) #transactionsafe=True)
+
+get_errno_tl = rffi.llexternal(
+    'pypy__get_errno_tl',
+    [], lltype.Signed,
+    compilation_info=eci,
+    _nowrapper=True,
+    ) #transactionsafe=True)
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
@@ -23,6 +23,8 @@
     OS_SHRINK_ARRAY             = 3    # rgc.ll_shrink_array
     OS_DICT_LOOKUP              = 4    # ll_dict_lookup
     OS_THREADLOCALREF_GET       = 5    # llop.threadlocalref_get
+    OS_GET_ERRNO                = 6    # rposix.get_errno
+    OS_SET_ERRNO                = 7    # rposix.set_errno
     #
     OS_STR_CONCAT               = 22   # "stroruni.concat"
     OS_STR_SLICE                = 23   # "stroruni.slice"
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
@@ -438,6 +438,8 @@
         elif oopspec_name.endswith('dict.lookup'):
             # also ordereddict.lookup
             prepare = self._handle_dict_lookup_call
+        elif oopspec_name.startswith('rposix.'):
+            prepare = self._handle_rposix_call
         else:
             prepare = self.prepare_builtin_call
         try:
@@ -1898,6 +1900,16 @@
         else:
             raise NotImplementedError(oopspec_name)
 
+    def _handle_rposix_call(self, op, oopspec_name, args):
+        if oopspec_name == 'rposix.get_errno':
+            return self._handle_oopspec_call(op, args, EffectInfo.OS_GET_ERRNO,
+                                             EffectInfo.EF_CANNOT_RAISE)
+        elif oopspec_name == 'rposix.set_errno':
+            return self._handle_oopspec_call(op, args, EffectInfo.OS_SET_ERRNO,
+                                             EffectInfo.EF_CANNOT_RAISE)
+        else:
+            raise NotImplementedError(oopspec_name)
+
     def rewrite_op_jit_force_quasi_immutable(self, op):
         v_inst, c_fieldname = op.args
         descr1 = self.cpu.fielddescrof(v_inst.concretetype.TO,
diff --git a/rpython/jit/codewriter/test/test_jtransform.py b/rpython/jit/codewriter/test/test_jtransform.py
--- a/rpython/jit/codewriter/test/test_jtransform.py
+++ b/rpython/jit/codewriter/test/test_jtransform.py
@@ -148,6 +148,8 @@
              EI.OS_RAW_MALLOC_VARSIZE_CHAR: ([INT], ARRAYPTR),
              EI.OS_RAW_FREE:             ([ARRAYPTR], lltype.Void),
              EI.OS_THREADLOCALREF_GET:   ([], rclass.OBJECTPTR),
+             EI.OS_GET_ERRNO:            ([], INT),
+             EI.OS_SET_ERRNO:            ([INT], lltype.Void),
             }
             argtypes = argtypes[oopspecindex]
             assert argtypes[0] == [v.concretetype for v in op.args[1:]]
@@ -156,7 +158,9 @@
                 assert extraeffect == EI.EF_ELIDABLE_CAN_RAISE
             elif oopspecindex == EI.OS_RAW_MALLOC_VARSIZE_CHAR:
                 assert extraeffect == EI.EF_CAN_RAISE
-            elif oopspecindex == EI.OS_RAW_FREE:
+            elif oopspecindex in (EI.OS_RAW_FREE,
+                                  EI.OS_GET_ERRNO,
+                                  EI.OS_SET_ERRNO):
                 assert extraeffect == EI.EF_CANNOT_RAISE
             elif oopspecindex == EI.OS_THREADLOCALREF_GET:
                 assert extraeffect == EI.EF_LOOPINVARIANT
@@ -1320,6 +1324,38 @@
     assert op0.args[2] == 'calldescr-%d' % OS_THREADLOCALREF_GET
     assert op0.result == v2
 
+def test_get_errno():
+    # test that the oopspec is present and correctly transformed
+    from rpython.rlib import rposix
+    FUNC = lltype.FuncType([], lltype.Signed)
+    func = lltype.functionptr(FUNC, 'get_errno', _callable=rposix.get_errno)
+    v3 = varoftype(lltype.Signed)
+    op = SpaceOperation('direct_call', [const(func)], 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] == ListOfKind('ref', [])
+    assert op1.args[2] == 'calldescr-%d' % effectinfo.EffectInfo.OS_GET_ERRNO
+    assert op1.result == v3
+
+def test_set_errno():
+    # test that the oopspec is present and correctly transformed
+    from rpython.rlib import rposix
+    FUNC = lltype.FuncType([lltype.Signed], lltype.Void)
+    func = lltype.functionptr(FUNC, 'set_errno', _callable=rposix.set_errno)
+    v1 = varoftype(lltype.Signed)
+    v3 = varoftype(lltype.Void)
+    op = SpaceOperation('direct_call', [const(func), v1], v3)
+    tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+    op1 = tr.rewrite_operation(op)
+    assert op1.opname == 'residual_call_ir_v'
+    assert op1.args[0].value == func
+    assert op1.args[1] == ListOfKind('int', [v1])
+    assert op1.args[2] == ListOfKind('ref', [])
+    assert op1.args[3] == 'calldescr-%d' % effectinfo.EffectInfo.OS_SET_ERRNO
+    assert op1.result == v3
+
 def test_unknown_operation():
     op = SpaceOperation('foobar', [], varoftype(lltype.Void))
     tr = Transformer()
diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -98,9 +98,11 @@
 # the default wrapper for set_errno is not suitable for use in critical places
 # like around GIL handling logic, so we provide our own wrappers.
 
+ at jit.oopspec("rposix.get_errno()")
 def get_errno():
     return intmask(_get_errno())
 
+ at jit.oopspec("rposix.set_errno(errno)")
 def set_errno(errno):
     _set_errno(rffi.cast(INT, errno))
 


More information about the pypy-commit mailing list