[pypy-commit] pypy kill-multimethod: hg merge remove-remaining-smm
Manuel Jacob
noreply at buildbot.pypy.org
Thu Feb 27 03:00:18 CET 2014
Author: Manuel Jacob
Branch: kill-multimethod
Changeset: r69489:99fb1bea6848
Date: 2014-02-27 02:59 +0100
http://bitbucket.org/pypy/pypy/changeset/99fb1bea6848/
Log: hg merge remove-remaining-smm
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 at hermann:/opt/pypy$ gcc -o x x.c -lpypy-c -L.
+ fijal at 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/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -592,6 +592,17 @@
elif unwrap_spec == [ObjSpace, W_Root, Arguments]:
self.__class__ = BuiltinCodePassThroughArguments1
self.func__args__ = func
+ elif unwrap_spec == [self_type, ObjSpace, Arguments]:
+ self.__class__ = BuiltinCodePassThroughArguments1
+ miniglobals = {'func': func, 'self_type': self_type}
+ d = {}
+ source = """if 1:
+ def _call(space, w_obj, args):
+ self = space.descr_self_interp_w(self_type, w_obj)
+ return func(self, space, args)
+ \n"""
+ exec compile2(source) in miniglobals, d
+ self.func__args__ = d['_call']
else:
self.__class__ = globals()['BuiltinCode%d' % arity]
setattr(self, 'fastfunc_%d' % arity, fastfunc)
diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py
--- a/pypy/interpreter/test/test_gateway.py
+++ b/pypy/interpreter/test/test_gateway.py
@@ -824,6 +824,28 @@
assert len(called) == 1
assert isinstance(called[0], argument.Arguments)
+ def test_pass_trough_arguments_method(self):
+ space = self.space
+
+ called = []
+
+ class W_Something(W_Root):
+ def f(self, space, __args__):
+ called.append(__args__)
+ a_w, _ = __args__.unpack()
+ return space.newtuple([space.wrap('f')]+a_w)
+
+ w_f = space.wrap(gateway.interp2app_temp(W_Something.f))
+
+ w_self = space.wrap(W_Something())
+ args = argument.Arguments(space, [space.wrap(7)])
+
+ w_res = space.call_obj_args(w_f, w_self, args)
+ assert space.is_true(space.eq(w_res, space.wrap(('f', 7))))
+
+ # white-box check for opt
+ assert called[0] is args
+
class AppTestKeywordsToBuiltinSanity(object):
def test_type(self):
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)
More information about the pypy-commit
mailing list