[pypy-commit] pypy default: merge heads
arigo
noreply at buildbot.pypy.org
Tue Oct 16 16:15:23 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r58138:b9b11f265aff
Date: 2012-10-16 16:14 +0200
http://bitbucket.org/pypy/pypy/changeset/b9b11f265aff/
Log: merge heads
diff too long, truncating to 2000 out of 14821 lines
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -661,7 +661,7 @@
raise operationerrfmt(space.w_ValueError, "Unknown order: %s",
order)
if isinstance(w_object, W_NDimArray):
- if (not space.is_w(w_dtype, space.w_None) and
+ if (not space.is_none(w_dtype) and
w_object.get_dtype() is not w_dtype):
raise OperationError(space.w_NotImplementedError, space.wrap(
"copying over different dtypes unsupported"))
diff --git a/pypy/module/micronumpy/loop.py b/pypy/module/micronumpy/loop.py
--- a/pypy/module/micronumpy/loop.py
+++ b/pypy/module/micronumpy/loop.py
@@ -424,39 +424,44 @@
self._done = True
@jit.unroll_safe
- def get_index(self, space):
- return [space.wrap(i) for i in self.indexes]
+ def get_index(self, space, shapelen):
+ return [space.wrap(self.indexes[i]) for i in range(shapelen)]
getitem_int_driver = jit.JitDriver(name = 'numpy_getitem_int',
- greens = ['shapelen', 'indexlen', 'dtype'],
+ greens = ['shapelen', 'indexlen',
+ 'prefixlen', 'dtype'],
reds = ['arr', 'res', 'iter', 'indexes_w',
'prefix_w'])
def getitem_array_int(space, arr, res, iter_shape, indexes_w, prefix_w):
shapelen = len(iter_shape)
+ prefixlen = len(prefix_w)
indexlen = len(indexes_w)
dtype = arr.get_dtype()
iter = PureShapeIterator(iter_shape, indexes_w)
+ indexlen = len(indexes_w)
while not iter.done():
getitem_int_driver.jit_merge_point(shapelen=shapelen, indexlen=indexlen,
dtype=dtype, arr=arr, res=res,
iter=iter, indexes_w=indexes_w,
- prefix_w=prefix_w)
+ prefix_w=prefix_w,
+ prefixlen=prefixlen)
# prepare the index
- index_w = [None] * len(indexes_w)
- for i in range(len(indexes_w)):
+ index_w = [None] * indexlen
+ for i in range(indexlen):
if iter.idx_w[i] is not None:
index_w[i] = iter.idx_w[i].getitem()
else:
index_w[i] = indexes_w[i]
- res.descr_setitem(space, space.newtuple(prefix_w +
- iter.get_index(space)),
+ res.descr_setitem(space, space.newtuple(prefix_w[:prefixlen] +
+ iter.get_index(space, shapelen)),
arr.descr_getitem(space, space.newtuple(index_w)))
iter.next()
return res
setitem_int_driver = jit.JitDriver(name = 'numpy_setitem_int',
- greens = ['shapelen', 'indexlen', 'dtype'],
+ greens = ['shapelen', 'indexlen',
+ 'prefixlen', 'dtype'],
reds = ['arr', 'iter', 'indexes_w',
'prefix_w', 'val_arr'])
@@ -464,21 +469,24 @@
prefix_w):
shapelen = len(iter_shape)
indexlen = len(indexes_w)
+ prefixlen = len(prefix_w)
dtype = arr.get_dtype()
iter = PureShapeIterator(iter_shape, indexes_w)
while not iter.done():
setitem_int_driver.jit_merge_point(shapelen=shapelen, indexlen=indexlen,
dtype=dtype, arr=arr,
iter=iter, indexes_w=indexes_w,
- prefix_w=prefix_w, val_arr=val_arr)
+ prefix_w=prefix_w, val_arr=val_arr,
+ prefixlen=prefixlen)
# prepare the index
- index_w = [None] * len(indexes_w)
- for i in range(len(indexes_w)):
+ index_w = [None] * indexlen
+ for i in range(indexlen):
if iter.idx_w[i] is not None:
index_w[i] = iter.idx_w[i].getitem()
else:
index_w[i] = indexes_w[i]
- w_idx = space.newtuple(prefix_w + iter.get_index(space))
+ w_idx = space.newtuple(prefix_w[:prefixlen] + iter.get_index(space,
+ shapelen))
arr.descr_setitem(space, space.newtuple(index_w),
val_arr.descr_getitem(space, w_idx))
iter.next()
diff --git a/pypy/module/oracle/interp_cursor.py b/pypy/module/oracle/interp_cursor.py
--- a/pypy/module/oracle/interp_cursor.py
+++ b/pypy/module/oracle/interp_cursor.py
@@ -1,5 +1,4 @@
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.gateway import NoneNotWrapped
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
from pypy.interpreter.gateway import interp2app, unwrap_spec
diff --git a/pypy/objspace/flow/bytecode.py b/pypy/objspace/flow/bytecode.py
--- a/pypy/objspace/flow/bytecode.py
+++ b/pypy/objspace/flow/bytecode.py
@@ -1,24 +1,22 @@
"""
Bytecode handling classes and functions for use by the flow space.
"""
-from pypy.interpreter.pycode import (PyCode, BytecodeCorruption, cpython_magic,
+from pypy.interpreter.pycode import (BytecodeCorruption,
cpython_code_signature)
from pypy.tool.stdlib_opcode import (host_bytecode_spec, EXTENDED_ARG,
HAVE_ARGUMENT)
from pypy.interpreter.astcompiler.consts import CO_GENERATOR
-class HostCode(PyCode):
+class HostCode(object):
"""
A wrapper around a native code object of the host interpreter
"""
opnames = host_bytecode_spec.method_names
- def __init__(self, space, argcount, nlocals, stacksize, flags,
+ def __init__(self, argcount, nlocals, stacksize, flags,
code, consts, names, varnames, filename,
- name, firstlineno, lnotab, freevars, cellvars,
- hidden_applevel=False, magic=cpython_magic):
+ name, firstlineno, lnotab, freevars):
"""Initialize a new code object"""
- self.space = space
self.co_name = name
assert nlocals >= 0
self.co_argcount = argcount
@@ -26,43 +24,39 @@
self.co_stacksize = stacksize
self.co_flags = flags
self.co_code = code
- self.co_consts_w = consts
- self.co_names_w = [space.wrap(aname) for aname in names]
+ self.consts = consts
+ self.names = names
self.co_varnames = varnames
self.co_freevars = freevars
- self.co_cellvars = cellvars
self.co_filename = filename
self.co_name = name
self.co_firstlineno = firstlineno
self.co_lnotab = lnotab
- self.hidden_applevel = hidden_applevel
- self.magic = magic
- self._signature = cpython_code_signature(self)
- self._initialize()
+ self.signature = cpython_code_signature(self)
- def _initialize(self):
- # Precompute what arguments need to be copied into cellvars
- self._args_as_cellvars = []
+ @classmethod
+ def _from_code(cls, code):
+ """Initialize the code object from a real (CPython) one.
+ """
+ return cls(code.co_argcount,
+ code.co_nlocals,
+ code.co_stacksize,
+ code.co_flags,
+ code.co_code,
+ list(code.co_consts),
+ list(code.co_names),
+ list(code.co_varnames),
+ code.co_filename,
+ code.co_name,
+ code.co_firstlineno,
+ code.co_lnotab,
+ list(code.co_freevars))
- if self.co_cellvars:
- argcount = self.co_argcount
- assert argcount >= 0 # annotator hint
- if self.co_flags & CO_VARARGS:
- argcount += 1
- if self.co_flags & CO_VARKEYWORDS:
- argcount += 1
- # Cell vars could shadow already-set arguments.
- # See comment in PyCode._initialize()
- argvars = self.co_varnames
- cellvars = self.co_cellvars
- for i in range(len(cellvars)):
- cellname = cellvars[i]
- for j in range(argcount):
- if cellname == argvars[j]:
- # argument j has the same name as the cell var i
- while len(self._args_as_cellvars) <= i:
- self._args_as_cellvars.append(-1) # pad
- self._args_as_cellvars[i] = j
+ @property
+ def formalargcount(self):
+ """Total number of arguments passed into the frame, including *vararg
+ and **varkwarg, if they exist."""
+ return self.signature.scope_length()
def read(self, pos):
"""
diff --git a/pypy/objspace/flow/flowcontext.py b/pypy/objspace/flow/flowcontext.py
--- a/pypy/objspace/flow/flowcontext.py
+++ b/pypy/objspace/flow/flowcontext.py
@@ -1,15 +1,18 @@
+"""Implements the core parts of flow graph creation, in tandem
+with pypy.objspace.flow.objspace.
+"""
+
+import sys
import collections
+
from pypy.tool.error import source_lines
from pypy.interpreter import pyframe
-from pypy.interpreter.nestedscope import Cell
-from pypy.interpreter.pycode import CO_NEWLOCALS
from pypy.interpreter.argument import ArgumentsForTranslation
from pypy.interpreter.pyopcode import Return, BytecodeCorruption
from pypy.objspace.flow.model import (Constant, Variable, Block, Link,
- UnwrapException, SpaceOperation, FunctionGraph, c_last_exception)
+ UnwrapException, c_last_exception)
from pypy.objspace.flow.framestate import (FrameState, recursively_unflatten,
recursively_flatten)
-from pypy.objspace.flow.bytecode import HostCode
from pypy.objspace.flow.specialcase import (rpython_print_item,
rpython_print_newline)
@@ -67,6 +70,14 @@
self.last_exception = last_exception
def fixeggblocks(graph):
+ varnames = graph.func.func_code.co_varnames
+ for block in graph.iterblocks():
+ if isinstance(block, SpamBlock):
+ for name, w_value in zip(varnames, block.framestate.mergeable):
+ if isinstance(w_value, Variable):
+ w_value.rename(name)
+ del block.framestate # memory saver
+
# EggBlocks reuse the variables of their previous block,
# which is deemed not acceptable for simplicity of the operations
# that will be performed later on the flow graph.
@@ -87,9 +98,6 @@
for a in block.inputargs:
mapping[a] = Variable(a)
block.renamevariables(mapping)
- for block in graph.iterblocks():
- if isinstance(link, SpamBlock):
- del link.framestate # memory saver
# ____________________________________________________________
@@ -98,9 +106,6 @@
def append(self, operation):
raise NotImplementedError
- def bytecode_trace(self, frame):
- pass
-
def guessbool(self, frame, w_condition, **kwds):
raise AssertionError, "cannot guessbool(%s)" % (w_condition,)
@@ -110,31 +115,13 @@
def __init__(self, block):
self.crnt_block = block
- # saved state at the join point most recently seen
- self.last_join_point = None
- self.enterspamblock = isinstance(block, SpamBlock)
+ # Final frame state after the operations in the block
+ # If this is set, no new space op may be recorded.
+ self.final_state = None
def append(self, operation):
self.crnt_block.operations.append(operation)
- def bytecode_trace(self, frame):
- if self.enterspamblock:
- # If we have a SpamBlock, the first call to bytecode_trace()
- # occurs as soon as frame.resume() starts, before interpretation
- # really begins.
- varnames = frame.pycode.getvarnames()
- for name, w_value in zip(varnames, frame.getfastscope()):
- if isinstance(w_value, Variable):
- w_value.rename(name)
- self.enterspamblock = False
- else:
- # At this point, we progress to the next bytecode. When this
- # occurs, we no longer allow any more operations to be recorded in
- # the same block. We will continue, to figure out where the next
- # such operation *would* appear, and we make a join point just
- # before.
- self.last_join_point = frame.getstate()
-
def guessbool(self, frame, w_condition):
block = self.crnt_block
vars = block.getvariables()
@@ -235,69 +222,45 @@
class FlowSpaceFrame(pyframe.CPythonFrame):
- def __init__(self, space, func, constargs=None):
- code = HostCode._from_code(space, func.func_code)
+ def __init__(self, space, graph, code):
+ self.graph = graph
+ func = graph.func
self.pycode = code
self.space = space
self.w_globals = Constant(func.func_globals)
- self.locals_stack_w = [None] * (code.co_nlocals + code.co_stacksize)
- self.valuestackdepth = code.co_nlocals
self.lastblock = None
- if func.func_closure is not None:
- cl = [c.cell_contents for c in func.func_closure]
- closure = [Cell(Constant(value)) for value in cl]
- else:
- closure = []
- self.initialize_frame_scopes(closure, code)
+ self.init_closure(func.func_closure)
self.f_lineno = code.co_firstlineno
self.last_instr = 0
- if constargs is None:
- constargs = {}
- formalargcount = code.getformalargcount()
- arg_list = [Variable() for i in range(formalargcount)]
- for position, value in constargs.items():
- arg_list[position] = Constant(value)
- self.setfastscope(arg_list)
-
+ self.init_locals_stack(code)
self.w_locals = None # XXX: only for compatibility with PyFrame
self.joinpoints = {}
- self._init_graph(func)
- self.pendingblocks = collections.deque([self.graph.startblock])
- def initialize_frame_scopes(self, closure, code):
- if not (code.co_flags & CO_NEWLOCALS):
- raise ValueError("The code object for a function should have "
- "the flag CO_NEWLOCALS set.")
- if len(closure) != len(code.co_freevars):
- raise ValueError("code object received a closure with "
- "an unexpected number of free variables")
- self.cells = [Cell() for _ in code.co_cellvars] + closure
+ def init_closure(self, closure):
+ if closure is None:
+ self.closure = []
+ else:
+ self.closure = [self.space.wrap(c.cell_contents) for c in closure]
+ assert len(self.closure) == len(self.pycode.co_freevars)
- def _init_graph(self, func):
- # CallableFactory.pycall may add class_ to functions that are methods
- name = func.func_name
- class_ = getattr(func, 'class_', None)
- if class_ is not None:
- name = '%s.%s' % (class_.__name__, name)
- for c in "<>&!":
- name = name.replace(c, '_')
+ def init_locals_stack(self, code):
+ """
+ Initialize the locals and the stack.
- initialblock = SpamBlock(self.getstate())
- if self.pycode.is_generator:
- initialblock.operations.append(
- SpaceOperation('generator_mark', [], Variable()))
- graph = FunctionGraph(name, initialblock)
- graph.func = func
- # attach a signature and defaults to the graph
- # so that it becomes even more interchangeable with the function
- # itself
- graph.signature = self.pycode.signature()
- graph.defaults = func.func_defaults or ()
- graph.is_generator = self.pycode.is_generator
- self.graph = graph
+ The locals are ordered according to self.pycode.signature.
+ """
+ self.valuestackdepth = code.co_nlocals
+ self.locals_stack_w = [None] * (code.co_stacksize + code.co_nlocals)
+
+ def save_locals_stack(self):
+ return self.locals_stack_w[:self.valuestackdepth]
+
+ def restore_locals_stack(self, items_w):
+ self.locals_stack_w[:len(items_w)] = items_w
+ self.dropvaluesuntil(len(items_w))
def getstate(self):
# getfastscope() can return real None, for undefined locals
@@ -309,9 +272,7 @@
data.append(self.last_exception.w_type)
data.append(self.last_exception.w_value)
recursively_flatten(self.space, data)
- nonmergeable = (self.get_blocklist(),
- self.last_instr) # == next_instr when between bytecodes
- return FrameState(data, nonmergeable)
+ return FrameState(data, self.get_blocklist(), self.last_instr)
def setstate(self, state):
""" Reset the frame to the given state. """
@@ -323,8 +284,8 @@
self.last_exception = None
else:
self.last_exception = FSException(data[-2], data[-1])
- blocklist, self.last_instr = state.nonmergeable
- self.set_blocklist(blocklist)
+ self.last_instr = state.next_instr
+ self.set_blocklist(state.blocklist)
def recording(self, block):
""" Setup recording of the block and return the recorder. """
@@ -347,8 +308,8 @@
def record(self, spaceop):
"""Record an operation into the active block"""
recorder = self.recorder
- if getattr(recorder, 'last_join_point', None) is not None:
- self.mergeblock(recorder.crnt_block, recorder.last_join_point)
+ if getattr(recorder, 'final_state', None) is not None:
+ self.mergeblock(recorder.crnt_block, recorder.final_state)
raise StopFlowing
recorder.append(spaceop)
@@ -369,14 +330,16 @@
return self.recorder.guessexception(self, *exceptions)
def build_flow(self):
+ graph = self.graph
+ self.pendingblocks = collections.deque([graph.startblock])
while self.pendingblocks:
block = self.pendingblocks.popleft()
try:
self.recorder = self.recording(block)
self.frame_finished_execution = False
- next_instr = self.last_instr
while True:
- next_instr = self.handle_bytecode(next_instr)
+ self.last_instr = self.handle_bytecode(self.last_instr)
+ self.recorder.final_state = self.getstate()
except ImplicitOperationError, e:
if isinstance(e.w_type, Constant):
@@ -386,14 +349,14 @@
msg = "implicit %s shouldn't occur" % exc_cls.__name__
w_type = Constant(AssertionError)
w_value = Constant(AssertionError(msg))
- link = Link([w_type, w_value], self.graph.exceptblock)
+ link = Link([w_type, w_value], graph.exceptblock)
self.recorder.crnt_block.closeblock(link)
except FSException, e:
if e.w_type is self.space.w_ImportError:
msg = 'import statement always raises %s' % e
raise ImportError(msg)
- link = Link([e.w_type, e.w_value], self.graph.exceptblock)
+ link = Link([e.w_type, e.w_value], graph.exceptblock)
self.recorder.crnt_block.closeblock(link)
except StopFlowing:
@@ -402,7 +365,7 @@
except Return:
w_result = self.popvalue()
assert w_result is not None
- link = Link([w_result], self.graph.returnblock)
+ link = Link([w_result], graph.returnblock)
self.recorder.crnt_block.closeblock(link)
del self.recorder
@@ -414,37 +377,35 @@
candidates = self.joinpoints.setdefault(next_instr, [])
for block in candidates:
newstate = block.framestate.union(currentstate)
- if newstate is not None:
- # yes
- finished = newstate == block.framestate
+ if newstate is None:
+ continue
+ elif newstate == block.framestate:
+ outputargs = currentstate.getoutputargs(newstate)
+ currentblock.closeblock(Link(outputargs, block))
+ return
+ else:
break
else:
- # no
newstate = currentstate.copy()
- finished = False
block = None
- if finished:
- newblock = block
- else:
- newblock = SpamBlock(newstate)
+ newblock = SpamBlock(newstate)
# unconditionally link the current block to the newblock
outputargs = currentstate.getoutputargs(newstate)
link = Link(outputargs, newblock)
currentblock.closeblock(link)
- # phew
- if not finished:
- if block is not None:
- # to simplify the graph, we patch the old block to point
- # directly at the new block which is its generalization
- block.dead = True
- block.operations = ()
- block.exitswitch = None
- outputargs = block.framestate.getoutputargs(newstate)
- block.recloseblock(Link(outputargs, newblock))
- candidates.remove(block)
- candidates.insert(0, newblock)
- self.pendingblocks.append(newblock)
+
+ if block is not None:
+ # to simplify the graph, we patch the old block to point
+ # directly at the new block which is its generalization
+ block.dead = True
+ block.operations = ()
+ block.exitswitch = None
+ outputargs = block.framestate.getoutputargs(newstate)
+ block.recloseblock(Link(outputargs, newblock))
+ candidates.remove(block)
+ candidates.insert(0, newblock)
+ self.pendingblocks.append(newblock)
# hack for unrolling iterables, don't use this
def replace_in_stack(self, oldvalue, newvalue):
@@ -460,17 +421,12 @@
break
def handle_bytecode(self, next_instr):
+ next_instr, methodname, oparg = self.pycode.read(next_instr)
try:
- while True:
- self.last_instr = next_instr
- self.recorder.bytecode_trace(self)
- next_instr, methodname, oparg = self.pycode.read(next_instr)
- res = getattr(self, methodname)(oparg, next_instr)
- if res is not None:
- next_instr = res
+ res = getattr(self, methodname)(oparg, next_instr)
+ return res if res is not None else next_instr
except FSException, operr:
- next_instr = self.handle_operation_error(operr)
- return next_instr
+ return self.handle_operation_error(operr)
def handle_operation_error(self, operr):
block = self.unrollstack(SApplicationException.kind)
@@ -481,6 +437,18 @@
next_instr = block.handle(self, unroller)
return next_instr
+ def getlocalvarname(self, index):
+ return self.pycode.co_varnames[index]
+
+ def getconstant_w(self, index):
+ return self.space.wrap(self.pycode.consts[index])
+
+ def getname_u(self, index):
+ return self.pycode.names[index]
+
+ def getname_w(self, index):
+ return Constant(self.pycode.names[index])
+
def BAD_OPCODE(self, _, next_instr):
raise FlowingError(self, "This operation is not RPython")
@@ -685,19 +653,13 @@
# Note: RPython context managers receive None in lieu of tracebacks
# and cannot suppress the exception.
# This opcode changed a lot between CPython versions
- if (self.pycode.magic >= 0xa0df2ef
- # Implementation since 2.7a0: 62191 (introduce SETUP_WITH)
- or self.pycode.magic >= 0xa0df2d1):
- # implementation since 2.6a1: 62161 (WITH_CLEANUP optimization)
+ if sys.version_info >= (2, 6):
w_unroller = self.popvalue()
w_exitfunc = self.popvalue()
self.pushvalue(w_unroller)
- elif self.pycode.magic >= 0xa0df28c:
- # Implementation since 2.5a0: 62092 (changed WITH_CLEANUP opcode)
+ else:
w_exitfunc = self.popvalue()
w_unroller = self.peekvalue(0)
- else:
- raise NotImplementedError("WITH_CLEANUP for CPython <= 2.4")
unroller = self.space.unwrap(w_unroller)
w_None = self.space.w_None
@@ -710,6 +672,12 @@
else:
self.space.call_function(w_exitfunc, w_None, w_None, w_None)
+ def LOAD_FAST(self, varindex, next_instr):
+ w_value = self.locals_stack_w[varindex]
+ if w_value is None:
+ raise FlowingError(self, "Local variable referenced before assignment")
+ self.pushvalue(w_value)
+
def LOAD_GLOBAL(self, nameindex, next_instr):
w_result = self.space.find_global(self.w_globals, self.getname_u(nameindex))
self.pushvalue(w_result)
@@ -722,6 +690,9 @@
self.pushvalue(w_value)
LOOKUP_METHOD = LOAD_ATTR
+ def LOAD_DEREF(self, varindex, next_instr):
+ self.pushvalue(self.closure[varindex])
+
def BUILD_LIST_FROM_ARG(self, _, next_instr):
# This opcode was added with pypy-1.8. Here is a simpler
# version, enough for annotation.
@@ -729,6 +700,12 @@
self.pushvalue(self.space.newlist([]))
self.pushvalue(last_val)
+ def MAKE_FUNCTION(self, numdefaults, next_instr):
+ w_codeobj = self.popvalue()
+ defaults = self.popvalues(numdefaults)
+ fn = self.space.newfunction(w_codeobj, self.w_globals, defaults)
+ self.pushvalue(fn)
+
# XXX Unimplemented 2.7 opcodes ----------------
# Set literals, set comprehensions
@@ -744,6 +721,12 @@
def MAP_ADD(self, oparg, next_instr):
raise NotImplementedError("MAP_ADD")
+ # Closures
+
+ STORE_DEREF = BAD_OPCODE
+ LOAD_CLOSURE = BAD_OPCODE
+ MAKE_CLOSURE = BAD_OPCODE
+
def make_arguments(self, nargs):
return ArgumentsForTranslation(self.space, self.peekvalues(nargs))
def argument_factory(self, *args):
diff --git a/pypy/objspace/flow/framestate.py b/pypy/objspace/flow/framestate.py
--- a/pypy/objspace/flow/framestate.py
+++ b/pypy/objspace/flow/framestate.py
@@ -2,10 +2,10 @@
from pypy.objspace.flow.model import *
class FrameState:
- def __init__(self, mergeable, nonmergeable):
+ def __init__(self, mergeable, blocklist, next_instr):
self.mergeable = mergeable
- self.nonmergeable = nonmergeable
- self.next_instr = self.nonmergeable[1]
+ self.blocklist = blocklist
+ self.next_instr = next_instr
for w1 in self.mergeable:
assert isinstance(w1, (Variable, Constant)) or w1 is None, (
'%r found in frame state' % w1)
@@ -17,7 +17,7 @@
if isinstance(w, Variable):
w = Variable()
newstate.append(w)
- return FrameState(newstate, self.nonmergeable)
+ return FrameState(newstate, self.blocklist, self.next_instr)
def getvariables(self):
return [w for w in self.mergeable if isinstance(w, Variable)]
@@ -29,7 +29,8 @@
# nonmergeable states
assert isinstance(other, FrameState)
assert len(self.mergeable) == len(other.mergeable)
- assert self.nonmergeable == other.nonmergeable
+ assert self.blocklist == other.blocklist
+ assert self.next_instr == other.next_instr
for w1, w2 in zip(self.mergeable, other.mergeable):
if not (w1 == w2 or (isinstance(w1, Variable) and
isinstance(w2, Variable))):
@@ -50,7 +51,7 @@
newstate.append(union(w1, w2))
except UnionError:
return None
- return FrameState(newstate, self.nonmergeable)
+ return FrameState(newstate, self.blocklist, self.next_instr)
def getoutputargs(self, targetstate):
"Return the output arguments needed to link self to targetstate."
diff --git a/pypy/objspace/flow/generator.py b/pypy/objspace/flow/generator.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/flow/generator.py
@@ -0,0 +1,186 @@
+"""Flow graph building for generators"""
+
+from pypy.objspace.flow.model import Block, Link, SpaceOperation, checkgraph
+from pypy.objspace.flow.model import Variable, Constant
+from pypy.translator.unsimplify import insert_empty_startblock
+from pypy.translator.unsimplify import split_block
+from pypy.translator.simplify import eliminate_empty_blocks, simplify_graph
+from pypy.tool.sourcetools import func_with_new_name
+from pypy.interpreter.argument import Signature
+
+
+class AbstractPosition(object):
+ _immutable_ = True
+ _attrs_ = ()
+
+def bootstrap_generator(graph):
+ # This is the first copy of the graph. We replace it with
+ # a small bootstrap graph.
+ GeneratorIterator = make_generatoriterator_class(graph)
+ replace_graph_with_bootstrap(GeneratorIterator, graph)
+ # We attach a 'next' method to the GeneratorIterator class
+ # that will invoke the real function, based on a second
+ # copy of the graph.
+ attach_next_method(GeneratorIterator, graph)
+ return graph
+
+def tweak_generator_graph(graph):
+ # This is the second copy of the graph. Tweak it.
+ GeneratorIterator = graph.func._generator_next_method_of_
+ tweak_generator_body_graph(GeneratorIterator.Entry, graph)
+
+
+def make_generatoriterator_class(graph):
+ class GeneratorIterator(object):
+ class Entry(AbstractPosition):
+ _immutable_ = True
+ varnames = get_variable_names(graph.startblock.inputargs)
+
+ def __init__(self, entry):
+ self.current = entry
+
+ def __iter__(self):
+ return self
+
+ return GeneratorIterator
+
+def replace_graph_with_bootstrap(GeneratorIterator, graph):
+ Entry = GeneratorIterator.Entry
+ newblock = Block(graph.startblock.inputargs)
+ v_generator = Variable('generator')
+ v_entry = Variable('entry')
+ newblock.operations.append(
+ SpaceOperation('simple_call', [Constant(Entry)], v_entry))
+ assert len(graph.startblock.inputargs) == len(Entry.varnames)
+ for v, name in zip(graph.startblock.inputargs, Entry.varnames):
+ newblock.operations.append(
+ SpaceOperation('setattr', [v_entry, Constant(name), v],
+ Variable()))
+ newblock.operations.append(
+ SpaceOperation('simple_call', [Constant(GeneratorIterator), v_entry],
+ v_generator))
+ newblock.closeblock(Link([v_generator], graph.returnblock))
+ graph.startblock = newblock
+
+def attach_next_method(GeneratorIterator, graph):
+ func = graph.func
+ func = func_with_new_name(func, '%s__next' % (func.func_name,))
+ func._generator_next_method_of_ = GeneratorIterator
+ func._always_inline_ = True
+ #
+ def next(self):
+ entry = self.current
+ self.current = None
+ assert entry is not None # else, recursive generator invocation
+ (next_entry, return_value) = func(entry)
+ self.current = next_entry
+ return return_value
+ GeneratorIterator.next = next
+ return func # for debugging
+
+def get_variable_names(variables):
+ seen = set()
+ result = []
+ for v in variables:
+ name = v._name.strip('_')
+ while name in seen:
+ name += '_'
+ result.append('g_' + name)
+ seen.add(name)
+ return result
+
+def _insert_reads(block, varnames):
+ assert len(varnames) == len(block.inputargs)
+ v_entry1 = Variable('entry')
+ for i, name in enumerate(varnames):
+ block.operations.insert(i,
+ SpaceOperation('getattr', [v_entry1, Constant(name)],
+ block.inputargs[i]))
+ block.inputargs = [v_entry1]
+
+def tweak_generator_body_graph(Entry, graph):
+ # First, always run simplify_graph in order to reduce the number of
+ # variables passed around
+ simplify_graph(graph)
+ #
+ assert graph.startblock.operations[0].opname == 'generator_mark'
+ graph.startblock.operations.pop(0)
+ #
+ insert_empty_startblock(None, graph)
+ _insert_reads(graph.startblock, Entry.varnames)
+ Entry.block = graph.startblock
+ #
+ mappings = [Entry]
+ #
+ stopblock = Block([])
+ v0 = Variable(); v1 = Variable()
+ stopblock.operations = [
+ SpaceOperation('simple_call', [Constant(StopIteration)], v0),
+ SpaceOperation('type', [v0], v1),
+ ]
+ stopblock.closeblock(Link([v1, v0], graph.exceptblock))
+ #
+ for block in list(graph.iterblocks()):
+ for exit in block.exits:
+ if exit.target is graph.returnblock:
+ exit.args = []
+ exit.target = stopblock
+ assert block is not stopblock
+ for index in range(len(block.operations)-1, -1, -1):
+ op = block.operations[index]
+ if op.opname == 'yield':
+ [v_yielded_value] = op.args
+ del block.operations[index]
+ newlink = split_block(None, block, index)
+ newblock = newlink.target
+ #
+ class Resume(AbstractPosition):
+ _immutable_ = True
+ block = newblock
+ Resume.__name__ = 'Resume%d' % len(mappings)
+ mappings.append(Resume)
+ varnames = get_variable_names(newlink.args)
+ #
+ _insert_reads(newblock, varnames)
+ #
+ v_resume = Variable('resume')
+ block.operations.append(
+ SpaceOperation('simple_call', [Constant(Resume)],
+ v_resume))
+ for i, name in enumerate(varnames):
+ block.operations.append(
+ SpaceOperation('setattr', [v_resume, Constant(name),
+ newlink.args[i]],
+ Variable()))
+ v_pair = Variable('pair')
+ block.operations.append(
+ SpaceOperation('newtuple', [v_resume, v_yielded_value],
+ v_pair))
+ newlink.args = [v_pair]
+ newlink.target = graph.returnblock
+ #
+ regular_entry_block = Block([Variable('entry')])
+ block = regular_entry_block
+ for Resume in mappings:
+ v_check = Variable()
+ block.operations.append(
+ SpaceOperation('simple_call', [Constant(isinstance),
+ block.inputargs[0],
+ Constant(Resume)],
+ v_check))
+ block.exitswitch = v_check
+ link1 = Link([block.inputargs[0]], Resume.block)
+ link1.exitcase = True
+ nextblock = Block([Variable('entry')])
+ link2 = Link([block.inputargs[0]], nextblock)
+ link2.exitcase = False
+ block.closeblock(link1, link2)
+ block = nextblock
+ block.closeblock(Link([Constant(AssertionError),
+ Constant(AssertionError("bad generator class"))],
+ graph.exceptblock))
+ graph.startblock = regular_entry_block
+ graph.signature = Signature(['entry'])
+ graph.defaults = ()
+ checkgraph(graph)
+ eliminate_empty_blocks(graph)
diff --git a/pypy/objspace/flow/objspace.py b/pypy/objspace/flow/objspace.py
--- a/pypy/objspace/flow/objspace.py
+++ b/pypy/objspace/flow/objspace.py
@@ -1,14 +1,23 @@
-# ______________________________________________________________________
+"""Implements the core parts of flow graph creation, in tandem
+with pypy.objspace.flow.flowcontext.
+"""
+
import __builtin__
import sys
import types
+from inspect import CO_NEWLOCALS
+
from pypy.interpreter.baseobjspace import ObjSpace
from pypy.interpreter.argument import ArgumentsForTranslation
from pypy.objspace.flow.model import (Constant, Variable, WrapException,
UnwrapException, checkgraph, SpaceOperation)
+from pypy.objspace.flow.bytecode import HostCode
from pypy.objspace.flow import operation
from pypy.objspace.flow.flowcontext import (FlowSpaceFrame, fixeggblocks,
FSException, FlowingError)
+from pypy.objspace.flow.generator import (tweak_generator_graph,
+ bootstrap_generator)
+from pypy.objspace.flow.pygraph import PyGraph
from pypy.objspace.flow.specialcase import SPECIAL_CASES
from pypy.rlib.unroll import unrolling_iterable, _unroller
from pypy.rlib import rstackovf, rarithmetic
@@ -37,6 +46,17 @@
}
}
+def _assert_rpythonic(func):
+ """Raise ValueError if ``func`` is obviously not RPython"""
+ if func.func_doc and func.func_doc.lstrip().startswith('NOT_RPYTHON'):
+ raise ValueError("%r is tagged as NOT_RPYTHON" % (func,))
+ if func.func_code.co_cellvars:
+ raise ValueError("RPython functions cannot create closures")
+ if not (func.func_code.co_flags & CO_NEWLOCALS):
+ raise ValueError("The code object for a RPython function should have "
+ "the flag CO_NEWLOCALS set.")
+
+
# ______________________________________________________________________
class FlowObjSpace(object):
"""NOT_RPYTHON.
@@ -94,6 +114,17 @@
else:
return self.w_False
+ def newfunction(self, w_code, w_globals, defaults_w):
+ try:
+ code = self.unwrap(w_code)
+ globals = self.unwrap(w_globals)
+ defaults = tuple([self.unwrap(value) for value in defaults_w])
+ except UnwrapException:
+ raise FlowingError(self.frame, "Dynamically created function must"
+ " have constant default values.")
+ fn = types.FunctionType(code, globals, code.co_name, defaults)
+ return Constant(fn)
+
def wrap(self, obj):
if isinstance(obj, (Variable, Constant)):
raise TypeError("already wrapped: " + repr(obj))
@@ -232,18 +263,25 @@
w_type = w_instclass
return FSException(w_type, w_value)
- def build_flow(self, func, constargs={}, tweak_for_generator=True):
+ def build_flow(self, func):
"""
"""
- if func.func_doc and func.func_doc.lstrip().startswith('NOT_RPYTHON'):
- raise Exception, "%r is tagged as NOT_RPYTHON" % (func,)
- frame = self.frame = FlowSpaceFrame(self, func, constargs)
+ _assert_rpythonic(func)
+ code = HostCode._from_code(func.func_code)
+ if (code.is_generator and
+ not hasattr(func, '_generator_next_method_of_')):
+ graph = PyGraph(func, code)
+ block = graph.startblock
+ for name, w_value in zip(code.co_varnames, block.framestate.mergeable):
+ if isinstance(w_value, Variable):
+ w_value.rename(name)
+ return bootstrap_generator(graph)
+ graph = PyGraph(func, code)
+ frame = self.frame = FlowSpaceFrame(self, graph, code)
frame.build_flow()
- graph = frame.graph
fixeggblocks(graph)
checkgraph(graph)
- if graph.is_generator and tweak_for_generator:
- from pypy.translator.generator import tweak_generator_graph
+ if code.is_generator:
tweak_generator_graph(graph)
return graph
diff --git a/pypy/objspace/flow/pygraph.py b/pypy/objspace/flow/pygraph.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/flow/pygraph.py
@@ -0,0 +1,39 @@
+"""
+Implements flow graphs for Python callables
+"""
+from pypy.objspace.flow.model import (FunctionGraph, Constant, Variable,
+ SpaceOperation)
+from pypy.objspace.flow.framestate import FrameState
+
+class PyGraph(FunctionGraph):
+ """
+ Flow graph for a Python function
+ """
+
+ def __init__(self, func, code):
+ from pypy.objspace.flow.flowcontext import SpamBlock
+ data = [None] * code.co_nlocals
+ for i in range(code.formalargcount):
+ data[i] = Variable()
+ state = FrameState(data + [Constant(None), Constant(None)], [], 0)
+ initialblock = SpamBlock(state)
+ if code.is_generator:
+ initialblock.operations.append(
+ SpaceOperation('generator_mark', [], Variable()))
+
+ super(PyGraph, self).__init__(self._sanitize_funcname(func), initialblock)
+ self.func = func
+ self.signature = code.signature
+ self.defaults = func.func_defaults or ()
+
+ @staticmethod
+ def _sanitize_funcname(func):
+ # CallableFactory.pycall may add class_ to functions that are methods
+ name = func.func_name
+ class_ = getattr(func, 'class_', None)
+ if class_ is not None:
+ name = '%s.%s' % (class_.__name__, name)
+ for c in "<>&!":
+ name = name.replace(c, '_')
+ return name
+
diff --git a/pypy/objspace/flow/test/test_framestate.py b/pypy/objspace/flow/test/test_framestate.py
--- a/pypy/objspace/flow/test/test_framestate.py
+++ b/pypy/objspace/flow/test/test_framestate.py
@@ -2,6 +2,8 @@
from pypy.rlib.unroll import SpecTag
from pypy.objspace.flow.objspace import FlowObjSpace
from pypy.objspace.flow.flowcontext import FlowSpaceFrame
+from pypy.objspace.flow.bytecode import HostCode
+from pypy.objspace.flow.pygraph import PyGraph
class TestFrameState:
def setup_class(cls):
@@ -12,8 +14,11 @@
func = func.im_func
except AttributeError:
pass
- frame = FlowSpaceFrame(self.space, func)
+ code = HostCode._from_code(func.func_code)
+ graph = PyGraph(func, code)
+ frame = FlowSpaceFrame(self.space, graph, code)
# hack the frame
+ frame.setstate(graph.startblock.framestate)
frame.locals_stack_w[frame.pycode.co_nlocals-1] = Constant(None)
return frame
diff --git a/pypy/objspace/flow/test/test_generator.py b/pypy/objspace/flow/test/test_generator.py
--- a/pypy/objspace/flow/test/test_generator.py
+++ b/pypy/objspace/flow/test/test_generator.py
@@ -1,18 +1,127 @@
-from pypy.objspace.flow.test.test_objspace import Base
+from pypy.conftest import option
+from pypy.objspace.flow.objspace import FlowObjSpace
+from pypy.objspace.flow.model import Variable
+from pypy.objspace.flow.generator import (make_generatoriterator_class,
+ replace_graph_with_bootstrap, get_variable_names, attach_next_method)
+from pypy.translator.simplify import join_blocks
-class TestGenerator(Base):
+# ____________________________________________________________
- def test_simple_generator(self):
- def f(n):
+def f_gen(n):
+ i = 0
+ while i < n:
+ yield i
+ i += 1
+
+class GeneratorIterator(object):
+ def __init__(self, entry):
+ self.current = entry
+ def next(self):
+ e = self.current
+ self.current = None
+ if isinstance(e, Yield1):
+ n = e.n_0
+ i = e.i_0
+ i += 1
+ else:
+ n = e.n_0
i = 0
- while i < n:
- yield i
- yield i
- i += 1
- graph = self.codetest(f, tweak_for_generator=False)
- ops = self.all_operations(graph)
- assert ops == {'generator_mark': 1,
- 'lt': 1, 'is_true': 1,
- 'yield': 2,
- 'inplace_add': 1}
+ if i < n:
+ e = Yield1()
+ e.n_0 = n
+ e.i_0 = i
+ self.current = e
+ return i
+ raise StopIteration
+
+ def __iter__(self):
+ return self
+
+class AbstractPosition(object):
+ _immutable_ = True
+class Entry1(AbstractPosition):
+ _immutable_ = True
+class Yield1(AbstractPosition):
+ _immutable_ = True
+
+def f_explicit(n):
+ e = Entry1()
+ e.n_0 = n
+ return GeneratorIterator(e)
+
+def test_explicit():
+ assert list(f_gen(10)) == list(f_explicit(10))
+
+def test_get_variable_names():
+ lst = get_variable_names([Variable('a'), Variable('b_'), Variable('a')])
+ assert lst == ['g_a', 'g_b', 'g_a_']
+
+# ____________________________________________________________
+
+
+class TestGenerator:
+
+ def test_replace_graph_with_bootstrap(self):
+ def func(n, x, y, z):
+ yield n
+ yield n
+ #
+ graph = FlowObjSpace().build_flow(func)
+ if option.view:
+ graph.show()
+ block = graph.startblock
+ ops = block.operations
+ assert ops[0].opname == 'simple_call' # e = Entry1()
+ assert ops[1].opname == 'setattr' # e.g_n = n
+ assert ops[1].args[1].value == 'g_n'
+ assert ops[2].opname == 'setattr' # e.g_x = x
+ assert ops[2].args[1].value == 'g_x'
+ assert ops[3].opname == 'setattr' # e.g_y = y
+ assert ops[3].args[1].value == 'g_y'
+ assert ops[4].opname == 'setattr' # e.g_z = z
+ assert ops[4].args[1].value == 'g_z'
+ assert ops[5].opname == 'simple_call' # g = GeneratorIterator(e)
+ assert ops[5].args[1] == ops[0].result
+ assert len(ops) == 6
+ assert len(block.exits) == 1
+ assert block.exits[0].target is graph.returnblock
+
+ def test_tweak_generator_graph(self):
+ def f(n, x, y, z):
+ z *= 10
+ yield n + 1
+ z -= 10
+ #
+ space = FlowObjSpace()
+ graph = space.build_flow(f)
+ GeneratorIterator = make_generatoriterator_class(graph)
+ replace_graph_with_bootstrap(GeneratorIterator, graph)
+ func1 = attach_next_method(GeneratorIterator, graph)
+ if option.view:
+ graph.show()
+ #
+ assert func1._generator_next_method_of_ is GeneratorIterator
+ assert hasattr(GeneratorIterator, 'next')
+ #
+ graph_next = space.build_flow(GeneratorIterator.next.im_func)
+ join_blocks(graph_next)
+ if option.view:
+ graph_next.show()
+ #
+ graph1 = space.build_flow(func1)
+ if option.view:
+ graph1.show()
+
+ def test_automatic(self):
+ def f(n, x, y, z):
+ z *= 10
+ yield n + 1
+ z -= 10
+ #
+ graph = FlowObjSpace().build_flow(f)
+ if option.view:
+ graph.show()
+ block = graph.startblock
+ assert len(block.exits) == 1
+ assert block.exits[0].target is graph.returnblock
diff --git a/pypy/objspace/flow/test/test_objspace.py b/pypy/objspace/flow/test/test_objspace.py
--- a/pypy/objspace/flow/test/test_objspace.py
+++ b/pypy/objspace/flow/test/test_objspace.py
@@ -1054,6 +1054,80 @@
with py.test.raises(FlowingError):
self.codetest(f)
+ @py.test.mark.xfail(reason="closures aren't supported")
+ def test_cellvar_store(self):
+ def f():
+ x = 5
+ return x
+ lambda: x # turn x into a cell variable
+ graph = self.codetest(f)
+ assert len(graph.startblock.exits) == 1
+ assert graph.startblock.exits[0].target == graph.returnblock
+
+ @py.test.mark.xfail(reason="closures aren't supported")
+ def test_arg_as_cellvar(self):
+ def f(x, y, z):
+ a, b, c = 1, 2, 3
+ z = b
+ return z
+ lambda: (a, b, x, z) # make cell variables
+ graph = self.codetest(f)
+ assert len(graph.startblock.exits) == 1
+ assert graph.startblock.exits[0].target == graph.returnblock
+ assert not graph.startblock.operations
+ assert graph.startblock.exits[0].args[0].value == 2
+
+ def test_lambda(self):
+ def f():
+ g = lambda m, n: n*m
+ return g
+ graph = self.codetest(f)
+ assert len(graph.startblock.exits) == 1
+ assert graph.startblock.exits[0].target == graph.returnblock
+ g = graph.startblock.exits[0].args[0].value
+ assert g(4, 4) == 16
+
+ def test_lambda_with_defaults(self):
+ def f():
+ g = lambda m, n=5: n*m
+ return g
+ graph = self.codetest(f)
+ assert len(graph.startblock.exits) == 1
+ assert graph.startblock.exits[0].target == graph.returnblock
+ g = graph.startblock.exits[0].args[0].value
+ assert g(4) == 20
+
+ def f2(x):
+ g = lambda m, n=x: n*m
+ return g
+ with py.test.raises(FlowingError):
+ self.codetest(f2)
+
+ @py.test.mark.xfail(reason="closures aren't supported")
+ def test_closure(self):
+ def f():
+ m = 5
+ return lambda n: m * n
+ graph = self.codetest(f)
+ assert len(graph.startblock.exits) == 1
+ assert graph.startblock.exits[0].target == graph.returnblock
+ g = graph.startblock.exits[0].args[0].value
+ assert g(4) == 20
+
+ def test_closure_error(self):
+ def f():
+ m = 5
+ return lambda n: m * n
+ with py.test.raises(ValueError) as excinfo:
+ self.codetest(f)
+ assert "closure" in str(excinfo.value)
+
+ def test_unbound_local(self):
+ def f():
+ x += 1
+ with py.test.raises(FlowingError):
+ self.codetest(f)
+
DATA = {'x': 5,
'y': 6}
diff --git a/pypy/objspace/std/bytearraytype.py b/pypy/objspace/std/bytearraytype.py
--- a/pypy/objspace/std/bytearraytype.py
+++ b/pypy/objspace/std/bytearraytype.py
@@ -1,7 +1,6 @@
-import sys
-from pypy.interpreter import gateway
from pypy.interpreter.baseobjspace import ObjSpace, W_Root
from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import interp2app
from pypy.objspace.std.register_all import register_all
from pypy.objspace.std.stdtypedef import StdTypeDef, SMM
@@ -163,9 +162,9 @@
bytearray(sequence) -> bytearray initialized from sequence\'s items
If the argument is a bytearray, the return value is the same object.''',
- __new__ = gateway.interp2app(descr__new__),
+ __new__ = interp2app(descr__new__),
__hash__ = None,
- __reduce__ = gateway.interp2app(descr_bytearray__reduce__),
- fromhex = gateway.interp2app(descr_fromhex, as_classmethod=True)
+ __reduce__ = interp2app(descr_bytearray__reduce__),
+ fromhex = interp2app(descr_fromhex, as_classmethod=True)
)
bytearray_typedef.registermethods(globals())
diff --git a/pypy/rlib/rcomplex.py b/pypy/rlib/rcomplex.py
--- a/pypy/rlib/rcomplex.py
+++ b/pypy/rlib/rcomplex.py
@@ -57,6 +57,9 @@
denom = r2 + i2 * ratio
rr = (r1 + i1 * ratio) / denom
ir = (i1 - r1 * ratio) / denom
+ elif isnan(r2):
+ rr = NAN
+ ir = NAN
else:
ratio = r2 / i2
denom = r2 * ratio + i2
diff --git a/pypy/rlib/test/test_rcomplex.py b/pypy/rlib/test/test_rcomplex.py
--- a/pypy/rlib/test/test_rcomplex.py
+++ b/pypy/rlib/test/test_rcomplex.py
@@ -33,6 +33,9 @@
]:
assert c.c_mul(c1, c2) == result
+def test_div():
+ c.c_div((2., 3.), (float('nan'), 0.)) == (float('nan'), float('nan'))
+
def parse_testfile2(fname):
"""Parse a file with test values
diff --git a/pypy/rpython/microbench/__init__.py b/pypy/rpython/microbench/__init__.py
deleted file mode 100644
diff --git a/pypy/rpython/microbench/autopath.py b/pypy/rpython/microbench/autopath.py
deleted file mode 100644
--- a/pypy/rpython/microbench/autopath.py
+++ /dev/null
@@ -1,131 +0,0 @@
-"""
-self cloning, automatic path configuration
-
-copy this into any subdirectory of pypy from which scripts need
-to be run, typically all of the test subdirs.
-The idea is that any such script simply issues
-
- import autopath
-
-and this will make sure that the parent directory containing "pypy"
-is in sys.path.
-
-If you modify the master "autopath.py" version (in pypy/tool/autopath.py)
-you can directly run it which will copy itself on all autopath.py files
-it finds under the pypy root directory.
-
-This module always provides these attributes:
-
- pypydir pypy root directory path
- this_dir directory where this autopath.py resides
-
-"""
-
-def __dirinfo(part):
- """ return (partdir, this_dir) and insert parent of partdir
- into sys.path. If the parent directories don't have the part
- an EnvironmentError is raised."""
-
- import sys, os
- try:
- head = this_dir = os.path.realpath(os.path.dirname(__file__))
- except NameError:
- head = this_dir = os.path.realpath(os.path.dirname(sys.argv[0]))
-
- error = None
- while head:
- partdir = head
- head, tail = os.path.split(head)
- if tail == part:
- checkfile = os.path.join(partdir, os.pardir, 'pypy', '__init__.py')
- if not os.path.exists(checkfile):
- error = "Cannot find %r" % (os.path.normpath(checkfile),)
- break
- else:
- error = "Cannot find the parent directory %r of the path %r" % (
- partdir, this_dir)
- if not error:
- # check for bogus end-of-line style (e.g. files checked out on
- # Windows and moved to Unix)
- f = open(__file__.replace('.pyc', '.py'), 'r')
- data = f.read()
- f.close()
- if data.endswith('\r\n') or data.endswith('\r'):
- error = ("Bad end-of-line style in the .py files. Typically "
- "caused by a zip file or a checkout done on Windows and "
- "moved to Unix or vice-versa.")
- if error:
- raise EnvironmentError("Invalid source tree - bogus checkout! " +
- error)
-
- pypy_root = os.path.join(head, '')
- try:
- sys.path.remove(head)
- except ValueError:
- pass
- sys.path.insert(0, head)
-
- munged = {}
- for name, mod in sys.modules.items():
- if '.' in name:
- continue
- fn = getattr(mod, '__file__', None)
- if not isinstance(fn, str):
- continue
- newname = os.path.splitext(os.path.basename(fn))[0]
- if not newname.startswith(part + '.'):
- continue
- path = os.path.join(os.path.dirname(os.path.realpath(fn)), '')
- if path.startswith(pypy_root) and newname != part:
- modpaths = os.path.normpath(path[len(pypy_root):]).split(os.sep)
- if newname != '__init__':
- modpaths.append(newname)
- modpath = '.'.join(modpaths)
- if modpath not in sys.modules:
- munged[modpath] = mod
-
- for name, mod in munged.iteritems():
- if name not in sys.modules:
- sys.modules[name] = mod
- if '.' in name:
- prename = name[:name.rfind('.')]
- postname = name[len(prename)+1:]
- if prename not in sys.modules:
- __import__(prename)
- if not hasattr(sys.modules[prename], postname):
- setattr(sys.modules[prename], postname, mod)
-
- return partdir, this_dir
-
-def __clone():
- """ clone master version of autopath.py into all subdirs """
- from os.path import join, walk
- if not this_dir.endswith(join('pypy','tool')):
- raise EnvironmentError("can only clone master version "
- "'%s'" % join(pypydir, 'tool',_myname))
-
-
- def sync_walker(arg, dirname, fnames):
- if _myname in fnames:
- fn = join(dirname, _myname)
- f = open(fn, 'rwb+')
- try:
- if f.read() == arg:
- print "checkok", fn
- else:
- print "syncing", fn
- f = open(fn, 'w')
- f.write(arg)
- finally:
- f.close()
- s = open(join(pypydir, 'tool', _myname), 'rb').read()
- walk(pypydir, sync_walker, s)
-
-_myname = 'autopath.py'
-
-# set guaranteed attributes
-
-pypydir, this_dir = __dirinfo('pypy')
-
-if __name__ == '__main__':
- __clone()
diff --git a/pypy/rpython/microbench/dict.py b/pypy/rpython/microbench/dict.py
deleted file mode 100644
--- a/pypy/rpython/microbench/dict.py
+++ /dev/null
@@ -1,65 +0,0 @@
-from pypy.rpython.microbench.microbench import MetaBench
-
-class str_dict__set_item:
- __metaclass__ = MetaBench
-
- def init():
- return {}
- args = ['obj', 'i']
- def loop(obj, i):
- obj['foo'] = i
- obj['bar'] = i
-
-class str_dict__get_item:
- __metaclass__ = MetaBench
-
- def init():
- return {'foo': 0, 'bar': 1}
- args = ['obj', 'i']
- def loop(obj, i):
- return obj['foo'] + obj['bar']
-
-class int_dict__set_item:
- __metaclass__ = MetaBench
-
- def init():
- return {}
- args = ['obj', 'i']
- def loop(obj, i):
- obj[42] = i
- obj[43] = i
-
-class int_dict__get_item:
- __metaclass__ = MetaBench
-
- def init():
- return {42: 0, 43: 1}
- args = ['obj', 'i']
- def loop(obj, i):
- return obj[42] + obj[43]
-
-
-class Foo:
- pass
-
-obj1 = Foo()
-obj2 = Foo()
-
-class obj_dict__set_item:
- __metaclass__ = MetaBench
-
- def init():
- return {}
- args = ['obj', 'i']
- def loop(obj, i):
- obj[obj1] = i
- obj[obj2] = i
-
-class obj_dict__get_item:
- __metaclass__ = MetaBench
-
- def init():
- return {obj1: 0, obj2: 1}
- args = ['obj', 'i']
- def loop(obj, i):
- return obj[obj1] + obj[obj2]
diff --git a/pypy/rpython/microbench/indirect.py b/pypy/rpython/microbench/indirect.py
deleted file mode 100644
--- a/pypy/rpython/microbench/indirect.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from pypy.rpython.microbench.microbench import MetaBench
-
-def f1(x):
- return x
-
-def f2(x):
- return x+1
-
-def f3(x):
- return x+2
-
-def f4(x):
- return x+3
-
-FUNCS = [f1, f2, f3, f4]
-
-class indirect__call:
- __metaclass__ = MetaBench
-
- def init():
- return FUNCS
- args = ['obj', 'i']
- def loop(obj, i):
- return obj[i%4](i)
diff --git a/pypy/rpython/microbench/list.py b/pypy/rpython/microbench/list.py
deleted file mode 100644
--- a/pypy/rpython/microbench/list.py
+++ /dev/null
@@ -1,79 +0,0 @@
-from pypy.rpython.microbench.microbench import MetaBench
-
-class list__append:
- __metaclass__ = MetaBench
- def init():
- return []
- args = ['obj', 'i']
- def loop(obj, i):
- obj.append(i)
-
-class list__get_item:
- __metaclass__ = MetaBench
- LOOPS = 100000000
- def init():
- obj = []
- for i in xrange(1000):
- obj.append(i)
- return obj
- args = ['obj', 'i']
- def loop(obj, i):
- return obj[i%1000]
-
-class list__set_item:
- __metaclass__ = MetaBench
- LOOPS = 100000000
- def init():
- obj = []
- for i in xrange(1000):
- obj.append(i)
- return obj
- args = ['obj', 'i']
- def loop(obj, i):
- obj[i%1000] = i
-
-class fixed_list__get_item:
- __metaclass__ = MetaBench
- LOOPS = 100000000
- def init():
- return [0] * 1000
- args = ['obj', 'i']
- def loop(obj, i):
- return obj[i%1000]
-
-class fixed_list__set_item:
- __metaclass__ = MetaBench
- LOOPS = 100000000
- def init():
- return [0] * 1000
- args = ['obj', 'i']
- def loop(obj, i):
- obj[i%1000] = i
-
-class list__iteration__int:
- __metaclass__ = MetaBench
- LOOPS = 100000
- def init():
- obj = [0]*1000
- obj[0] = 42
- return obj
- args = ['obj']
- def loop(obj):
- tot = 0
- for item in obj:
- tot += item
- return tot
-
-class list__iteration__string:
- __metaclass__ = MetaBench
- LOOPS = 100000
- def init():
- obj = ['foo']*1000
- obj[0] = 'bar'
- return obj
- args = ['obj']
- def loop(obj):
- tot = 0
- for item in obj:
- tot += len(item)
- return tot
diff --git a/pypy/rpython/microbench/microbench.py b/pypy/rpython/microbench/microbench.py
deleted file mode 100644
--- a/pypy/rpython/microbench/microbench.py
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-import autopath
-from time import clock
-import subprocess
-from pypy.translator.interactive import Translation
-
-LOOPS = 10000000
-
-class MetaBench(type):
- def __new__(self, cls_name, bases, cls_dict):
- loop = cls_dict['loop']
- loop._dont_inline_ = True
- myglob = {
- 'init': cls_dict['init'],
- 'loop': loop,
- 'LOOPS': cls_dict.get('LOOPS', LOOPS),
- 'clock': clock,
- }
- args = ', '.join(cls_dict['args'])
- source = """
-def %(cls_name)s():
- obj = init()
- start = clock()
- for i in xrange(LOOPS):
- loop(%(args)s)
- return clock() - start
-""" % locals()
- exec source in myglob
- func = myglob[cls_name]
- func.benchmark = True
- return func
-
-
-def run_benchmark(exe):
- from pypy.translator.cli.test.runtest import CliFunctionWrapper
- from pypy.translator.jvm.test.runtest import JvmGeneratedSourceWrapper
-
- if exe.__class__ in [CliFunctionWrapper,JvmGeneratedSourceWrapper]:
- stdout, stderr, retval = exe.run()
- else:
- assert isinstance(exe, str)
- bench = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = bench.communicate()
- retval = bench.wait()
-
- if retval != 0:
- print 'Running benchmark failed'
- print 'Standard Output:'
- print stdout
- print '-' * 40
- print 'Standard Error:'
- print stderr
- raise SystemExit(-1)
-
- mydict = {}
- for line in stdout.splitlines():
- name, res = line.split(':')
- mydict[name.strip()] = float(res)
- return mydict
-
-def import_benchmarks():
- modules = sys.argv[1:]
- if len(modules) == 0:
- # import all the microbenchs
- from glob import glob
- for module in glob('*.py'):
- if module not in ('__init__.py', 'autopath.py', 'microbench.py'):
- modules.append(module)
-
- for module in modules:
- module = module.rstrip('.py')
- exec 'from %s import *' % module in globals()
-
-def main():
- import_benchmarks()
- benchmarks = []
- for name, thing in globals().iteritems():
- if getattr(thing, 'benchmark', False):
- benchmarks.append((name, thing))
- benchmarks.sort()
-
- def entry_point(argv):
- for name, func in benchmarks:
- print name, ':', func()
- return 0
-
- t = Translation(entry_point, standalone=True, backend='c')
- c_exe = t.compile()
- t = Translation(entry_point, standalone=True, backend='cli')
- cli_exe = t.compile()
- t = Translation(entry_point, standalone=True, backend='jvm')
- jvm_exe = t.compile()
-
- c_res = run_benchmark(c_exe)
- cli_res = run_benchmark(cli_exe)
- jvm_res = run_benchmark(jvm_exe)
-
- print 'benchmark genc gencli cli_ratio genjvm jvm_ratio'
- print
- for name, _ in benchmarks:
- c_time = c_res[name]
- cli_time = cli_res[name]
- jvm_time = jvm_res[name]
- if c_time == 0:
- cli_ratio = '%10s' % '---'
- else:
- cli_ratio = '%10.2f' % (cli_time/c_time)
- if c_time == 0:
- jvm_ratio = '%10s' % '---'
- else:
- jvm_ratio = '%10.2f' % (jvm_time/c_time)
- print '%-32s %10.2f %10.2f %s %10.2f %s' % (name, c_time, cli_time, cli_ratio, jvm_time, jvm_ratio)
-
-if __name__ == '__main__':
- main()
diff --git a/pypy/rpython/microbench/rdict.py b/pypy/rpython/microbench/rdict.py
deleted file mode 100644
--- a/pypy/rpython/microbench/rdict.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from pypy.rlib.objectmodel import r_dict
-from pypy.rpython.microbench.microbench import MetaBench
-
-class Obj:
- def __init__(self, x):
- self.x = x
-
-def myhash(obj):
- return obj.x
-
-def mycmp(obj1, obj2):
- return obj1.x == obj2.x
-
-class Space:
- def myhash(self, obj):
- return obj.x
-
- def mycmp(self, obj1, obj2):
- return obj1.x == obj2.x
-
- def _freeze_(self):
- return True
-
-space = Space()
-obj1 = Obj(1)
-obj2 = Obj(2)
-
-class r_dict__set_item:
- __metaclass__ = MetaBench
-
- def init():
- return r_dict(mycmp, myhash)
- args = ['obj', 'i']
- def loop(obj, i):
- obj[obj1] = i
- obj[obj2] = i
-
-class r_dict__get_item:
- __metaclass__ = MetaBench
-
- def init():
- res = r_dict(mycmp, myhash)
- res[obj1] = 42
- res[obj2] = 43
- return res
- args = ['obj', 'i']
- def loop(obj, i):
- return obj[obj1] + obj[obj2]
-
-class r_dict__frozen_pbc__set_item:
- __metaclass__ = MetaBench
-
- def init():
- return r_dict(space.mycmp, space.myhash)
- args = ['obj', 'i']
- def loop(obj, i):
- obj[obj1] = i
- obj[obj2] = i
-
-class r_dict__frozen_pbc__get_item:
- __metaclass__ = MetaBench
-
- def init():
- res = r_dict(space.mycmp, space.myhash)
- res[obj1] = 42
- res[obj2] = 43
- return res
- args = ['obj', 'i']
- def loop(obj, i):
- return obj[obj1] + obj[obj2]
diff --git a/pypy/translator/generator.py b/pypy/translator/generator.py
deleted file mode 100644
--- a/pypy/translator/generator.py
+++ /dev/null
@@ -1,184 +0,0 @@
-from pypy.objspace.flow.model import Block, Link, SpaceOperation, checkgraph
-from pypy.objspace.flow.model import Variable, Constant, FunctionGraph
-from pypy.translator.unsimplify import insert_empty_startblock
-from pypy.translator.unsimplify import split_block
-from pypy.translator.simplify import eliminate_empty_blocks, simplify_graph
-from pypy.tool.sourcetools import func_with_new_name
-from pypy.interpreter.argument import Signature
-
-
-class AbstractPosition(object):
- _immutable_ = True
- _attrs_ = ()
-
-
-def tweak_generator_graph(graph):
- if not hasattr(graph.func, '_generator_next_method_of_'):
- # This is the first copy of the graph. We replace it with
- # a small bootstrap graph.
- GeneratorIterator = make_generatoriterator_class(graph)
- replace_graph_with_bootstrap(GeneratorIterator, graph)
- # We attach a 'next' method to the GeneratorIterator class
- # that will invoke the real function, based on a second
- # copy of the graph.
- attach_next_method(GeneratorIterator, graph)
- else:
- # This is the second copy of the graph. Tweak it.
- GeneratorIterator = graph.func._generator_next_method_of_
- tweak_generator_body_graph(GeneratorIterator.Entry, graph)
-
-
-def make_generatoriterator_class(graph):
- class GeneratorIterator(object):
- class Entry(AbstractPosition):
- _immutable_ = True
- varnames = get_variable_names(graph.startblock.inputargs)
-
- def __init__(self, entry):
- self.current = entry
-
- def __iter__(self):
- return self
-
- return GeneratorIterator
-
-def replace_graph_with_bootstrap(GeneratorIterator, graph):
- Entry = GeneratorIterator.Entry
- newblock = Block(graph.startblock.inputargs)
- v_generator = Variable('generator')
- v_entry = Variable('entry')
- newblock.operations.append(
- SpaceOperation('simple_call', [Constant(Entry)], v_entry))
- assert len(graph.startblock.inputargs) == len(Entry.varnames)
- for v, name in zip(graph.startblock.inputargs, Entry.varnames):
- newblock.operations.append(
- SpaceOperation('setattr', [v_entry, Constant(name), v],
- Variable()))
- newblock.operations.append(
- SpaceOperation('simple_call', [Constant(GeneratorIterator), v_entry],
- v_generator))
- newblock.closeblock(Link([v_generator], graph.returnblock))
- graph.startblock = newblock
-
-def attach_next_method(GeneratorIterator, graph):
- func = graph.func
- func = func_with_new_name(func, '%s__next' % (func.func_name,))
- func._generator_next_method_of_ = GeneratorIterator
- func._always_inline_ = True
- #
- def next(self):
- entry = self.current
- self.current = None
- assert entry is not None # else, recursive generator invocation
- (next_entry, return_value) = func(entry)
- self.current = next_entry
- return return_value
- GeneratorIterator.next = next
- return func # for debugging
-
-def get_variable_names(variables):
- seen = set()
- result = []
- for v in variables:
- name = v._name.strip('_')
- while name in seen:
- name += '_'
- result.append('g_' + name)
- seen.add(name)
- return result
-
-def _insert_reads(block, varnames):
- assert len(varnames) == len(block.inputargs)
- v_entry1 = Variable('entry')
- for i, name in enumerate(varnames):
- block.operations.insert(i,
- SpaceOperation('getattr', [v_entry1, Constant(name)],
- block.inputargs[i]))
- block.inputargs = [v_entry1]
-
-def tweak_generator_body_graph(Entry, graph):
- # First, always run simplify_graph in order to reduce the number of
- # variables passed around
- simplify_graph(graph)
- #
- assert graph.startblock.operations[0].opname == 'generator_mark'
- graph.startblock.operations.pop(0)
- #
- insert_empty_startblock(None, graph)
- _insert_reads(graph.startblock, Entry.varnames)
- Entry.block = graph.startblock
- #
- mappings = [Entry]
- #
- stopblock = Block([])
- v0 = Variable(); v1 = Variable()
- stopblock.operations = [
- SpaceOperation('simple_call', [Constant(StopIteration)], v0),
- SpaceOperation('type', [v0], v1),
- ]
- stopblock.closeblock(Link([v1, v0], graph.exceptblock))
- #
- for block in list(graph.iterblocks()):
- for exit in block.exits:
- if exit.target is graph.returnblock:
- exit.args = []
- exit.target = stopblock
- assert block is not stopblock
- for index in range(len(block.operations)-1, -1, -1):
- op = block.operations[index]
- if op.opname == 'yield':
- [v_yielded_value] = op.args
- del block.operations[index]
- newlink = split_block(None, block, index)
- newblock = newlink.target
- #
- class Resume(AbstractPosition):
- _immutable_ = True
- block = newblock
- Resume.__name__ = 'Resume%d' % len(mappings)
- mappings.append(Resume)
- varnames = get_variable_names(newlink.args)
- #
- _insert_reads(newblock, varnames)
- #
- v_resume = Variable('resume')
- block.operations.append(
- SpaceOperation('simple_call', [Constant(Resume)],
- v_resume))
- for i, name in enumerate(varnames):
More information about the pypy-commit
mailing list