[pypy-commit] pypy default: Merge store-sink-array: implements store sinking of SETARRAYITEM_GC,
arigo
noreply at buildbot.pypy.org
Fri Jun 24 11:38:24 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r45097:e19e88b51376
Date: 2011-06-24 11:41 +0200
http://bitbucket.org/pypy/pypy/changeset/e19e88b51376/
Log: Merge store-sink-array: implements store sinking of SETARRAYITEM_GC,
and replace PyFrame.blockstack_w and PyFrame.fastlocals_w with a
single list, to avoid confusing the two in the optimizer. Gives a
small improvement (10-20%) on the speed of generators, whose frames
are neither virtual nor virtualizable.
diff --git a/pypy/interpreter/eval.py b/pypy/interpreter/eval.py
--- a/pypy/interpreter/eval.py
+++ b/pypy/interpreter/eval.py
@@ -100,12 +100,12 @@
@jit.dont_look_inside
def fast2locals(self):
- # Copy values from self.fastlocals_w to self.w_locals
+ # Copy values from the fastlocals to self.w_locals
if self.w_locals is None:
self.w_locals = self.space.newdict()
varnames = self.getcode().getvarnames()
fastscope_w = self.getfastscope()
- for i in range(min(len(varnames), len(fastscope_w))):
+ for i in range(min(len(varnames), self.getfastscopelength())):
name = varnames[i]
w_value = fastscope_w[i]
if w_value is not None:
@@ -114,7 +114,7 @@
@jit.dont_look_inside
def locals2fast(self):
- # Copy values from self.w_locals to self.fastlocals_w
+ # Copy values from self.w_locals to the fastlocals
assert self.w_locals is not None
varnames = self.getcode().getvarnames()
numlocals = self.getfastscopelength()
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -98,7 +98,7 @@
self.closure)
for i in funccallunrolling:
if i < nargs:
- new_frame.fastlocals_w[i] = args_w[i]
+ new_frame.locals_stack_w[i] = args_w[i]
return new_frame.run()
elif nargs >= 1 and fast_natural_arity == Code.PASSTHROUGHARGS1:
assert isinstance(code, gateway.BuiltinCodePassThroughArguments1)
@@ -158,7 +158,7 @@
self.closure)
for i in xrange(nargs):
w_arg = frame.peekvalue(nargs-1-i)
- new_frame.fastlocals_w[i] = w_arg
+ new_frame.locals_stack_w[i] = w_arg
return new_frame.run()
@@ -169,13 +169,13 @@
self.closure)
for i in xrange(nargs):
w_arg = frame.peekvalue(nargs-1-i)
- new_frame.fastlocals_w[i] = w_arg
+ new_frame.locals_stack_w[i] = w_arg
ndefs = len(self.defs_w)
start = ndefs - defs_to_load
i = nargs
for j in xrange(start, ndefs):
- new_frame.fastlocals_w[i] = self.defs_w[j]
+ new_frame.locals_stack_w[i] = self.defs_w[j]
i += 1
return new_frame.run()
diff --git a/pypy/interpreter/nestedscope.py b/pypy/interpreter/nestedscope.py
--- a/pypy/interpreter/nestedscope.py
+++ b/pypy/interpreter/nestedscope.py
@@ -170,7 +170,7 @@
for i in range(len(args_to_copy)):
argnum = args_to_copy[i]
if argnum >= 0:
- self.cells[i].set(self.fastlocals_w[argnum])
+ self.cells[i].set(self.locals_stack_w[argnum])
def getfreevarname(self, index):
freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -63,6 +63,7 @@
the pypy compiler"""
self.space = space
eval.Code.__init__(self, name)
+ assert nlocals >= 0
self.co_argcount = argcount
self.co_nlocals = nlocals
self.co_stacksize = stacksize
@@ -202,7 +203,7 @@
# speed hack
fresh_frame = jit.hint(frame, access_directly=True,
fresh_virtualizable=True)
- args_matched = args.parse_into_scope(None, fresh_frame.fastlocals_w,
+ args_matched = args.parse_into_scope(None, fresh_frame.locals_stack_w,
func.name,
sig, func.defs_w)
fresh_frame.init_cells()
@@ -215,7 +216,7 @@
# speed hack
fresh_frame = jit.hint(frame, access_directly=True,
fresh_virtualizable=True)
- args_matched = args.parse_into_scope(w_obj, fresh_frame.fastlocals_w,
+ args_matched = args.parse_into_scope(w_obj, fresh_frame.locals_stack_w,
func.name,
sig, func.defs_w)
fresh_frame.init_cells()
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -9,7 +9,7 @@
from pypy.interpreter import pytraceback
from pypy.rlib.objectmodel import we_are_translated, instantiate
from pypy.rlib.jit import hint
-from pypy.rlib.debug import make_sure_not_resized
+from pypy.rlib.debug import make_sure_not_resized, check_nonneg
from pypy.rlib.rarithmetic import intmask
from pypy.rlib import jit
from pypy.tool import stdlib_opcode
@@ -56,16 +56,18 @@
assert isinstance(code, pycode.PyCode)
self.pycode = code
eval.Frame.__init__(self, space, w_globals)
- self.valuestack_w = [None] * code.co_stacksize
- self.valuestackdepth = 0
+ self.locals_stack_w = [None] * (code.co_nlocals + code.co_stacksize)
+ self.nlocals = code.co_nlocals
+ self.valuestackdepth = code.co_nlocals
self.lastblock = None
+ make_sure_not_resized(self.locals_stack_w)
+ check_nonneg(self.nlocals)
+ #
if space.config.objspace.honor__builtins__:
self.builtin = space.builtin.pick_builtin(w_globals)
# regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
# class bodies only have CO_NEWLOCALS.
self.initialize_frame_scopes(closure, code)
- self.fastlocals_w = [None] * code.co_nlocals
- make_sure_not_resized(self.fastlocals_w)
self.f_lineno = code.co_firstlineno
def mark_as_escaped(self):
@@ -184,14 +186,14 @@
# stack manipulation helpers
def pushvalue(self, w_object):
depth = self.valuestackdepth
- self.valuestack_w[depth] = w_object
+ self.locals_stack_w[depth] = w_object
self.valuestackdepth = depth + 1
def popvalue(self):
depth = self.valuestackdepth - 1
- assert depth >= 0, "pop from empty value stack"
- w_object = self.valuestack_w[depth]
- self.valuestack_w[depth] = None
+ assert depth >= self.nlocals, "pop from empty value stack"
+ w_object = self.locals_stack_w[depth]
+ self.locals_stack_w[depth] = None
self.valuestackdepth = depth
return w_object
@@ -217,24 +219,24 @@
def peekvalues(self, n):
values_w = [None] * n
base = self.valuestackdepth - n
- assert base >= 0
+ assert base >= self.nlocals
while True:
n -= 1
if n < 0:
break
- values_w[n] = self.valuestack_w[base+n]
+ values_w[n] = self.locals_stack_w[base+n]
return values_w
@jit.unroll_safe
def dropvalues(self, n):
n = hint(n, promote=True)
finaldepth = self.valuestackdepth - n
- assert finaldepth >= 0, "stack underflow in dropvalues()"
+ assert finaldepth >= self.nlocals, "stack underflow in dropvalues()"
while True:
n -= 1
if n < 0:
break
- self.valuestack_w[finaldepth+n] = None
+ self.locals_stack_w[finaldepth+n] = None
self.valuestackdepth = finaldepth
@jit.unroll_safe
@@ -261,30 +263,30 @@
# Contrast this with CPython where it's PEEK(-1).
index_from_top = hint(index_from_top, promote=True)
index = self.valuestackdepth + ~index_from_top
- assert index >= 0, "peek past the bottom of the stack"
- return self.valuestack_w[index]
+ assert index >= self.nlocals, "peek past the bottom of the stack"
+ return self.locals_stack_w[index]
def settopvalue(self, w_object, index_from_top=0):
index_from_top = hint(index_from_top, promote=True)
index = self.valuestackdepth + ~index_from_top
- assert index >= 0, "settop past the bottom of the stack"
- self.valuestack_w[index] = w_object
+ assert index >= self.nlocals, "settop past the bottom of the stack"
+ self.locals_stack_w[index] = w_object
@jit.unroll_safe
def dropvaluesuntil(self, finaldepth):
depth = self.valuestackdepth - 1
finaldepth = hint(finaldepth, promote=True)
while depth >= finaldepth:
- self.valuestack_w[depth] = None
+ self.locals_stack_w[depth] = None
depth -= 1
self.valuestackdepth = finaldepth
- def savevaluestack(self):
- return self.valuestack_w[:self.valuestackdepth]
+ def save_locals_stack(self):
+ return self.locals_stack_w[:self.valuestackdepth]
- def restorevaluestack(self, items_w):
- assert None not in items_w
- self.valuestack_w[:len(items_w)] = items_w
+ def restore_locals_stack(self, items_w):
+ self.locals_stack_w[:len(items_w)] = items_w
+ self.init_cells()
self.dropvaluesuntil(len(items_w))
def make_arguments(self, nargs):
@@ -314,11 +316,12 @@
else:
f_lineno = self.f_lineno
- values_w = self.valuestack_w[0:self.valuestackdepth]
+ values_w = self.locals_stack_w[self.nlocals:self.valuestackdepth]
w_valuestack = maker.slp_into_tuple_with_nulls(space, values_w)
w_blockstack = nt([block._get_state_(space) for block in self.get_blocklist()])
- w_fastlocals = maker.slp_into_tuple_with_nulls(space, self.fastlocals_w)
+ w_fastlocals = maker.slp_into_tuple_with_nulls(
+ space, self.locals_stack_w[:self.nlocals])
if self.last_exception is None:
w_exc_value = space.w_None
w_tb = space.w_None
@@ -399,7 +402,8 @@
new_frame.last_instr = space.int_w(w_last_instr)
new_frame.frame_finished_execution = space.is_true(w_finished)
new_frame.f_lineno = space.int_w(w_f_lineno)
- new_frame.fastlocals_w = maker.slp_from_tuple_with_nulls(space, w_fastlocals)
+ fastlocals_w = maker.slp_from_tuple_with_nulls(space, w_fastlocals)
+ new_frame.locals_stack_w[:len(fastlocals_w)] = fastlocals_w
if space.is_w(w_f_trace, space.w_None):
new_frame.w_f_trace = None
@@ -423,28 +427,28 @@
@jit.dont_look_inside
def getfastscope(self):
"Get the fast locals as a list."
- return self.fastlocals_w
+ return self.locals_stack_w
@jit.dont_look_inside
def setfastscope(self, scope_w):
"""Initialize the fast locals from a list of values,
where the order is according to self.pycode.signature()."""
scope_len = len(scope_w)
- if scope_len > len(self.fastlocals_w):
+ if scope_len > self.nlocals:
raise ValueError, "new fastscope is longer than the allocated area"
- # don't assign directly to 'fastlocals_w[:scope_len]' to be
+ # don't assign directly to 'locals_stack_w[:scope_len]' to be
# virtualizable-friendly
for i in range(scope_len):
- self.fastlocals_w[i] = scope_w[i]
+ self.locals_stack_w[i] = scope_w[i]
self.init_cells()
def init_cells(self):
- """Initialize cellvars from self.fastlocals_w
+ """Initialize cellvars from self.locals_stack_w.
This is overridden in nestedscope.py"""
pass
def getfastscopelength(self):
- return self.pycode.co_nlocals
+ return self.nlocals
def getclosure(self):
return None
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -324,7 +324,7 @@
def LOAD_FAST(self, varindex, next_instr):
# access a local variable directly
- w_value = self.fastlocals_w[varindex]
+ w_value = self.locals_stack_w[varindex]
if w_value is None:
self._load_fast_failed(varindex)
self.pushvalue(w_value)
@@ -343,7 +343,7 @@
def STORE_FAST(self, varindex, next_instr):
w_newvalue = self.popvalue()
assert w_newvalue is not None
- self.fastlocals_w[varindex] = w_newvalue
+ self.locals_stack_w[varindex] = w_newvalue
def POP_TOP(self, oparg, next_instr):
self.popvalue()
@@ -696,12 +696,12 @@
LOAD_GLOBAL._always_inline_ = True
def DELETE_FAST(self, varindex, next_instr):
- if self.fastlocals_w[varindex] is None:
+ if self.locals_stack_w[varindex] is None:
varname = self.getlocalvarname(varindex)
message = "local variable '%s' referenced before assignment"
raise operationerrfmt(self.space.w_UnboundLocalError, message,
varname)
- self.fastlocals_w[varindex] = None
+ self.locals_stack_w[varindex] = None
def BUILD_TUPLE(self, itemcount, next_instr):
items = self.popvalues(itemcount)
diff --git a/pypy/interpreter/test/test_eval.py b/pypy/interpreter/test/test_eval.py
--- a/pypy/interpreter/test/test_eval.py
+++ b/pypy/interpreter/test/test_eval.py
@@ -15,16 +15,16 @@
self.code = code
Frame.__init__(self, space)
self.numlocals = numlocals
- self.fastlocals_w = [None] * self.numlocals
+ self._fastlocals_w = [None] * self.numlocals
def getcode(self):
return self.code
def setfastscope(self, scope_w):
- self.fastlocals_w = scope_w
+ self._fastlocals_w = scope_w
def getfastscope(self):
- return self.fastlocals_w
+ return self._fastlocals_w
def getfastscopelength(self):
return self.numlocals
@@ -38,11 +38,11 @@
self.f.fast2locals()
assert space.eq_w(self.f.w_locals, self.space.wrap({}))
- self.f.fastlocals_w[0] = w(5)
+ self.f._fastlocals_w[0] = w(5)
self.f.fast2locals()
assert space.eq_w(self.f.w_locals, self.space.wrap({'x': 5}))
- self.f.fastlocals_w[2] = w(7)
+ self.f._fastlocals_w[2] = w(7)
self.f.fast2locals()
assert space.eq_w(self.f.w_locals, self.space.wrap({'x': 5, 'args': 7}))
@@ -57,13 +57,13 @@
w = self.space.wrap
self.f.w_locals = self.space.wrap({})
self.f.locals2fast()
- self.sameList(self.f.fastlocals_w, [None]*5)
+ self.sameList(self.f._fastlocals_w, [None]*5)
self.f.w_locals = self.space.wrap({'x': 5})
self.f.locals2fast()
- self.sameList(self.f.fastlocals_w, [w(5)] + [None]*4)
+ self.sameList(self.f._fastlocals_w, [w(5)] + [None]*4)
self.f.w_locals = self.space.wrap({'x':5, 'args':7})
self.f.locals2fast()
- self.sameList(self.f.fastlocals_w, [w(5), None, w(7),
- None, None])
+ self.sameList(self.f._fastlocals_w, [w(5), None, w(7),
+ None, None])
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -75,12 +75,13 @@
#
OS_MATH_SQRT = 100
- def __new__(cls, readonly_descrs_fields,
+ def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
write_descrs_fields, write_descrs_arrays,
extraeffect=EF_CAN_RAISE,
oopspecindex=OS_NONE,
can_invalidate=False):
key = (frozenset(readonly_descrs_fields),
+ frozenset(readonly_descrs_arrays),
frozenset(write_descrs_fields),
frozenset(write_descrs_arrays),
extraeffect,
@@ -89,6 +90,7 @@
return cls._cache[key]
result = object.__new__(cls)
result.readonly_descrs_fields = readonly_descrs_fields
+ result.readonly_descrs_arrays = readonly_descrs_arrays
if extraeffect == EffectInfo.EF_LOOPINVARIANT or \
extraeffect == EffectInfo.EF_PURE:
result.write_descrs_fields = []
@@ -119,7 +121,7 @@
if effects is top_set:
return None
readonly_descrs_fields = []
- # readonly_descrs_arrays = [] --- not enabled for now
+ readonly_descrs_arrays = []
write_descrs_fields = []
write_descrs_arrays = []
@@ -145,10 +147,13 @@
elif tup[0] == "array":
add_array(write_descrs_arrays, tup)
elif tup[0] == "readarray":
- pass
+ tupw = ("array",) + tup[1:]
+ if tupw not in effects:
+ add_array(readonly_descrs_arrays, tup)
else:
assert 0
return EffectInfo(readonly_descrs_fields,
+ readonly_descrs_arrays,
write_descrs_fields,
write_descrs_arrays,
extraeffect,
diff --git a/pypy/jit/codewriter/test/test_effectinfo.py b/pypy/jit/codewriter/test/test_effectinfo.py
--- a/pypy/jit/codewriter/test/test_effectinfo.py
+++ b/pypy/jit/codewriter/test/test_effectinfo.py
@@ -34,6 +34,15 @@
assert not effectinfo.readonly_descrs_fields
assert not effectinfo.write_descrs_arrays
+def test_include_read_array():
+ A = lltype.GcArray(lltype.Signed)
+ effects = frozenset([("readarray", lltype.Ptr(A))])
+ effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+ assert not effectinfo.readonly_descrs_fields
+ assert list(effectinfo.readonly_descrs_arrays) == [('arraydescr', A)]
+ assert not effectinfo.write_descrs_fields
+ assert not effectinfo.write_descrs_arrays
+
def test_include_write_array():
A = lltype.GcArray(lltype.Signed)
effects = frozenset([("array", lltype.Ptr(A))])
@@ -51,6 +60,16 @@
assert list(effectinfo.write_descrs_fields) == [('fielddescr', S, "a")]
assert not effectinfo.write_descrs_arrays
+def test_dont_include_read_and_write_array():
+ A = lltype.GcArray(lltype.Signed)
+ effects = frozenset([("readarray", lltype.Ptr(A)),
+ ("array", lltype.Ptr(A))])
+ effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+ assert not effectinfo.readonly_descrs_fields
+ assert not effectinfo.readonly_descrs_arrays
+ assert not effectinfo.write_descrs_fields
+ assert list(effectinfo.write_descrs_arrays) == [('arraydescr', A)]
+
def test_filter_out_typeptr():
effects = frozenset([("struct", lltype.Ptr(OBJECT), "typeptr")])
diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py
--- a/pypy/jit/metainterp/compile.py
+++ b/pypy/jit/metainterp/compile.py
@@ -15,7 +15,7 @@
from pypy.jit.metainterp import history
from pypy.jit.metainterp.typesystem import llhelper, oohelper
from pypy.jit.metainterp.optimize import InvalidLoop
-from pypy.jit.metainterp.resume import NUMBERING
+from pypy.jit.metainterp.resume import NUMBERING, PENDINGFIELDSP
from pypy.jit.codewriter import heaptracker, longlong
def giveup():
@@ -302,7 +302,7 @@
rd_numb = lltype.nullptr(NUMBERING)
rd_consts = None
rd_virtuals = None
- rd_pendingfields = None
+ rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
CNT_INT = -0x20000000
CNT_REF = -0x40000000
diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -8,8 +8,8 @@
class CachedField(object):
def __init__(self):
- # Cache information for a field descr. It can be in one
- # of two states:
+ # Cache information for a field descr, or for an (array descr, index)
+ # pair. It can be in one of two states:
#
# 1. 'cached_fields' is a dict mapping OptValues of structs
# to OptValues of fields. All fields on-heap are
@@ -27,19 +27,19 @@
self._lazy_setfield_registered = False
def do_setfield(self, optheap, op):
- # Update the state with the SETFIELD_GC operation 'op'.
+ # Update the state with the SETFIELD_GC/SETARRAYITEM_GC operation 'op'.
structvalue = optheap.getvalue(op.getarg(0))
- fieldvalue = optheap.getvalue(op.getarg(1))
+ fieldvalue = optheap.getvalue(op.getarglist()[-1])
if self.possible_aliasing(optheap, structvalue):
self.force_lazy_setfield(optheap)
assert not self.possible_aliasing(optheap, structvalue)
cached_fieldvalue = self._cached_fields.get(structvalue, None)
if cached_fieldvalue is not fieldvalue:
# common case: store the 'op' as lazy_setfield, and register
- # myself in the optheap's _lazy_setfields list
+ # myself in the optheap's _lazy_setfields_and_arrayitems list
self._lazy_setfield = op
if not self._lazy_setfield_registered:
- optheap._lazy_setfields.append(self)
+ optheap._lazy_setfields_and_arrayitems.append(self)
self._lazy_setfield_registered = True
else:
# this is the case where the pending setfield ends up
@@ -65,7 +65,7 @@
if self._lazy_setfield is not None:
op = self._lazy_setfield
assert optheap.getvalue(op.getarg(0)) is structvalue
- return optheap.getvalue(op.getarg(1))
+ return optheap.getvalue(op.getarglist()[-1])
else:
return self._cached_fields.get(structvalue, None)
@@ -87,7 +87,7 @@
# back in the cache: the value of this particular structure's
# field.
structvalue = optheap.getvalue(op.getarg(0))
- fieldvalue = optheap.getvalue(op.getarg(1))
+ fieldvalue = optheap.getvalue(op.getarglist()[-1])
self.remember_field_value(structvalue, fieldvalue)
def get_reconstructed(self, optimizer, valuemap):
@@ -100,12 +100,6 @@
return cf
-class CachedArrayItems(object):
- def __init__(self):
- self.fixed_index_items = {}
- self.var_index_item = None
- self.var_index_indexvalue = None
-
class BogusPureField(JitException):
pass
@@ -116,9 +110,10 @@
def __init__(self):
# cached fields: {descr: CachedField}
self.cached_fields = {}
- self._lazy_setfields = []
- # cached array items: {descr: CachedArrayItems}
+ # cached array items: {array descr: {index: CachedField}}
self.cached_arrayitems = {}
+ #
+ self._lazy_setfields_and_arrayitems = []
self._remove_guard_not_invalidated = False
self._seen_guard_not_invalidated = False
@@ -126,34 +121,23 @@
new = OptHeap()
if True:
- self.force_all_lazy_setfields()
+ self.force_all_lazy_setfields_and_arrayitems()
else:
assert 0 # was: new.lazy_setfields = self.lazy_setfields
for descr, d in self.cached_fields.items():
new.cached_fields[descr] = d.get_reconstructed(optimizer, valuemap)
- new.cached_arrayitems = {}
- for descr, d in self.cached_arrayitems.items():
- newd = {}
- new.cached_arrayitems[descr] = newd
- for value, cache in d.items():
- newcache = CachedArrayItems()
- newd[value.get_reconstructed(optimizer, valuemap)] = newcache
- if cache.var_index_item:
- newcache.var_index_item = \
- cache.var_index_item.get_reconstructed(optimizer, valuemap)
- if cache.var_index_indexvalue:
- newcache.var_index_indexvalue = \
- cache.var_index_indexvalue.get_reconstructed(optimizer, valuemap)
- for index, fieldvalue in cache.fixed_index_items.items():
- newcache.fixed_index_items[index] = \
- fieldvalue.get_reconstructed(optimizer, valuemap)
+ for descr, submap in self.cached_arrayitems.items():
+ newdict = {}
+ for index, d in submap.items():
+ newdict[index] = d.get_reconstructed(optimizer, valuemap)
+ new.cached_arrayitems[descr] = newdict
return new
def clean_caches(self):
- del self._lazy_setfields[:]
+ del self._lazy_setfields_and_arrayitems[:]
self.cached_fields.clear()
self.cached_arrayitems.clear()
@@ -164,50 +148,16 @@
cf = self.cached_fields[descr] = CachedField()
return cf
- def cache_arrayitem_value(self, descr, value, indexvalue, fieldvalue, write=False):
- d = self.cached_arrayitems.get(descr, None)
- if d is None:
- d = self.cached_arrayitems[descr] = {}
- cache = d.get(value, None)
- if cache is None:
- cache = d[value] = CachedArrayItems()
- indexbox = self.get_constant_box(indexvalue.box)
- if indexbox is not None:
- index = indexbox.getint()
- if write:
- for value, othercache in d.iteritems():
- # fixed index, clean the variable index cache, in case the
- # index is the same
- othercache.var_index_indexvalue = None
- othercache.var_index_item = None
- try:
- del othercache.fixed_index_items[index]
- except KeyError:
- pass
- cache.fixed_index_items[index] = fieldvalue
- else:
- if write:
- for value, othercache in d.iteritems():
- # variable index, clear all caches for this descr
- othercache.var_index_indexvalue = None
- othercache.var_index_item = None
- othercache.fixed_index_items.clear()
- cache.var_index_indexvalue = indexvalue
- cache.var_index_item = fieldvalue
-
- def read_cached_arrayitem(self, descr, value, indexvalue):
- d = self.cached_arrayitems.get(descr, None)
- if d is None:
- return None
- cache = d.get(value, None)
- if cache is None:
- return None
- indexbox = self.get_constant_box(indexvalue.box)
- if indexbox is not None:
- return cache.fixed_index_items.get(indexbox.getint(), None)
- elif cache.var_index_indexvalue is indexvalue:
- return cache.var_index_item
- return None
+ def arrayitem_cache(self, descr, index):
+ try:
+ submap = self.cached_arrayitems[descr]
+ except KeyError:
+ submap = self.cached_arrayitems[descr] = {}
+ try:
+ cf = submap[index]
+ except KeyError:
+ cf = submap[index] = CachedField()
+ return cf
def emit_operation(self, op):
self.emitting_operation(op)
@@ -219,7 +169,8 @@
if op.is_ovf():
return
if op.is_guard():
- self.optimizer.pendingfields = self.force_lazy_setfields_for_guard()
+ self.optimizer.pendingfields = (
+ self.force_lazy_setfields_and_arrayitems_for_guard())
return
opnum = op.getopnum()
if (opnum == rop.SETFIELD_GC or # handled specially
@@ -248,6 +199,8 @@
# XXX stored on effectinfo are large
for fielddescr in effectinfo.readonly_descrs_fields:
self.force_lazy_setfield(fielddescr)
+ for arraydescr in effectinfo.readonly_descrs_arrays:
+ self.force_lazy_setarrayitem(arraydescr)
for fielddescr in effectinfo.write_descrs_fields:
self.force_lazy_setfield(fielddescr)
try:
@@ -256,8 +209,11 @@
except KeyError:
pass
for arraydescr in effectinfo.write_descrs_arrays:
+ self.force_lazy_setarrayitem(arraydescr)
try:
- del self.cached_arrayitems[arraydescr]
+ submap = self.cached_arrayitems[arraydescr]
+ for cf in submap.itervalues():
+ cf._cached_fields.clear()
except KeyError:
pass
if effectinfo.check_forces_virtual_or_virtualizable():
@@ -266,7 +222,7 @@
# ^^^ we only need to force this field; the other fields
# of virtualref_info and virtualizable_info are not gcptrs.
return
- self.force_all_lazy_setfields()
+ self.force_all_lazy_setfields_and_arrayitems()
self.clean_caches()
@@ -277,6 +233,10 @@
for cf in self.cached_fields.itervalues():
if value in cf._cached_fields:
cf._cached_fields[newvalue] = cf._cached_fields[value]
+ for submap in self.cached_arrayitems.itervalues():
+ for cf in submap.itervalues():
+ if value in cf._cached_fields:
+ cf._cached_fields[newvalue] = cf._cached_fields[value]
def force_lazy_setfield(self, descr):
try:
@@ -285,6 +245,14 @@
return
cf.force_lazy_setfield(self)
+ def force_lazy_setarrayitem(self, arraydescr):
+ try:
+ submap = self.cached_arrayitems[arraydescr]
+ except KeyError:
+ return
+ for cf in submap.values():
+ cf.force_lazy_setfield(self)
+
def fixup_guard_situation(self):
# hackish: reverse the order of the last two operations if it makes
# sense to avoid a situation like "int_eq/setfield_gc/guard_true",
@@ -309,30 +277,49 @@
newoperations[-2] = lastop
newoperations[-1] = prevop
- def force_all_lazy_setfields(self):
- for cf in self._lazy_setfields:
- if not we_are_translated():
- assert cf in self.cached_fields.values()
+ def _assert_valid_cf(self, cf):
+ # check that 'cf' is in cached_fields or cached_arrayitems
+ if not we_are_translated():
+ if cf not in self.cached_fields.values():
+ for submap in self.cached_arrayitems.values():
+ if cf in submap.values():
+ break
+ else:
+ assert 0, "'cf' not in cached_fields/cached_arrayitems"
+
+ def force_all_lazy_setfields_and_arrayitems(self):
+ for cf in self._lazy_setfields_and_arrayitems:
+ self._assert_valid_cf(cf)
cf.force_lazy_setfield(self)
- def force_lazy_setfields_for_guard(self):
+ def force_lazy_setfields_and_arrayitems_for_guard(self):
pendingfields = []
- for cf in self._lazy_setfields:
- if not we_are_translated():
- assert cf in self.cached_fields.values()
+ for cf in self._lazy_setfields_and_arrayitems:
+ self._assert_valid_cf(cf)
op = cf._lazy_setfield
if op is None:
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.
+ # into a field of a non-virtual object. Here, 'op' in either
+ # SETFIELD_GC or SETARRAYITEM_GC.
value = self.getvalue(op.getarg(0))
assert not value.is_virtual() # it must be a non-virtual
- fieldvalue = self.getvalue(op.getarg(1))
+ fieldvalue = self.getvalue(op.getarglist()[-1])
if fieldvalue.is_virtual():
# this is the case that we leave to resume.py
+ opnum = op.getopnum()
+ if opnum == rop.SETFIELD_GC:
+ itemindex = -1
+ elif opnum == rop.SETARRAYITEM_GC:
+ indexvalue = self.getvalue(op.getarg(1))
+ assert indexvalue.is_constant()
+ itemindex = indexvalue.box.getint()
+ assert itemindex >= 0
+ else:
+ assert 0
pendingfields.append((op.getdescr(), value.box,
- fieldvalue.get_key_box()))
+ fieldvalue.get_key_box(), itemindex))
else:
cf.force_lazy_setfield(self)
self.fixup_guard_situation()
@@ -364,24 +351,45 @@
cf.do_setfield(self, op)
def optimize_GETARRAYITEM_GC(self, op):
- value = self.getvalue(op.getarg(0))
+ arrayvalue = self.getvalue(op.getarg(0))
indexvalue = self.getvalue(op.getarg(1))
- fieldvalue = self.read_cached_arrayitem(op.getdescr(), value, indexvalue)
- if fieldvalue is not None:
- self.make_equal_to(op.result, fieldvalue)
- return
- ###self.optimizer.optimize_default(op)
+ cf = None
+ if indexvalue.is_constant():
+ # use the cache on (arraydescr, index), which is a constant
+ cf = self.arrayitem_cache(op.getdescr(), indexvalue.box.getint())
+ fieldvalue = cf.getfield_from_cache(self, arrayvalue)
+ if fieldvalue is not None:
+ self.make_equal_to(op.result, fieldvalue)
+ return
+ else:
+ # variable index, so make sure the lazy setarrayitems are done
+ self.force_lazy_setarrayitem(op.getdescr())
+ # default case: produce the operation
+ arrayvalue.ensure_nonnull()
self.emit_operation(op)
- fieldvalue = self.getvalue(op.result)
- self.cache_arrayitem_value(op.getdescr(), value, indexvalue, fieldvalue)
+ # the remember the result of reading the array item
+ if cf is not None:
+ fieldvalue = self.getvalue(op.result)
+ cf.remember_field_value(arrayvalue, fieldvalue)
def optimize_SETARRAYITEM_GC(self, op):
- self.emit_operation(op)
- value = self.getvalue(op.getarg(0))
- fieldvalue = self.getvalue(op.getarg(2))
+ if self.has_pure_result(rop.GETARRAYITEM_GC_PURE, [op.getarg(0),
+ op.getarg(1)],
+ op.getdescr()):
+ os.write(2, '[bogus immutable array declaration: %s]\n' %
+ (op.getdescr().repr_of_descr()))
+ raise BogusPureField
+ #
indexvalue = self.getvalue(op.getarg(1))
- self.cache_arrayitem_value(op.getdescr(), value, indexvalue, fieldvalue,
- write=True)
+ if indexvalue.is_constant():
+ # use the cache on (arraydescr, index), which is a constant
+ cf = self.arrayitem_cache(op.getdescr(), indexvalue.box.getint())
+ cf.do_setfield(self, op)
+ else:
+ # variable index, so make sure the lazy setarrayitems are done
+ self.force_lazy_setarrayitem(op.getdescr())
+ # and then emit the operation
+ self.emit_operation(op)
def optimize_QUASIIMMUT_FIELD(self, op):
# Pattern: QUASIIMMUT_FIELD(s, descr=QuasiImmutDescr)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -1070,8 +1070,8 @@
"""
expected = """
[i1, p0]
+ p1 = new_array(i1, descr=arraydescr)
setarrayitem_gc(p0, 0, i1, descr=arraydescr)
- p1 = new_array(i1, descr=arraydescr)
jump(i1, p1)
"""
self.optimize_loop(ops, expected)
@@ -1436,9 +1436,9 @@
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)
+ setarrayitem_gc(p3, 0, i5, descr=arraydescr)
setfield_gc(p1, i4, descr=nextdescr)
jump(p1, i1, i2, p3)
"""
@@ -1612,6 +1612,7 @@
self.optimize_loop(ops, expected)
def test_duplicate_getarrayitem_after_setarrayitem_2(self):
+ py.test.skip("setarrayitem with variable index")
ops = """
[p1, p2, p3, i1]
setarrayitem_gc(p1, 0, p2, descr=arraydescr2)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
@@ -51,7 +51,7 @@
restype=types.sint)
#
def calldescr(cpu, FUNC, oopspecindex, extraeffect=None):
- einfo = EffectInfo([], [], [], oopspecindex=oopspecindex,
+ einfo = EffectInfo([], [], [], [], oopspecindex=oopspecindex,
extraeffect=extraeffect)
return cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, einfo)
#
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -1381,8 +1381,8 @@
"""
expected = """
[i1, p0]
+ p1 = new_array(i1, descr=arraydescr)
setarrayitem_gc(p0, 0, i1, descr=arraydescr)
- p1 = new_array(i1, descr=arraydescr)
jump(i1, p1)
"""
self.optimize_loop(ops, expected)
@@ -1806,9 +1806,9 @@
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)
+ setarrayitem_gc(p3, 0, i5, descr=arraydescr)
setfield_gc(p1, i4, descr=nextdescr)
escape()
jump(p1, i1, i2, p3, i3)
@@ -1818,9 +1818,9 @@
#
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)
+ setarrayitem_gc(p3, 0, i5, descr=arraydescr)
setfield_gc(p1, i4, descr=nextdescr)
escape()
jump(p1, i1, i2, p3, i3)
@@ -2055,6 +2055,7 @@
self.optimize_loop(ops, expected)
def test_duplicate_getarrayitem_after_setarrayitem_2(self):
+ py.test.skip("setarrayitem with variable index")
ops = """
[p1, p2, p3, i1]
setarrayitem_gc(p1, 0, p2, descr=arraydescr2)
@@ -5876,4 +5877,25 @@
escape(p0)
jump(p0)
"""
- self.optimize_loop(ops, expected)
\ No newline at end of file
+ self.optimize_loop(ops, expected)
+
+ def test_setarrayitem_lazy(self):
+ ops = """
+ [i0, i1]
+ p0 = escape()
+ i2 = escape()
+ p1 = new_with_vtable(ConstClass(node_vtable))
+ setarrayitem_gc(p0, 2, p1, descr=arraydescr)
+ guard_true(i2) []
+ setarrayitem_gc(p0, 2, p0, descr=arraydescr)
+ jump(i0, i1)
+ """
+ expected = """
+ [i0, i1]
+ p0 = escape()
+ i2 = escape()
+ guard_true(i2) [p0]
+ setarrayitem_gc(p0, 2, p0, descr=arraydescr)
+ jump(i0, i1)
+ """
+ self.optimize_loop(ops, expected)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_util.py b/pypy/jit/metainterp/optimizeopt/test/test_util.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_util.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_util.py
@@ -166,19 +166,19 @@
FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
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([], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY))
+ EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY))
for _name, _os in [
('strconcatdescr', 'OS_STR_CONCAT'),
@@ -195,15 +195,15 @@
_oopspecindex = getattr(EffectInfo, _os)
locals()[_name] = \
cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], oopspecindex=_oopspecindex))
+ EffectInfo([], [], [], [], oopspecindex=_oopspecindex))
#
_oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
locals()[_name.replace('str', 'unicode')] = \
cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], oopspecindex=_oopspecindex))
+ EffectInfo([], [], [], [], oopspecindex=_oopspecindex))
s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
+ EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
#
class LoopToken(AbstractDescr):
diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py
--- a/pypy/jit/metainterp/resume.py
+++ b/pypy/jit/metainterp/resume.py
@@ -2,10 +2,12 @@
from pypy.jit.metainterp.history import Box, Const, ConstInt, getkind
from pypy.jit.metainterp.history import BoxInt, BoxPtr, BoxFloat
from pypy.jit.metainterp.history import INT, REF, FLOAT, HOLE
+from pypy.jit.metainterp.history import AbstractDescr
from pypy.jit.metainterp.resoperation import rop
from pypy.jit.metainterp import jitprof
from pypy.jit.codewriter.effectinfo import EffectInfo
from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rstr
+from pypy.rpython import annlowlevel
from pypy.rlib import rarithmetic, rstack
from pypy.rlib.objectmodel import we_are_translated, specialize
from pypy.rlib.debug import have_debug_prints, ll_assert
@@ -82,6 +84,13 @@
('nums', lltype.Array(rffi.SHORT)))
NUMBERINGP.TO.become(NUMBERING)
+PENDINGFIELDSTRUCT = lltype.Struct('PendingField',
+ ('lldescr', annlowlevel.base_ptr_lltype()),
+ ('num', rffi.SHORT),
+ ('fieldnum', rffi.SHORT),
+ ('itemindex', rffi.INT))
+PENDINGFIELDSP = lltype.Ptr(lltype.GcArray(PENDINGFIELDSTRUCT))
+
TAGMASK = 3
def tag(value, tagbits):
@@ -329,7 +338,7 @@
value = values[box]
value.get_args_for_fail(self)
- for _, box, fieldbox in pending_setfields:
+ for _, box, fieldbox, _ in pending_setfields:
self.register_box(box)
self.register_box(fieldbox)
value = values[fieldbox]
@@ -405,13 +414,25 @@
return False
def _add_pending_fields(self, pending_setfields):
- rd_pendingfields = None
+ rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
if pending_setfields:
- rd_pendingfields = []
- for descr, box, fieldbox in pending_setfields:
+ n = len(pending_setfields)
+ rd_pendingfields = lltype.malloc(PENDINGFIELDSP.TO, n)
+ for i in range(n):
+ descr, box, fieldbox, itemindex = pending_setfields[i]
+ lldescr = annlowlevel.cast_instance_to_base_ptr(descr)
num = self._gettagged(box)
fieldnum = self._gettagged(fieldbox)
- rd_pendingfields.append((descr, num, fieldnum))
+ # the index is limited to 2147483647 (64-bit machines only)
+ if itemindex > 2147483647:
+ from pypy.jit.metainterp import compile
+ compile.giveup()
+ itemindex = rffi.cast(rffi.INT, itemindex)
+ #
+ rd_pendingfields[i].lldescr = lldescr
+ rd_pendingfields[i].num = num
+ rd_pendingfields[i].fieldnum = fieldnum
+ rd_pendingfields[i].itemindex= itemindex
self.storage.rd_pendingfields = rd_pendingfields
def _gettagged(self, box):
@@ -727,10 +748,28 @@
self.virtuals_cache = [self.virtual_default] * len(virtuals)
def _prepare_pendingfields(self, pendingfields):
- if pendingfields is not None:
- for descr, num, fieldnum in pendingfields:
+ if pendingfields:
+ for i in range(len(pendingfields)):
+ lldescr = pendingfields[i].lldescr
+ num = pendingfields[i].num
+ fieldnum = pendingfields[i].fieldnum
+ itemindex= pendingfields[i].itemindex
+ descr = annlowlevel.cast_base_ptr_to_instance(AbstractDescr,
+ lldescr)
struct = self.decode_ref(num)
- self.setfield(descr, struct, fieldnum)
+ itemindex = rffi.cast(lltype.Signed, itemindex)
+ if itemindex < 0:
+ self.setfield(descr, struct, fieldnum)
+ else:
+ self.setarrayitem(descr, struct, itemindex, fieldnum)
+
+ def setarrayitem(self, arraydescr, array, index, fieldnum):
+ if arraydescr.is_array_of_pointers():
+ self.setarrayitem_ref(arraydescr, array, index, fieldnum)
+ elif arraydescr.is_array_of_floats():
+ self.setarrayitem_float(arraydescr, array, index, fieldnum)
+ else:
+ self.setarrayitem_int(arraydescr, array, index, fieldnum)
def _prepare_next_section(self, info):
# Use info.enumerate_vars(), normally dispatching to
@@ -903,15 +942,15 @@
structbox, fieldbox)
def setarrayitem_int(self, arraydescr, arraybox, index, fieldnum):
- self.setarrayitem(arraydescr, arraybox, index, fieldnum, INT)
+ self._setarrayitem(arraydescr, arraybox, index, fieldnum, INT)
def setarrayitem_ref(self, arraydescr, arraybox, index, fieldnum):
- self.setarrayitem(arraydescr, arraybox, index, fieldnum, REF)
+ self._setarrayitem(arraydescr, arraybox, index, fieldnum, REF)
def setarrayitem_float(self, arraydescr, arraybox, index, fieldnum):
- self.setarrayitem(arraydescr, arraybox, index, fieldnum, FLOAT)
+ self._setarrayitem(arraydescr, arraybox, index, fieldnum, FLOAT)
- def setarrayitem(self, arraydescr, arraybox, index, fieldnum, kind):
+ def _setarrayitem(self, arraydescr, arraybox, index, fieldnum, kind):
itembox = self.decode_box(fieldnum, kind)
self.metainterp.execute_and_record(rop.SETARRAYITEM_GC,
arraydescr, arraybox,
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -1677,6 +1677,8 @@
res = self.meta_interp(g, [6, 14])
assert res == g(6, 14)
self.check_loop_count(9)
+ self.check_loops(getarrayitem_gc=8, everywhere=True)
+ py.test.skip("for the following, we need setarrayitem(varindex)")
self.check_loops(getarrayitem_gc=6, everywhere=True)
def test_multiple_specialied_versions_bridge(self):
@@ -2296,6 +2298,21 @@
res = self.meta_interp(f, [1])
assert res == f(1)
+ def test_remove_array_operations(self):
+ myjitdriver = JitDriver(greens = [], reds = ['a'])
+ class W_Int:
+ def __init__(self, intvalue):
+ self.intvalue = intvalue
+ def f(x):
+ a = [W_Int(x)]
+ while a[0].intvalue > 0:
+ myjitdriver.jit_merge_point(a=a)
+ a[0] = W_Int(a[0].intvalue - 3)
+ return a[0].intvalue
+ res = self.meta_interp(f, [100])
+ assert res == -2
+ #self.check_loops(getarrayitem_gc=0, setarrayitem_gc=0) -- xxx?
+
class TestOOtype(BasicTests, OOJitMixin):
def test_oohash(self):
diff --git a/pypy/jit/metainterp/test/test_list.py b/pypy/jit/metainterp/test/test_list.py
--- a/pypy/jit/metainterp/test/test_list.py
+++ b/pypy/jit/metainterp/test/test_list.py
@@ -49,7 +49,7 @@
x = l[n]
l = [3] * 100
l[3] = x
- l[3] = x + 1
+ l[4] = x + 1
n -= 1
return l[0]
diff --git a/pypy/jit/metainterp/test/test_resume.py b/pypy/jit/metainterp/test/test_resume.py
--- a/pypy/jit/metainterp/test/test_resume.py
+++ b/pypy/jit/metainterp/test/test_resume.py
@@ -1238,7 +1238,7 @@
liveboxes = []
modifier._number_virtuals(liveboxes, values, 0)
assert liveboxes == [b2s, b4s] or liveboxes == [b4s, b2s]
- modifier._add_pending_fields([(LLtypeMixin.nextdescr, b2s, b4s)])
+ modifier._add_pending_fields([(LLtypeMixin.nextdescr, b2s, b4s, -1)])
storage.rd_consts = memo.consts[:]
storage.rd_numb = None
# resume
@@ -1259,6 +1259,106 @@
assert len(expected) == len(trace)
assert demo55.next == demo66
+def test_virtual_adder_pending_fields_and_arrayitems():
+ class Storage(object):
+ pass
+ storage = Storage()
+ modifier = ResumeDataVirtualAdder(storage, None)
+ modifier._add_pending_fields([])
+ assert not storage.rd_pendingfields
+ #
+ class FieldDescr(object):
+ pass
+ field_a = FieldDescr()
+ storage = Storage()
+ modifier = ResumeDataVirtualAdder(storage, None)
+ modifier.liveboxes_from_env = {42: rffi.cast(rffi.SHORT, 1042),
+ 61: rffi.cast(rffi.SHORT, 1061)}
+ modifier._add_pending_fields([(field_a, 42, 61, -1)])
+ pf = storage.rd_pendingfields
+ assert len(pf) == 1
+ assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[0].lldescr)
+ is field_a)
+ assert rffi.cast(lltype.Signed, pf[0].num) == 1042
+ assert rffi.cast(lltype.Signed, pf[0].fieldnum) == 1061
+ assert rffi.cast(lltype.Signed, pf[0].itemindex) == -1
+ #
+ array_a = FieldDescr()
+ storage = Storage()
+ modifier = ResumeDataVirtualAdder(storage, None)
+ modifier.liveboxes_from_env = {42: rffi.cast(rffi.SHORT, 1042),
+ 61: rffi.cast(rffi.SHORT, 1061),
+ 62: rffi.cast(rffi.SHORT, 1062),
+ 63: rffi.cast(rffi.SHORT, 1063)}
+ modifier._add_pending_fields([(array_a, 42, 61, 0),
+ (array_a, 42, 62, 2147483647)])
+ pf = storage.rd_pendingfields
+ assert len(pf) == 2
+ assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[0].lldescr)
+ is array_a)
+ assert rffi.cast(lltype.Signed, pf[0].num) == 1042
+ assert rffi.cast(lltype.Signed, pf[0].fieldnum) == 1061
+ assert rffi.cast(lltype.Signed, pf[0].itemindex) == 0
+ assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[1].lldescr)
+ is array_a)
+ assert rffi.cast(lltype.Signed, pf[1].num) == 1042
+ assert rffi.cast(lltype.Signed, pf[1].fieldnum) == 1062
+ assert rffi.cast(lltype.Signed, pf[1].itemindex) == 2147483647
+ #
+ from pypy.jit.metainterp.pyjitpl import SwitchToBlackhole
+ py.test.raises(SwitchToBlackhole, modifier._add_pending_fields,
+ [(array_a, 42, 63, 2147483648)])
+
+def test_resume_reader_fields_and_arrayitems():
+ class ResumeReader(AbstractResumeDataReader):
+ def __init__(self, got=None, got_array=None):
+ self.got = got
+ self.got_array = got_array
+ def setfield(self, descr, struct, fieldnum):
+ assert lltype.typeOf(struct) is lltype.Signed
+ assert lltype.typeOf(fieldnum) is rffi.SHORT
+ fieldnum = rffi.cast(lltype.Signed, fieldnum)
+ self.got.append((descr, struct, fieldnum))
+ def setarrayitem(self, arraydescr, array, index, fieldnum):
+ assert lltype.typeOf(array) is lltype.Signed
+ assert lltype.typeOf(index) is lltype.Signed
+ assert lltype.typeOf(fieldnum) is rffi.SHORT
+ fieldnum = rffi.cast(lltype.Signed, fieldnum)
+ self.got_array.append((arraydescr, array, index, fieldnum))
+ def decode_ref(self, num):
+ return rffi.cast(lltype.Signed, num) * 100
+ got = []
+ pf = lltype.nullptr(PENDINGFIELDSP.TO)
+ ResumeReader(got)._prepare_pendingfields(pf)
+ assert got == []
+ #
+ class FieldDescr(AbstractDescr):
+ pass
+ field_a = FieldDescr()
+ field_b = FieldDescr()
+ pf = lltype.malloc(PENDINGFIELDSP.TO, 2)
+ pf[0].lldescr = annlowlevel.cast_instance_to_base_ptr(field_a)
+ pf[0].num = rffi.cast(rffi.SHORT, 1042)
+ pf[0].fieldnum = rffi.cast(rffi.SHORT, 1061)
+ pf[0].itemindex = rffi.cast(rffi.INT, -1)
+ pf[1].lldescr = annlowlevel.cast_instance_to_base_ptr(field_b)
+ pf[1].num = rffi.cast(rffi.SHORT, 2042)
+ pf[1].fieldnum = rffi.cast(rffi.SHORT, 2061)
+ pf[1].itemindex = rffi.cast(rffi.INT, -1)
+ got = []
+ ResumeReader(got)._prepare_pendingfields(pf)
+ assert got == [(field_a, 104200, 1061), (field_b, 204200, 2061)]
+ #
+ array_a = FieldDescr()
+ pf = lltype.malloc(PENDINGFIELDSP.TO, 1)
+ pf[0].lldescr = annlowlevel.cast_instance_to_base_ptr(array_a)
+ pf[0].num = rffi.cast(rffi.SHORT, 1042)
+ pf[0].fieldnum = rffi.cast(rffi.SHORT, 1063)
+ pf[0].itemindex = rffi.cast(rffi.INT, 123)
+ got_array = []
+ ResumeReader(got_array=got_array)._prepare_pendingfields(pf)
+ assert got_array == [(array_a, 104200, 123, 1063)]
+
def test_invalidation_needed():
class options:
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -294,7 +294,7 @@
break
new_frame = space.createframe(code, w_func.w_func_globals,
w_func.closure)
- new_frame.fastlocals_w[0] = w_item
+ new_frame.locals_stack_w[0] = w_item
w_res = new_frame.run()
result_w.append(w_res)
return result_w
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -21,8 +21,7 @@
from pypy.module.pypyjit.interp_resop import debug_merge_point_from_boxes
PyFrame._virtualizable2_ = ['last_instr', 'pycode',
- 'valuestackdepth', 'valuestack_w[*]',
- 'fastlocals_w[*]',
+ 'valuestackdepth', 'locals_stack_w[*]',
'last_exception',
'lastblock',
'is_being_profiled',
diff --git a/pypy/objspace/flow/flowcontext.py b/pypy/objspace/flow/flowcontext.py
--- a/pypy/objspace/flow/flowcontext.py
+++ b/pypy/objspace/flow/flowcontext.py
@@ -384,8 +384,9 @@
# hack for unrolling iterables, don't use this
def replace_in_stack(self, oldvalue, newvalue):
w_new = Constant(newvalue)
- stack_items_w = self.crnt_frame.valuestack_w
- for i in range(self.crnt_frame.valuestackdepth-1, -1, -1):
+ f = self.crnt_frame
+ stack_items_w = f.locals_stack_w
+ for i in range(f.valuestackdepth-1, f.nlocals-1, -1):
w_v = stack_items_w[i]
if isinstance(w_v, Constant):
if w_v.value is oldvalue:
diff --git a/pypy/objspace/flow/framestate.py b/pypy/objspace/flow/framestate.py
--- a/pypy/objspace/flow/framestate.py
+++ b/pypy/objspace/flow/framestate.py
@@ -10,7 +10,7 @@
def __init__(self, state):
if isinstance(state, PyFrame):
# getfastscope() can return real None, for undefined locals
- data = state.getfastscope() + state.savevaluestack()
+ data = state.save_locals_stack()
if state.last_exception is None:
data.append(Constant(None))
data.append(Constant(None))
@@ -36,11 +36,9 @@
def restoreframe(self, frame):
if isinstance(frame, PyFrame):
- fastlocals = len(frame.fastlocals_w)
data = self.mergeable[:]
recursively_unflatten(frame.space, data)
- frame.setfastscope(data[:fastlocals]) # Nones == undefined locals
- frame.restorevaluestack(data[fastlocals:-2])
+ frame.restore_locals_stack(data[:-2]) # Nones == undefined locals
if data[-2] == Constant(None):
assert data[-1] == Constant(None)
frame.last_exception = None
diff --git a/pypy/objspace/flow/test/test_framestate.py b/pypy/objspace/flow/test/test_framestate.py
--- a/pypy/objspace/flow/test/test_framestate.py
+++ b/pypy/objspace/flow/test/test_framestate.py
@@ -25,7 +25,7 @@
dummy = Constant(None)
#dummy.dummy = True
arg_list = ([Variable() for i in range(formalargcount)] +
- [dummy] * (len(frame.fastlocals_w) - formalargcount))
+ [dummy] * (frame.nlocals - formalargcount))
frame.setfastscope(arg_list)
return frame
@@ -42,7 +42,7 @@
def test_neq_hacked_framestate(self):
frame = self.getframe(self.func_simple)
fs1 = FrameState(frame)
- frame.fastlocals_w[-1] = Variable()
+ frame.locals_stack_w[frame.nlocals-1] = Variable()
fs2 = FrameState(frame)
assert fs1 != fs2
@@ -55,7 +55,7 @@
def test_union_on_hacked_framestates(self):
frame = self.getframe(self.func_simple)
fs1 = FrameState(frame)
- frame.fastlocals_w[-1] = Variable()
+ frame.locals_stack_w[frame.nlocals-1] = Variable()
fs2 = FrameState(frame)
assert fs1.union(fs2) == fs2 # fs2 is more general
assert fs2.union(fs1) == fs2 # fs2 is more general
@@ -63,7 +63,7 @@
def test_restore_frame(self):
frame = self.getframe(self.func_simple)
fs1 = FrameState(frame)
- frame.fastlocals_w[-1] = Variable()
+ frame.locals_stack_w[frame.nlocals-1] = Variable()
fs1.restoreframe(frame)
assert fs1 == FrameState(frame)
@@ -82,25 +82,26 @@
def test_getoutputargs(self):
frame = self.getframe(self.func_simple)
fs1 = FrameState(frame)
- frame.fastlocals_w[-1] = Variable()
+ frame.locals_stack_w[frame.nlocals-1] = Variable()
fs2 = FrameState(frame)
outputargs = fs1.getoutputargs(fs2)
# 'x' -> 'x' is a Variable
- # fastlocals_w[-1] -> fastlocals_w[-1] is Constant(None)
- assert outputargs == [frame.fastlocals_w[0], Constant(None)]
+ # locals_w[n-1] -> locals_w[n-1] is Constant(None)
+ assert outputargs == [frame.locals_stack_w[0], Constant(None)]
def test_union_different_constants(self):
frame = self.getframe(self.func_simple)
fs1 = FrameState(frame)
- frame.fastlocals_w[-1] = Constant(42)
+ frame.locals_stack_w[frame.nlocals-1] = Constant(42)
fs2 = FrameState(frame)
fs3 = fs1.union(fs2)
fs3.restoreframe(frame)
- assert isinstance(frame.fastlocals_w[-1], Variable) # generalized
+ assert isinstance(frame.locals_stack_w[frame.nlocals-1], Variable)
+ # ^^^ generalized
def test_union_spectag(self):
frame = self.getframe(self.func_simple)
fs1 = FrameState(frame)
- frame.fastlocals_w[-1] = Constant(SpecTag())
+ frame.locals_stack_w[frame.nlocals-1] = Constant(SpecTag())
fs2 = FrameState(frame)
assert fs1.union(fs2) is None # UnionError
More information about the pypy-commit
mailing list