[pypy-commit] pypy portable-threadlocal: Implement this thread-local variant in the JIT.

arigo noreply at buildbot.pypy.org
Tue Nov 25 23:28:04 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: portable-threadlocal
Changeset: r74718:d739638f9b7f
Date: 2014-11-25 22:48 +0100
http://bitbucket.org/pypy/pypy/changeset/d739638f9b7f/

Log:	Implement this thread-local variant in the JIT.

diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py
--- a/rpython/jit/backend/arm/opassembler.py
+++ b/rpython/jit/backend/arm/opassembler.py
@@ -1274,8 +1274,10 @@
         regalloc.rm.possibly_free_var(dstaddr_box)
         return fcond
 
-    def emit_opx_threadlocalref_addr(self, op, arglocs, regalloc, fcond):
-        res, = arglocs
+    def emit_opx_threadlocalref_get(self, op, arglocs, regalloc, fcond):
+        ofs0, res = arglocs
+        assert ofs0.is_imm()
         ofs = self.saved_threadlocal_addr
         self.load_reg(self.mc, res, r.sp, ofs)
+        self.load_reg(self.mc, res, res, ofs0.value)
         return fcond
diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py
--- a/rpython/jit/backend/arm/regalloc.py
+++ b/rpython/jit/backend/arm/regalloc.py
@@ -559,8 +559,8 @@
                 args = self._prepare_op_math_sqrt(op, fcond)
                 self.perform_extra(op, args, fcond)
                 return
-            elif oopspecindex == EffectInfo.OS_THREADLOCALREF_ADDR:
-                args = self._prepare_threadlocalref_addr(op, fcond)
+            elif oopspecindex == EffectInfo.OS_THREADLOCALREF_GET:
+                args = self._prepare_threadlocalref_get(op, fcond)
                 self.perform_extra(op, args, fcond)
                 return
             #elif oopspecindex == EffectInfo.OS_MATH_READ_TIMESTAMP:
@@ -619,9 +619,10 @@
         res = self.force_allocate_reg(op.result)
         return [loc0, res]
 
-    def _prepare_threadlocalref_addr(self, op, fcond):
+    def _prepare_threadlocalref_get(self, op, fcond):
+        ofs0 = imm(op.getarg(1).getint())
         res = self.force_allocate_reg(op.result)
-        return [res]
+        return [ofs0, res]
 
     def _prepare_guard(self, op, args=None):
         if args is None:
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
@@ -220,8 +220,8 @@
         # The JIT backend must generate functions with the following
         # signature: it takes the jitframe and the threadlocal_addr
         # as arguments, and it returns the (possibly reallocated) jitframe.
-        # The backend can optimize OS_THREADLOCALREF_ADDR calls to return
-        # this threadlocal_addr, but only if 'translate_support_code':
+        # The backend can optimize OS_THREADLOCALREF_GET calls to return a
+        # field of this threadlocal_addr, but only if 'translate_support_code':
         # in untranslated tests, threadlocal_addr is a dummy NULL.
         FUNCPTR = lltype.Ptr(lltype.FuncType([llmemory.GCREF, llmemory.Address],
                                              llmemory.GCREF))
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
@@ -2322,38 +2322,16 @@
         assert isinstance(reg, RegLoc)
         self.mc.MOV_rr(reg.value, ebp.value)
 
-    def threadlocalref_addr(self, resloc):
-        # This simply loads the stack location THREADLOCAL_OFS into a
-        # register.  It is only supported if 'translate_support_code' is
+    def threadlocalref_get(self, offset, resloc):
+        # 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.
         assert self.cpu.translate_support_code
         assert isinstance(resloc, RegLoc)
         self.mc.MOV_rs(resloc.value, THREADLOCAL_OFS)
-
-    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
+        self.mc.MOV_rm(resloc.value, (resloc.value, offset))
 
     def genop_discard_zero_array(self, op, arglocs):
         (base_loc, startindex_loc, bytes_loc,
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,10 +693,11 @@
         loc0 = self.xrm.force_result_in_reg(op.result, op.getarg(1))
         self.perform_math(op, [loc0], loc0)
 
-    def _consider_threadlocalref_addr(self, op):
+    def _consider_threadlocalref_get(self, op):
         if self.translate_support_code:
+            offset = op.getarg(1).getint()   # getarg(0) == 'threadlocalref_get'
             resloc = self.force_allocate_reg(op.result)
-            self.assembler.threadlocalref_addr(resloc)
+            self.assembler.threadlocalref_get(offset, resloc)
         else:
             self._consider_call(op)
 
@@ -777,8 +778,8 @@
                         return
             if oopspecindex == EffectInfo.OS_MATH_SQRT:
                 return self._consider_math_sqrt(op)
-            if oopspecindex == EffectInfo.OS_THREADLOCALREF_ADDR:
-                return self._consider_threadlocalref_addr(op)
+            if oopspecindex == EffectInfo.OS_THREADLOCALREF_GET:
+                return self._consider_threadlocalref_get(op)
             if oopspecindex == EffectInfo.OS_MATH_READ_TIMESTAMP:
                 return self._consider_math_read_timestamp(op)
         self._consider_call(op)
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
@@ -22,7 +22,7 @@
     OS_STR2UNICODE              = 2    # "str.str2unicode"
     OS_SHRINK_ARRAY             = 3    # rgc.ll_shrink_array
     OS_DICT_LOOKUP              = 4    # ll_dict_lookup
-    OS_THREADLOCALREF_ADDR      = 5    # llop.threadlocalref_addr
+    OS_THREADLOCALREF_GET       = 5    # llop.threadlocalref_get
     OS_NOT_IN_TRACE             = 8    # for calls not recorded in the jit trace
     #
     OS_STR_CONCAT               = 22   # "stroruni.concat"
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
@@ -1999,11 +1999,16 @@
                              None)
         return [op0, op1]
 
-    def rewrite_op_threadlocalref_addr(self, op):
-        op1 = self.prepare_builtin_call(op, 'threadlocalref_addr', [])
+    def rewrite_op_threadlocalref_get(self, op):
+        # only supports RESTYPE being exactly one word.
+        RESTYPE = op.result.concretetype
+        assert (RESTYPE in (lltype.Signed, lltype.Unsigned, llmemory.Address)
+                or isinstance(RESTYPE, lltype.Ptr))
+        c_offset, = op.args
+        op1 = self.prepare_builtin_call(op, 'threadlocalref_get', [c_offset])
         return self.handle_residual_call(op1,
-            oopspecindex=EffectInfo.OS_THREADLOCALREF_ADDR,
-            extraeffect=EffectInfo.EF_CANNOT_RAISE)
+            oopspecindex=EffectInfo.OS_THREADLOCALREF_GET,
+            extraeffect=EffectInfo.EF_LOOPINVARIANT)
 
 # ____________________________________________________________
 
diff --git a/rpython/jit/codewriter/support.py b/rpython/jit/codewriter/support.py
--- a/rpython/jit/codewriter/support.py
+++ b/rpython/jit/codewriter/support.py
@@ -702,8 +702,9 @@
     build_ll_1_raw_free_no_track_allocation = (
         build_raw_free_builder(track_allocation=False))
 
-    def _ll_0_threadlocalref_addr():
-        return llop.threadlocalref_addr(llmemory.Address)
+    def _ll_1_threadlocalref_get(TP, offset):
+        return llop.threadlocalref_get(TP, offset)
+    _ll_1_threadlocalref_get.need_result_type = 'exact'   # don't deref
 
     def _ll_1_weakref_create(obj):
         return llop.weakref_create(llmemory.WeakRefPtr, obj)
@@ -816,8 +817,18 @@
     s_result = lltype_to_annotation(ll_res)
     impl = setup_extra_builtin(rtyper, oopspec_name, len(args_s), extra)
     if getattr(impl, 'need_result_type', False):
-        bk = rtyper.annotator.bookkeeper
-        args_s.insert(0, annmodel.SomePBC([bk.getdesc(deref(ll_res))]))
+        if hasattr(rtyper, 'annotator'):
+            bk = rtyper.annotator.bookkeeper
+            ll_restype = ll_res
+            if impl.need_result_type != 'exact':
+                ll_restype = deref(ll_restype)
+            desc = bk.getdesc(ll_restype)
+        else:
+            class TestingDesc(object):
+                knowntype = int
+                pyobj = None
+            desc = TestingDesc()
+        args_s.insert(0, annmodel.SomePBC([desc]))
     #
     if hasattr(rtyper, 'annotator'):  # regular case
         mixlevelann = MixLevelHelperAnnotator(rtyper)
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,7 +148,7 @@
              EI.OS_UNIEQ_LENGTHOK:       ([PUNICODE, PUNICODE], INT),
              EI.OS_RAW_MALLOC_VARSIZE_CHAR: ([INT], ARRAYPTR),
              EI.OS_RAW_FREE:             ([ARRAYPTR], lltype.Void),
-             EI.OS_THREADLOCALREF_ADDR:  ([], llmemory.Address),
+             EI.OS_THREADLOCALREF_GET:   ([INT], INT),   # for example
             }
             argtypes = argtypes[oopspecindex]
             assert argtypes[0] == [v.concretetype for v in op.args[1:]]
@@ -157,9 +157,10 @@
                 assert extraeffect == EI.EF_ELIDABLE_CAN_RAISE
             elif oopspecindex == EI.OS_RAW_MALLOC_VARSIZE_CHAR:
                 assert extraeffect == EI.EF_CAN_RAISE
-            elif oopspecindex in (EI.OS_RAW_FREE,
-                                  EI.OS_THREADLOCALREF_ADDR):
+            elif oopspecindex == EI.OS_RAW_FREE:
                 assert extraeffect == EI.EF_CANNOT_RAISE
+            elif oopspecindex == EI.OS_THREADLOCALREF_GET:
+                assert extraeffect == EI.EF_LOOPINVARIANT
             else:
                 assert extraeffect == EI.EF_ELIDABLE_CANNOT_RAISE
         return 'calldescr-%d' % oopspecindex
@@ -1341,16 +1342,20 @@
     assert op1.result is None
     assert op2 is None
 
-def test_threadlocalref_addr():
-    OS_THREADLOCALREF_ADDR = effectinfo.EffectInfo.OS_THREADLOCALREF_ADDR
-    v = varoftype(llmemory.Address)
-    op = SpaceOperation('threadlocalref_addr', [], v)
+def test_threadlocalref_get():
+    from rpython.rlib.rthread import ThreadLocalField
+    tlfield = ThreadLocalField(lltype.Signed, 'foobar_test_')
+    OS_THREADLOCALREF_GET = effectinfo.EffectInfo.OS_THREADLOCALREF_GET
+    c = const(tlfield.offset)
+    v = varoftype(lltype.Signed)
+    op = SpaceOperation('threadlocalref_get', [c], v)
     tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
     op0 = tr.rewrite_operation(op)
-    assert op0.opname == 'residual_call_r_i'
-    assert op0.args[0].value == 'threadlocalref_addr' # pseudo-function as str
-    assert op0.args[1] == ListOfKind("ref", [])
-    assert op0.args[2] == 'calldescr-%d' % OS_THREADLOCALREF_ADDR
+    assert op0.opname == 'residual_call_ir_i'
+    assert op0.args[0].value == 'threadlocalref_get' # pseudo-function as str
+    assert op0.args[1] == ListOfKind("int", [c])
+    assert op0.args[2] == ListOfKind("ref", [])
+    assert op0.args[3] == 'calldescr-%d' % OS_THREADLOCALREF_GET
     assert op0.result == v
 
 def test_unknown_operation():
diff --git a/rpython/jit/metainterp/test/test_threadlocal.py b/rpython/jit/metainterp/test/test_threadlocal.py
--- a/rpython/jit/metainterp/test/test_threadlocal.py
+++ b/rpython/jit/metainterp/test/test_threadlocal.py
@@ -1,19 +1,20 @@
 import py
+from rpython.rlib import rthread
 from rpython.jit.metainterp.test.support import LLJitMixin
-from rpython.rtyper.lltypesystem import llmemory
+from rpython.rtyper.lltypesystem import lltype
 from rpython.rtyper.lltypesystem.lloperation import llop
 
 
 class ThreadLocalTest(object):
 
     def test_threadlocalref_get(self):
+        tlfield = rthread.ThreadLocalField(lltype.Signed, 'foobar_test_')
+
         def f():
-            addr1 = llop.threadlocalref_addr(llmemory.Address)
-            # a "does not crash" test only
-            return 1
+            return tlfield.getraw()
 
         res = self.interp_operations(f, [])
-        assert res == 1
+        assert res == 0x544c    # magic value returned by llinterp
 
 
 class TestLLtype(ThreadLocalTest, LLJitMixin):
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -921,14 +921,12 @@
 
     def op_threadlocalref_addr(self):
         raise NotImplementedError("threadlocalref_addr")
-        ## class FakeThreadLocalAddr(object):
-        ##     is_fake_thread_local_addr = True
-        ##     _TYPE = llmemory.Address
-        ##     def _cast_to_int(self, symbolic=None):
-        ##         return FakeThreadLocalAddrAsInt()
-        ## class FakeThreadLocalAddrAsInt(object):
-        ##     _TYPE = lltype.Signed
-        ## return FakeThreadLocalAddr()
+
+    def op_threadlocalref_get(self, offset):
+        if (type(offset) is CDefinedIntSymbolic and
+                offset.expr == 'RPY_TLOFS_foobar_test_'):   # used in tests
+            return 0x544c
+        raise NotImplementedError("threadlocalref_get")
 
     # __________________________________________________________
     # operations on addresses
diff --git a/rpython/translator/c/funcgen.py b/rpython/translator/c/funcgen.py
--- a/rpython/translator/c/funcgen.py
+++ b/rpython/translator/c/funcgen.py
@@ -902,15 +902,20 @@
             return None    # use the default
 
     def OP_THREADLOCALREF_GET(self, op):
-        assert isinstance(op.args[0], Constant)
-        assert isinstance(op.args[0].value, CDefinedIntSymbolic)
-        fieldname = op.args[0].value.expr
-        assert fieldname.startswith('RPY_TLOFS_')
-        fieldname = fieldname[10:]
         typename = self.db.gettype(op.result.concretetype)
-        return '%s = (%s)RPY_THREADLOCALREF_GET(%s);' % (
-            self.expr(op.result),
-            cdecl(typename, ''),
-            fieldname)
+        if isinstance(op.args[0], Constant):
+            assert isinstance(op.args[0].value, CDefinedIntSymbolic)
+            fieldname = op.args[0].value.expr
+            assert fieldname.startswith('RPY_TLOFS_')
+            fieldname = fieldname[10:]
+            return '%s = (%s)RPY_THREADLOCALREF_GET(%s);' % (
+                self.expr(op.result),
+                cdecl(typename, ''),
+                fieldname)
+        else:
+            return 'OP_THREADLOCALREF_GET_NONCONST(%s, %s, %s);' % (
+                cdecl(typename, ''),
+                self.expr(op.args[0]),
+                self.expr(op.result))
 
 assert not USESLOTS or '__dict__' not in dir(FunctionCodeGenerator)
diff --git a/rpython/translator/c/src/threadlocal.h b/rpython/translator/c/src/threadlocal.h
--- a/rpython/translator/c/src/threadlocal.h
+++ b/rpython/translator/c/src/threadlocal.h
@@ -86,4 +86,13 @@
 /* ------------------------------------------------------------ */
 
 
+/* only for the fall-back path in the JIT */
+#define OP_THREADLOCALREF_GET_NONCONST(RESTYPE, offset, r)      \
+    do {                                                        \
+        char *a;                                                \
+        OP_THREADLOCALREF_ADDR(a);                              \
+        r = *(RESTYPE *)(a + offset);                           \
+    } while (0)
+
+
 #endif /* _SRC_THREADLOCAL_H */


More information about the pypy-commit mailing list