Author: Brian Kearns <bdkea...@gmail.com> Branch: numpy-refactor Changeset: r69491:8da9e6ab9957 Date: 2014-02-26 23:05 -0500 http://bitbucket.org/pypy/pypy/changeset/8da9e6ab9957/
Log: merge default diff --git a/include/PyPy.h b/include/PyPy.h --- a/include/PyPy.h +++ b/include/PyPy.h @@ -8,9 +8,14 @@ extern "C" { #endif +/* You should call this first once. */ +#define pypy_init(need_threads) do { pypy_asm_stack_bottom(); \ +rpython_startup_code();\ + if (need_threads) pypy_init_threads(); } while (0) -/* You should call this first once. */ +// deprecated interface void rpython_startup_code(void); +void pypy_init_threads(void); /* Initialize the home directory of PyPy. It is necessary to call this. @@ -26,11 +31,10 @@ /* If your program has multiple threads, then you need to call - pypy_init_threads() once at init time, and then pypy_thread_attach() - once in each other thread that just started and in which you want to - run Python code (including via callbacks, see below). + pypy_thread_attach() once in each other thread that just started + and in which you want to run Python code (including via callbacks, + see below). DO NOT CALL IT IN THE MAIN THREAD */ -void pypy_init_threads(void); void pypy_thread_attach(void); diff --git a/pypy/doc/embedding.rst b/pypy/doc/embedding.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/embedding.rst @@ -0,0 +1,101 @@ + +PyPy has a very minimal and a very strange embedding interface, based on +the usage of `cffi`_ and the philosophy that Python is a better language in C. +It was developed in collaboration with Roberto De Ioris from the `uwsgi`_ +project. The `PyPy uwsgi plugin`_ is a good example of usage of such interface. + +The first thing that you need, that we plan to change in the future, is to +compile PyPy yourself with an option ``--shared``. Consult the +`how to compile PyPy`_ doc for details. That should result in ``libpypy.so`` +or ``pypy.dll`` file or something similar, depending on your platform. Consult +your platform specification for details. + +The resulting shared library has very few functions that are however enough +to make a full API working, provided you'll follow a few principles. The API +is: + +.. function:: void pypy_init(int need_threads); + + This is a function that you have to call (once) before calling anything. + It initializes the RPython/PyPy GC and does a bunch of necessary startup + code. This function cannot fail. Pass 1 in case you need thread support, 0 + otherwise. + +.. function:: long pypy_setup_home(char* home, int verbose); + + This is another function that you have to call at some point, without + it you would not be able to find the standard library (and run pretty much + nothing). Arguments: + + * ``home``: null terminated path + + * ``verbose``: if non-zero, would print error messages to stderr + + Function returns 0 on success or 1 on failure, can be called multiple times + until the library is found. + +.. function:: int pypy_execute_source(char* source); + + Execute the source code given in the ``source`` argument. Will print + the error message to stderr upon failure and return 1, otherwise returns 0. + You should really do your own error handling in the source. It'll acquire + the GIL. + +.. function:: void pypy_thread_attach(void); + + In case your application uses threads that are initialized outside of PyPy, + you need to call this function to tell the PyPy GC to track this thread. + Note that this function is not thread-safe itself, so you need to guard it + with a mutex. Do not call it from the main thread. + +Simple example +-------------- + +Note that this API is a lot more minimal than say CPython C API, so at first +it's obvious to think that you can't do much. However, the trick is to do +all the logic in Python and expose it via `cffi`_ callbacks. Let's assume +we're on linux and pypy is put in ``/opt/pypy`` (a source checkout) and +library is in ``/opt/pypy/libpypy-c.so``. We write a little C program +(for simplicity assuming that all operations will be performed:: + + #include "include/PyPy.h" + #include <stdio.h> + + const char source[] = "print 'hello from pypy'"; + + int main() + { + int res; + + rpython_startup_code(); + res = pypy_execute_source((char*)source); + if (res) { + printf("Error calling pypy_execute_source!\n"); + } + return res; + } + +If we save it as ``x.c`` now, compile it and run it with:: + + fijal@hermann:/opt/pypy$ gcc -o x x.c -lpypy-c -L. + fijal@hermann:~/src/pypy$ LD_LIBRARY_PATH=. ./x + hello from pypy + +Worked! + +More advanced example +--------------------- + +Typically we need something more to do than simply execute source. The following +is a fully fledged example, please consult cffi documentation for details. + +xxx + +Threading +--------- + +XXXX I don't understand what's going on, discuss with unbit + +.. _`cffi`: http://cffi.readthedocs.org/ +.. _`uwsgi`: http://uwsgi-docs.readthedocs.org/en/latest/ +.. _`PyPy uwsgi plugin`: http://uwsgi-docs.readthedocs.org/en/latest/PyPy.html diff --git a/pypy/doc/getting-started.rst b/pypy/doc/getting-started.rst --- a/pypy/doc/getting-started.rst +++ b/pypy/doc/getting-started.rst @@ -145,11 +145,13 @@ After you successfully manage to get PyPy's source you can read more about: - `Building and using PyPy's Python interpreter`_ + - `Embedding PyPy`_ - `Learning more about the RPython toolchain and how to develop (with) PyPy`_ - `Tutorial for how to write an interpreter with the RPython toolchain and make it fast`_ - `Look at our benchmark results`_ .. _`Building and using PyPy's Python interpreter`: getting-started-python.html +.. _`Embedding PyPy`: embedding.html .. _`Learning more about the RPython toolchain and how to develop (with) PyPy`: getting-started-dev.html .. _`Tutorial for how to write an interpreter with the RPython toolchain and make it fast`: http://morepypy.blogspot.com/2011/04/tutorial-writing-interpreter-with-pypy.html .. _`Look at our benchmark results`: http://speed.pypy.org diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -82,6 +82,7 @@ from rpython.rlib.entrypoint import entrypoint from rpython.rtyper.lltypesystem import rffi, lltype + from rpython.rtyper.lltypesystem.lloperation import llop w_pathsetter = space.appexec([], """(): def f(path): @@ -93,6 +94,7 @@ @entrypoint('main', [rffi.CCHARP, rffi.INT], c_name='pypy_setup_home') def pypy_setup_home(ll_home, verbose): from pypy.module.sys.initpath import pypy_find_stdlib + llop.gc_stack_bottom(lltype.Void) verbose = rffi.cast(lltype.Signed, verbose) if ll_home: home = rffi.charp2str(ll_home) @@ -120,8 +122,11 @@ @entrypoint('main', [rffi.CCHARP], c_name='pypy_execute_source') def pypy_execute_source(ll_source): + rffi.aroundstate.after() + llop.gc_stack_bottom(lltype.Void) source = rffi.charp2str(ll_source) res = _pypy_execute_source(source) + rffi.aroundstate.before() return rffi.cast(rffi.INT, res) @entrypoint('main', [], c_name='pypy_init_threads') 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 @@ -9,8 +9,7 @@ from pypy.module.cpyext.api import PyObject from pypy.module.micronumpy.interp_numarray import W_NDimArray, array from pypy.module.micronumpy.interp_dtype import get_dtype_cache, W_Dtype -from pypy.module.micronumpy.arrayimpl.concrete import ConcreteArray -from pypy.module.micronumpy.arrayimpl.scalar import Scalar +from pypy.module.micronumpy.concrete import ConcreteArray from rpython.rlib.rawstorage import RAW_STORAGE_PTR NPY_C_CONTIGUOUS = 0x0001 @@ -167,7 +166,7 @@ # void *data = PyArray_DATA(arr); impl = w_array.implementation w_array = W_NDimArray.from_shape(space, [1], impl.dtype) - w_array.implementation.setitem(0, impl.value) + w_array.implementation.setitem(0, impl.getitem(impl.start + 0)) w_array.implementation.shape = [] return w_array @@ -214,12 +213,8 @@ order='C', owning=False, w_subtype=None): shape, dtype = get_shape_and_dtype(space, nd, dims, typenum) storage = rffi.cast(RAW_STORAGE_PTR, data) - if nd == 0: - w_val = dtype.itemtype.box_raw_data(storage) - return W_NDimArray(Scalar(dtype, w_val)) - else: - return W_NDimArray.from_shape_and_storage(space, shape, storage, dtype, - order=order, owning=owning, w_subtype=w_subtype) + return W_NDimArray.from_shape_and_storage(space, shape, storage, dtype, + order=order, owning=owning, w_subtype=w_subtype) @cpython_api([Py_ssize_t, rffi.LONGP, Py_ssize_t], PyObject) diff --git a/pypy/module/cpyext/test/test_ndarrayobject.py b/pypy/module/cpyext/test/test_ndarrayobject.py --- a/pypy/module/cpyext/test/test_ndarrayobject.py +++ b/pypy/module/cpyext/test/test_ndarrayobject.py @@ -77,7 +77,7 @@ def test_FromAny_scalar(self, space, api): a0 = scalar(space) - assert a0.implementation.get_scalar_value().value == 10. + assert a0.get_scalar_value().value == 10. a = api._PyArray_FromAny(a0, NULL, 0, 0, 0, NULL) assert api._PyArray_NDIM(a) == 0 diff --git a/pypy/module/micronumpy/base.py b/pypy/module/micronumpy/base.py --- a/pypy/module/micronumpy/base.py +++ b/pypy/module/micronumpy/base.py @@ -31,7 +31,6 @@ @staticmethod def from_shape(space, shape, dtype, order='C', w_instance=None): from pypy.module.micronumpy import concrete - strides, backstrides = calc_strides(shape, dtype.base, order) impl = concrete.ConcreteArray(shape, dtype.base, order, strides, backstrides) @@ -43,7 +42,6 @@ def from_shape_and_storage(space, shape, storage, dtype, order='C', owning=False, w_subtype=None, w_base=None, writable=True): from pypy.module.micronumpy import concrete - assert shape strides, backstrides = calc_strides(shape, dtype, order) if w_base is not None: if owning: @@ -56,7 +54,6 @@ impl = concrete.ConcreteNonWritableArrayWithBase(shape, dtype, order, strides, backstrides, storage, w_base) - elif owning: # Will free storage when GCd impl = concrete.ConcreteArray(shape, dtype, order, strides, diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py --- a/rpython/jit/codewriter/jtransform.py +++ b/rpython/jit/codewriter/jtransform.py @@ -499,6 +499,16 @@ def rewrite_op_hint(self, op): hints = op.args[1].value + + # hack: if there are both 'promote' and 'promote_string', kill + # one of them based on the type of the value + if hints.get('promote_string') and hints.get('promote'): + hints = hints.copy() + if op.args[0].concretetype == lltype.Ptr(rstr.STR): + del hints['promote'] + else: + del hints['promote_string'] + if hints.get('promote') and op.args[0].concretetype is not lltype.Void: assert op.args[0].concretetype != lltype.Ptr(rstr.STR) kind = getkind(op.args[0].concretetype) diff --git a/rpython/jit/codewriter/test/test_jtransform.py b/rpython/jit/codewriter/test/test_jtransform.py --- a/rpython/jit/codewriter/test/test_jtransform.py +++ b/rpython/jit/codewriter/test/test_jtransform.py @@ -1050,6 +1050,37 @@ assert op1.result == v2 assert op0.opname == '-live-' +def test_double_promote_str(): + PSTR = lltype.Ptr(rstr.STR) + v1 = varoftype(PSTR) + v2 = varoftype(PSTR) + tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) + op1 = SpaceOperation('hint', + [v1, Constant({'promote_string': True}, lltype.Void)], + v2) + op2 = SpaceOperation('hint', + [v1, Constant({'promote_string': True, + 'promote': True}, lltype.Void)], + v2) + lst1 = tr.rewrite_operation(op1) + lst2 = tr.rewrite_operation(op2) + assert lst1 == lst2 + +def test_double_promote_nonstr(): + v1 = varoftype(lltype.Signed) + v2 = varoftype(lltype.Signed) + tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) + op1 = SpaceOperation('hint', + [v1, Constant({'promote': True}, lltype.Void)], + v2) + op2 = SpaceOperation('hint', + [v1, Constant({'promote_string': True, + 'promote': True}, lltype.Void)], + v2) + lst1 = tr.rewrite_operation(op1) + lst2 = tr.rewrite_operation(op2) + assert lst1 == lst2 + def test_unicode_concat(): # test that the oopspec is present and correctly transformed PSTR = lltype.Ptr(rstr.UNICODE) diff --git a/rpython/jit/metainterp/test/test_list.py b/rpython/jit/metainterp/test/test_list.py --- a/rpython/jit/metainterp/test/test_list.py +++ b/rpython/jit/metainterp/test/test_list.py @@ -98,8 +98,8 @@ self.check_resops(setarrayitem_gc=0, call=0, getarrayitem_gc=0) def test_vlist_alloc_and_set(self): - # the check_loops fails, because [non-null] * n is not supported yet - # (it is implemented as a residual call) + # the check_loops fails, because [non-null] * n is only supported + # if n < 15 (otherwise it is implemented as a residual call) jitdriver = JitDriver(greens = [], reds = ['n']) def f(n): l = [1] * 20 @@ -116,7 +116,7 @@ res = self.meta_interp(f, [10], listops=True) assert res == f(10) - py.test.skip("'[non-null] * n' gives a residual call so far") + py.test.skip("'[non-null] * n' for n >= 15 gives a residual call so far") self.check_loops(setarrayitem_gc=0, getarrayitem_gc=0, call=0) def test_arraycopy_simpleoptimize(self): @@ -287,6 +287,74 @@ assert res == 5 self.check_resops(call=0) + def test_list_mul_virtual(self): + class Foo: + def __init__(self, l): + self.l = l + l[0] = self + + myjitdriver = JitDriver(greens = [], reds = ['y']) + def f(y): + while y > 0: + myjitdriver.jit_merge_point(y=y) + Foo([None] * 5) + y -= 1 + return 42 + + self.meta_interp(f, [5]) + self.check_resops({'int_sub': 2, + 'int_gt': 2, + 'guard_true': 2, + 'jump': 1}) + + def test_list_mul_virtual_nonzero(self): + class base: + pass + class Foo(base): + def __init__(self, l): + self.l = l + l[0] = self + class nil(base): + pass + + nil = nil() + + myjitdriver = JitDriver(greens = [], reds = ['y']) + def f(y): + while y > 0: + myjitdriver.jit_merge_point(y=y) + Foo([nil] * 5) + y -= 1 + return 42 + + self.meta_interp(f, [5]) + self.check_resops({'int_sub': 2, + 'int_gt': 2, + 'guard_true': 2, + 'jump': 1}) + + def test_list_mul_unsigned_virtual(self): + from rpython.rlib.rarithmetic import r_uint + + class Foo: + def __init__(self, l): + self.l = l + l[0] = self + + myjitdriver = JitDriver(greens = [], reds = ['y']) + def f(y): + while y > 0: + myjitdriver.jit_merge_point(y=y) + Foo([None] * r_uint(5)) + y -= 1 + return 42 + + self.meta_interp(f, [5]) + self.check_resops({'int_sub': 2, + 'int_gt': 2, + 'guard_true': 2, + 'jump': 1}) + class TestLLtype(ListTests, LLJitMixin): def test_listops_dont_invalidate_caches(self): diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -130,7 +130,9 @@ if promote_args != 'all': args = [args[int(i)] for i in promote_args.split(",")] for arg in args: - code.append(" %s = hint(%s, promote=True)\n" % (arg, arg)) + code.append( #use both hints, and let jtransform pick the right one + " %s = hint(%s, promote=True, promote_string=True)\n" % + (arg, arg)) code.append(" return _orig_func_unlikely_name(%s)\n" % (argstring, )) d = {"_orig_func_unlikely_name": func, "hint": hint} exec py.code.Source("\n".join(code)).compile() in d diff --git a/rpython/rtyper/test/test_generator.py b/rpython/rtyper/test/test_generator.py --- a/rpython/rtyper/test/test_generator.py +++ b/rpython/rtyper/test/test_generator.py @@ -88,3 +88,16 @@ return s res = self.interpret(g, []) assert res == 6 + + def test_send(self): + def f(): + yield (yield 1) + 1 + def g(): + gen = f() + res = f.send(2) + assert res == 1 + res = f.next() + assert res == 3 + + res = self.interpret(g, []) + diff --git a/rpython/rtyper/test/test_rlist.py b/rpython/rtyper/test/test_rlist.py --- a/rpython/rtyper/test/test_rlist.py +++ b/rpython/rtyper/test/test_rlist.py @@ -1619,3 +1619,17 @@ rgc.ll_arraycopy = old_arraycopy # assert 2 <= res <= 10 + + def test_alloc_and_set(self): + def fn(i): + lst = [0] * r_uint(i) + return lst + t, rtyper, graph = self.gengraph(fn, [int]) + block = graph.startblock + seen = 0 + for op in block.operations: + if op.opname in ['cast_int_to_uint', 'cast_uint_to_int']: + continue + assert op.opname == 'direct_call' + seen += 1 + assert seen == 1 diff --git a/rpython/translator/transform.py b/rpython/translator/transform.py --- a/rpython/translator/transform.py +++ b/rpython/translator/transform.py @@ -30,7 +30,7 @@ # [a] * b # --> # c = newlist(a) -# d = mul(c, int b) +# d = mul(c, b) # --> # d = alloc_and_set(b, a) @@ -44,8 +44,7 @@ len(op.args) == 1): length1_lists[op.result] = op.args[0] elif (op.opname == 'mul' and - op.args[0] in length1_lists and - self.gettype(op.args[1]) is int): + op.args[0] in length1_lists): new_op = SpaceOperation('alloc_and_set', (op.args[1], length1_lists[op.args[0]]), op.result) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit