Author: Maciej Fijalkowski <[email protected]>
Branch:
Changeset: r70114:0679ba1c1143
Date: 2014-03-20 16:37 +0200
http://bitbucket.org/pypy/pypy/changeset/0679ba1c1143/
Log: Merge "improve-consecutive-dict-lookups". This branch makes it
possible to cache consecutive dict lookups (for both normal and
ordered dicts)
diff --git a/rpython/jit/backend/test/runner_test.py
b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -3736,7 +3736,7 @@
assert False, 'should not be called'
from rpython.jit.codewriter.effectinfo import EffectInfo
- effectinfo = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
EffectInfo.OS_MATH_SQRT)
+ effectinfo = EffectInfo([], [], [], [], [], [],
EffectInfo.EF_CANNOT_RAISE, EffectInfo.OS_MATH_SQRT)
FPTR = self.Ptr(self.FuncType([lltype.Float], lltype.Float))
func_ptr = llhelper(FPTR, math_sqrt)
FUNC = deref(FPTR)
diff --git a/rpython/jit/codewriter/call.py b/rpython/jit/codewriter/call.py
--- a/rpython/jit/codewriter/call.py
+++ b/rpython/jit/codewriter/call.py
@@ -178,7 +178,7 @@
return (fnaddr, calldescr)
def getcalldescr(self, op, oopspecindex=EffectInfo.OS_NONE,
- extraeffect=None):
+ extraeffect=None, extradescr=None):
"""Return the calldescr that describes all calls done by 'op'.
This returns a calldescr that we can put in the corresponding
call operation in the calling jitcode. It gets an effectinfo
@@ -259,6 +259,7 @@
effectinfo = effectinfo_from_writeanalyze(
self.readwrite_analyzer.analyze(op, self.seen), self.cpu,
extraeffect, oopspecindex, can_invalidate, call_release_gil_target,
+ extradescr,
)
#
assert effectinfo is not None
diff --git a/rpython/jit/codewriter/effectinfo.py
b/rpython/jit/codewriter/effectinfo.py
--- a/rpython/jit/codewriter/effectinfo.py
+++ b/rpython/jit/codewriter/effectinfo.py
@@ -21,6 +21,7 @@
OS_ARRAYCOPY = 1 # "list.ll_arraycopy"
OS_STR2UNICODE = 2 # "str.str2unicode"
OS_SHRINK_ARRAY = 3 # rgc.ll_shrink_array
+ OS_DICT_LOOKUP = 4 # ll_dict_lookup
#
OS_STR_CONCAT = 22 # "stroruni.concat"
OS_STR_SLICE = 23 # "stroruni.slice"
@@ -88,15 +89,18 @@
# for debugging:
_OS_CANRAISE = set([
OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL, OS_RAW_MALLOC_VARSIZE_CHAR,
- OS_JIT_FORCE_VIRTUAL, OS_SHRINK_ARRAY,
+ OS_JIT_FORCE_VIRTUAL, OS_SHRINK_ARRAY, OS_DICT_LOOKUP,
])
def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
+ readonly_descrs_interiorfields,
write_descrs_fields, write_descrs_arrays,
+ write_descrs_interiorfields,
extraeffect=EF_CAN_RAISE,
oopspecindex=OS_NONE,
can_invalidate=False,
- call_release_gil_target=llmemory.NULL):
+ call_release_gil_target=llmemory.NULL,
+ extradescrs=None):
key = (frozenset_or_none(readonly_descrs_fields),
frozenset_or_none(readonly_descrs_arrays),
frozenset_or_none(write_descrs_fields),
@@ -121,17 +125,21 @@
result = object.__new__(cls)
result.readonly_descrs_fields = readonly_descrs_fields
result.readonly_descrs_arrays = readonly_descrs_arrays
+ result.readonly_descrs_interiorfields = readonly_descrs_interiorfields
if extraeffect == EffectInfo.EF_LOOPINVARIANT or \
extraeffect == EffectInfo.EF_ELIDABLE_CANNOT_RAISE or \
extraeffect == EffectInfo.EF_ELIDABLE_CAN_RAISE:
result.write_descrs_fields = []
result.write_descrs_arrays = []
+ result.write_descrs_interiorfields = []
else:
result.write_descrs_fields = write_descrs_fields
result.write_descrs_arrays = write_descrs_arrays
+ result.write_descrs_interiorfields = write_descrs_interiorfields
result.extraeffect = extraeffect
result.can_invalidate = can_invalidate
result.oopspecindex = oopspecindex
+ result.extradescrs = extradescrs
result.call_release_gil_target = call_release_gil_target
if result.check_can_raise():
assert oopspecindex in cls._OS_CANRAISE
@@ -163,7 +171,7 @@
return None
return frozenset(x)
-EffectInfo.MOST_GENERAL = EffectInfo(None, None, None, None,
+EffectInfo.MOST_GENERAL = EffectInfo(None, None, None, None, None, None,
EffectInfo.EF_RANDOM_EFFECTS,
can_invalidate=True)
@@ -172,19 +180,24 @@
extraeffect=EffectInfo.EF_CAN_RAISE,
oopspecindex=EffectInfo.OS_NONE,
can_invalidate=False,
- call_release_gil_target=llmemory.NULL):
+ call_release_gil_target=llmemory.NULL,
+ extradescr=None):
from rpython.translator.backendopt.writeanalyze import top_set
if effects is top_set or extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
readonly_descrs_fields = None
readonly_descrs_arrays = None
+ readonly_descrs_interiorfields = None
write_descrs_fields = None
write_descrs_arrays = None
+ write_descrs_interiorfields = None
extraeffect = EffectInfo.EF_RANDOM_EFFECTS
else:
readonly_descrs_fields = []
readonly_descrs_arrays = []
+ readonly_descrs_interiorfields = []
write_descrs_fields = []
write_descrs_arrays = []
+ write_descrs_interiorfields = []
def add_struct(descrs_fields, (_, T, fieldname)):
T = deref(T)
@@ -198,6 +211,17 @@
descr = cpu.arraydescrof(ARRAY)
descrs_arrays.append(descr)
+ def add_interiorfield(descrs_interiorfields, (_, T, fieldname)):
+ T = deref(T)
+ if not isinstance(T, lltype.Array):
+ return # let's not consider structs for now
+ if not consider_array(T):
+ return
+ if getattr(T.OF, fieldname) is lltype.Void:
+ return
+ descr = cpu.interiorfielddescrof(T, fieldname)
+ descrs_interiorfields.append(descr)
+
for tup in effects:
if tup[0] == "struct":
add_struct(write_descrs_fields, tup)
@@ -205,6 +229,12 @@
tupw = ("struct",) + tup[1:]
if tupw not in effects:
add_struct(readonly_descrs_fields, tup)
+ elif tup[0] == "interiorfield":
+ add_interiorfield(write_descrs_interiorfields, tup)
+ elif tup[0] == "readinteriorfield":
+ tupw = ('interiorfield',) + tup[1:]
+ if tupw not in effects:
+ add_interiorfield(readonly_descrs_interiorfields, tup)
elif tup[0] == "array":
add_array(write_descrs_arrays, tup)
elif tup[0] == "readarray":
@@ -216,12 +246,15 @@
#
return EffectInfo(readonly_descrs_fields,
readonly_descrs_arrays,
+ readonly_descrs_interiorfields,
write_descrs_fields,
write_descrs_arrays,
+ write_descrs_interiorfields,
extraeffect,
oopspecindex,
can_invalidate,
- call_release_gil_target)
+ call_release_gil_target,
+ extradescr)
def consider_struct(TYPE, fieldname):
if fieldType(TYPE, fieldname) is lltype.Void:
diff --git a/rpython/jit/codewriter/jtransform.py
b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -403,6 +403,9 @@
prepare = self._handle_math_sqrt_call
elif oopspec_name.startswith('rgc.'):
prepare = self._handle_rgc_call
+ elif oopspec_name.endswith('dict.lookup'):
+ # also ordereddict.lookup
+ prepare = self._handle_dict_lookup_call
else:
prepare = self.prepare_builtin_call
try:
@@ -1680,9 +1683,11 @@
# ----------
# Strings and Unicodes.
- def _handle_oopspec_call(self, op, args, oopspecindex, extraeffect=None):
+ def _handle_oopspec_call(self, op, args, oopspecindex, extraeffect=None,
+ extradescr=None):
calldescr = self.callcontrol.getcalldescr(op, oopspecindex,
- extraeffect)
+ extraeffect,
+ extradescr=extradescr)
if extraeffect is not None:
assert (is_test_calldescr(calldescr) # for tests
or calldescr.get_extra_info().extraeffect == extraeffect)
@@ -1846,6 +1851,14 @@
return self._handle_oopspec_call(op, args, EffectInfo.OS_MATH_SQRT,
EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
+ def _handle_dict_lookup_call(self, op, oopspec_name, args):
+ extradescr1 = self.cpu.fielddescrof(op.args[1].concretetype.TO,
+ 'entries')
+ extradescr2 = self.cpu.interiorfielddescrof(
+ op.args[1].concretetype.TO.entries.TO, 'key')
+ return self._handle_oopspec_call(op, args, EffectInfo.OS_DICT_LOOKUP,
+ extradescr=[extradescr1, extradescr2])
+
def _handle_rgc_call(self, op, oopspec_name, args):
if oopspec_name == 'rgc.ll_shrink_array':
return self._handle_oopspec_call(op, args,
EffectInfo.OS_SHRINK_ARRAY, EffectInfo.EF_CAN_RAISE)
diff --git a/rpython/jit/codewriter/test/test_jtransform.py
b/rpython/jit/codewriter/test/test_jtransform.py
--- a/rpython/jit/codewriter/test/test_jtransform.py
+++ b/rpython/jit/codewriter/test/test_jtransform.py
@@ -60,7 +60,8 @@
class FakeResidualCallControl:
def guess_call_kind(self, op):
return 'residual'
- def getcalldescr(self, op, oopspecindex=None, extraeffect=None):
+ def getcalldescr(self, op, oopspecindex=None, extraeffect=None,
+ extradescr=None):
return 'calldescr'
def calldescr_canraise(self, calldescr):
return True
@@ -117,7 +118,8 @@
self.callinfocollection = FakeCallInfoCollection()
def guess_call_kind(self, op):
return 'builtin'
- def getcalldescr(self, op, oopspecindex=None, extraeffect=None):
+ def getcalldescr(self, op, oopspecindex=None, extraeffect=None,
+ extradescr=None):
assert oopspecindex is not None # in this test
EI = effectinfo.EffectInfo
if oopspecindex != EI.OS_ARRAYCOPY:
diff --git a/rpython/jit/codewriter/test/test_list.py
b/rpython/jit/codewriter/test/test_list.py
--- a/rpython/jit/codewriter/test/test_list.py
+++ b/rpython/jit/codewriter/test/test_list.py
@@ -37,7 +37,8 @@
class FakeCallControl:
class getcalldescr(AbstractDescr):
- def __init__(self, op, oopspecindex=0, extraeffect=None):
+ def __init__(self, op, oopspecindex=0, extraeffect=None,
+ extradescr=None):
self.op = op
self.oopspecindex = oopspecindex
def __repr__(self):
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py
b/rpython/jit/metainterp/optimizeopt/heap.py
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -1,8 +1,10 @@
import os
+from rpython.jit.codewriter.effectinfo import EffectInfo
+from rpython.jit.metainterp.optimizeopt.util import args_dict
from rpython.jit.metainterp.history import Const
from rpython.jit.metainterp.jitexc import JitException
-from rpython.jit.metainterp.optimizeopt.optimizer import Optimization,
MODE_ARRAY, LEVEL_KNOWNCLASS
+from rpython.jit.metainterp.optimizeopt.optimizer import Optimization,
MODE_ARRAY, LEVEL_KNOWNCLASS, REMOVED
from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
from rpython.jit.metainterp.resoperation import rop, ResOperation
from rpython.rlib.objectmodel import we_are_translated
@@ -173,6 +175,10 @@
self.cached_fields = {}
# cached array items: {array descr: {index: CachedField}}
self.cached_arrayitems = {}
+ # cached dict items: {dict descr: {(optval, index): box-or-const}}
+ self.cached_dict_reads = {}
+ # cache of corresponding array descrs
+ self.corresponding_array_descrs = {}
#
self._lazy_setfields_and_arrayitems = []
self._remove_guard_not_invalidated = False
@@ -180,9 +186,13 @@
self.postponed_op = None
def force_at_end_of_preamble(self):
+ self.cached_dict_reads.clear()
+ self.corresponding_array_descrs.clear()
self.force_all_lazy_setfields_and_arrayitems()
def flush(self):
+ self.cached_dict_reads.clear()
+ self.corresponding_array_descrs.clear()
self.force_all_lazy_setfields_and_arrayitems()
self.emit_postponed_op()
@@ -214,6 +224,7 @@
del self._lazy_setfields_and_arrayitems[:]
self.cached_fields.clear()
self.cached_arrayitems.clear()
+ self.cached_dict_reads.clear()
def field_cache(self, descr):
try:
@@ -282,6 +293,44 @@
self.force_all_lazy_setfields_and_arrayitems()
self.clean_caches()
+ def optimize_CALL(self, op):
+ # dispatch based on 'oopspecindex' to a method that handles
+ # specifically the given oopspec call. For non-oopspec calls,
+ # oopspecindex is just zero.
+ effectinfo = op.getdescr().get_extra_info()
+ oopspecindex = effectinfo.oopspecindex
+ if oopspecindex == EffectInfo.OS_DICT_LOOKUP:
+ if self._optimize_CALL_DICT_LOOKUP(op):
+ return
+ self.emit_operation(op)
+
+ def _optimize_CALL_DICT_LOOKUP(self, op):
+ descrs = op.getdescr().get_extra_info().extradescrs
+ descr1 = descrs[0]
+ descr2 = descrs[1]
+ if descr1 in self.cached_dict_reads:
+ d = self.cached_dict_reads[descr1]
+ else:
+ d = self.cached_dict_reads[descr1] = args_dict()
+ self.corresponding_array_descrs[descr2] = descr1
+ args = self.optimizer.make_args_key(op)
+ try:
+ res_v = d[args]
+ except KeyError:
+ d[args] = self.getvalue(op.result)
+ return False
+ else:
+ self.make_equal_to(op.result, res_v)
+ self.last_emitted_operation = REMOVED
+ return True
+
+ def optimize_GUARD_NO_EXCEPTION(self, op):
+ if self.last_emitted_operation is REMOVED:
+ return
+ self.emit_operation(op)
+
+ optimize_GUARD_EXCEPTION = optimize_GUARD_NO_EXCEPTION
+
def force_from_effectinfo(self, effectinfo):
# XXX we can get the wrong complexity here, if the lists
# XXX stored on effectinfo are large
@@ -290,9 +339,20 @@
for arraydescr in effectinfo.readonly_descrs_arrays:
self.force_lazy_setarrayitem(arraydescr)
for fielddescr in effectinfo.write_descrs_fields:
+ try:
+ del self.cached_dict_reads[fielddescr]
+ except KeyError:
+ pass
self.force_lazy_setfield(fielddescr, can_cache=False)
for arraydescr in effectinfo.write_descrs_arrays:
self.force_lazy_setarrayitem(arraydescr, can_cache=False)
+ for descr in effectinfo.write_descrs_interiorfields:
+ if descr in self.corresponding_array_descrs:
+ dictdescr = self.corresponding_array_descrs.pop(descr)
+ try:
+ del self.cached_dict_reads[dictdescr]
+ except KeyError:
+ pass # someone did it already
if effectinfo.check_forces_virtual_or_virtualizable():
vrefinfo = self.optimizer.metainterp_sd.virtualref_info
self.force_lazy_setfield(vrefinfo.descr_forced)
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
@@ -5444,6 +5444,21 @@
"""
self.optimize_loop(ops, expected)
+ def test_consecutive_getinteriorfields(self):
+ py.test.skip("we want this to pass")
+ ops = """
+ [p0, i0]
+ i1 = getinteriorfield_gc(p0, i0, descr=valuedescr)
+ i2 = getinteriorfield_gc(p0, i0, descr=valuedescr)
+ jump(i1, i2)
+ """
+ expected = """
+ [p0, i0]
+ i1 = getinteriorfield_gc(p0, i0, descr=valuedescr)
+ jump(i1, i1)
+ """
+ self.optimize_loop(ops, expected)
+
class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
pass
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
@@ -181,28 +181,29 @@
plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
EffectInfo.MOST_GENERAL)
nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], []))
+ EffectInfo([], [], [], [], [], []))
writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [adescr], []))
+ EffectInfo([], [], [], [adescr], [], []))
writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [adescr], [arraydescr]))
+ EffectInfo([], [], [], [adescr],
[arraydescr],
+ []))
readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([adescr], [], [], []))
+ EffectInfo([adescr], [], [], [], [], []))
mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([nextdescr], [], [], [],
+ EffectInfo([nextdescr], [], [], [], [], [],
EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE,
can_invalidate=True))
arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [arraydescr], [], [arraydescr],
+ EffectInfo([], [arraydescr], [], [], [arraydescr], [],
EffectInfo.EF_CANNOT_RAISE,
oopspecindex=EffectInfo.OS_ARRAYCOPY))
raw_malloc_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [],
+ EffectInfo([], [], [], [], [], [],
EffectInfo.EF_CAN_RAISE,
oopspecindex=EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR))
raw_free_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [],
+ EffectInfo([], [], [], [], [], [],
EffectInfo.EF_CANNOT_RAISE,
oopspecindex=EffectInfo.OS_RAW_FREE))
@@ -251,17 +252,18 @@
_oopspecindex = getattr(EffectInfo, _os)
locals()[_name] = \
cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+ EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
oopspecindex=_oopspecindex))
#
_oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
locals()[_name.replace('str', 'unicode')] = \
cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+ EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
oopspecindex=_oopspecindex))
s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
+ EffectInfo([], [], [], [], [], [],
+ oopspecindex=EffectInfo.OS_STR2UNICODE))
#
class LoopToken(AbstractDescr):
@@ -277,7 +279,7 @@
virtualtokendescr = vrefinfo.descr_virtual_token
virtualforceddescr = vrefinfo.descr_forced
FUNC = lltype.FuncType([], lltype.Void)
- ei = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+ ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
can_invalidate=False,
oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE)
clear_vable = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei)
diff --git a/rpython/jit/metainterp/test/test_dict.py
b/rpython/jit/metainterp/test/test_dict.py
--- a/rpython/jit/metainterp/test/test_dict.py
+++ b/rpython/jit/metainterp/test/test_dict.py
@@ -193,6 +193,107 @@
self.check_simple_loop({'int_sub': 1, 'int_gt': 1, 'guard_true': 1,
'jump': 1})
+ def test_dict_two_lookups(self):
+ driver = JitDriver(greens = [], reds = 'auto')
+ d = {'a': 3, 'b': 4}
+ indexes = ['a', 'b']
+
+ def f(n):
+ s = 0
+ while n > 0:
+ driver.jit_merge_point()
+ s += d[indexes[n & 1]]
+ s += d[indexes[n & 1]]
+ n -= 1
+ return s
+
+ self.meta_interp(f, [10])
+ # XXX should be one getinteriorfield_gc
+ self.check_simple_loop(call=1, getinteriorfield_gc=2,
+ guard_no_exception=1)
+
+ def test_ordered_dict_two_lookups(self):
+ driver = JitDriver(greens = [], reds = 'auto')
+ d = OrderedDict()
+ d['a'] = 3
+ d['b'] = 4
+ indexes = ['a', 'b']
+
+ def f(n):
+ s = 0
+ while n > 0:
+ driver.jit_merge_point()
+ s += d[indexes[n & 1]]
+ s += d[indexes[n & 1]]
+ n -= 1
+ return s
+
+ self.meta_interp(f, [10])
+ # XXX should be one getinteriorfield_gc
+ self.check_simple_loop(call=1, getinteriorfield_gc=2,
+ guard_no_exception=1)
+
+ def test_dict_insert_invalidates_caches(self):
+ driver = JitDriver(greens = [], reds = 'auto')
+ indexes = ['aa', 'b', 'cc']
+
+ def f(n):
+ d = {'aa': 3, 'b': 4, 'cc': 5}
+ s = 0
+ while n > 0:
+ driver.jit_merge_point()
+ index = indexes[n & 1]
+ s += d[index]
+ d['aa'] += 1 # this will invalidate the index
+ s += d[index]
+ n -= 1
+ return s
+
+ res = self.meta_interp(f, [10])
+ assert res == f(10)
+ self.check_simple_loop(call=5)
+
+ def test_dict_array_write_invalidates_caches(self):
+ driver = JitDriver(greens = [], reds = 'auto')
+ indexes = ['aa', 'b', 'cc']
+
+ def f(n):
+ d = {'aa': 3, 'b': 4, 'cc': 5}
+ s = 0
+ while n > 0:
+ driver.jit_merge_point()
+ index = indexes[n & 1]
+ s += d[index]
+ del d['cc']
+ s += d[index]
+ d['cc'] = 3
+ n -= 1
+ return s
+
+ exp = f(10)
+ res = self.meta_interp(f, [10])
+ assert res == exp
+ self.check_simple_loop(call=7)
+
+ def test_dict_double_lookup_2(self):
+ driver = JitDriver(greens = [], reds = 'auto')
+ indexes = ['aa', 'b', 'cc']
+
+ def f(n):
+ d = {'aa': 3, 'b': 4, 'cc': 5}
+ s = 0
+ while n > 0:
+ driver.jit_merge_point()
+ index = indexes[n & 1]
+ s += d[index]
+ d[index] += 1
+ n -= 1
+ return s
+
+ res = self.meta_interp(f, [10])
+ assert res == f(10)
+ self.check_simple_loop(call=3)
+
class TestLLtype(DictTests, LLJitMixin):
pass
diff --git a/rpython/jit/metainterp/virtualizable.py
b/rpython/jit/metainterp/virtualizable.py
--- a/rpython/jit/metainterp/virtualizable.py
+++ b/rpython/jit/metainterp/virtualizable.py
@@ -302,7 +302,7 @@
self.clear_vable_ptr = self.warmrunnerdesc.helper_func(
FUNCPTR, self.clear_vable_token)
FUNC = FUNCPTR.TO
- ei = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+ ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
can_invalidate=False,
oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE)
diff --git a/rpython/rtyper/lltypesystem/rdict.py
b/rpython/rtyper/lltypesystem/rdict.py
--- a/rpython/rtyper/lltypesystem/rdict.py
+++ b/rpython/rtyper/lltypesystem/rdict.py
@@ -569,6 +569,7 @@
PERTURB_SHIFT = 5
@jit.look_inside_iff(lambda d, key, hash: jit.isvirtual(d) and
jit.isconstant(key))
[email protected]('dict.lookup(d, key, hash)')
def ll_dict_lookup(d, key, hash):
entries = d.entries
ENTRIES = lltype.typeOf(entries).TO
diff --git a/rpython/rtyper/lltypesystem/rordereddict.py
b/rpython/rtyper/lltypesystem/rordereddict.py
--- a/rpython/rtyper/lltypesystem/rordereddict.py
+++ b/rpython/rtyper/lltypesystem/rordereddict.py
@@ -709,6 +709,7 @@
@jit.look_inside_iff(lambda d, key, hash, store_flag, T:
jit.isvirtual(d) and jit.isconstant(key))
[email protected]('ordereddict.lookup(d, key, hash, store_flag, T)')
def ll_dict_lookup(d, key, hash, store_flag, T):
INDEXES = _ll_ptr_to_array_of(T)
entries = d.entries
diff --git a/rpython/translator/backendopt/test/test_writeanalyze.py
b/rpython/translator/backendopt/test/test_writeanalyze.py
--- a/rpython/translator/backendopt/test/test_writeanalyze.py
+++ b/rpython/translator/backendopt/test/test_writeanalyze.py
@@ -353,3 +353,23 @@
result = wa.analyze(fgraph.startblock.operations[-1])
assert list(result) == [("struct", lltype.Ptr(S), "x")]
+
+ def test_interiorfield(self):
+ A = lltype.GcArray(lltype.Struct('x', ('x', lltype.Signed),
+ ('y', lltype.Signed)))
+
+ def g(x):
+ a = lltype.malloc(A, 1)
+ a[0].y = 3
+ return f(a, x)
+
+ def f(a, x):
+ a[0].x = x
+ return a[0].y
+
+ t, wa = self.translate(g, [int])
+ ggraph = graphof(t, g)
+ result = wa.analyze(ggraph.startblock.operations[-1])
+ res = list(result)
+ assert ('readinteriorfield', lltype.Ptr(A), 'y') in res
+ assert ('interiorfield', lltype.Ptr(A), 'x') in res
diff --git a/rpython/translator/backendopt/writeanalyze.py
b/rpython/translator/backendopt/writeanalyze.py
--- a/rpython/translator/backendopt/writeanalyze.py
+++ b/rpython/translator/backendopt/writeanalyze.py
@@ -1,4 +1,4 @@
-from rpython.flowspace.model import Variable
+from rpython.flowspace.model import Variable, Constant
from rpython.translator.backendopt import graphanalyze
top_set = object()
@@ -37,6 +37,12 @@
return top_set
return result1.union(result2)
+ def _getinteriorname(self, op):
+ if (isinstance(op.args[1], Constant) and
+ isinstance(op.args[1].value, str)):
+ return op.args[1].value
+ return op.args[2].value
+
def analyze_simple_operation(self, op, graphinfo):
if op.opname == "setfield":
if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
@@ -45,11 +51,18 @@
elif op.opname == "setarrayitem":
if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
return self._array_result(op.args[0].concretetype)
+ elif op.opname == "setinteriorfield":
+ if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
+ name = self._getinteriorname(op)
+ return self._interiorfield_result(op.args[0].concretetype,
name)
return empty_set
def _array_result(self, TYPE):
return frozenset([("array", TYPE)])
+ def _interiorfield_result(self, TYPE, fieldname):
+ return frozenset([("interiorfield", TYPE, fieldname)])
+
def compute_graph_info(self, graph):
return FreshMallocs(graph)
@@ -99,4 +112,8 @@
elif op.opname == "getarrayitem":
return frozenset([
("readarray", op.args[0].concretetype)])
+ elif op.opname == "getinteriorfield":
+ name = self._getinteriorname(op)
+ return frozenset([("readinteriorfield", op.args[0].concretetype,
+ name)])
return WriteAnalyzer.analyze_simple_operation(self, op, graphinfo)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit