[pypy-svn] r70324 - in pypy/trunk/pypy: jit/metainterp jit/metainterp/test rpython rpython/test translator/backendopt translator/backendopt/test
arigo at codespeak.net
arigo at codespeak.net
Tue Dec 29 10:03:15 CET 2009
Author: arigo
Date: Tue Dec 29 10:03:14 2009
New Revision: 70324
Modified:
pypy/trunk/pypy/jit/metainterp/codewriter.py
pypy/trunk/pypy/jit/metainterp/effectinfo.py
pypy/trunk/pypy/jit/metainterp/optimizeopt.py
pypy/trunk/pypy/jit/metainterp/resume.py
pypy/trunk/pypy/jit/metainterp/test/test_effectinfo.py
pypy/trunk/pypy/jit/metainterp/test/test_optimizefindnode.py
pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
pypy/trunk/pypy/jit/metainterp/test/test_resume.py
pypy/trunk/pypy/rpython/rptr.py
pypy/trunk/pypy/rpython/test/test_rptr.py
pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py
pypy/trunk/pypy/translator/backendopt/writeanalyze.py
Log:
Merge jit-delayed-write. Allows repeated setfield_gc's to get
optimized out. The main result is that if a setfield_gc's
would stick a virtual structure on a non-virtual object, and
if that setfield_gc is later overridden, then optimizing it
out allows the complete structure to remain virtual.
Modified: pypy/trunk/pypy/jit/metainterp/codewriter.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/codewriter.py (original)
+++ pypy/trunk/pypy/jit/metainterp/codewriter.py Tue Dec 29 10:03:14 2009
@@ -11,7 +11,7 @@
from pypy.tool.udir import udir
from pypy.translator.simplify import get_funcobj, get_functype
from pypy.translator.backendopt.canraise import RaiseAnalyzer
-from pypy.translator.backendopt.writeanalyze import WriteAnalyzer
+from pypy.translator.backendopt.writeanalyze import ReadWriteAnalyzer
from pypy.jit.metainterp.typesystem import deref, arrayItem, fieldType
from pypy.jit.metainterp.effectinfo import effectinfo_from_writeanalyze
from pypy.jit.metainterp.effectinfo import VirtualizableAnalyzer
@@ -185,7 +185,7 @@
self.portal_runner_ptr = portal_runner_ptr
translator = self.rtyper.annotator.translator
self.raise_analyzer = RaiseAnalyzer(translator)
- self.write_analyzer = WriteAnalyzer(translator)
+ self.readwrite_analyzer = ReadWriteAnalyzer(translator)
self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
def make_portal_bytecode(self, graph):
@@ -326,7 +326,7 @@
# ok
if consider_effects_of is not None:
effectinfo = effectinfo_from_writeanalyze(
- self.write_analyzer.analyze(consider_effects_of),
+ self.readwrite_analyzer.analyze(consider_effects_of),
self.cpu,
self.virtualizable_analyzer.analyze(consider_effects_of))
calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS), RESULT, effectinfo)
Modified: pypy/trunk/pypy/jit/metainterp/effectinfo.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/effectinfo.py (original)
+++ pypy/trunk/pypy/jit/metainterp/effectinfo.py Tue Dec 29 10:03:14 2009
@@ -7,13 +7,16 @@
class EffectInfo(object):
_cache = {}
- def __new__(cls, write_descrs_fields, write_descrs_arrays,
- promotes_virtualizables=False):
- key = (frozenset(write_descrs_fields), frozenset(write_descrs_arrays),
+ def __new__(cls, readonly_descrs_fields, write_descrs_fields,
+ write_descrs_arrays, promotes_virtualizables=False):
+ key = (frozenset(readonly_descrs_fields),
+ frozenset(write_descrs_fields),
+ frozenset(write_descrs_arrays),
promotes_virtualizables)
if key in cls._cache:
return cls._cache[key]
result = object.__new__(cls)
+ result.readonly_descrs_fields = readonly_descrs_fields
result.write_descrs_fields = write_descrs_fields
result.write_descrs_arrays = write_descrs_arrays
result.promotes_virtualizables = promotes_virtualizables
@@ -24,26 +27,39 @@
from pypy.translator.backendopt.writeanalyze import top_set
if effects is top_set:
return None
+ readonly_descrs_fields = []
+ # readonly_descrs_arrays = [] --- not enabled for now
write_descrs_fields = []
write_descrs_arrays = []
+
+ def add_struct(descrs_fields, (_, T, fieldname)):
+ T = deref(T)
+ if consider_struct(T, fieldname):
+ descr = cpu.fielddescrof(T, fieldname)
+ descrs_fields.append(descr)
+
+ def add_array(descrs_arrays, (_, T)):
+ ARRAY = deref(T)
+ if consider_array(ARRAY):
+ descr = cpu.arraydescrof(ARRAY)
+ descrs_arrays.append(descr)
+
for tup in effects:
if tup[0] == "struct":
- _, T, fieldname = tup
- T = deref(T)
- if not consider_struct(T, fieldname):
- continue
- descr = cpu.fielddescrof(T, fieldname)
- write_descrs_fields.append(descr)
+ add_struct(write_descrs_fields, tup)
+ elif tup[0] == "readstruct":
+ tupw = ("struct",) + tup[1:]
+ if tupw not in effects:
+ add_struct(readonly_descrs_fields, tup)
elif tup[0] == "array":
- _, T = tup
- ARRAY = deref(T)
- if not consider_array(ARRAY):
- continue
- descr = cpu.arraydescrof(ARRAY)
- write_descrs_arrays.append(descr)
+ add_array(write_descrs_arrays, tup)
+ elif tup[0] == "readarray":
+ pass
else:
assert 0
- return EffectInfo(write_descrs_fields, write_descrs_arrays,
+ return EffectInfo(readonly_descrs_fields,
+ write_descrs_fields,
+ write_descrs_arrays,
promotes_virtualizables)
def consider_struct(TYPE, fieldname):
Modified: pypy/trunk/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/optimizeopt.py (original)
+++ pypy/trunk/pypy/jit/metainterp/optimizeopt.py Tue Dec 29 10:03:14 2009
@@ -161,18 +161,18 @@
return self.box
def make_virtual_info(self, modifier, fieldnums):
- vinfo = self._cached_vinfo
- if vinfo is not None and resume.tagged_list_eq(
- vinfo.fieldnums, fieldnums):
+ vinfo = self._cached_vinfo
+ if vinfo is not None and vinfo.equals(fieldnums):
return vinfo
vinfo = self._make_virtual(modifier)
- vinfo.fieldnums = fieldnums
+ vinfo.set_content(fieldnums)
self._cached_vinfo = vinfo
return vinfo
def _make_virtual(self, modifier):
raise NotImplementedError("abstract base")
+
def get_fielddescrlist_cache(cpu):
if not hasattr(cpu, '_optimizeopt_fielddescrlist_cache'):
result = descrlist_dict()
@@ -512,6 +512,9 @@
def emit_operation(self, op, must_clone=True):
self.heap_op_optimizer.emitting_operation(op)
+ self._emit_operation(op, must_clone)
+
+ def _emit_operation(self, op, must_clone=True):
for i in range(len(op.args)):
arg = op.args[i]
if arg in self.values:
@@ -532,10 +535,11 @@
self.newoperations.append(op)
def store_final_boxes_in_guard(self, op):
+ pendingfields = self.heap_op_optimizer.force_lazy_setfields_for_guard()
descr = op.descr
assert isinstance(descr, compile.ResumeGuardDescr)
modifier = resume.ResumeDataVirtualAdder(descr, self.resumedata_memo)
- newboxes = modifier.finish(self.values)
+ newboxes = modifier.finish(self.values, pendingfields)
if len(newboxes) > self.metainterp_sd.options.failargs_limit: # XXX be careful here
raise compile.GiveUp
descr.store_final_boxes(op, newboxes)
@@ -836,11 +840,13 @@
class HeapOpOptimizer(object):
def __init__(self, optimizer):
self.optimizer = optimizer
- # cached OptValues for each field descr
+ # cached fields: {descr: {OptValue_instance: OptValue_fieldvalue}}
self.cached_fields = {}
-
- # cached OptValues for each field descr
+ # cached array items: {descr: CachedArrayItems}
self.cached_arrayitems = {}
+ # lazily written setfields (at most one per descr): {descr: op}
+ self.lazy_setfields = {}
+ self.lazy_setfields_descrs = [] # keys (at least) of previous dict
def clean_caches(self):
self.cached_fields.clear()
@@ -848,6 +854,9 @@
def cache_field_value(self, descr, value, fieldvalue, write=False):
if write:
+ # when seeing a setfield, we have to clear the cache for the same
+ # field on any other structure, just in case they are aliasing
+ # each other
d = self.cached_fields[descr] = {}
else:
d = self.cached_fields.setdefault(descr, {})
@@ -920,7 +929,12 @@
opnum == rop.CALL_MAY_FORCE):
effectinfo = op.descr.get_extra_info()
if effectinfo is not None:
+ # XXX we can get the wrong complexity here, if the lists
+ # XXX stored on effectinfo are large
+ for fielddescr in effectinfo.readonly_descrs_fields:
+ self.force_lazy_setfield(fielddescr)
for fielddescr in effectinfo.write_descrs_fields:
+ self.force_lazy_setfield(fielddescr)
try:
del self.cached_fields[fielddescr]
except KeyError:
@@ -931,9 +945,73 @@
except KeyError:
pass
return
+ self.force_all_lazy_setfields()
+ elif op.is_final() or (not we_are_translated() and
+ op.opnum < 0): # escape() operations
+ self.force_all_lazy_setfields()
self.clean_caches()
+ def force_lazy_setfield(self, descr, before_guard=False):
+ try:
+ op = self.lazy_setfields[descr]
+ except KeyError:
+ return
+ del self.lazy_setfields[descr]
+ self.optimizer._emit_operation(op)
+ #
+ # hackish: reverse the order of the last two operations if it makes
+ # sense to avoid a situation like "int_eq/setfield_gc/guard_true",
+ # which the backend (at least the x86 backend) does not handle well.
+ newoperations = self.optimizer.newoperations
+ if before_guard and len(newoperations) >= 2:
+ lastop = newoperations[-1]
+ prevop = newoperations[-2]
+ # - is_comparison() for cases like "int_eq/setfield_gc/guard_true"
+ # - CALL_MAY_FORCE: "call_may_force/setfield_gc/guard_not_forced"
+ if ((prevop.is_comparison() or prevop.opnum == rop.CALL_MAY_FORCE)
+ and prevop.result not in lastop.args):
+ newoperations[-2] = lastop
+ newoperations[-1] = prevop
+
+ def force_all_lazy_setfields(self):
+ if len(self.lazy_setfields_descrs) > 0:
+ for descr in self.lazy_setfields_descrs:
+ self.force_lazy_setfield(descr)
+ del self.lazy_setfields_descrs[:]
+
+ def force_lazy_setfields_for_guard(self):
+ pendingfields = []
+ for descr in self.lazy_setfields_descrs:
+ try:
+ op = self.lazy_setfields[descr]
+ except KeyError:
+ continue
+ # the only really interesting case that we need to handle in the
+ # guards' resume data is that of a virtual object that is stored
+ # into a field of a non-virtual object.
+ value = self.optimizer.getvalue(op.args[0])
+ assert not value.is_virtual() # it must be a non-virtual
+ fieldvalue = self.optimizer.getvalue(op.args[1])
+ if fieldvalue.is_virtual():
+ # this is the case that we leave to resume.py
+ pendingfields.append((descr, value.box,
+ fieldvalue.get_key_box()))
+ else:
+ self.force_lazy_setfield(descr, before_guard=True)
+ return pendingfields
+
+ def force_lazy_setfield_if_necessary(self, op, value, write=False):
+ try:
+ op1 = self.lazy_setfields[op.descr]
+ except KeyError:
+ if write:
+ self.lazy_setfields_descrs.append(op.descr)
+ else:
+ if self.optimizer.getvalue(op1.args[0]) is not value:
+ self.force_lazy_setfield(op.descr)
+
def optimize_GETFIELD_GC(self, op, value):
+ self.force_lazy_setfield_if_necessary(op, value)
# check if the field was read from another getfield_gc just before
# or has been written to recently
fieldvalue = self.read_cached_field(op.descr, value)
@@ -948,7 +1026,8 @@
self.cache_field_value(op.descr, value, fieldvalue)
def optimize_SETFIELD_GC(self, op, value, fieldvalue):
- self.optimizer.emit_operation(op)
+ self.force_lazy_setfield_if_necessary(op, value, write=True)
+ self.lazy_setfields[op.descr] = op
# remember the result of future reads of the field
self.cache_field_value(op.descr, value, fieldvalue, write=True)
Modified: pypy/trunk/pypy/jit/metainterp/resume.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/resume.py (original)
+++ pypy/trunk/pypy/jit/metainterp/resume.py Tue Dec 29 10:03:14 2009
@@ -252,8 +252,6 @@
if (isinstance(box, Box) and box not in self.liveboxes_from_env
and box not in self.liveboxes):
self.liveboxes[box] = UNASSIGNED
- return True
- return False
def _register_boxes(self, boxes):
for box in boxes:
@@ -268,7 +266,7 @@
_, tagbits = untag(tagged)
return tagbits == TAGVIRTUAL
- def finish(self, values):
+ def finish(self, values, pending_setfields=[]):
# compute the numbering
storage = self.storage
numb, liveboxes_from_env, v = self.memo.number(values,
@@ -291,13 +289,21 @@
value = values[box]
value.get_args_for_fail(self)
+ for _, box, fieldbox in pending_setfields:
+ self.register_box(box)
+ self.register_box(fieldbox)
+ value = values[fieldbox]
+ value.get_args_for_fail(self)
+
self._number_virtuals(liveboxes, values, v)
+ self._add_pending_fields(pending_setfields)
storage.rd_consts = self.memo.consts
dump_storage(storage, liveboxes)
return liveboxes[:]
def _number_virtuals(self, liveboxes, values, num_env_virtuals):
+ # !! 'liveboxes' is a list that is extend()ed in-place !!
memo = self.memo
new_liveboxes = [None] * memo.num_cached_boxes()
count = 0
@@ -358,6 +364,16 @@
return True
return False
+ def _add_pending_fields(self, pending_setfields):
+ rd_pendingfields = None
+ if pending_setfields:
+ rd_pendingfields = []
+ for descr, box, fieldbox in pending_setfields:
+ num = self._gettagged(box)
+ fieldnum = self._gettagged(fieldbox)
+ rd_pendingfields.append((descr, num, fieldnum))
+ self.storage.rd_pendingfields = rd_pendingfields
+
def _gettagged(self, box):
if isinstance(box, Const):
return self.memo.getconst(box)
@@ -366,11 +382,16 @@
return self.liveboxes_from_env[box]
return self.liveboxes[box]
+
class AbstractVirtualInfo(object):
def allocate(self, metainterp):
raise NotImplementedError
def setfields(self, metainterp, box, fn_decode_box):
raise NotImplementedError
+ def equals(self, fieldnums):
+ return tagged_list_eq(self.fieldnums, fieldnums)
+ def set_content(self, fieldnums):
+ self.fieldnums = fieldnums
class AbstractVirtualStructInfo(AbstractVirtualInfo):
@@ -471,6 +492,7 @@
self.liveboxes = liveboxes
self.cpu = metainterp.cpu
self._prepare_virtuals(metainterp, storage.rd_virtuals)
+ self._prepare_pendingfields(metainterp, storage.rd_pendingfields)
def _prepare_virtuals(self, metainterp, virtuals):
if virtuals:
@@ -489,6 +511,16 @@
vinfo.setfields(metainterp, self.virtuals[i],
self._decode_box)
+ def _prepare_pendingfields(self, metainterp, pendingfields):
+ if pendingfields:
+ if metainterp._already_allocated_resume_virtuals is not None:
+ return
+ for descr, num, fieldnum in pendingfields:
+ box = self._decode_box(num)
+ fieldbox = self._decode_box(fieldnum)
+ metainterp.execute_and_record(rop.SETFIELD_GC,
+ descr, box, fieldbox)
+
def consume_boxes(self):
numb = self.cur_numb
assert numb is not None
Modified: pypy/trunk/pypy/jit/metainterp/test/test_effectinfo.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_effectinfo.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_effectinfo.py Tue Dec 29 10:03:14 2009
@@ -3,32 +3,77 @@
from pypy.rpython.ootypesystem import ootype
from pypy.jit.metainterp.effectinfo import effectinfo_from_writeanalyze
+class FakeCPU:
+ def fielddescrof(self, T, fieldname):
+ return ('fielddescr', T, fieldname)
+ def arraydescrof(self, A):
+ return ('arraydescr', A)
+
+def test_include_read_field():
+ S = lltype.GcStruct("S", ("a", lltype.Signed))
+ effects = frozenset([("readstruct", lltype.Ptr(S), "a")])
+ effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+ assert list(effectinfo.readonly_descrs_fields) == [('fielddescr', S, "a")]
+ assert not effectinfo.write_descrs_fields
+ assert not effectinfo.write_descrs_arrays
+
+def test_include_write_field():
+ S = lltype.GcStruct("S", ("a", lltype.Signed))
+ effects = frozenset([("struct", lltype.Ptr(S), "a")])
+ effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+ assert list(effectinfo.write_descrs_fields) == [('fielddescr', S, "a")]
+ assert not effectinfo.readonly_descrs_fields
+ assert not effectinfo.write_descrs_arrays
+
+def test_include_write_array():
+ A = lltype.GcArray(lltype.Signed)
+ effects = frozenset([("array", lltype.Ptr(A))])
+ effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+ assert not effectinfo.readonly_descrs_fields
+ assert not effectinfo.write_descrs_fields
+ assert list(effectinfo.write_descrs_arrays) == [('arraydescr', A)]
+
+def test_dont_include_read_and_write_field():
+ S = lltype.GcStruct("S", ("a", lltype.Signed))
+ effects = frozenset([("readstruct", lltype.Ptr(S), "a"),
+ ("struct", lltype.Ptr(S), "a")])
+ effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+ assert not effectinfo.readonly_descrs_fields
+ assert list(effectinfo.write_descrs_fields) == [('fielddescr', S, "a")]
+ assert not effectinfo.write_descrs_arrays
+
+
def test_filter_out_typeptr():
effects = frozenset([("struct", lltype.Ptr(OBJECT), "typeptr")])
effectinfo = effectinfo_from_writeanalyze(effects, None)
+ assert not effectinfo.readonly_descrs_fields
assert not effectinfo.write_descrs_fields
assert not effectinfo.write_descrs_arrays
def test_filter_out_array_of_void():
effects = frozenset([("array", lltype.Ptr(lltype.GcArray(lltype.Void)))])
effectinfo = effectinfo_from_writeanalyze(effects, None)
+ assert not effectinfo.readonly_descrs_fields
assert not effectinfo.write_descrs_fields
assert not effectinfo.write_descrs_arrays
def test_filter_out_struct_with_void():
effects = frozenset([("struct", lltype.Ptr(lltype.GcStruct("x", ("a", lltype.Void))), "a")])
effectinfo = effectinfo_from_writeanalyze(effects, None)
+ assert not effectinfo.readonly_descrs_fields
assert not effectinfo.write_descrs_fields
assert not effectinfo.write_descrs_arrays
def test_filter_out_ooarray_of_void():
effects = frozenset([("array", ootype.Array(ootype.Void))])
effectinfo = effectinfo_from_writeanalyze(effects, None)
+ assert not effectinfo.readonly_descrs_fields
assert not effectinfo.write_descrs_fields
assert not effectinfo.write_descrs_arrays
def test_filter_out_instance_with_void():
effects = frozenset([("struct", ootype.Instance("x", ootype.ROOT, {"a": ootype.Void}), "a")])
effectinfo = effectinfo_from_writeanalyze(effects, None)
+ assert not effectinfo.readonly_descrs_fields
assert not effectinfo.write_descrs_fields
assert not effectinfo.write_descrs_arrays
Modified: pypy/trunk/pypy/jit/metainterp/test/test_optimizefindnode.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_optimizefindnode.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_optimizefindnode.py Tue Dec 29 10:03:14 2009
@@ -95,9 +95,15 @@
onedescr = cpu.fielddescrof(U, 'one')
FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
- nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], []))
- writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([adescr], []))
- writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([adescr], [arraydescr]))
+ plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
+ nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [], []))
+ writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [adescr], []))
+ writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [adescr], [arraydescr]))
+ readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([adescr], [], []))
cpu.class_sizes = {cpu.cast_adr_to_int(node_vtable_adr): cpu.sizeof(NODE),
cpu.cast_adr_to_int(node_vtable_adr2): cpu.sizeof(NODE2),
Modified: pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py Tue Dec 29 10:03:14 2009
@@ -87,7 +87,10 @@
def test_reuse_vinfo():
class FakeVInfo(object):
- pass
+ def set_content(self, fieldnums):
+ self.fieldnums = fieldnums
+ def equals(self, fieldnums):
+ return self.fieldnums == fieldnums
class FakeVirtualValue(optimizeopt.AbstractVirtualValue):
def _make_virtual(self, *args):
return FakeVInfo()
@@ -606,10 +609,10 @@
p3sub = getfield_gc(p3, descr=nextdescr)
i3 = getfield_gc(p3sub, descr=valuedescr)
escape(i3)
+ p1 = new_with_vtable(ConstClass(node_vtable))
p2sub = new_with_vtable(ConstClass(node_vtable2))
setfield_gc(p2sub, i1, descr=valuedescr)
setfield_gc(p2, p2sub, descr=nextdescr)
- p1 = new_with_vtable(ConstClass(node_vtable))
jump(i1, p1, p2)
"""
# The same as test_p123_simple, but in the end the "old" p2 contains
@@ -1293,6 +1296,182 @@
"""
self.optimize_loop(ops, 'Not, Not', ops)
+ def test_duplicate_setfield_1(self):
+ ops = """
+ [p1, i1, i2]
+ setfield_gc(p1, i1, descr=valuedescr)
+ setfield_gc(p1, i2, descr=valuedescr)
+ jump(p1, i1, i2)
+ """
+ expected = """
+ [p1, i1, i2]
+ setfield_gc(p1, i2, descr=valuedescr)
+ jump(p1, i1, i2)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+ def test_duplicate_setfield_2(self):
+ ops = """
+ [p1, i1, i3]
+ setfield_gc(p1, i1, descr=valuedescr)
+ i2 = getfield_gc(p1, descr=valuedescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ escape(i2)
+ jump(p1, i1, i3)
+ """
+ expected = """
+ [p1, i1, i3]
+ setfield_gc(p1, i3, descr=valuedescr)
+ escape(i1)
+ jump(p1, i1, i3)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+ def test_duplicate_setfield_3(self):
+ ops = """
+ [p1, p2, i1, i3]
+ setfield_gc(p1, i1, descr=valuedescr)
+ i2 = getfield_gc(p2, descr=valuedescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ escape(i2)
+ jump(p1, p2, i1, i3)
+ """
+ # potential aliasing of p1 and p2 means that we cannot kill the
+ # the setfield_gc
+ self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+
+ def test_duplicate_setfield_4(self):
+ ops = """
+ [p1, i1, i2, p3]
+ setfield_gc(p1, i1, descr=valuedescr)
+ #
+ # some operations on which the above setfield_gc cannot have effect
+ i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr)
+ i4 = getarrayitem_gc(p3, i3, descr=arraydescr)
+ i5 = int_add(i3, i4)
+ setarrayitem_gc(p3, 0, i5, descr=arraydescr)
+ setfield_gc(p1, i4, descr=nextdescr)
+ #
+ setfield_gc(p1, i2, descr=valuedescr)
+ jump(p1, i1, i2, p3)
+ """
+ expected = """
+ [p1, i1, i2, p3]
+ #
+ i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr)
+ i4 = getarrayitem_gc(p3, i3, descr=arraydescr)
+ i5 = int_add(i3, i4)
+ setarrayitem_gc(p3, 0, i5, descr=arraydescr)
+ #
+ setfield_gc(p1, i2, descr=valuedescr)
+ setfield_gc(p1, i4, descr=nextdescr)
+ jump(p1, i1, i2, p3)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+ def test_duplicate_setfield_sideeffects_1(self):
+ ops = """
+ [p1, i1, i2]
+ setfield_gc(p1, i1, descr=valuedescr)
+ escape()
+ setfield_gc(p1, i2, descr=valuedescr)
+ jump(p1, i1, i2)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not', ops)
+
+ def test_duplicate_setfield_residual_guard_1(self):
+ ops = """
+ [p1, i1, i2, i3]
+ setfield_gc(p1, i1, descr=valuedescr)
+ guard_true(i3) []
+ i4 = int_neg(i2)
+ setfield_gc(p1, i2, descr=valuedescr)
+ jump(p1, i1, i2, i4)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+
+ def test_duplicate_setfield_residual_guard_2(self):
+ # the difference with the previous test is that the field value is
+ # a virtual, which we try hard to keep virtual
+ ops = """
+ [p1, i2, i3]
+ p2 = new_with_vtable(ConstClass(node_vtable))
+ setfield_gc(p1, p2, descr=nextdescr)
+ guard_true(i3) []
+ i4 = int_neg(i2)
+ setfield_gc(p1, NULL, descr=nextdescr)
+ jump(p1, i2, i4)
+ """
+ expected = """
+ [p1, i2, i3]
+ guard_true(i3) [p1]
+ i4 = int_neg(i2)
+ setfield_gc(p1, NULL, descr=nextdescr)
+ jump(p1, i2, i4)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+ def test_duplicate_setfield_residual_guard_3(self):
+ ops = """
+ [p1, i2, i3]
+ p2 = new_with_vtable(ConstClass(node_vtable))
+ setfield_gc(p2, i2, descr=valuedescr)
+ setfield_gc(p1, p2, descr=nextdescr)
+ guard_true(i3) []
+ i4 = int_neg(i2)
+ setfield_gc(p1, NULL, descr=nextdescr)
+ jump(p1, i2, i4)
+ """
+ expected = """
+ [p1, i2, i3]
+ guard_true(i3) [p1, i2]
+ i4 = int_neg(i2)
+ setfield_gc(p1, NULL, descr=nextdescr)
+ jump(p1, i2, i4)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+ def test_duplicate_setfield_residual_guard_4(self):
+ # test that the setfield_gc does not end up between int_eq and
+ # the following guard_true
+ ops = """
+ [p1, i1, i2, i3]
+ setfield_gc(p1, i1, descr=valuedescr)
+ i5 = int_eq(i3, 5)
+ guard_true(i5) []
+ i4 = int_neg(i2)
+ setfield_gc(p1, i2, descr=valuedescr)
+ jump(p1, i1, i2, i4)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+
+ def test_duplicate_setfield_aliasing(self):
+ # a case where aliasing issues (and not enough cleverness) mean
+ # that we fail to remove any setfield_gc
+ ops = """
+ [p1, p2, i1, i2, i3]
+ setfield_gc(p1, i1, descr=valuedescr)
+ setfield_gc(p2, i2, descr=valuedescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ jump(p1, p2, i1, i2, i3)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not, Not', ops)
+
+ def test_duplicate_setfield_guard_value_const(self):
+ ops = """
+ [p1, i1, i2]
+ guard_value(p1, ConstPtr(myptr)) []
+ setfield_gc(p1, i1, descr=valuedescr)
+ setfield_gc(ConstPtr(myptr), i2, descr=valuedescr)
+ jump(p1, i1, i2)
+ """
+ expected = """
+ [i1, i2]
+ setfield_gc(ConstPtr(myptr), i2, descr=valuedescr)
+ jump(i1, i2)
+ """
+ self.optimize_loop(ops, 'Constant(myptr), Not, Not', expected)
+
def test_duplicate_getarrayitem_1(self):
ops = """
[p1]
@@ -1634,6 +1813,14 @@
tag = ('virtual', self.namespace[match.group(2)])
virtuals[pvar] = (tag, None, fieldstext)
#
+ r2 = re.compile(r"([\w\d()]+)[.](\w+)\s*=\s*([\w\d()]+)")
+ pendingfields = []
+ for match in r2.finditer(text):
+ pvar = match.group(1)
+ pfieldname = match.group(2)
+ pfieldvar = match.group(3)
+ pendingfields.append((pvar, pfieldname, pfieldvar))
+ #
def _variables_equal(box, varname, strict):
if varname not in virtuals:
if strict:
@@ -1655,11 +1842,21 @@
else:
virtuals[varname] = tag, box, fieldstext
#
- basetext = text[:ends[0]]
+ basetext = text.splitlines()[0]
varnames = [s.strip() for s in basetext.split(',')]
+ if varnames == ['']:
+ varnames = []
assert len(boxes) == len(varnames)
for box, varname in zip(boxes, varnames):
_variables_equal(box, varname, strict=True)
+ for pvar, pfieldname, pfieldvar in pendingfields:
+ box = oparse.getvar(pvar)
+ fielddescr = self.namespace[pfieldname.strip()]
+ fieldbox = executor.execute(self.cpu,
+ rop.GETFIELD_GC,
+ fielddescr,
+ box)
+ _variables_equal(fieldbox, pfieldvar, strict=True)
#
for match in parts:
pvar = match.group(1)
@@ -1918,6 +2115,57 @@
where p7v is a node_vtable, valuedescr=iv
''')
+ def test_expand_fail_lazy_setfield_1(self):
+ self.make_fail_descr()
+ ops = """
+ [p1, i2, i3]
+ p2 = new_with_vtable(ConstClass(node_vtable))
+ setfield_gc(p2, i2, descr=valuedescr)
+ setfield_gc(p1, p2, descr=nextdescr)
+ guard_true(i3, descr=fdescr) []
+ i4 = int_neg(i2)
+ setfield_gc(p1, NULL, descr=nextdescr)
+ jump(p1, i2, i4)
+ """
+ expected = """
+ [p1, i2, i3]
+ guard_true(i3, descr=fdescr) [p1, i2]
+ i4 = int_neg(i2)
+ setfield_gc(p1, NULL, descr=nextdescr)
+ jump(p1, i2, i4)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not', expected)
+ self.loop.inputargs[0].value = self.nodebox.value
+ self.check_expanded_fail_descr('''
+ p1.nextdescr = p2
+ where p2 is a node_vtable, valuedescr=i2
+ ''')
+
+ def test_expand_fail_lazy_setfield_2(self):
+ self.make_fail_descr()
+ ops = """
+ [i2, i3]
+ p2 = new_with_vtable(ConstClass(node_vtable))
+ setfield_gc(p2, i2, descr=valuedescr)
+ setfield_gc(ConstPtr(myptr), p2, descr=nextdescr)
+ guard_true(i3, descr=fdescr) []
+ i4 = int_neg(i2)
+ setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr)
+ jump(i2, i4)
+ """
+ expected = """
+ [i2, i3]
+ guard_true(i3, descr=fdescr) [i2]
+ i4 = int_neg(i2)
+ setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr)
+ jump(i2, i4)
+ """
+ self.optimize_loop(ops, 'Not, Not', expected)
+ self.check_expanded_fail_descr('''
+ ConstPtr(myptr).nextdescr = p2
+ where p2 is a node_vtable, valuedescr=i2
+ ''')
+
class TestLLtype(BaseTestOptimizeOpt, LLtypeMixin):
@@ -2031,6 +2279,58 @@
"""
self.optimize_loop(ops, 'Not, Not, Not', expected)
+ def test_residual_call_invalidates_some_read_caches_1(self):
+ ops = """
+ [p1, i1, p2, i2]
+ setfield_gc(p1, i1, descr=valuedescr)
+ setfield_gc(p2, i2, descr=adescr)
+ i3 = call(i1, descr=readadescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ setfield_gc(p2, i3, descr=adescr)
+ jump(p1, i1, p2, i2)
+ """
+ expected = """
+ [p1, i1, p2, i2]
+ setfield_gc(p2, i2, descr=adescr)
+ i3 = call(i1, descr=readadescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ setfield_gc(p2, i3, descr=adescr)
+ jump(p1, i1, p2, i2)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+ def test_residual_call_invalidates_some_read_caches_2(self):
+ ops = """
+ [p1, i1, p2, i2]
+ setfield_gc(p1, i1, descr=valuedescr)
+ setfield_gc(p2, i2, descr=adescr)
+ i3 = call(i1, descr=writeadescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ setfield_gc(p2, i3, descr=adescr)
+ jump(p1, i1, p2, i2)
+ """
+ expected = """
+ [p1, i1, p2, i2]
+ setfield_gc(p2, i2, descr=adescr)
+ i3 = call(i1, descr=writeadescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ setfield_gc(p2, i3, descr=adescr)
+ jump(p1, i1, p2, i2)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+ def test_residual_call_invalidates_some_read_caches_3(self):
+ ops = """
+ [p1, i1, p2, i2]
+ setfield_gc(p1, i1, descr=valuedescr)
+ setfield_gc(p2, i2, descr=adescr)
+ i3 = call(i1, descr=plaincalldescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ setfield_gc(p2, i3, descr=adescr)
+ jump(p1, i1, p2, i2)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+
class TestOOtype(BaseTestOptimizeOpt, OOtypeMixin):
Modified: pypy/trunk/pypy/jit/metainterp/test/test_resume.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_resume.py (original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_resume.py Tue Dec 29 10:03:14 2009
@@ -12,6 +12,8 @@
rd_frame_info_list = None
rd_numb = None
rd_consts = []
+ rd_virtuals = None
+ rd_pendingfields = None
def test_tag():
assert tag(3, 1) == rffi.r_short(3<<2|1)
@@ -40,6 +42,12 @@
assert not tagged_list_eq([tag(1, TAGBOX)], [tag(-2, TAGBOX)])
assert not tagged_list_eq([tag(1, TAGBOX), tag(-2, TAGBOX)], [tag(1, TAGBOX)])
+def test_vinfo():
+ v1 = AbstractVirtualInfo()
+ v1.set_content([1, 2, 4])
+ assert v1.equals([1, 2, 4])
+ assert not v1.equals([1, 2, 6])
+
class MyMetaInterp:
_already_allocated_resume_virtuals = None
@@ -80,7 +88,6 @@
tag(0, TAGBOX),
tag(1, TAGBOX)])
storage.rd_numb = numb
- storage.rd_virtuals = None
b1s, b2s, b3s = [BoxInt(), BoxPtr(), BoxInt()]
assert b1s != b3s
@@ -103,7 +110,6 @@
tag(0, TAGBOX),
tag(1, TAGBOX)])
storage.rd_numb = numb
- storage.rd_virtuals = None
b1s, b2s, b3s = [BoxInt(), BoxPtr(), BoxInt()]
assert b1s != b3s
reader = ResumeDataReader(storage, [b1s, b2s, b3s], MyMetaInterp())
@@ -125,6 +131,7 @@
rd_virtuals = [FakeVinfo(), None]
rd_numb = []
rd_consts = []
+ rd_pendingfields = None
class FakeMetainterp(object):
_already_allocated_resume_virtuals = None
cpu = None
@@ -960,6 +967,46 @@
assert ptr.a == 111
assert ptr.b == lltype.nullptr(LLtypeMixin.NODE)
+
+def test_virtual_adder_pending_fields():
+ b2s, b4s = [BoxPtr(), BoxPtr()]
+ storage = Storage()
+ memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
+ modifier = ResumeDataVirtualAdder(storage, memo)
+ modifier.liveboxes_from_env = {}
+ modifier.liveboxes = {}
+ modifier.vfieldboxes = {}
+
+ v2 = OptValue(b2s)
+ v4 = OptValue(b4s)
+ modifier.register_box(b2s)
+ modifier.register_box(b4s)
+
+ values = {b4s: v4, b2s: v2}
+ liveboxes = []
+ modifier._number_virtuals(liveboxes, values, 0)
+ assert liveboxes == [b2s, b4s]
+ modifier._add_pending_fields([(LLtypeMixin.nextdescr, b2s, b4s)])
+ storage.rd_consts = memo.consts[:]
+ storage.rd_numb = None
+ # resume
+ demo55.next = lltype.nullptr(LLtypeMixin.NODE)
+ b2t = BoxPtr(demo55o)
+ b4t = BoxPtr(demo66o)
+ newboxes = _resume_remap(liveboxes, [b2s, b4s], b2t, b4t)
+
+ metainterp = MyMetaInterp()
+ reader = ResumeDataReader(storage, newboxes, metainterp)
+ assert reader.virtuals is None
+ trace = metainterp.trace
+ b2set = (rop.SETFIELD_GC, [b2t, b4t], None, LLtypeMixin.nextdescr)
+ expected = [b2set]
+
+ for x, y in zip(expected, trace):
+ assert x == y
+ assert demo55.next == demo66
+
+
def test_invalidation_needed():
class options:
failargs_limit = 10
Modified: pypy/trunk/pypy/rpython/rptr.py
==============================================================================
--- pypy/trunk/pypy/rpython/rptr.py (original)
+++ pypy/trunk/pypy/rpython/rptr.py Tue Dec 29 10:03:14 2009
@@ -39,6 +39,14 @@
attr = hop.args_s[1].const
if isinstance(hop.s_result, annmodel.SomeLLADTMeth):
return hop.inputarg(hop.r_result, arg=0)
+ try:
+ self.lowleveltype._example()._lookup_adtmeth(attr)
+ except AttributeError:
+ pass
+ else:
+ assert hop.s_result.is_constant()
+ return hop.inputconst(hop.r_result, hop.s_result.const)
+ assert attr in self.lowleveltype.TO._flds # check that the field exists
FIELD_TYPE = getattr(self.lowleveltype.TO, attr)
if isinstance(FIELD_TYPE, lltype.ContainerType):
if (attr, FIELD_TYPE) == self.lowleveltype.TO._first_struct():
Modified: pypy/trunk/pypy/rpython/test/test_rptr.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rptr.py (original)
+++ pypy/trunk/pypy/rpython/test/test_rptr.py Tue Dec 29 10:03:14 2009
@@ -337,3 +337,13 @@
return f([1])
s, t = ll_rtype(lltest, [])
assert s.is_constant() == False
+
+def test_staticadtmeths():
+ ll_func = staticAdtMethod(lambda x: x + 42)
+ S = GcStruct('S', adtmeths={'ll_func': ll_func})
+ def f():
+ return malloc(S).ll_func(5)
+ s, t = ll_rtype(f, [])
+ graphf = t.graphs[0]
+ for op in graphf.startblock.operations:
+ assert op.opname != 'getfield'
Modified: pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py (original)
+++ pypy/trunk/pypy/translator/backendopt/test/test_writeanalyze.py Tue Dec 29 10:03:14 2009
@@ -4,14 +4,15 @@
from pypy.translator.translator import TranslationContext, graphof
from pypy.translator.simplify import get_funcobj
from pypy.translator.backendopt.writeanalyze import WriteAnalyzer, top_set
+from pypy.translator.backendopt.writeanalyze import ReadWriteAnalyzer
from pypy.translator.backendopt.all import backend_optimizations
from pypy.conftest import option
-class BaseTestCanRaise(object):
+class BaseTest(object):
type_system = None
-
+ Analyzer = WriteAnalyzer
def translate(self, func, sig):
t = TranslationContext()
@@ -19,7 +20,10 @@
t.buildrtyper(type_system=self.type_system).specialize()
if option.view:
t.view()
- return t, WriteAnalyzer(t)
+ return t, self.Analyzer(t)
+
+
+class BaseTestWriteAnalyze(BaseTest):
def test_writes_simple(self):
def g(x):
@@ -146,7 +150,7 @@
assert not result
-class TestLLtype(BaseTestCanRaise):
+class TestLLtype(BaseTestWriteAnalyze):
type_system = 'lltype'
def test_list(self):
@@ -205,7 +209,7 @@
assert name.endswith("foobar")
-class TestOOtype(BaseTestCanRaise):
+class TestOOtype(BaseTestWriteAnalyze):
type_system = 'ootype'
def test_array(self):
@@ -240,3 +244,88 @@
result = wa.analyze(ggraph.startblock.operations[0])
assert result is top_set
+
+
+class TestLLtypeReadWriteAnalyze(BaseTest):
+ Analyzer = ReadWriteAnalyzer
+ type_system = 'lltype'
+
+ def test_read_simple(self):
+ def g(x):
+ return True
+
+ def f(x):
+ return g(x - 1)
+ t, wa = self.translate(f, [int])
+ fgraph = graphof(t, f)
+ result = wa.analyze(fgraph.startblock.operations[0])
+ assert not result
+
+ def test_read_really(self):
+ class A(object):
+ def __init__(self, y):
+ self.y = y
+ def f(self):
+ self.x = 1
+ return self.y
+ def h(flag):
+ obj = A(flag)
+ return obj.f()
+
+ t, wa = self.translate(h, [int])
+ hgraph = graphof(t, h)
+ op_call_f = hgraph.startblock.operations[-1]
+
+ # check that we fished the expected ops
+ assert op_call_f.opname == "direct_call"
+ assert get_funcobj(op_call_f.args[0].value)._name == 'A.f'
+
+ result = wa.analyze(op_call_f)
+ assert len(result) == 2
+ result = list(result)
+ result.sort()
+ [(struct1, T1, name1), (struct2, T2, name2)] = result
+ assert struct1 == "readstruct"
+ assert name1.endswith("y")
+ assert struct2 == "struct"
+ assert name2.endswith("x")
+ assert T1 == T2
+
+ def test_contains(self):
+ def g(x, y, z):
+ l = [x]
+ return f(l, y, z)
+ def f(x, y, z):
+ return y in x
+
+ t, wa = self.translate(g, [int, int, int])
+ ggraph = graphof(t, g)
+ assert ggraph.startblock.operations[-1].opname == 'direct_call'
+
+ result = wa.analyze(ggraph.startblock.operations[-1])
+ ARRAYPTR = list(result)[0][1]
+ assert list(result) == [("readarray", ARRAYPTR)]
+ assert isinstance(ARRAYPTR.TO, lltype.GcArray)
+
+ def test_adt_method(self):
+ def ll_callme(n):
+ return n
+ ll_callme = lltype.staticAdtMethod(ll_callme)
+ S = lltype.GcStruct('S', ('x', lltype.Signed),
+ adtmeths = {'yep': True,
+ 'callme': ll_callme})
+ def g(x, y, z):
+ p = lltype.malloc(S)
+ p.x = x
+ if p.yep:
+ z *= p.callme(y)
+ return z
+ def f(x, y, z):
+ return g(x, y, z)
+
+ t, wa = self.translate(f, [int, int, int])
+ fgraph = graphof(t, f)
+ assert fgraph.startblock.operations[-1].opname == 'direct_call'
+
+ result = wa.analyze(fgraph.startblock.operations[-1])
+ assert list(result) == [("struct", lltype.Ptr(S), "x")]
Modified: pypy/trunk/pypy/translator/backendopt/writeanalyze.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/writeanalyze.py (original)
+++ pypy/trunk/pypy/translator/backendopt/writeanalyze.py Tue Dec 29 10:03:14 2009
@@ -45,3 +45,15 @@
elif methname in ('ll_getitem_fast', 'll_length'):
return self.bottom_result()
return graphanalyze.GraphAnalyzer.analyze_external_method(self, op, TYPE, meth)
+
+
+class ReadWriteAnalyzer(WriteAnalyzer):
+
+ def analyze_simple_operation(self, op):
+ if op.opname == "getfield":
+ return frozenset([
+ ("readstruct", op.args[0].concretetype, op.args[1].value)])
+ elif op.opname == "getarrayitem":
+ return frozenset([
+ ("readarray", op.args[0].concretetype)])
+ return WriteAnalyzer.analyze_simple_operation(self, op)
More information about the Pypy-commit
mailing list