Re: [pypy-dev] [pypy-commit] pypy default: Merge a branch that makes space.isinstance(w_obj, <a constant>) do a fastpath
Hi Maciek, The objspace part of this merge really needs tests! You should write tests that the .interplevel_cls attribute is set, and that calling isinstance_w actually goes through the fast path. Cheers, Carl Friedrich On 09/29/2011 04:40 AM, fijal wrote:
Author: Maciej Fijalkowski<fijall@gmail.com> Branch: Changeset: r47667:ffbf1bcf89d6 Date: 2011-09-28 23:39 -0300 http://bitbucket.org/pypy/pypy/changeset/ffbf1bcf89d6/
Log: Merge a branch that makes space.isinstance(w_obj,<a constant>) do a fastpath with isinstance(w_obj,<an RPython class representing the constant>)
diff --git a/pypy/annotation/policy.py b/pypy/annotation/policy.py --- a/pypy/annotation/policy.py +++ b/pypy/annotation/policy.py @@ -1,6 +1,6 @@ # base annotation policy for specialization from pypy.annotation.specialize import default_specialize as default -from pypy.annotation.specialize import specialize_argvalue, specialize_argtype, specialize_arglistitemtype +from pypy.annotation.specialize import specialize_argvalue, specialize_argtype, specialize_arglistitemtype, specialize_arg_or_var from pypy.annotation.specialize import memo, specialize_call_location # for some reason, model must be imported first, # or we create a cycle. @@ -73,6 +73,7 @@ default_specialize = staticmethod(default) specialize__memo = staticmethod(memo) specialize__arg = staticmethod(specialize_argvalue) # specialize:arg(N) + specialize__arg_or_var = staticmethod(specialize_arg_or_var) specialize__argtype = staticmethod(specialize_argtype) # specialize:argtype(N) specialize__arglistitemtype = staticmethod(specialize_arglistitemtype) specialize__call_location = staticmethod(specialize_call_location) diff --git a/pypy/annotation/specialize.py b/pypy/annotation/specialize.py --- a/pypy/annotation/specialize.py +++ b/pypy/annotation/specialize.py @@ -353,6 +353,16 @@ key = tuple(key) return maybe_star_args(funcdesc, key, args_s)
+def specialize_arg_or_var(funcdesc, args_s, *argindices): + for argno in argindices: + if not args_s[argno].is_constant(): + break + else: + # all constant + return specialize_argvalue(funcdesc, args_s, *argindices) + # some not constant + return maybe_star_args(funcdesc, None, args_s) + def specialize_argtype(funcdesc, args_s, *argindices): key = tuple([args_s[i].knowntype for i in argindices]) for cls in key: diff --git a/pypy/annotation/test/test_annrpython.py b/pypy/annotation/test/test_annrpython.py --- a/pypy/annotation/test/test_annrpython.py +++ b/pypy/annotation/test/test_annrpython.py @@ -1194,6 +1194,20 @@ assert len(executedesc._cache[(0, 'star', 2)].startblock.inputargs) == 4 assert len(executedesc._cache[(1, 'star', 3)].startblock.inputargs) == 5
+ def test_specialize_arg_or_var(self): + def f(a): + return 1 + f._annspecialcase_ = 'specialize:arg_or_var(0)' + + def fn(a): + return f(3) + f(a) + + a = self.RPythonAnnotator() + a.build_types(fn, [int]) + executedesc = a.bookkeeper.getdesc(f) + assert sorted(executedesc._cache.keys()) == [None, (3,)] + # we got two different special + def test_specialize_call_location(self): def g(a): return a diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py --- a/pypy/objspace/descroperation.py +++ b/pypy/objspace/descroperation.py @@ -6,6 +6,7 @@ from pypy.interpreter.typedef import default_identity_hash from pypy.tool.sourcetools import compile2, func_with_new_name from pypy.module.__builtin__.interp_classobj import W_InstanceObject +from pypy.rlib.objectmodel import specialize
def object_getattribute(space): "Utility that returns the app-level descriptor object.__getattribute__." @@ -507,6 +508,7 @@ def issubtype(space, w_sub, w_type): return space._type_issubtype(w_sub, w_type)
+ @specialize.arg_or_var(2) def isinstance(space, w_inst, w_type): return space.wrap(space._type_isinstance(w_inst, w_type))
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 @@ -7,7 +7,7 @@ from pypy.objspace.std import (builtinshortcut, stdtypedef, frame, model, transparent, callmethod, proxyobject) from pypy.objspace.descroperation import DescrOperation, raiseattrerror -from pypy.rlib.objectmodel import instantiate, r_dict, specialize +from pypy.rlib.objectmodel import instantiate, r_dict, specialize, is_constant from pypy.rlib.debug import make_sure_not_resized from pypy.rlib.rarithmetic import base_int, widen from pypy.rlib.objectmodel import we_are_translated @@ -83,6 +83,12 @@ if self.config.objspace.std.withtproxy: transparent.setup(self)
+ for type, classes in self.model.typeorder.iteritems(): + if len(classes) == 3: + # W_Root, AnyXxx and actual object + self.gettypefor(type).interplevel_cls = classes[0][0] + + def get_builtin_types(self): return self.builtin_types
@@ -567,10 +573,19 @@ return self.wrap(w_sub.issubtype(w_type)) raise OperationError(self.w_TypeError, self.wrap("need type objects"))
+ @specialize.arg_or_var(2) def _type_isinstance(self, w_inst, w_type): - if isinstance(w_type, W_TypeObject): - return self.type(w_inst).issubtype(w_type) - raise OperationError(self.w_TypeError, self.wrap("need type object")) + if not isinstance(w_type, W_TypeObject): + raise OperationError(self.w_TypeError, + self.wrap("need type object")) + if is_constant(w_type): + cls = w_type.interplevel_cls + if cls is not None: + assert w_inst is not None + if isinstance(w_inst, cls): + return True + return self.type(w_inst).issubtype(w_type)
+ @specialize.arg_or_var(2) def isinstance_w(space, w_inst, w_type): return space._type_isinstance(w_inst, w_type) diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -115,6 +115,9 @@ # of the __new__ is an instance of the type w_bltin_new = None
+ interplevel_cls = None # not None for prebuilt instances of + # interpreter-level types + @dont_look_inside def __init__(w_self, space, name, bases_w, dict_w, overridetypedef=None): diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py --- a/pypy/rlib/objectmodel.py +++ b/pypy/rlib/objectmodel.py @@ -46,6 +46,17 @@
return decorated_func
+ def arg_or_var(self, *args): + """ Same as arg, but additionally allow for a 'variable' annotation, + that would simply be a situation where designated arg is not + a constant + """ + def decorated_func(func): + func._annspecialcase_ = 'specialize:arg_or_var' + self._wrap(args) + return func + + return decorated_func + def argtype(self, *args): """ Specialize function based on types of arguments on given positions.
@@ -165,6 +176,22 @@ def keepalive_until_here(*values): pass
+def is_constant(thing): + return True + +class Entry(ExtRegistryEntry): + _about_ = is_constant + + def compute_result_annotation(self, s_arg): + from pypy.annotation import model + r = model.SomeBool() + r.const = s_arg.is_constant() + return r + + def specialize_call(self, hop): + from pypy.rpython.lltypesystem import lltype + return hop.inputconst(lltype.Bool, hop.s_result.const) + # ____________________________________________________________
class FREED_OBJECT(object): diff --git a/pypy/rlib/test/test_objectmodel.py b/pypy/rlib/test/test_objectmodel.py --- a/pypy/rlib/test/test_objectmodel.py +++ b/pypy/rlib/test/test_objectmodel.py @@ -339,6 +339,19 @@ res = self.interpret(f, [42]) assert res == 84
+ def test_isconstant(self): + from pypy.rlib.objectmodel import is_constant, specialize + + @specialize.arg_or_var(0) + def f(arg): + if is_constant(arg): + return 1 + return 10 + + def fn(arg): + return f(arg) + f(3) + + assert self.interpret(fn, [15]) == 11
class TestLLtype(BaseTestObjectModel, LLRtypeMixin):
@@ -451,5 +464,4 @@ if llop.opname == 'malloc_varsize': break assert llop.args[2] is graph.startblock.inputargs[0] -
_______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit
Hi Maciek, On Thu, Sep 29, 2011 at 10:33, Carl Friedrich Bolz <cfbolz@gmx.de> wrote:
+class Entry(ExtRegistryEntry): + _about_ = is_constant + + def compute_result_annotation(self, s_arg): + from pypy.annotation import model + r = model.SomeBool() + r.const = s_arg.is_constant() + return r
This is wrong. We tried at some point to have is_constant() but failed. The issue is that when calling is_constant(x), even if 'x' turns out not to be constant in the end, it's possible that its initial annotation says that it is a constant. In this case, crash, because you return "SomeBool(const=True)" and later "SomeBool(const=False)", which is not a superset of the previous value. Try with a test like this: def f(n): is_constant(n) def g(n): f(5) f(n) A bientôt, Armin.
On Thu, Sep 29, 2011 at 5:51 AM, Armin Rigo <arigo@tunes.org> wrote:
Hi Maciek,
On Thu, Sep 29, 2011 at 10:33, Carl Friedrich Bolz <cfbolz@gmx.de> wrote:
+class Entry(ExtRegistryEntry): + _about_ = is_constant + + def compute_result_annotation(self, s_arg): + from pypy.annotation import model + r = model.SomeBool() + r.const = s_arg.is_constant() + return r
This is wrong. We tried at some point to have is_constant() but failed. The issue is that when calling is_constant(x), even if 'x' turns out not to be constant in the end, it's possible that its initial annotation says that it is a constant. In this case, crash, because you return "SomeBool(const=True)" and later "SomeBool(const=False)", which is not a superset of the previous value.
Try with a test like this:
def f(n): is_constant(n) def g(n): f(5) f(n)
A bientôt,
Armin. _______________________________________________ pypy-dev mailing list pypy-dev@python.org http://mail.python.org/mailman/listinfo/pypy-dev
It works (maybe should be documented so) because you have a specialization on the constantness of function arg.
participants (3)
-
Armin Rigo -
Carl Friedrich Bolz -
Maciej Fijalkowski