[pypy-commit] pypy default: merge small-unroll-improvements
cfbolz
noreply at buildbot.pypy.org
Tue Apr 22 11:47:17 CEST 2014
Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch:
Changeset: r70851:82141f03207d
Date: 2014-04-22 11:46 +0200
http://bitbucket.org/pypy/pypy/changeset/82141f03207d/
Log: merge small-unroll-improvements
a cleanup and generalization of unroll, in particularly the virtual
state handling. reduces code duplication and various hacks. Fixes a
few rare miscompiles. This also improves optimization by
generalizing a few matching cases in the virtualstate matching.
diff too long, truncating to 2000 out of 2662 lines
diff --git a/pypy/doc/whatsnew-2.3.0.rst b/pypy/doc/whatsnew-2.3.0.rst
--- a/pypy/doc/whatsnew-2.3.0.rst
+++ b/pypy/doc/whatsnew-2.3.0.rst
@@ -149,3 +149,6 @@
.. branch: openbsd-lib-prefix
add 'lib' prefix to link libraries on OpenBSD
+
+.. branch: small-unroll-improvements
+Improve optimiziation of small allocation-heavy loops in the JIT
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -5,3 +5,7 @@
.. this is a revision shortly after release-2.3.x
.. startrev: ba569fe1efdb
+
+
+.. branch: small-unroll-improvements
+Improve optimiziation of small allocation-heavy loops in the JIT
diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py
--- a/rpython/jit/metainterp/compile.py
+++ b/rpython/jit/metainterp/compile.py
@@ -106,7 +106,7 @@
def compile_loop(metainterp, greenkey, start,
inputargs, jumpargs,
- resume_at_jump_descr, full_preamble_needed=True,
+ full_preamble_needed=True,
try_disabling_unroll=False):
"""Try to compile a new procedure by closing the current history back
to the first operation.
@@ -128,7 +128,6 @@
part = create_empty_loop(metainterp)
part.inputargs = inputargs[:]
h_ops = history.operations
- part.resume_at_jump_descr = resume_at_jump_descr
part.operations = [ResOperation(rop.LABEL, inputargs, None, descr=TargetToken(jitcell_token))] + \
[h_ops[i].clone() for i in range(start, len(h_ops))] + \
[ResOperation(rop.LABEL, jumpargs, None, descr=jitcell_token)]
@@ -187,7 +186,7 @@
def compile_retrace(metainterp, greenkey, start,
inputargs, jumpargs,
- resume_at_jump_descr, partial_trace, resumekey):
+ partial_trace, resumekey):
"""Try to compile a new procedure by closing the current history back
to the first operation.
"""
@@ -203,7 +202,6 @@
part = create_empty_loop(metainterp)
part.inputargs = inputargs[:]
- part.resume_at_jump_descr = resume_at_jump_descr
h_ops = history.operations
part.operations = [partial_trace.operations[-1]] + \
@@ -765,7 +763,7 @@
metainterp_sd.stats.add_jitcell_token(jitcell_token)
-def compile_trace(metainterp, resumekey, resume_at_jump_descr=None):
+def compile_trace(metainterp, resumekey):
"""Try to compile a new bridge leading from the beginning of the history
to some existing place.
"""
@@ -781,7 +779,6 @@
# clone ops, as optimize_bridge can mutate the ops
new_trace.operations = [op.clone() for op in metainterp.history.operations]
- new_trace.resume_at_jump_descr = resume_at_jump_descr
metainterp_sd = metainterp.staticdata
state = metainterp.jitdriver_sd.warmstate
if isinstance(resumekey, ResumeAtPositionDescr):
diff --git a/rpython/jit/metainterp/history.py b/rpython/jit/metainterp/history.py
--- a/rpython/jit/metainterp/history.py
+++ b/rpython/jit/metainterp/history.py
@@ -628,7 +628,6 @@
call_pure_results = None
logops = None
quasi_immutable_deps = None
- resume_at_jump_descr = None
def _token(*args):
raise Exception("TreeLoop.token is killed")
diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py b/rpython/jit/metainterp/optimizeopt/optimizer.py
--- a/rpython/jit/metainterp/optimizeopt/optimizer.py
+++ b/rpython/jit/metainterp/optimizeopt/optimizer.py
@@ -31,6 +31,12 @@
def clone(self):
return LenBound(self.mode, self.descr, self.bound.clone())
+ def generalization_of(self, other):
+ return (other is not None and
+ self.mode == other.mode and
+ self.descr == other.descr and
+ self.bound.contains_bound(other.bound))
+
class OptValue(object):
__metaclass__ = extendabletype
_attrs_ = ('box', 'known_class', 'last_guard', 'level', 'intbound', 'lenbound')
@@ -129,13 +135,21 @@
def force_at_end_of_preamble(self, already_forced, optforce):
return self
- def get_args_for_fail(self, modifier):
+ # visitor API
+
+ def visitor_walk_recursive(self, visitor):
pass
- def make_virtual_info(self, modifier, fieldnums):
- #raise NotImplementedError # should not be called on this level
- assert fieldnums is None
- return modifier.make_not_virtual(self)
+ @specialize.argtype(1)
+ def visitor_dispatch_virtual_type(self, visitor):
+ if self.is_virtual():
+ return self._visitor_dispatch_virtual_type(visitor)
+ else:
+ return visitor.visit_not_virtual(self)
+
+ @specialize.argtype(1)
+ def _visitor_dispatch_virtual_type(self, visitor):
+ assert 0, "unreachable"
def is_constant(self):
return self.level == LEVEL_CONSTANT
diff --git a/rpython/jit/metainterp/optimizeopt/rewrite.py b/rpython/jit/metainterp/optimizeopt/rewrite.py
--- a/rpython/jit/metainterp/optimizeopt/rewrite.py
+++ b/rpython/jit/metainterp/optimizeopt/rewrite.py
@@ -543,6 +543,9 @@
return
self.emit_operation(op)
+ def optimize_GUARD_FUTURE_CONDITION(self, op):
+ pass # just remove it
+
def optimize_INT_FLOORDIV(self, op):
v1 = self.getvalue(op.getarg(0))
v2 = self.getvalue(op.getarg(1))
diff --git a/rpython/jit/metainterp/optimizeopt/simplify.py b/rpython/jit/metainterp/optimizeopt/simplify.py
--- a/rpython/jit/metainterp/optimizeopt/simplify.py
+++ b/rpython/jit/metainterp/optimizeopt/simplify.py
@@ -61,6 +61,9 @@
op.setdescr(descr.target_tokens[0])
self.emit_operation(op)
+ def optimize_GUARD_FUTURE_CONDITION(self, op):
+ pass
+
dispatch_opt = make_dispatcher_method(OptSimplify, 'optimize_',
default=OptSimplify.emit_operation)
OptSimplify.propagate_forward = dispatch_opt
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_multilabel.py b/rpython/jit/metainterp/optimizeopt/test/test_multilabel.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_multilabel.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_multilabel.py
@@ -1,6 +1,6 @@
from __future__ import with_statement
from rpython.jit.metainterp.optimizeopt.test.test_util import (
- LLtypeMixin, BaseTest, Storage, _sortboxes, FakeDescrWithSnapshot,
+ LLtypeMixin, BaseTest, Storage, _sortboxes,
FakeMetaInterpStaticData)
from rpython.jit.metainterp.history import TreeLoop, JitCellToken, TargetToken
from rpython.jit.metainterp.resoperation import rop, opname, ResOperation
@@ -8,6 +8,8 @@
from py.test import raises
from rpython.jit.metainterp.optimizeopt.optimizer import Optimization
from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
+from rpython.jit.metainterp.optimizeopt.heap import OptHeap
+from rpython.jit.metainterp.optimizeopt.rewrite import OptRewrite
class BaseTestMultiLabel(BaseTest):
@@ -20,7 +22,6 @@
part = TreeLoop('part')
part.inputargs = loop.inputargs
- part.resume_at_jump_descr = FakeDescrWithSnapshot()
token = loop.original_jitcell_token
optimized = TreeLoop('optimized')
@@ -42,6 +43,7 @@
operations.append(label)
part.operations = operations
+ self.add_guard_future_condition(part)
self._do_optimize_loop(part, None)
if part.operations[-1].getopnum() == rop.LABEL:
last_label = [part.operations.pop()]
@@ -502,7 +504,7 @@
self.loop = loop
loop.call_pure_results = args_dict()
metainterp_sd = FakeMetaInterpStaticData(self.cpu)
- optimize_unroll(metainterp_sd, loop, [OptRenameStrlen(), OptPure()], True)
+ optimize_unroll(metainterp_sd, loop, [OptRewrite(), OptRenameStrlen(), OptHeap(), OptPure()], True)
def test_optimizer_renaming_boxes1(self):
ops = """
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -61,24 +61,6 @@
lst6 = virt1._get_field_descr_list()
assert lst6 is lst3
-def test_reuse_vinfo():
- class FakeVInfo(object):
- def set_content(self, fieldnums):
- self.fieldnums = fieldnums
- def equals(self, fieldnums):
- return self.fieldnums == fieldnums
- class FakeVirtualValue(virtualize.AbstractVirtualValue):
- def _make_virtual(self, *args):
- return FakeVInfo()
- v1 = FakeVirtualValue(None, None)
- vinfo1 = v1.make_virtual_info(None, [1, 2, 4])
- vinfo2 = v1.make_virtual_info(None, [1, 2, 4])
- assert vinfo1 is vinfo2
- vinfo3 = v1.make_virtual_info(None, [1, 2, 6])
- assert vinfo3 is not vinfo2
- vinfo4 = v1.make_virtual_info(None, [1, 2, 6])
- assert vinfo3 is vinfo4
-
def test_descrlist_dict():
from rpython.jit.metainterp.optimizeopt import util as optimizeutil
h1 = optimizeutil.descrlist_hash([])
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -51,7 +51,8 @@
if expected_preamble:
expected_preamble = self.parse(expected_preamble)
if expected_short:
- expected_short = self.parse(expected_short)
+ # the short preamble doesn't have fail descrs, they are patched in when it is used
+ expected_short = self.parse(expected_short, want_fail_descr=False)
preamble = self.unroll_and_optimize(loop, call_pure_results)
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -355,11 +355,21 @@
class BaseTest(object):
- def parse(self, s, boxkinds=None):
+ def parse(self, s, boxkinds=None, want_fail_descr=True):
+ if want_fail_descr:
+ invent_fail_descr = self.invent_fail_descr
+ else:
+ invent_fail_descr = lambda *args: None
return parse(s, self.cpu, self.namespace,
type_system=self.type_system,
boxkinds=boxkinds,
- invent_fail_descr=self.invent_fail_descr)
+ invent_fail_descr=invent_fail_descr)
+
+ def add_guard_future_condition(self, res):
+ # invent a GUARD_FUTURE_CONDITION to not have to change all tests
+ if res.operations[-1].getopnum() == rop.JUMP:
+ guard = ResOperation(rop.GUARD_FUTURE_CONDITION, [], None, descr=self.invent_fail_descr(None, -1, []))
+ res.operations.insert(-1, guard)
def invent_fail_descr(self, model, opnum, fail_args):
if fail_args is None:
@@ -397,6 +407,7 @@
optimize_trace(metainterp_sd, loop, self.enable_opts)
def unroll_and_optimize(self, loop, call_pure_results=None):
+ self.add_guard_future_condition(loop)
operations = loop.operations
jumpop = operations[-1]
assert jumpop.getopnum() == rop.JUMP
@@ -408,7 +419,6 @@
preamble = TreeLoop('preamble')
preamble.inputargs = inputargs
- preamble.resume_at_jump_descr = FakeDescrWithSnapshot()
token = JitCellToken()
preamble.operations = [ResOperation(rop.LABEL, inputargs, None, descr=TargetToken(token))] + \
@@ -419,7 +429,6 @@
assert preamble.operations[-1].getopnum() == rop.LABEL
inliner = Inliner(inputargs, jump_args)
- loop.resume_at_jump_descr = preamble.resume_at_jump_descr
loop.operations = [preamble.operations[-1]] + \
[inliner.inline_op(op, clone=False) for op in cloned_operations] + \
[ResOperation(rop.JUMP, [inliner.inline_arg(a) for a in jump_args],
@@ -450,18 +459,6 @@
def __eq__(self, other):
return isinstance(other, FakeDescr)
-class FakeDescrWithSnapshot(compile.ResumeGuardDescr):
- class rd_snapshot:
- class prev:
- prev = None
- boxes = []
- boxes = []
- def clone_if_mutable(self):
- return FakeDescrWithSnapshot()
- def __eq__(self, other):
- return isinstance(other, Storage) or isinstance(other, FakeDescrWithSnapshot)
-
-
def convert_old_style_to_targets(loop, jump):
newloop = TreeLoop(loop.name)
newloop.inputargs = loop.inputargs
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py b/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py
@@ -1,43 +1,103 @@
from __future__ import with_statement
import py
-from rpython.jit.metainterp.optimize import InvalidLoop
from rpython.jit.metainterp.optimizeopt.virtualstate import VirtualStateInfo, VStructStateInfo, \
- VArrayStateInfo, NotVirtualStateInfo, VirtualState, ShortBoxes
+ VArrayStateInfo, NotVirtualStateInfo, VirtualState, ShortBoxes, GenerateGuardState, \
+ VirtualStatesCantMatch, VArrayStructStateInfo
from rpython.jit.metainterp.optimizeopt.optimizer import OptValue
from rpython.jit.metainterp.history import BoxInt, BoxFloat, BoxPtr, ConstInt, ConstPtr
from rpython.rtyper.lltypesystem import lltype, llmemory
from rpython.jit.metainterp.optimizeopt.test.test_util import LLtypeMixin, BaseTest, \
- equaloplists, FakeDescrWithSnapshot
+ equaloplists
from rpython.jit.metainterp.optimizeopt.intutils import IntBound
+from rpython.jit.metainterp.optimizeopt.virtualize import (VirtualValue,
+ VArrayValue, VStructValue, VArrayStructValue)
from rpython.jit.metainterp.history import TreeLoop, JitCellToken
from rpython.jit.metainterp.optimizeopt.test.test_optimizeopt import FakeMetaInterpStaticData
from rpython.jit.metainterp.resoperation import ResOperation, rop
-class TestBasic:
- someptr1 = LLtypeMixin.myptr
- someptr2 = LLtypeMixin.myptr2
+class BaseTestGenerateGuards(BaseTest):
+
+ def _box_or_value(self, box_or_value=None):
+ if box_or_value is None:
+ return None, None
+ elif isinstance(box_or_value, OptValue):
+ value = box_or_value
+ box = value.box
+ else:
+ box = box_or_value
+ value = OptValue(box)
+ return value, box
+
+ def guards(self, info1, info2, box_or_value, expected, inputargs=None):
+ value, box = self._box_or_value(box_or_value)
+ if inputargs is None:
+ inputargs = [box]
+ info1.position = info2.position = 0
+ state = GenerateGuardState(self.cpu)
+ info1.generate_guards(info2, value, state)
+ self.compare(state.extra_guards, expected, inputargs)
+
+ def compare(self, guards, expected, inputargs):
+ loop = self.parse(expected)
+ boxmap = {}
+ assert len(loop.inputargs) == len(inputargs)
+ for a, b in zip(loop.inputargs, inputargs):
+ boxmap[a] = b
+ for op in loop.operations:
+ if op.is_guard():
+ op.setdescr(None)
+ assert equaloplists(guards, loop.operations, False,
+ boxmap)
+
+ def check_no_guards(self, info1, info2, box_or_value=None, state=None):
+ value, _ = self._box_or_value(box_or_value)
+ if info1.position == -1:
+ info1.position = 0
+ if info2.position == -1:
+ info2.position = 0
+ if state is None:
+ state = GenerateGuardState(self.cpu)
+ info1.generate_guards(info2, value, state)
+ assert not state.extra_guards
+ return state
+
+ def check_invalid(self, info1, info2, box_or_value=None, state=None):
+ value, _ = self._box_or_value(box_or_value)
+ if info1.position == -1:
+ info1.position = 0
+ if info2.position == -1:
+ info2.position = 0
+ if state is None:
+ state = GenerateGuardState(self.cpu)
+ with py.test.raises(VirtualStatesCantMatch):
+ info1.generate_guards(info2, value, state)
+
def test_position_generalization(self):
def postest(info1, info2):
info1.position = 0
- assert info1.generalization_of(info1, {}, {})
+ self.check_no_guards(info1, info1)
info2.position = 0
- assert info1.generalization_of(info2, {}, {})
+ self.check_no_guards(info1, info2)
info2.position = 1
- renum = {}
- assert info1.generalization_of(info2, renum, {})
- assert renum == {0:1}
- assert info1.generalization_of(info2, {0:1}, {})
- assert info1.generalization_of(info2, {1:1}, {})
- bad = {}
- assert not info1.generalization_of(info2, {0:0}, bad)
- assert info1 in bad and info2 in bad
+ state = self.check_no_guards(info1, info2)
+ assert state.renum == {0:1}
+
+ assert self.check_no_guards(info1, info2, state=state)
+
+ # feed fake renums
+ state.renum = {1: 1}
+ self.check_no_guards(info1, info2, state=state)
+
+ state.renum = {0: 0}
+ self.check_invalid(info1, info2, state=state)
+ assert info1 in state.bad and info2 in state.bad
for BoxType in (BoxInt, BoxFloat, BoxPtr):
info1 = NotVirtualStateInfo(OptValue(BoxType()))
info2 = NotVirtualStateInfo(OptValue(BoxType()))
postest(info1, info2)
-
+
info1, info2 = VArrayStateInfo(42), VArrayStateInfo(42)
info1.fieldstate = info2.fieldstate = []
postest(info1, info2)
@@ -56,7 +116,7 @@
info1.position = 0
info2 = NotVirtualStateInfo(value2)
info2.position = 0
- return info1.generalization_of(info2, {}, {})
+ return VirtualState([info1]).generalization_of(VirtualState([info2]), cpu=self.cpu)
assert isgeneral(OptValue(BoxInt()), OptValue(ConstInt(7)))
assert not isgeneral(OptValue(ConstInt(7)), OptValue(BoxInt()))
@@ -65,10 +125,11 @@
nonnull = OptValue(BoxPtr())
nonnull.make_nonnull(0)
knownclass = OptValue(BoxPtr())
- knownclass.make_constant_class(ConstPtr(self.someptr1), 0)
+ clsbox = self.cpu.ts.cls_of_box(BoxPtr(self.myptr))
+ knownclass.make_constant_class(clsbox, 0)
const = OptValue(BoxPtr)
- const.make_constant_class(ConstPtr(self.someptr1), 0)
- const.make_constant(ConstPtr(self.someptr1))
+ const.make_constant_class(clsbox, 0)
+ const.make_constant(ConstPtr(self.myptr))
inorder = [ptr, nonnull, knownclass, const]
for i in range(len(inorder)):
for j in range(i, len(inorder)):
@@ -91,48 +152,51 @@
value1 = OptValue(BoxPtr())
value1.make_nonnull(None)
- value2 = OptValue(ConstPtr(LLtypeMixin.nullptr))
+ value2 = OptValue(ConstPtr(self.nullptr))
assert not isgeneral(value1, value2)
def test_field_matching_generalization(self):
const1 = NotVirtualStateInfo(OptValue(ConstInt(1)))
const2 = NotVirtualStateInfo(OptValue(ConstInt(2)))
const1.position = const2.position = 1
- assert not const1.generalization_of(const2, {}, {})
- assert not const2.generalization_of(const1, {}, {})
+ self.check_invalid(const1, const2)
+ self.check_invalid(const2, const1)
def fldtst(info1, info2):
info1.position = info2.position = 0
info1.fieldstate = [const1]
info2.fieldstate = [const2]
- assert not info1.generalization_of(info2, {}, {})
- assert not info2.generalization_of(info1, {}, {})
- assert info1.generalization_of(info1, {}, {})
- assert info2.generalization_of(info2, {}, {})
- fldtst(VArrayStateInfo(42), VArrayStateInfo(42))
- fldtst(VStructStateInfo(42, [7]), VStructStateInfo(42, [7]))
- fldtst(VirtualStateInfo(ConstInt(42), [7]), VirtualStateInfo(ConstInt(42), [7]))
+ self.check_invalid(info1, info2)
+ self.check_invalid(info2, info1)
+ self.check_no_guards(info1, info1)
+ self.check_no_guards(info2, info2)
+ fakedescr = object()
+ fielddescr = object()
+ fldtst(VArrayStateInfo(fakedescr), VArrayStateInfo(fakedescr))
+ fldtst(VStructStateInfo(fakedescr, [fielddescr]), VStructStateInfo(fakedescr, [fielddescr]))
+ fldtst(VirtualStateInfo(ConstInt(42), [fielddescr]), VirtualStateInfo(ConstInt(42), [fielddescr]))
+ fldtst(VArrayStructStateInfo(fakedescr, [[fielddescr]]), VArrayStructStateInfo(fakedescr, [[fielddescr]]))
def test_known_class_generalization(self):
knownclass1 = OptValue(BoxPtr())
- knownclass1.make_constant_class(ConstPtr(self.someptr1), 0)
+ knownclass1.make_constant_class(ConstPtr(self.myptr), 0)
info1 = NotVirtualStateInfo(knownclass1)
info1.position = 0
knownclass2 = OptValue(BoxPtr())
- knownclass2.make_constant_class(ConstPtr(self.someptr1), 0)
+ knownclass2.make_constant_class(ConstPtr(self.myptr), 0)
info2 = NotVirtualStateInfo(knownclass2)
info2.position = 0
- assert info1.generalization_of(info2, {}, {})
- assert info2.generalization_of(info1, {}, {})
+ self.check_no_guards(info1, info2)
+ self.check_no_guards(info2, info1)
knownclass3 = OptValue(BoxPtr())
- knownclass3.make_constant_class(ConstPtr(self.someptr2), 0)
+ knownclass3.make_constant_class(ConstPtr(self.myptr2), 0)
info3 = NotVirtualStateInfo(knownclass3)
info3.position = 0
- assert not info1.generalization_of(info3, {}, {})
- assert not info2.generalization_of(info3, {}, {})
- assert not info3.generalization_of(info2, {}, {})
- assert not info3.generalization_of(info1, {}, {})
+ self.check_invalid(info1, info3)
+ self.check_invalid(info2, info3)
+ self.check_invalid(info3, info2)
+ self.check_invalid(info3, info1)
def test_circular_generalization(self):
@@ -140,29 +204,157 @@
VirtualStateInfo(ConstInt(42), [7])):
info.position = 0
info.fieldstate = [info]
- assert info.generalization_of(info, {}, {})
+ self.check_no_guards(info, info)
-class BaseTestGenerateGuards(BaseTest):
- def guards(self, info1, info2, box, expected):
- info1.position = info2.position = 0
- guards = []
- info1.generate_guards(info2, box, self.cpu, guards, {})
- self.compare(guards, expected, [box])
+ def test_generate_guards_nonvirtual_all_combinations(self):
+ # set up infos
+ unknown_val = OptValue(self.nodebox)
+ unknownnull_val = OptValue(BoxPtr(self.nullptr))
+ unknown_info = NotVirtualStateInfo(unknown_val)
- def compare(self, guards, expected, inputargs):
- loop = self.parse(expected)
- boxmap = {}
- assert len(loop.inputargs) == len(inputargs)
- for a, b in zip(loop.inputargs, inputargs):
- boxmap[a] = b
- for op in loop.operations:
- if op.is_guard():
- op.setdescr(None)
- assert equaloplists(guards, loop.operations, False,
- boxmap)
+ nonnull_val = OptValue(self.nodebox)
+ nonnull_val.make_nonnull(None)
+ nonnull_info = NotVirtualStateInfo(nonnull_val)
+
+ knownclass_val = OptValue(self.nodebox)
+ classbox = self.cpu.ts.cls_of_box(self.nodebox)
+ knownclass_val.make_constant_class(classbox, -1)
+ knownclass_info = NotVirtualStateInfo(knownclass_val)
+ knownclass2_val = OptValue(self.nodebox2)
+ classbox = self.cpu.ts.cls_of_box(self.nodebox2)
+ knownclass2_val.make_constant_class(classbox, -1)
+ knownclass2_info = NotVirtualStateInfo(knownclass2_val)
+
+ constant_val = OptValue(BoxInt())
+ constant_val.make_constant(ConstInt(1))
+ constant_info = NotVirtualStateInfo(constant_val)
+ constclass_val = OptValue(self.nodebox)
+ constclass_val.make_constant(self.nodebox.constbox())
+ constclass_info = NotVirtualStateInfo(constclass_val)
+ constclass2_val = OptValue(self.nodebox)
+ constclass2_val.make_constant(self.nodebox2.constbox())
+ constclass2_info = NotVirtualStateInfo(constclass2_val)
+ constantnull_val = OptValue(ConstPtr(self.nullptr))
+ constantnull_info = NotVirtualStateInfo(constantnull_val)
+
+ # unknown unknown
+ self.check_no_guards(unknown_info, unknown_info, unknown_val)
+ self.check_no_guards(unknown_info, unknown_info)
+
+ # unknown nonnull
+ self.check_no_guards(unknown_info, nonnull_info, nonnull_val)
+ self.check_no_guards(unknown_info, nonnull_info)
+
+ # unknown knownclass
+ self.check_no_guards(unknown_info, knownclass_info, knownclass_val)
+ self.check_no_guards(unknown_info, knownclass_info)
+
+ # unknown constant
+ self.check_no_guards(unknown_info, constant_info, constant_val)
+ self.check_no_guards(unknown_info, constant_info)
+
+
+ # nonnull unknown
+ expected = """
+ [p0]
+ guard_nonnull(p0) []
+ """
+ self.guards(nonnull_info, unknown_info, unknown_val, expected)
+ self.check_invalid(nonnull_info, unknown_info, unknownnull_val)
+ self.check_invalid(nonnull_info, unknown_info)
+ self.check_invalid(nonnull_info, unknown_info)
+
+ # nonnull nonnull
+ self.check_no_guards(nonnull_info, nonnull_info, nonnull_val)
+ self.check_no_guards(nonnull_info, nonnull_info, nonnull_val)
+
+ # nonnull knownclass
+ self.check_no_guards(nonnull_info, knownclass_info, knownclass_val)
+ self.check_no_guards(nonnull_info, knownclass_info)
+
+ # nonnull constant
+ self.check_no_guards(nonnull_info, constant_info, constant_val)
+ self.check_invalid(nonnull_info, constantnull_info, constantnull_val)
+ self.check_no_guards(nonnull_info, constant_info)
+ self.check_invalid(nonnull_info, constantnull_info)
+
+
+ # knownclass unknown
+ expected = """
+ [p0]
+ guard_nonnull(p0) []
+ guard_class(p0, ConstClass(node_vtable)) []
+ """
+ self.guards(knownclass_info, unknown_info, unknown_val, expected)
+ self.check_invalid(knownclass_info, unknown_info, unknownnull_val)
+ self.check_invalid(knownclass_info, unknown_info, knownclass2_val)
+ self.check_invalid(knownclass_info, unknown_info)
+ self.check_invalid(knownclass_info, unknown_info)
+ self.check_invalid(knownclass_info, unknown_info)
+
+ # knownclass nonnull
+ expected = """
+ [p0]
+ guard_class(p0, ConstClass(node_vtable)) []
+ """
+ self.guards(knownclass_info, nonnull_info, knownclass_val, expected)
+ self.check_invalid(knownclass_info, nonnull_info, knownclass2_val)
+ self.check_invalid(knownclass_info, nonnull_info)
+ self.check_invalid(knownclass_info, nonnull_info)
+
+ # knownclass knownclass
+ self.check_no_guards(knownclass_info, knownclass_info, knownclass_val)
+ self.check_invalid(knownclass_info, knownclass2_info, knownclass2_val)
+ self.check_no_guards(knownclass_info, knownclass_info)
+ self.check_invalid(knownclass_info, knownclass2_info)
+
+ # knownclass constant
+ self.check_invalid(knownclass_info, constantnull_info, constantnull_val)
+ self.check_invalid(knownclass_info, constclass2_info, constclass2_val)
+ self.check_invalid(knownclass_info, constantnull_info)
+ self.check_invalid(knownclass_info, constclass2_info)
+
+
+ # constant unknown
+ expected = """
+ [i0]
+ guard_value(i0, 1) []
+ """
+ self.guards(constant_info, unknown_info, constant_val, expected)
+ self.check_invalid(constant_info, unknown_info, unknownnull_val)
+ self.check_invalid(constant_info, unknown_info)
+ self.check_invalid(constant_info, unknown_info)
+
+ # constant nonnull
+ expected = """
+ [i0]
+ guard_value(i0, 1) []
+ """
+ self.guards(constant_info, nonnull_info, constant_val, expected)
+ self.check_invalid(constant_info, nonnull_info, constclass2_val)
+ self.check_invalid(constant_info, nonnull_info)
+ self.check_invalid(constant_info, nonnull_info)
+
+ # constant knownclass
+ expected = """
+ [i0]
+ guard_value(i0, 1) []
+ """
+ self.guards(constant_info, knownclass_info, constant_val, expected)
+ self.check_invalid(constant_info, knownclass_info, unknownnull_val)
+ self.check_invalid(constant_info, knownclass_info)
+ self.check_invalid(constant_info, knownclass_info)
+
+ # constant constant
+ self.check_no_guards(constant_info, constant_info, constant_val)
+ self.check_invalid(constant_info, constantnull_info, constantnull_val)
+ self.check_no_guards(constant_info, constant_info)
+ self.check_invalid(constant_info, constantnull_info)
+
+
def test_intbounds(self):
- value1 = OptValue(BoxInt())
+ value1 = OptValue(BoxInt(15))
value1.intbound.make_ge(IntBound(0, 10))
value1.intbound.make_le(IntBound(20, 30))
info1 = NotVirtualStateInfo(value1)
@@ -174,10 +366,19 @@
i2 = int_le(i0, 30)
guard_true(i2) []
"""
- self.guards(info1, info2, BoxInt(15), expected)
- py.test.raises(InvalidLoop, self.guards,
- info1, info2, BoxInt(50), expected)
+ self.guards(info1, info2, value1, expected)
+ self.check_invalid(info1, info2, BoxInt(50))
+ def test_intbounds_constant(self):
+ value1 = OptValue(BoxInt(15))
+ value1.intbound.make_ge(IntBound(0, 10))
+ value1.intbound.make_le(IntBound(20, 30))
+ info1 = NotVirtualStateInfo(value1)
+ info2 = NotVirtualStateInfo(OptValue(ConstInt(10000)))
+ self.check_invalid(info1, info2)
+ info1 = NotVirtualStateInfo(value1)
+ info2 = NotVirtualStateInfo(OptValue(ConstInt(11)))
+ self.check_no_guards(info1, info2)
def test_known_class(self):
value1 = OptValue(self.nodebox)
@@ -191,8 +392,7 @@
guard_class(p0, ConstClass(node_vtable)) []
"""
self.guards(info1, info2, self.nodebox, expected)
- py.test.raises(InvalidLoop, self.guards,
- info1, info2, BoxPtr(), expected)
+ self.check_invalid(info1, info2, BoxPtr())
def test_known_class_value(self):
value1 = OptValue(self.nodebox)
@@ -219,7 +419,7 @@
self.compare(guards, expected, [box])
def test_equal_inputargs(self):
- value = OptValue(self.nodebox)
+ value = OptValue(self.nodebox)
classbox = self.cpu.ts.cls_of_box(self.nodebox)
value.make_constant_class(classbox, -1)
knownclass_info = NotVirtualStateInfo(value)
@@ -242,22 +442,130 @@
expected = """
[p0]
- guard_nonnull(p0) []
+ guard_nonnull(p0) []
guard_class(p0, ConstClass(node_vtable)) []
"""
- guards = []
- vstate1.generate_guards(vstate2, [self.nodebox, self.nodebox], self.cpu, guards)
- self.compare(guards, expected, [self.nodebox])
+ state = vstate1.generate_guards(vstate2, [value, value], self.cpu)
+ self.compare(state.extra_guards, expected, [self.nodebox])
- with py.test.raises(InvalidLoop):
- guards = []
- vstate1.generate_guards(vstate3, [self.nodebox, self.nodebox],
- self.cpu, guards)
- with py.test.raises(InvalidLoop):
- guards = []
- vstate2.generate_guards(vstate3, [self.nodebox, self.nodebox],
- self.cpu, guards)
-
+ with py.test.raises(VirtualStatesCantMatch):
+ vstate1.generate_guards(vstate3, [value, value],
+ self.cpu)
+ with py.test.raises(VirtualStatesCantMatch):
+ vstate2.generate_guards(vstate3, [value, value],
+ self.cpu)
+
+
+ def test_generate_guards_on_virtual_fields_matches_array(self):
+ innervalue1 = OptValue(self.nodebox)
+ constclassbox = self.cpu.ts.cls_of_box(self.nodebox)
+ innervalue1.make_constant_class(constclassbox, -1)
+ innerinfo1 = NotVirtualStateInfo(innervalue1)
+ innerinfo1.position = 1
+ innerinfo2 = NotVirtualStateInfo(OptValue(self.nodebox))
+ innerinfo2.position = 1
+
+ descr = object()
+
+ info1 = VArrayStateInfo(descr)
+ info1.fieldstate = [innerinfo1]
+
+ info2 = VArrayStateInfo(descr)
+ info2.fieldstate = [innerinfo2]
+
+ value1 = VArrayValue(descr, None, 1, self.nodebox)
+ value1._items[0] = OptValue(self.nodebox)
+
+ expected = """
+ [p0]
+ guard_nonnull(p0) []
+ guard_class(p0, ConstClass(node_vtable)) []
+ """
+ self.guards(info1, info2, value1, expected, [self.nodebox])
+
+ def test_generate_guards_on_virtual_fields_matches_instance(self):
+ innervalue1 = OptValue(self.nodebox)
+ constclassbox = self.cpu.ts.cls_of_box(self.nodebox)
+ innervalue1.make_constant_class(constclassbox, -1)
+ innerinfo1 = NotVirtualStateInfo(innervalue1)
+ innerinfo1.position = 1
+ innerinfo2 = NotVirtualStateInfo(OptValue(self.nodebox))
+ innerinfo2.position = 1
+
+ info1 = VirtualStateInfo(ConstInt(42), [1])
+ info1.fieldstate = [innerinfo1]
+
+ info2 = VirtualStateInfo(ConstInt(42), [1])
+ info2.fieldstate = [innerinfo2]
+
+ value1 = VirtualValue(self.cpu, constclassbox, self.nodebox)
+ value1._fields = {1: OptValue(self.nodebox)}
+
+ expected = """
+ [p0]
+ guard_nonnull(p0) []
+ guard_class(p0, ConstClass(node_vtable)) []
+ """
+ self.guards(info1, info2, value1, expected, [self.nodebox])
+
+ def test_generate_guards_on_virtual_fields_matches_struct(self):
+ innervalue1 = OptValue(self.nodebox)
+ constclassbox = self.cpu.ts.cls_of_box(self.nodebox)
+ innervalue1.make_constant_class(constclassbox, -1)
+ innerinfo1 = NotVirtualStateInfo(innervalue1)
+ innerinfo1.position = 1
+ innerinfo2 = NotVirtualStateInfo(OptValue(self.nodebox))
+ innerinfo2.position = 1
+
+ structdescr = object()
+
+ info1 = VStructStateInfo(structdescr, [1])
+ info1.fieldstate = [innerinfo1]
+
+ info2 = VStructStateInfo(structdescr, [1])
+ info2.fieldstate = [innerinfo2]
+
+ value1 = VStructValue(self.cpu, structdescr, self.nodebox)
+ value1._fields = {1: OptValue(self.nodebox)}
+
+ expected = """
+ [p0]
+ guard_nonnull(p0) []
+ guard_class(p0, ConstClass(node_vtable)) []
+ """
+ self.guards(info1, info2, value1, expected, [self.nodebox])
+
+ def test_generate_guards_on_virtual_fields_matches_arraystruct(self):
+ innervalue1 = OptValue(self.nodebox)
+ constclassbox = self.cpu.ts.cls_of_box(self.nodebox)
+ innervalue1.make_constant_class(constclassbox, -1)
+ innerinfo1 = NotVirtualStateInfo(innervalue1)
+ innerinfo1.position = 1
+ innerinfo2 = NotVirtualStateInfo(OptValue(self.nodebox))
+ innerinfo2.position = 1
+
+ arraydescr = object()
+ fielddescr = object()
+
+ info1 = VArrayStructStateInfo(arraydescr, [[fielddescr]])
+ info1.fieldstate = [innerinfo1]
+
+ info2 = VArrayStructStateInfo(arraydescr, [[fielddescr]])
+ info2.fieldstate = [innerinfo2]
+
+ value1 = VArrayStructValue(arraydescr, 1, self.nodebox)
+ value1._items[0][fielddescr] = OptValue(self.nodebox)
+
+ expected = """
+ [p0]
+ guard_nonnull(p0) []
+ guard_class(p0, ConstClass(node_vtable)) []
+ """
+ self.guards(info1, info2, value1, expected, [self.nodebox])
+
+ # _________________________________________________________________________
+ # the below tests don't really have anything to do with guard generation
+
def test_virtuals_with_equal_fields(self):
info1 = VirtualStateInfo(ConstInt(42), [1, 2])
value = OptValue(self.nodebox)
@@ -471,7 +779,6 @@
if hasattr(self, 'callinfocollection'):
metainterp_sd.callinfocollection = self.callinfocollection
#
- bridge.resume_at_jump_descr = FakeDescrWithSnapshot()
optimize_trace(metainterp_sd, bridge, self.enable_opts)
@@ -480,6 +787,7 @@
loops = (loops, )
loops = [self.parse(loop) for loop in loops]
bridge = self.parse(bridge)
+ self.add_guard_future_condition(bridge)
for loop in loops:
loop.preamble = self.unroll_and_optimize(loop)
preamble = loops[0].preamble
@@ -615,26 +923,26 @@
def test_constant(self):
loops = """
- [p0]
- p1 = same_as(ConstPtr(myptr))
- jump(p1)
+ [i0]
+ i1 = same_as(1)
+ jump(i1)
""", """
- [p0]
- p1 = same_as(ConstPtr(myptr2))
- jump(p1)
+ [i0]
+ i1 = same_as(2)
+ jump(i1)
""", """
- [p0]
- jump(p0)
+ [i0]
+ jump(i0)
"""
expected = """
- [p0]
+ [i0]
jump()
"""
self.optimize_bridge(loops, loops[0], expected, 'Loop0')
self.optimize_bridge(loops, loops[1], expected, 'Loop1')
expected = """
- [p0]
- jump(p0)
+ [i0]
+ jump(i0)
"""
self.optimize_bridge(loops, loops[2], expected, 'Loop2')
@@ -658,7 +966,7 @@
"""
self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
- def test_virtual(self):
+ def test_simple_virtual(self):
loops = """
[p0, p1]
p2 = new_with_vtable(ConstClass(node_vtable))
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -6,7 +6,8 @@
from rpython.jit.metainterp.optimize import InvalidLoop
from rpython.jit.metainterp.optimizeopt.generalize import KillHugeIntBounds
from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer, Optimization
-from rpython.jit.metainterp.optimizeopt.virtualstate import VirtualStateAdder, ShortBoxes, BadVirtualState
+from rpython.jit.metainterp.optimizeopt.virtualstate import (VirtualStateConstructor,
+ ShortBoxes, BadVirtualState, VirtualStatesCantMatch)
from rpython.jit.metainterp.resoperation import rop, ResOperation
from rpython.jit.metainterp.resume import Snapshot
from rpython.rlib.debug import debug_print, debug_start, debug_stop
@@ -53,6 +54,10 @@
self.optimizer = UnrollableOptimizer(metainterp_sd, loop, optimizations)
self.boxes_created_this_iteration = None
+ def get_virtual_state(self, args):
+ modifier = VirtualStateConstructor(self.optimizer)
+ return modifier.get_virtual_state(args)
+
def fix_snapshot(self, jump_args, snapshot):
if snapshot is None:
return None
@@ -77,6 +82,12 @@
else:
start_label = None
+ patchguardop = None
+ if len(loop.operations) > 1:
+ patchguardop = loop.operations[-2]
+ if patchguardop.getopnum() != rop.GUARD_FUTURE_CONDITION:
+ patchguardop = None
+
jumpop = loop.operations[-1]
if jumpop.getopnum() == rop.JUMP or jumpop.getopnum() == rop.LABEL:
loop.operations = loop.operations[:-1]
@@ -94,7 +105,7 @@
stop_label = ResOperation(rop.LABEL, jumpop.getarglist(), None, TargetToken(cell_token))
if jumpop.getopnum() == rop.JUMP:
- if self.jump_to_already_compiled_trace(jumpop):
+ if self.jump_to_already_compiled_trace(jumpop, patchguardop):
# Found a compiled trace to jump to
if self.short:
# Construct our short preamble
@@ -108,7 +119,7 @@
descr=start_label.getdescr())
if self.short:
# Construct our short preamble
- self.close_loop(start_label, jumpop)
+ self.close_loop(start_label, jumpop, patchguardop)
else:
self.optimizer.send_extra_operation(jumpop)
return
@@ -147,28 +158,14 @@
start_target = start_label.getdescr()
assert isinstance(stop_target, TargetToken)
assert isinstance(start_target, TargetToken)
- if stop_target.targeting_jitcell_token is not start_target.targeting_jitcell_token:
- return False
+ return stop_target.targeting_jitcell_token is start_target.targeting_jitcell_token
- return True
-
- #args = stop_label.getarglist()
- #modifier = VirtualStateAdder(self.optimizer)
- #virtual_state = modifier.get_virtual_state(args)
- #if self.initial_virtual_state.generalization_of(virtual_state):
- # return True
def export_state(self, targetop):
original_jump_args = targetop.getarglist()
jump_args = [self.getvalue(a).get_key_box() for a in original_jump_args]
- assert self.optimizer.loop.resume_at_jump_descr
- resume_at_jump_descr = self.optimizer.loop.resume_at_jump_descr.clone_if_mutable()
- assert isinstance(resume_at_jump_descr, ResumeGuardDescr)
- resume_at_jump_descr.rd_snapshot = self.fix_snapshot(jump_args, resume_at_jump_descr.rd_snapshot)
-
- modifier = VirtualStateAdder(self.optimizer)
- virtual_state = modifier.get_virtual_state(jump_args)
+ virtual_state = self.get_virtual_state(jump_args)
values = [self.getvalue(arg) for arg in jump_args]
inputargs = virtual_state.make_inputargs(values, self.optimizer)
@@ -195,7 +192,6 @@
targetop.initarglist(inputargs)
target_token.virtual_state = virtual_state
target_token.short_preamble = [ResOperation(rop.LABEL, short_inputargs, None)]
- target_token.resume_at_jump_descr = resume_at_jump_descr
exported_values = {}
for box in inputargs:
@@ -222,15 +218,13 @@
if not exported_state:
# No state exported, construct one without virtuals
self.short = None
- modifier = VirtualStateAdder(self.optimizer)
- virtual_state = modifier.get_virtual_state(self.inputargs)
+ virtual_state = self.get_virtual_state(self.inputargs)
self.initial_virtual_state = virtual_state
return
self.short = target_token.short_preamble[:]
self.short_seen = {}
self.short_boxes = exported_state.short_boxes
- self.short_resume_at_jump_descr = target_token.resume_at_jump_descr
self.initial_virtual_state = target_token.virtual_state
seen = {}
@@ -286,19 +280,13 @@
self.boxes_created_this_iteration = {}
i = 0
while i < len(newoperations):
- op = newoperations[i]
- self.boxes_created_this_iteration[op.result] = None
- args = op.getarglist()
- if op.is_guard():
- args = args + op.getfailargs()
- for a in args:
- self.import_box(a, inputargs, short_jumpargs, [])
+ self._import_op(newoperations[i], inputargs, short_jumpargs, [])
i += 1
newoperations = self.optimizer.get_newoperations()
self.short.append(ResOperation(rop.JUMP, short_jumpargs, None, descr=start_label.getdescr()))
self.finalize_short_preamble(start_label)
- def close_loop(self, start_label, jumpop):
+ def close_loop(self, start_label, jumpop, patchguardop):
virtual_state = self.initial_virtual_state
short_inputargs = self.short[0].getarglist()
inputargs = self.inputargs
@@ -330,19 +318,8 @@
args[short_inputargs[i]] = jmp_to_short_args[i]
self.short_inliner = Inliner(short_inputargs, jmp_to_short_args)
- i = 1
- while i < len(self.short):
- # Note that self.short might be extended during this loop
- op = self.short[i]
- newop = self.short_inliner.inline_op(op)
- self.optimizer.send_extra_operation(newop)
- if op.result in self.short_boxes.assumed_classes:
- classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu)
- assumed_classbox = self.short_boxes.assumed_classes[op.result]
- if not classbox or not classbox.same_constant(assumed_classbox):
- raise InvalidLoop('Class of opaque pointer needed in short ' +
- 'preamble unknown at end of loop')
- i += 1
+ self._inline_short_preamble(self.short, self.short_inliner,
+ patchguardop, self.short_boxes.assumed_classes)
# Import boxes produced in the preamble but used in the loop
newoperations = self.optimizer.get_newoperations()
@@ -357,19 +334,7 @@
self.import_box(a, inputargs, short_jumpargs, jumpargs)
j += 1
else:
- op = newoperations[i]
-
- self.boxes_created_this_iteration[op.result] = None
- args = op.getarglist()
- if op.is_guard():
- args = args + op.getfailargs()
-
- #if self.optimizer.loop.logops:
- # debug_print('OP: ' + self.optimizer.loop.logops.repr_of_resop(op))
- for a in args:
- #if self.optimizer.loop.logops:
- # debug_print('A: ' + self.optimizer.loop.logops.repr_of_arg(a))
- self.import_box(a, inputargs, short_jumpargs, jumpargs)
+ self._import_op(newoperations[i], inputargs, short_jumpargs, jumpargs)
i += 1
newoperations = self.optimizer.get_newoperations()
@@ -379,12 +344,12 @@
# Verify that the virtual state at the end of the loop is one
# that is compatible with the virtual state at the start of the loop
- modifier = VirtualStateAdder(self.optimizer)
- final_virtual_state = modifier.get_virtual_state(original_jumpargs)
+ final_virtual_state = self.get_virtual_state(original_jumpargs)
#debug_start('jit-log-virtualstate')
#virtual_state.debug_print('Closed loop with ')
bad = {}
- if not virtual_state.generalization_of(final_virtual_state, bad):
+ if not virtual_state.generalization_of(final_virtual_state, bad,
+ cpu=self.optimizer.cpu):
# We ended up with a virtual state that is not compatible
# and we are thus unable to jump to the start of the loop
#final_virtual_state.debug_print("Bad virtual state at end of loop, ",
@@ -417,8 +382,7 @@
if op.is_guard():
op = op.clone()
op.setfailargs(None)
- descr = target_token.resume_at_jump_descr.clone_if_mutable()
- op.setdescr(descr)
+ op.setdescr(None) # will be set to a proper descr when the preamble is used
short[i] = op
# Clone ops and boxes to get private versions and
@@ -440,8 +404,6 @@
if op.result and op.result in self.short_boxes.assumed_classes:
target_token.assumed_classes[newop.result] = self.short_boxes.assumed_classes[op.result]
short[i] = newop
- target_token.resume_at_jump_descr = target_token.resume_at_jump_descr.clone_if_mutable()
- inliner.inline_descr_inplace(target_token.resume_at_jump_descr)
# Forget the values to allow them to be freed
for box in short[0].getarglist():
@@ -485,8 +447,7 @@
if not isinstance(a, Const) and a not in self.short_seen:
self.add_op_to_short(self.short_boxes.producer(a), emit, guards_needed)
if op.is_guard():
- descr = self.short_resume_at_jump_descr.clone_if_mutable()
- op.setdescr(descr)
+ op.setdescr(None) # will be set to a proper descr when the preamble is used
if guards_needed and self.short_boxes.has_producer(op.result):
value_guards = self.getvalue(op.result).make_guards(op.result)
@@ -528,7 +489,17 @@
box = self.optimizer.values[box].force_box(self.optimizer)
jumpargs.append(box)
- def jump_to_already_compiled_trace(self, jumpop):
+
+ def _import_op(self, op, inputargs, short_jumpargs, jumpargs):
+ self.boxes_created_this_iteration[op.result] = None
+ args = op.getarglist()
+ if op.is_guard():
+ args = args + op.getfailargs()
+
+ for a in args:
+ self.import_box(a, inputargs, short_jumpargs, jumpargs)
+
+ def jump_to_already_compiled_trace(self, jumpop, patchguardop):
assert jumpop.getopnum() == rop.JUMP
cell_token = jumpop.getdescr()
@@ -543,72 +514,84 @@
return True
args = jumpop.getarglist()
- modifier = VirtualStateAdder(self.optimizer)
- virtual_state = modifier.get_virtual_state(args)
+ virtual_state = self.get_virtual_state(args)
+ values = [self.getvalue(arg)
+ for arg in jumpop.getarglist()]
debug_start('jit-log-virtualstate')
- virtual_state.debug_print("Looking for ")
+ virtual_state.debug_print("Looking for ", metainterp_sd=self.optimizer.metainterp_sd)
for target in cell_token.target_tokens:
if not target.virtual_state:
continue
- ok = False
extra_guards = []
- bad = {}
- debugmsg = 'Did not match '
- if target.virtual_state.generalization_of(virtual_state, bad):
- ok = True
- debugmsg = 'Matched '
- else:
- try:
- cpu = self.optimizer.cpu
- target.virtual_state.generate_guards(virtual_state,
- args, cpu,
- extra_guards)
+ try:
+ cpu = self.optimizer.cpu
+ state = target.virtual_state.generate_guards(virtual_state,
+ values,
+ cpu)
- ok = True
+ extra_guards = state.extra_guards
+ if extra_guards:
debugmsg = 'Guarded to match '
- except InvalidLoop:
- pass
- target.virtual_state.debug_print(debugmsg, bad)
+ else:
+ debugmsg = 'Matched '
+ except VirtualStatesCantMatch, e:
+ debugmsg = 'Did not match:\n%s\n' % (e.msg, )
+ target.virtual_state.debug_print(debugmsg, e.state.bad, metainterp_sd=self.optimizer.metainterp_sd)
+ continue
- if ok:
- debug_stop('jit-log-virtualstate')
+ assert patchguardop is not None or (extra_guards == [] and len(target.short_preamble) == 1)
- values = [self.getvalue(arg)
- for arg in jumpop.getarglist()]
- args = target.virtual_state.make_inputargs(values, self.optimizer,
- keyboxes=True)
- short_inputargs = target.short_preamble[0].getarglist()
- inliner = Inliner(short_inputargs, args)
+ target.virtual_state.debug_print(debugmsg, {})
- for guard in extra_guards:
- if guard.is_guard():
- descr = target.resume_at_jump_descr.clone_if_mutable()
- inliner.inline_descr_inplace(descr)
- guard.setdescr(descr)
- self.optimizer.send_extra_operation(guard)
+ debug_stop('jit-log-virtualstate')
- try:
- for shop in target.short_preamble[1:]:
- newop = inliner.inline_op(shop)
- self.optimizer.send_extra_operation(newop)
- if shop.result in target.assumed_classes:
- classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu)
- if not classbox or not classbox.same_constant(target.assumed_classes[shop.result]):
- raise InvalidLoop('The class of an opaque pointer at the end ' +
- 'of the bridge does not mach the class ' +
- 'it has at the start of the target loop')
- except InvalidLoop:
- #debug_print("Inlining failed unexpectedly",
- # "jumping to preamble instead")
- assert cell_token.target_tokens[0].virtual_state is None
- jumpop.setdescr(cell_token.target_tokens[0])
- self.optimizer.send_extra_operation(jumpop)
- return True
+ args = target.virtual_state.make_inputargs(values, self.optimizer,
+ keyboxes=True)
+ short_inputargs = target.short_preamble[0].getarglist()
+ inliner = Inliner(short_inputargs, args)
+
+ for guard in extra_guards:
+ if guard.is_guard():
+ descr = patchguardop.getdescr().clone_if_mutable()
+ guard.setdescr(descr)
+ self.optimizer.send_extra_operation(guard)
+
+ try:
+ # NB: the short_preamble ends with a jump
+ self._inline_short_preamble(target.short_preamble, inliner, patchguardop, target.assumed_classes)
+ except InvalidLoop:
+ #debug_print("Inlining failed unexpectedly",
+ # "jumping to preamble instead")
+ assert cell_token.target_tokens[0].virtual_state is None
+ jumpop.setdescr(cell_token.target_tokens[0])
+ self.optimizer.send_extra_operation(jumpop)
+ return True
debug_stop('jit-log-virtualstate')
return False
+ def _inline_short_preamble(self, short_preamble, inliner, patchguardop, assumed_classes):
+ i = 1
+ # XXX this is intentiontal :-(. short_preamble can change during the
+ # loop in some cases
+ while i < len(short_preamble):
+ shop = short_preamble[i]
+ newop = inliner.inline_op(shop)
+ if newop.is_guard():
+ if not patchguardop:
+ raise InvalidLoop("would like to have short preamble, but it has a guard and there's no guard_future_condition")
+ descr = patchguardop.getdescr().clone_if_mutable()
+ newop.setdescr(descr)
+ self.optimizer.send_extra_operation(newop)
+ if shop.result in assumed_classes:
+ classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu)
+ if not classbox or not classbox.same_constant(assumed_classes[shop.result]):
+ raise InvalidLoop('The class of an opaque pointer before the jump ' +
+ 'does not mach the class ' +
+ 'it has at the start of the target loop')
+ i += 1
+
class ValueImporter(object):
def __init__(self, unroll, value, op):
diff --git a/rpython/jit/metainterp/optimizeopt/virtualize.py b/rpython/jit/metainterp/optimizeopt/virtualize.py
--- a/rpython/jit/metainterp/optimizeopt/virtualize.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualize.py
@@ -10,7 +10,7 @@
from rpython.jit.metainterp.optimizeopt.rawbuffer import RawBuffer, InvalidRawOperation
from rpython.jit.metainterp.resoperation import rop, ResOperation
-from rpython.rlib.objectmodel import we_are_translated
+from rpython.rlib.objectmodel import we_are_translated, specialize
class AbstractVirtualValue(optimizer.OptValue):
@@ -45,27 +45,17 @@
return value
return OptValue(self.force_box(optforce))
- def get_args_for_fail(self, modifier):
+ def visitor_walk_recursive(self, visitor):
# checks for recursion: it is False unless
# we have already seen the very same keybox
- if self.box is None and not modifier.already_seen_virtual(self.keybox):
- self._get_args_for_fail(modifier)
+ if self.box is None and not visitor.already_seen_virtual(self.keybox):
+ self._visitor_walk_recursive(visitor)
- def _get_args_for_fail(self, modifier):
+ def _visitor_walk_recursive(self, visitor):
raise NotImplementedError("abstract base")
- def make_virtual_info(self, modifier, fieldnums):
- if fieldnums is None:
- return self._make_virtual(modifier)
- vinfo = self._cached_vinfo
- if vinfo is not None and vinfo.equals(fieldnums):
- return vinfo
- vinfo = self._make_virtual(modifier)
- vinfo.set_content(fieldnums)
- self._cached_vinfo = vinfo
- return vinfo
-
- def _make_virtual(self, modifier):
+ @specialize.argtype(1)
+ def _visitor_dispatch_virtual_type(self, visitor):
raise NotImplementedError("abstract base")
def _really_force(self, optforce):
@@ -202,13 +192,13 @@
self._cached_sorted_fields = lst
return lst
- def _get_args_for_fail(self, modifier):
+ def _visitor_walk_recursive(self, visitor):
lst = self._get_field_descr_list()
fieldboxes = [self._fields[ofs].get_key_box() for ofs in lst]
- modifier.register_virtual_fields(self.keybox, fieldboxes)
+ visitor.register_virtual_fields(self.keybox, fieldboxes)
for ofs in lst:
fieldvalue = self._fields[ofs]
- fieldvalue.get_args_for_fail(modifier)
+ fieldvalue.visitor_walk_recursive(visitor)
class VirtualValue(AbstractVirtualStructValue):
level = optimizer.LEVEL_KNOWNCLASS
@@ -218,9 +208,10 @@
assert isinstance(known_class, Const)
self.known_class = known_class
- def _make_virtual(self, modifier):
+ @specialize.argtype(1)
+ def _visitor_dispatch_virtual_type(self, visitor):
fielddescrs = self._get_field_descr_list()
- return modifier.make_virtual(self.known_class, fielddescrs)
+ return visitor.visit_virtual(self.known_class, fielddescrs)
def _get_descr(self):
return vtable2descr(self.cpu, self.known_class.getint())
@@ -238,9 +229,10 @@
AbstractVirtualStructValue.__init__(self, cpu, keybox, source_op)
self.structdescr = structdescr
- def _make_virtual(self, modifier):
+ @specialize.argtype(1)
+ def _visitor_dispatch_virtual_type(self, visitor):
fielddescrs = self._get_field_descr_list()
- return modifier.make_vstruct(self.structdescr, fielddescrs)
+ return visitor.visit_vstruct(self.structdescr, fielddescrs)
def _get_descr(self):
return self.structdescr
@@ -260,15 +252,15 @@
def set_item_value(self, i, newval):
raise NotImplementedError
- def _get_args_for_fail(self, modifier):
+ def _visitor_walk_recursive(self, visitor):
itemboxes = []
for i in range(self.getlength()):
itemvalue = self.get_item_value(i)
itemboxes.append(itemvalue.get_key_box())
- modifier.register_virtual_fields(self.keybox, itemboxes)
+ visitor.register_virtual_fields(self.keybox, itemboxes)
for i in range(self.getlength()):
itemvalue = self.get_item_value(i)
- itemvalue.get_args_for_fail(modifier)
+ itemvalue.visitor_walk_recursive(visitor)
class VArrayValue(AbstractVArrayValue):
@@ -326,8 +318,9 @@
descr=self.arraydescr)
optforce.emit_operation(op)
- def _make_virtual(self, modifier):
- return modifier.make_varray(self.arraydescr)
+ @specialize.argtype(1)
+ def _visitor_dispatch_virtual_type(self, visitor):
+ return visitor.visit_varray(self.arraydescr)
class VArrayStructValue(AbstractVirtualValue):
@@ -373,16 +366,16 @@
descrs.append(item_descrs)
return descrs
- def _get_args_for_fail(self, modifier):
+ def _visitor_walk_recursive(self, visitor):
itemdescrs = self._get_list_of_descrs()
itemboxes = []
for i in range(len(self._items)):
for descr in itemdescrs[i]:
itemboxes.append(self._items[i][descr].get_key_box())
- modifier.register_virtual_fields(self.keybox, itemboxes)
+ visitor.register_virtual_fields(self.keybox, itemboxes)
for i in range(len(self._items)):
for descr in itemdescrs[i]:
- self._items[i][descr].get_args_for_fail(modifier)
+ self._items[i][descr].visitor_walk_recursive(visitor)
def force_at_end_of_preamble(self, already_forced, optforce):
if self in already_forced:
@@ -393,8 +386,9 @@
self._items[index][descr] = self._items[index][descr].force_at_end_of_preamble(already_forced, optforce)
return self
- def _make_virtual(self, modifier):
- return modifier.make_varraystruct(self.arraydescr, self._get_list_of_descrs())
+ @specialize.argtype(1)
+ def _visitor_dispatch_virtual_type(self, visitor):
+ return visitor.visit_varraystruct(self.arraydescr, self._get_list_of_descrs())
class VRawBufferValue(AbstractVArrayValue):
@@ -442,11 +436,12 @@
descr=descr)
optforce.emit_operation(op)
- def _make_virtual(self, modifier):
+ @specialize.argtype(1)
+ def _visitor_dispatch_virtual_type(self, visitor):
# I *think* we need to make a copy of offsets and descrs because we
# want a snapshot of the virtual state right now: if we grow more
# elements later, we don't want them to go in this virtual state
- return modifier.make_vrawbuffer(self.size,
+ return visitor.visit_vrawbuffer(self.size,
self.buffer.offsets[:],
self.buffer.descrs[:])
@@ -474,13 +469,14 @@
def getitem_raw(self, offset, length, descr):
return self.rawbuffer_value.getitem_raw(self.offset+offset, length, descr)
- def _get_args_for_fail(self, modifier):
+ def _visitor_walk_recursive(self, visitor):
box = self.rawbuffer_value.get_key_box()
- modifier.register_virtual_fields(self.keybox, [box])
- self.rawbuffer_value.get_args_for_fail(modifier)
+ visitor.register_virtual_fields(self.keybox, [box])
+ self.rawbuffer_value.visitor_walk_recursive(visitor)
- def _make_virtual(self, modifier):
- return modifier.make_vrawslice(self.offset)
+ @specialize.argtype(1)
+ def _visitor_dispatch_virtual_type(self, visitor):
+ return visitor.visit_vrawslice(self.offset)
class OptVirtualize(optimizer.Optimization):
diff --git a/rpython/jit/metainterp/optimizeopt/virtualstate.py b/rpython/jit/metainterp/optimizeopt/virtualstate.py
--- a/rpython/jit/metainterp/optimizeopt/virtualstate.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualstate.py
@@ -1,46 +1,74 @@
-from rpython.jit.metainterp import resume
-from rpython.jit.metainterp.history import BoxInt, ConstInt, BoxPtr, Const
-from rpython.jit.metainterp.optimize import InvalidLoop
+from rpython.jit.metainterp.walkvirtual import VirtualVisitor
+from rpython.jit.metainterp.history import (BoxInt, ConstInt, BoxPtr, Const,
+ ConstPtr, ConstFloat)
from rpython.jit.metainterp.optimizeopt import virtualize
from rpython.jit.metainterp.optimizeopt.intutils import IntUnbounded
from rpython.jit.metainterp.optimizeopt.optimizer import (LEVEL_CONSTANT,
- LEVEL_KNOWNCLASS, LEVEL_NONNULL, LEVEL_UNKNOWN)
+ LEVEL_KNOWNCLASS, LEVEL_NONNULL, LEVEL_UNKNOWN, OptValue)
from rpython.jit.metainterp.resoperation import rop, ResOperation
from rpython.rlib.debug import debug_start, debug_stop, debug_print
-from rpython.rlib.objectmodel import we_are_translated
+from rpython.rlib.objectmodel import we_are_translated, import_from_mixin
class BadVirtualState(Exception):
pass
+class VirtualStatesCantMatch(Exception):
+ def __init__(self, msg='?', state=None):
+ self.msg = msg
+ self.state = state
-class AbstractVirtualStateInfo(resume.AbstractVirtualInfo):
+class GenerateGuardState(object):
+ def __init__(self, cpu=None, guards=None, renum=None, bad=None):
+ self.cpu = cpu
+ if guards is None:
+ guards = []
+ self.extra_guards = guards
+ if renum is None:
+ renum = {}
+ self.renum = renum
+ if bad is None:
+ bad = {}
+ self.bad = bad
+
+class AbstractVirtualStateInfo(object):
position = -1
- def generalization_of(self, other, renum, bad):
+ def generate_guards(self, other, value, state):
+ """ generate guards (output in the list extra_guards) that make runtime
+ values of the shape other match the shape of self. if that's not
+ possible, VirtualStatesCantMatch is thrown and bad gets keys set which
+ parts of the state are the problem.
+
+ the function can peek into value (and particularly also the boxes in
+ the value) as a guiding heuristic whether making such guards makes
+ sense. if None is passed in for value, no guard is ever generated, and
+ this function degenerates to a generalization check."""
+ assert value is None or isinstance(value, OptValue)
assert self.position != -1
- if self.position in renum:
- result = renum[self.position] == other.position
+ if self.position in state.renum:
+ if state.renum[self.position] != other.position:
+ state.bad[self] = state.bad[other] = None
+ raise VirtualStatesCantMatch(
+ 'The numbering of the virtual states does not ' +
+ 'match. This means that two virtual fields ' +
+ 'have been set to the same Box in one of the ' +
+ 'virtual states but not in the other.',
+ state)
else:
- renum[self.position] = other.position
- result = self.generalization_of_renumbering_done(other, renum, bad)
- if not result:
- bad[self] = bad[other] = None
- return result
+ state.renum[self.position] = other.position
+ try:
+ self._generate_guards(other, value, state)
+ except VirtualStatesCantMatch, e:
+ state.bad[self] = state.bad[other] = None
+ if e.state is None:
+ e.state = state
+ raise e
- def generate_guards(self, other, box, cpu, extra_guards, renum):
- if self.generalization_of(other, renum, {}):
- return
- if renum[self.position] != other.position:
- raise InvalidLoop('The numbering of the virtual states does not ' +
- 'match. This means that two virtual fields ' +
- 'have been set to the same Box in one of the ' +
- 'virtual states but not in the other.')
- self._generate_guards(other, box, cpu, extra_guards)
-
- def _generate_guards(self, other, box, cpu, extra_guards):
- raise InvalidLoop('Generating guards for making the VirtualStates ' +
- 'at hand match have not been implemented')
+ def _generate_guards(self, other, value, state):
+ raise VirtualStatesCantMatch(
+ 'Generating guards for making the VirtualStates ' +
+ 'at hand match have not been implemented')
def enum_forced_boxes(self, boxes, value, optimizer):
raise NotImplementedError
@@ -55,7 +83,7 @@
def _enum(self, virtual_state):
raise NotImplementedError
- def debug_print(self, indent, seen, bad):
+ def debug_print(self, indent, seen, bad, metainterp_sd):
mark = ''
if self in bad:
mark = '*'
@@ -63,7 +91,7 @@
if self not in seen:
seen[self] = True
for s in self.fieldstate:
- s.debug_print(indent + " ", seen, bad)
+ s.debug_print(indent + " ", seen, bad, metainterp_sd)
else:
debug_print(indent + " ...")
@@ -75,26 +103,31 @@
def __init__(self, fielddescrs):
self.fielddescrs = fielddescrs
- def generalization_of_renumbering_done(self, other, renum, bad):
- if not self._generalization_of(other):
- return False
+ def _generate_guards(self, other, value, state):
+ if not self._generalization_of_structpart(other):
+ raise VirtualStatesCantMatch("different kinds of structs")
assert isinstance(other, AbstractVirtualStructStateInfo)
assert len(self.fielddescrs) == len(self.fieldstate)
assert len(other.fielddescrs) == len(other.fieldstate)
+ if value is not None:
+ assert isinstance(value, virtualize.AbstractVirtualStructValue)
+ assert value.is_virtual()
+
if len(self.fielddescrs) != len(other.fielddescrs):
- return False
+ raise VirtualStatesCantMatch("field descrs don't match")
for i in range(len(self.fielddescrs)):
if other.fielddescrs[i] is not self.fielddescrs[i]:
- return False
- if not self.fieldstate[i].generalization_of(other.fieldstate[i],
- renum, bad):
- return False
+ raise VirtualStatesCantMatch("field descrs don't match")
+ if value is not None:
+ v = value._fields[self.fielddescrs[i]] # must be there
+ else:
+ v = None
+ self.fieldstate[i].generate_guards(other.fieldstate[i], v, state)
- return True
- def _generalization_of(self, other):
+ def _generalization_of_structpart(self, other):
raise NotImplementedError
def enum_forced_boxes(self, boxes, value, optimizer):
@@ -121,10 +154,11 @@
AbstractVirtualStructStateInfo.__init__(self, fielddescrs)
self.known_class = known_class
- def _generalization_of(self, other):
+ def _generalization_of_structpart(self, other):
return (isinstance(other, VirtualStateInfo) and
self.known_class.same_constant(other.known_class))
+
def debug_header(self, indent):
debug_print(indent + 'VirtualStateInfo(%d):' % self.position)
@@ -134,7 +168,7 @@
AbstractVirtualStructStateInfo.__init__(self, fielddescrs)
self.typedescr = typedescr
- def _generalization_of(self, other):
+ def _generalization_of_structpart(self, other):
return (isinstance(other, VStructStateInfo) and
self.typedescr is other.typedescr)
@@ -147,20 +181,20 @@
def __init__(self, arraydescr):
self.arraydescr = arraydescr
- def _generalization_of(self, other):
- return (isinstance(other, VArrayStateInfo) and
- self.arraydescr is other.arraydescr)
-
- def generalization_of_renumbering_done(self, other, renum, bad):
- if not self._generalization_of(other):
- return False
+ def _generate_guards(self, other, value, state):
+ if not isinstance(other, VArrayStateInfo):
+ raise VirtualStatesCantMatch("other is not an array")
+ if self.arraydescr is not other.arraydescr:
+ raise VirtualStatesCantMatch("other is a different kind of array")
if len(self.fieldstate) != len(other.fieldstate):
- return False
+ raise VirtualStatesCantMatch("other has a different length")
+ v = None
for i in range(len(self.fieldstate)):
- if not self.fieldstate[i].generalization_of(other.fieldstate[i],
- renum, bad):
- return False
- return True
+ if value is not None:
+ assert isinstance(value, virtualize.VArrayValue)
+ v = value._items[i]
+ self.fieldstate[i].generate_guards(other.fieldstate[i],
+ v, state)
def enum_forced_boxes(self, boxes, value, optimizer):
if not isinstance(value, virtualize.VArrayValue):
@@ -188,30 +222,31 @@
self.arraydescr = arraydescr
self.fielddescrs = fielddescrs
- def generalization_of_renumbering_done(self, other, renum, bad):
- if not self._generalization_of(other):
- return False
+ def _generate_guards(self, other, value, state):
+ if not isinstance(other, VArrayStructStateInfo):
+ raise VirtualStatesCantMatch("other is not an VArrayStructStateInfo")
+ if self.arraydescr is not other.arraydescr:
+ raise VirtualStatesCantMatch("other is a different kind of array")
- assert isinstance(other, VArrayStructStateInfo)
if len(self.fielddescrs) != len(other.fielddescrs):
- return False
+ raise VirtualStatesCantMatch("other has a different length")
p = 0
+ v = None
for i in range(len(self.fielddescrs)):
if len(self.fielddescrs[i]) != len(other.fielddescrs[i]):
- return False
+ raise VirtualStatesCantMatch("other has a different length")
for j in range(len(self.fielddescrs[i])):
- if self.fielddescrs[i][j] is not other.fielddescrs[i][j]:
- return False
- if not self.fieldstate[p].generalization_of(other.fieldstate[p],
- renum, bad):
- return False
+ descr = self.fielddescrs[i][j]
+ if descr is not other.fielddescrs[i][j]:
+ raise VirtualStatesCantMatch("other is a different kind of array")
+ if value is not None:
+ assert isinstance(value, virtualize.VArrayStructValue)
+ v = value._items[i][descr]
+ self.fieldstate[p].generate_guards(other.fieldstate[p],
+ v,
+ state)
p += 1
- return True
-
- def _generalization_of(self, other):
- return (isinstance(other, VArrayStructStateInfo) and
- self.arraydescr is other.arraydescr)
def _enum(self, virtual_state):
for s in self.fieldstate:
@@ -256,97 +291,109 @@
self.position_in_notvirtuals = -1
self.lenbound = value.lenbound
- def generalization_of_renumbering_done(self, other, renum, bad):
+
+ def _generate_guards(self, other, value, state):
+ if value is None or self.is_opaque:
+ box = None # generating guards for opaque pointers isn't safe
+ else:
+ box = value.box
# XXX This will always retrace instead of forcing anything which
# might be what we want sometimes?
if not isinstance(other, NotVirtualStateInfo):
- return False
- if other.level < self.level:
- return False
- if self.level == LEVEL_CONSTANT:
- if not self.constbox.same_constant(other.constbox):
- return False
+ raise VirtualStatesCantMatch(
+ 'The VirtualStates does not match as a ' +
+ 'virtual appears where a pointer is needed ' +
+ 'and it is too late to force it.')
+
+
+ extra_guards = state.extra_guards
+ cpu = state.cpu
+ if self.lenbound and not self.lenbound.generalization_of(other.lenbound):
+ raise VirtualStatesCantMatch("length bound does not match")
+
+ if self.level == LEVEL_UNKNOWN:
+ # confusingly enough, this is done also for pointers
+ # which have the full range as the "bound", so it always works
+ return self._generate_guards_intbounds(other, box, extra_guards)
+
+ # the following conditions often peek into the runtime value that the
+ # box had when tracing. This value is only used as an educated guess.
+ # It is used here to choose between either emitting a guard and jumping
+ # to an existing compiled loop or retracing the loop. Both alternatives
+ # will always generate correct behaviour, but performance will differ.
+ elif self.level == LEVEL_NONNULL:
+ if other.level == LEVEL_UNKNOWN:
+ if box is not None and box.nonnull():
+ op = ResOperation(rop.GUARD_NONNULL, [box], None)
+ extra_guards.append(op)
+ return
+ else:
+ raise VirtualStatesCantMatch("other not known to be nonnull")
+ elif other.level == LEVEL_NONNULL:
+ return
+ elif other.level == LEVEL_KNOWNCLASS:
+ return # implies nonnull
+ else:
+ assert other.level == LEVEL_CONSTANT
+ assert other.constbox
+ if not other.constbox.nonnull():
+ raise VirtualStatesCantMatch("constant is null")
+ return
+
elif self.level == LEVEL_KNOWNCLASS:
- if not self.known_class.same_constant(other.known_class):
- return False
- elif self.level == LEVEL_NONNULL:
- if other.constbox and not other.constbox.nonnull():
- return False
+ if other.level == LEVEL_UNKNOWN:
+ if (box and box.nonnull() and
+ self.known_class.same_constant(cpu.ts.cls_of_box(box))):
+ op = ResOperation(rop.GUARD_NONNULL, [box], None)
+ extra_guards.append(op)
+ op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
+ extra_guards.append(op)
+ return
+ else:
+ raise VirtualStatesCantMatch("other's class is unknown")
+ elif other.level == LEVEL_NONNULL:
+ if box and self.known_class.same_constant(cpu.ts.cls_of_box(box)):
+ op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
+ extra_guards.append(op)
+ return
+ else:
+ raise VirtualStatesCantMatch("other's class is unknown")
+ elif other.level == LEVEL_KNOWNCLASS:
+ if self.known_class.same_constant(other.known_class):
+ return
+ raise VirtualStatesCantMatch("classes don't match")
+ else:
+ assert other.level == LEVEL_CONSTANT
+ if (other.constbox.nonnull() and
+ self.known_class.same_constant(cpu.ts.cls_of_box(other.constbox))):
+ return
+ else:
+ raise VirtualStatesCantMatch("classes don't match")
- if not self.intbound.contains_bound(other.intbound):
- return False
- if self.lenbound and other.lenbound:
- if self.lenbound.mode != other.lenbound.mode or \
- self.lenbound.descr != other.lenbound.descr or \
- not self.lenbound.bound.contains_bound(other.lenbound.bound):
- return False
- elif self.lenbound:
- return False
- return True
+ else:
+ assert self.level == LEVEL_CONSTANT
+ if other.level == LEVEL_CONSTANT:
+ if self.constbox.same_constant(other.constbox):
+ return
+ raise VirtualStatesCantMatch("different constants")
+ if box is not None and self.constbox.same_constant(box.constbox()):
+ op = ResOperation(rop.GUARD_VALUE, [box, self.constbox], None)
+ extra_guards.append(op)
+ return
+ else:
+ raise VirtualStatesCantMatch("other not constant")
+ assert 0, "unreachable"
- def _generate_guards(self, other, box, cpu, extra_guards):
- if not isinstance(other, NotVirtualStateInfo):
- raise InvalidLoop('The VirtualStates does not match as a ' +
- 'virtual appears where a pointer is needed ' +
- 'and it is too late to force it.')
-
- if self.lenbound or other.lenbound:
- raise InvalidLoop('The array length bounds does not match.')
-
- if self.is_opaque:
- raise InvalidLoop('Generating guards for opaque pointers is not safe')
-
- if self.level == LEVEL_KNOWNCLASS and \
- box.nonnull() and \
- self.known_class.same_constant(cpu.ts.cls_of_box(box)):
- # Note: This is only a hint on what the class of box was
- # during the trace. There are actually no guarentees that this
- # box realy comes from a trace. The hint is used here to choose
- # between either eimtting a guard_class and jumping to an
- # excisting compiled loop or retracing the loop. Both
- # alternatives will always generate correct behaviour, but
- # performace will differ.
- op = ResOperation(rop.GUARD_NONNULL, [box], None)
- extra_guards.append(op)
- op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
- extra_guards.append(op)
+ def _generate_guards_intbounds(self, other, box, extra_guards):
+ if self.intbound.contains_bound(other.intbound):
return
-
- if (self.level == LEVEL_NONNULL and
- other.level == LEVEL_UNKNOWN and
- isinstance(box, BoxPtr) and
- box.nonnull()):
- op = ResOperation(rop.GUARD_NONNULL, [box], None)
- extra_guards.append(op)
+ if (box is not None and isinstance(box, BoxInt) and
+ self.intbound.contains(box.getint())):
+ # this may generate a few more guards than needed, but they are
+ # optimized away when emitting them
+ self.intbound.make_guards(box, extra_guards)
return
-
- if (self.level == LEVEL_UNKNOWN and
- other.level == LEVEL_UNKNOWN and
- isinstance(box, BoxInt) and
- self.intbound.contains(box.getint())):
- if self.intbound.has_lower:
- bound = self.intbound.lower
- if not (other.intbound.has_lower and
- other.intbound.lower >= bound):
- res = BoxInt()
- op = ResOperation(rop.INT_GE, [box, ConstInt(bound)], res)
- extra_guards.append(op)
- op = ResOperation(rop.GUARD_TRUE, [res], None)
- extra_guards.append(op)
- if self.intbound.has_upper:
- bound = self.intbound.upper
- if not (other.intbound.has_upper and
- other.intbound.upper <= bound):
- res = BoxInt()
- op = ResOperation(rop.INT_LE, [box, ConstInt(bound)], res)
- extra_guards.append(op)
- op = ResOperation(rop.GUARD_TRUE, [res], None)
- extra_guards.append(op)
- return
-
- # Remaining cases are probably not interesting
- raise InvalidLoop('Generating guards for making the VirtualStates ' +
- 'at hand match have not been implemented')
+ raise VirtualStatesCantMatch("intbounds don't match")
def enum_forced_boxes(self, boxes, value, optimizer):
if self.level == LEVEL_CONSTANT:
@@ -363,25 +410,37 @@
def _enum(self, virtual_state):
if self.level == LEVEL_CONSTANT:
return
- self.position_in_notvirtuals = len(virtual_state.notvirtuals)
- virtual_state.notvirtuals.append(self)
+ self.position_in_notvirtuals = virtual_state.numnotvirtuals
+ virtual_state.numnotvirtuals += 1
- def debug_print(self, indent, seen, bad):
+ def debug_print(self, indent, seen, bad, metainterp_sd=None):
mark = ''
if self in bad:
mark = '*'
- if we_are_translated():
- l = {LEVEL_UNKNOWN: 'Unknown',
- LEVEL_NONNULL: 'NonNull',
- LEVEL_KNOWNCLASS: 'KnownClass',
- LEVEL_CONSTANT: 'Constant',
- }[self.level]
+ if self.level == LEVEL_UNKNOWN:
More information about the pypy-commit
mailing list