From 00e57e74f81587a2c49341341a7a291a54e22dc8 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Jan 06 2011 22:09:03 +0000 Subject: * Wed Jan 5 2011 David Malcolm - 1.4.1-4 - rebuild pypy using itself, for speed, with a boolean to break this cycle in the build-requirement graph (falling back to using "python-devel" aka CPython) - add work-in-progress patch to try to make generated c more readable (rhbz#666963) - capture the RPython source code files from the build within the debuginfo package (rhbz#666975) --- diff --git a/pypy-1.4.1-more-readable-c-code.patch b/pypy-1.4.1-more-readable-c-code.patch new file mode 100644 index 0000000..45fa534 --- /dev/null +++ b/pypy-1.4.1-more-readable-c-code.patch @@ -0,0 +1,695 @@ +diff -r cd083843b67a pypy/interpreter/pycode.py +--- a/pypy/interpreter/pycode.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/interpreter/pycode.py Wed Jan 05 16:14:35 2011 -0500 +@@ -14,6 +14,7 @@ + from pypy.interpreter.astcompiler.consts import (CO_OPTIMIZED, + CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS, CO_NESTED, + CO_GENERATOR, CO_CONTAINSGLOBALS) ++from pypy.interpreter.pytraceback import offset2lineno + from pypy.rlib.rarithmetic import intmask + from pypy.rlib.debug import make_sure_not_resized + from pypy.rlib import jit +@@ -81,6 +82,7 @@ + self.hidden_applevel = hidden_applevel + self.magic = magic + self._signature = cpython_code_signature(self) ++ self._cached_source = None + self._initialize() + + def _initialize(self): +@@ -403,3 +405,25 @@ + def repr(self, space): + return space.wrap(self.get_repr()) + repr.unwrap_spec = ['self', ObjSpace] ++ ++ def get_linenum_for_offset(self, offset): ++ # Given a bytecode offset, return a 1-based index into the lines of the ++ # source code ++ return offset2lineno(self, offset) ++ ++ def _ensure_source(self): ++ # Lazily grab the source lines into self._cached_source (or raise ++ # an IOError) ++ if not self._cached_source: ++ f = open(self.co_filename, 'r') ++ source = [line.rstrip() for line in f.readlines()] ++ f.close() ++ self._cached_source = source ++ ++ def get_source_text(self, linenum): ++ # Given a 1-based index, get the corresponding line of source code (or ++ # raise an IOError) ++ self._ensure_source() ++ return self._cached_source[linenum - 1] ++ ++ +diff -r cd083843b67a pypy/objspace/flow/model.py +--- a/pypy/objspace/flow/model.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/objspace/flow/model.py Wed Jan 05 16:14:35 2011 -0500 +@@ -31,6 +31,120 @@ + + __metaclass__ = type + ++class SourceLoc(object): ++ # A srcloc is a specific location within the RPython source code, ++ # intended for human display ++ __slots__ = ('code', # code object ++ 'linenum' # 1-based index, as displayed to a user ++ ) ++ def __init__(self, code, linenum): ++ self.code = code ++ self.linenum = linenum ++ ++ def get_text(self): ++ # Get the actual source text of this line ++ return self.code.get_source_text(self.linenum) ++ ++ def __eq__(self, other): ++ return self.code == other.code and self.linenum == other.linenum ++ ++ def __ne__(self, other): ++ if other: ++ return self.code != other.code or self.linenum != other.linenum ++ else: ++ return True ++ ++class CodeLoc(object): ++ # A codeloc is a specific location within the RPython bytecode ++ __slots__ = ('code', # code object ++ 'offset' # int index into bytecode, or -1 ++ ) ++ ++ def __init__(self, code, offset): ++ self.code = code ++ self.offset = offset ++ ++ def __str__(self): ++ if self.offset >= 0: ++ return "%s@%d" % (self.code.co_name, self.offset) ++ else: ++ return "" ++ ++ def __ne__(self, other): ++ if other: ++ return self.code != other.code or self.offset != other.offset ++ else: ++ return True ++ ++ def __cmp__(self, other): ++ # Partial ordering, for those locations that have an offset: ++ if other: ++ if self.offset >= 0 and other.offset >= 0: ++ return self.offset - other.offset ++ return 0 ++ ++ def get_source_loc(self): ++ # Convert to a SourceLoc: ++ return SourceLoc(self.code, self.code.get_linenum_for_offset(self.offset)) ++ ++class OperationLoc(object): ++ # An oploc is the location within the RPython source code of a given ++ # operation ++ # ++ # This is a list consisting of CodeLoc instances, some of which may be None ++ # ++ # For the simple case, this is list of length 1 with a single CodeLoc ++ # ++ # For an operation inside an inlined callsite, we have a list of length 2: ++ # [codeloc of callsite, ++ # codeloc of operation within inlined body] ++ # ++ # For more interesting inlined cases, we have a chain of source locations: ++ # [codeloc of callsite, ++ # codeloc of inner callsite, ++ # ... , ++ # codeloc of innermost inlined callsite, ++ # codeloc of operation within inlined body] ++ # ++ ++ __slots__ = ('codelocs', ) ++ ++ def __init__(self, codelocs): ++ self.codelocs = codelocs ++ ++ def __str__(self): ++ return '[' + ' > '.join(str(codeloc) for codeloc in self.codelocs) + ']' ++ ++ def __cmp__(self, other): ++ return cmp(self.codelocs, other.codelocs) ++ ++def block_comparator(blk0, blk1): ++ ''' ++ Sort function for blocks, putting them in an ordering that attempts to ++ maximize readability of the generated C code ++ ''' ++ # print 'comparing %r and %r' % (blk0, blk1) ++ # Put the start/end block at the top/bottom: ++ if blk0.isstartblock: ++ return -1 ++ ++ if blk1.isstartblock: ++ return 1 ++ ++ # Order blocks by the offset, where present: ++ if blk0.operations: ++ if blk1.operations: ++ return cmp(blk0.operations[0].oploc, blk1.operations[0].oploc) ++ else: ++ return -1 ++ else: ++ if blk1.operations: ++ return 1 ++ else: ++ return 0 ++ ++def edge_comparator(edge0, edge1): ++ return block_comparator(edge0.target, edge1.target) + + class FunctionGraph(object): + __slots__ = ['startblock', 'returnblock', 'exceptblock', '__dict__'] +@@ -94,6 +208,21 @@ + seen[block] = True + stack += block.exits[::-1] + ++ def iterblocks_by_source(self): ++ # Try to preserve logical source ordering in the blocks ++ block = self.startblock ++ yield block ++ seen = {block: True} ++ stack = list(block.exits[::-1]) ++ stack.sort(edge_comparator) ++ while stack: ++ block = stack.pop().target ++ if block not in seen: ++ yield block ++ seen[block] = True ++ stack += block.exits[::-1] ++ stack.sort(edge_comparator) ++ + def iterlinks(self): + block = self.startblock + seen = {block: True} +@@ -183,14 +312,14 @@ + self.exits = [] # list of Link(s) + + def at(self): +- if self.operations and self.operations[0].offset >= 0: +- return "@%d" % self.operations[0].offset ++ if self.operations: ++ return str(self.operations[0].oploc) + else: + return "" + + def __str__(self): + if self.operations: +- txt = "block@%d" % self.operations[0].offset ++ txt = "block%s" % self.operations[0].oploc + else: + if (not self.exits) and len(self.inputargs) == 1: + txt = "return block" +@@ -245,6 +374,21 @@ + from pypy.translator.tool.graphpage import try_show + try_show(self) + ++ def isreturnblock(self): ++ return (not self.operations) and (not self.exits) and len(self.inputargs) == 1 ++ ++ def get_base_label(self, blocknum): ++ # Generate a more friendly C label for this block ++ if self.operations: ++ txt = "block" ++ elif (not self.exits) and len(self.inputargs) == 1: ++ txt = "return_block" ++ elif (not self.exits) and len(self.inputargs) == 2: ++ txt = "raise_block" ++ else: ++ txt = "codeless_block" ++ return '%s%d' % (txt, blocknum) ++ + + class Variable(object): + __slots__ = ["_name", "_nr", "concretetype"] +@@ -331,13 +475,15 @@ + + + class SpaceOperation(object): +- __slots__ = "opname args result offset".split() ++ __slots__ = "opname args result oploc".split() + +- def __init__(self, opname, args, result, offset=-1): ++ def __init__(self, opname, args, result, oploc=None): + self.opname = intern(opname) # operation name + self.args = list(args) # mixed list of var/const + self.result = result # either Variable or Constant instance +- self.offset = offset # offset in code string ++ if oploc is None: ++ oploc = OperationLoc([None]) ++ self.oploc = oploc + + def __eq__(self, other): + return (self.__class__ is other.__class__ and +@@ -352,8 +498,9 @@ + return hash((self.opname,tuple(self.args),self.result)) + + def __repr__(self): +- return "%r = %s(%s)" % (self.result, self.opname, +- ", ".join(map(repr, self.args))) ++ return "%r = %s(%s) (%s)" % (self.result, self.opname, ++ ", ".join(map(repr, self.args)), ++ self.oploc) + + class Atom(object): + def __init__(self, name): +@@ -448,8 +595,7 @@ + for op in oplist: + copyop = SpaceOperation(op.opname, + [copyvar(v) for v in op.args], +- copyvar(op.result), op.offset) +- #copyop.offset = op.offset ++ copyvar(op.result), op.oploc) + result.append(copyop) + return result + newblock.operations = copyoplist(block.operations) +diff -r cd083843b67a pypy/objspace/flow/objspace.py +--- a/pypy/objspace/flow/objspace.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/objspace/flow/objspace.py Wed Jan 05 16:14:35 2011 -0500 +@@ -310,7 +310,9 @@ + def do_operation(self, name, *args_w): + spaceop = SpaceOperation(name, args_w, Variable()) + if hasattr(self, 'executioncontext'): # not here during bootstrapping +- spaceop.offset = self.executioncontext.crnt_offset ++ codeloc = CodeLoc(self.executioncontext.code, ++ self.executioncontext.crnt_offset) ++ spaceop.oploc = OperationLoc([codeloc]) + self.executioncontext.recorder.append(spaceop) + return spaceop.result + +diff -r cd083843b67a pypy/objspace/flow/test/test_model.py +--- a/pypy/objspace/flow/test/test_model.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/objspace/flow/test/test_model.py Wed Jan 05 16:14:35 2011 -0500 +@@ -132,3 +132,25 @@ + assert v2.renamed + assert v2.name.startswith("foobar_") and v2.name != v.name + assert v2.name.split('_', 1)[1].isdigit() ++ ++def test_source_locations(): ++ # Invent some random offsets into the code: ++ co = sample_function.__code__ ++ codelocA = CodeLoc(co, 42) ++ codelocB = CodeLoc(co, 87) ++ ++ assert str(codelocA) == 'sample_function@42' ++ assert str(codelocB) == 'sample_function@87' ++ ++ assert cmp(codelocA, codelocB) < 0 ++ assert cmp(codelocB, codelocA) > 0 ++ ++ oplocA = OperationLoc([codelocA]) ++ oplocB = OperationLoc([codelocB]) ++ ++ assert str(oplocA) == '[sample_function@42]' ++ assert str(oplocB) == '[sample_function@87]' ++ ++ assert cmp(oplocA, oplocB) < 0 ++ assert cmp(oplocB, oplocA) > 0 ++ +diff -r cd083843b67a pypy/rpython/rtyper.py +--- a/pypy/rpython/rtyper.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/rpython/rtyper.py Wed Jan 05 16:14:35 2011 -0500 +@@ -800,7 +800,7 @@ + return vars + + def genop(self, opname, args_v, resulttype=None): +- return self.llops.genop(opname, args_v, resulttype) ++ return self.llops.genop(opname, args_v, resulttype, self.spaceop.oploc) + + def gendirectcall(self, ll_function, *args_v): + return self.llops.gendirectcall(ll_function, *args_v) +@@ -935,7 +935,7 @@ + v.concretetype)) + return v + +- def genop(self, opname, args_v, resulttype=None): ++ def genop(self, opname, args_v, resulttype=None, oploc=None): + try: + for v in args_v: + v.concretetype +@@ -944,7 +944,7 @@ + " and pass its result to genop()," + " never hop.args_v directly.") + vresult = Variable() +- self.append(SpaceOperation(opname, args_v, vresult)) ++ self.append(SpaceOperation(opname, args_v, vresult, oploc)) + if resulttype is None: + vresult.concretetype = Void + return None +diff -r cd083843b67a pypy/translator/backendopt/inline.py +--- a/pypy/translator/backendopt/inline.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/translator/backendopt/inline.py Wed Jan 05 16:14:35 2011 -0500 +@@ -4,6 +4,7 @@ + from pypy.translator.unsimplify import copyvar + from pypy.objspace.flow.model import Variable, Constant, Block, Link + from pypy.objspace.flow.model import SpaceOperation, c_last_exception ++from pypy.objspace.flow.model import OperationLoc + from pypy.objspace.flow.model import FunctionGraph + from pypy.objspace.flow.model import traverse, mkentrymap, checkgraph + from pypy.annotation import model as annmodel +@@ -231,6 +232,7 @@ + self.varmap = {} + self._copied_blocks = {} + self.op = block.operations[index_operation] ++ self.callsite_oploc = self.op.oploc + self.graph_to_inline = self.get_graph_from_op(self.op) + self.exception_guarded = False + if (block.exitswitch == c_last_exception and +@@ -297,7 +299,9 @@ + + def copy_operation(self, op): + args = [self.get_new_name(arg) for arg in op.args] +- result = SpaceOperation(op.opname, args, self.get_new_name(op.result)) ++ new_oploc = OperationLoc(self.callsite_oploc.codelocs[:] + op.oploc.codelocs[:]) ++ result = SpaceOperation(op.opname, args, self.get_new_name(op.result), ++ new_oploc) + return result + + def copy_block(self, block): +diff -r cd083843b67a pypy/translator/c/funcgen.py +--- a/pypy/translator/c/funcgen.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/translator/c/funcgen.py Wed Jan 05 16:14:35 2011 -0500 +@@ -1,4 +1,6 @@ + import sys ++import inspect ++import dis + from pypy.translator.c.support import USESLOTS # set to False if necessary while refactoring + from pypy.translator.c.support import cdecl + from pypy.translator.c.support import llvalue_from_constant, gen_assignments +@@ -22,6 +24,38 @@ + + KEEP_INLINED_GRAPHS = False + ++def block_comparator(blk0, blk1): ++ ''' ++ Sort function for blocks, putting them in an ordering that attempts to ++ maximize readability of the generated C code ++ ''' ++ # print 'comparing %r and %r' % (blk0, blk1) ++ # Put the start/end block at the top/bottom: ++ if blk0.isstartblock: ++ return -1 ++ ++ if blk1.isstartblock: ++ return 1 ++ ++ # Order blocks by the offset, where present: ++ if blk0.operations: ++ if blk1.operations: ++ return cmp(blk0.operations[0].oploc, blk1.operations[0].oploc) ++ else: ++ return -1 ++ else: ++ if blk1.operations: ++ return 1 ++ else: ++ return 0 ++ ++def escape_c_comments(py_src): ++ # Escape C comments within RPython source, to avoid generating bogus ++ # comments in our generated C source: ++ py_src = py_src.replace('/*', '') ++ py_src = py_src.replace('*/', '') ++ return py_src ++ + class FunctionCodeGenerator(object): + """ + Collects information about a function which we have to generate +@@ -210,14 +244,57 @@ + + def cfunction_body(self): + graph = self.graph +- yield 'goto block0;' # to avoid a warning "this label is not used" ++ # Try to print python source code: ++ if hasattr(graph, 'func'): ++ filename = inspect.getfile(graph.func) ++ #yield '/* name: %r */' % filename ++ try: ++ src, startline = inspect.getsourcelines(graph.func) ++ except IOError: ++ pass # No source found ++ except IndexError: ++ pass # Bulletproofing ++ else: ++ yield '/* Python source %r' % filename ++ for i, line in enumerate(src): ++ line = line.rstrip() ++ line = escape_c_comments(line) ++ # FuncNode.funcgen_implementation treats lines ending in ':' ++ # as C blocks, which messes up the formatting. ++ # Work around this: ++ if line.endswith(':'): ++ line += ' ' ++ yield ' * %4d : %s' % (startline + i, line) ++ yield ' */' ++ ++ label = graph.startblock.get_base_label(self.blocknum[graph.startblock]) ++ yield 'goto %s;' % label # to avoid a warning "this label is not used" ++ ++ # Sort the blocks into a (hopefully) readable order: ++ blocks = list(graph.iterblocks_by_source()) ++ blocks.sort(block_comparator) + + # generate the body of each block +- for block in graph.iterblocks(): ++ for block in blocks: ++ cursrcloc = None + myblocknum = self.blocknum[block] + yield '' +- yield 'block%d:' % myblocknum ++ yield '%s:' % block.get_base_label(myblocknum) ++ #yield "/* repr(block): %r */" % (block, ) ++ #yield "/* type(block): %r */" % (type(block), ) + for i, op in enumerate(block.operations): ++ #yield "/* type(op): %r */" % (type(op), ) ++ #yield "/* op.oploc: %s */" % (op.oploc, ) ++ codeloc = op.oploc.codelocs[-1] ++ if codeloc: ++ srcloc = codeloc.get_source_loc() ++ if srcloc != cursrcloc: ++ try: ++ yield "/* %s:%d : %s */" % (codeloc.code.co_name, srcloc.linenum, escape_c_comments(srcloc.get_text())) ++ cursrcloc = srcloc ++ except IOError: ++ pass ++ + for line in self.gen_op(op): + yield line + if len(block.exits) == 0: +@@ -310,7 +387,7 @@ + assignments.append((a2typename, dest, src)) + for line in gen_assignments(assignments): + yield line +- label = 'block%d' % self.blocknum[link.target] ++ label = link.target.get_base_label(self.blocknum[link.target]) + if link.target in self.innerloops: + loop = self.innerloops[link.target] + if link is loop.links[-1]: # link that ends a loop +diff -r cd083843b67a pypy/translator/c/test/test_genc.py +--- a/pypy/translator/c/test/test_genc.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/translator/c/test/test_genc.py Wed Jan 05 16:14:35 2011 -0500 +@@ -1,4 +1,5 @@ + import autopath, sys, os, py ++import re + from pypy.rpython.lltypesystem.lltype import * + from pypy.annotation import model as annmodel + from pypy.translator.translator import TranslationContext +@@ -498,3 +499,130 @@ + 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 a (optimized fn, c source code, c source filename) 3-tuple ++ t = Translation(fn) ++ t.annotate(types) ++ c_filename_path = t.source_c() ++ h = c_filename_path.open() ++ src = h.read() ++ h.close() ++ c_fn = t.compile_c() ++ return (c_fn, src, c_filename_path) ++ ++def extract_c_function(c_src, fname): ++ # Extract the source for a given C function out of a the given src string ++ # Makes assumptions about the layout of the source ++ pattern = '^(.+) \**%s\(.*\) {$' % fname ++ within_fn = False ++ result = '' ++ for line in c_src.splitlines(): ++ if within_fn: ++ result += line + '\n' ++ if line.startswith('}'): ++ return result ++ else: ++ m = re.match(pattern, line) ++ if m: ++ within_fn = True ++ result += line + '\n' ++ return result ++ ++ ++ ++def test_generated_c_source(): ++ # Verify that generate C source "looks good" ++ # We'll use is_perfect_number, as it contains a loop and a conditional ++ ++ # Generate C source code ++ from pypy.translator.test.snippet import is_perfect_number ++ c_fn, c_src, c_filename_path = get_generated_c_source(is_perfect_number, ++ [int]) ++ ++ # Locate the C source for the type-specialized function: ++ c_fn_src = extract_c_function(c_src, 'pypy_g_is_perfect_number') ++ ++ # Verify that the C source contains embedded comments containing the lines ++ # of the python source: ++ expected_comment_lines = [ ++ '/* is_perfect_number:31 : while div < n: */', ++ '/* is_perfect_number:32 : if n % div == 0: */', ++ '/* is_perfect_number:33 : sum += div */', ++ '/* is_perfect_number:34 : div += 1 */', ++ '/* is_perfect_number:35 : return n == sum */'] ++ for exp_line in expected_comment_lines: ++ assert exp_line in c_fn_src ++ ++ # Verify that the lines occur in the correct order ++ # ...we do this by filtering the function's generated C source to just ++ # those lines containing our comments (and dropping whitespace): ++ lines = c_fn_src.splitlines() ++ lines = [line.strip() ++ for line in lines ++ if '/* is_perfect_number:' in line] ++ ++ # ...we should now have exact equality: the ordering should be as expected, ++ # and each comment should appear exactly once: ++ assert lines == expected_comment_lines ++ ++ # Ensure that the generated C function does the right thing: ++ assert c_fn(5) == False ++ assert c_fn(6) == True ++ assert c_fn(7) == False ++ ++ assert c_fn(5.0) == False ++ assert c_fn(6.0) == True ++ assert c_fn(7.0) == False ++ ++ assert c_fn(5L) == False ++ assert c_fn(6L) == True ++ assert c_fn(7L) == False ++ ++ try: ++ c_fn('hello world') ++ except: ++ pass ++ else: ++ raise 'Was expected exception' ++ ++def test_escaping_c_comments(): ++ # Ensure that c comments within RPython code get escaped when we generate ++ # our .c code (to avoid generating bogus C) ++ # See e.g. pypy.module.cpyext.dictobject's PyDict_Next, which has a ++ # docstring embedding a C comment ++ def c_style_comment(a, b): ++ '''Here is a C-style comment within an RPython docstring: ++ /* hello world */ ++ ''' ++ # and here's one in a string literal: ++ return '/* hello world a:%s b:%s */' % (a, b) ++ ++ def cplusplus_style_comment(a, b): ++ '''Here is a C++-style comment within an RPython docstring: ++ // hello world ++ ''' ++ # and here are some in string literals, and one as the floor division ++ # operator: ++ return '// hello world: a // b = %s' % (a // b) ++ ++ for fn_name, exp_output in [('c_style_comment', ++ '/* hello world a:6 b:3 */'), ++ ('cplusplus_style_comment', ++ '// hello world: a // b = 2')]: ++ fn = locals()[fn_name] ++ ++ c_fn, c_src, c_filename_path = get_generated_c_source(fn, [int, int]) ++ # If the above survived, then the C compiler managed to handle ++ # the generated C code ++ ++ # Verify that the generated code works (i.e. that we didn't ++ # accidentally change the meaning): ++ assert c_fn(6, 3) == exp_output ++ ++ # Ensure that at least part of the docstrings made it into the C ++ # code: ++ c_fn_src = extract_c_function(c_src, 'pypy_g_' + fn_name) ++ assert 'Here is a ' in c_fn_src ++ assert 'style comment within an RPython docstring' in c_fn_src ++ +diff -r cd083843b67a pypy/translator/driver.py +--- a/pypy/translator/driver.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/translator/driver.py Wed Jan 05 16:14:35 2011 -0500 +@@ -539,6 +539,7 @@ + dstname = self.compute_exe_name() + '.staticdata.info' + shutil.copy(str(fname), str(dstname)) + self.log.info('Static data info written to %s' % dstname) ++ return c_source_filename + + # + task_source_c = taskdef(task_source_c, ['database_c'], "Generating c source") +diff -r cd083843b67a pypy/translator/gensupp.py +--- a/pypy/translator/gensupp.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/translator/gensupp.py Wed Jan 05 16:14:35 2011 -0500 +@@ -16,8 +16,8 @@ + def visit(block): + if isinstance(block, Block): + # first we order by offset in the code string +- if block.operations: +- ofs = block.operations[0].offset ++ if block.operations and block.operations[0].oploc.codelocs[0]: ++ ofs = block.operations[0].oploc.codelocs[0].offset + else: + ofs = sys.maxint + # then we order by input variable name or value +diff -r cd083843b67a pypy/translator/interactive.py +--- a/pypy/translator/interactive.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/translator/interactive.py Wed Jan 05 16:14:35 2011 -0500 +@@ -138,7 +138,7 @@ + def source_c(self, argtypes=None, **kwds): + self.update_options(argtypes, kwds) + self.ensure_backend('c') +- self.driver.source_c() ++ return self.driver.source_c() + + def source_cl(self, argtypes=None, **kwds): + self.update_options(argtypes, kwds) +diff -r cd083843b67a pypy/translator/llsupport/wrapper.py +--- a/pypy/translator/llsupport/wrapper.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/translator/llsupport/wrapper.py Wed Jan 05 16:14:35 2011 -0500 +@@ -59,6 +59,8 @@ + # "return result" + block = Block(wrapper_inputargs) + wgraph = FunctionGraph('pyfn_' + (newname or func.func_name), block) ++ if hasattr(graph, 'func'): ++ wgraph.func = graph.func + translator.update_call_graph(wgraph, graph, object()) + translator.graphs.append(wgraph) + block.operations[:] = newops +diff -r cd083843b67a pypy/translator/simplify.py +--- a/pypy/translator/simplify.py Mon Dec 20 17:17:45 2010 +0100 ++++ b/pypy/translator/simplify.py Wed Jan 05 16:14:35 2011 -0500 +@@ -294,7 +294,7 @@ + return renaming.get(v, v) + def rename_op(op): + args = [rename(a) for a in op.args] +- op = SpaceOperation(op.opname, args, rename(op.result), op.offset) ++ op = SpaceOperation(op.opname, args, rename(op.result), op.oploc) + # special case... + if op.opname == 'indirect_call': + if isinstance(op.args[0], Constant): diff --git a/pypy.spec b/pypy.spec index 96354e2..162ed5f 100644 --- a/pypy.spec +++ b/pypy.spec @@ -1,6 +1,6 @@ Name: pypy Version: 1.4.1 -Release: 3%{?dist} +Release: 4%{?dist} Summary: Python implementation with a Just-In-Time compiler Group: Development/Languages @@ -136,9 +136,33 @@ Patch2: fix-test_commands-expected-ls-output-issue7108.patch # https://codespeak.net/issue/pypy-dev/issue614 Patch3: pypy-1.4.1-add-LIBRARY_INSTALLATION_PATH.patch +# Try to improve the readability of the generated .c code, by adding in the +# RPython source as comments where possible. +# A version of this was sent upstream as: +# http://codespeak.net/pipermail/pypy-dev/2010q4/006532.html +# TODO: get this into the upstream bug tracker, and finish inlining +# support (rhbz#666963) +Patch4: pypy-1.4.1-more-readable-c-code.patch + + # Build-time requirements: -BuildRequires: python-devel +# pypy's can be rebuilt using itself, rather than with CPython; doing so +# halves the build time. +# +# Turn it off with this boolean, to revert back to rebuilding using CPython +# and avoid a cycle in the build-time dependency graph: +# +%global use_self_when_building 1 +%if 0%{use_self_when_building} +BuildRequires: pypy +%global bootstrap_python_interp pypy +%else +BuildRequires: python-devel +%global bootstrap_python_interp python +%endif + + # FIXME: I'm seeing errors like this in the logs: # [translation:WARNING] The module '_rawffi' is disabled @@ -215,6 +239,8 @@ sed -i \ -e 's|LIBRARY_INSTALLATION_PATH|"%{pypyprefix}"|' \ pypy/translator/goal/app_main.py +%patch4 -p1 -b .more-readable-c-code + # Replace /usr/local/bin/python shebangs with /usr/bin/python: find -name "*.py" -exec \ @@ -304,6 +330,7 @@ BuildPyPy() { # doesn't interract well with the results of using our standard build flags. # For now, filter our CFLAGS of everything that could be conflicting with # pypy. Need to check these and reenable ones that are okay later. + # Filed as https://bugzilla.redhat.com/show_bug.cgi?id=666966 export CFLAGS=$(echo "$RPM_OPT_FLAGS" | sed -e 's/-Wp,-D_FORTIFY_SOURCE=2//' -e 's/-fexceptions//' -e 's/-fstack-protector//' -e 's/--param=ssp-buffer-size=4//' -e 's/-O2//' -e 's/-fasynchronous-unwind-tables//' -e 's/-march=i686//' -e 's/-mtune=atom//') # If we're already built the JIT-enabled "pypy", then use it for subsequent @@ -311,7 +338,10 @@ BuildPyPy() { if test -x './pypy' ; then INTERP='./pypy' else - INTERP='python' + # First pypy build within this rpm build? + # Fall back to using the bootstrap python interpreter, which might be a + # system copy of pypy from an earlier rpm, or be cpython's /usr/bin/python: + INTERP='%{bootstrap_python_interp}' fi # Here's where we actually invoke the build: @@ -484,6 +514,34 @@ mkdir -p %{buildroot}/%{pypyprefix}/site-packages %{buildroot}/%{_bindir}/pypy \ 0 +# Capture the RPython source code files from the build within the debuginfo +# package (rhbz#666975) +%global pypy_debuginfo_dir /usr/src/debug/pypy-%{version}-src +mkdir -p %{buildroot}%{pypy_debuginfo_dir} + +# copy over everything: +cp -a pypy %{buildroot}%{pypy_debuginfo_dir} + +# ...then delete files that aren't .py files: +find \ + %{buildroot}%{pypy_debuginfo_dir} \ + \( -type f \ + -a \ + \! -name "*.py" \ + \) \ + -delete + +# We don't need bytecode for these files; they are being included for reference +# purposes. +# There are some rpmlint warnings from these files: +# non-executable-script +# wrong-script-interpreter +# zero-length +# script-without-shebang +# dangling-symlink +# but given that the objective is to preserve a copy of the source code, those +# are acceptable. + %check topdir=$(pwd) @@ -511,6 +569,7 @@ CheckPyPy() { # Gather a list of tests to skip, due to known failures # TODO: report these failures to pypy upstream + # See also rhbz#666967 and rhbz#666969 TESTS_TO_SKIP="" # Test failures relating to missing codecs @@ -622,6 +681,14 @@ CheckPyPy() { # AssertionError: ValueError not raised SkipTest test_zlib + %if 0%{use_self_when_building} + # Patch 3 prioritizes the installed copy of pypy's libraries over the + # build copy. + # This leads to test failures of test_pep263 and test_tarfile + # For now, suppress these when building using pypy itself: + SkipTest test_pep263 # on-disk encoding issues + SkipTest test_tarfile # permissions issues + %endif # Run the built binary through the selftests: time ./$ExeName -m test.regrtest -x $TESTS_TO_SKIP @@ -696,6 +763,14 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Wed Jan 5 2011 David Malcolm - 1.4.1-4 +- rebuild pypy using itself, for speed, with a boolean to break this cycle in +the build-requirement graph (falling back to using "python-devel" aka CPython) +- add work-in-progress patch to try to make generated c more readable +(rhbz#666963) +- capture the RPython source code files from the build within the debuginfo +package (rhbz#666975) + * Wed Dec 22 2010 David Malcolm - 1.4.1-3 - try to respect the FHS by installing libraries below libdir, rather than datadir; patch app_main.py to look in this installation location first when