[pypy-commit] pypy default: Merged in inline-blocks (pull request #467)
arigo
pypy.commits at gmail.com
Tue Aug 9 05:16:23 EDT 2016
Author: Armin Rigo <armin.rigo at gmail.com>
Branch:
Changeset: r86100:4c64ef74c612
Date: 2016-08-09 11:14 +0200
http://bitbucket.org/pypy/pypy/changeset/4c64ef74c612/
Log: Merged in inline-blocks (pull request #467)
Inline gotos to blocks with only one predecessor.
diff --git a/rpython/translator/c/funcgen.py b/rpython/translator/c/funcgen.py
--- a/rpython/translator/c/funcgen.py
+++ b/rpython/translator/c/funcgen.py
@@ -2,7 +2,7 @@
from rpython.translator.c.support import cdecl
from rpython.translator.c.support import llvalue_from_constant, gen_assignments
from rpython.translator.c.support import c_string_constant, barebonearray
-from rpython.flowspace.model import Variable, Constant
+from rpython.flowspace.model import Variable, Constant, mkentrymap
from rpython.rtyper.lltypesystem.lltype import (Ptr, Void, Bool, Signed, Unsigned,
SignedLongLong, Float, UnsignedLongLong, Char, UniChar, ContainerType,
Array, FixedSizeArray, ForwardReference, FuncType)
@@ -173,17 +173,37 @@
def cfunction_body(self):
graph = self.graph
- yield 'goto block0;' # to avoid a warning "this label is not used"
+ # yield 'goto block0;' # to avoid a warning "this label is not used"
- # generate the body of each block
+ # Locate blocks with a single predecessor, which can be written
+ # inline in place of a "goto":
+ entrymap = mkentrymap(graph)
+ self.inlinable_blocks = {
+ block for block in entrymap if len(entrymap[block]) == 1}
+
+ yield ''
+ for line in self.gen_goto(graph.startblock):
+ yield line
+
+ # Only blocks left are those that have more than one predecessor.
for block in graph.iterblocks():
+ if block in self.inlinable_blocks:
+ continue
+ for line in self.gen_block(block):
+ yield line
+
+ def gen_block(self, block):
+ if 1: # (preserve indentation)
myblocknum = self.blocknum[block]
- yield ''
- yield 'block%d:' % myblocknum
+ if block in self.inlinable_blocks:
+ # debug comment
+ yield '/* block%d: (inlined) */' % myblocknum
+ else:
+ yield 'block%d:' % myblocknum
if block in self.innerloops:
for line in self.gen_while_loop_hack(block):
yield line
- continue
+ return
for i, op in enumerate(block.operations):
for line in self.gen_op(op):
yield line
@@ -194,7 +214,7 @@
if self.exception_policy != "exc_helper":
yield 'RPY_DEBUG_RETURN();'
yield 'return %s;' % retval
- continue
+ return
elif block.exitswitch is None:
# single-exit block
assert len(block.exits) == 1
@@ -256,12 +276,25 @@
assignments.append((a2typename, dest, src))
for line in gen_assignments(assignments):
yield line
- label = 'block%d' % self.blocknum[link.target]
- if link.target in self.innerloops:
- loop = self.innerloops[link.target]
+ for line in self.gen_goto(link.target, link):
+ yield line
+
+ def gen_goto(self, target, link=None):
+ """Recursively expand block with inlining or goto.
+
+ Blocks that have only one predecessor are inlined directly, all others
+ are reached via goto.
+ """
+ label = 'block%d' % self.blocknum[target]
+ if target in self.innerloops:
+ loop = self.innerloops[target]
if link is loop.links[-1]: # link that ends a loop
label += '_back'
- yield 'goto %s;' % label
+ if target in self.inlinable_blocks:
+ for line in self.gen_block(target):
+ yield line
+ else:
+ yield 'goto %s;' % label
def gen_op(self, op):
macro = 'OP_%s' % op.opname.upper()
diff --git a/rpython/translator/c/test/test_genc.py b/rpython/translator/c/test/test_genc.py
--- a/rpython/translator/c/test/test_genc.py
+++ b/rpython/translator/c/test/test_genc.py
@@ -1,4 +1,5 @@
import ctypes
+import re
from collections import OrderedDict
import py
@@ -13,6 +14,7 @@
from rpython.rtyper.lltypesystem.rstr import STR
from rpython.tool.nullpath import NullPyPathLocal
from rpython.translator.c import genc
+from rpython.translator.backendopt.merge_if_blocks import merge_if_blocks
from rpython.translator.interactive import Translation
from rpython.translator.translator import TranslationContext, graphof
@@ -604,3 +606,23 @@
else:
assert 0, "the call was not found in the C source"
assert 'PYPY_INHIBIT_TAIL_CALL();' in lines[i+1]
+
+def get_generated_c_source(fn, types):
+ """Return the generated C source for fn."""
+ t = Translation(fn, types, backend="c")
+ t.annotate()
+ merge_if_blocks(t.driver.translator.graphs[0])
+ c_filename_path = t.source_c()
+ return t.driver.cbuilder.c_source_filename.join('..',
+ 'rpython_translator_c_test.c').read()
+
+def test_generated_c_source_no_gotos():
+ # We want simple functions to have no indirection/goto.
+ # Instead, PyPy can inline blocks when they aren't reused.
+
+ def main(x):
+ return x + 1
+
+ c_src = get_generated_c_source(main, [int])
+ assert 'goto' not in c_src
+ assert not re.search(r'block\w*:(?! \(inlined\))', c_src)
More information about the pypy-commit
mailing list