[pypy-svn] r55667 - in pypy/branch/eval-loop-experiments: config interpreter interpreter/test

antocuni at codespeak.net antocuni at codespeak.net
Sun Jun 8 15:56:36 CEST 2008


Author: antocuni
Date: Sun Jun  8 15:56:34 2008
New Revision: 55667

Modified:
   pypy/branch/eval-loop-experiments/config/pypyoption.py
   pypy/branch/eval-loop-experiments/interpreter/pycode.py
   pypy/branch/eval-loop-experiments/interpreter/pyopcode.py
   pypy/branch/eval-loop-experiments/interpreter/test/test_interpreter.py
Log:
experimental option that precomputes opcode args and pack the bytecode
to contains only opcodes, not opargs; the intent is to make the
interpreter loop a bit smaller.

The benchmarks are a bit controversial, it's not clear if you win or
loose.



Modified: pypy/branch/eval-loop-experiments/config/pypyoption.py
==============================================================================
--- pypy/branch/eval-loop-experiments/config/pypyoption.py	(original)
+++ pypy/branch/eval-loop-experiments/config/pypyoption.py	Sun Jun  8 15:56:34 2008
@@ -138,7 +138,11 @@
 
     BoolOption("usepycfiles", "Write and read pyc files when importing",
                default=True),
-   
+
+    BoolOption("usecodeargs", "store opcodes and opargs in separate lists",
+               default=False,
+               requires=[("objspace.usepycfiles", False)]),
+
     BoolOption("honor__builtins__",
                "Honor the __builtins__ key of a module dictionary",
                default=False),

Modified: pypy/branch/eval-loop-experiments/interpreter/pycode.py
==============================================================================
--- pypy/branch/eval-loop-experiments/interpreter/pycode.py	(original)
+++ pypy/branch/eval-loop-experiments/interpreter/pycode.py	Sun Jun  8 15:56:34 2008
@@ -11,6 +11,7 @@
 from pypy.interpreter.gateway import NoneNotWrapped 
 from pypy.interpreter.baseobjspace import ObjSpace, W_Root
 from pypy.rlib.rarithmetic import intmask
+from pypy.tool import stdlib_opcode
 
 # helper
 
@@ -65,7 +66,7 @@
         self.co_nlocals = nlocals
         self.co_stacksize = stacksize
         self.co_flags = flags
-        self.co_code = code
+        self.precompute_code(code)
         self.co_consts_w = consts
         self.co_names_w = [space.new_interned_str(aname) for aname in names]
         self.co_varnames = varnames
@@ -114,7 +115,53 @@
 
     def signature(self):
         return self._signature
-    
+
+    def precompute_code(self, code):
+        from pypy.interpreter.pyopcode import decode_opcode
+        if not self.space.config.objspace.usecodeargs:
+            self.co_code = code
+            self.co_codeargs = None
+            return
+
+        next_instr = 0
+        codeargs = []
+        codelist = []
+        orig2new = {} # index in code --> index in codelist
+        new2orig = {} # index in codelist --> index in code
+        while next_instr < len(code):
+            opcode = ord(code[next_instr])
+            orig2new[next_instr] = len(codelist)
+            new2orig[len(codelist)] = next_instr
+            next_instr += 1
+            next_instr, opcode, oparg = decode_opcode(code, next_instr, opcode)
+            codelist.append(chr(opcode))
+            codeargs.append(oparg)
+
+        # sanity check
+        assert len(codelist) == len(codeargs)
+        for i, j in orig2new.iteritems():
+            assert code[i] == codelist[j]
+
+        # recompute target addresses
+        i = 0
+        while i<len(codelist):
+            opcode = ord(codelist[i])
+            if opcode in stdlib_opcode.hasjabs:
+                oparg = codeargs[i]
+                codeargs[i] = orig2new[oparg]
+            elif opcode in stdlib_opcode.hasjrel:
+                oparg = codeargs[i]
+                orig_from = new2orig[i]
+                orig_to = orig_from + oparg + 3
+                new_from = i
+                new_to = orig2new[orig_to]
+                new_offset = new_to - new_from - 1
+                codeargs[i] = new_offset
+            i+=1
+
+        self.co_code = ''.join(codelist)
+        self.co_codeargs = codeargs
+
     def _from_code(space, code, hidden_applevel=False):
         """ Initialize the code object from a real (CPython) one.
             This is just a hack, until we have our own compile.

Modified: pypy/branch/eval-loop-experiments/interpreter/pyopcode.py
==============================================================================
--- pypy/branch/eval-loop-experiments/interpreter/pyopcode.py	(original)
+++ pypy/branch/eval-loop-experiments/interpreter/pyopcode.py	Sun Jun  8 15:56:34 2008
@@ -60,6 +60,31 @@
 unrolling_compare_dispatch_table = unrolling_iterable(
     enumerate(compare_dispatch_table))
 
+def decode_opcode(co_code, next_instr, opcode):
+    if opcode >= HAVE_ARGUMENT:
+        lo = ord(co_code[next_instr])
+        hi = ord(co_code[next_instr+1])
+        next_instr += 2
+        oparg = (hi << 8) | lo
+        hint(opcode, concrete=True)
+        hint(oparg, concrete=True)
+    else:
+        oparg = 0
+
+    while opcode == opcodedesc.EXTENDED_ARG.index:
+        opcode = ord(co_code[next_instr])
+        if opcode < HAVE_ARGUMENT:
+            raise BytecodeCorruption
+        lo = ord(co_code[next_instr+1])
+        hi = ord(co_code[next_instr+2])
+        next_instr += 3
+        oparg = (oparg << 16) | (hi << 8) | lo
+        hint(opcode, concrete=True)
+        hint(oparg, concrete=True)
+
+    return next_instr, opcode, oparg
+decode_opcode._always_inline_ = True
+
 
 class __extend__(pyframe.PyFrame):
     """A PyFrame that knows about interpretation of standard Python opcodes
@@ -73,20 +98,21 @@
 
         next_instr = r_uint(next_instr)
         co_code = pycode.co_code
+        co_codeargs = pycode.co_codeargs
 
         try:
             while True:
-                next_instr = self.handle_bytecode(co_code, next_instr, ec)
+                next_instr = self.handle_bytecode(co_code, co_codeargs, next_instr, ec)
                 rstack.resume_point("dispatch", self, co_code, ec,
                                     returns=next_instr)
         except ExitFrame:
             return self.popvalue()
 
-    def handle_bytecode(self, co_code, next_instr, ec):
+    def handle_bytecode(self, co_code, co_codeargs, next_instr, ec):
         from pypy.rlib import rstack # for resume points
 
         try:
-            next_instr = self.dispatch_bytecode(co_code, next_instr, ec)
+            next_instr = self.dispatch_bytecode(co_code, co_codeargs, next_instr, ec)
             rstack.resume_point("handle_bytecode", self, co_code, ec,
                                 returns=next_instr)
         except OperationError, operr:
@@ -144,38 +170,25 @@
             next_instr = block.handle(self, unroller)
             return next_instr
 
-    def dispatch_bytecode(self, co_code, next_instr, ec):
+    def dispatch_bytecode(self, co_code, co_codeargs, next_instr, ec):
         space = self.space
         while True:
+            
             self.last_instr = intmask(next_instr)
             if not we_are_jitted():
                 ec.bytecode_trace(self)
                 next_instr = r_uint(self.last_instr)
             opcode = ord(co_code[next_instr])
-            next_instr += 1
+
             if space.config.objspace.logbytecodes:
                 space.bytecodecounts[opcode] = space.bytecodecounts.get(opcode, 0) + 1
 
-            if opcode >= HAVE_ARGUMENT:
-                lo = ord(co_code[next_instr])
-                hi = ord(co_code[next_instr+1])
-                next_instr += 2
-                oparg = (hi << 8) | lo
+            if space.config.objspace.usecodeargs:
+                oparg = co_codeargs[next_instr]
+                next_instr += 1
             else:
-                oparg = 0
-            hint(opcode, concrete=True)
-            hint(oparg, concrete=True)
-
-            while opcode == opcodedesc.EXTENDED_ARG.index:
-                opcode = ord(co_code[next_instr])
-                if opcode < HAVE_ARGUMENT:
-                    raise BytecodeCorruption
-                lo = ord(co_code[next_instr+1])
-                hi = ord(co_code[next_instr+2])
-                next_instr += 3
-                oparg = (oparg << 16) | (hi << 8) | lo
-                hint(opcode, concrete=True)
-                hint(oparg, concrete=True)
+                next_instr += 1
+                next_instr, opcode, oparg = decode_opcode(co_code, next_instr, opcode)
 
             if opcode == opcodedesc.RETURN_VALUE.index:
                 w_returnvalue = self.popvalue()

Modified: pypy/branch/eval-loop-experiments/interpreter/test/test_interpreter.py
==============================================================================
--- pypy/branch/eval-loop-experiments/interpreter/test/test_interpreter.py	(original)
+++ pypy/branch/eval-loop-experiments/interpreter/test/test_interpreter.py	Sun Jun  8 15:56:34 2008
@@ -43,6 +43,16 @@
         ec = self.space.getexecutioncontext() 
         ec.compiler = self.saved_compiler
 
+    def test_jump(self):
+        code ='''
+            def f(s):
+                if len(s) == 0:
+                    return 42
+                return 24
+        '''
+        assert self.codetest(code, 'f', ['']) == 42
+        assert self.codetest(code, 'f', ['x']) == 24
+
     def test_exception_trivial(self):
         x = self.codetest('''\
                 def f():



More information about the Pypy-commit mailing list