Author: mattip <[email protected]> Branch: release-2.5.x Changeset: r75639:7abc5b13f7f0 Date: 2015-02-02 09:26 +0200 http://bitbucket.org/pypy/pypy/changeset/7abc5b13f7f0/
Log: merge default into branch diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst --- a/pypy/doc/faq.rst +++ b/pypy/doc/faq.rst @@ -197,6 +197,55 @@ (now-dead) object are still true about the new object. + +Would type annotations help PyPy's performance? +----------------------------------------------- + +Two examples of type annotations that are being proposed for improved +performance are `Cython types`__ and `PEP 484 - Type Hints`__. + +.. __: http://docs.cython.org/src/reference/language_basics.html#declaring-data-types +.. __: https://www.python.org/dev/peps/pep-0484/ + +**Cython types** are, by construction, similar to C declarations. For +example, a local variable or an instance attribute can be declared +``"cdef int"`` to force a machine word to be used. This changes the +usual Python semantics (e.g. no overflow checks, and errors when +trying to write other types of objects there). It gives some extra +performance, but the exact benefits are unclear: right now +(January 2015) for example we are investigating a technique that would +store machine-word integers directly on instances, giving part of the +benefits without the user-supplied ``"cdef int"``. + +**PEP 484 - Type Hints,** on the other hand, is almost entirely +useless if you're looking at performance. First, as the name implies, +they are *hints:* they must still be checked at runtime, like PEP 484 +says. Or maybe you're fine with a mode in which you get very obscure +crashes when the type annotations are wrong; but even in that case the +speed benefits would be extremely minor. + +There are several reasons for why. One of them is that annotations +are at the wrong level (e.g. a PEP 484 "int" corresponds to Python 3's +int type, which does not necessarily fits inside one machine word; +even worse, an "int" annotation allows arbitrary int subclasses). +Another is that a lot more information is needed to produce good code +(e.g. "this ``f()`` called here really means this function there, and +will never be monkey-patched" -- same with ``len()`` or ``list()``, +btw). The third reason is that some "guards" in PyPy's JIT traces +don't really have an obvious corresponding type (e.g. "this dict is so +far using keys which don't override ``__hash__`` so a more efficient +implementation was used"). Many guards don't even have any correspondence +with types at all ("this class attribute was not modified"; "the loop +counter did not reach zero so we don't need to release the GIL"; and +so on). + +As PyPy works right now, it is able to derive far more useful +information than can ever be given by PEP 484, and it works +automatically. As far as we know, this is true even if we would add +other techniques to PyPy, like a fast first-pass JIT. + + + .. _`prolog and javascript`: Can I use PyPy's translation toolchain for other languages besides Python? diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1017,6 +1017,9 @@ def newlist_unicode(self, list_u): return self.newlist([self.wrap(u) for u in list_u]) + def newlist_int(self, list_i): + return self.newlist([self.wrap(i) for i in list_i]) + def newlist_hint(self, sizehint): from pypy.objspace.std.listobject import make_empty_list_with_size return make_empty_list_with_size(self, sizehint) diff --git a/pypy/module/cpyext/ndarrayobject.py b/pypy/module/cpyext/ndarrayobject.py --- a/pypy/module/cpyext/ndarrayobject.py +++ b/pypy/module/cpyext/ndarrayobject.py @@ -291,6 +291,6 @@ Py_ssize_t, Py_ssize_t, rffi.CCHARP, rffi.CCHARP, Py_ssize_t], PyObject) def PyUFunc_FromFuncAndData(space, funcs, data, types, ntypes, nin, nout, identity, name, doc, check_return): - w_signature = "" + w_signature = ','.join(['()'] * nin) + '->' + ','.join(['()'] * nout) return do_ufunc(space, funcs, data, types, ntypes, nin, nout, identity, name, doc, check_return, w_signature) diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py --- a/pypy/module/micronumpy/ctors.py +++ b/pypy/module/micronumpy/ctors.py @@ -82,9 +82,18 @@ return w_object.descr_copy(space, w_order) elif not copy and (subok or type(w_object) is W_NDimArray): return w_object - - # not an array or incorrect dtype - shape, elems_w = strides.find_shape_and_elems(space, w_object, dtype) + if isinstance(w_object, W_NDimArray) and copy and not subok: + # TODO do the loop.assign without copying elems_w + shape = w_object.get_shape() + _elems_w = w_object.reshape(space, space.wrap(-1)) + elems_w = [None] * w_object.get_size() + for i in range(len(elems_w)): + elems_w[i] = _elems_w.descr_getitem(space, space.wrap(i)) + if space.is_none(w_dtype): + dtype = w_object.get_dtype() + else: + # not an array + shape, elems_w = strides.find_shape_and_elems(space, w_object, dtype) if dtype is None or (dtype.is_str_or_unicode() and dtype.elsize < 1): dtype = strides.find_dtype_for_seq(space, elems_w, dtype) if dtype is None: diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py --- a/pypy/module/micronumpy/ndarray.py +++ b/pypy/module/micronumpy/ndarray.py @@ -430,9 +430,15 @@ order = 'C' else: order = space.str_w(w_order) + if order == 'K' and is_c_contiguous(self.implementation): + for s in self.implementation.get_strides(): + if s < 0: + break + else: + order = 'C' if order != 'C': raise OperationError(space.w_NotImplementedError, space.wrap( - "order not implemented")) + "order != 'C' only partially implemented")) return self.reshape(space, space.wrap(-1)) @unwrap_spec(w_axis=WrappedDefault(None), diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py --- a/pypy/module/micronumpy/test/test_ndarray.py +++ b/pypy/module/micronumpy/test/test_ndarray.py @@ -2994,6 +2994,7 @@ assert (arange(3).ravel() == arange(3)).all() assert (arange(6).reshape(2, 3).ravel() == arange(6)).all() assert (arange(6).reshape(2, 3).T.ravel() == [0, 3, 1, 4, 2, 5]).all() + assert (arange(3).ravel('K') == arange(3)).all() def test_nonzero(self): from numpy import array diff --git a/pypy/module/micronumpy/test/test_subtype.py b/pypy/module/micronumpy/test/test_subtype.py --- a/pypy/module/micronumpy/test/test_subtype.py +++ b/pypy/module/micronumpy/test/test_subtype.py @@ -304,10 +304,13 @@ out.shape = (sh, 1) else: out.shape = (1, sh) - print 'out, shape was',old_shape,'now',out.shape + #print 'out, shape was',old_shape,'now',out.shape,'out',out return out - a = matrix([[1., 2.]]) + a = matrix([[1., 2.], [3., 4.]]) b = N.array([a]) + assert (b == a).all() + b = N.array(a) + assert len(b.shape) == 2 def test_setstate_no_version(self): # Some subclasses of ndarray, like MaskedArray, do not use diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py --- a/pypy/module/micronumpy/test/test_ufuncs.py +++ b/pypy/module/micronumpy/test/test_ufuncs.py @@ -7,6 +7,7 @@ from pypy.module.micronumpy.concrete import VoidBoxStorage from pypy.interpreter.gateway import interp2app from pypy.conftest import option +from pypy.interpreter.error import OperationError class TestUfuncCoercion(object): @@ -129,7 +130,10 @@ '', ufunc.dtypes) assert index == 0 assert dtypes == [f32_dtype, c64_dtype] - + raises(OperationError, ufunc.type_resolver, space, [f32_array], [None], + 'u->u', ufunc.dtypes) + exc = raises(OperationError, ufunc.type_resolver, space, [f32_array], [None], + 'i->i', ufunc.dtypes) class AppTestUfuncs(BaseNumpyAppTest): def test_constants(self): @@ -169,8 +173,7 @@ dtypes=[int, int, int, float, float, float]) int_func22 = frompyfunc([int, int], 2, 2, signature='(i),(i)->(i),(i)', dtypes=['match']) - int_func12 = frompyfunc([int], 1, 2, signature='(i)->(i),(i)', - dtypes=['match']) + int_func12 = frompyfunc([int], 1, 2, dtypes=['match']) retype = dtype(int) a = arange(10) assert isinstance(adder_ufunc1, ufunc) @@ -223,6 +226,7 @@ assert len(in_array.shape) == 2 assert in_array.shape == out_array.shape out_array[:] = in_array * 2 + from numpy import frompyfunc, dtype, arange ufunc = frompyfunc([times_2], 1, 1, signature='(m,n)->(n,m)', @@ -233,6 +237,7 @@ ai3 = ufunc(ai[0,:,:]) ai2 = ufunc(ai) assert (ai2 == ai * 2).all() + ufunc = frompyfunc([times_2], 1, 1, signature='(m,m)->(m,m)', dtypes=[dtype(int), dtype(int)], @@ -245,6 +250,21 @@ ai2 = ufunc(ai) assert (ai2 == ai * 2).all() + def test_frompyfunc_needs_nditer(self): + def summer(in0): + print 'in summer, in0=',in0,'in0.shape=',in0.shape + return in0.sum() + + from numpy import frompyfunc, dtype, arange + ufunc = frompyfunc([summer], 1, 1, + signature='(m,m)->()', + dtypes=[dtype(int), dtype(int)], + stack_inputs=False, + ) + ai = arange(12, dtype=int).reshape(3, 2, 2) + ao = ufunc(ai) + assert ao.size == 3 + def test_frompyfunc_sig_broadcast(self): def sum_along_0(in_array, out_array): out_array[...] = in_array.sum(axis=0) diff --git a/pypy/module/micronumpy/ufuncs.py b/pypy/module/micronumpy/ufuncs.py --- a/pypy/module/micronumpy/ufuncs.py +++ b/pypy/module/micronumpy/ufuncs.py @@ -520,7 +520,9 @@ ''' _immutable_fields_ = ["funcs", "dtypes", "data", "match_dtypes"] - def __init__(self, space, funcs, name, identity, nin, nout, dtypes, signature, match_dtypes=False, stack_inputs=False): + def __init__(self, space, funcs, name, identity, nin, nout, dtypes, + signature, match_dtypes=False, stack_inputs=False, + external_loop=False): # XXX make sure funcs, signature, dtypes, nin, nout are consistent # These don't matter, we use the signature and dtypes for determining @@ -549,6 +551,7 @@ self.core_num_dims = [0] * self.nargs # number of core dimensions of each nargs self.core_offsets = [0] * self.nargs self.core_dim_ixs = [] # indices into unique shapes for each arg + self.external_loop = external_loop def reduce(self, space, w_obj, w_axis, keepdims=False, out=None, dtype=None, cumulative=False): @@ -586,29 +589,21 @@ _dtypes.append(_dtypes[0]) index, dtypes = self.type_resolver(space, inargs, outargs, sig, _dtypes) func = self.funcs[index] - if not self.core_enabled: - # func is going to do all the work, it must accept W_NDimArray args - inargs0 = inargs[0] - assert isinstance(inargs0, W_NDimArray) - arg_shapes = [inargs0.get_shape()] * self.nargs - inargs, outargs, need_to_cast = self.alloc_args(space, inargs, outargs, - dtypes, arg_shapes) - for tf in need_to_cast: - if tf: - raise oefmt(space.w_NotImplementedError, "casting not supported yet") - if self.stack_inputs: - arglist = space.newlist(list(inargs + outargs)) - space.call_args(func, Arguments.frompacked(space, arglist)) - else: - arglist = space.newlist(inargs) - outargs = space.call_args(func, Arguments.frompacked(space, arglist)) - return outargs - if len(outargs) < 2: - return outargs[0] - return space.newtuple(outargs) iter_shape, arg_shapes, matched_dims = self.verify_args(space, inargs, outargs) inargs, outargs, need_to_cast = self.alloc_args(space, inargs, outargs, dtypes, arg_shapes) + if not self.external_loop: + inargs0 = inargs[0] + outargs0 = outargs[0] + assert isinstance(inargs0, W_NDimArray) + assert isinstance(outargs0, W_NDimArray) + res_dtype = outargs0.get_dtype() + new_shape = inargs0.get_shape() + if len(outargs) < 2: + return loop.call_many_to_one(space, new_shape, func, + res_dtype, inargs, outargs[0]) + return loop.call_many_to_many(space, new_shape, func, + res_dtype, inargs, outargs) for tf in need_to_cast: if tf: raise oefmt(space.w_NotImplementedError, "casting not supported yet") @@ -619,44 +614,44 @@ w_casting = space.w_None w_op_axes = space.w_None + #print '\nsignature', sig + #print [(d, getattr(self,d)) for d in dir(self) if 'core' in d or 'broad' in d] + #print [(d, locals()[d]) for d in locals() if 'core' in d or 'broad' in d] + #print 'shapes',[d.get_shape() for d in inargs + outargs] + #print 'steps',[d.implementation.strides for d in inargs + outargs] + if isinstance(func, W_GenericUFuncCaller): + # Use GeneralizeUfunc interface with signature + # Unlike numpy, we will not broadcast dims before + # the core_ndims rather we use nditer iteration + # so dims[0] == 1 + dims = [1] + matched_dims + steps = [] + allargs = inargs + outargs + for i in range(len(allargs)): + steps.append(0) + for i in range(len(allargs)): + _arg = allargs[i] + assert isinstance(_arg, W_NDimArray) + start_dim = len(iter_shape) + steps += _arg.implementation.strides[start_dim:] + func.set_dims_and_steps(space, dims, steps) + else: + # it is a function, ready to be called by the iterator, + # from frompyfunc + pass + # mimic NpyIter_AdvancedNew with a nditer + w_itershape = space.newlist([space.wrap(i) for i in iter_shape]) + nd_it = W_NDIter(space, space.newlist(inargs + outargs), w_flags, + w_op_flags, w_op_dtypes, w_casting, w_op_axes, + w_itershape) + # coalesce each iterators, according to inner_dimensions + for i in range(len(inargs) + len(outargs)): + for j in range(self.core_num_dims[i]): + new_iter = coalesce_iter(nd_it.iters[i][0], nd_it.op_flags[i], + nd_it, nd_it.order, flat=False) + nd_it.iters[i] = (new_iter, new_iter.reset()) + # do the iteration if self.stack_inputs: - #print '\nsignature', sig - #print [(d, getattr(self,d)) for d in dir(self) if 'core' in d or 'broad' in d] - #print [(d, locals()[d]) for d in locals() if 'core' in d or 'broad' in d] - #print 'shapes',[d.get_shape() for d in inargs + outargs] - #print 'steps',[d.implementation.strides for d in inargs + outargs] - if isinstance(func, W_GenericUFuncCaller): - # Use GeneralizeUfunc interface with signature - # Unlike numpy, we will not broadcast dims before - # the core_ndims rather we use nditer iteration - # so dims[0] == 1 - dims = [1] + matched_dims - steps = [] - allargs = inargs + outargs - for i in range(len(allargs)): - steps.append(0) - for i in range(len(allargs)): - _arg = allargs[i] - assert isinstance(_arg, W_NDimArray) - start_dim = len(iter_shape) - steps += _arg.implementation.strides[start_dim:] - func.set_dims_and_steps(space, dims, steps) - else: - # it is a function, ready to be called by the iterator, - # from frompyfunc - pass - # mimic NpyIter_AdvancedNew with a nditer - w_itershape = space.newlist([space.wrap(i) for i in iter_shape]) - nd_it = W_NDIter(space, space.newlist(inargs + outargs), w_flags, - w_op_flags, w_op_dtypes, w_casting, w_op_axes, - w_itershape) - # coalesce each iterators, according to inner_dimensions - for i in range(len(inargs) + len(outargs)): - for j in range(self.core_num_dims[i]): - new_iter = coalesce_iter(nd_it.iters[i][0], nd_it.op_flags[i], - nd_it, nd_it.order, flat=False) - nd_it.iters[i] = (new_iter, new_iter.reset()) - # do the iteration while not nd_it.done: # XXX jit me for it, st in nd_it.iters: @@ -670,20 +665,35 @@ args.append(nd_it.getitem(it, st)) nd_it.iters[i] = (it, it.next(st)) space.call_args(func, Arguments.frompacked(space, space.newlist(args))) - if len(outargs) > 1: - return space.newtuple([convert_to_array(space, o) for o in outargs]) - return outargs[0] - inargs0 = inargs[0] - outargs0 = outargs[0] - assert isinstance(inargs0, W_NDimArray) - assert isinstance(outargs0, W_NDimArray) - res_dtype = outargs0.get_dtype() - new_shape = inargs0.get_shape() - if len(outargs) < 2: - return loop.call_many_to_one(space, new_shape, func, - res_dtype, inargs, outargs[0]) - return loop.call_many_to_many(space, new_shape, func, - res_dtype, inargs, outargs) + else: + # do the iteration + while not nd_it.done: + # XXX jit me + for it, st in nd_it.iters: + if not it.done(st): + break + else: + nd_it.done = True + break + initers = [] + outiters = [] + nin = len(inargs) + for i, (it, st) in enumerate(nd_it.iters[:nin]): + initers.append(nd_it.getitem(it, st)) + nd_it.iters[i] = (it, it.next(st)) + for i, (it, st) in enumerate(nd_it.iters[nin:]): + outiters.append(nd_it.getitem(it, st)) + nd_it.iters[i + nin] = (it, it.next(st)) + outs = space.call_args(func, Arguments.frompacked(space, space.newlist(initers))) + if len(outiters) < 2: + outiters[0].descr_setitem(space, space.w_Ellipsis, outs) + else: + for i in range(self.nout): + w_val = space.getitem(outs, space.wrap(i)) + outiters[i].descr_setitem(space, space.w_Ellipsis, w_val) + if len(outargs) > 1: + return space.newtuple([convert_to_array(space, o) for o in outargs]) + return outargs[0] def parse_kwargs(self, space, kwargs_w): w_subok, w_out, casting, sig, extobj = \ @@ -723,20 +733,24 @@ nop = len(inargs) + len(outargs) dtypes = [] if isinstance(type_tup, str) and len(type_tup) > 0: - if len(type_tup) == 1: - dtypes = [get_dtype_cache(space).dtypes_by_name[type_tup]] * self.nargs - elif len(type_tup) == self.nargs + 2: - for i in range(self.nin): - dtypes.append(get_dtype_cache(space).dtypes_by_name[type_tup[i]]) - #skip the '->' in the signature - for i in range(self.nout): - j = i + self.nin + 2 - dtypes.append(get_dtype_cache(space).dtypes_by_name[type_tup[j]]) - else: - raise oefmt(space.w_TypeError, "a type-string for %s " \ - "requires 1 typecode or %d typecode(s) before and %d" \ - " after the -> sign, not '%s'", self.name, self.nin, - self.nout, type_tup) + try: + if len(type_tup) == 1: + dtypes = [get_dtype_cache(space).dtypes_by_name[type_tup]] * self.nargs + elif len(type_tup) == self.nargs + 2: + for i in range(self.nin): + dtypes.append(get_dtype_cache(space).dtypes_by_name[type_tup[i]]) + #skip the '->' in the signature + for i in range(self.nout): + j = i + self.nin + 2 + dtypes.append(get_dtype_cache(space).dtypes_by_name[type_tup[j]]) + else: + raise oefmt(space.w_TypeError, "a type-string for %s " \ + "requires 1 typecode or %d typecode(s) before and %d" \ + " after the -> sign, not '%s'", self.name, self.nin, + self.nout, type_tup) + except KeyError: + raise oefmt(space.w_ValueError, "unknown typecode in" \ + " call to %s with type-string '%s'", self.name, type_tup) else: # XXX why does the next line not pass translation? # dtypes = [i.get_dtype() for i in inargs] @@ -760,9 +774,13 @@ break else: if len(self.funcs) > 1: + dtypesstr = ','.join(['%s%s%s' % (d.byteorder, d.kind, d.elsize) \ + for d in dtypes]) + _dtypesstr = ','.join(['%s%s%s' % (d.byteorder, d.kind, d.elsize) \ + for d in _dtypes]) raise oefmt(space.w_TypeError, - 'input dtype did not match any known dtypes', - ) + "input dtype [%s] did not match any known dtypes [%s] ", + dtypesstr,_dtypesstr) i = 0 # Fill in empty dtypes for j in range(self.nargs): @@ -1235,7 +1253,8 @@ w_identity=None, name='', doc='', stack_inputs=False): ''' frompyfunc(func, nin, nout) #cpython numpy compatible frompyfunc(func, nin, nout, dtypes=None, signature='', - identity=None, name='', doc='', stack_inputs=False) + identity=None, name='', doc='', + stack_inputs=False) Takes an arbitrary Python function and returns a ufunc. @@ -1251,10 +1270,12 @@ nout : int The number of arrays returned by `func`. dtypes: None or [dtype, ...] of the input, output args for each function, - or 'match' to force output to exactly match input dtype + or 'match' to force output to exactly match input dtype + Note that 'match' is a pypy-only extension to allow non-object + return dtypes signature*: str, default='' The mapping of input args to output args, defining the - inner-loop indexing + inner-loop indexing. If it is empty, the func operates on scalars identity*: None (default) or int For reduce-type ufuncs, the default value name: str, default='' @@ -1273,7 +1294,7 @@ Notes ----- - If the signature and out_dtype are both missing, the returned ufunc + If the signature and dtype are both missing, the returned ufunc always returns PyObject arrays (cpython numpy compatability). Input arguments marked with a * are pypy-only extensions @@ -1329,16 +1350,15 @@ 'identity must be None or an int') if len(signature) == 0: - # cpython compatability, func is of the form (),()->() - signature = ','.join(['()'] * nin) + '->' + ','.join(['()'] * nout) + external_loop=False else: - #stack_inputs = True - pass + external_loop=True w_ret = W_UfuncGeneric(space, func, name, identity, nin, nout, dtypes, signature, match_dtypes=match_dtypes, - stack_inputs=stack_inputs) - _parse_signature(space, w_ret, w_ret.signature) + stack_inputs=stack_inputs, external_loop=external_loop) + if w_ret.external_loop: + _parse_signature(space, w_ret, w_ret.signature) if doc: w_ret.w_doc = space.wrap(doc) return w_ret diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py --- a/pypy/module/pypyjit/interp_jit.py +++ b/pypy/module/pypyjit/interp_jit.py @@ -173,7 +173,7 @@ exiting (blackhole) steps, but just not from the final assembler. Note that the return value of the callable is ignored, because -there is no reasonable way to guess what it sound be in case the +there is no reasonable way to guess what it should be in case the function is not called. This is meant to be used notably in sys.settrace() for coverage- diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py --- a/pypy/objspace/std/dictmultiobject.py +++ b/pypy/objspace/std/dictmultiobject.py @@ -1173,8 +1173,8 @@ def wrapkey(space, key): return space.wrap(key) - # XXX there is no space.newlist_int yet to implement w_keys more - # efficiently + def w_keys(self, w_dict): + return self.space.newlist_int(self.listview_int(w_dict)) create_iterator_classes(IntDictStrategy) diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -176,6 +176,12 @@ storage = strategy.erase(list_u) return W_ListObject.from_storage_and_strategy(space, storage, strategy) + @staticmethod + def newlist_int(space, list_i): + strategy = space.fromcache(IntegerListStrategy) + storage = strategy.erase(list_i) + return W_ListObject.from_storage_and_strategy(space, storage, strategy) + def __repr__(self): """ representation for debugging purposes """ return "%s(%s, %s)" % (self.__class__.__name__, self.strategy, diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -288,6 +288,9 @@ def newlist_unicode(self, list_u): return W_ListObject.newlist_unicode(self, list_u) + def newlist_int(self, list_i): + return W_ListObject.newlist_int(self, list_i) + def newdict(self, module=False, instance=False, kwargs=False, strdict=False): return W_DictMultiObject.allocate_and_init_instance( diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py --- a/rpython/rtyper/rclass.py +++ b/rpython/rtyper/rclass.py @@ -711,8 +711,15 @@ continue value = self.classdef.classdesc.read_attribute(fldname, None) if value is not None: - cvalue = inputconst(r.lowleveltype, - r.convert_desc_or_const(value)) + ll_value = r.convert_desc_or_const(value) + # don't write NULL GC pointers: we know that the malloc + # done above initialized at least the GC Ptr fields to + # NULL already, and that's true for all our GCs + if (isinstance(r.lowleveltype, Ptr) and + r.lowleveltype.TO._gckind == 'gc' and + not ll_value): + continue + cvalue = inputconst(r.lowleveltype, ll_value) self.setfield(vptr, fldname, cvalue, llops, flags={'access_directly': True}) return vptr diff --git a/rpython/translator/backendopt/test/test_malloc.py b/rpython/translator/backendopt/test/test_malloc.py --- a/rpython/translator/backendopt/test/test_malloc.py +++ b/rpython/translator/backendopt/test/test_malloc.py @@ -340,3 +340,15 @@ u[0].s.x = x return u[0].s.x graph = self.check(f, [int], [42], 42) + + def test_two_paths_one_with_constant(self): + py.test.skip("XXX implement me?") + def fn(n): + if n > 100: + tup = (0,) + else: + tup = (n,) + (n,) # <- flowspace + return tup[0] + + self.check(fn, [int], [42], 42) 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 @@ -808,12 +808,7 @@ t, cbuilder = self.compile(entry_point, shared=True) assert cbuilder.shared_library_name is not None assert cbuilder.shared_library_name != cbuilder.executable_name - if os.name == 'posix': - library_path = cbuilder.shared_library_name.dirpath() - if sys.platform == 'darwin': - monkeypatch.setenv('DYLD_LIBRARY_PATH', library_path) - else: - monkeypatch.setenv('LD_LIBRARY_PATH', library_path) + #Do not set LD_LIBRARY_PATH, make sure $ORIGIN flag is working out, err = cbuilder.cmdexec("a b") assert out == "3" diff --git a/rpython/translator/platform/freebsd.py b/rpython/translator/platform/freebsd.py --- a/rpython/translator/platform/freebsd.py +++ b/rpython/translator/platform/freebsd.py @@ -12,6 +12,7 @@ cflags = tuple( ['-O3', '-pthread', '-fomit-frame-pointer'] + os.environ.get('CFLAGS', '').split()) + rpath_flags = ['-Wl,-rpath=\'$$ORIGIN/\'', '-Wl,-z,origin'] class Freebsd_64(Freebsd): shared_only = ('-fPIC',) diff --git a/rpython/translator/platform/posix.py b/rpython/translator/platform/posix.py --- a/rpython/translator/platform/posix.py +++ b/rpython/translator/platform/posix.py @@ -112,9 +112,9 @@ target_name = exe_name.basename if shared: - cflags = self.cflags + self.get_shared_only_compile_flags() + cflags = tuple(self.cflags) + self.get_shared_only_compile_flags() else: - cflags = self.cflags + self.standalone_only + cflags = tuple(self.cflags) + tuple(self.standalone_only) m = GnuMakefile(path) m.exe_name = path.join(exe_name.basename) _______________________________________________ pypy-commit mailing list [email protected] https://mail.python.org/mailman/listinfo/pypy-commit
