Skip to content

Commit

Permalink
changes
Browse files Browse the repository at this point in the history
* rename dump_code() to dump_bytecode()
* document dump_bytecode()
* rename _CodePeepholeOptimizer to PeepholeOptimizer
* fix type: _is_final() => is_final()
* add an example for the peephole optimizer
  • Loading branch information
vstinner committed Feb 29, 2016
1 parent 007494d commit 918382b
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 44 deletions.
20 changes: 10 additions & 10 deletions bytecode/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@


# FIXME: move code into a submodule or inside Bytecode classes?
def dump_code(code):
def dump_bytecode(bytecode):
indent = ' ' * 4
if isinstance(code, ConcreteBytecode):
if isinstance(bytecode, ConcreteBytecode):
line_width = 3

offset = 0
lineno = None
for instr in code:
for instr in bytecode:
fields = []
if instr.lineno != lineno:
fields.append(str(instr.lineno).rjust(line_width))
Expand All @@ -32,13 +32,13 @@ def dump_code(code):
print(''.join(fields))

offset += instr.size
elif isinstance(code, Bytecode):
elif isinstance(bytecode, Bytecode):
labels = {}
for index, instr in enumerate(code):
for index, instr in enumerate(bytecode):
if isinstance(instr, Label):
labels[instr] = 'label_instr%s' % index

for index, instr in enumerate(code):
for index, instr in enumerate(bytecode):
if isinstance(instr, Label):
label = labels[instr]
line = "%s:" % label
Expand All @@ -48,17 +48,17 @@ def dump_code(code):
line = indent + instr._format(labels)
print(line)
print()
elif isinstance(code, BytecodeBlocks):
elif isinstance(bytecode, BytecodeBlocks):
labels = {}
for block_index, block in enumerate(code, 1):
for block_index, block in enumerate(bytecode, 1):
block_label = 'label_block%s' % block_index
labels[block.label] = block_label

for index, instr in enumerate(block):
if isinstance(instr, Label):
labels[instr] = '%s_instr%s' % (block_label, index)

for block_index, block in enumerate(code, 1):
for block_index, block in enumerate(bytecode, 1):
print('%s:' % labels[block.label])
for instr in block:
if isinstance(instr, Label):
Expand All @@ -71,4 +71,4 @@ def dump_code(code):
print(indent + "-> %s" % labels[block.next_block.label])
print()
else:
raise TypeError("unknown bycode code")
raise TypeError("unknown bytecode class")
4 changes: 2 additions & 2 deletions bytecode/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,12 @@ def from_bytecode(bytecode):
old_label = block_starts[index]
if index != 0:
new_block = bytecode_blocks.add_block()
if not block[-1]._is_final():
if not block[-1].is_final():
block.next_block = new_block
block = new_block
if old_label is not None:
labels[old_label] = block.label
elif block and block[-1]._is_final():
elif block and block[-1].is_final():
block = bytecode_blocks.add_block()

if not isinstance(instr, Label):
Expand Down
10 changes: 5 additions & 5 deletions bytecode/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
class DumpCodeTests(unittest.TestCase):
maxDiff = 80 * 100

def check_dump_code(self, code, expected):
def check_dump_bytecode(self, code, expected):
with contextlib.redirect_stdout(io.StringIO()) as stderr:
bytecode.dump_code(code)
bytecode.dump_bytecode(code)
output = stderr.getvalue()

self.assertEqual(output, expected)
Expand Down Expand Up @@ -51,7 +51,7 @@ def func(test):
RETURN_VALUE
"""[1:].rstrip(" ")
self.check_dump_code(code, expected)
self.check_dump_bytecode(code, expected)

def test_bytecode_blocks(self):
source = """
Expand Down Expand Up @@ -86,7 +86,7 @@ def func(test):
RETURN_VALUE
""").lstrip()
self.check_dump_code(code, expected)
self.check_dump_bytecode(code, expected)

def test_concrete_bytecode(self):
source = """
Expand Down Expand Up @@ -116,7 +116,7 @@ def func(test):
6 32 LOAD_CONST 3
35 RETURN_VALUE
""".lstrip("\n")
self.check_dump_code(code, expected)
self.check_dump_bytecode(code, expected)


class MiscTests(unittest.TestCase):
Expand Down
6 changes: 3 additions & 3 deletions bytecode/tests/test_peephole_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ class Tests(TestCase):
def optimize_blocks(self, code):
if isinstance(code, Bytecode):
code = BytecodeBlocks.from_bytecode(code)
optimizer = peephole_opt._CodePeepholeOptimizer()
optimizer = peephole_opt.PeepholeOptimizer()
optimizer._optimize(code)
return code

def check(self, code, *expected):
if isinstance(code, Bytecode):
code = BytecodeBlocks.from_bytecode(code)
optimizer = peephole_opt._CodePeepholeOptimizer()
optimizer = peephole_opt.PeepholeOptimizer()
optimizer._optimize(code)
code = code.to_bytecode()

Expand Down Expand Up @@ -347,7 +347,7 @@ def test_optimize_code_obj(self):
Instr('RETURN_VALUE')])
noopt = noopt.to_code()

optimizer = peephole_opt._CodePeepholeOptimizer()
optimizer = peephole_opt.PeepholeOptimizer()
optim = optimizer.optimize(noopt)

code = Bytecode.from_code(optim)
Expand Down
10 changes: 10 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ Bytecode classes:
* :class:`BytecodeBlocks`


Functions
=========

.. function:: dump_code(bytecode)

Dump a bytecode to the standard output.

This function is written for debug purpose.


Instructions
============

Expand Down
89 changes: 65 additions & 24 deletions doc/peephole.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,71 @@ Peephole optimizer based on the peephole optimizer of CPython 3.6 written in C.
It is implemented with the :class:`BytecodeBlocks` class.


API
===

Content of the ``bytecode.peephole_opt`` module:

.. class:: PeepholeOptimizer

.. method:: optimize(code: types.CodeType) -> types.CodeType

Optimize a Python code object.

Return a new optimized Python code object.


.. class:: CodeTransformer

Code transformer for the API of the `PEP 511
<https://www.python.org/dev/peps/pep-0511/>`_ (API for code transformers).

.. method:: code_transformer(code, context)

Run the :class:`PeepholeOptimizer` optimizer on the code.

Return a new optimized Python code object.


Example
=======

Code::

import dis
from bytecode.peephole_opt import PeepholeOptimizer

code = compile('print(5+5)', '<string>', 'exec')
print("Non-optimized:")
dis.dis(code)
print()

code = PeepholeOptimizer().optimize(code)
print("Optimized:")
dis.dis(code)

Output of Python 3.6 patched with the PEP 511 with ``python -o noopt`` (to
disable the builtin peephole optimizer)::

Non-optimized:
1 0 LOAD_NAME 0 (print)
3 LOAD_CONST 0 (5)
6 LOAD_CONST 0 (5)
9 BINARY_ADD
10 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
13 POP_TOP
14 LOAD_CONST 1 (None)
17 RETURN_VALUE

Optimized:
1 0 LOAD_NAME 0 (print)
3 LOAD_CONST 0 (10)
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 POP_TOP
10 LOAD_CONST 1 (None)
13 RETURN_VALUE


Optimizations
=============

Expand Down Expand Up @@ -53,27 +118,3 @@ knownledge of the code.

.. note::
``3 < 5`` or ``(1, 2, 3)[1]`` are not optimized.


API
===

.. class:: PeepholeOptimizer

.. method:: optimize(code: types.CodeType) -> types.CodeType

Optimize a Python code object.

Return a new optimized Python code object.


.. class:: CodeTransformer

Code transformer for the API of the `PEP 511
<https://www.python.org/dev/peps/pep-0511/>`_ (API for code transformers).

.. method:: code_transformer(code, context)

Run the :class:`PeepholeOptimizer` optimizer on the code.

Return a new optimized Python code object.

0 comments on commit 918382b

Please sign in to comment.