[pypy-commit] pypy default: merge
cfbolz
noreply at buildbot.pypy.org
Wed Oct 26 14:19:08 CEST 2011
Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch:
Changeset: r48472:2c94dfea4323
Date: 2011-10-26 14:18 +0200
http://bitbucket.org/pypy/pypy/changeset/2c94dfea4323/
Log: merge
diff --git a/lib-python/modified-2.7/urllib2.py b/lib-python/modified-2.7/urllib2.py
--- a/lib-python/modified-2.7/urllib2.py
+++ b/lib-python/modified-2.7/urllib2.py
@@ -395,11 +395,7 @@
meth_name = protocol+"_response"
for processor in self.process_response.get(protocol, []):
meth = getattr(processor, meth_name)
- try:
- response = meth(req, response)
- except:
- response.close()
- raise
+ response = meth(req, response)
return response
diff --git a/pypy/jit/backend/llsupport/gc.py b/pypy/jit/backend/llsupport/gc.py
--- a/pypy/jit/backend/llsupport/gc.py
+++ b/pypy/jit/backend/llsupport/gc.py
@@ -649,10 +649,13 @@
def malloc_basic(size, tid):
type_id = llop.extract_ushort(llgroup.HALFWORD, tid)
has_finalizer = bool(tid & (1<<llgroup.HALFSHIFT))
+ has_light_finalizer = bool(tid & (1<<(llgroup.HALFSHIFT + 1)))
check_typeid(type_id)
res = llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
type_id, size,
- has_finalizer, False)
+ has_finalizer,
+ has_light_finalizer,
+ False)
# In case the operation above failed, we are returning NULL
# from this function to assembler. There is also an RPython
# exception set, typically MemoryError; but it's easier and
@@ -723,7 +726,7 @@
# also use it to allocate varsized objects. The tid
# and possibly the length are both set afterward.
gcref = llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
- 0, size, False, False)
+ 0, size, False, False, False)
return rffi.cast(lltype.Signed, gcref)
self.malloc_slowpath = malloc_slowpath
self.MALLOC_SLOWPATH = lltype.FuncType([lltype.Signed], lltype.Signed)
@@ -747,7 +750,9 @@
type_id = self.layoutbuilder.get_type_id(S)
assert not self.layoutbuilder.is_weakref_type(S)
has_finalizer = bool(self.layoutbuilder.has_finalizer(S))
- flags = int(has_finalizer) << llgroup.HALFSHIFT
+ has_light_finalizer = bool(self.layoutbuilder.has_light_finalizer(S))
+ flags = (int(has_finalizer) << llgroup.HALFSHIFT |
+ int(has_light_finalizer) << (llgroup.HALFSHIFT + 1))
descr.tid = llop.combine_ushort(lltype.Signed, type_id, flags)
def init_array_descr(self, A, descr):
diff --git a/pypy/jit/backend/llsupport/test/test_gc.py b/pypy/jit/backend/llsupport/test/test_gc.py
--- a/pypy/jit/backend/llsupport/test/test_gc.py
+++ b/pypy/jit/backend/llsupport/test/test_gc.py
@@ -247,12 +247,14 @@
self.record = []
def do_malloc_fixedsize_clear(self, RESTYPE, type_id, size,
- has_finalizer, contains_weakptr):
+ has_finalizer, has_light_finalizer,
+ contains_weakptr):
assert not contains_weakptr
+ assert not has_finalizer # in these tests
+ assert not has_light_finalizer # in these tests
p = llmemory.raw_malloc(size)
p = llmemory.cast_adr_to_ptr(p, RESTYPE)
- flags = int(has_finalizer) << 16
- tid = llop.combine_ushort(lltype.Signed, type_id, flags)
+ tid = llop.combine_ushort(lltype.Signed, type_id, 0)
self.record.append(("fixedsize", repr(size), tid, p))
return p
diff --git a/pypy/jit/backend/test/test_ll_random.py b/pypy/jit/backend/test/test_ll_random.py
--- a/pypy/jit/backend/test/test_ll_random.py
+++ b/pypy/jit/backend/test/test_ll_random.py
@@ -28,16 +28,27 @@
fork.structure_types_and_vtables = self.structure_types_and_vtables
return fork
- def get_structptr_var(self, r, must_have_vtable=False, type=lltype.Struct):
+ def _choose_ptr_vars(self, from_, type, array_of_structs):
+ ptrvars = []
+ for i in range(len(from_)):
+ v, S = from_[i][:2]
+ if not isinstance(S, type):
+ continue
+ if (isinstance(S, lltype.Array) and
+ isinstance(S.OF, lltype.Struct) == array_of_structs):
+ ptrvars.append((v, S))
+ return ptrvars
+
+ def get_structptr_var(self, r, must_have_vtable=False, type=lltype.Struct,
+ array_of_structs=False):
while True:
- ptrvars = [(v, S) for (v, S) in self.ptrvars
- if isinstance(S, type)]
+ ptrvars = self._choose_ptr_vars(self.ptrvars, type,
+ array_of_structs)
if ptrvars and r.random() < 0.8:
v, S = r.choice(ptrvars)
else:
- prebuilt_ptr_consts = [(v, S)
- for (v, S, _) in self.prebuilt_ptr_consts
- if isinstance(S, type)]
+ prebuilt_ptr_consts = self._choose_ptr_vars(
+ self.prebuilt_ptr_consts, type, array_of_structs)
if prebuilt_ptr_consts and r.random() < 0.7:
v, S = r.choice(prebuilt_ptr_consts)
else:
@@ -48,7 +59,8 @@
has_vtable=must_have_vtable)
else:
# create a new constant array
- p = self.get_random_array(r)
+ p = self.get_random_array(r,
+ must_be_array_of_structs=array_of_structs)
S = lltype.typeOf(p).TO
v = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, p))
self.prebuilt_ptr_consts.append((v, S,
@@ -74,7 +86,8 @@
TYPE = lltype.Signed
return TYPE
- def get_random_structure_type(self, r, with_vtable=None, cache=True):
+ def get_random_structure_type(self, r, with_vtable=None, cache=True,
+ type=lltype.GcStruct):
if cache and self.structure_types and r.random() < 0.5:
return r.choice(self.structure_types)
fields = []
@@ -85,7 +98,7 @@
for i in range(r.randrange(1, 5)):
TYPE = self.get_random_primitive_type(r)
fields.append(('f%d' % i, TYPE))
- S = lltype.GcStruct('S%d' % self.counter, *fields, **kwds)
+ S = type('S%d' % self.counter, *fields, **kwds)
self.counter += 1
if cache:
self.structure_types.append(S)
@@ -125,17 +138,29 @@
setattr(p, fieldname, rffi.cast(TYPE, r.random_integer()))
return p
- def get_random_array_type(self, r):
- TYPE = self.get_random_primitive_type(r)
+ def get_random_array_type(self, r, can_be_array_of_struct=False,
+ must_be_array_of_structs=False):
+ if ((can_be_array_of_struct and r.random() < 0.1) or
+ must_be_array_of_structs):
+ TYPE = self.get_random_structure_type(r, cache=False,
+ type=lltype.Struct)
+ else:
+ TYPE = self.get_random_primitive_type(r)
return lltype.GcArray(TYPE)
- def get_random_array(self, r):
- A = self.get_random_array_type(r)
+ def get_random_array(self, r, must_be_array_of_structs=False):
+ A = self.get_random_array_type(r,
+ must_be_array_of_structs=must_be_array_of_structs)
length = (r.random_integer() // 15) % 300 # length: between 0 and 299
# likely to be small
p = lltype.malloc(A, length)
- for i in range(length):
- p[i] = rffi.cast(A.OF, r.random_integer())
+ if isinstance(A.OF, lltype.Primitive):
+ for i in range(length):
+ p[i] = rffi.cast(A.OF, r.random_integer())
+ else:
+ for i in range(length):
+ for fname, TP in A.OF._flds.iteritems():
+ setattr(p[i], fname, rffi.cast(TP, r.random_integer()))
return p
def get_index(self, length, r):
@@ -220,7 +245,7 @@
class GetFieldOperation(test_random.AbstractOperation):
def field_descr(self, builder, r):
- v, S = builder.get_structptr_var(r)
+ v, S = builder.get_structptr_var(r, )
names = S._names
if names[0] == 'parent':
names = names[1:]
@@ -239,6 +264,31 @@
continue
break
+class GetInteriorFieldOperation(test_random.AbstractOperation):
+ def field_descr(self, builder, r):
+ v, A = builder.get_structptr_var(r, type=lltype.Array,
+ array_of_structs=True)
+ array = v.getref(lltype.Ptr(A))
+ v_index = builder.get_index(len(array), r)
+ names = A.OF._names
+ if names[0] == 'parent':
+ names = names[1:]
+ name = r.choice(names)
+ descr = builder.cpu.interiorfielddescrof(A, name)
+ descr._random_info = 'cpu.interiorfielddescrof(%s, %r)' % (A.OF._name,
+ name)
+ TYPE = getattr(A.OF, name)
+ return v, v_index, descr, TYPE
+
+ def produce_into(self, builder, r):
+ while True:
+ try:
+ v, v_index, descr, _ = self.field_descr(builder, r)
+ self.put(builder, [v, v_index], descr)
+ except lltype.UninitializedMemoryAccess:
+ continue
+ break
+
class SetFieldOperation(GetFieldOperation):
def produce_into(self, builder, r):
v, descr, TYPE = self.field_descr(builder, r)
@@ -251,6 +301,20 @@
break
builder.do(self.opnum, [v, w], descr)
+class SetInteriorFieldOperation(GetFieldOperation):
+ def produce_into(self, builder, r):
+ import pdb
+ pdb.set_trace()
+ v, descr, TYPE = self.field_descr(builder, r)
+ while True:
+ if r.random() < 0.3:
+ w = ConstInt(r.random_integer())
+ else:
+ w = r.choice(builder.intvars)
+ if rffi.cast(lltype.Signed, rffi.cast(TYPE, w.value)) == w.value:
+ break
+ builder.do(self.opnum, [v, w], descr)
+
class NewOperation(test_random.AbstractOperation):
def size_descr(self, builder, S):
descr = builder.cpu.sizeof(S)
@@ -306,7 +370,7 @@
class NewArrayOperation(ArrayOperation):
def produce_into(self, builder, r):
- A = builder.get_random_array_type(r)
+ A = builder.get_random_array_type(r, can_be_array_of_struct=True)
v_size = builder.get_index(300, r)
v_ptr = builder.do(self.opnum, [v_size], self.array_descr(builder, A))
builder.ptrvars.append((v_ptr, A))
@@ -586,7 +650,9 @@
for i in range(4): # make more common
OPERATIONS.append(GetFieldOperation(rop.GETFIELD_GC))
OPERATIONS.append(GetFieldOperation(rop.GETFIELD_GC))
+ OPERATIONS.append(GetInteriorFieldOperation(rop.GETINTERIORFIELD_GC))
OPERATIONS.append(SetFieldOperation(rop.SETFIELD_GC))
+ #OPERATIONS.append(SetInteriorFieldOperation(rop.SETINTERIORFIELD_GC))
OPERATIONS.append(NewOperation(rop.NEW))
OPERATIONS.append(NewOperation(rop.NEW_WITH_VTABLE))
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -1276,8 +1276,8 @@
genop_int_ne = _cmpop("NE", "NE")
genop_int_gt = _cmpop("G", "L")
genop_int_ge = _cmpop("GE", "LE")
- genop_ptr_eq = genop_int_eq
- genop_ptr_ne = genop_int_ne
+ genop_ptr_eq = genop_instance_ptr_eq = genop_int_eq
+ genop_ptr_ne = genop_instance_ptr_ne = genop_int_ne
genop_float_lt = _cmpop_float('B', 'A')
genop_float_le = _cmpop_float('BE', 'AE')
@@ -1297,8 +1297,8 @@
genop_guard_int_ne = _cmpop_guard("NE", "NE", "E", "E")
genop_guard_int_gt = _cmpop_guard("G", "L", "LE", "GE")
genop_guard_int_ge = _cmpop_guard("GE", "LE", "L", "G")
- genop_guard_ptr_eq = genop_guard_int_eq
- genop_guard_ptr_ne = genop_guard_int_ne
+ genop_guard_ptr_eq = genop_guard_instance_ptr_eq = genop_guard_int_eq
+ genop_guard_ptr_ne = genop_guard_instance_ptr_ne = genop_guard_int_ne
genop_guard_uint_gt = _cmpop_guard("A", "B", "BE", "AE")
genop_guard_uint_lt = _cmpop_guard("B", "A", "AE", "BE")
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -651,8 +651,8 @@
consider_uint_lt = _consider_compop
consider_uint_le = _consider_compop
consider_uint_ge = _consider_compop
- consider_ptr_eq = _consider_compop
- consider_ptr_ne = _consider_compop
+ consider_ptr_eq = consider_instance_ptr_eq = _consider_compop
+ consider_ptr_ne = consider_instance_ptr_ne = _consider_compop
def _consider_float_op(self, op):
loc1 = self.xrm.loc(op.getarg(1))
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -443,6 +443,8 @@
rewrite_op_gc_identityhash = _do_builtin_call
rewrite_op_gc_id = _do_builtin_call
rewrite_op_uint_mod = _do_builtin_call
+ rewrite_op_cast_float_to_uint = _do_builtin_call
+ rewrite_op_cast_uint_to_float = _do_builtin_call
# ----------
# getfield/setfield/mallocs etc.
@@ -798,6 +800,9 @@
def _is_gc(self, v):
return getattr(getattr(v.concretetype, "TO", None), "_gckind", "?") == 'gc'
+ def _is_rclass_instance(self, v):
+ return lltype._castdepth(v.concretetype.TO, rclass.OBJECT) >= 0
+
def _rewrite_cmp_ptrs(self, op):
if self._is_gc(op.args[0]):
return op
@@ -815,11 +820,21 @@
return self._rewrite_equality(op, 'int_is_true')
def rewrite_op_ptr_eq(self, op):
- op1 = self._rewrite_equality(op, 'ptr_iszero')
+ prefix = ''
+ if self._is_rclass_instance(op.args[0]):
+ assert self._is_rclass_instance(op.args[1])
+ op = SpaceOperation('instance_ptr_eq', op.args, op.result)
+ prefix = 'instance_'
+ op1 = self._rewrite_equality(op, prefix + 'ptr_iszero')
return self._rewrite_cmp_ptrs(op1)
def rewrite_op_ptr_ne(self, op):
- op1 = self._rewrite_equality(op, 'ptr_nonzero')
+ prefix = ''
+ if self._is_rclass_instance(op.args[0]):
+ assert self._is_rclass_instance(op.args[1])
+ op = SpaceOperation('instance_ptr_ne', op.args, op.result)
+ prefix = 'instance_'
+ op1 = self._rewrite_equality(op, prefix + 'ptr_nonzero')
return self._rewrite_cmp_ptrs(op1)
rewrite_op_ptr_iszero = _rewrite_cmp_ptrs
@@ -848,26 +863,44 @@
elif not float_arg and float_res:
# some int -> some float
ops = []
- v1 = varoftype(lltype.Signed)
- oplist = self.rewrite_operation(
- SpaceOperation('force_cast', [v_arg], v1)
- )
- if oplist:
- ops.extend(oplist)
+ v2 = varoftype(lltype.Float)
+ sizesign = rffi.size_and_sign(v_arg.concretetype)
+ if sizesign <= rffi.size_and_sign(lltype.Signed):
+ # cast from a type that fits in an int: either the size is
+ # smaller, or it is equal and it is not unsigned
+ v1 = varoftype(lltype.Signed)
+ oplist = self.rewrite_operation(
+ SpaceOperation('force_cast', [v_arg], v1)
+ )
+ if oplist:
+ ops.extend(oplist)
+ else:
+ v1 = v_arg
+ op = self.rewrite_operation(
+ SpaceOperation('cast_int_to_float', [v1], v2)
+ )
+ ops.append(op)
else:
- v1 = v_arg
- v2 = varoftype(lltype.Float)
- op = self.rewrite_operation(
- SpaceOperation('cast_int_to_float', [v1], v2)
- )
- ops.append(op)
+ if sizesign == rffi.size_and_sign(lltype.Unsigned):
+ opname = 'cast_uint_to_float'
+ elif sizesign == rffi.size_and_sign(lltype.SignedLongLong):
+ opname = 'cast_longlong_to_float'
+ elif sizesign == rffi.size_and_sign(lltype.UnsignedLongLong):
+ opname = 'cast_ulonglong_to_float'
+ else:
+ raise AssertionError('cast_x_to_float: %r' % (sizesign,))
+ ops1 = self.rewrite_operation(
+ SpaceOperation(opname, [v_arg], v2)
+ )
+ if not isinstance(ops1, list): ops1 = [ops1]
+ ops.extend(ops1)
op2 = self.rewrite_operation(
SpaceOperation('force_cast', [v2], v_result)
)
if op2:
ops.append(op2)
else:
- op.result = v_result
+ ops[-1].result = v_result
return ops
elif float_arg and not float_res:
# some float -> some int
@@ -880,18 +913,36 @@
ops.append(op1)
else:
v1 = v_arg
- v2 = varoftype(lltype.Signed)
- op = self.rewrite_operation(
- SpaceOperation('cast_float_to_int', [v1], v2)
- )
- ops.append(op)
- oplist = self.rewrite_operation(
- SpaceOperation('force_cast', [v2], v_result)
- )
- if oplist:
- ops.extend(oplist)
+ sizesign = rffi.size_and_sign(v_result.concretetype)
+ if sizesign <= rffi.size_and_sign(lltype.Signed):
+ # cast to a type that fits in an int: either the size is
+ # smaller, or it is equal and it is not unsigned
+ v2 = varoftype(lltype.Signed)
+ op = self.rewrite_operation(
+ SpaceOperation('cast_float_to_int', [v1], v2)
+ )
+ ops.append(op)
+ oplist = self.rewrite_operation(
+ SpaceOperation('force_cast', [v2], v_result)
+ )
+ if oplist:
+ ops.extend(oplist)
+ else:
+ op.result = v_result
else:
- op.result = v_result
+ if sizesign == rffi.size_and_sign(lltype.Unsigned):
+ opname = 'cast_float_to_uint'
+ elif sizesign == rffi.size_and_sign(lltype.SignedLongLong):
+ opname = 'cast_float_to_longlong'
+ elif sizesign == rffi.size_and_sign(lltype.UnsignedLongLong):
+ opname = 'cast_float_to_ulonglong'
+ else:
+ raise AssertionError('cast_float_to_x: %r' % (sizesign,))
+ ops1 = self.rewrite_operation(
+ SpaceOperation(opname, [v1], v_result)
+ )
+ if not isinstance(ops1, list): ops1 = [ops1]
+ ops.extend(ops1)
return ops
else:
assert False
@@ -1097,8 +1148,6 @@
# The new operation is optionally further processed by rewrite_operation().
for _old, _new in [('bool_not', 'int_is_zero'),
('cast_bool_to_float', 'cast_int_to_float'),
- ('cast_uint_to_float', 'cast_int_to_float'),
- ('cast_float_to_uint', 'cast_float_to_int'),
('int_add_nonneg_ovf', 'int_add_ovf'),
('keepalive', '-live-'),
diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -229,6 +229,17 @@
else:
return x
+def _ll_1_cast_uint_to_float(x):
+ # XXX on 32-bit platforms, this should be done using cast_longlong_to_float
+ # (which is a residual call right now in the x86 backend)
+ return llop.cast_uint_to_float(lltype.Float, x)
+
+def _ll_1_cast_float_to_uint(x):
+ # XXX on 32-bit platforms, this should be done using cast_float_to_longlong
+ # (which is a residual call right now in the x86 backend)
+ return llop.cast_float_to_uint(lltype.Unsigned, x)
+
+
# math support
# ------------
diff --git a/pypy/jit/codewriter/test/test_flatten.py b/pypy/jit/codewriter/test/test_flatten.py
--- a/pypy/jit/codewriter/test/test_flatten.py
+++ b/pypy/jit/codewriter/test/test_flatten.py
@@ -8,7 +8,7 @@
from pypy.rpython.lltypesystem import lltype, rclass, rstr
from pypy.objspace.flow.model import SpaceOperation, Variable, Constant
from pypy.translator.unsimplify import varoftype
-from pypy.rlib.rarithmetic import ovfcheck, r_uint
+from pypy.rlib.rarithmetic import ovfcheck, r_uint, r_longlong, r_ulonglong
from pypy.rlib.jit import dont_look_inside, _we_are_jitted, JitDriver
from pypy.rlib.objectmodel import keepalive_until_here
from pypy.rlib import jit
@@ -70,7 +70,8 @@
return 'residual'
def getcalldescr(self, op, oopspecindex=None, extraeffect=None):
try:
- if 'cannot_raise' in op.args[0].value._obj.graph.name:
+ name = op.args[0].value._obj._name
+ if 'cannot_raise' in name or name.startswith('cast_'):
return self._descr_cannot_raise
except AttributeError:
pass
@@ -900,6 +901,67 @@
int_return %i4
""", transform=True)
+ def f(dbl):
+ return rffi.cast(rffi.UCHAR, dbl)
+ self.encoding_test(f, [12.456], """
+ cast_float_to_int %f0 -> %i0
+ int_and %i0, $255 -> %i1
+ int_return %i1
+ """, transform=True)
+
+ def f(dbl):
+ return rffi.cast(lltype.Unsigned, dbl)
+ self.encoding_test(f, [12.456], """
+ residual_call_irf_i $<* fn cast_float_to_uint>, <Descr>, I[], R[], F[%f0] -> %i0
+ int_return %i0
+ """, transform=True)
+
+ def f(i):
+ return rffi.cast(lltype.Float, chr(i)) # "char -> float"
+ self.encoding_test(f, [12], """
+ cast_int_to_float %i0 -> %f0
+ float_return %f0
+ """, transform=True)
+
+ def f(i):
+ return rffi.cast(lltype.Float, r_uint(i)) # "uint -> float"
+ self.encoding_test(f, [12], """
+ residual_call_irf_f $<* fn cast_uint_to_float>, <Descr>, I[%i0], R[], F[] -> %f0
+ float_return %f0
+ """, transform=True)
+
+ if not longlong.is_64_bit:
+ def f(dbl):
+ return rffi.cast(lltype.SignedLongLong, dbl)
+ self.encoding_test(f, [12.3], """
+ residual_call_irf_f $<* fn llong_from_float>, <Descr>, I[], R[], F[%f0] -> %f1
+ float_return %f1
+ """, transform=True)
+
+ def f(dbl):
+ return rffi.cast(lltype.UnsignedLongLong, dbl)
+ self.encoding_test(f, [12.3], """
+ residual_call_irf_f $<* fn ullong_from_float>, <Descr>, I[], R[], F[%f0] -> %f1
+ float_return %f1
+ """, transform=True)
+
+ def f(x):
+ ll = r_longlong(x)
+ return rffi.cast(lltype.Float, ll)
+ self.encoding_test(f, [12], """
+ residual_call_irf_f $<* fn llong_from_int>, <Descr>, I[%i0], R[], F[] -> %f0
+ residual_call_irf_f $<* fn llong_to_float>, <Descr>, I[], R[], F[%f0] -> %f1
+ float_return %f1
+ """, transform=True)
+
+ def f(x):
+ ll = r_ulonglong(x)
+ return rffi.cast(lltype.Float, ll)
+ self.encoding_test(f, [12], """
+ residual_call_irf_f $<* fn ullong_from_int>, <Descr>, I[%i0], R[], F[] -> %f0
+ residual_call_irf_f $<* fn ullong_u_to_float>, <Descr>, I[], R[], F[%f0] -> %f1
+ float_return %f1
+ """, transform=True)
def test_direct_ptradd(self):
from pypy.rpython.lltypesystem import rffi
diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -576,10 +576,10 @@
assert op1.args == [v2]
def test_ptr_eq():
- v1 = varoftype(rclass.OBJECTPTR)
- v2 = varoftype(rclass.OBJECTPTR)
+ v1 = varoftype(lltype.Ptr(rstr.STR))
+ v2 = varoftype(lltype.Ptr(rstr.STR))
v3 = varoftype(lltype.Bool)
- c0 = const(lltype.nullptr(rclass.OBJECT))
+ c0 = const(lltype.nullptr(rstr.STR))
#
for opname, reducedname in [('ptr_eq', 'ptr_iszero'),
('ptr_ne', 'ptr_nonzero')]:
@@ -598,6 +598,31 @@
assert op1.opname == reducedname
assert op1.args == [v2]
+def test_instance_ptr_eq():
+ v1 = varoftype(rclass.OBJECTPTR)
+ v2 = varoftype(rclass.OBJECTPTR)
+ v3 = varoftype(lltype.Bool)
+ c0 = const(lltype.nullptr(rclass.OBJECT))
+
+ for opname, newopname, reducedname in [
+ ('ptr_eq', 'instance_ptr_eq', 'instance_ptr_iszero'),
+ ('ptr_ne', 'instance_ptr_ne', 'instance_ptr_nonzero')
+ ]:
+ op = SpaceOperation(opname, [v1, v2], v3)
+ op1 = Transformer().rewrite_operation(op)
+ assert op1.opname == newopname
+ assert op1.args == [v1, v2]
+
+ op = SpaceOperation(opname, [v1, c0], v3)
+ op1 = Transformer().rewrite_operation(op)
+ assert op1.opname == reducedname
+ assert op1.args == [v1]
+
+ op = SpaceOperation(opname, [c0, v1], v3)
+ op1 = Transformer().rewrite_operation(op)
+ assert op1.opname == reducedname
+ assert op1.args == [v1]
+
def test_nongc_ptr_eq():
v1 = varoftype(rclass.NONGCOBJECTPTR)
v2 = varoftype(rclass.NONGCOBJECTPTR)
diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py
--- a/pypy/jit/metainterp/blackhole.py
+++ b/pypy/jit/metainterp/blackhole.py
@@ -499,6 +499,12 @@
@arguments("r", returns="i")
def bhimpl_ptr_nonzero(a):
return bool(a)
+ @arguments("r", "r", returns="i")
+ def bhimpl_instance_ptr_eq(a, b):
+ return a == b
+ @arguments("r", "r", returns="i")
+ def bhimpl_instance_ptr_ne(a, b):
+ return a != b
@arguments("r", returns="r")
def bhimpl_cast_opaque_ptr(a):
return a
@@ -630,6 +636,9 @@
a = longlong.getrealfloat(a)
# note: we need to call int() twice to care for the fact that
# int(-2147483648.0) returns a long :-(
+ # we could also call intmask() instead of the outermost int(), but
+ # it's probably better to explicitly crash (by getting a long) if a
+ # non-translated version tries to cast a too large float to an int.
return int(int(a))
@arguments("i", returns="f")
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -337,7 +337,7 @@
def optimize_INT_IS_ZERO(self, op):
self._optimize_nullness(op, op.getarg(0), False)
- def _optimize_oois_ooisnot(self, op, expect_isnot):
+ def _optimize_oois_ooisnot(self, op, expect_isnot, instance):
value0 = self.getvalue(op.getarg(0))
value1 = self.getvalue(op.getarg(1))
if value0.is_virtual():
@@ -355,21 +355,28 @@
elif value0 is value1:
self.make_constant_int(op.result, not expect_isnot)
else:
- cls0 = value0.get_constant_class(self.optimizer.cpu)
- if cls0 is not None:
- cls1 = value1.get_constant_class(self.optimizer.cpu)
- if cls1 is not None and not cls0.same_constant(cls1):
- # cannot be the same object, as we know that their
- # class is different
- self.make_constant_int(op.result, expect_isnot)
- return
+ if instance:
+ cls0 = value0.get_constant_class(self.optimizer.cpu)
+ if cls0 is not None:
+ cls1 = value1.get_constant_class(self.optimizer.cpu)
+ if cls1 is not None and not cls0.same_constant(cls1):
+ # cannot be the same object, as we know that their
+ # class is different
+ self.make_constant_int(op.result, expect_isnot)
+ return
self.emit_operation(op)
+ def optimize_PTR_EQ(self, op):
+ self._optimize_oois_ooisnot(op, False, False)
+
def optimize_PTR_NE(self, op):
- self._optimize_oois_ooisnot(op, True)
+ self._optimize_oois_ooisnot(op, True, False)
- def optimize_PTR_EQ(self, op):
- self._optimize_oois_ooisnot(op, False)
+ def optimize_INSTANCE_PTR_EQ(self, op):
+ self._optimize_oois_ooisnot(op, False, True)
+
+ def optimize_INSTANCE_PTR_NE(self, op):
+ self._optimize_oois_ooisnot(op, True, True)
## def optimize_INSTANCEOF(self, op):
## value = self.getvalue(op.args[0])
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
@@ -508,13 +508,13 @@
ops = """
[p0]
guard_class(p0, ConstClass(node_vtable)) []
- i0 = ptr_ne(p0, NULL)
+ i0 = instance_ptr_ne(p0, NULL)
guard_true(i0) []
- i1 = ptr_eq(p0, NULL)
+ i1 = instance_ptr_eq(p0, NULL)
guard_false(i1) []
- i2 = ptr_ne(NULL, p0)
+ i2 = instance_ptr_ne(NULL, p0)
guard_true(i0) []
- i3 = ptr_eq(NULL, p0)
+ i3 = instance_ptr_eq(NULL, p0)
guard_false(i1) []
jump(p0)
"""
@@ -2026,7 +2026,7 @@
ops = """
[p1]
guard_class(p1, ConstClass(node_vtable2)) []
- i = ptr_ne(ConstPtr(myptr), p1)
+ i = instance_ptr_ne(ConstPtr(myptr), p1)
guard_true(i) []
jump(p1)
"""
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
@@ -2683,7 +2683,7 @@
ops = """
[p1]
guard_class(p1, ConstClass(node_vtable2)) []
- i = ptr_ne(ConstPtr(myptr), p1)
+ i = instance_ptr_ne(ConstPtr(myptr), p1)
guard_true(i) []
jump(p1)
"""
@@ -3331,7 +3331,7 @@
jump(p1, i1, i2, i6)
'''
self.optimize_loop(ops, expected, preamble)
-
+
# ----------
@@ -7280,7 +7280,7 @@
ops = """
[p1, p2]
setarrayitem_gc(p1, 2, 10, descr=arraydescr)
- setarrayitem_gc(p2, 3, 13, descr=arraydescr)
+ setarrayitem_gc(p2, 3, 13, descr=arraydescr)
call(0, p1, p2, 0, 0, 10, descr=arraycopydescr)
jump(p1, p2)
"""
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -165,7 +165,7 @@
if not we_are_translated():
for b in registers[count:]:
assert not oldbox.same_box(b)
-
+
def make_result_of_lastop(self, resultbox):
got_type = resultbox.type
@@ -199,7 +199,7 @@
'float_add', 'float_sub', 'float_mul', 'float_truediv',
'float_lt', 'float_le', 'float_eq',
'float_ne', 'float_gt', 'float_ge',
- 'ptr_eq', 'ptr_ne',
+ 'ptr_eq', 'ptr_ne', 'instance_ptr_eq', 'instance_ptr_ne',
]:
exec py.code.Source('''
@arguments("box", "box")
@@ -604,7 +604,7 @@
opimpl_setinteriorfield_gc_i = _opimpl_setinteriorfield_gc_any
opimpl_setinteriorfield_gc_f = _opimpl_setinteriorfield_gc_any
opimpl_setinteriorfield_gc_r = _opimpl_setinteriorfield_gc_any
-
+
@arguments("box", "descr")
def _opimpl_getfield_raw_any(self, box, fielddescr):
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -404,8 +404,8 @@
'FLOAT_TRUEDIV/2',
'FLOAT_NEG/1',
'FLOAT_ABS/1',
- 'CAST_FLOAT_TO_INT/1',
- 'CAST_INT_TO_FLOAT/1',
+ 'CAST_FLOAT_TO_INT/1', # don't use for unsigned ints; we would
+ 'CAST_INT_TO_FLOAT/1', # need some messy code in the backend
'CAST_FLOAT_TO_SINGLEFLOAT/1',
'CAST_SINGLEFLOAT_TO_FLOAT/1',
#
@@ -437,6 +437,8 @@
#
'PTR_EQ/2b',
'PTR_NE/2b',
+ 'INSTANCE_PTR_EQ/2b',
+ 'INSTANCE_PTR_NE/2b',
'CAST_OPAQUE_PTR/1b',
#
'ARRAYLEN_GC/1d',
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
@@ -3436,7 +3436,7 @@
res = self.meta_interp(f, [16])
assert res == f(16)
- def test_ptr_eq_str_constants(self):
+ def test_ptr_eq(self):
myjitdriver = JitDriver(greens = [], reds = ["n", "x"])
class A(object):
def __init__(self, v):
@@ -3452,6 +3452,42 @@
res = self.meta_interp(f, [10, 1])
assert res == 0
+ def test_instance_ptr_eq(self):
+ myjitdriver = JitDriver(greens = [], reds = ["n", "i", "a1", "a2"])
+ class A(object):
+ pass
+ def f(n):
+ a1 = A()
+ a2 = A()
+ i = 0
+ while n > 0:
+ myjitdriver.jit_merge_point(n=n, i=i, a1=a1, a2=a2)
+ if n % 2:
+ a = a2
+ else:
+ a = a1
+ i += a is a1
+ n -= 1
+ return i
+ res = self.meta_interp(f, [10])
+ assert res == f(10)
+ def f(n):
+ a1 = A()
+ a2 = A()
+ i = 0
+ while n > 0:
+ myjitdriver.jit_merge_point(n=n, i=i, a1=a1, a2=a2)
+ if n % 2:
+ a = a2
+ else:
+ a = a1
+ if a is a2:
+ i += 1
+ n -= 1
+ return i
+ res = self.meta_interp(f, [10])
+ assert res == f(10)
+
def test_virtual_array_of_structs(self):
myjitdriver = JitDriver(greens = [], reds=["n", "d"])
def f(n):
diff --git a/pypy/jit/metainterp/test/test_float.py b/pypy/jit/metainterp/test/test_float.py
--- a/pypy/jit/metainterp/test/test_float.py
+++ b/pypy/jit/metainterp/test/test_float.py
@@ -1,5 +1,6 @@
-import math
+import math, sys
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
+from pypy.rlib.rarithmetic import intmask, r_uint
class FloatTests:
@@ -45,6 +46,34 @@
res = self.interp_operations(f, [-2.0])
assert res == -8.5
+ def test_cast_float_to_int(self):
+ def g(f):
+ return int(f)
+ res = self.interp_operations(g, [-12345.9])
+ assert res == -12345
+
+ def test_cast_float_to_uint(self):
+ def g(f):
+ return intmask(r_uint(f))
+ res = self.interp_operations(g, [sys.maxint*2.0])
+ assert res == intmask(long(sys.maxint*2.0))
+ res = self.interp_operations(g, [-12345.9])
+ assert res == -12345
+
+ def test_cast_int_to_float(self):
+ def g(i):
+ return float(i)
+ res = self.interp_operations(g, [-12345])
+ assert type(res) is float and res == -12345.0
+
+ def test_cast_uint_to_float(self):
+ def g(i):
+ return float(r_uint(i))
+ res = self.interp_operations(g, [intmask(sys.maxint*2)])
+ assert type(res) is float and res == float(sys.maxint*2)
+ res = self.interp_operations(g, [-12345])
+ assert type(res) is float and res == float(long(r_uint(-12345)))
+
class TestOOtype(FloatTests, OOJitMixin):
pass
diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py
--- a/pypy/module/_socket/interp_socket.py
+++ b/pypy/module/_socket/interp_socket.py
@@ -19,7 +19,7 @@
class W_RSocket(Wrappable, RSocket):
def __del__(self):
self.clear_all_weakrefs()
- self.close()
+ RSocket.__del__(self)
def accept_w(self, space):
"""accept() -> (socket object, address info)
diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -211,7 +211,9 @@
return result
def __del__(self):
- self.clear_all_weakrefs()
+ # note that we don't call clear_all_weakrefs here because
+ # an array with freed buffer is ok to see - it's just empty with 0
+ # length
self.setlen(0)
def setlen(self, size):
diff --git a/pypy/module/array/test/test_array.py b/pypy/module/array/test/test_array.py
--- a/pypy/module/array/test/test_array.py
+++ b/pypy/module/array/test/test_array.py
@@ -824,6 +824,22 @@
r = weakref.ref(a)
assert r() is a
+ def test_subclass_del(self):
+ import array, gc, weakref
+ l = []
+
+ class A(array.array):
+ pass
+
+ a = A('d')
+ a.append(3.0)
+ r = weakref.ref(a, lambda a: l.append(a()))
+ del a
+ gc.collect()
+ assert l
+ assert l[0] is None or len(l[0]) == 0
+
+
class TestCPythonsOwnArray(BaseArrayTests):
def setup_class(cls):
@@ -844,11 +860,7 @@
cls.w_tempfile = cls.space.wrap(
str(py.test.ensuretemp('array').join('tmpfile')))
cls.w_maxint = cls.space.wrap(sys.maxint)
-
-
-
-
-
+
def test_buffer_info(self):
a = self.array('c', 'Hi!')
bi = a.buffer_info()
diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py
--- a/pypy/module/pypyjit/policy.py
+++ b/pypy/module/pypyjit/policy.py
@@ -16,7 +16,8 @@
if modname in ['pypyjit', 'signal', 'micronumpy', 'math', 'exceptions',
'imp', 'sys', 'array', '_ffi', 'itertools', 'operator',
'posix', '_socket', '_sre', '_lsprof', '_weakref',
- '__pypy__', 'cStringIO', '_collections', 'struct']:
+ '__pypy__', 'cStringIO', '_collections', 'struct',
+ 'mmap']:
return True
return False
diff --git a/pypy/module/rctime/interp_time.py b/pypy/module/rctime/interp_time.py
--- a/pypy/module/rctime/interp_time.py
+++ b/pypy/module/rctime/interp_time.py
@@ -245,6 +245,9 @@
if sys.platform != 'win32':
@unwrap_spec(secs=float)
def sleep(space, secs):
+ if secs < 0:
+ raise OperationError(space.w_IOError,
+ space.wrap("Invalid argument: negative time in sleep"))
pytime.sleep(secs)
else:
from pypy.rlib import rwin32
@@ -265,6 +268,9 @@
OSError(EINTR, "sleep() interrupted"))
@unwrap_spec(secs=float)
def sleep(space, secs):
+ if secs < 0:
+ raise OperationError(space.w_IOError,
+ space.wrap("Invalid argument: negative time in sleep"))
# as decreed by Guido, only the main thread can be
# interrupted.
main_thread = space.fromcache(State).main_thread
diff --git a/pypy/module/rctime/test/test_rctime.py b/pypy/module/rctime/test/test_rctime.py
--- a/pypy/module/rctime/test/test_rctime.py
+++ b/pypy/module/rctime/test/test_rctime.py
@@ -20,8 +20,9 @@
import sys
import os
raises(TypeError, rctime.sleep, "foo")
- rctime.sleep(1.2345)
-
+ rctime.sleep(0.12345)
+ raises(IOError, rctime.sleep, -1.0)
+
def test_clock(self):
import time as rctime
rctime.clock()
diff --git a/pypy/pytest.ini b/pypy/pytest.ini
--- a/pypy/pytest.ini
+++ b/pypy/pytest.ini
@@ -1,2 +1,2 @@
[pytest]
-addopts = --assertmode=old
\ No newline at end of file
+addopts = --assertmode=old -rf
diff --git a/pypy/rlib/rgc.py b/pypy/rlib/rgc.py
--- a/pypy/rlib/rgc.py
+++ b/pypy/rlib/rgc.py
@@ -214,6 +214,10 @@
func._gc_no_collect_ = True
return func
+def is_light_finalizer(func):
+ func._is_light_finalizer_ = True
+ return func
+
# ____________________________________________________________
def get_rpy_roots():
diff --git a/pypy/rlib/rmmap.py b/pypy/rlib/rmmap.py
--- a/pypy/rlib/rmmap.py
+++ b/pypy/rlib/rmmap.py
@@ -292,6 +292,9 @@
elif _POSIX:
self.closed = True
if self.fd != -1:
+ # XXX this is buggy - raising in an RPython del is not a good
+ # idea, we should swallow the exception or ignore the
+ # underlaying close error code
os.close(self.fd)
self.fd = -1
if self.size > 0:
diff --git a/pypy/rlib/rsocket.py b/pypy/rlib/rsocket.py
--- a/pypy/rlib/rsocket.py
+++ b/pypy/rlib/rsocket.py
@@ -56,6 +56,7 @@
_FAMILIES = {}
+
class Address(object):
"""The base class for RPython-level objects representing addresses.
Fields: addr - a _c.sockaddr_ptr (memory owned by the Address instance)
@@ -77,9 +78,8 @@
self.addrlen = addrlen
def __del__(self):
- addr = self.addr_p
- if addr:
- lltype.free(addr, flavor='raw')
+ if self.addr_p:
+ lltype.free(self.addr_p, flavor='raw')
def setdata(self, addr, addrlen):
# initialize self.addr and self.addrlen. 'addr' can be a different
@@ -613,7 +613,10 @@
self.timeout = defaults.timeout
def __del__(self):
- self.close()
+ fd = self.fd
+ if fd != _c.INVALID_SOCKET:
+ self.fd = _c.INVALID_SOCKET
+ _c.socketclose(fd)
if hasattr(_c, 'fcntl'):
def _setblocking(self, block):
diff --git a/pypy/rpython/llinterp.py b/pypy/rpython/llinterp.py
--- a/pypy/rpython/llinterp.py
+++ b/pypy/rpython/llinterp.py
@@ -1095,13 +1095,6 @@
assert y >= 0
return self.op_int_add_ovf(x, y)
- def op_cast_float_to_int(self, f):
- assert type(f) is float
- try:
- return ovfcheck(int(f))
- except OverflowError:
- self.make_llexception()
-
def op_int_is_true(self, x):
# special case
if type(x) is CDefinedIntSymbolic:
diff --git a/pypy/rpython/lltypesystem/lloperation.py b/pypy/rpython/lltypesystem/lloperation.py
--- a/pypy/rpython/lltypesystem/lloperation.py
+++ b/pypy/rpython/lltypesystem/lloperation.py
@@ -343,8 +343,8 @@
'cast_uint_to_float': LLOp(canfold=True),
'cast_longlong_to_float' :LLOp(canfold=True),
'cast_ulonglong_to_float':LLOp(canfold=True),
- 'cast_float_to_int': LLOp(canraise=(OverflowError,), tryfold=True),
- 'cast_float_to_uint': LLOp(canfold=True), # XXX need OverflowError?
+ 'cast_float_to_int': LLOp(canfold=True),
+ 'cast_float_to_uint': LLOp(canfold=True),
'cast_float_to_longlong' :LLOp(canfold=True),
'cast_float_to_ulonglong':LLOp(canfold=True),
'truncate_longlong_to_int':LLOp(canfold=True),
diff --git a/pypy/rpython/lltypesystem/opimpl.py b/pypy/rpython/lltypesystem/opimpl.py
--- a/pypy/rpython/lltypesystem/opimpl.py
+++ b/pypy/rpython/lltypesystem/opimpl.py
@@ -355,6 +355,10 @@
assert type(b) is bool
return float(b)
+def op_cast_float_to_int(f):
+ assert type(f) is float
+ return intmask(int(f))
+
def op_cast_float_to_uint(f):
assert type(f) is float
return r_uint(long(f))
diff --git a/pypy/rpython/memory/gc/base.py b/pypy/rpython/memory/gc/base.py
--- a/pypy/rpython/memory/gc/base.py
+++ b/pypy/rpython/memory/gc/base.py
@@ -1,4 +1,5 @@
-from pypy.rpython.lltypesystem import lltype, llmemory, llarena
+from pypy.rpython.lltypesystem import lltype, llmemory, llarena, rffi
+from pypy.rpython.lltypesystem.lloperation import llop
from pypy.rlib.debug import ll_assert
from pypy.rpython.memory.gcheader import GCHeaderBuilder
from pypy.rpython.memory.support import DEFAULT_CHUNK_SIZE
@@ -62,6 +63,7 @@
def set_query_functions(self, is_varsize, has_gcptr_in_varsize,
is_gcarrayofgcptr,
getfinalizer,
+ getlightfinalizer,
offsets_to_gc_pointers,
fixed_size, varsize_item_sizes,
varsize_offset_to_variable_part,
@@ -74,6 +76,7 @@
get_custom_trace,
fast_path_tracing):
self.getfinalizer = getfinalizer
+ self.getlightfinalizer = getlightfinalizer
self.is_varsize = is_varsize
self.has_gcptr_in_varsize = has_gcptr_in_varsize
self.is_gcarrayofgcptr = is_gcarrayofgcptr
@@ -139,6 +142,7 @@
size = self.fixed_size(typeid)
needs_finalizer = bool(self.getfinalizer(typeid))
+ finalizer_is_light = bool(self.getlightfinalizer(typeid))
contains_weakptr = self.weakpointer_offset(typeid) >= 0
assert not (needs_finalizer and contains_weakptr)
if self.is_varsize(typeid):
@@ -158,6 +162,7 @@
else:
malloc_fixedsize = self.malloc_fixedsize
ref = malloc_fixedsize(typeid, size, needs_finalizer,
+ finalizer_is_light,
contains_weakptr)
# lots of cast and reverse-cast around...
return llmemory.cast_ptr_to_adr(ref)
diff --git a/pypy/rpython/memory/gc/generation.py b/pypy/rpython/memory/gc/generation.py
--- a/pypy/rpython/memory/gc/generation.py
+++ b/pypy/rpython/memory/gc/generation.py
@@ -167,7 +167,9 @@
return self.nursery <= addr < self.nursery_top
def malloc_fixedsize_clear(self, typeid, size,
- has_finalizer=False, contains_weakptr=False):
+ has_finalizer=False,
+ is_finalizer_light=False,
+ contains_weakptr=False):
if (has_finalizer or
(raw_malloc_usage(size) > self.lb_young_fixedsize and
raw_malloc_usage(size) > self.largest_young_fixedsize)):
@@ -179,6 +181,7 @@
# "non-simple" case or object too big: don't use the nursery
return SemiSpaceGC.malloc_fixedsize_clear(self, typeid, size,
has_finalizer,
+ is_finalizer_light,
contains_weakptr)
size_gc_header = self.gcheaderbuilder.size_gc_header
totalsize = size_gc_header + size
diff --git a/pypy/rpython/memory/gc/marksweep.py b/pypy/rpython/memory/gc/marksweep.py
--- a/pypy/rpython/memory/gc/marksweep.py
+++ b/pypy/rpython/memory/gc/marksweep.py
@@ -93,7 +93,8 @@
pass
def malloc_fixedsize(self, typeid16, size,
- has_finalizer=False, contains_weakptr=False):
+ has_finalizer=False, is_finalizer_light=False,
+ contains_weakptr=False):
self.maybe_collect()
size_gc_header = self.gcheaderbuilder.size_gc_header
try:
@@ -128,7 +129,9 @@
malloc_fixedsize._dont_inline_ = True
def malloc_fixedsize_clear(self, typeid16, size,
- has_finalizer=False, contains_weakptr=False):
+ has_finalizer=False,
+ is_finalizer_light=False,
+ contains_weakptr=False):
self.maybe_collect()
size_gc_header = self.gcheaderbuilder.size_gc_header
try:
diff --git a/pypy/rpython/memory/gc/minimark.py b/pypy/rpython/memory/gc/minimark.py
--- a/pypy/rpython/memory/gc/minimark.py
+++ b/pypy/rpython/memory/gc/minimark.py
@@ -290,6 +290,8 @@
#
# A list of all objects with finalizers (these are never young).
self.objects_with_finalizers = self.AddressDeque()
+ self.young_objects_with_light_finalizers = self.AddressStack()
+ self.old_objects_with_light_finalizers = self.AddressStack()
#
# Two lists of the objects with weakrefs. No weakref can be an
# old object weakly pointing to a young object: indeed, weakrefs
@@ -457,14 +459,16 @@
def malloc_fixedsize_clear(self, typeid, size,
- needs_finalizer=False, contains_weakptr=False):
+ needs_finalizer=False,
+ is_finalizer_light=False,
+ contains_weakptr=False):
size_gc_header = self.gcheaderbuilder.size_gc_header
totalsize = size_gc_header + size
rawtotalsize = raw_malloc_usage(totalsize)
#
# If the object needs a finalizer, ask for a rawmalloc.
# The following check should be constant-folded.
- if needs_finalizer:
+ if needs_finalizer and not is_finalizer_light:
ll_assert(not contains_weakptr,
"'needs_finalizer' and 'contains_weakptr' both specified")
obj = self.external_malloc(typeid, 0, can_make_young=False)
@@ -494,13 +498,14 @@
#
# Build the object.
llarena.arena_reserve(result, totalsize)
+ obj = result + size_gc_header
+ if is_finalizer_light:
+ self.young_objects_with_light_finalizers.append(obj)
self.init_gc_object(result, typeid, flags=0)
#
# If it is a weakref, record it (check constant-folded).
if contains_weakptr:
- self.young_objects_with_weakrefs.append(result+size_gc_header)
- #
- obj = result + size_gc_header
+ self.young_objects_with_weakrefs.append(obj)
#
return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
@@ -1264,6 +1269,8 @@
# weakrefs' targets.
if self.young_objects_with_weakrefs.non_empty():
self.invalidate_young_weakrefs()
+ if self.young_objects_with_light_finalizers.non_empty():
+ self.deal_with_young_objects_with_finalizers()
#
# Clear this mapping.
if self.nursery_objects_shadows.length() > 0:
@@ -1584,6 +1591,9 @@
# Weakref support: clear the weak pointers to dying objects
if self.old_objects_with_weakrefs.non_empty():
self.invalidate_old_weakrefs()
+ if self.old_objects_with_light_finalizers.non_empty():
+ self.deal_with_old_objects_with_finalizers()
+
#
# Walk all rawmalloced objects and free the ones that don't
# have the GCFLAG_VISITED flag.
@@ -1649,8 +1659,7 @@
if self.header(obj).tid & GCFLAG_VISITED:
self.header(obj).tid &= ~GCFLAG_VISITED
return False # survives
- else:
- return True # dies
+ return True # dies
def _reset_gcflag_visited(self, obj, ignored):
self.header(obj).tid &= ~GCFLAG_VISITED
@@ -1829,6 +1838,39 @@
# ----------
# Finalizers
+ def deal_with_young_objects_with_finalizers(self):
+ """ This is a much simpler version of dealing with finalizers
+ and an optimization - we can reasonably assume that those finalizers
+ don't do anything fancy and *just* call them. Among other things
+ they won't resurrect objects
+ """
+ while self.young_objects_with_light_finalizers.non_empty():
+ obj = self.young_objects_with_light_finalizers.pop()
+ if not self.is_forwarded(obj):
+ finalizer = self.getlightfinalizer(self.get_type_id(obj))
+ ll_assert(bool(finalizer), "no light finalizer found")
+ finalizer(obj, llmemory.NULL)
+
+ def deal_with_old_objects_with_finalizers(self):
+ """ This is a much simpler version of dealing with finalizers
+ and an optimization - we can reasonably assume that those finalizers
+ don't do anything fancy and *just* call them. Among other things
+ they won't resurrect objects
+ """
+ new_objects = self.AddressStack()
+ while self.old_objects_with_light_finalizers.non_empty():
+ obj = self.old_objects_with_light_finalizers.pop()
+ if self.header(obj).tid & GCFLAG_VISITED:
+ # surviving
+ new_objects.append(obj)
+ else:
+ # dying
+ finalizer = self.getlightfinalizer(self.get_type_id(obj))
+ ll_assert(bool(finalizer), "no light finalizer found")
+ finalizer(obj, llmemory.NULL)
+ self.old_objects_with_light_finalizers.delete()
+ self.old_objects_with_light_finalizers = new_objects
+
def deal_with_objects_with_finalizers(self):
# Walk over list of objects with finalizers.
# If it is not surviving, add it to the list of to-be-called
@@ -1959,7 +2001,6 @@
#
self.old_objects_with_weakrefs.append(obj)
-
def invalidate_old_weakrefs(self):
"""Called during a major collection."""
# walk over list of objects that contain weakrefs
diff --git a/pypy/rpython/memory/gc/semispace.py b/pypy/rpython/memory/gc/semispace.py
--- a/pypy/rpython/memory/gc/semispace.py
+++ b/pypy/rpython/memory/gc/semispace.py
@@ -82,6 +82,7 @@
self.free = self.tospace
MovingGCBase.setup(self)
self.objects_with_finalizers = self.AddressDeque()
+ self.objects_with_light_finalizers = self.AddressStack()
self.objects_with_weakrefs = self.AddressStack()
def _teardown(self):
@@ -93,7 +94,9 @@
# because the spaces are filled with zeroes in advance.
def malloc_fixedsize_clear(self, typeid16, size,
- has_finalizer=False, contains_weakptr=False):
+ has_finalizer=False,
+ is_finalizer_light=False,
+ contains_weakptr=False):
size_gc_header = self.gcheaderbuilder.size_gc_header
totalsize = size_gc_header + size
result = self.free
@@ -102,7 +105,9 @@
llarena.arena_reserve(result, totalsize)
self.init_gc_object(result, typeid16)
self.free = result + totalsize
- if has_finalizer:
+ if is_finalizer_light:
+ self.objects_with_light_finalizers.append(result + size_gc_header)
+ elif has_finalizer:
self.objects_with_finalizers.append(result + size_gc_header)
if contains_weakptr:
self.objects_with_weakrefs.append(result + size_gc_header)
@@ -263,6 +268,8 @@
if self.run_finalizers.non_empty():
self.update_run_finalizers()
scan = self.scan_copied(scan)
+ if self.objects_with_light_finalizers.non_empty():
+ self.deal_with_objects_with_light_finalizers()
if self.objects_with_finalizers.non_empty():
scan = self.deal_with_objects_with_finalizers(scan)
if self.objects_with_weakrefs.non_empty():
@@ -471,6 +478,23 @@
# immortal objects always have GCFLAG_FORWARDED set;
# see get_forwarding_address().
+ def deal_with_objects_with_light_finalizers(self):
+ """ This is a much simpler version of dealing with finalizers
+ and an optimization - we can reasonably assume that those finalizers
+ don't do anything fancy and *just* call them. Among other things
+ they won't resurrect objects
+ """
+ new_objects = self.AddressStack()
+ while self.objects_with_light_finalizers.non_empty():
+ obj = self.objects_with_light_finalizers.pop()
+ if self.surviving(obj):
+ new_objects.append(self.get_forwarding_address(obj))
+ else:
+ finalizer = self.getfinalizer(self.get_type_id(obj))
+ finalizer(obj, llmemory.NULL)
+ self.objects_with_light_finalizers.delete()
+ self.objects_with_light_finalizers = new_objects
+
def deal_with_objects_with_finalizers(self, scan):
# walk over list of objects with finalizers
# if it is not copied, add it to the list of to-be-called finalizers
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -12,6 +12,7 @@
from pypy.rlib.objectmodel import we_are_translated
from pypy.translator.backendopt import graphanalyze
from pypy.translator.backendopt.support import var_needsgc
+from pypy.translator.backendopt.finalizer import FinalizerAnalyzer
from pypy.annotation import model as annmodel
from pypy.rpython import annlowlevel
from pypy.rpython.rbuiltin import gen_cast
@@ -258,6 +259,7 @@
[s_gc, s_typeid16,
annmodel.SomeInteger(nonneg=True),
annmodel.SomeBool(),
+ annmodel.SomeBool(),
annmodel.SomeBool()], s_gcref,
inline = False)
if hasattr(GCClass, 'malloc_fixedsize'):
@@ -267,6 +269,7 @@
[s_gc, s_typeid16,
annmodel.SomeInteger(nonneg=True),
annmodel.SomeBool(),
+ annmodel.SomeBool(),
annmodel.SomeBool()], s_gcref,
inline = False)
else:
@@ -319,7 +322,7 @@
raise NotImplementedError("GC needs write barrier, but does not provide writebarrier_before_copy functionality")
# in some GCs we can inline the common case of
- # malloc_fixedsize(typeid, size, True, False, False)
+ # malloc_fixedsize(typeid, size, False, False, False)
if getattr(GCClass, 'inline_simple_malloc', False):
# make a copy of this function so that it gets annotated
# independently and the constants are folded inside
@@ -337,7 +340,7 @@
malloc_fast,
[s_gc, s_typeid16,
annmodel.SomeInteger(nonneg=True),
- s_False, s_False], s_gcref,
+ s_False, s_False, s_False], s_gcref,
inline = True)
else:
self.malloc_fast_ptr = None
@@ -668,7 +671,13 @@
kind_and_fptr = self.special_funcptr_for_type(TYPE)
has_finalizer = (kind_and_fptr is not None and
kind_and_fptr[0] == "finalizer")
+ has_light_finalizer = (kind_and_fptr is not None and
+ kind_and_fptr[0] == "light_finalizer")
+ if has_light_finalizer:
+ has_finalizer = True
c_has_finalizer = rmodel.inputconst(lltype.Bool, has_finalizer)
+ c_has_light_finalizer = rmodel.inputconst(lltype.Bool,
+ has_light_finalizer)
if not op.opname.endswith('_varsize') and not flags.get('varsize'):
#malloc_ptr = self.malloc_fixedsize_ptr
@@ -682,7 +691,8 @@
else:
malloc_ptr = self.malloc_fixedsize_ptr
args = [self.c_const_gc, c_type_id, c_size,
- c_has_finalizer, rmodel.inputconst(lltype.Bool, False)]
+ c_has_finalizer, c_has_light_finalizer,
+ rmodel.inputconst(lltype.Bool, False)]
else:
assert not c_has_finalizer.value
info_varsize = self.layoutbuilder.get_info_varsize(type_id)
@@ -847,12 +857,13 @@
# used by the JIT (see pypy.jit.backend.llsupport.gc)
op = hop.spaceop
[v_typeid, v_size,
- v_has_finalizer, v_contains_weakptr] = op.args
+ v_has_finalizer, v_has_light_finalizer, v_contains_weakptr] = op.args
livevars = self.push_roots(hop)
hop.genop("direct_call",
[self.malloc_fixedsize_clear_ptr, self.c_const_gc,
v_typeid, v_size,
- v_has_finalizer, v_contains_weakptr],
+ v_has_finalizer, v_has_light_finalizer,
+ v_contains_weakptr],
resultvar=op.result)
self.pop_roots(hop, livevars)
@@ -912,10 +923,10 @@
info = self.layoutbuilder.get_info(type_id)
c_size = rmodel.inputconst(lltype.Signed, info.fixedsize)
malloc_ptr = self.malloc_fixedsize_ptr
- c_has_finalizer = rmodel.inputconst(lltype.Bool, False)
+ c_false = rmodel.inputconst(lltype.Bool, False)
c_has_weakptr = rmodel.inputconst(lltype.Bool, True)
args = [self.c_const_gc, c_type_id, c_size,
- c_has_finalizer, c_has_weakptr]
+ c_false, c_false, c_has_weakptr]
# push and pop the current live variables *including* the argument
# to the weakref_create operation, which must be kept alive and
@@ -1250,6 +1261,7 @@
lltype2vtable = translator.rtyper.lltype2vtable
else:
lltype2vtable = None
+ self.translator = translator
super(TransformerLayoutBuilder, self).__init__(GCClass, lltype2vtable)
def has_finalizer(self, TYPE):
@@ -1257,6 +1269,10 @@
return rtti is not None and getattr(rtti._obj, 'destructor_funcptr',
None)
+ def has_light_finalizer(self, TYPE):
+ special = self.special_funcptr_for_type(TYPE)
+ return special is not None and special[0] == 'light_finalizer'
+
def has_custom_trace(self, TYPE):
rtti = get_rtti(TYPE)
return rtti is not None and getattr(rtti._obj, 'custom_trace_funcptr',
@@ -1264,7 +1280,7 @@
def make_finalizer_funcptr_for_type(self, TYPE):
if not self.has_finalizer(TYPE):
- return None
+ return None, False
rtti = get_rtti(TYPE)
destrptr = rtti._obj.destructor_funcptr
DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
@@ -1276,7 +1292,9 @@
return llmemory.NULL
fptr = self.transformer.annotate_finalizer(ll_finalizer,
[llmemory.Address, llmemory.Address], llmemory.Address)
- return fptr
+ g = destrptr._obj.graph
+ light = not FinalizerAnalyzer(self.translator).analyze_light_finalizer(g)
+ return fptr, light
def make_custom_trace_funcptr_for_type(self, TYPE):
if not self.has_custom_trace(TYPE):
diff --git a/pypy/rpython/memory/gctypelayout.py b/pypy/rpython/memory/gctypelayout.py
--- a/pypy/rpython/memory/gctypelayout.py
+++ b/pypy/rpython/memory/gctypelayout.py
@@ -1,7 +1,6 @@
from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup
from pypy.rpython.lltypesystem import rclass
from pypy.rpython.lltypesystem.lloperation import llop
-from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.debug import ll_assert
from pypy.rlib.rarithmetic import intmask
from pypy.tool.identity_dict import identity_dict
@@ -85,6 +84,13 @@
else:
return lltype.nullptr(GCData.FINALIZER_OR_CT_FUNC)
+ def q_light_finalizer(self, typeid):
+ typeinfo = self.get(typeid)
+ if typeinfo.infobits & T_HAS_LIGHTWEIGHT_FINALIZER:
+ return typeinfo.finalizer_or_customtrace
+ else:
+ return lltype.nullptr(GCData.FINALIZER_OR_CT_FUNC)
+
def q_offsets_to_gc_pointers(self, typeid):
return self.get(typeid).ofstoptrs
@@ -142,6 +148,7 @@
self.q_has_gcptr_in_varsize,
self.q_is_gcarrayofgcptr,
self.q_finalizer,
+ self.q_light_finalizer,
self.q_offsets_to_gc_pointers,
self.q_fixed_size,
self.q_varsize_item_sizes,
@@ -157,16 +164,17 @@
# the lowest 16bits are used to store group member index
-T_MEMBER_INDEX = 0xffff
-T_IS_VARSIZE = 0x010000
-T_HAS_GCPTR_IN_VARSIZE = 0x020000
-T_IS_GCARRAY_OF_GCPTR = 0x040000
-T_IS_WEAKREF = 0x080000
-T_IS_RPYTHON_INSTANCE = 0x100000 # the type is a subclass of OBJECT
-T_HAS_FINALIZER = 0x200000
-T_HAS_CUSTOM_TRACE = 0x400000
-T_KEY_MASK = intmask(0xFF000000)
-T_KEY_VALUE = intmask(0x5A000000) # bug detection only
+T_MEMBER_INDEX = 0xffff
+T_IS_VARSIZE = 0x010000
+T_HAS_GCPTR_IN_VARSIZE = 0x020000
+T_IS_GCARRAY_OF_GCPTR = 0x040000
+T_IS_WEAKREF = 0x080000
+T_IS_RPYTHON_INSTANCE = 0x100000 # the type is a subclass of OBJECT
+T_HAS_FINALIZER = 0x200000
+T_HAS_CUSTOM_TRACE = 0x400000
+T_HAS_LIGHTWEIGHT_FINALIZER = 0x800000
+T_KEY_MASK = intmask(0xFF000000)
+T_KEY_VALUE = intmask(0x5A000000) # bug detection only
def _check_valid_type_info(p):
ll_assert(p.infobits & T_KEY_MASK == T_KEY_VALUE, "invalid type_id")
@@ -194,6 +202,8 @@
info.finalizer_or_customtrace = fptr
if kind == "finalizer":
infobits |= T_HAS_FINALIZER
+ elif kind == 'light_finalizer':
+ infobits |= T_HAS_FINALIZER | T_HAS_LIGHTWEIGHT_FINALIZER
elif kind == "custom_trace":
infobits |= T_HAS_CUSTOM_TRACE
else:
@@ -367,12 +377,15 @@
def special_funcptr_for_type(self, TYPE):
if TYPE in self._special_funcptrs:
return self._special_funcptrs[TYPE]
- fptr1 = self.make_finalizer_funcptr_for_type(TYPE)
+ fptr1, is_lightweight = self.make_finalizer_funcptr_for_type(TYPE)
fptr2 = self.make_custom_trace_funcptr_for_type(TYPE)
assert not (fptr1 and fptr2), (
"type %r needs both a finalizer and a custom tracer" % (TYPE,))
if fptr1:
- kind_and_fptr = "finalizer", fptr1
+ if is_lightweight:
+ kind_and_fptr = "light_finalizer", fptr1
+ else:
+ kind_and_fptr = "finalizer", fptr1
elif fptr2:
kind_and_fptr = "custom_trace", fptr2
else:
@@ -382,7 +395,7 @@
def make_finalizer_funcptr_for_type(self, TYPE):
# must be overridden for proper finalizer support
- return None
+ return None, False
def make_custom_trace_funcptr_for_type(self, TYPE):
# must be overridden for proper custom tracer support
diff --git a/pypy/rpython/memory/gcwrapper.py b/pypy/rpython/memory/gcwrapper.py
--- a/pypy/rpython/memory/gcwrapper.py
+++ b/pypy/rpython/memory/gcwrapper.py
@@ -1,3 +1,4 @@
+from pypy.translator.backendopt.finalizer import FinalizerAnalyzer
from pypy.rpython.lltypesystem import lltype, llmemory, llheap
from pypy.rpython import llinterp
from pypy.rpython.annlowlevel import llhelper
@@ -196,9 +197,11 @@
DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
destrgraph = destrptr._obj.graph
else:
- return None
+ return None, False
assert not type_contains_pyobjs(TYPE), "not implemented"
+ t = self.llinterp.typer.annotator.translator
+ light = not FinalizerAnalyzer(t).analyze_light_finalizer(destrgraph)
def ll_finalizer(addr, dummy):
assert dummy == llmemory.NULL
try:
@@ -208,7 +211,7 @@
raise RuntimeError(
"a finalizer raised an exception, shouldn't happen")
return llmemory.NULL
- return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer)
+ return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer), light
def make_custom_trace_funcptr_for_type(self, TYPE):
from pypy.rpython.memory.gctransform.support import get_rtti, \
diff --git a/pypy/rpython/memory/test/test_gc.py b/pypy/rpython/memory/test/test_gc.py
--- a/pypy/rpython/memory/test/test_gc.py
+++ b/pypy/rpython/memory/test/test_gc.py
@@ -5,7 +5,6 @@
from pypy.rpython.memory.test import snippet
from pypy.rpython.test.test_llinterp import get_interpreter
from pypy.rpython.lltypesystem import lltype
-from pypy.rpython.lltypesystem.rstr import STR
from pypy.rpython.lltypesystem.lloperation import llop
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.objectmodel import compute_unique_id
@@ -57,7 +56,7 @@
while j < 20:
j += 1
a.append(j)
- res = self.interpret(malloc_a_lot, [])
+ self.interpret(malloc_a_lot, [])
#assert simulator.current_size - curr < 16000 * INT_SIZE / 4
#print "size before: %s, size after %s" % (curr, simulator.current_size)
@@ -73,7 +72,7 @@
while j < 20:
j += 1
b.append((1, j, i))
- res = self.interpret(malloc_a_lot, [])
+ self.interpret(malloc_a_lot, [])
#assert simulator.current_size - curr < 16000 * INT_SIZE / 4
#print "size before: %s, size after %s" % (curr, simulator.current_size)
@@ -129,7 +128,7 @@
res = self.interpret(concat, [100])
assert res == concat(100)
#assert simulator.current_size - curr < 16000 * INT_SIZE / 4
-
+
def test_finalizer(self):
class B(object):
pass
@@ -278,7 +277,7 @@
self.interpret, f, [])
def test_weakref(self):
- import weakref, gc
+ import weakref
class A(object):
pass
def g():
@@ -299,7 +298,7 @@
assert res
def test_weakref_to_object_with_finalizer(self):
- import weakref, gc
+ import weakref
class A(object):
count = 0
a = A()
@@ -338,7 +337,7 @@
assert res
def test_cycle_with_weakref_and_del(self):
- import weakref, gc
+ import weakref
class A(object):
count = 0
a = A()
@@ -367,7 +366,7 @@
assert res == 11
def test_weakref_to_object_with_finalizer_ordering(self):
- import weakref, gc
+ import weakref
class A(object):
count = 0
a = A()
@@ -616,7 +615,7 @@
assert not rgc.can_move(a)
return 1
return 0
- except Exception, e:
+ except Exception:
return 2
assert self.interpret(func, []) == int(self.GC_CAN_MALLOC_NONMOVABLE)
@@ -647,8 +646,6 @@
assert self.interpret(f, [bigsize, 0, flag]) == 0x62024241
def test_tagged_simple(self):
- from pypy.rlib.objectmodel import UnboxedValue
-
class Unrelated(object):
pass
@@ -689,8 +686,6 @@
assert res == -897
def test_tagged_id(self):
- from pypy.rlib.objectmodel import UnboxedValue, compute_unique_id
-
class Unrelated(object):
pass
diff --git a/pypy/rpython/memory/test/test_transformed_gc.py b/pypy/rpython/memory/test/test_transformed_gc.py
--- a/pypy/rpython/memory/test/test_transformed_gc.py
+++ b/pypy/rpython/memory/test/test_transformed_gc.py
@@ -345,22 +345,22 @@
b = B()
b.nextid = 0
b.num_deleted = 0
- class A(object):
+ class AAA(object):
def __init__(self):
self.id = b.nextid
b.nextid += 1
def __del__(self):
b.num_deleted += 1
C()
- class C(A):
+ class C(AAA):
def __del__(self):
b.num_deleted += 1
def f(x, y):
- a = A()
+ a = AAA()
i = 0
while i < x:
i += 1
- a = A()
+ a = AAA()
llop.gc__collect(lltype.Void)
llop.gc__collect(lltype.Void)
return b.num_deleted
@@ -807,6 +807,7 @@
op.args = [Constant(type_id, llgroup.HALFWORD),
Constant(llmemory.sizeof(P), lltype.Signed),
Constant(False, lltype.Bool), # has_finalizer
+ Constant(False, lltype.Bool), # is_finalizer_light
Constant(False, lltype.Bool)] # contains_weakptr
break
else:
@@ -843,6 +844,7 @@
op.args = [Constant(type_id, llgroup.HALFWORD),
Constant(llmemory.sizeof(P), lltype.Signed),
Constant(False, lltype.Bool), # has_finalizer
+ Constant(False, lltype.Bool), # is_finalizer_light
Constant(False, lltype.Bool)] # contains_weakptr
break
else:
diff --git a/pypy/rpython/module/ll_os.py b/pypy/rpython/module/ll_os.py
--- a/pypy/rpython/module/ll_os.py
+++ b/pypy/rpython/module/ll_os.py
@@ -959,8 +959,6 @@
os_ftruncate(rffi.cast(rffi.INT, fd),
rffi.cast(rffi.LONGLONG, length)))
if res < 0:
- # Note: for consistency we raise OSError, but CPython
- # raises IOError here
raise OSError(rposix.get_errno(), "os_ftruncate failed")
return extdef([int, r_longlong], s_None,
diff --git a/pypy/rpython/rtyper.py b/pypy/rpython/rtyper.py
--- a/pypy/rpython/rtyper.py
+++ b/pypy/rpython/rtyper.py
@@ -717,7 +717,7 @@
raise TyperError("runtime type info function %r returns %r, "
"excepted Ptr(RuntimeTypeInfo)" % (func, s))
funcptr = self.getcallable(graph)
- attachRuntimeTypeInfo(GCSTRUCT, funcptr, destrptr)
+ attachRuntimeTypeInfo(GCSTRUCT, funcptr, destrptr, None)
# register operations from annotation model
RPythonTyper._registeroperations(annmodel)
diff --git a/pypy/rpython/test/test_rclass.py b/pypy/rpython/test/test_rclass.py
--- a/pypy/rpython/test/test_rclass.py
+++ b/pypy/rpython/test/test_rclass.py
@@ -3,7 +3,7 @@
from pypy.translator.translator import TranslationContext, graphof
from pypy.rpython.lltypesystem.lltype import *
from pypy.rpython.ootypesystem import ootype
-from pypy.rlib.rarithmetic import intmask, r_longlong
+from pypy.rlib.rarithmetic import r_longlong
from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
from pypy.rpython.rclass import IR_IMMUTABLE, IR_IMMUTABLE_ARRAY
from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
@@ -972,10 +972,10 @@
graph = graphof(t, f)
TYPE = graph.startblock.operations[0].args[0].value
RTTI = getRuntimeTypeInfo(TYPE)
- queryptr = RTTI._obj.query_funcptr # should not raise
+ RTTI._obj.query_funcptr # should not raise
destrptr = RTTI._obj.destructor_funcptr
assert destrptr is not None
-
+
def test_del_inheritance(self):
from pypy.rlib import rgc
class State:
diff --git a/pypy/translator/backendopt/finalizer.py b/pypy/translator/backendopt/finalizer.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/backendopt/finalizer.py
@@ -0,0 +1,46 @@
+
+from pypy.translator.backendopt import graphanalyze
+from pypy.rpython.lltypesystem import lltype
+
+class FinalizerError(Exception):
+ """ __del__ marked as lightweight finalizer, but the analyzer did
+ not agreed
+ """
+
+class FinalizerAnalyzer(graphanalyze.BoolGraphAnalyzer):
+ """ Analyzer that determines whether a finalizer is lightweight enough
+ so it can be called without all the complicated logic in the garbage
+ collector. The set of operations here is restrictive for a good reason
+ - it's better to be safe. Specifically disallowed operations:
+
+ * anything that escapes self
+ * anything that can allocate
+ """
+ ok_operations = ['ptr_nonzero', 'ptr_eq', 'ptr_ne', 'free', 'same_as',
+ 'direct_ptradd', 'force_cast', 'track_alloc_stop',
+ 'raw_free']
+
+ def analyze_light_finalizer(self, graph):
+ result = self.analyze_direct_call(graph)
+ if (result is self.top_result() and
+ getattr(graph.func, '_is_light_finalizer_', False)):
+ raise FinalizerError(FinalizerError.__doc__, graph)
+ return result
+
+ def analyze_simple_operation(self, op, graphinfo):
+ if op.opname in self.ok_operations:
+ return self.bottom_result()
+ if (op.opname.startswith('int_') or op.opname.startswith('float_')
+ or op.opname.startswith('cast_')):
+ return self.bottom_result()
+ if op.opname == 'setfield' or op.opname == 'bare_setfield':
+ TP = op.args[2].concretetype
+ if not isinstance(TP, lltype.Ptr) or TP.TO._gckind == 'raw':
+ # primitive type
+ return self.bottom_result()
+ if op.opname == 'getfield':
+ TP = op.result.concretetype
+ if not isinstance(TP, lltype.Ptr) or TP.TO._gckind == 'raw':
+ # primitive type
+ return self.bottom_result()
+ return self.top_result()
diff --git a/pypy/translator/backendopt/test/test_finalizer.py b/pypy/translator/backendopt/test/test_finalizer.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/backendopt/test/test_finalizer.py
@@ -0,0 +1,142 @@
+
+import py
+from pypy.translator.backendopt.finalizer import FinalizerAnalyzer,\
+ FinalizerError
+from pypy.translator.translator import TranslationContext, graphof
+from pypy.translator.backendopt.all import backend_optimizations
+from pypy.translator.unsimplify import varoftype
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.conftest import option
+from pypy.rlib import rgc
+
+
+class BaseFinalizerAnalyzerTests(object):
+ """ Below are typical destructors that we encounter in pypy
+ """
+
+ type_system = None
+
+ def analyze(self, func, sig, func_to_analyze=None, backendopt=False):
+ if func_to_analyze is None:
+ func_to_analyze = func
+ t = TranslationContext()
+ t.buildannotator().build_types(func, sig)
+ t.buildrtyper(type_system=self.type_system).specialize()
+ if backendopt:
+ backend_optimizations(t)
+ if option.view:
+ t.view()
+ a = FinalizerAnalyzer(t)
+ fgraph = graphof(t, func_to_analyze)
+ result = a.analyze_light_finalizer(fgraph)
+ return result
+
+ def test_nothing(self):
+ def f():
+ pass
+ r = self.analyze(f, [])
+ assert not r
+
+def test_various_ops():
+ from pypy.objspace.flow.model import SpaceOperation, Constant
+
+ X = lltype.Ptr(lltype.GcStruct('X'))
+ Z = lltype.Ptr(lltype.Struct('Z'))
+ S = lltype.GcStruct('S', ('x', lltype.Signed),
+ ('y', X),
+ ('z', Z))
+ v1 = varoftype(lltype.Bool)
+ v2 = varoftype(lltype.Signed)
+ f = FinalizerAnalyzer(None)
+ r = f.analyze(SpaceOperation('cast_int_to_bool', [v2],
+ v1))
+ assert not r
+ v1 = varoftype(lltype.Ptr(S))
+ v2 = varoftype(lltype.Signed)
+ v3 = varoftype(X)
+ v4 = varoftype(Z)
+ assert not f.analyze(SpaceOperation('bare_setfield', [v1, Constant('x'),
+ v2], None))
+ assert f.analyze(SpaceOperation('bare_setfield', [v1, Constant('y'),
+ v3], None))
+ assert not f.analyze(SpaceOperation('bare_setfield', [v1, Constant('z'),
+ v4], None))
+
+
+class TestLLType(BaseFinalizerAnalyzerTests):
+ type_system = 'lltype'
+
+ def test_malloc(self):
+ S = lltype.GcStruct('S')
+
+ def f():
+ return lltype.malloc(S)
+
+ r = self.analyze(f, [])
+ assert r
+
+ def test_raw_free_getfield(self):
+ S = lltype.Struct('S')
+
+ class A(object):
+ def __init__(self):
+ self.x = lltype.malloc(S, flavor='raw')
+
+ def __del__(self):
+ if self.x:
+ self.x = lltype.nullptr(S)
+ lltype.free(self.x, flavor='raw')
+
+ def f():
+ return A()
+
+ r = self.analyze(f, [], A.__del__.im_func)
+ assert not r
+
+ def test_c_call(self):
+ C = rffi.CArray(lltype.Signed)
+ c = rffi.llexternal('x', [lltype.Ptr(C)], lltype.Signed)
+
+ def g():
+ p = lltype.malloc(C, 3, flavor='raw')
+ f(p)
+
+ def f(p):
+ c(rffi.ptradd(p, 0))
+ lltype.free(p, flavor='raw')
+
+ r = self.analyze(g, [], f, backendopt=True)
+ assert not r
+
+ def test_chain(self):
+ class B(object):
+ def __init__(self):
+ self.counter = 1
+
+ class A(object):
+ def __init__(self):
+ self.x = B()
+
+ def __del__(self):
+ self.x.counter += 1
+
+ def f():
+ A()
+
+ r = self.analyze(f, [], A.__del__.im_func)
+ assert r
+
+ def test_is_light_finalizer_decorator(self):
+ S = lltype.GcStruct('S')
+
+ @rgc.is_light_finalizer
+ def f():
+ lltype.malloc(S)
+ @rgc.is_light_finalizer
+ def g():
+ pass
+ self.analyze(g, []) # did not explode
+ py.test.raises(FinalizerError, self.analyze, f, [])
+
+class TestOOType(BaseFinalizerAnalyzerTests):
+ type_system = 'ootype'
More information about the pypy-commit
mailing list