Commits

Armin Rigo committed 3ab1f1a

Complete pypy.rlib.register.

  • Participants
  • Parent commits c292d7b
  • Branches shadowstack-perf

Comments (0)

Files changed (4)

pypy/rlib/register.py

+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.rpython.tool import rffi_platform
+
+# On platforms with enough hardware registers and with gcc, we can
+# (ab)use gcc to globally assign a register to a single global void*
+# variable.  We use it with a double meaning:
+#
+# - when it is NULL upon return from a function, it means that an
+#   exception occurred.  It allows the caller to quickly check for
+#   exceptions.
+#
+# - in other cases, with --gcrootfinder=shadowstack, it points to
+#   the top of the shadow stack.
+
+
+# For now, only for x86-64.  Tries to use the register r15.
+eci = ExternalCompilationInfo(
+    post_include_bits=['register void* pypy_r15 asm("r15");\n'
+                       '#define PYPY_GET_R15() pypy_r15\n'
+                       '#define PYPY_SET_R15(x) (pypy_r15 = x)\n'
+                       ],
+    )
+
+_test_eci = eci.merge(ExternalCompilationInfo(
+    post_include_bits=["""
+            void f(void) {
+                pypy_r15 = &f;
+            }
+    """]))
+
+try:
+    rffi_platform.verify_eci(_test_eci)
+    register_number = 15      # r15
+except rffi_platform.CompilationError:
+    eci = None
+    register_number = None
+else:
+
+    from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+
+    # use load_from_reg(TYPE) and store_into_reg(llvalue) to load and store
+    # a value out of the special register.  When running on top of Python.
+    # the behavior is emulated.
+
+    _value_reg = None
+
+    def _pypy_get_r15():
+        assert _value_reg is not None
+        return _value_reg
+
+    def _pypy_set_r15(addr):
+        global _value_reg
+        _value_reg = addr
+
+    load_from_reg = rffi.llexternal('PYPY_GET_R15', [], llmemory.Address,
+                                    _callable=_pypy_get_r15,
+                                    compilation_info=eci,
+                                    _nowrapper=True)
+
+    store_into_reg = rffi.llexternal('PYPY_SET_R15', [llmemory.Address],
+                                     lltype.Void,
+                                     _callable=_pypy_set_r15,
+                                     compilation_info=eci,
+                                     _nowrapper=True)

pypy/rlib/test/test_register.py

+import py
+from pypy.rlib import register
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+
+
+def test_register():
+    #
+    from pypy.jit.backend.detect_cpu import autodetect
+    if autodetect() == 'x86_64':
+        assert register.eci is not None
+        assert register.register_number == 15        # r15
+    else:
+        assert register.eci is None
+        assert register.register_number is None
+
+
+class TestLoadStore(object):
+    def setup_class(cls):
+        if register.register_number is None:
+            py.test.skip("rlib/register not supported on this platform")
+
+    def test_direct(self):
+        a = rffi.cast(llmemory.Address, 27)
+        register.store_into_reg(a)
+        b = register.load_from_reg()
+        assert lltype.typeOf(b) == llmemory.Address
+        assert rffi.cast(lltype.Signed, b) == 27
+
+    def test_llinterp(self):
+        from pypy.rpython.test.test_llinterp import interpret
+        def f(n):
+            a = rffi.cast(llmemory.Address, n)
+            register.store_into_reg(a)
+            b = register.load_from_reg()
+            return rffi.cast(lltype.Signed, b)
+        res = interpret(f, [41])
+        assert res == 41
+
+    def test_compiled(self):
+        from pypy.translator.c.test.test_genc import compile
+        def f(n):
+            a = rffi.cast(llmemory.Address, n)
+            register.store_into_reg(a)
+            b = register.load_from_reg()
+            return rffi.cast(lltype.Signed, b)
+        cfn = compile(f, [int])
+        res = cfn(43)
+        assert res == 43

pypy/translator/register.py

-from pypy.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.rpython.tool import rffi_platform
-
-# On platforms with enough hardware registers and with gcc, we can
-# (ab)use gcc to globally assign a register to a single global void*
-# variable.  We use it with a double meaning:
-#
-# - when it is NULL upon return from a function, it means that an
-#   exception occurred.  It allows the caller to quickly check for
-#   exceptions.
-#
-# - in other cases, with --gcrootfinder=shadowstack, it points to
-#   the top of the shadow stack.
-
-
-# For now, only for x86-64.  Tries to use the register r15.
-eci = ExternalCompilationInfo(
-    post_include_bits=['register void* pypy_reg asm("r15");'],
-    )
-
-_test_eci = eci.merge(ExternalCompilationInfo(
-    post_include_bits=["""
-            void f(void) {
-                pypy_reg = &f;
-            }
-    """]))
-
-try:
-    rffi_platform.verify_eci(_test_eci)
-    var_name_in_c = 'pypy_reg'
-    register_number = 15      # r15
-except rffi_platform.CompilationError:
-    eci = None
-    var_name_in_c = None
-    register_number = None

pypy/translator/test/test_register.py

-
-def test_register():
-    from pypy.translator import register
-    #
-    from pypy.jit.backend.detect_cpu import autodetect
-    if autodetect() == 'x86_64':
-        assert register.eci is not None
-        assert register.var_name_in_c is not None
-        assert register.register_number == 15        # r15
-    else:
-        assert register.eci is None
-        assert register.var_name_in_c is None
-        assert register.register_number is None