[pypy-svn] pypy jit-fromstart: Allow the entire optimizer and its state to be cloned to allow the optimization to be resumed at any point (work in progress). This is used by the unroller to optimize the loop twice. The second time around the full set of inputargs are known from the start and these can be passed on to the bridges through the failargs.
hakanardo
commits-noreply at bitbucket.org
Tue Feb 1 20:08:54 CET 2011
Author: Hakan Ardo <hakan at debian.org>
Branch: jit-fromstart
Changeset: r41529:9d324e858411
Date: 2011-02-01 20:08 +0100
http://bitbucket.org/pypy/pypy/changeset/9d324e858411/
Log: Allow the entire optimizer and its state to be cloned to allow the
optimization to be resumed at any point (work in progress). This is
used by the unroller to optimize the loop twice. The second time
around the full set of inputargs are known from the start and these
can be passed on to the bridges through the failargs.
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ b/pypy/jit/metainterp/optimizeopt/fficall.py
@@ -67,7 +67,7 @@
def __init__(self):
self.funcinfo = None
- def reconstruct_for_next_iteration(self, optimizer, valuemap):
+ def clone_for_next_iteration(self, optimizer, valuemap):
return OptFfiCall()
# FIXME: Should any status be saved for next iteration?
diff --git a/pypy/jit/metainterp/optimizeopt/string.py b/pypy/jit/metainterp/optimizeopt/string.py
--- a/pypy/jit/metainterp/optimizeopt/string.py
+++ b/pypy/jit/metainterp/optimizeopt/string.py
@@ -367,9 +367,8 @@
"Handling of strings and unicodes."
enabled = True
- def reconstruct_for_next_iteration(self, optimizer, valuemap):
- self.enabled = True
- return self
+ def clone_for_next_iteration(self, optimizer, valuemap):
+ return OptString()
def make_vstring_plain(self, box, source_op, mode):
vvalue = VStringPlainValue(self.optimizer, box, source_op, mode)
diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -155,61 +155,29 @@
if jumpop:
assert jumpop.getdescr() is loop.token
loop.preamble.operations = self.optimizer.newoperations
- self.optimizer = self.optimizer.reconstruct_for_next_iteration()
+ jump_args = jumpop.getarglist()
+ jumpop.initarglist([])
- jump_args = jumpop.getarglist()
- #for i in range(len(jump_args)):
- # self.getvalue(jump_args[i]).start_index = i
-
- jumpop.initarglist([])
+ preamble_optmizer = self.optimizer
+ self.optimizer = preamble_optmizer.clone_for_next_iteration()
inputargs = self.inline(self.cloned_operations,
loop.inputargs, jump_args)
+
+ loop_inputargs = loop.inputargs
loop.inputargs = inputargs
jmp = ResOperation(rop.JUMP, loop.inputargs[:], None)
jmp.setdescr(loop.token)
loop.preamble.operations.append(jmp)
-
loop.operations = self.optimizer.newoperations
- start_resumedescr = loop.preamble.start_resumedescr.clone_if_mutable()
- assert isinstance(start_resumedescr, ResumeGuardDescr)
- snapshot = start_resumedescr.rd_snapshot
- while snapshot is not None:
- snapshot_args = snapshot.boxes
- new_snapshot_args = []
- for a in snapshot_args:
- if not isinstance(a, Const):
- a = loop.preamble.inputargs[jump_args.index(a)]
- new_snapshot_args.append(a)
- snapshot.boxes = new_snapshot_args
- snapshot = snapshot.prev
-
- short = self.create_short_preamble(loop.preamble, loop)
- if short:
+ short_loop = self.create_short_preamble(loop.preamble, loop,
+ jump_args)
+ if short_loop:
if False:
# FIXME: This should save some memory but requires
- # a lot of tests to be fixed...
- loop.preamble.operations = short[:]
-
- # Turn guards into conditional jumps to the preamble
- for i in range(len(short)):
- op = short[i]
- if op.is_guard():
- op = op.clone()
- op.setfailargs(None)
- op.setdescr(start_resumedescr.clone_if_mutable())
- short[i] = op
-
- short_loop = TreeLoop('short preamble')
- short_loop.inputargs = loop.preamble.inputargs[:]
- short_loop.operations = short
-
- # Clone ops and boxes to get private versions and
- newargs = [a.clonebox() for a in short_loop.inputargs]
- inliner = Inliner(short_loop.inputargs, newargs)
- short_loop.inputargs = newargs
- ops = [inliner.inline_op(op) for op in short_loop.operations]
- short_loop.operations = ops
+ # a lot of tests to be fixed, and the guards to be
+ # replaced with ResumeGuardDescr
+ loop.preamble.operations = short_loop.operations[:]
assert isinstance(loop.preamble.token, LoopToken)
if loop.preamble.token.short_preamble:
@@ -217,12 +185,14 @@
else:
loop.preamble.token.short_preamble = [short_loop]
- # Forget the values to allow them to be freed
- for box in short_loop.inputargs:
- box.forget_value()
- for op in short_loop.operations:
- if op.result:
- op.result.forget_value()
+ if True:
+ self.optimizer = \
+ preamble_optmizer.clone_for_next_iteration()
+ self.optimizer.extraargs = loop.inputargs
+ loop.inputargs = self.inline(self.cloned_operations,
+ loop_inputargs, jump_args)
+ assert loop.inputargs == inputargs
+ loop.operations = self.optimizer.newoperations
for op in loop.operations:
if op.is_guard():
@@ -235,6 +205,21 @@
i = -1
descr.start_indexes.append(i)
descr.parent_short_preamble = short_loop
+
+ # Clone ops and boxes to get private versions
+ newargs = [a.clonebox() for a in short_loop.inputargs]
+ inliner = Inliner(short_loop.inputargs, newargs)
+ short_loop.inputargs = newargs
+ ops = [inliner.inline_op(op) for op in short_loop.operations]
+ short_loop.operations = ops
+
+ # Forget the values to allow them to be freed
+ for box in short_loop.inputargs:
+ box.forget_value()
+ for op in short_loop.operations:
+ if op.result:
+ op.result.forget_value()
+
@@ -242,10 +227,7 @@
def inline(self, loop_operations, loop_args, jump_args):
self.inliner = inliner = Inliner(loop_args, jump_args)
- for v in self.optimizer.values.values():
- v.last_guard_index = -1 # FIXME: Are there any more indexes stored?
-
- self.optimizer.extraargs = inputargs = []
+ inputargs = []
seen_inputargs = {}
for arg in jump_args:
boxes = []
@@ -257,30 +239,13 @@
# This loop is equivalent to the main optimization loop in
# Optimizer.propagate_all_forward
boxes_created_this_iteration = {}
- last_emitted_len = -1
for newop in loop_operations:
if newop.getopnum() == rop.JUMP:
newop.initarglist(inputargs)
- newop = inliner.inline_op(newop, clone=False)
+ newop = inliner.inline_op(newop) #, clone=False)
self.optimizer.first_optimization.propagate_forward(newop)
- # This should take care of updating inputargs in most cases i.e.
- # when newoperations is not reorder too much. The rest should be
- # pacthed below. Bridges will only be able to inherit the part of
- # the short preamble that produces the boxes that are placed in
- # inputargs before the guard producing the bridge is emitted.
- i = max(last_emitted_len - 2, 0)
- while i < len(self.optimizer.newoperations):
- op = self.optimizer.newoperations[i]
- boxes_created_this_iteration[op.result] = True
- for a in op.getarglist():
- if not isinstance(a, Const) and not a in boxes_created_this_iteration:
- if a not in inputargs:
- inputargs.append(a)
- i += 1
- last_emitted_len = len(self.optimizer.newoperations)
-
# Remove jump to make sure forced code are placed before it
newoperations = self.optimizer.newoperations
jmp = newoperations[-1]
@@ -335,7 +300,7 @@
return True
- def create_short_preamble(self, preamble, loop):
+ def create_short_preamble(self, preamble, loop, original_jump_args):
#return None # Dissable
preamble_ops = preamble.operations
@@ -417,8 +382,36 @@
return None
if op.result:
seen[op.result] = True
-
- return short_preamble
+
+ # Make all guards resume at the top of the preamble
+ start_resumedescr = loop.preamble.start_resumedescr
+ start_resumedescr = start_resumedescr.clone_if_mutable()
+ assert isinstance(start_resumedescr, ResumeGuardDescr)
+ snapshot = start_resumedescr.rd_snapshot
+ while snapshot is not None:
+ snapshot_args = snapshot.boxes
+ new_snapshot_args = []
+ for a in snapshot_args:
+ if not isinstance(a, Const):
+ a = loop.preamble.inputargs[original_jump_args.index(a)]
+ new_snapshot_args.append(a)
+ snapshot.boxes = new_snapshot_args
+ snapshot = snapshot.prev
+
+ for i in range(len(short_preamble)):
+ op = short_preamble[i]
+ if op.is_guard():
+ op = op.clone()
+ op.setfailargs(None)
+ op.setdescr(start_resumedescr.clone_if_mutable())
+ short_preamble[i] = op
+
+ # Contruct the TreeLoop object
+ short_loop = TreeLoop('short preamble')
+ short_loop.inputargs = loop.preamble.inputargs[:]
+ short_loop.operations = short_preamble
+
+ return short_loop
class ExeState(object):
def __init__(self, optimizer):
@@ -543,8 +536,8 @@
return self.map[loopbox]
class OptInlineShortPreamble(Optimization):
- def reconstruct_for_next_iteration(self, optimizer, valuemap):
- return self
+ def clone_for_next_iteration(self, optimizer, valuemap):
+ return OptInlineShortPreamble()
def propagate_forward(self, op):
if op.getopnum() == rop.JUMP:
diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -24,8 +24,10 @@
self.lazy_setfields = {}
self.lazy_setfields_descrs = [] # keys (at least) of previous dict
- def reconstruct_for_next_iteration(self, optimizer, valuemap):
+ def clone_for_next_iteration(self, optimizer, valuemap):
new = OptHeap()
+ return new
+ # FIXME:
if True:
self.force_all_lazy_setfields()
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -13,8 +13,8 @@
This includes already executed operations and constants.
"""
- def reconstruct_for_next_iteration(self, optimizer, valuemap):
- return self
+ def clone_for_next_iteration(self, optimizer, valuemap):
+ return OptRewrite()
def propagate_forward(self, op):
args = self.optimizer.make_args_key(op)
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -27,15 +27,19 @@
__metaclass__ = extendabletype
_attrs_ = ('box', 'known_class', 'last_guard_index', 'level', 'intbound')
last_guard_index = -1
- #start_index = -1
level = LEVEL_UNKNOWN
known_class = None
intbound = None
- def __init__(self, box):
+ def __init__(self, box, level=None, known_class=None, intbound=None):
self.box = box
- self.intbound = IntBound(MININT, MAXINT) #IntUnbounded()
+ self.level = level
+ self.known_class = known_class
+ if intbound:
+ self.intbound = intbound
+ else:
+ self.intbound = IntBound(MININT, MAXINT) #IntUnbounded()
if isinstance(box, Const):
self.make_constant(box)
# invariant: box is a Const if and only if level == LEVEL_CONSTANT
@@ -55,13 +59,17 @@
def get_reconstructed(self, optimizer, valuemap):
if self in valuemap:
return valuemap[self]
- new = self.reconstruct_for_next_iteration(optimizer)
+ new = self.clone_for_next_iteration(optimizer)
+ assert new.__class__ == self.__class__
valuemap[self] = new
self.reconstruct_childs(new, valuemap)
return new
- def reconstruct_for_next_iteration(self, optimizer):
- return self
+ def clone_for_next_iteration(self, optimizer):
+ # last_guard_index should not be propagated here as the list into
+ # which it is an index is cleared
+ return OptValue(self.box, self.level, self.known_class,
+ self.intbound.clone())
def reconstruct_childs(self, new, valuemap):
pass
@@ -224,8 +232,7 @@
def turned_constant(self, value):
pass
- def reconstruct_for_next_iteration(self, optimizer=None, valuemap=None):
- #return self.__class__()
+ def clone_for_next_iteration(self, optimizer=None, valuemap=None):
raise NotImplementedError
@@ -304,31 +311,39 @@
for o in self.optimizations:
o.force_at_end_of_preamble()
- def reconstruct_for_next_iteration(self, optimizer=None, valuemap=None):
+ def clone_for_next_iteration(self, optimizer=None, valuemap=None):
assert optimizer is None
assert valuemap is None
valuemap = {}
new = Optimizer(self.metainterp_sd, self.loop)
- optimizations = [o.reconstruct_for_next_iteration(new, valuemap) for o in
+ new.values = {}
+ optimizations = [o.clone_for_next_iteration(new, valuemap) for o in
self.optimizations]
new.set_optimizations(optimizations)
- new.values = {}
- for box, value in self.values.items():
- new.values[box] = value.get_reconstructed(new, valuemap)
- new.interned_refs = self.interned_refs
- new.bool_boxes = {}
- for value in new.bool_boxes.keys():
- new.bool_boxes[value.get_reconstructed(new, valuemap)] = None
+ # FIXME: new.interned_refs = self.interned_refs
+ # FIXME:
+ #new.bool_boxes = {}
+ #for value in new.bool_boxes.keys():
+ # new.bool_boxes[value.get_reconstructed(new, valuemap)] = None
# FIXME: Move to rewrite.py
new.loop_invariant_results = {}
for key, value in self.loop_invariant_results.items():
new.loop_invariant_results[key] = \
value.get_reconstructed(new, valuemap)
+
+ for args, op in self.pure_operations.items():
+ newargs = args[:]
+ for i in range(len(newargs)):
+ if isinstance(newargs[i], OptValue):
+ newargs[i] = newargs[i].get_reconstructed(new, valuemap)
+ v = self.getvalue(op.result)
+ new.values[op.result] = v.get_reconstructed(new, valuemap)
+ new.pure_operations[newargs] = op
+ # FIXME: This will not work for ops with mutable descr
- new.pure_operations = self.pure_operations
- new.producer = self.producer
+ # FIXME: Any point in propagating these? new.producer = self.producer
assert self.posponedop is None
return new
@@ -539,14 +554,15 @@
# all constant arguments: constant-fold away
argboxes = [self.get_constant_box(op.getarg(i))
for i in range(op.numargs())]
- resbox = execute_nonspec(self.cpu, None,
- op.getopnum(), argboxes, op.getdescr())
+ resbox = execute_nonspec(self.cpu, None, op.getopnum(),
+ argboxes, op.getdescr())
# FIXME: Don't we need to check for an overflow here?
self.make_constant(op.result, resbox.constbox())
return
# did we do the exact same operation already?
args = self.make_args_key(op)
+
oldop = self.pure_operations.get(args, None)
if oldop is not None and oldop.getdescr() is op.getdescr():
assert oldop.getopnum() == op.getopnum()
diff --git a/pypy/jit/metainterp/test/test_basic.py b/pypy/jit/metainterp/test/test_basic.py
--- a/pypy/jit/metainterp/test/test_basic.py
+++ b/pypy/jit/metainterp/test/test_basic.py
@@ -403,6 +403,26 @@
'jump': 2,
'int_gt': 1, 'guard_true': 1})
+ def test_loop_invariant_mul_bridge_maintaining3(self):
+ myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
+ def f(x, y):
+ res = 0
+ while y > 0:
+ myjitdriver.can_enter_jit(x=x, y=y, res=res)
+ myjitdriver.jit_merge_point(x=x, y=y, res=res)
+ if y<16:
+ res += 1
+ res += x * x
+ y -= 1
+ return res
+ res = self.meta_interp(f, [6, 32])
+ assert res == 1167
+ self.check_loop_count(3)
+ self.check_loops({'int_add': 3, 'int_lt': 1,
+ 'int_sub': 2, 'guard_false': 1,
+ 'jump': 2,
+ 'int_gt': 1, 'guard_true': 1})
+
def test_loop_invariant_intbox(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
class I:
diff --git a/pypy/jit/metainterp/optimizeopt/intbounds.py b/pypy/jit/metainterp/optimizeopt/intbounds.py
--- a/pypy/jit/metainterp/optimizeopt/intbounds.py
+++ b/pypy/jit/metainterp/optimizeopt/intbounds.py
@@ -13,9 +13,9 @@
self.posponedop = None
self.nextop = None
- def reconstruct_for_next_iteration(self, optimizer, valuemap):
+ def clone_for_next_iteration(self, optimizer, valuemap):
assert self.posponedop is None
- return self
+ return OptIntBounds()
def propagate_forward(self, op):
if op.is_ovf():
diff --git a/pypy/jit/metainterp/optimizeopt/intutils.py b/pypy/jit/metainterp/optimizeopt/intutils.py
--- a/pypy/jit/metainterp/optimizeopt/intutils.py
+++ b/pypy/jit/metainterp/optimizeopt/intutils.py
@@ -76,7 +76,7 @@
return r
def add(self, offset):
- res = self.copy()
+ res = self.clone()
try:
res.lower = ovfcheck(res.lower + offset)
except OverflowError:
@@ -91,7 +91,7 @@
return self.mul_bound(IntBound(value, value))
def add_bound(self, other):
- res = self.copy()
+ res = self.clone()
if other.has_upper:
try:
res.upper = ovfcheck(res.upper + other.upper)
@@ -109,7 +109,7 @@
return res
def sub_bound(self, other):
- res = self.copy()
+ res = self.clone()
if other.has_lower:
try:
res.upper = ovfcheck(res.upper - other.lower)
@@ -204,7 +204,7 @@
u = 'Inf'
return '%s <= x <= %s' % (l, u)
- def copy(self):
+ def clone(self):
res = IntBound(self.lower, self.upper)
res.has_lower = self.has_lower
res.has_upper = self.has_upper
diff --git a/pypy/jit/metainterp/optimizeopt/virtualize.py b/pypy/jit/metainterp/optimizeopt/virtualize.py
--- a/pypy/jit/metainterp/optimizeopt/virtualize.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualize.py
@@ -252,8 +252,8 @@
class OptVirtualize(optimizer.Optimization):
"Virtualize objects until they escape."
- def reconstruct_for_next_iteration(self, optimizer, valuemap):
- return self
+ def clone_for_next_iteration(self, optimizer, valuemap):
+ return OptVirtualize()
def make_virtual(self, known_class, box, source_op=None):
vvalue = VirtualValue(self.optimizer, known_class, box, source_op)
More information about the Pypy-commit
mailing list