Author: Tim Felgentreff <timfelgentr...@gmail.com> Branch: Changeset: r89901:c613943ef39a Date: 2017-02-02 11:07 +0100 http://bitbucket.org/pypy/pypy/changeset/c613943ef39a/
Log: merge default diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py --- a/lib_pypy/_ctypes/basics.py +++ b/lib_pypy/_ctypes/basics.py @@ -102,7 +102,11 @@ % (len(buf) + offset, size + offset)) raw_addr = buf._pypy_raw_address() result = self.from_address(raw_addr) - result._ensure_objects()['ffffffff'] = obj + objects = result._ensure_objects() + if objects is not None: + objects['ffffffff'] = obj + else: # case e.g. of a primitive type like c_int + result._objects = obj return result def from_buffer_copy(self, obj, offset=0): diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -198,10 +198,21 @@ def getdict(self, space): if self.w_func_dict is None: + if not self.can_change_code: + raise oefmt(space.w_AttributeError, + "cannot set extra attributes on built-in functions") self.w_func_dict = space.newdict(instance=True) return self.w_func_dict + def getdictvalue(self, space, attr): + if not self.can_change_code: + return None + return W_Root.getdictvalue(self, space, attr) + def setdict(self, space, w_dict): + if not self.can_change_code: + raise oefmt(space.w_AttributeError, + "cannot set __dict__ on built-in functions") if not space.isinstance_w(w_dict, space.w_dict): raise oefmt(space.w_TypeError, "setting function's dictionary to a non-dict") @@ -660,7 +671,7 @@ Function.__init__(self, func.space, func.code, func.w_func_globals, func.defs_w, func.closure, func.name) self.w_doc = func.w_doc - self.w_func_dict = func.w_func_dict + #self.w_func_dict = func.w_func_dict---nowadays, always None self.w_module = func.w_module def descr_builtinfunction__new__(space, w_subtype): diff --git a/pypy/interpreter/test/test_function.py b/pypy/interpreter/test/test_function.py --- a/pypy/interpreter/test/test_function.py +++ b/pypy/interpreter/test/test_function.py @@ -95,8 +95,16 @@ def test_write_code_builtin_forbidden(self): def f(*args): return 42 - raises(TypeError, "dir.func_code = f.func_code") - raises(TypeError, "list.append.im_func.func_code = f.func_code") + raises(TypeError, "dir.func_code = f.func_code") + raises(TypeError, "list.append.im_func.func_code = f.func_code") + + def test_write_extra_attributes_builtin_forbidden(self): + raises(AttributeError, "dir.abcd = 5") + raises(AttributeError, "list.append.im_func.efgh = 6") + raises(AttributeError, "dir.__dict__") + raises(AttributeError, "dir.__dict__ = {}") + c = all.__call__ # this should work + assert c([4, 5, 6]) is True def test_set_module_to_name_eagerly(self): skip("fails on PyPy but works on CPython. Unsure we want to care") 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 @@ -122,12 +122,120 @@ hints={'nolength': True})) class W_ArrayBase(W_Root): - _attrs_ = ('space', 'len', 'allocated', '_lifeline_') # no buffer + _attrs_ = ('space', 'len', 'allocated', '_lifeline_', '_buffer') def __init__(self, space): self.space = space self.len = 0 self.allocated = 0 + self._buffer = lltype.nullptr(rffi.CCHARP.TO) + + @rgc.must_be_light_finalizer + def __del__(self): + if self._buffer: + lltype.free(self._buffer, flavor='raw') + + def setlen(self, size, zero=False, overallocate=True): + if size > 0: + if size > self.allocated or size < self.allocated / 2: + if overallocate: + if size < 9: + some = 3 + else: + some = 6 + some += size >> 3 + else: + some = 0 + self.allocated = size + some + byte_size = self.allocated * self.itemsize + if zero: + new_buffer = lltype.malloc( + rffi.CCHARP.TO, byte_size, flavor='raw', + add_memory_pressure=True, zero=True) + else: + new_buffer = lltype.malloc( + rffi.CCHARP.TO, byte_size, flavor='raw', + add_memory_pressure=True) + copy_bytes = min(size, self.len) * self.itemsize + rffi.c_memcpy(rffi.cast(rffi.VOIDP, new_buffer), + rffi.cast(rffi.VOIDP, self._buffer), + copy_bytes) + else: + self.len = size + return + else: + assert size == 0 + self.allocated = 0 + new_buffer = lltype.nullptr(rffi.CCHARP.TO) + + if self._buffer: + lltype.free(self._buffer, flavor='raw') + self._buffer = new_buffer + self.len = size + + def _fromiterable(self, w_seq): + # used by fromsequence(). + # a more careful case if w_seq happens to be a very large + # iterable: don't copy the items into some intermediate list + w_iterator = self.space.iter(w_seq) + tp = self.space.type(w_iterator) + while True: + unpack_driver.jit_merge_point(selfclass=self.__class__, + tp=tp, self=self, + w_iterator=w_iterator) + space = self.space + try: + w_item = space.next(w_iterator) + except OperationError as e: + if not e.match(space, space.w_StopIteration): + raise + break # done + self.descr_append(space, w_item) + + def _charbuf_start(self): + return self._buffer + + def _buffer_as_unsigned(self): + return rffi.cast(lltype.Unsigned, self._buffer) + + def _charbuf_stop(self): + keepalive_until_here(self) + + def delitem(self, space, i, j): + if i < 0: + i += self.len + if i < 0: + i = 0 + if j < 0: + j += self.len + if j < 0: + j = 0 + if j > self.len: + j = self.len + if i >= j: + return None + oldbuffer = self._buffer + self._buffer = lltype.malloc(rffi.CCHARP.TO, + (self.len - (j - i)) * self.itemsize, flavor='raw', + add_memory_pressure=True) + if i: + rffi.c_memcpy( + rffi.cast(rffi.VOIDP, self._buffer), + rffi.cast(rffi.VOIDP, oldbuffer), + i * self.itemsize + ) + if j < self.len: + rffi.c_memcpy( + rffi.cast(rffi.VOIDP, rffi.ptradd(self._buffer, + i * self.itemsize)), + rffi.cast(rffi.VOIDP, rffi.ptradd(oldbuffer, + j * self.itemsize)), + (self.len - j) * self.itemsize + ) + self.len -= j - i + self.allocated = self.len + if oldbuffer: + lltype.free(oldbuffer, flavor='raw') def readbuf_w(self, space): return ArrayBuffer(self, True) @@ -154,14 +262,24 @@ Return number of occurrences of x in the array. """ - raise NotImplementedError + cnt = 0 + for i in range(self.len): + # XXX jitdriver + w_item = self.w_getitem(space, i) + if space.is_true(space.eq(w_item, w_val)): + cnt += 1 + return space.wrap(cnt) def descr_index(self, space, w_x): """ index(x) Return index of first occurrence of x in the array. """ - raise NotImplementedError + for i in range(self.len): + w_item = self.w_getitem(space, i) + if space.is_true(space.eq(w_item, w_x)): + return space.wrap(i) + raise oefmt(space.w_ValueError, "array.index(x): x not in list") def descr_reverse(self, space): """ reverse() @@ -175,7 +293,8 @@ Remove the first occurrence of x in the array. """ - raise NotImplementedError + w_idx = self.descr_index(space, w_val) + self.descr_pop(space, space.int_w(w_idx)) @unwrap_spec(i=int) def descr_pop(self, space, i=-1): @@ -453,16 +572,102 @@ return space.newseqiter(self) def descr_add(self, space, w_other): - raise NotImplementedError + if (not isinstance(w_other, W_ArrayBase) + or w_other.typecode != self.typecode): + return space.w_NotImplemented + a = self.constructor(space) + a.setlen(self.len + w_other.len, overallocate=False) + if self.len: + rffi.c_memcpy( + rffi.cast(rffi.VOIDP, a._buffer), + rffi.cast(rffi.VOIDP, self._buffer), + self.len * self.itemsize + ) + if w_other.len: + rffi.c_memcpy( + rffi.cast(rffi.VOIDP, rffi.ptradd(a._buffer, + self.len * self.itemsize)), + rffi.cast(rffi.VOIDP, w_other._buffer), + w_other.len * self.itemsize + ) + keepalive_until_here(self) + keepalive_until_here(a) + return a def descr_inplace_add(self, space, w_other): - raise NotImplementedError + if (not isinstance(w_other, W_ArrayBase) + or w_other.typecode != self.typecode): + return space.w_NotImplemented + oldlen = self.len + otherlen = w_other.len + self.setlen(oldlen + otherlen) + if otherlen: + rffi.c_memcpy( + rffi.cast(rffi.VOIDP, rffi.ptradd(self._buffer, + oldlen * self.itemsize)), + rffi.cast(rffi.VOIDP, w_other._buffer), + otherlen * self.itemsize + ) + keepalive_until_here(self) + keepalive_until_here(w_other) + return self + + def _mul_helper(self, space, w_repeat, is_inplace): + try: + repeat = space.getindex_w(w_repeat, space.w_OverflowError) + except OperationError as e: + if e.match(space, space.w_TypeError): + return space.w_NotImplemented + raise + if is_inplace: + a = self + start = 1 + else: + a = self.constructor(space) + start = 0 + if repeat <= start: + if repeat <= 0: + a.setlen(0, overallocate=False) + return a + oldlen = self.len + try: + newlen = ovfcheck(oldlen * repeat) + except OverflowError: + raise MemoryError + # + srcbuf = self._buffer + srcsize = self.len * self.itemsize + for i in range(srcsize): + if srcbuf[i] != '\x00': + break + else: + # the source is entirely zero: initialize the target + # with zeroes too + a.setlen(newlen, zero=True, overallocate=False) + return a + # + a.setlen(newlen, overallocate=False) + srcbuf = self._buffer # reload this, in case self is a + if oldlen == 1: + self._repeat_single_item(a, start, repeat) + else: + dstbuf = a._buffer + if start == 1: + dstbuf = rffi.ptradd(dstbuf, srcsize) + for r in range(start, repeat): + rffi.c_memcpy(rffi.cast(rffi.VOIDP, dstbuf), + rffi.cast(rffi.VOIDP, srcbuf), + srcsize) + dstbuf = rffi.ptradd(dstbuf, srcsize) + keepalive_until_here(self) + keepalive_until_here(a) + return a def descr_mul(self, space, w_repeat): - raise NotImplementedError + return self._mul_helper(space, w_repeat, False) def descr_inplace_mul(self, space, w_repeat): - raise NotImplementedError + return self._mul_helper(space, w_repeat, True) def descr_radd(self, space, w_other): return self.descr_add(space, w_other) @@ -551,6 +756,7 @@ self.itemtype = itemtype self.bytes = rffi.sizeof(itemtype) self.arraytype = lltype.Array(itemtype, hints={'nolength': True}) + self.arrayptrtype = lltype.Ptr(self.arraytype) self.unwrap = unwrap self.signed = signed self.canoverflow = canoverflow @@ -640,22 +846,21 @@ return self.array._charbuf_start() +unpack_driver = jit.JitDriver(name='unpack_array', + greens=['selfclass', 'tp'], + reds=['self', 'w_iterator']) + def make_array(mytype): W_ArrayBase = globals()['W_ArrayBase'] - unpack_driver = jit.JitDriver(name='unpack_array', - greens=['tp'], - reds=['self', 'w_iterator']) - class W_Array(W_ArrayBase): itemsize = mytype.bytes typecode = mytype.typecode - _attrs_ = ('space', 'len', 'allocated', '_lifeline_', 'buffer') + _attrs_ = W_ArrayBase._attrs_ - def __init__(self, space): - W_ArrayBase.__init__(self, space) - self.buffer = lltype.nullptr(mytype.arraytype) + def get_buffer(self): + return rffi.cast(mytype.arrayptrtype, self._buffer) def item_w(self, w_item): space = self.space @@ -709,46 +914,6 @@ self.space.wrap(msg)) return result - @rgc.must_be_light_finalizer - def __del__(self): - if self.buffer: - lltype.free(self.buffer, flavor='raw') - - def setlen(self, size, zero=False, overallocate=True): - if size > 0: - if size > self.allocated or size < self.allocated / 2: - if overallocate: - if size < 9: - some = 3 - else: - some = 6 - some += size >> 3 - else: - some = 0 - self.allocated = size + some - if zero: - new_buffer = lltype.malloc( - mytype.arraytype, self.allocated, flavor='raw', - add_memory_pressure=True, zero=True) - else: - new_buffer = lltype.malloc( - mytype.arraytype, self.allocated, flavor='raw', - add_memory_pressure=True) - for i in range(min(size, self.len)): - new_buffer[i] = self.buffer[i] - else: - self.len = size - return - else: - assert size == 0 - self.allocated = 0 - new_buffer = lltype.nullptr(mytype.arraytype) - - if self.buffer: - lltype.free(self.buffer, flavor='raw') - self.buffer = new_buffer - self.len = size - def fromsequence(self, w_seq): space = self.space oldlen = self.len @@ -764,20 +929,21 @@ if lst is not None: self.setlen(oldlen + len(lst)) try: - buf = self.buffer + buf = self.get_buffer() for num in lst: buf[newlen] = self.item_from_int_or_float(num) newlen += 1 except OperationError: self.setlen(newlen) raise + keepalive_until_here(self) return # this is the common case: w_seq is a list or a tuple lst_w = space.listview_no_unpack(w_seq) if lst_w is not None: self.setlen(oldlen + len(lst_w)) - buf = self.buffer + buf = self.get_buffer() try: for w_num in lst_w: # note: self.item_w() might invoke arbitrary code. @@ -788,30 +954,14 @@ buf[newlen] = self.item_w(w_num) newlen += 1 except OperationError: - if buf == self.buffer: + if buf == self.get_buffer(): self.setlen(newlen) raise + keepalive_until_here(self) return self._fromiterable(w_seq) - def _fromiterable(self, w_seq): - # a more careful case if w_seq happens to be a very large - # iterable: don't copy the items into some intermediate list - w_iterator = self.space.iter(w_seq) - tp = self.space.type(w_iterator) - while True: - unpack_driver.jit_merge_point(tp=tp, self=self, - w_iterator=w_iterator) - space = self.space - try: - w_item = space.next(w_iterator) - except OperationError as e: - if not e.match(space, space.w_StopIteration): - raise - break # done - self.descr_append(space, w_item) - def extend(self, w_iterable, accept_different_array=False): space = self.space if isinstance(w_iterable, W_Array): @@ -819,11 +969,14 @@ new = w_iterable.len self.setlen(self.len + new) i = 0 + buf = self.get_buffer() + srcbuf = w_iterable.get_buffer() while i < new: if oldlen + i >= self.len: self.setlen(oldlen + i + 1) - self.buffer[oldlen + i] = w_iterable.buffer[i] + buf[oldlen + i] = srcbuf[i] i += 1 + keepalive_until_here(w_iterable) self.setlen(oldlen + i) elif (not accept_different_array and isinstance(w_iterable, W_ArrayBase)): @@ -832,17 +985,9 @@ else: self.fromsequence(w_iterable) - def _charbuf_start(self): - return rffi.cast(rffi.CCHARP, self.buffer) - - def _buffer_as_unsigned(self): - return rffi.cast(lltype.Unsigned, self.buffer) - - def _charbuf_stop(self): + def w_getitem(self, space, idx): + item = self.get_buffer()[idx] keepalive_until_here(self) - - def w_getitem(self, space, idx): - item = self.buffer[idx] if mytype.typecode in 'bBhHil': item = rffi.cast(lltype.Signed, item) elif mytype.typecode == 'f': @@ -855,29 +1000,16 @@ x = self.item_w(w_x) index = self.len self.setlen(index + 1) - self.buffer[index] = x + self.get_buffer()[index] = x + keepalive_until_here(self) # List interface - def descr_count(self, space, w_val): - cnt = 0 - for i in range(self.len): - # XXX jitdriver - w_item = self.w_getitem(space, i) - if space.is_true(space.eq(w_item, w_val)): - cnt += 1 - return space.wrap(cnt) - - def descr_index(self, space, w_val): - for i in range(self.len): - w_item = self.w_getitem(space, i) - if space.is_true(space.eq(w_item, w_val)): - return space.wrap(i) - raise oefmt(space.w_ValueError, "array.index(x): x not in list") def descr_reverse(self, space): - b = self.buffer + b = self.get_buffer() for i in range(self.len / 2): b[i], b[self.len - i - 1] = b[self.len - i - 1], b[i] + keepalive_until_here(self) def descr_pop(self, space, i): if i < 0: @@ -885,16 +1017,14 @@ if i < 0 or i >= self.len: raise oefmt(space.w_IndexError, "pop index out of range") w_val = self.w_getitem(space, i) + b = self.get_buffer() while i < self.len - 1: - self.buffer[i] = self.buffer[i + 1] + b[i] = b[i + 1] i += 1 + keepalive_until_here(self) self.setlen(self.len - 1) return w_val - def descr_remove(self, space, w_val): - w_idx = self.descr_index(space, w_val) - self.descr_pop(space, space.int_w(w_idx)) - def descr_insert(self, space, idx, w_val): if idx < 0: idx += self.len @@ -906,10 +1036,12 @@ val = self.item_w(w_val) self.setlen(self.len + 1) i = self.len - 1 + b = self.get_buffer() while i > idx: - self.buffer[i] = self.buffer[i - 1] + b[i] = b[i - 1] i -= 1 - self.buffer[i] = val + b[i] = val + keepalive_until_here(self) def getitem_slice(self, space, w_idx): start, stop, step, size = space.decode_index4(w_idx, self.len) @@ -917,9 +1049,13 @@ w_a.setlen(size, overallocate=False) assert step != 0 j = 0 + buf = w_a.get_buffer() + srcbuf = self.get_buffer() for i in range(start, stop, step): - w_a.buffer[j] = self.buffer[i] + buf[j] = srcbuf[i] j += 1 + keepalive_until_here(self) + keepalive_until_here(w_a) return w_a def setitem(self, space, w_idx, w_item): @@ -928,7 +1064,8 @@ raise oefmt(self.space.w_TypeError, "can only assign array to array slice") item = self.item_w(w_item) - self.buffer[idx] = item + self.get_buffer()[idx] = item + keepalive_until_here(self) def setitem_slice(self, space, w_idx, w_item): if not isinstance(w_item, W_Array): @@ -945,127 +1082,21 @@ self.fromsequence(w_lst) else: j = 0 + buf = self.get_buffer() + srcbuf = w_item.get_buffer() for i in range(start, stop, step): - self.buffer[i] = w_item.buffer[j] + buf[i] = srcbuf[j] j += 1 + keepalive_until_here(w_item) + keepalive_until_here(self) - def delitem(self, space, i, j): - if i < 0: - i += self.len - if i < 0: - i = 0 - if j < 0: - j += self.len - if j < 0: - j = 0 - if j > self.len: - j = self.len - if i >= j: - return None - oldbuffer = self.buffer - self.buffer = lltype.malloc( - mytype.arraytype, max(self.len - (j - i), 0), flavor='raw', - add_memory_pressure=True) - if i: - rffi.c_memcpy( - rffi.cast(rffi.VOIDP, self.buffer), - rffi.cast(rffi.VOIDP, oldbuffer), - i * mytype.bytes - ) - if j < self.len: - rffi.c_memcpy( - rffi.cast(rffi.VOIDP, rffi.ptradd(self.buffer, i)), - rffi.cast(rffi.VOIDP, rffi.ptradd(oldbuffer, j)), - (self.len - j) * mytype.bytes - ) - self.len -= j - i - self.allocated = self.len - if oldbuffer: - lltype.free(oldbuffer, flavor='raw') - - # Add and mul methods - def descr_add(self, space, w_other): - if not isinstance(w_other, W_Array): - return space.w_NotImplemented - a = mytype.w_class(space) - a.setlen(self.len + w_other.len, overallocate=False) - if self.len: - rffi.c_memcpy( - rffi.cast(rffi.VOIDP, a.buffer), - rffi.cast(rffi.VOIDP, self.buffer), - self.len * mytype.bytes - ) - if w_other.len: - rffi.c_memcpy( - rffi.cast(rffi.VOIDP, rffi.ptradd(a.buffer, self.len)), - rffi.cast(rffi.VOIDP, w_other.buffer), - w_other.len * mytype.bytes - ) - return a - - def descr_inplace_add(self, space, w_other): - if not isinstance(w_other, W_Array): - return space.w_NotImplemented - oldlen = self.len - otherlen = w_other.len - self.setlen(oldlen + otherlen) - if otherlen: - rffi.c_memcpy( - rffi.cast(rffi.VOIDP, rffi.ptradd(self.buffer, oldlen)), - rffi.cast(rffi.VOIDP, w_other.buffer), - otherlen * mytype.bytes - ) - return self - - def descr_mul(self, space, w_repeat): - return _mul_helper(space, self, w_repeat, False) - - def descr_inplace_mul(self, space, w_repeat): - return _mul_helper(space, self, w_repeat, True) - - def _mul_helper(space, self, w_repeat, is_inplace): - try: - repeat = space.getindex_w(w_repeat, space.w_OverflowError) - except OperationError as e: - if e.match(space, space.w_TypeError): - return space.w_NotImplemented - raise - repeat = max(repeat, 0) - try: - newlen = ovfcheck(self.len * repeat) - except OverflowError: - raise MemoryError - oldlen = self.len - if is_inplace: - a = self - start = 1 - else: - a = mytype.w_class(space) - start = 0 - # <a performance hack> - if oldlen == 1: - if mytype.unwrap == 'str_w' or mytype.unwrap == 'unicode_w': - zero = not ord(self.buffer[0]) - elif mytype.unwrap == 'int_w' or mytype.unwrap == 'bigint_w': - zero = not widen(self.buffer[0]) - #elif mytype.unwrap == 'float_w': - # value = ...float(self.buffer[0]) xxx handle the case of -0.0 - else: - zero = False - if zero: - a.setlen(newlen, zero=True, overallocate=False) - return a - a.setlen(newlen, overallocate=False) - item = self.buffer[0] + def _repeat_single_item(self, a, start, repeat): + # <a performance hack> + assert isinstance(a, W_Array) + item = self.get_buffer()[0] + dstbuf = a.get_buffer() for r in range(start, repeat): - a.buffer[r] = item - return a - # </a performance hack> - a.setlen(newlen, overallocate=False) - for r in range(start, repeat): - for i in range(oldlen): - a.buffer[r * oldlen + i] = self.buffer[i] - return a + dstbuf[r] = item mytype.w_class = W_Array W_Array.constructor = W_Array diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py b/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py +++ b/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py @@ -24,6 +24,16 @@ assert b[0] == "a" assert b[:] == "abc\0" + def test_from_buffer(self): + b1 = bytearray("abcde") + b = (c_char * 5).from_buffer(b1) + assert b[2] == "c" + # + b1 = bytearray("abcd") + b = c_int.from_buffer(b1) + assert b.value in (1684234849, # little endian + 1633837924) # big endian + try: c_wchar except NameError: diff --git a/rpython/jit/codewriter/test/test_call.py b/rpython/jit/codewriter/test/test_call.py --- a/rpython/jit/codewriter/test/test_call.py +++ b/rpython/jit/codewriter/test/test_call.py @@ -281,6 +281,8 @@ def test_elidable_kinds(): from rpython.jit.backend.llgraph.runner import LLGraphCPU + from rpython.rlib.objectmodel import compute_hash + from rpython.rlib.rsiphash import enable_siphash24 @jit.elidable def f1(n, m): @@ -293,12 +295,17 @@ if n > m: raise ValueError return n + m + @jit.elidable + def f4(n, m): + return compute_hash(str(n) + str(m)) def f(n, m): a = f1(n, m) b = f2(n, m) c = f3(n, m) - return a + len(b) + c + d = f4(n, m) + enable_siphash24() + return a + len(b) + c + d rtyper = support.annotate(f, [7, 9]) jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0]) @@ -309,7 +316,8 @@ for index, expected in [ (0, EffectInfo.EF_ELIDABLE_CANNOT_RAISE), (1, EffectInfo.EF_ELIDABLE_OR_MEMORYERROR), - (2, EffectInfo.EF_ELIDABLE_CAN_RAISE)]: + (2, EffectInfo.EF_ELIDABLE_CAN_RAISE), + (3, EffectInfo.EF_ELIDABLE_OR_MEMORYERROR)]: call_op = f_graph.startblock.operations[index] assert call_op.opname == 'direct_call' call_descr = cc.getcalldescr(call_op) diff --git a/rpython/memory/gctransform/asmgcroot.py b/rpython/memory/gctransform/asmgcroot.py --- a/rpython/memory/gctransform/asmgcroot.py +++ b/rpython/memory/gctransform/asmgcroot.py @@ -26,7 +26,6 @@ class AsmGcRootFrameworkGCTransformer(BaseFrameworkGCTransformer): _asmgcc_save_restore_arguments = None - _seen_gctransformer_hint_close_stack = None def push_roots(self, hop, keep_current_args=False): livevars = self.get_livevars_for_roots(hop, keep_current_args) @@ -50,29 +49,28 @@ hop.genop("direct_call", [c_asm_nocollect, name]) def gct_direct_call(self, hop): + # just a sanity check: if we find a fnptr with the hint on the + # _callable, then we'd also find the hint by looking only at the + # graph. We'll actually change this graph only later, in + # start_transforming_graph(). fnptr = hop.spaceop.args[0].value try: close_stack = fnptr._obj._callable._gctransformer_hint_close_stack_ except AttributeError: + pass + else: + assert fnptr._obj.graph.func is fnptr._obj._callable + BaseFrameworkGCTransformer.gct_direct_call(self, hop) + + def start_transforming_graph(self, graph): + try: + close_stack = graph.func._gctransformer_hint_close_stack_ + except AttributeError: close_stack = False if close_stack: - self.handle_call_with_close_stack(hop) - else: - BaseFrameworkGCTransformer.gct_direct_call(self, hop) + self._transform_hint_close_stack(graph) - def handle_call_with_close_stack(self, hop): - fnptr = hop.spaceop.args[0].value - if self._seen_gctransformer_hint_close_stack is None: - self._seen_gctransformer_hint_close_stack = {} - if fnptr._obj.graph not in self._seen_gctransformer_hint_close_stack: - self._transform_hint_close_stack(fnptr) - self._seen_gctransformer_hint_close_stack[fnptr._obj.graph] = True - # - livevars = self.push_roots(hop) - self.default(hop) - self.pop_roots(hop, livevars) - - def _transform_hint_close_stack(self, fnptr): + def _transform_hint_close_stack(self, graph): # We cannot easily pass variable amount of arguments of the call # across the call to the pypy_asm_stackwalk helper. So we store # them away and restore them. More precisely, we need to @@ -83,8 +81,8 @@ sradict = self._asmgcc_save_restore_arguments sra = [] # list of pointers to raw-malloced containers for args seen = {} - FUNC1 = lltype.typeOf(fnptr).TO - for TYPE in FUNC1.ARGS: + ARGS = [v.concretetype for v in graph.getargs()] + for TYPE in ARGS: if isinstance(TYPE, lltype.Ptr): TYPE = llmemory.Address num = seen.get(TYPE, 0) @@ -98,8 +96,10 @@ sra.append(sradict[key]) # # make a copy of the graph that will reload the values - graph = fnptr._obj.graph graph2 = copygraph(graph) + del graph2.func # otherwise, start_transforming_graph() will + # again transform graph2, and we get an + # infinite loop # # edit the original graph to only store the value of the arguments block = Block(graph.startblock.inputargs) @@ -116,17 +116,18 @@ SpaceOperation("bare_setfield", [c_p, c_item0, v_arg], v_void)) # # call asm_stackwalk(graph2) - FUNC2 = lltype.FuncType([], FUNC1.RESULT) + RESULT = graph.getreturnvar().concretetype + FUNC2 = lltype.FuncType([], RESULT) fnptr2 = lltype.functionptr(FUNC2, - fnptr._obj._name + '_reload', + graph.name + '_reload', graph=graph2) c_fnptr2 = Constant(fnptr2, lltype.Ptr(FUNC2)) HELPERFUNC = lltype.FuncType([lltype.Ptr(FUNC2), - ASM_FRAMEDATA_HEAD_PTR], FUNC1.RESULT) + ASM_FRAMEDATA_HEAD_PTR], RESULT) v_asm_stackwalk = varoftype(lltype.Ptr(HELPERFUNC), "asm_stackwalk") block.operations.append( SpaceOperation("cast_pointer", [c_asm_stackwalk], v_asm_stackwalk)) - v_result = varoftype(FUNC1.RESULT) + v_result = varoftype(RESULT) block.operations.append( SpaceOperation("indirect_call", [v_asm_stackwalk, c_fnptr2, c_gcrootanchor, diff --git a/rpython/memory/gctransform/transform.py b/rpython/memory/gctransform/transform.py --- a/rpython/memory/gctransform/transform.py +++ b/rpython/memory/gctransform/transform.py @@ -201,6 +201,9 @@ self.var_last_needed_in = None self.curr_block = None + def start_transforming_graph(self, graph): + pass # for asmgcc.py + def transform_graph(self, graph): if graph in self.minimal_transform: if self.minimalgctransformer: @@ -210,6 +213,7 @@ if graph in self.seen_graphs: return self.seen_graphs.add(graph) + self.start_transforming_graph(graph) self.links_to_split = {} # link -> vars to pop_alive across the link diff --git a/rpython/rlib/test/test_rsignal.py b/rpython/rlib/test/test_rsignal.py --- a/rpython/rlib/test/test_rsignal.py +++ b/rpython/rlib/test/test_rsignal.py @@ -1,4 +1,4 @@ -import os, py +import os, py, errno from rpython.translator.c.test.test_genc import compile from rpython.rlib import rsignal @@ -37,3 +37,24 @@ def test_compile(): fn = compile(test_simple, []) fn() + +def test_compile_wakeup_fd(): + def fn(): + rd, wr = os.pipe() + rsignal.pypysig_set_wakeup_fd(wr, False) + for i in range(3): + rsignal.pypysig_setflag(rsignal.SIGUSR1) + os.kill(os.getpid(), rsignal.SIGUSR1) + check(rsignal.SIGUSR1) + check(-1) + check(-1) + x = os.read(rd, 10) + assert x == chr(rsignal.SIGUSR1) * 3 + # + rsignal.pypysig_set_wakeup_fd(rd, False) # can't write there + os.kill(os.getpid(), rsignal.SIGUSR1) + + fn = compile(fn, [], return_stderr=True) + stderr = fn() + assert stderr.endswith('Exception ignored when trying to write to the ' + 'signal wakeup fd: Errno %d\n' % errno.EBADF) diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -539,7 +539,6 @@ 'decode_arg_def': LLOp(canraise=(Exception,)), 'getslice': LLOp(canraise=(Exception,)), 'check_and_clear_exc': LLOp(), - 'call_at_startup': LLOp(canrun=True), 'threadlocalref_addr': LLOp(), # get (or make) addr of tl 'threadlocalref_get': LLOp(sideeffects=False), # read field (no check) diff --git a/rpython/rtyper/lltypesystem/opimpl.py b/rpython/rtyper/lltypesystem/opimpl.py --- a/rpython/rtyper/lltypesystem/opimpl.py +++ b/rpython/rtyper/lltypesystem/opimpl.py @@ -742,9 +742,6 @@ def op_gc_move_out_of_nursery(obj): return obj -def op_call_at_startup(init_func): - pass # do nothing - # ____________________________________________________________ def get_op_impl(opname): diff --git a/rpython/translator/c/database.py b/rpython/translator/c/database.py --- a/rpython/translator/c/database.py +++ b/rpython/translator/c/database.py @@ -60,7 +60,6 @@ self.completed = False self.instrument_ncounter = 0 - self.call_at_startup = set() def gettypedefnode(self, T, varlength=None): if varlength is None: diff --git a/rpython/translator/c/funcgen.py b/rpython/translator/c/funcgen.py --- a/rpython/translator/c/funcgen.py +++ b/rpython/translator/c/funcgen.py @@ -941,18 +941,3 @@ cdecl(typename, ''), self.expr(op.args[0]), self.expr(op.result)) - - def OP_CALL_AT_STARTUP(self, op): - c = op.args[0] - if not isinstance(c, Constant): - # Bah, maybe it comes from a same_as(const) just before... - # Can occur if running without backendopts - for op1 in self._current_block.operations: - if op1.result is op.args[0]: - assert op1.opname == "same_as" - c = op1.args[0] - break - assert isinstance(c, Constant) - func = self.expr(c) - self.db.call_at_startup.add(func) - return '/* call_at_startup %s */' % (func,) diff --git a/rpython/translator/c/src/signals.c b/rpython/translator/c/src/signals.c --- a/rpython/translator/c/src/signals.c +++ b/rpython/translator/c/src/signals.c @@ -2,6 +2,7 @@ #include <limits.h> #include <stdlib.h> +#include <errno.h> #ifdef _WIN32 #include <process.h> #include <io.h> @@ -82,27 +83,51 @@ } } +static void write_str(int fd, const char *p) +{ + int i = 0; + while (p[i] != '\x00') + i++; + (void)write(fd, p, i); +} + static void signal_setflag_handler(int signum) { pypysig_pushback(signum); - if (wakeup_fd != -1) - { + /* Warning, this logic needs to be async-signal-safe */ + if (wakeup_fd != -1) { #ifndef _WIN32 ssize_t res; #else int res; #endif - if (wakeup_with_nul_byte) - { - res = write(wakeup_fd, "\0", 1); - } else { - unsigned char byte = (unsigned char)signum; - res = write(wakeup_fd, &byte, 1); - } - - /* the return value is ignored here */ - } + int old_errno = errno; + retry: + if (wakeup_with_nul_byte) { + res = write(wakeup_fd, "\0", 1); + } else { + unsigned char byte = (unsigned char)signum; + res = write(wakeup_fd, &byte, 1); + } + if (res < 0) { + unsigned int e = (unsigned int)errno; + char c[27], *p; + if (e == EINTR) + goto retry; + write_str(2, "Exception ignored when trying to write to the " + "signal wakeup fd: Errno "); + p = c + sizeof(c); + *--p = 0; + *--p = '\n'; + do { + *--p = '0' + e % 10; + e /= 10; + } while (e != 0); + write_str(2, p); + } + errno = old_errno; + } } void pypysig_setflag(int signum) diff --git a/rpython/translator/c/test/test_standalone.py b/rpython/translator/c/test/test_standalone.py --- a/rpython/translator/c/test/test_standalone.py +++ b/rpython/translator/c/test/test_standalone.py @@ -1063,23 +1063,39 @@ assert out.strip() == expected def test_call_at_startup(self): - from rpython.rtyper.lltypesystem import lltype - from rpython.rtyper.lltypesystem.lloperation import llop - from rpython.rtyper.annlowlevel import llhelper + from rpython.rtyper.extregistry import ExtRegistryEntry + class State: seen = 0 state = State() def startup(): state.seen += 1 - F = lltype.Ptr(lltype.FuncType([], lltype.Void)) + def enablestartup(): + "NOT_RPYTHON" def entry_point(argv): state.seen += 100 assert state.seen == 101 print 'ok' - ll = llhelper(F, startup) - llop.call_at_startup(lltype.Void, ll) + enablestartup() return 0 + class Entry(ExtRegistryEntry): + _about_ = enablestartup + + def compute_result_annotation(self): + bk = self.bookkeeper + s_callable = bk.immutablevalue(startup) + key = (enablestartup,) + bk.emulate_pbc_call(key, s_callable, []) + + def specialize_call(self, hop): + hop.exception_cannot_occur() + bk = hop.rtyper.annotator.bookkeeper + s_callable = bk.immutablevalue(startup) + r_callable = hop.rtyper.getrepr(s_callable) + ll_init = r_callable.get_unique_llfn().value + bk.annotator.translator._call_at_startup.append(ll_init) + t, cbuilder = self.compile(entry_point) out = cbuilder.cmdexec('') assert out.strip() == 'ok' _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit