Blame more-readable-c-code.patch

4ef3f10
diff -up pypy-pypy-release-1.7/pypy/interpreter/pycode.py.more-readable-c-code pypy-pypy-release-1.7/pypy/interpreter/pycode.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/interpreter/pycode.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/interpreter/pycode.py	2011-11-21 16:16:15.673463780 -0500
169de85
@@ -13,6 +13,7 @@ from pypy.interpreter.gateway import Non
4ef3f10
 from pypy.interpreter.astcompiler.consts import (
00e57e7
     CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS, CO_NESTED,
00e57e7
     CO_GENERATOR, CO_CONTAINSGLOBALS)
00e57e7
+from pypy.interpreter.pytraceback import offset2lineno
00e57e7
 from pypy.rlib.rarithmetic import intmask
00e57e7
 from pypy.rlib.debug import make_sure_not_resized
00e57e7
 from pypy.rlib import jit
4ef3f10
@@ -81,6 +82,7 @@ class PyCode(eval.Code):
00e57e7
         self.hidden_applevel = hidden_applevel
00e57e7
         self.magic = magic
00e57e7
         self._signature = cpython_code_signature(self)
00e57e7
+        self._cached_source = None
00e57e7
         self._initialize()
00e57e7
 
00e57e7
     def _initialize(self):
4ef3f10
@@ -397,3 +399,23 @@ class PyCode(eval.Code):
169de85
 
00e57e7
     def repr(self, space):
00e57e7
         return space.wrap(self.get_repr())
00e57e7
+
00e57e7
+    def get_linenum_for_offset(self, offset):
00e57e7
+        # Given a bytecode offset, return a 1-based index into the lines of the
00e57e7
+        # source code
00e57e7
+        return offset2lineno(self, offset)
00e57e7
+
00e57e7
+    def _ensure_source(self):
00e57e7
+        # Lazily grab the source lines into self._cached_source (or raise
00e57e7
+        # an IOError)
00e57e7
+        if not self._cached_source:
00e57e7
+            f = open(self.co_filename, 'r')
00e57e7
+            source = [line.rstrip() for line in f.readlines()]
00e57e7
+            f.close()
00e57e7
+            self._cached_source = source
00e57e7
+    
00e57e7
+    def get_source_text(self, linenum):
00e57e7
+        # Given a 1-based index, get the corresponding line of source code (or
00e57e7
+        # raise an IOError)
00e57e7
+        self._ensure_source()
00e57e7
+        return self._cached_source[linenum - 1]
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/objspace/flow/model.py.more-readable-c-code pypy-pypy-release-1.7/pypy/objspace/flow/model.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/objspace/flow/model.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/objspace/flow/model.py	2011-11-21 16:15:36.599466455 -0500
169de85
@@ -31,6 +31,120 @@ from pypy.tool.identity_dict import iden
00e57e7
 
00e57e7
 __metaclass__ = type
00e57e7
 
00e57e7
+class SourceLoc(object):
00e57e7
+    # A srcloc is a specific location within the RPython source code,
00e57e7
+    # intended for human display
00e57e7
+    __slots__ = ('code', # code object
00e57e7
+                 'linenum' # 1-based index, as displayed to a user
00e57e7
+                 )
00e57e7
+    def __init__(self, code, linenum):
00e57e7
+        self.code = code
00e57e7
+        self.linenum = linenum
00e57e7
+
00e57e7
+    def get_text(self):
00e57e7
+        # Get the actual source text of this line
00e57e7
+        return self.code.get_source_text(self.linenum)
00e57e7
+
00e57e7
+    def __eq__(self, other):
00e57e7
+        return self.code == other.code and self.linenum == other.linenum
00e57e7
+
00e57e7
+    def __ne__(self, other):
00e57e7
+        if other:
00e57e7
+            return self.code != other.code or self.linenum != other.linenum
00e57e7
+        else:
00e57e7
+            return True
00e57e7
+
00e57e7
+class CodeLoc(object):
00e57e7
+    # A codeloc is a specific location within the RPython bytecode
00e57e7
+    __slots__ = ('code', # code object
00e57e7
+                 'offset' # int index into bytecode, or -1
00e57e7
+                 )
00e57e7
+
00e57e7
+    def __init__(self, code, offset):
00e57e7
+        self.code = code
00e57e7
+        self.offset = offset
00e57e7
+
00e57e7
+    def __str__(self):
00e57e7
+        if self.offset >= 0:
00e57e7
+            return "%s@%d" % (self.code.co_name, self.offset)
00e57e7
+        else:
00e57e7
+            return ""
00e57e7
+
00e57e7
+    def __ne__(self, other):
00e57e7
+        if other:
00e57e7
+            return self.code != other.code or self.offset != other.offset
00e57e7
+        else:
00e57e7
+            return True
00e57e7
+
00e57e7
+    def __cmp__(self, other):
00e57e7
+        # Partial ordering, for those locations that have an offset:
00e57e7
+        if other:
00e57e7
+            if self.offset >= 0 and other.offset >= 0:
00e57e7
+                return self.offset - other.offset
00e57e7
+        return 0
00e57e7
+
00e57e7
+    def get_source_loc(self):
00e57e7
+        # Convert to a SourceLoc:
00e57e7
+        return SourceLoc(self.code, self.code.get_linenum_for_offset(self.offset))
00e57e7
+
00e57e7
+class OperationLoc(object):
00e57e7
+    # An oploc is the location within the RPython source code of a given
00e57e7
+    # operation
00e57e7
+    # 
00e57e7
+    # This is a list consisting of CodeLoc instances, some of which may be None
00e57e7
+    #
00e57e7
+    # For the simple case, this is list of length 1 with a single CodeLoc
00e57e7
+    #
00e57e7
+    # For an operation inside an inlined callsite, we have a list of length 2:
00e57e7
+    #    [codeloc of callsite,
00e57e7
+    #     codeloc of operation within inlined body]
00e57e7
+    #
00e57e7
+    # For more interesting inlined cases, we have a chain of source locations:
00e57e7
+    #    [codeloc of callsite,
00e57e7
+    #     codeloc of inner callsite,
00e57e7
+    #     ... ,
00e57e7
+    #     codeloc of innermost inlined callsite,
00e57e7
+    #     codeloc of operation within inlined body]
00e57e7
+    #
00e57e7
+
00e57e7
+    __slots__ = ('codelocs', )
00e57e7
+
00e57e7
+    def __init__(self, codelocs):
00e57e7
+        self.codelocs = codelocs
00e57e7
+
00e57e7
+    def __str__(self):
00e57e7
+        return '[' + ' > '.join(str(codeloc) for codeloc in self.codelocs) + ']'
00e57e7
+
00e57e7
+    def __cmp__(self, other):
00e57e7
+        return cmp(self.codelocs, other.codelocs)
00e57e7
+
00e57e7
+def block_comparator(blk0, blk1):
00e57e7
+    '''
00e57e7
+    Sort function for blocks, putting them in an ordering that attempts to
00e57e7
+    maximize readability of the generated C code
00e57e7
+    '''
00e57e7
+    # print 'comparing %r and %r' % (blk0, blk1)
00e57e7
+    # Put the start/end block at the top/bottom:
00e57e7
+    if blk0.isstartblock:
00e57e7
+        return -1
00e57e7
+
00e57e7
+    if blk1.isstartblock:
00e57e7
+        return 1
00e57e7
+
00e57e7
+    # Order blocks by the offset, where present:
00e57e7
+    if blk0.operations:
00e57e7
+        if blk1.operations:
00e57e7
+            return cmp(blk0.operations[0].oploc, blk1.operations[0].oploc)
00e57e7
+        else:
00e57e7
+            return -1
00e57e7
+    else:
00e57e7
+        if blk1.operations:
00e57e7
+            return 1
00e57e7
+        else:
00e57e7
+            return 0
00e57e7
+
00e57e7
+def edge_comparator(edge0, edge1):
00e57e7
+    return block_comparator(edge0.target, edge1.target)
00e57e7
 
00e57e7
 class FunctionGraph(object):
00e57e7
     __slots__ = ['startblock', 'returnblock', 'exceptblock', '__dict__']
169de85
@@ -94,6 +208,21 @@ class FunctionGraph(object):
00e57e7
                 seen[block] = True
00e57e7
                 stack += block.exits[::-1]
00e57e7
 
00e57e7
+    def iterblocks_by_source(self):
00e57e7
+        # Try to preserve logical source ordering in the blocks
00e57e7
+        block = self.startblock
00e57e7
+        yield block
00e57e7
+        seen = {block: True}
00e57e7
+        stack = list(block.exits[::-1])
00e57e7
+        stack.sort(edge_comparator)
00e57e7
+        while stack:
00e57e7
+            block = stack.pop().target
00e57e7
+            if block not in seen:
00e57e7
+                yield block
00e57e7
+                seen[block] = True
00e57e7
+                stack += block.exits[::-1]
00e57e7
+                stack.sort(edge_comparator)
00e57e7
+
00e57e7
     def iterlinks(self):
00e57e7
         block = self.startblock
00e57e7
         seen = {block: True}
169de85
@@ -183,14 +312,14 @@ class Block(object):
00e57e7
         self.exits      = []              # list of Link(s)
00e57e7
 
00e57e7
     def at(self):
00e57e7
-        if self.operations and self.operations[0].offset >= 0:
00e57e7
-            return "@%d" % self.operations[0].offset
00e57e7
+        if self.operations:
00e57e7
+            return str(self.operations[0].oploc)
00e57e7
         else:
00e57e7
             return ""
00e57e7
 
00e57e7
     def __str__(self):
00e57e7
         if self.operations:
00e57e7
-            txt = "block@%d" % self.operations[0].offset
00e57e7
+            txt = "block%s" % self.operations[0].oploc
00e57e7
         else:
00e57e7
             if (not self.exits) and len(self.inputargs) == 1:
00e57e7
                 txt = "return block"
169de85
@@ -245,6 +374,21 @@ class Block(object):
00e57e7
         from pypy.translator.tool.graphpage import try_show
00e57e7
         try_show(self)
00e57e7
 
00e57e7
+    def isreturnblock(self):
00e57e7
+        return (not self.operations) and (not self.exits) and len(self.inputargs) == 1
00e57e7
+
00e57e7
+    def get_base_label(self, blocknum):
00e57e7
+        # Generate a more friendly C label for this block
00e57e7
+        if self.operations:
00e57e7
+            txt = "block"
00e57e7
+        elif (not self.exits) and len(self.inputargs) == 1:
00e57e7
+            txt = "return_block"
00e57e7
+        elif (not self.exits) and len(self.inputargs) == 2:
00e57e7
+            txt = "raise_block"
00e57e7
+        else:
00e57e7
+            txt = "codeless_block"
00e57e7
+        return '%s%d' % (txt, blocknum)
00e57e7
+
00e57e7
 
00e57e7
 class Variable(object):
00e57e7
     __slots__ = ["_name", "_nr", "concretetype"]
169de85
@@ -331,13 +475,15 @@ class WrapException(Exception):
00e57e7
 
00e57e7
 
00e57e7
 class SpaceOperation(object):
00e57e7
-    __slots__ = "opname args result offset".split()
00e57e7
+    __slots__ = "opname args result oploc".split()
00e57e7
 
00e57e7
-    def __init__(self, opname, args, result, offset=-1):
00e57e7
+    def __init__(self, opname, args, result, oploc=None):
00e57e7
         self.opname = intern(opname)      # operation name
00e57e7
         self.args   = list(args)  # mixed list of var/const
00e57e7
         self.result = result      # either Variable or Constant instance
00e57e7
-        self.offset = offset      # offset in code string
00e57e7
+        if oploc is None:
00e57e7
+            oploc = OperationLoc([None])
00e57e7
+        self.oploc = oploc
00e57e7
 
00e57e7
     def __eq__(self, other):
00e57e7
         return (self.__class__ is other.__class__ and 
169de85
@@ -352,8 +498,9 @@ class SpaceOperation(object):
00e57e7
         return hash((self.opname,tuple(self.args),self.result))
00e57e7
 
00e57e7
     def __repr__(self):
00e57e7
-        return "%r = %s(%s)" % (self.result, self.opname,
00e57e7
-                                ", ".join(map(repr, self.args)))
00e57e7
+        return "%r = %s(%s) (%s)" % (self.result, self.opname,
00e57e7
+                                     ", ".join(map(repr, self.args)),
00e57e7
+                                     self.oploc)
00e57e7
 
00e57e7
 class Atom(object):
00e57e7
     def __init__(self, name):
169de85
@@ -427,8 +574,7 @@ def copygraph(graph, shallow=False, varm
00e57e7
                 for op in oplist:
00e57e7
                     copyop = SpaceOperation(op.opname,
00e57e7
                                             [copyvar(v) for v in op.args],
00e57e7
-                                            copyvar(op.result), op.offset)
00e57e7
-                    #copyop.offset = op.offset
00e57e7
+                                            copyvar(op.result), op.oploc)
00e57e7
                     result.append(copyop)
00e57e7
                 return result
00e57e7
             newblock.operations = copyoplist(block.operations)
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/objspace/flow/objspace.py.more-readable-c-code pypy-pypy-release-1.7/pypy/objspace/flow/objspace.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/objspace/flow/objspace.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/objspace/flow/objspace.py	2011-11-21 16:15:36.600466455 -0500
4ef3f10
@@ -315,7 +315,9 @@ class FlowObjSpace(ObjSpace):
00e57e7
     def do_operation(self, name, *args_w):
00e57e7
         spaceop = SpaceOperation(name, args_w, Variable())
00e57e7
         if hasattr(self, 'executioncontext'):  # not here during bootstrapping
00e57e7
-            spaceop.offset = self.executioncontext.crnt_offset
00e57e7
+            codeloc = CodeLoc(self.executioncontext.code,
00e57e7
+                              self.executioncontext.crnt_offset)
00e57e7
+            spaceop.oploc = OperationLoc([codeloc])
00e57e7
             self.executioncontext.recorder.append(spaceop)
00e57e7
         return spaceop.result
00e57e7
 
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/objspace/flow/test/test_model.py.more-readable-c-code pypy-pypy-release-1.7/pypy/objspace/flow/test/test_model.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/objspace/flow/test/test_model.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/objspace/flow/test/test_model.py	2011-11-21 16:15:36.600466455 -0500
169de85
@@ -119,3 +119,25 @@ def test_variable():
00e57e7
     assert v2.renamed
00e57e7
     assert v2.name.startswith("foobar_") and v2.name != v.name
00e57e7
     assert v2.name.split('_', 1)[1].isdigit()
00e57e7
+
00e57e7
+def test_source_locations():
00e57e7
+    # Invent some random offsets into the code:
00e57e7
+    co = sample_function.__code__
00e57e7
+    codelocA = CodeLoc(co, 42)
00e57e7
+    codelocB = CodeLoc(co, 87)
00e57e7
+
00e57e7
+    assert str(codelocA) == 'sample_function@42'
00e57e7
+    assert str(codelocB) == 'sample_function@87'
00e57e7
+
00e57e7
+    assert cmp(codelocA, codelocB) < 0
00e57e7
+    assert cmp(codelocB, codelocA) > 0
00e57e7
+    
00e57e7
+    oplocA = OperationLoc([codelocA])
00e57e7
+    oplocB = OperationLoc([codelocB])
00e57e7
+
00e57e7
+    assert str(oplocA) == '[sample_function@42]'
00e57e7
+    assert str(oplocB) == '[sample_function@87]'
00e57e7
+
00e57e7
+    assert cmp(oplocA, oplocB) < 0
00e57e7
+    assert cmp(oplocB, oplocA) > 0
00e57e7
+
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/rpython/rtyper.py.more-readable-c-code pypy-pypy-release-1.7/pypy/rpython/rtyper.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/rpython/rtyper.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/rpython/rtyper.py	2011-11-21 16:15:36.601466455 -0500
169de85
@@ -800,7 +800,7 @@ class HighLevelOp(object):
00e57e7
         return vars
00e57e7
 
00e57e7
     def genop(self, opname, args_v, resulttype=None):
00e57e7
-        return self.llops.genop(opname, args_v, resulttype)
00e57e7
+        return self.llops.genop(opname, args_v, resulttype, self.spaceop.oploc)
00e57e7
 
00e57e7
     def gendirectcall(self, ll_function, *args_v):
00e57e7
         return self.llops.gendirectcall(ll_function, *args_v)
169de85
@@ -935,7 +935,7 @@ class LowLevelOpList(list):
00e57e7
                                                     v.concretetype))
00e57e7
         return v
00e57e7
 
00e57e7
-    def genop(self, opname, args_v, resulttype=None):
00e57e7
+    def genop(self, opname, args_v, resulttype=None, oploc=None):
00e57e7
         try:
00e57e7
             for v in args_v:
00e57e7
                 v.concretetype
169de85
@@ -944,7 +944,7 @@ class LowLevelOpList(list):
00e57e7
                                  " and pass its result to genop(),"
00e57e7
                                  " never hop.args_v directly.")
00e57e7
         vresult = Variable()
00e57e7
-        self.append(SpaceOperation(opname, args_v, vresult))
00e57e7
+        self.append(SpaceOperation(opname, args_v, vresult, oploc))
00e57e7
         if resulttype is None:
00e57e7
             vresult.concretetype = Void
00e57e7
             return None
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/translator/backendopt/inline.py.more-readable-c-code pypy-pypy-release-1.7/pypy/translator/backendopt/inline.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/translator/backendopt/inline.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/translator/backendopt/inline.py	2011-11-21 16:15:36.601466455 -0500
169de85
@@ -4,6 +4,7 @@ from pypy.translator.simplify import get
00e57e7
 from pypy.translator.unsimplify import copyvar
00e57e7
 from pypy.objspace.flow.model import Variable, Constant, Block, Link
00e57e7
 from pypy.objspace.flow.model import SpaceOperation, c_last_exception
00e57e7
+from pypy.objspace.flow.model import OperationLoc
00e57e7
 from pypy.objspace.flow.model import FunctionGraph
169de85
 from pypy.objspace.flow.model import mkentrymap, checkgraph
00e57e7
 from pypy.annotation import model as annmodel
169de85
@@ -231,6 +232,7 @@ class BaseInliner(object):
00e57e7
         self.varmap = {}
00e57e7
         self._copied_blocks = {}
00e57e7
         self.op = block.operations[index_operation]
00e57e7
+        self.callsite_oploc = self.op.oploc
00e57e7
         self.graph_to_inline = self.get_graph_from_op(self.op)
00e57e7
         self.exception_guarded = False
00e57e7
         if (block.exitswitch == c_last_exception and
169de85
@@ -290,7 +292,9 @@ class BaseInliner(object):
00e57e7
         
00e57e7
     def copy_operation(self, op):
00e57e7
         args = [self.get_new_name(arg) for arg in op.args]
00e57e7
-        result = SpaceOperation(op.opname, args, self.get_new_name(op.result))
00e57e7
+        new_oploc = OperationLoc(self.callsite_oploc.codelocs[:] + op.oploc.codelocs[:])
00e57e7
+        result = SpaceOperation(op.opname, args, self.get_new_name(op.result), 
00e57e7
+                                new_oploc)
00e57e7
         return result
00e57e7
 
00e57e7
     def copy_block(self, block):
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/translator/c/funcgen.py.more-readable-c-code pypy-pypy-release-1.7/pypy/translator/c/funcgen.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/translator/c/funcgen.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/translator/c/funcgen.py	2011-11-21 16:15:36.602466455 -0500
00e57e7
@@ -1,4 +1,6 @@
00e57e7
 import sys
00e57e7
+import inspect
00e57e7
+import dis
00e57e7
 from pypy.translator.c.support import USESLOTS # set to False if necessary while refactoring
00e57e7
 from pypy.translator.c.support import cdecl
00e57e7
 from pypy.translator.c.support import llvalue_from_constant, gen_assignments
169de85
@@ -22,6 +24,38 @@ LOCALVAR = 'l_%s'
00e57e7
 
00e57e7
 KEEP_INLINED_GRAPHS = False
00e57e7
 
00e57e7
+def block_comparator(blk0, blk1):
00e57e7
+    '''
00e57e7
+    Sort function for blocks, putting them in an ordering that attempts to
00e57e7
+    maximize readability of the generated C code
00e57e7
+    '''
00e57e7
+    # print 'comparing %r and %r' % (blk0, blk1)
00e57e7
+    # Put the start/end block at the top/bottom:
00e57e7
+    if blk0.isstartblock:
00e57e7
+        return -1
00e57e7
+
00e57e7
+    if blk1.isstartblock:
00e57e7
+        return 1
00e57e7
+
00e57e7
+    # Order blocks by the offset, where present:
00e57e7
+    if blk0.operations:
00e57e7
+        if blk1.operations:
00e57e7
+            return cmp(blk0.operations[0].oploc, blk1.operations[0].oploc)
00e57e7
+        else:
00e57e7
+            return -1
00e57e7
+    else:
00e57e7
+        if blk1.operations:
00e57e7
+            return 1
00e57e7
+        else:
00e57e7
+            return 0
00e57e7
+
00e57e7
+def escape_c_comments(py_src):
00e57e7
+    # Escape C comments within RPython source, to avoid generating bogus
00e57e7
+    # comments in our generated C source:
00e57e7
+    py_src = py_src.replace('/*', '')
00e57e7
+    py_src = py_src.replace('*/', '')
00e57e7
+    return py_src
00e57e7
+
00e57e7
 class FunctionCodeGenerator(object):
00e57e7
     """
00e57e7
     Collects information about a function which we have to generate
4ef3f10
@@ -207,14 +241,57 @@ class FunctionCodeGenerator(object):
00e57e7
 
00e57e7
     def cfunction_body(self):
00e57e7
         graph = self.graph
00e57e7
-        yield 'goto block0;'    # to avoid a warning "this label is not used"
00e57e7
+        # Try to print python source code:
00e57e7
+        if hasattr(graph, 'func'):
00e57e7
+            filename = inspect.getfile(graph.func)
00e57e7
+            #yield '/* name: %r */' % filename
00e57e7
+            try:
00e57e7
+                src, startline = inspect.getsourcelines(graph.func)
00e57e7
+            except IOError:
00e57e7
+                pass # No source found
00e57e7
+            except IndexError:
00e57e7
+                pass # Bulletproofing
00e57e7
+            else:
00e57e7
+                yield '/* Python source %r' % filename
00e57e7
+                for i, line in enumerate(src):
00e57e7
+                    line = line.rstrip()
00e57e7
+                    line = escape_c_comments(line)
00e57e7
+                    # FuncNode.funcgen_implementation treats lines ending in ':'
00e57e7
+                    # as C blocks, which messes up the formatting.
00e57e7
+                    # Work around this:
00e57e7
+                    if line.endswith(':'):
00e57e7
+                        line += ' '
00e57e7
+                    yield ' * %4d : %s' % (startline + i, line)
00e57e7
+                yield ' */'
00e57e7
+
00e57e7
+        label = graph.startblock.get_base_label(self.blocknum[graph.startblock])
00e57e7
+        yield 'goto %s;' % label # to avoid a warning "this label is not used"
00e57e7
+
00e57e7
+        # Sort the blocks into a (hopefully) readable order:
00e57e7
+        blocks = list(graph.iterblocks_by_source())
00e57e7
+        blocks.sort(block_comparator)
00e57e7
 
00e57e7
         # generate the body of each block
00e57e7
-        for block in graph.iterblocks():
00e57e7
+        for block in blocks:
00e57e7
+            cursrcloc = None
00e57e7
             myblocknum = self.blocknum[block]
00e57e7
             yield ''
00e57e7
-            yield 'block%d:' % myblocknum
00e57e7
+            yield '%s:' % block.get_base_label(myblocknum)
00e57e7
+            #yield "/* repr(block): %r */" % (block, )
00e57e7
+            #yield "/* type(block): %r */" % (type(block), )
00e57e7
             for i, op in enumerate(block.operations):
00e57e7
+                #yield "/* type(op): %r */" % (type(op), )
00e57e7
+                #yield "/* op.oploc: %s */" % (op.oploc, )
00e57e7
+                codeloc = op.oploc.codelocs[-1]
00e57e7
+                if codeloc:
00e57e7
+                    srcloc = codeloc.get_source_loc()
00e57e7
+                    if srcloc != cursrcloc:
00e57e7
+                        try:
00e57e7
+                            yield "/* %s:%d : %s */" % (codeloc.code.co_name, srcloc.linenum, escape_c_comments(srcloc.get_text()))
00e57e7
+                            cursrcloc = srcloc
00e57e7
+                        except IOError:
00e57e7
+                            pass
00e57e7
+
00e57e7
                 for line in self.gen_op(op):
00e57e7
                     yield line
00e57e7
             if len(block.exits) == 0:
4ef3f10
@@ -306,7 +383,7 @@ class FunctionCodeGenerator(object):
00e57e7
             assignments.append((a2typename, dest, src))
00e57e7
         for line in gen_assignments(assignments):
00e57e7
             yield line
00e57e7
-        label = 'block%d' % self.blocknum[link.target]
00e57e7
+        label = link.target.get_base_label(self.blocknum[link.target])
00e57e7
         if link.target in self.innerloops:
00e57e7
             loop = self.innerloops[link.target]
00e57e7
             if link is loop.links[-1]:   # link that ends a loop
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/translator/c/test/test_genc.py.more-readable-c-code pypy-pypy-release-1.7/pypy/translator/c/test/test_genc.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/translator/c/test/test_genc.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/translator/c/test/test_genc.py	2011-11-21 16:15:36.602466455 -0500
00e57e7
@@ -1,4 +1,5 @@
00e57e7
 import autopath, sys, os, py
00e57e7
+import re
00e57e7
 from pypy.rpython.lltypesystem.lltype import *
00e57e7
 from pypy.annotation import model as annmodel
00e57e7
 from pypy.translator.translator import TranslationContext
4ef3f10
@@ -532,3 +533,130 @@ def test_inhibit_tail_call():
00e57e7
     else:
00e57e7
         assert 0, "the call was not found in the C source"
00e57e7
     assert 'PYPY_INHIBIT_TAIL_CALL();' in lines[i+1]
00e57e7
+
00e57e7
+def get_generated_c_source(fn, types):
00e57e7
+    # Return a (optimized fn, c source code, c source filename) 3-tuple
00e57e7
+    t = Translation(fn)
00e57e7
+    t.annotate(types)
00e57e7
+    c_filename_path = t.source_c()
00e57e7
+    h = c_filename_path.open()
00e57e7
+    src = h.read()
00e57e7
+    h.close()
00e57e7
+    c_fn = t.compile_c()
00e57e7
+    return (c_fn, src, c_filename_path)
00e57e7
+
00e57e7
+def extract_c_function(c_src, fname):
00e57e7
+    # Extract the source for a given C function out of a the given src string
00e57e7
+    # Makes assumptions about the layout of the source
00e57e7
+    pattern = '^(.+) \**%s\(.*\) {$' % fname
00e57e7
+    within_fn = False
00e57e7
+    result = ''
00e57e7
+    for line in c_src.splitlines():
00e57e7
+        if within_fn:
00e57e7
+            result += line + '\n'
00e57e7
+            if line.startswith('}'):
00e57e7
+                return result
00e57e7
+        else:
00e57e7
+            m = re.match(pattern, line)
00e57e7
+            if m:
00e57e7
+                within_fn = True
00e57e7
+                result += line + '\n'
00e57e7
+    return result
00e57e7
+    
00e57e7
+    
00e57e7
+
00e57e7
+def test_generated_c_source():
00e57e7
+    # Verify that generate C source "looks good"
00e57e7
+    # We'll use is_perfect_number, as it contains a loop and a conditional
00e57e7
+
00e57e7
+    # Generate C source code
00e57e7
+    from pypy.translator.test.snippet import is_perfect_number
00e57e7
+    c_fn, c_src, c_filename_path = get_generated_c_source(is_perfect_number,
00e57e7
+                                                        [int])
00e57e7
+
00e57e7
+    # Locate the C source for the type-specialized function:
00e57e7
+    c_fn_src = extract_c_function(c_src, 'pypy_g_is_perfect_number')
00e57e7
+    
00e57e7
+    # Verify that the C source contains embedded comments containing the lines
00e57e7
+    # of the python source:
00e57e7
+    expected_comment_lines = [
00e57e7
+        '/* is_perfect_number:31 :     while div < n: */',
00e57e7
+        '/* is_perfect_number:32 :         if n % div == 0: */',
00e57e7
+        '/* is_perfect_number:33 :             sum += div */',
00e57e7
+        '/* is_perfect_number:34 :         div += 1 */',
00e57e7
+        '/* is_perfect_number:35 :     return n == sum */']
00e57e7
+    for exp_line in expected_comment_lines:
00e57e7
+        assert exp_line in c_fn_src
00e57e7
+        
00e57e7
+    # Verify that the lines occur in the correct order
00e57e7
+    # ...we do this by filtering the function's generated C source to just
00e57e7
+    # those lines containing our comments (and dropping whitespace):
00e57e7
+    lines = c_fn_src.splitlines()
00e57e7
+    lines = [line.strip()
00e57e7
+             for line in lines
00e57e7
+             if '/* is_perfect_number:' in line]
00e57e7
+
00e57e7
+    # ...we should now have exact equality: the ordering should be as expected,
00e57e7
+    # and each comment should appear exactly once:
00e57e7
+    assert lines == expected_comment_lines
00e57e7
+
00e57e7
+    # Ensure that the generated C function does the right thing:
00e57e7
+    assert c_fn(5) == False
00e57e7
+    assert c_fn(6) == True
00e57e7
+    assert c_fn(7) == False
00e57e7
+
00e57e7
+    assert c_fn(5.0) == False
00e57e7
+    assert c_fn(6.0) == True
00e57e7
+    assert c_fn(7.0) == False
00e57e7
+
00e57e7
+    assert c_fn(5L) == False
00e57e7
+    assert c_fn(6L) == True
00e57e7
+    assert c_fn(7L) == False
00e57e7
+
00e57e7
+    try:
00e57e7
+        c_fn('hello world')
00e57e7
+    except:
00e57e7
+        pass
00e57e7
+    else:
00e57e7
+        raise 'Was expected exception'
00e57e7
+    
00e57e7
+def test_escaping_c_comments():
00e57e7
+    # Ensure that c comments within RPython code get escaped when we generate
00e57e7
+    # our .c code (to avoid generating bogus C)
00e57e7
+    # See e.g. pypy.module.cpyext.dictobject's PyDict_Next, which has a
00e57e7
+    # docstring embedding a C comment
00e57e7
+    def c_style_comment(a, b):
00e57e7
+        '''Here is a C-style comment within an RPython docstring:
00e57e7
+                /* hello world */
00e57e7
+        '''
00e57e7
+        # and here's one in a string literal:
00e57e7
+        return '/* hello world a:%s b:%s */' % (a, b)
00e57e7
+
00e57e7
+    def cplusplus_style_comment(a, b):
00e57e7
+        '''Here is a C++-style comment within an RPython docstring:
00e57e7
+                // hello world
00e57e7
+        '''
00e57e7
+        # and here are some in string literals, and one as the floor division
00e57e7
+        # operator:
00e57e7
+        return '// hello world: a // b = %s' % (a // b)
00e57e7
+
00e57e7
+    for fn_name, exp_output in [('c_style_comment',
00e57e7
+                                 '/* hello world a:6 b:3 */'),
00e57e7
+                                ('cplusplus_style_comment',
00e57e7
+                                 '// hello world: a // b = 2')]:
00e57e7
+        fn = locals()[fn_name]
00e57e7
+
00e57e7
+        c_fn, c_src, c_filename_path = get_generated_c_source(fn, [int, int])
00e57e7
+        # If the above survived, then the C compiler managed to handle
00e57e7
+        # the generated C code
00e57e7
+
00e57e7
+        # Verify that the generated code works (i.e. that we didn't
00e57e7
+        # accidentally change the meaning):
00e57e7
+        assert c_fn(6, 3) == exp_output
00e57e7
+
00e57e7
+        # Ensure that at least part of the docstrings made it into the C
00e57e7
+        # code:
00e57e7
+        c_fn_src = extract_c_function(c_src, 'pypy_g_' + fn_name)
00e57e7
+        assert 'Here is a ' in c_fn_src
00e57e7
+        assert 'style comment within an RPython docstring' in c_fn_src
00e57e7
+        
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/translator/driver.py.more-readable-c-code pypy-pypy-release-1.7/pypy/translator/driver.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/translator/driver.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/translator/driver.py	2011-11-21 16:15:36.603466455 -0500
4ef3f10
@@ -535,6 +535,7 @@ class TranslationDriver(SimpleTaskEngine
00e57e7
             dstname = self.compute_exe_name() + '.staticdata.info'
00e57e7
             shutil.copy(str(fname), str(dstname))
00e57e7
             self.log.info('Static data info written to %s' % dstname)
00e57e7
+        return c_source_filename
00e57e7
 
00e57e7
     #
00e57e7
     task_source_c = taskdef(task_source_c, ['database_c'], "Generating c source")
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/translator/gensupp.py.more-readable-c-code pypy-pypy-release-1.7/pypy/translator/gensupp.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/translator/gensupp.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/translator/gensupp.py	2011-11-21 16:15:36.603466455 -0500
169de85
@@ -14,8 +14,8 @@ def ordered_blocks(graph):
169de85
     allblocks = []
169de85
     for block in graph.iterblocks():
00e57e7
             # first we order by offset in the code string
00e57e7
-            if block.operations:
00e57e7
-                ofs = block.operations[0].offset
00e57e7
+            if block.operations and block.operations[0].oploc.codelocs[0]:
00e57e7
+                ofs = block.operations[0].oploc.codelocs[0].offset
00e57e7
             else:
00e57e7
                 ofs = sys.maxint
00e57e7
             # then we order by input variable name or value
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/translator/interactive.py.more-readable-c-code pypy-pypy-release-1.7/pypy/translator/interactive.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/translator/interactive.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/translator/interactive.py	2011-11-21 16:15:36.604466454 -0500
169de85
@@ -138,7 +138,7 @@ class Translation(object):
00e57e7
     def source_c(self, argtypes=None, **kwds):
00e57e7
         self.update_options(argtypes, kwds)
00e57e7
         self.ensure_backend('c')
00e57e7
-        self.driver.source_c()
00e57e7
+        return self.driver.source_c()
00e57e7
 
00e57e7
     def source_cl(self, argtypes=None, **kwds):
00e57e7
         self.update_options(argtypes, kwds)
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/translator/llsupport/wrapper.py.more-readable-c-code pypy-pypy-release-1.7/pypy/translator/llsupport/wrapper.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/translator/llsupport/wrapper.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/translator/llsupport/wrapper.py	2011-11-21 16:15:36.604466454 -0500
169de85
@@ -59,6 +59,8 @@ def new_wrapper(func, translator, newnam
00e57e7
     # "return result"
00e57e7
     block = Block(wrapper_inputargs)
00e57e7
     wgraph = FunctionGraph('pyfn_' + (newname or func.func_name), block)
00e57e7
+    if hasattr(graph, 'func'):
00e57e7
+        wgraph.func = graph.func
00e57e7
     translator.update_call_graph(wgraph, graph, object())
00e57e7
     translator.graphs.append(wgraph)
00e57e7
     block.operations[:] = newops
4ef3f10
diff -up pypy-pypy-release-1.7/pypy/translator/simplify.py.more-readable-c-code pypy-pypy-release-1.7/pypy/translator/simplify.py
4ef3f10
--- pypy-pypy-release-1.7/pypy/translator/simplify.py.more-readable-c-code	2011-11-19 02:44:54.000000000 -0500
4ef3f10
+++ pypy-pypy-release-1.7/pypy/translator/simplify.py	2011-11-21 16:15:36.605466454 -0500
4ef3f10
@@ -292,7 +292,7 @@ def join_blocks(graph):
00e57e7
                 return renaming.get(v, v)
00e57e7
             def rename_op(op):
00e57e7
                 args = [rename(a) for a in op.args]
00e57e7
-                op = SpaceOperation(op.opname, args, rename(op.result), op.offset)
00e57e7
+                op = SpaceOperation(op.opname, args, rename(op.result), op.oploc)
00e57e7
                 # special case...
00e57e7
                 if op.opname == 'indirect_call':
00e57e7
                     if isinstance(op.args[0], Constant):