Author: Carl Friedrich Bolz <[email protected]>
Branch: applevel-unroll-safe
Changeset: r84976:b2124aa81410
Date: 2016-06-06 17:17 +0200
http://bitbucket.org/pypy/pypy/changeset/b2124aa81410/

Log:    experimental hack: implement @unroll_safe at app-level

diff --git a/lib-python/2.7/opcode.py b/lib-python/2.7/opcode.py
--- a/lib-python/2.7/opcode.py
+++ b/lib-python/2.7/opcode.py
@@ -194,5 +194,6 @@
 def_op('CALL_METHOD', 202)            # #args not including 'self'
 def_op('BUILD_LIST_FROM_ARG', 203)
 jrel_op('JUMP_IF_NOT_DEBUG', 204)     # jump over assert statements
+jabs_op('JUMP_ABSOLUTE_UNROLL', 205)  # ""
 
 del def_op, name_op, jrel_op, jabs_op
diff --git a/pypy/interpreter/astcompiler/assemble.py 
b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -657,6 +657,7 @@
 
     ops.JUMP_FORWARD: 0,
     ops.JUMP_ABSOLUTE: 0,
+    ops.JUMP_ABSOLUTE_UNROLL: 0,
     ops.JUMP_IF_TRUE_OR_POP: 0,
     ops.JUMP_IF_FALSE_OR_POP: 0,
     ops.POP_JUMP_IF_TRUE: -1,
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -198,6 +198,8 @@
                 return next_instr
             elif opcode == opcodedesc.JUMP_ABSOLUTE.index:
                 return self.jump_absolute(oparg, ec)
+            elif opcode == opcodedesc.JUMP_ABSOLUTE_UNROLL.index:
+                return self.jump_absolute_unroll(oparg, ec)
             elif opcode == opcodedesc.BREAK_LOOP.index:
                 next_instr = self.BREAK_LOOP(oparg, next_instr)
             elif opcode == opcodedesc.CONTINUE_LOOP.index:
@@ -1055,6 +1057,10 @@
         check_nonneg(jumpto)
         return jumpto
 
+    def jump_absolute_unroll(self, jumpto, ec):
+        check_nonneg(jumpto)
+        return jumpto
+
     def JUMP_FORWARD(self, jumpby, next_instr):
         next_instr += jumpby
         return next_instr
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -59,6 +59,7 @@
 
 class Module(MixedModule):
     appleveldefs = {
+        '_unroll_safe'              : 'app_magic._unroll_safe',
     }
 
     interpleveldefs = {
@@ -90,7 +91,7 @@
         'save_module_content_for_future_reload':
                           'interp_magic.save_module_content_for_future_reload',
         'decode_long'               : 'interp_magic.decode_long',
-        '_promote'                   : 'interp_magic._promote',
+        '_promote'                  : 'interp_magic._promote',
     }
     if sys.platform == 'win32':
         interpleveldefs['get_console_cp'] = 'interp_magic.get_console_cp'
diff --git a/pypy/module/__pypy__/app_magic.py 
b/pypy/module/__pypy__/app_magic.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/__pypy__/app_magic.py
@@ -0,0 +1,42 @@
+
+
+def _unroll_safe(func):
+    """ Decorator to mark a function as unroll-safe, meaning the JIT will not
+    trace any of the loops in the function. Instead, the loops will be unrolled
+    fully into the caller.
+
+    This function is experimental.
+    """
+    # bit of a hack: replace JUMP_ABSOLUTE bytecode with JUMP_ABSOLUTE_UNROLL,
+    # which will not trigger jitting
+    from opcode import opname, HAVE_ARGUMENT, EXTENDED_ARG, opmap
+    from types import CodeType, FunctionType
+    code = list(func.func_code.co_code)
+    n = len(code)
+    i = 0
+    replaced = False
+    while i < n:
+        orig_i = i
+        c = code[i]
+        op = ord(c)
+        i = i+1
+        if op >= HAVE_ARGUMENT:
+            i = i+2
+            if op == opmap['JUMP_ABSOLUTE']:
+                replaced = True
+                code[orig_i] = chr(opmap['JUMP_ABSOLUTE_UNROLL'])
+    if not replaced:
+        raise TypeError("function %s does not contain a loop" % func)
+    new_codestring = "".join(code)
+    code = func.func_code
+    new_code = CodeType(code.co_argcount, code.co_nlocals, code.co_stacksize,
+            code.co_flags, new_codestring, code.co_consts, code.co_names,
+            code.co_varnames, code.co_filename, code.co_name, 
code.co_firstlineno,
+            code.co_lnotab, code.co_freevars, code.co_cellvars)
+    f = FunctionType(new_code, func.func_globals, func.func_name,
+                     func.func_defaults, func.func_closure)
+    if func.func_dict:
+        f.func_dict = {}
+        f.func_dict.update(func.func_dict)
+    f.func_doc = func.func_doc
+    return f
diff --git a/pypy/module/__pypy__/test/test_magic.py 
b/pypy/module/__pypy__/test/test_magic.py
--- a/pypy/module/__pypy__/test/test_magic.py
+++ b/pypy/module/__pypy__/test/test_magic.py
@@ -60,3 +60,22 @@
             pass
         a = A()
         assert _promote(a) is a
+
+    def test_unroll_safe(self):
+        from __pypy__ import _unroll_safe
+        import opcode
+        def f(x):
+            r = 0
+            for i in range(x):
+                r += i
+            return r
+        assert chr(opcode.opmap['JUMP_ABSOLUTE_UNROLL']) not in 
f.func_code.co_code
+        f1 = _unroll_safe(f)
+        assert chr(opcode.opmap['JUMP_ABSOLUTE_UNROLL']) in 
f1.func_code.co_code
+        assert f(10) == f1(10)
+
+        def decorate_no_loop():
+            @_unroll_safe
+            def f(x):
+                pass
+        raises(TypeError, decorate_no_loop)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to