[pypy-svn] r32893 - in pypy/dist/pypy/translator/cli: . test

antocuni at codespeak.net antocuni at codespeak.net
Wed Oct 4 23:37:11 CEST 2006


Author: antocuni
Date: Wed Oct  4 23:37:10 2006
New Revision: 32893

Modified:
   pypy/dist/pypy/translator/cli/conftest.py
   pypy/dist/pypy/translator/cli/gencli.py
   pypy/dist/pypy/translator/cli/ilgenerator.py
   pypy/dist/pypy/translator/cli/option.py
   pypy/dist/pypy/translator/cli/stackopt.py
   pypy/dist/pypy/translator/cli/test/test_stackopt.py
Log:
Implemented first two stack optimizations (variable renaming and
temporary variable removal). Benchmarks on rpystonedalone show the
following results:

  - source code size reduced by 10%
  - executable size reduced by 13%
  - rpystone speeded up by 5%
  - richards speeded up by 12%



Modified: pypy/dist/pypy/translator/cli/conftest.py
==============================================================================
--- pypy/dist/pypy/translator/cli/conftest.py	(original)
+++ pypy/dist/pypy/translator/cli/conftest.py	Wed Oct  4 23:37:10 2006
@@ -25,6 +25,9 @@
 
           Option('--norun', action='store_true', dest="norun", default=False,
                  help="don't run the compiled executable"),
+
+          Option('--nostackopt', action='store_true', dest='nostackopt', default=False,
+                 help="don't optimize stack load/store operations"),
           )
 
 

Modified: pypy/dist/pypy/translator/cli/gencli.py
==============================================================================
--- pypy/dist/pypy/translator/cli/gencli.py	(original)
+++ pypy/dist/pypy/translator/cli/gencli.py	Wed Oct  4 23:37:10 2006
@@ -16,7 +16,7 @@
 from pypy.translator.cli.prebuiltnodes import get_prebuilt_nodes
 from pypy.translator.cli.stackopt import StackOptGenerator
 
-USE_STACKOPT = True
+USE_STACKOPT = True and not getoption('nostackopt')
 
 class GenCli(object):
     def __init__(self, tmpdir, translator, entrypoint=None, type_system_class=CTS,

Modified: pypy/dist/pypy/translator/cli/ilgenerator.py
==============================================================================
--- pypy/dist/pypy/translator/cli/ilgenerator.py	(original)
+++ pypy/dist/pypy/translator/cli/ilgenerator.py	Wed Oct  4 23:37:10 2006
@@ -61,6 +61,12 @@
     def writeline(self, s=''):
         self.code.writeline(s)
 
+    def openblock(self):
+        self.code.openblock()
+
+    def closeblock(self):
+        self.code.closeblock()
+
     def begin_class(self, name, base=None, sealed=False, interfaces=(), abstract=False,
                     beforefieldinit=False, serializable=True):
         if base is None:
@@ -115,20 +121,18 @@
         self.code.closeblock()
 
     def begin_try(self):
-        self.code.writeline('.try')
-        self.code.openblock()
+        self.writeline('.try')
+        self.openblock()
 
     def end_try(self):
-        self.flush()
-        self.code.closeblock()
+        self.closeblock()
 
     def begin_catch(self, type_):
-        self.code.writeline('catch ' + type_)
-        self.code.openblock()
+        self.writeline('catch ' + type_)
+        self.openblock()
 
     def end_catch(self):
-        self.flush()
-        self.code.closeblock()
+        self.closeblock()
 
     def locals(self, vars):
         varlist = ', '.join(['%s %s' % var for var in vars])        

Modified: pypy/dist/pypy/translator/cli/option.py
==============================================================================
--- pypy/dist/pypy/translator/cli/option.py	(original)
+++ pypy/dist/pypy/translator/cli/option.py	Wed Oct  4 23:37:10 2006
@@ -1,6 +1,6 @@
 from pypy.translator.cli.conftest import option
 
-_defaultopt = dict(wd = False, source = False, nostop = False, stdout = False)
+_defaultopt = dict(wd = False, source = False, nostop = False, stdout = False, nostackopt = False)
 
 def getoption(name):
     return getattr(option, name, _defaultopt.get(name))

Modified: pypy/dist/pypy/translator/cli/stackopt.py
==============================================================================
--- pypy/dist/pypy/translator/cli/stackopt.py	(original)
+++ pypy/dist/pypy/translator/cli/stackopt.py	Wed Oct  4 23:37:10 2006
@@ -1,6 +1,9 @@
 from pypy.translator.cli.ilgenerator import IlasmGenerator
 
 class StackOptMixin(object):
+    LOADS = set(['ldloc', 'ldarg']) # maybe ldarg.0?
+    STORES = set(['stloc'])
+    
     def __init__(self, *args):
         self.super = super(StackOptMixin, self)
         self.super.__init__(*args)
@@ -8,44 +11,68 @@
 
     def _reset(self):
         self.pending_ops = []
+        self.mapping = {} # varname --> (opcode, args) needed to load it
 
     def opcode(self, op, *args):
         self.pending_ops.append((op, args))
 
     def writeline(self, s=''):
-        self.pending_ops.append(('WRITELINE', (s,)))
+        self.pending_ops.append(('SUPER', ('writeline', s)))
 
     def write(self, s, indent=0):
-        self.pending_ops.append(('WRITE', (s, indent)))
+        self.pending_ops.append(('SUPER', ('write', s, indent)))
 
-    def _optimize(self):
-        pass
+    def openblock(self):
+        self.pending_ops.append(('SUPER', ('openblock',)))
 
-    def do_load(self, vartype, var):
-        if vartype == 'local':
-            self.super.load_local(var)
-        elif vartype == 'arg':
-            self.super.load_arg(var)
-        elif vartype == 'self':
-            assert var is None
-            self.super.load_self()
-        else:
-            assert False
-
-    def do_store(self, vartype, var):
-        assert vartype == 'local'
-        self.super.store_local(var)
+    def closeblock(self):
+        self.pending_ops.append(('SUPER', ('closeblock',)))
 
-    def do_opcode(self, opcode, *args):
-        self.super.opcode(opcode, *args)
+    def _optimize(self):
+        assign_count = {}
+        read_count = {}
+        for op, args in self.pending_ops:
+            if op in self.STORES:
+                varname, = args
+                assign_count[varname] = assign_count.get(varname, 0) + 1
+            elif op in self.LOADS:
+                varname, = args
+                read_count[varname] = read_count.get(varname, 0) + 1
+
+        prev_op, prev_args = None, None
+        for i, (op, args) in enumerate(self.pending_ops):
+            if op in self.STORES and prev_op in self.LOADS:
+                # ldloc x, stloc x0 --> remove both, map x0 to x
+                varname, = args
+                if assign_count[varname] == 1:
+                    self.mapping[varname] = self.mapping.get(prev_args[0], (prev_op, prev_args))
+                    self.pending_ops[i-1] = None
+                    self.pending_ops[i] = None
+                    op, args = None, None # to prevent the next opcode thinking the previous was a store
+            elif op in self.LOADS:
+                if prev_op in self.STORES and args == prev_args and read_count[args[0]] == 1:
+                    # stloc x, ldloc x --> remove both
+                    self.pending_ops[i-1] = None
+                    self.pending_ops[i] = None
+                    op, args = None, None # to prevent the next opcode thinking the previous was a load
+                else:
+                    # ldloc x, stloc x1, ..., ldloc x1 --> ..., ldloc x
+                    varname, = args
+                    try:
+                        self.pending_ops[i] = self.mapping[varname]
+                    except KeyError:
+                        pass
+            prev_op, prev_args = op, args
 
     def flush(self):
         self._optimize()
-        for opcode, args in self.pending_ops:
-            if opcode == 'WRITELINE':
-                self.super.writeline(*args)
-            elif opcode == 'WRITE':
-                self.super.write(*args)
+        for item in self.pending_ops:
+            if item is None:
+                continue
+            opcode, args = item
+            if opcode == 'SUPER':
+                method = args[0]
+                getattr(self.super, method)(*args[1:])
             else:
                 self.super.opcode(opcode, *args)
         self._reset()

Modified: pypy/dist/pypy/translator/cli/test/test_stackopt.py
==============================================================================
--- pypy/dist/pypy/translator/cli/test/test_stackopt.py	(original)
+++ pypy/dist/pypy/translator/cli/test/test_stackopt.py	Wed Oct  4 23:37:10 2006
@@ -23,7 +23,6 @@
 class TestStackOpt(StackOptMixin, FakeGenerator):
     pass
 
-
 def test_code_generation():
     ilasm = TestStackOpt()
     ilasm.write('.method public static void foo()')
@@ -48,3 +47,55 @@
     ilasm.opcode('ldloc', 'y')
     ilasm.flush()
     assert ilasm.opcodes == [('ldloc', ('x',)), ('ldloc', ('y',))]
+
+def test_renaming():
+    ilasm = TestStackOpt()
+    ilasm.opcode('ldloc', 'x')
+    ilasm.opcode('stloc', 'x0')
+    ilasm.opcode('ldloc', 'x0')
+    ilasm.flush()
+    assert ilasm.opcodes[0] == ('ldloc', ('x',))
+
+def test_renaming_arg():
+    ilasm = TestStackOpt()
+    ilasm.opcode('ldarg', 'x')
+    ilasm.opcode('stloc', 'x0')
+    ilasm.opcode('ldloc', 'x0')
+    ilasm.flush()
+    assert ilasm.opcodes[0] == ('ldarg', ('x',))
+
+def test_renaming_twice():
+    ilasm = TestStackOpt()
+    ilasm.opcode('ldarg', 'x')
+    ilasm.opcode('stloc', 'x0')
+    ilasm.opcode('ldloc', 'x0')
+    ilasm.opcode('stloc', 'x1')
+    ilasm.opcode('ldloc', 'x1')
+    ilasm.flush()
+    assert ilasm.opcodes[0] == ('ldarg', ('x',))
+
+def test_not_renaming():
+    ilasm = TestStackOpt()
+    ilasm.opcode('ldloc', 'x')
+    ilasm.opcode('stloc', 'x0')
+    ilasm.opcode('ldc.i4.0')
+    ilasm.opcode('stloc', 'x0')
+    ilasm.opcode('nop')
+    ilasm.opcode('ldloc', 'x0')
+    ilasm.flush()
+    assert ilasm.opcodes[-1] == ('ldloc', ('x0',))
+
+def test_remove_tmp():
+    ilasm = TestStackOpt()
+    ilasm.opcode('stloc', 'x')
+    ilasm.opcode('ldloc', 'x')
+    ilasm.flush()
+    assert not ilasm.opcodes
+
+def test_dont_remove_tmp():
+    ilasm = TestStackOpt()
+    ilasm.opcode('stloc', 'x')
+    ilasm.opcode('ldloc', 'x')
+    ilasm.opcode('ldloc', 'x')
+    ilasm.flush()
+    assert ilasm.opcodes[0] == ('stloc', ('x',))



More information about the Pypy-commit mailing list