[pypy-commit] pypy errno-again: Write and pass test_call_release_gil_save_errno, the first test

arigo noreply at buildbot.pypy.org
Thu Jan 15 11:07:21 CET 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: errno-again
Changeset: r75337:de4fa7e98110
Date: 2015-01-15 11:07 +0100
http://bitbucket.org/pypy/pypy/changeset/de4fa7e98110/

Log:	Write and pass test_call_release_gil_save_errno, the first test
	about the errno handling in callbuilder.

diff --git a/rpython/jit/backend/llsupport/callbuilder.py b/rpython/jit/backend/llsupport/callbuilder.py
--- a/rpython/jit/backend/llsupport/callbuilder.py
+++ b/rpython/jit/backend/llsupport/callbuilder.py
@@ -42,9 +42,9 @@
         self.pop_gcmap()
         self.load_result()
 
-    def emit_call_release_gil(self):
+    def emit_call_release_gil(self, save_err):
         """Emit a CALL_RELEASE_GIL, including calls to releasegil_addr
-        and reacqgil_addr."""
+        and reacqgil_addr.  'save_err' is a combination of rffi.RFFI_*ERR*."""
         fastgil = rffi.cast(lltype.Signed, rgil.gil_fetch_fastgil())
         self.select_call_release_gil_mode()
         self.prepare_arguments()
@@ -52,6 +52,7 @@
         self.call_releasegil_addr_and_move_real_arguments(fastgil)
         self.emit_raw_call()
         self.restore_stack_pointer()
+        self.save_errno(save_err)
         self.move_real_result_and_call_reacqgil_addr(fastgil)
         self.pop_gcmap()
         self.load_result()
@@ -62,6 +63,9 @@
     def move_real_result_and_call_reacqgil_addr(self, fastgil):
         raise NotImplementedError
 
+    def save_errno(self, save_err):
+        raise NotImplementedError
+
     def select_call_release_gil_mode(self):
         """Overridden in CallBuilder64"""
         self.is_call_release_gil = True
diff --git a/rpython/jit/backend/llsupport/llerrno.py b/rpython/jit/backend/llsupport/llerrno.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/llsupport/llerrno.py
@@ -0,0 +1,41 @@
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.translator.tool.cbuild import ExternalCompilationInfo
+from rpython.jit.backend.llsupport.symbolic import WORD
+
+
+def get_debug_saved_errno(cpu):
+    return cpu._debug_errno_container[3]
+
+def set_debug_saved_errno(cpu, nerrno):
+    assert nerrno >= 0
+    cpu._debug_errno_container[3] = nerrno
+
+def get_rpy_errno_offset(cpu):
+    if cpu.translate_support_code:
+        from rpython.rlib import rthread
+        return rthread.tlfield_rpy_errno.offset
+    else:
+        return 3 * WORD
+
+def _fetch_addr_errno():
+    eci = ExternalCompilationInfo(
+        separate_module_sources=['''
+            #include <errno.h>
+            RPY_EXPORTED long fetch_addr_errno(void) {
+                return (long)(&errno);
+            }
+        '''])
+    func1_ptr = rffi.llexternal('fetch_addr_errno', [], lltype.Signed,
+                                compilation_info=eci, _nowrapper=True)
+    return func1_ptr()
+
+def get_p_errno_offset(cpu):
+    if cpu.translate_support_code:
+        from rpython.rlib import rthread
+        return rthread.tlfield_p_errno.offset
+    else:
+        if cpu._debug_errno_container[2] == 0:
+            addr_errno = _fetch_addr_errno()
+            assert addr_errno != 0
+            cpu._debug_errno_container[2] = addr_errno
+        return 2 * WORD
diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -62,6 +62,9 @@
             self.floatarraydescr = ArrayDescr(ad.basesize, ad.itemsize,
                                               ad.lendescr, FLAG_FLOAT)
         self.setup()
+        self._debug_errno_container = lltype.malloc(
+            rffi.CArray(lltype.Signed), 5, flavor='raw', zero=True,
+            track_allocation=False)
 
     def getarraydescr_for_frame(self, type):
         if type == history.FLOAT:
@@ -259,7 +262,8 @@
                     ll_threadlocal_addr = llop.threadlocalref_addr(
                         llmemory.Address)
                 else:
-                    ll_threadlocal_addr = llmemory.NULL
+                    ll_threadlocal_addr = rffi.cast(llmemory.Address,
+                        self._debug_errno_container)
                 llop.gc_writebarrier(lltype.Void, ll_frame)
                 ll_frame = func(ll_frame, ll_threadlocal_addr)
             finally:
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
@@ -2920,7 +2920,45 @@
                 ['bad args, signature %r' % codes[1:]] + different_values)
 
     def test_call_release_gil_save_errno(self):
-        XXX
+        from rpython.translator.tool.cbuild import ExternalCompilationInfo
+        from rpython.rlib.libffi import types
+        from rpython.jit.backend.llsupport import llerrno
+        #
+        eci = ExternalCompilationInfo(
+            separate_module_sources=['''
+                #include <errno.h>
+                RPY_EXPORTED void test_call_release_gil_save_errno(void) {
+                    errno = 42;
+                }
+            '''])
+        fn_name = 'test_call_release_gil_save_errno'
+        func1_ptr = rffi.llexternal(fn_name, [], lltype.Void,
+                                    compilation_info=eci, _nowrapper=True)
+        func1_adr = rffi.cast(lltype.Signed, func1_ptr)
+        calldescr = self.cpu._calldescr_dynamic_for_tests([], types.void)
+        #
+        for saveerr in [rffi.RFFI_ERR_NONE, rffi.RFFI_SAVE_ERRNO]:
+            faildescr = BasicFailDescr(1)
+            ops = [
+                ResOperation(rop.CALL_RELEASE_GIL,
+                             [ConstInt(saveerr), ConstInt(func1_adr)], None,
+                             descr=calldescr),
+                ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
+                ResOperation(rop.FINISH, [], None, descr=BasicFinalDescr(0))
+            ]
+            ops[-2].setfailargs([])
+            looptoken = JitCellToken()
+            self.cpu.compile_loop([], ops, looptoken)
+            #
+            llerrno.set_debug_saved_errno(self.cpu, 24)
+            self.cpu.execute_token(looptoken)
+            result = llerrno.get_debug_saved_errno(self.cpu)
+            print 'saveerr =', saveerr, ': got result =', result
+            #
+            if saveerr == rffi.RFFI_SAVE_ERRNO:
+                assert result == 42      # from the C code
+            else:
+                assert result == 24      # not touched
 
     def test_call_release_gil_readsaved_errno(self):
         XXX
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
@@ -1917,7 +1917,9 @@
     def _genop_call(self, op, arglocs, resloc, is_call_release_gil=False):
         from rpython.jit.backend.llsupport.descr import CallDescr
 
-        cb = callbuilder.CallBuilder(self, arglocs[2], arglocs[3:], resloc)
+        func_index = 2 + is_call_release_gil
+        cb = callbuilder.CallBuilder(self, arglocs[func_index],
+                                     arglocs[func_index+1:], resloc)
 
         descr = op.getdescr()
         assert isinstance(descr, CallDescr)
@@ -1932,7 +1934,9 @@
         cb.ressign = signloc.value
 
         if is_call_release_gil:
-            cb.emit_call_release_gil()
+            saveerrloc = arglocs[2]
+            assert isinstance(saveerrloc, ImmedLoc)
+            cb.emit_call_release_gil(saveerrloc.value)
         else:
             cb.emit()
 
@@ -2345,8 +2349,9 @@
         # This loads the stack location THREADLOCAL_OFS into a
         # register, and then read the word at the given offset.
         # It is only supported if 'translate_support_code' is
-        # true; otherwise, the original call to the piece of assembler
-        # was done with a dummy NULL value.
+        # true; otherwise, the execute_token() was done with a
+        # dummy value for the stack location THREADLOCAL_OFS
+        # 
         assert self.cpu.translate_support_code
         assert isinstance(resloc, RegLoc)
         self.mc.MOV_rs(resloc.value, THREADLOCAL_OFS)
diff --git a/rpython/jit/backend/x86/callbuilder.py b/rpython/jit/backend/x86/callbuilder.py
--- a/rpython/jit/backend/x86/callbuilder.py
+++ b/rpython/jit/backend/x86/callbuilder.py
@@ -3,13 +3,16 @@
 from rpython.rlib.objectmodel import we_are_translated
 from rpython.jit.metainterp.history import INT, FLOAT
 from rpython.jit.backend.x86.arch import (WORD, IS_X86_64, IS_X86_32,
-                                          PASS_ON_MY_FRAME, FRAME_FIXED_SIZE)
+                                          PASS_ON_MY_FRAME, FRAME_FIXED_SIZE,
+                                          THREADLOCAL_OFS)
 from rpython.jit.backend.x86.regloc import (eax, ecx, edx, ebx, esp, ebp, esi,
     xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, r8, r9, r10, r11, edi,
     r12, r13, r14, r15, X86_64_SCRATCH_REG, X86_64_XMM_SCRATCH_REG,
     RegLoc, RawEspLoc, RawEbpLoc, imm, ImmedLoc)
 from rpython.jit.backend.x86.jump import remap_frame_layout
 from rpython.jit.backend.llsupport.callbuilder import AbstractCallBuilder
+from rpython.jit.backend.llsupport import llerrno
+from rpython.rtyper.lltypesystem import rffi
 
 
 # darwin requires the stack to be 16 bytes aligned on calls.
@@ -146,6 +149,19 @@
         if not we_are_translated():        # for testing: we should not access
             self.mc.ADD(ebp, imm(1))       # ebp any more
 
+    def save_errno(self, save_err):
+        if save_err & rffi.RFFI_SAVE_ERRNO:
+            # Just after a call, read the real 'errno' and save a copy of
+            # it inside our thread-local 'rpy_errno'.  Most registers are
+            # free here, including the callee-saved ones, except 'ebx'.
+            rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu)
+            p_errno = llerrno.get_p_errno_offset(self.asm.cpu)
+            mc = self.mc
+            mc.MOV_rs(esi.value, THREADLOCAL_OFS)
+            mc.MOV_rm(edi.value, (esi.value, p_errno))
+            mc.MOV32_rm(edi.value, (edi.value, 0))
+            mc.MOV32_mr((esi.value, rpy_errno), edi.value)
+
     def move_real_result_and_call_reacqgil_addr(self, fastgil):
         from rpython.jit.backend.x86 import rx86
         #
@@ -195,7 +211,8 @@
             # in 'ebx'), and if not, we fall back to 'reacqgil_addr'.
             mc.J_il8(rx86.Conditions['NE'], 0)
             jne_location = mc.get_relative_pos()
-            # here, ecx is zero (so rpy_fastgil was not acquired)
+            # here, ecx is zero (so rpy_fastgil was in 'released' state
+            # before the XCHG, but the XCHG acquired it by writing 1)
             rst = gcrootmap.get_root_stack_top_addr()
             mc = self.mc
             mc.CMP(ebx, heap(rst))
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
@@ -743,10 +743,10 @@
         else:
             self.perform(op, arglocs, resloc)
 
-    def _consider_call(self, op, guard_not_forced_op=None):
+    def _consider_call(self, op, guard_not_forced_op=None, first_arg_index=1):
         calldescr = op.getdescr()
         assert isinstance(calldescr, CallDescr)
-        assert len(calldescr.arg_classes) == op.numargs() - 1
+        assert len(calldescr.arg_classes) == op.numargs() - first_arg_index
         size = calldescr.get_result_size()
         sign = calldescr.is_result_signed()
         if sign:
@@ -795,8 +795,9 @@
         self._consider_call(op, guard_op)
 
     def consider_call_release_gil(self, op, guard_op):
+        # [Const(save_err), func_addr, args...]
         assert guard_op is not None
-        self._consider_call(op, guard_op)
+        self._consider_call(op, guard_op, first_arg_index=2)
 
     def consider_call_malloc_gc(self, op):
         self._consider_call(op)


More information about the pypy-commit mailing list