[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