[pypy-svn] r9657 - in pypy/dist/pypy/objspace/flow: . test
arigo at codespeak.net
arigo at codespeak.net
Sat Mar 5 15:27:01 CET 2005
Author: arigo
Date: Sat Mar 5 15:27:00 2005
New Revision: 9657
Modified:
pypy/dist/pypy/objspace/flow/flowcontext.py
pypy/dist/pypy/objspace/flow/framestate.py
pypy/dist/pypy/objspace/flow/test/test_objspace.py
Log:
Dynamic merge point creation, just before any generated operation. This
enables a kind of automatic specialization in code where several
constants can enter.
Modified: pypy/dist/pypy/objspace/flow/flowcontext.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/flowcontext.py (original)
+++ pypy/dist/pypy/objspace/flow/flowcontext.py Sat Mar 5 15:27:00 2005
@@ -1,5 +1,4 @@
from pypy.interpreter.executioncontext import ExecutionContext
-from pypy.interpreter.pyframe import ExitFrame
from pypy.interpreter.error import OperationError
from pypy.objspace.flow.model import *
from pypy.objspace.flow.framestate import FrameState
@@ -8,6 +7,12 @@
class OperationThatShouldNotBePropagatedError(OperationError):
pass
+class StopFlowing(Exception):
+ pass
+
+class MergeBlock(Exception):
+ pass
+
class SpamBlock(Block):
dead = False
@@ -18,7 +23,7 @@
def patchframe(self, frame):
if self.dead:
- raise ExitFrame(None)
+ raise StopFlowing
self.framestate.restoreframe(frame)
return BlockRecorder(self)
@@ -64,54 +69,34 @@
def __init__(self, block):
self.crnt_block = block
+ # saved state at the join point most recently seen
+ self.last_join_point = None
+ self.progress = False
def append(self, operation):
+ if self.last_join_point is not None:
+ # don't add more operations if we already crossed a join point
+ raise MergeBlock(self.crnt_block, self.last_join_point)
self.crnt_block.operations.append(operation)
def bytecode_trace(self, ec, frame):
assert frame is ec.crnt_frame, "seeing an unexpected frame!"
next_instr = frame.next_instr
- ec.crnt_offset = next_instr # save offset for opcode
- varnames = frame.code.getvarnames()
- for name, w_value in zip(varnames, frame.getfastscope()):
- if isinstance(w_value, Variable):
- w_value.rename(name)
- if next_instr in ec.joinpoints:
- currentstate = FrameState(frame)
- # can 'currentstate' be merged with one of the blocks that
- # already exist for this bytecode position?
- for block in ec.joinpoints[next_instr]:
- newstate = block.framestate.union(currentstate)
- if newstate is not None:
- # yes
- finished = newstate == block.framestate
- break
- else:
- # no
- newstate = currentstate.copy()
- finished = False
- block = None
-
- if finished:
- newblock = block
- else:
- newblock = SpamBlock(newstate)
- # unconditionally link the current block to the newblock
- outputargs = currentstate.getoutputargs(newstate)
- self.crnt_block.closeblock(Link(outputargs, newblock))
- # phew
- if finished:
- raise ExitFrame(None)
- if block is not None and block.exits:
- # 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))
- ec.recorder = newblock.patchframe(frame)
- ec.joinpoints[next_instr].insert(0, newblock)
+ if self.crnt_block.operations or isinstance(self.crnt_block, EggBlock):
+ # if we have already produced at least one operation or a branch,
+ # make a join point as early as possible (but not before we
+ # actually try to generate more operations)
+ self.last_join_point = FrameState(frame)
+ else:
+ ec.crnt_offset = next_instr # save offset for opcode
+ varnames = frame.code.getvarnames()
+ for name, w_value in zip(varnames, frame.getfastscope()):
+ if isinstance(w_value, Variable):
+ w_value.rename(name)
+ # record passage over already-existing join points
+ if self.progress and next_instr in ec.joinpoints:
+ self.last_join_point = FrameState(frame)
+ self.progress = True
def guessbool(self, ec, w_condition, cases=[False,True],
replace_last_variable_except_in_first_case = None):
@@ -137,7 +122,7 @@
# in the exits tuple so that (just in case we need it) we
# actually have block.exits[False] = elseLink and
# block.exits[True] = ifLink.
- raise ExitFrame(None)
+ raise StopFlowing
class Replayer(Recorder):
@@ -198,8 +183,8 @@
arg_list[position] = Constant(value)
frame.setfastscope(arg_list)
self.joinpoints = {}
- for joinpoint in code.getjoinpoints():
- self.joinpoints[joinpoint] = [] # list of blocks
+ #for joinpoint in code.getjoinpoints():
+ # self.joinpoints[joinpoint] = [] # list of blocks
initialblock = SpamBlock(FrameState(frame).copy())
self.pendingblocks = [initialblock]
self.graph = FunctionGraph(name or code.co_name, initialblock)
@@ -236,7 +221,7 @@
frame = self.create_frame()
try:
self.recorder = block.patchframe(frame)
- except ExitFrame:
+ except StopFlowing:
continue # restarting a dead SpamBlock
try:
self.crnt_frame = frame
@@ -244,18 +229,29 @@
w_result = frame.resume()
finally:
self.crnt_frame = None
+
except OperationThatShouldNotBePropagatedError, e:
raise Exception(
'found an operation that always raises %s: %s' % (
self.space.unwrap(e.w_type).__name__,
self.space.unwrap(e.w_value)))
+
except OperationError, e:
link = Link([e.w_type, e.w_value], self.graph.exceptblock)
self.recorder.crnt_block.closeblock(link)
+
+ except StopFlowing:
+ pass
+
+ except MergeBlock, e:
+ block, currentstate = e.args
+ self.mergeblock(block, currentstate)
+
else:
- if w_result is not None:
- link = Link([w_result], self.graph.returnblock)
- self.recorder.crnt_block.closeblock(link)
+ assert w_result is not None
+ link = Link([w_result], self.graph.returnblock)
+ self.recorder.crnt_block.closeblock(link)
+
del self.recorder
self.fixeggblocks()
@@ -270,3 +266,40 @@
mapping[a] = Variable(a)
node.renamevariables(mapping)
traverse(fixegg, self.graph)
+
+ def mergeblock(self, currentblock, currentstate):
+ next_instr = currentstate.next_instr
+ # can 'currentstate' be merged with one of the blocks that
+ # already exist for this bytecode position?
+ 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
+ break
+ else:
+ # no
+ newstate = currentstate.copy()
+ finished = False
+ block = None
+
+ if finished:
+ newblock = block
+ else:
+ newblock = SpamBlock(newstate)
+ # unconditionally link the current block to the newblock
+ outputargs = currentstate.getoutputargs(newstate)
+ currentblock.closeblock(Link(outputargs, newblock))
+ # phew
+ if not finished:
+ if block is not None and block.exits:
+ # 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.insert(0, newblock)
+ self.pendingblocks.append(newblock)
Modified: pypy/dist/pypy/objspace/flow/framestate.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/framestate.py (original)
+++ pypy/dist/pypy/objspace/flow/framestate.py Sat Mar 5 15:27:00 2005
@@ -32,6 +32,7 @@
else:
raise TypeError("can't get framestate for %r" %
state.__class__.__name__)
+ self.next_instr = self.nonmergeable[1]
for w1 in self.mergeable:
assert isinstance(w1, (Variable, Constant)), (
'%r found in frame state' % w1)
Modified: pypy/dist/pypy/objspace/flow/test/test_objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/test/test_objspace.py (original)
+++ pypy/dist/pypy/objspace/flow/test/test_objspace.py Sat Mar 5 15:27:00 2005
@@ -1,5 +1,5 @@
import autopath
-from pypy.objspace.flow.model import Constant
+from pypy.objspace.flow.model import Constant, Block, traverse
from pypy.interpreter.argument import Arguments
@@ -338,6 +338,8 @@
def test_specialcases(self):
x = self.codetest(self.specialcases)
+ from pypy.translator.simplify import join_blocks
+ join_blocks(x)
assert len(x.startblock.operations) == 13
for op in x.startblock.operations:
assert op.opname in ['lt', 'le', 'eq', 'ne',
@@ -346,6 +348,23 @@
assert op.args[0].value == 2
assert op.args[1].value == 3
+ #__________________________________________________________
+ def jump_target_specialization(x):
+ if x:
+ n = 5
+ else:
+ n = 6
+ return n*2
+
+ def test_jump_target_specialization(self):
+ x = self.codetest(self.jump_target_specialization)
+ self.show(x)
+ def visitor(node):
+ if isinstance(node, Block):
+ for op in node.operations:
+ assert op.opname != 'mul', "mul should have disappeared"
+ traverse(visitor, x)
+
DATA = {'x': 5,
'y': 6}
More information about the Pypy-commit
mailing list