Author: Ronan Lamy <ronan.l...@gmail.com> Branch: Changeset: r82316:4c30448f0457 Date: 2016-02-17 22:59 +0000 http://bitbucket.org/pypy/pypy/changeset/4c30448f0457/
Log: Merge branch 'llimpl' Refactor register_external(), remove running_on_llinterp mechanism and apply sandbox transform on externals at the end of annotation. diff --git a/rpython/annotator/policy.py b/rpython/annotator/policy.py --- a/rpython/annotator/policy.py +++ b/rpython/annotator/policy.py @@ -3,6 +3,9 @@ from rpython.annotator.specialize import ( specialize_argvalue, specialize_argtype, specialize_arglistitemtype, specialize_arg_or_var, memo, specialize_call_location) +from rpython.flowspace.operation import op +from rpython.flowspace.model import Constant +from rpython.annotator.model import SomeTuple class AnnotatorPolicy(object): @@ -64,7 +67,34 @@ return LowLevelAnnotatorPolicy.specialize__ll_and_arg(*args) def no_more_blocks_to_annotate(pol, annotator): + bk = annotator.bookkeeper # hint to all pending specializers that we are done - for callback in annotator.bookkeeper.pending_specializations: + for callback in bk.pending_specializations: callback() - del annotator.bookkeeper.pending_specializations[:] + del bk.pending_specializations[:] + if annotator.added_blocks is not None: + all_blocks = annotator.added_blocks + else: + all_blocks = annotator.annotated + for block in list(all_blocks): + for i, instr in enumerate(block.operations): + if not isinstance(instr, (op.simple_call, op.call_args)): + continue + v_func = instr.args[0] + s_func = annotator.annotation(v_func) + if not hasattr(s_func, 'needs_sandboxing'): + continue + key = ('sandboxing', s_func.const) + if key not in bk.emulated_pbc_calls: + params_s = s_func.args_s + s_result = s_func.s_result + from rpython.translator.sandbox.rsandbox import make_sandbox_trampoline + sandbox_trampoline = make_sandbox_trampoline( + s_func.name, params_s, s_result) + sandbox_trampoline._signature_ = [SomeTuple(items=params_s)], s_result + bk.emulate_pbc_call(key, bk.immutablevalue(sandbox_trampoline), params_s) + else: + s_trampoline = bk.emulated_pbc_calls[key][0] + sandbox_trampoline = s_trampoline.const + new = instr.replace({instr.args[0]: Constant(sandbox_trampoline)}) + block.operations[i] = new diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py --- a/rpython/annotator/unaryop.py +++ b/rpython/annotator/unaryop.py @@ -113,8 +113,9 @@ @op.simple_call.register(SomeObject) def simple_call_SomeObject(annotator, func, *args): - return annotator.annotation(func).call( - simple_args([annotator.annotation(arg) for arg in args])) + s_func = annotator.annotation(func) + argspec = simple_args([annotator.annotation(arg) for arg in args]) + return s_func.call(argspec) @op.call_args.register_transform(SomeObject) def transform_varargs(annotator, v_func, v_shape, *data_v): diff --git a/rpython/memory/gctransform/test/test_transform.py b/rpython/memory/gctransform/test/test_transform.py --- a/rpython/memory/gctransform/test/test_transform.py +++ b/rpython/memory/gctransform/test/test_transform.py @@ -5,6 +5,7 @@ from rpython.translator.exceptiontransform import ExceptionTransformer from rpython.rtyper.lltypesystem import lltype from rpython.conftest import option +from rpython.rtyper.rtyper import llinterp_backend class LLInterpedTranformerTests: @@ -131,8 +132,10 @@ def rtype(func, inputtypes, specialize=True): t = TranslationContext() t.buildannotator().build_types(func, inputtypes) + rtyper = t.buildrtyper() + rtyper.backend = llinterp_backend if specialize: - t.buildrtyper().specialize() + rtyper.specialize() if option.view: t.view() return t diff --git a/rpython/memory/test/test_transformed_gc.py b/rpython/memory/test/test_transformed_gc.py --- a/rpython/memory/test/test_transformed_gc.py +++ b/rpython/memory/test/test_transformed_gc.py @@ -14,6 +14,7 @@ from rpython.conftest import option from rpython.rlib.rstring import StringBuilder from rpython.rlib.rarithmetic import LONG_BIT +from rpython.rtyper.rtyper import llinterp_backend WORD = LONG_BIT // 8 @@ -29,9 +30,11 @@ t.config.set(**extraconfigopts) ann = t.buildannotator() ann.build_types(func, inputtypes) + rtyper = t.buildrtyper() + rtyper.backend = llinterp_backend if specialize: - t.buildrtyper().specialize() + rtyper.specialize() if backendopt: from rpython.translator.backendopt.all import backend_optimizations backend_optimizations(t) diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py --- a/rpython/rlib/objectmodel.py +++ b/rpython/rlib/objectmodel.py @@ -275,8 +275,6 @@ return lltype.Signed malloc_zero_filled = CDefinedIntSymbolic('MALLOC_ZERO_FILLED', default=0) -running_on_llinterp = CDefinedIntSymbolic('RUNNING_ON_LLINTERP', default=1) -# running_on_llinterp is meant to have the value 0 in all backends # ____________________________________________________________ diff --git a/rpython/rtyper/extfunc.py b/rpython/rtyper/extfunc.py --- a/rpython/rtyper/extfunc.py +++ b/rpython/rtyper/extfunc.py @@ -1,99 +1,105 @@ -from rpython.rtyper.extregistry import ExtRegistryEntry -from rpython.rtyper.lltypesystem.lltype import typeOf, FuncType, functionptr -from rpython.annotator.model import unionof +from rpython.annotator.model import unionof, SomeObject from rpython.annotator.signature import annotation, SignatureError +from rpython.rtyper.extregistry import ExtRegistryEntry, lookup +from rpython.rtyper.lltypesystem.lltype import ( + typeOf, FuncType, functionptr, _ptr, Void) +from rpython.rtyper.error import TyperError +from rpython.rtyper.rmodel import Repr -import py +class SomeExternalFunction(SomeObject): + def __init__(self, name, args_s, s_result): + self.name = name + self.args_s = args_s + self.s_result = s_result + + def check_args(self, callspec): + params_s = self.args_s + args_s, kwargs = callspec.unpack() + if kwargs: + raise SignatureError( + "External functions cannot be called with keyword arguments") + if len(args_s) != len(params_s): + raise SignatureError("Argument number mismatch") + for i, s_param in enumerate(params_s): + arg = unionof(args_s[i], s_param) + if not s_param.contains(arg): + raise SignatureError( + "In call to external function %r:\n" + "arg %d must be %s,\n" + " got %s" % ( + self.name, i + 1, s_param, args_s[i])) + + def call(self, callspec): + self.check_args(callspec) + return self.s_result + + def rtyper_makerepr(self, rtyper): + if not self.is_constant(): + raise TyperError("Non-constant external function!") + entry = lookup(self.const) + impl = getattr(entry, 'lltypeimpl', None) + fakeimpl = getattr(entry, 'lltypefakeimpl', None) + return ExternalFunctionRepr(self, impl, fakeimpl) + + def rtyper_makekey(self): + return self.__class__, self + +class ExternalFunctionRepr(Repr): + lowleveltype = Void + + def __init__(self, s_func, impl, fakeimpl): + self.s_func = s_func + self.impl = impl + self.fakeimpl = fakeimpl + + def rtype_simple_call(self, hop): + rtyper = hop.rtyper + args_r = [rtyper.getrepr(s_arg) for s_arg in self.s_func.args_s] + r_result = rtyper.getrepr(self.s_func.s_result) + obj = self.get_funcptr(rtyper, args_r, r_result) + hop2 = hop.copy() + hop2.r_s_popfirstarg() + vlist = [hop2.inputconst(typeOf(obj), obj)] + hop2.inputargs(*args_r) + hop2.exception_is_here() + return hop2.genop('direct_call', vlist, r_result) + + def get_funcptr(self, rtyper, args_r, r_result): + from rpython.rtyper.rtyper import llinterp_backend + args_ll = [r_arg.lowleveltype for r_arg in args_r] + ll_result = r_result.lowleveltype + name = self.s_func.name + if self.fakeimpl and rtyper.backend is llinterp_backend: + FT = FuncType(args_ll, ll_result) + return functionptr( + FT, name, _external_name=name, _callable=self.fakeimpl) + elif self.impl: + if isinstance(self.impl, _ptr): + return self.impl + else: + # store some attributes to the 'impl' function, where + # the eventual call to rtyper.getcallable() will find them + # and transfer them to the final lltype.functionptr(). + self.impl._llfnobjattrs_ = {'_name': name} + return rtyper.getannmixlevel().delayedfunction( + self.impl, self.s_func.args_s, self.s_func.s_result) + else: + fakeimpl = self.fakeimpl or self.s_func.const + FT = FuncType(args_ll, ll_result) + return functionptr( + FT, name, _external_name=name, _callable=fakeimpl) + class ExtFuncEntry(ExtRegistryEntry): safe_not_sandboxed = False - # common case: args is a list of annotation or types - def normalize_args(self, *args_s): - args = self.signature_args - signature_args = [annotation(arg, None) for arg in args] - assert len(args_s) == len(signature_args),\ - "Argument number mismatch" + def compute_annotation(self): + s_result = SomeExternalFunction( + self.name, self.signature_args, self.signature_result) + if (self.bookkeeper.annotator.translator.config.translation.sandbox + and not self.safe_not_sandboxed): + s_result.needs_sandboxing = True + return s_result - for i, expected in enumerate(signature_args): - arg = unionof(args_s[i], expected) - if not expected.contains(arg): - name = getattr(self, 'name', None) - if not name: - try: - name = self.instance.__name__ - except AttributeError: - name = '?' - raise SignatureError("In call to external function %r:\n" - "arg %d must be %s,\n" - " got %s" % ( - name, i+1, expected, args_s[i])) - return signature_args - - def compute_result_annotation(self, *args_s): - self.normalize_args(*args_s) # check arguments - return self.signature_result - - def specialize_call(self, hop): - rtyper = hop.rtyper - signature_args = self.normalize_args(*hop.args_s) - args_r = [rtyper.getrepr(s_arg) for s_arg in signature_args] - args_ll = [r_arg.lowleveltype for r_arg in args_r] - s_result = hop.s_result - r_result = rtyper.getrepr(s_result) - ll_result = r_result.lowleveltype - name = getattr(self, 'name', None) or self.instance.__name__ - impl = getattr(self, 'lltypeimpl', None) - fakeimpl = getattr(self, 'lltypefakeimpl', self.instance) - if impl: - if (rtyper.annotator.translator.config.translation.sandbox - and not self.safe_not_sandboxed): - from rpython.translator.sandbox.rsandbox import ( - make_sandbox_trampoline) - impl = make_sandbox_trampoline( - self.name, signature_args, s_result) - if hasattr(self, 'lltypefakeimpl'): - # If we have both an llimpl and an llfakeimpl, - # we need a wrapper that selects the proper one and calls it - from rpython.tool.sourcetools import func_with_new_name - # Using '*args' is delicate because this wrapper is also - # created for init-time functions like llarena.arena_malloc - # which are called before the GC is fully initialized - args = ', '.join(['arg%d' % i for i in range(len(args_ll))]) - d = {'original_impl': impl, - 's_result': s_result, - 'fakeimpl': fakeimpl, - '__name__': __name__, - } - exec py.code.compile(""" - from rpython.rlib.objectmodel import running_on_llinterp - from rpython.rlib.debug import llinterpcall - from rpython.rlib.jit import dont_look_inside - # note: we say 'dont_look_inside' mostly because the - # JIT does not support 'running_on_llinterp', but in - # theory it is probably right to stop jitting anyway. - @dont_look_inside - def ll_wrapper(%s): - if running_on_llinterp: - return llinterpcall(s_result, fakeimpl, %s) - else: - return original_impl(%s) - """ % (args, args, args)) in d - impl = func_with_new_name(d['ll_wrapper'], name + '_wrapper') - # store some attributes to the 'impl' function, where - # the eventual call to rtyper.getcallable() will find them - # and transfer them to the final lltype.functionptr(). - impl._llfnobjattrs_ = {'_name': self.name} - obj = rtyper.getannmixlevel().delayedfunction( - impl, signature_args, hop.s_result) - else: - FT = FuncType(args_ll, ll_result) - obj = functionptr(FT, name, _external_name=self.name, - _callable=fakeimpl, - _safe_not_sandboxed=self.safe_not_sandboxed) - vlist = [hop.inputconst(typeOf(obj), obj)] + hop.inputargs(*args_r) - hop.exception_is_here() - return hop.genop('direct_call', vlist, r_result) def register_external(function, args, result=None, export_name=None, llimpl=None, llfakeimpl=None, sandboxsafe=False): @@ -109,32 +115,20 @@ if export_name is None: export_name = function.__name__ + params_s = [annotation(arg) for arg in args] + s_result = annotation(result) class FunEntry(ExtFuncEntry): _about_ = function safe_not_sandboxed = sandboxsafe - - if args is None: - def normalize_args(self, *args_s): - return args_s # accept any argument unmodified - elif callable(args): - # custom annotation normalizer (see e.g. os.utime()) - normalize_args = staticmethod(args) - else: # use common case behavior - signature_args = args - - signature_result = annotation(result, None) + signature_args = params_s + signature_result = s_result name = export_name if llimpl: lltypeimpl = staticmethod(llimpl) if llfakeimpl: lltypefakeimpl = staticmethod(llfakeimpl) - if export_name: - FunEntry.__name__ = export_name - else: - FunEntry.__name__ = function.func_name - def is_external(func): if hasattr(func, 'value'): func = func.value diff --git a/rpython/rtyper/rtyper.py b/rpython/rtyper/rtyper.py --- a/rpython/rtyper/rtyper.py +++ b/rpython/rtyper/rtyper.py @@ -32,11 +32,24 @@ from rpython.translator.sandbox.rsandbox import make_sandbox_trampoline +class RTyperBackend(object): + pass + +class GenCBackend(RTyperBackend): + pass +genc_backend = GenCBackend() + +class LLInterpBackend(RTyperBackend): + pass +llinterp_backend = LLInterpBackend() + + class RPythonTyper(object): from rpython.rtyper.rmodel import log - def __init__(self, annotator): + def __init__(self, annotator, backend=genc_backend): self.annotator = annotator + self.backend = backend self.lowlevel_ann_policy = LowLevelAnnotatorPolicy(self) self.reprs = {} self._reprs_must_call_setup = [] diff --git a/rpython/rtyper/test/test_extfunc.py b/rpython/rtyper/test/test_extfunc.py --- a/rpython/rtyper/test/test_extfunc.py +++ b/rpython/rtyper/test/test_extfunc.py @@ -1,7 +1,6 @@ import py -from rpython.rtyper.extfunc import ExtFuncEntry, register_external,\ - is_external +from rpython.rtyper.extfunc import register_external from rpython.annotator.model import SomeInteger, SomeString, AnnotatorError from rpython.annotator.annrpython import RPythonAnnotator from rpython.annotator.policy import AnnotatorPolicy @@ -19,11 +18,7 @@ "NOT_RPYTHON" return eval("x+40") - class BTestFuncEntry(ExtFuncEntry): - _about_ = b - name = 'b' - signature_args = [SomeInteger()] - signature_result = SomeInteger() + register_external(b, [int], result=int) def f(): return b(2) @@ -43,15 +38,11 @@ def c(y, x): yyy - class CTestFuncEntry(ExtFuncEntry): - _about_ = c - name = 'ccc' - signature_args = [SomeInteger()] * 2 - signature_result = SomeInteger() + def llimpl(y, x): + return y + x - def lltypeimpl(y, x): - return y + x - lltypeimpl = staticmethod(lltypeimpl) + register_external(c, [int, int], result=int, llimpl=llimpl, + export_name='ccc') def f(): return c(3, 4) @@ -59,22 +50,6 @@ res = interpret(f, []) assert res == 7 - def test_register_external_signature(self): - """ - Test the standard interface for external functions. - """ - def dd(): - pass - register_external(dd, [int], int) - - def f(): - return dd(3) - - policy = AnnotatorPolicy() - a = RPythonAnnotator(policy=policy) - s = a.build_types(f, []) - assert isinstance(s, SomeInteger) - def test_register_external_tuple_args(self): """ Verify the annotation of a registered external function which takes a @@ -121,23 +96,6 @@ s = a.build_types(f, []) assert isinstance(s, SomeInteger) - def test_register_external_specialcase(self): - """ - When args=None, the external function accepts any arguments unmodified. - """ - def function_withspecialcase(arg): - return repr(arg) - register_external(function_withspecialcase, args=None, result=str) - - def f(): - x = function_withspecialcase - return x(33) + x("aaa") + x([]) + "\n" - - policy = AnnotatorPolicy() - a = RPythonAnnotator(policy=policy) - s = a.build_types(f, []) - assert isinstance(s, SomeString) - def test_str0(self): str0 = SomeString(no_nul=True) def os_open(s): @@ -182,3 +140,22 @@ # fails with TooLateForChange a.build_types(g, [[str]]) a.build_types(g, [[str0]]) # Does not raise + + def test_register_external_llfakeimpl(self): + def a(i): + return i + def a_llimpl(i): + return i * 2 + def a_llfakeimpl(i): + return i * 3 + register_external(a, [int], int, llimpl=a_llimpl, + llfakeimpl=a_llfakeimpl) + def f(i): + return a(i) + + res = interpret(f, [7]) + assert res == 21 + + from rpython.translator.c.test.test_genc import compile + fc = compile(f, [int]) + assert fc(7) == 14 diff --git a/rpython/rtyper/test/test_llinterp.py b/rpython/rtyper/test/test_llinterp.py --- a/rpython/rtyper/test/test_llinterp.py +++ b/rpython/rtyper/test/test_llinterp.py @@ -13,7 +13,7 @@ from rpython.rlib.rarithmetic import r_uint, ovfcheck from rpython.tool import leakfinder from rpython.conftest import option - +from rpython.rtyper.rtyper import llinterp_backend # switch on logging of interp to show more info on failing tests @@ -39,6 +39,7 @@ t.view() global typer # we need it for find_exception typer = t.buildrtyper() + typer.backend = llinterp_backend typer.specialize() #t.view() t.checkgraphs() diff --git a/rpython/rtyper/test/test_rbuiltin.py b/rpython/rtyper/test/test_rbuiltin.py --- a/rpython/rtyper/test/test_rbuiltin.py +++ b/rpython/rtyper/test/test_rbuiltin.py @@ -3,8 +3,7 @@ import py -from rpython.rlib.debug import llinterpcall -from rpython.rlib.objectmodel import instantiate, running_on_llinterp, compute_unique_id, current_object_addr_as_int +from rpython.rlib.objectmodel import instantiate, compute_unique_id, current_object_addr_as_int from rpython.rlib.rarithmetic import (intmask, longlongmask, r_int64, is_valid_int, r_int, r_uint, r_longlong, r_ulonglong) from rpython.rlib.rstring import StringBuilder, UnicodeBuilder @@ -456,26 +455,6 @@ res = self.interpret(fn, [3.25]) assert res == 7.25 - def test_debug_llinterpcall(self): - S = lltype.Struct('S', ('m', lltype.Signed)) - SPTR = lltype.Ptr(S) - def foo(n): - "NOT_RPYTHON" - s = lltype.malloc(S, immortal=True) - s.m = eval("n*6", locals()) - return s - def fn(n): - if running_on_llinterp: - return llinterpcall(SPTR, foo, n).m - else: - return 321 - res = self.interpret(fn, [7]) - assert res == 42 - from rpython.translator.c.test.test_genc import compile - f = compile(fn, [int]) - res = f(7) - assert res == 321 - def test_id(self): class A: pass diff --git a/rpython/translator/c/node.py b/rpython/translator/c/node.py --- a/rpython/translator/c/node.py +++ b/rpython/translator/c/node.py @@ -913,6 +913,7 @@ return [] def new_funcnode(db, T, obj, forcename=None): + from rpython.rtyper.rtyper import llinterp_backend if db.sandbox: if (getattr(obj, 'external', None) is not None and not obj._safe_not_sandboxed): @@ -934,6 +935,9 @@ return ExternalFuncNode(db, T, obj, name) elif hasattr(obj._callable, "c_name"): return ExternalFuncNode(db, T, obj, name) # this case should only be used for entrypoints + elif db.translator.rtyper.backend is llinterp_backend: + # on llinterp, anything goes + return ExternalFuncNode(db, T, obj, name) else: raise ValueError("don't know how to generate code for %r" % (obj,)) diff --git a/rpython/translator/sandbox/test/test_sandbox.py b/rpython/translator/sandbox/test/test_sandbox.py --- a/rpython/translator/sandbox/test/test_sandbox.py +++ b/rpython/translator/sandbox/test/test_sandbox.py @@ -292,6 +292,21 @@ rescode = pipe.wait() assert rescode == 0 +def test_environ_items(): + def entry_point(argv): + print os.environ.items() + return 0 + + exe = compile(entry_point) + g, f = run_in_subprocess(exe) + expect(f, g, "ll_os.ll_os_envitems", (), []) + expect(f, g, "ll_os.ll_os_write", (1, "[]\n"), 3) + g.close() + tail = f.read() + f.close() + assert tail == "" + + class TestPrintedResults: def run(self, entry_point, args, expected): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit