[pypy-commit] pypy default: Merge in force-virtual-state
sbauman
pypy.commits at gmail.com
Thu Sep 8 11:34:28 EDT 2016
Author: Spenser Andrew Bauman <sabauma at gmail.com>
Branch:
Changeset: r86960:990479cf9d20
Date: 2016-09-08 11:33 -0400
http://bitbucket.org/pypy/pypy/changeset/990479cf9d20/
Log: Merge in force-virtual-state
diff --git a/rpython/jit/metainterp/optimizeopt/info.py b/rpython/jit/metainterp/optimizeopt/info.py
--- a/rpython/jit/metainterp/optimizeopt/info.py
+++ b/rpython/jit/metainterp/optimizeopt/info.py
@@ -365,6 +365,13 @@
def visitor_dispatch_virtual_type(self, visitor):
raise NotImplementedError("abstract")
+ def make_guards(self, op, short, optimizer):
+ from rpython.jit.metainterp.optimizeopt.optimizer import CONST_0
+ op = ResOperation(rop.INT_EQ, [op, CONST_0])
+ short.append(op)
+ op = ResOperation(rop.GUARD_FALSE, [op])
+ short.append(op)
+
class RawBufferPtrInfo(AbstractRawPtrInfo):
buffer = None
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
@@ -16,7 +16,7 @@
from rpython.rlib.debug import debug_print, debug_start, debug_stop,\
have_debug_prints
-class UnrollableOptimizer(Optimizer):
+class UnrollableOptimizer(Optimizer):
def force_op_from_preamble(self, preamble_op):
if isinstance(preamble_op, PreambleOp):
if self.optunroll.short_preamble_producer is None:
@@ -120,7 +120,8 @@
assert op.get_forwarded() is None
if check_newops:
assert not self.optimizer._newoperations
-
+
+
def optimize_preamble(self, trace, runtime_boxes, call_pure_results, memo):
info, newops = self.optimizer.propagate_all_forward(
trace.get_iter(), call_pure_results, flush=False)
@@ -156,7 +157,7 @@
current_vs = self.get_virtual_state(end_jump.getarglist())
# pick the vs we want to jump to
assert isinstance(celltoken, JitCellToken)
-
+
target_virtual_state = self.pick_virtual_state(current_vs,
state.virtual_state,
celltoken.target_tokens)
@@ -180,17 +181,27 @@
self.jump_to_preamble(celltoken, end_jump, info)
return (UnrollInfo(target_token, label_op, extra_same_as,
self.optimizer.quasi_immutable_deps),
- self.optimizer._newoperations)
+ self.optimizer._newoperations)
try:
- new_virtual_state = self.jump_to_existing_trace(end_jump, label_op,
- state.runtime_boxes)
+ new_virtual_state = self.jump_to_existing_trace(
+ end_jump, label_op, state.runtime_boxes, force_boxes=False)
except InvalidLoop:
# inlining short preamble failed, jump to preamble
self.jump_to_preamble(celltoken, end_jump, info)
return (UnrollInfo(target_token, label_op, extra_same_as,
self.optimizer.quasi_immutable_deps),
self.optimizer._newoperations)
+
+ if new_virtual_state is not None:
+ # Attempt to force virtual boxes in order to avoid jumping
+ # to the preamble.
+ try:
+ new_virtual_state = self.jump_to_existing_trace(
+ end_jump, label_op, state.runtime_boxes, force_boxes=True)
+ except InvalidLoop:
+ pass
+
if new_virtual_state is not None:
self.jump_to_preamble(celltoken, end_jump, info)
return (UnrollInfo(target_token, label_op, extra_same_as,
@@ -199,7 +210,7 @@
self.disable_retracing_if_max_retrace_guards(
self.optimizer._newoperations, target_token)
-
+
return (UnrollInfo(target_token, label_op, extra_same_as,
self.optimizer.quasi_immutable_deps),
self.optimizer._newoperations)
@@ -241,7 +252,8 @@
for a in jump_op.getarglist():
self.optimizer.force_box_for_end_of_preamble(a)
try:
- vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes)
+ vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes,
+ force_boxes=False)
except InvalidLoop:
return self.jump_to_preamble(cell_token, jump_op, info)
if vs is None:
@@ -252,6 +264,14 @@
cell_token.retraced_count += 1
debug_print('Retracing (%d/%d)' % (cell_token.retraced_count, limit))
else:
+ # Try forcing boxes to avoid jumping to the preamble
+ try:
+ vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes,
+ force_boxes=True)
+ except InvalidLoop:
+ pass
+ if vs is None:
+ return info, self.optimizer._newoperations[:]
debug_print("Retrace count reached, jumping to preamble")
return self.jump_to_preamble(cell_token, jump_op, info)
exported_state = self.export_state(info.jump_op.getarglist(),
@@ -288,7 +308,7 @@
return info, self.optimizer._newoperations[:]
- def jump_to_existing_trace(self, jump_op, label_op, runtime_boxes):
+ def jump_to_existing_trace(self, jump_op, label_op, runtime_boxes, force_boxes=False):
jitcelltoken = jump_op.getdescr()
assert isinstance(jitcelltoken, JitCellToken)
virtual_state = self.get_virtual_state(jump_op.getarglist())
@@ -299,7 +319,8 @@
continue
try:
extra_guards = target_virtual_state.generate_guards(
- virtual_state, args, runtime_boxes, self.optimizer)
+ virtual_state, args, runtime_boxes, self.optimizer,
+ force_boxes=force_boxes)
patchguardop = self.optimizer.patchguardop
for guard in extra_guards.extra_guards:
if isinstance(guard, GuardResOp):
@@ -308,8 +329,18 @@
self.send_extra_operation(guard)
except VirtualStatesCantMatch:
continue
- args, virtuals = target_virtual_state.make_inputargs_and_virtuals(
- args, self.optimizer)
+
+ # When force_boxes == True, creating the virtual args can fail when
+ # components of the virtual state alias. If this occurs, we must
+ # recompute the virtual state as boxes will have been forced.
+ try:
+ args, virtuals = target_virtual_state.make_inputargs_and_virtuals(
+ args, self.optimizer, force_boxes=force_boxes)
+ except VirtualStatesCantMatch:
+ assert force_boxes
+ virtual_state = self.get_virtual_state(args)
+ continue
+
short_preamble = target_token.short_preamble
try:
extra = self.inline_short_preamble(args + virtuals, args,
@@ -452,7 +483,7 @@
# by short preamble
label_args = exported_state.virtual_state.make_inputargs(
targetargs, self.optimizer)
-
+
self.short_preamble_producer = ShortPreambleBuilder(
label_args, exported_state.short_boxes,
exported_state.short_inputargs, exported_state.exported_infos,
@@ -497,7 +528,7 @@
* runtime_boxes - runtime values for boxes, necessary when generating
guards to jump to
"""
-
+
def __init__(self, end_args, next_iteration_args, virtual_state,
exported_infos, short_boxes, renamed_inputargs,
short_inputargs, runtime_boxes, memo):
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
@@ -4,7 +4,7 @@
ArrayStructInfo, AbstractStructPtrInfo
from rpython.jit.metainterp.optimizeopt.intutils import \
MININT, MAXINT, IntBound, IntLowerBound
-from rpython.jit.metainterp.resoperation import rop, ResOperation,\
+from rpython.jit.metainterp.resoperation import rop, ResOperation, \
InputArgInt, InputArgRef, InputArgFloat
from rpython.rlib.debug import debug_print
@@ -20,7 +20,7 @@
class GenerateGuardState(object):
- def __init__(self, optimizer=None, guards=None, renum=None, bad=None):
+ def __init__(self, optimizer=None, guards=None, renum=None, bad=None, force_boxes=False):
self.optimizer = optimizer
self.cpu = optimizer.cpu
if guards is None:
@@ -32,6 +32,7 @@
if bad is None:
bad = {}
self.bad = bad
+ self.force_boxes = force_boxes
def get_runtime_item(self, box, descr, i):
array = box.getref_base()
@@ -303,7 +304,7 @@
opinfo = state.optimizer.getptrinfo(box)
assert isinstance(opinfo, ArrayPtrInfo)
else:
- opinfo = None
+ opinfo = None
for i in range(self.length):
for descr in self.fielddescrs:
index = i * len(self.fielddescrs) + descr.get_index()
@@ -514,6 +515,8 @@
NotVirtualStateInfo.__init__(self, cpu, type, info)
def _generate_guards(self, other, box, runtime_box, state):
+ if state.force_boxes and isinstance(other, VirtualStateInfo):
+ return self._generate_virtual_guards(other, box, runtime_box, state)
if not isinstance(other, NotVirtualStateInfoPtr):
raise VirtualStatesCantMatch(
'The VirtualStates does not match as a ' +
@@ -545,6 +548,23 @@
# to an existing compiled loop or retracing the loop. Both alternatives
# will always generate correct behaviour, but performance will differ.
+ def _generate_virtual_guards(self, other, box, runtime_box, state):
+ """
+ Generate the guards and add state information for unifying a virtual
+ object with a non-virtual. This involves forcing the object in the
+ event that unification can succeed. Since virtual objects cannot be null,
+ this method need only check that the virtual object has the expected type.
+ """
+ assert state.force_boxes and isinstance(other, VirtualStateInfo)
+
+ if self.level == LEVEL_CONSTANT:
+ raise VirtualStatesCantMatch(
+ "cannot unify a constant value with a virtual object")
+
+ if self.level == LEVEL_KNOWNCLASS:
+ if not self.known_class.same_constant(other.known_class):
+ raise VirtualStatesCantMatch("classes don't match")
+
def _generate_guards_nonnull(self, other, box, runtime_box, extra_guards,
state):
if not isinstance(other, NotVirtualStateInfoPtr):
@@ -617,10 +637,10 @@
return False
return True
- def generate_guards(self, other, boxes, runtime_boxes, optimizer):
+ def generate_guards(self, other, boxes, runtime_boxes, optimizer, force_boxes=False):
assert (len(self.state) == len(other.state) == len(boxes) ==
len(runtime_boxes))
- state = GenerateGuardState(optimizer)
+ state = GenerateGuardState(optimizer, force_boxes=force_boxes)
for i in range(len(self.state)):
self.state[i].generate_guards(other.state[i], boxes[i],
runtime_boxes[i], state)
@@ -644,8 +664,8 @@
return boxes
- def make_inputargs_and_virtuals(self, inputargs, optimizer):
- inpargs = self.make_inputargs(inputargs, optimizer)
+ def make_inputargs_and_virtuals(self, inputargs, optimizer, force_boxes=False):
+ inpargs = self.make_inputargs(inputargs, optimizer, force_boxes)
# we append the virtuals here in case some stuff is proven
# to be not a virtual and there are getfields in the short preamble
# that will read items out of there
@@ -653,7 +673,7 @@
for i in range(len(inputargs)):
if not isinstance(self.state[i], NotVirtualStateInfo):
virtuals.append(inputargs[i])
-
+
return inpargs, virtuals
def debug_print(self, hdr='', bad=None, metainterp_sd=None):
diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -4507,3 +4507,54 @@
i += 1
return i
self.meta_interp(f, [])
+
+ def test_round_trip_raw_pointer(self):
+ # The goal of this test to to get a raw pointer op into the short preamble
+ # so we can check that the proper guards are generated
+ # In this case, the resulting short preamble contains
+ #
+ # i1 = getfield_gc_i(p0, descr=inst__ptr)
+ # i2 = int_eq(i1, 0)
+ # guard_false(i2)
+ #
+ # as opposed to what the JIT used to produce
+ #
+ # i1 = getfield_gc_i(p0, descr=inst__ptr)
+ # guard_nonnull(i1)
+ #
+ # Which will probably generate correct assembly, but the optimization
+ # pipline expects guard_nonnull arguments to be pointer ops and may crash
+ # and may crash on other input types.
+ driver = JitDriver(greens=[], reds=['i', 'val'])
+
+ class Box(object):
+ _ptr = lltype.nullptr(rffi.CCHARP.TO)
+
+ def new_int_buffer(value):
+ data = lltype.malloc(rffi.CCHARP.TO, rffi.sizeof(rffi.INT), flavor='raw')
+ rffi.cast(rffi.INTP, data)[0] = rffi.cast(rffi.INT, value)
+ return data
+
+ def read_int_buffer(buf):
+ return rffi.cast(rffi.INTP, buf)[0]
+
+ def f():
+ i = 0
+ val = Box()
+ val._ptr = new_int_buffer(1)
+
+ set_param(None, 'retrace_limit', -1)
+ while i < 100:
+ driver.jit_merge_point(i=i, val=val)
+ driver.can_enter_jit(i=i, val=val)
+ # Just to produce a side exit
+ if i & 0b100:
+ i += 1
+ i += int(read_int_buffer(val._ptr))
+ lltype.free(val._ptr, flavor='raw')
+ val._ptr = new_int_buffer(1)
+ lltype.free(val._ptr, flavor='raw')
+
+ self.meta_interp(f, [])
+ self.check_resops(guard_nonnull=0)
+
diff --git a/rpython/jit/metainterp/test/test_virtual.py b/rpython/jit/metainterp/test/test_virtual.py
--- a/rpython/jit/metainterp/test/test_virtual.py
+++ b/rpython/jit/metainterp/test/test_virtual.py
@@ -1,8 +1,8 @@
import py
-from rpython.rlib.jit import JitDriver, promote, dont_look_inside
+from rpython.rlib.jit import JitDriver, promote, dont_look_inside, set_param
from rpython.rlib.objectmodel import compute_unique_id
from rpython.jit.codewriter.policy import StopAtXPolicy
-from rpython.jit.metainterp.test.support import LLJitMixin
+from rpython.jit.metainterp.test.support import LLJitMixin, get_stats
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper import rclass
from rpython.rtyper.lltypesystem.lloperation import llop
@@ -965,6 +965,82 @@
self.check_aborted_count(0)
self.check_target_token_count(4)
+ def test_avoid_preamble(self):
+ driver = JitDriver(greens=[], reds=['i', 'val'])
+ class X(object):
+ def __init__(self, v):
+ self.v = v
+
+ class Box(object):
+ def __init__(self, v):
+ self.unbox = v
+
+ mask = -2
+ const = Box(X(5))
+ def f():
+ # Prevent all retracing of side exits. Ensures that the unroll
+ # optimizer will attempt to jump to either the preamble or loop.
+ set_param(driver, 'retrace_limit', -1)
+ set_param(driver, 'threshold', 1)
+ val = X(0)
+ i = 0
+ const.unbox = X(5)
+ while i < 17:
+ driver.can_enter_jit(i=i, val=val)
+ driver.jit_merge_point(i=i, val=val)
+ # Logical & rather than comparison to confuse range analysis.
+ # Test only succeeds on the first 2 iterations
+ if i & -2 == 0:
+ val = const.unbox
+ else:
+ val = X(i)
+ i += 1
+ return 0
+
+ self.meta_interp(f, [])
+
+ # With retracing disable, there will be one optimized loop expecting a
+ # non-virtual X object. The side exit creates a virtual object which must
+ # be allocated to jump to the optimized trace.
+ self.check_resops(jump=3, label=2, new_with_vtable=2)
+ self.check_target_token_count(2)
+ self.check_trace_count(3)
+
+ def test_conflated_virtual_states(self):
+ # All cases are covered when forcing one component of the virtual state
+ # also forces an as yet unseen component.
+ # i.e. expect [NotVirtual, Virtual] and given a pair of aliasing virtual
+ # objects
+ driver = JitDriver(greens=[], reds=['i', 'v1', 'v2'])
+ class Box(object):
+ def __init__(self, v):
+ self.v = v
+
+ class X(object):
+ def __init__(self, v):
+ self.v = v
+
+ const = Box(X(0))
+ def f():
+ set_param(None, 'retrace_limit', -1)
+ set_param(None, 'threshold', 1)
+ i = 0
+ v1 = X(0)
+ v2 = X(0)
+ const.v = X(0)
+ while i < 17:
+ driver.jit_merge_point(i=i, v1=v1, v2=v2)
+ driver.can_enter_jit(i=i, v1=v1, v2=v2)
+ if i & 1 == 0:
+ v1 = const.v
+ v2 = X(i)
+ else:
+ v1 = v2 = X(i)
+ i += 1
+ return None
+ self.meta_interp(f, [])
+ # assert did not crash
+
class VirtualMiscTests:
def test_multiple_equal_virtuals(self):
More information about the pypy-commit
mailing list