diff options
146 files changed, 9478 insertions, 4133 deletions
@@ -5,17 +5,25 @@ syntax: glob syntax: regexp ^testresult$ ^site-packages$ -^pypy/module/cpyext/src/.+\.o$ +^bin$ ^pypy/bin/pypy-c -^pypy/translator/jvm/src/pypy/.+\.class$ +^pypy/module/cpyext/src/.+\.o$ +^pypy/module/cpyext/src/.+\.obj$ ^pypy/module/cpyext/test/.+\.errors$ ^pypy/module/cpyext/test/.+\.o$ +^pypy/module/cpyext/test/.+\.obj$ +^pypy/module/cpyext/test/.+\.manifest$ ^pypy/doc/.+\.html$ ^pypy/doc/basicblock\.asc$ ^pypy/doc/.+\.svninfo$ +^pypy/translator/c/src/libffi_msvc/.+\.obj$ +^pypy/translator/c/src/libffi_msvc/.+\.dll$ +^pypy/translator/c/src/libffi_msvc/.+\.lib$ +^pypy/translator/c/src/libffi_msvc/.+\.exp$ ^pypy/translator/jvm/\.project$ ^pypy/translator/jvm/\.classpath$ ^pypy/translator/jvm/eclipse-bin$ +^pypy/translator/jvm/src/pypy/.+\.class$ ^pypy/translator/benchmark/docutils$ ^pypy/translator/benchmark/templess$ ^pypy/translator/benchmark/gadfly$ @@ -25,6 +33,7 @@ syntax: regexp ^pypy/translator/goal/pypy-translation-snapshot$ ^pypy/translator/goal/pypy-c ^pypy/translator/goal/.+\.exe$ +^pypy/translator/goal/.+\.dll$ ^pypy/translator/goal/target.+-c$ ^pypy/_cache$ ^site-packages/.+\.egg$ @@ -27,7 +27,7 @@ documentation in the 'pypy' directories is licensed as follows: DEALINGS IN THE SOFTWARE. -PyPy Copyright holders 2003-2010 +PyPy Copyright holders 2003-2011 ----------------------------------- Except when otherwise stated (look for LICENSE files or information at diff --git a/lib-python/modified-2.5.2/test/test_tarfile.py b/lib-python/modified-2.5.2/test/test_tarfile.py index 3d0e396b53..eb64ad0925 100644 --- a/lib-python/modified-2.5.2/test/test_tarfile.py +++ b/lib-python/modified-2.5.2/test/test_tarfile.py @@ -458,7 +458,9 @@ class WriteStreamTest(WriteTest): f.close() elif self.comp == "bz2": f = bz2.BZ2Decompressor() - s = file(self.dstname).read() + g = file(self.dstname) + s = g.read() + g.close() s = f.decompress(s) self.assertEqual(len(f.unused_data), 0, "trailing data") else: @@ -531,6 +533,7 @@ class WriteGNULongTest(unittest.TestCase): self.assert_(tarinfo.name == member.name and \ tarinfo.linkname == member.linkname, \ "unable to read longname member") + tar.close() def test_longname_1023(self): self._test(("longnam/" * 127) + "longnam") diff --git a/pypy/annotation/annrpython.py b/pypy/annotation/annrpython.py index 44a51d50c4..2aef1de90b 100644 --- a/pypy/annotation/annrpython.py +++ b/pypy/annotation/annrpython.py @@ -406,31 +406,6 @@ class RPythonAnnotator(object): #___ simplification (should be moved elsewhere?) _______ - # it should be! - # now simplify_calls is moved to transform.py. - # i kept reverse_binding here for future(?) purposes though. --sanxiyn - - def reverse_binding(self, known_variables, cell): - """This is a hack.""" - # In simplify_calls, when we are trying to create the new - # SpaceOperation, all we have are SomeValues. But SpaceOperations take - # Variables, not SomeValues. Trouble is, we don't always have a - # Variable that just happens to be bound to the given SomeValue. - # A typical example would be if the tuple of arguments was created - # from another basic block or even another function. Well I guess - # there is no clean solution, short of making the transformations - # more syntactic (e.g. replacing a specific sequence of SpaceOperations - # with another one). This is a real hack because we have to use - # the identity of 'cell'. - if cell.is_constant(): - return Constant(cell.const) - else: - for v in known_variables: - if self.bindings[v] is cell: - return v - else: - raise CannotSimplify - def simplify(self, block_subset=None, extra_passes=None): # Generic simplifications transform.transform_graph(self, block_subset=block_subset, @@ -783,10 +758,6 @@ def consider_op_%s(self, arg1, arg2, *args): RPythonAnnotator._registeroperations(annmodel) -class CannotSimplify(Exception): - pass - - class BlockedInference(Exception): """This exception signals the type inference engine that the situation is currently blocked, and that it should try to progress elsewhere.""" diff --git a/pypy/config/support.py b/pypy/config/support.py index d62cd13716..870f8cd798 100644 --- a/pypy/config/support.py +++ b/pypy/config/support.py @@ -14,8 +14,12 @@ def detect_number_of_processors(filename_or_file='/proc/cpuinfo'): f = open(filename_or_file, "r") else: f = filename_or_file - return max([int(re.split('processor.*(\d+)', line)[1]) + count = max([int(re.split('processor.*(\d+)', line)[1]) for line in f.readlines() if line.startswith('processor')]) + 1 + if count >= 4: + return max(count // 2, 3) + else: + return count except: return 1 # we really don't want to explode here, at worst we have 1 diff --git a/pypy/config/test/test_support.py b/pypy/config/test/test_support.py index 22a25f931d..1e50e52936 100644 --- a/pypy/config/test/test_support.py +++ b/pypy/config/test/test_support.py @@ -45,7 +45,7 @@ def test_cpuinfo(): saved = os.environ try: os.environ = FakeEnviron(None) - assert detect_number_of_processors(StringIO(cpuinfo)) == 4 + assert detect_number_of_processors(StringIO(cpuinfo)) == 3 assert detect_number_of_processors('random crap that does not exist') == 1 os.environ = FakeEnviron('-j2') assert detect_number_of_processors(StringIO(cpuinfo)) == 1 diff --git a/pypy/conftest.py b/pypy/conftest.py index 16d1248059..37e5a40012 100644 --- a/pypy/conftest.py +++ b/pypy/conftest.py @@ -1,6 +1,7 @@ -import py, sys, os +import py, sys, os, textwrap, types from pypy.interpreter.gateway import app2interp_temp from pypy.interpreter.error import OperationError +from pypy.interpreter.function import Method from pypy.tool.pytest import appsupport from pypy.tool.option import make_config, make_objspace from pypy.config.config import ConflictConfigError @@ -14,8 +15,8 @@ pytest_plugins = "resultlog", rsyncdirs = ['.', '../lib-python', '../lib_pypy', '../demo'] rsyncignore = ['_cache'] -# PyPy's command line extra options (these are added -# to py.test's standard options) +# PyPy's command line extra options (these are added +# to py.test's standard options) # def _set_platform(opt, opt_str, value, parser): @@ -54,8 +55,8 @@ def pytest_funcarg__space(request): _SPACECACHE={} def gettestobjspace(name=None, **kwds): - """ helper for instantiating and caching space's for testing. - """ + """ helper for instantiating and caching space's for testing. + """ try: config = make_config(option, objspace=name, **kwds) except ConflictConfigError, e: @@ -195,30 +196,30 @@ def check_keyboard_interrupt(e): except AttributeError: pass -# -# Interfacing/Integrating with py.test's collection process +# +# Interfacing/Integrating with py.test's collection process # # def ensure_pytest_builtin_helpers(helpers='skip raises'.split()): """ hack (py.test.) raises and skip into builtins, needed - for applevel tests to run directly on cpython but + for applevel tests to run directly on cpython but apparently earlier on "raises" was already added - to module's globals. - """ + to module's globals. + """ import __builtin__ - for helper in helpers: + for helper in helpers: if not hasattr(__builtin__, helper): setattr(__builtin__, helper, getattr(py.test, helper)) def pytest_pycollect_makemodule(path, parent): return PyPyModule(path, parent) -class PyPyModule(py.test.collect.Module): - """ we take care of collecting classes both at app level - and at interp-level (because we need to stick a space - at the class) ourselves. - """ +class PyPyModule(py.test.collect.Module): + """ we take care of collecting classes both at app level + and at interp-level (because we need to stick a space + at the class) ourselves. + """ def __init__(self, *args, **kwargs): if hasattr(sys, 'pypy_objspaceclass'): option.conf_iocapture = "sys" # pypy cannot do FD-based @@ -252,16 +253,16 @@ class PyPyModule(py.test.collect.Module): # return True return False - def setup(self): + def setup(self): # stick py.test raise in module globals -- carefully - ensure_pytest_builtin_helpers() - super(PyPyModule, self).setup() - # if hasattr(mod, 'objspacename'): + ensure_pytest_builtin_helpers() + super(PyPyModule, self).setup() + # if hasattr(mod, 'objspacename'): # mod.space = getttestobjspace(mod.objspacename) - def makeitem(self, name, obj): - if isclass(obj) and self.classnamefilter(name): - if name.startswith('AppTest'): + def makeitem(self, name, obj): + if isclass(obj) and self.classnamefilter(name): + if name.startswith('AppTest'): return AppClassCollector(name, parent=self) elif name.startswith('ExpectTest'): if option.rundirect: @@ -274,18 +275,18 @@ class PyPyModule(py.test.collect.Module): # return AppExpectClassCollector(name, parent=self) else: return IntClassCollector(name, parent=self) - - elif hasattr(obj, 'func_code') and self.funcnamefilter(name): - if name.startswith('app_test_'): + + elif hasattr(obj, 'func_code') and self.funcnamefilter(name): + if name.startswith('app_test_'): assert not obj.func_code.co_flags & 32, \ - "generator app level functions? you must be joking" - return AppTestFunction(name, parent=self) - elif obj.func_code.co_flags & 32: # generator function - return self.Generator(name, parent=self) - else: - return IntTestFunction(name, parent=self) - -def skip_on_missing_buildoption(**ropts): + "generator app level functions? you must be joking" + return AppTestFunction(name, parent=self) + elif obj.func_code.co_flags & 32: # generator function + return self.Generator(name, parent=self) + else: + return IntTestFunction(name, parent=self) + +def skip_on_missing_buildoption(**ropts): __tracebackhide__ = True import sys options = getattr(sys, 'pypy_translation_info', None) @@ -293,12 +294,12 @@ def skip_on_missing_buildoption(**ropts): py.test.skip("not running on translated pypy " "(btw, i would need options: %s)" % (ropts,)) - for opt in ropts: - if not options.has_key(opt) or options[opt] != ropts[opt]: + for opt in ropts: + if not options.has_key(opt) or options[opt] != ropts[opt]: break else: return - py.test.skip("need translated pypy with: %s, got %s" + py.test.skip("need translated pypy with: %s, got %s" %(ropts,options)) def getwithoutbinding(x, name): @@ -361,8 +362,8 @@ class PyPyTestFunction(py.test.collect.Function): tb = sys.exc_info()[2] if e.match(space, space.w_KeyboardInterrupt): raise OpErrKeyboardInterrupt, OpErrKeyboardInterrupt(), tb - appexcinfo = appsupport.AppExceptionInfo(space, e) - if appexcinfo.traceback: + appexcinfo = appsupport.AppExceptionInfo(space, e) + if appexcinfo.traceback: raise AppError, AppError(appexcinfo), tb raise @@ -420,7 +421,7 @@ class AppTestFunction(PyPyTestFunction): target = self.obj if option.runappdirect: return target() - space = gettestobjspace() + space = gettestobjspace() filename = self._getdynfilename(target) func = app2interp_temp(target, filename=filename) print "executing", func @@ -430,47 +431,56 @@ class AppTestFunction(PyPyTestFunction): code = getattr(func, 'im_func', func).func_code return "[%s:%s]" % (code.co_filename, code.co_firstlineno) -class AppTestMethod(AppTestFunction): - - def setup(self): - super(AppTestMethod, self).setup() - instance = self.parent.obj - w_instance = self.parent.w_instance - space = instance.space - for name in dir(instance): - if name.startswith('w_'): +class AppTestMethod(AppTestFunction): + def setup(self): + super(AppTestMethod, self).setup() + instance = self.parent.obj + w_instance = self.parent.w_instance + space = instance.space + for name in dir(instance): + if name.startswith('w_'): if option.runappdirect: # if the value is a function living on the class, # don't turn it into a bound method here obj = getwithoutbinding(instance, name) setattr(instance, name[2:], obj) else: - space.setattr(w_instance, space.wrap(name[2:]), - getattr(instance, name)) + obj = getattr(instance, name) + if isinstance(obj, types.MethodType): + source = py.std.inspect.getsource(obj).lstrip() + w_func = space.appexec([], textwrap.dedent(""" + (): + %s + return %s + """) % (source, name)) + w_obj = Method(space, w_func, w_instance, space.w_None) + else: + w_obj = obj + space.setattr(w_instance, space.wrap(name[2:]), w_obj) def runtest_perform(self): target = self.obj if option.runappdirect: return target() - space = target.im_self.space + space = target.im_self.space filename = self._getdynfilename(target) - func = app2interp_temp(target.im_func, filename=filename) - w_instance = self.parent.w_instance - self.execute_appex(space, func, space, w_instance) + func = app2interp_temp(target.im_func, filename=filename) + w_instance = self.parent.w_instance + self.execute_appex(space, func, space, w_instance) class PyPyClassCollector(py.test.collect.Class): def setup(self): - cls = self.obj + cls = self.obj if not hasattr(cls, 'spaceconfig'): - cls.space = LazyObjSpaceGetter() + cls.space = LazyObjSpaceGetter() else: assert hasattr(cls, 'space') # set by pytest_runtest_setup - super(PyPyClassCollector, self).setup() + super(PyPyClassCollector, self).setup() class IntInstanceCollector(py.test.collect.Instance): - Function = IntTestFunction - -class IntClassCollector(PyPyClassCollector): + Function = IntTestFunction + +class IntClassCollector(PyPyClassCollector): Instance = IntInstanceCollector def _haskeyword(self, keyword): @@ -480,21 +490,21 @@ class IntClassCollector(PyPyClassCollector): def _keywords(self): return super(IntClassCollector, self)._keywords() + ['interplevel'] -class AppClassInstance(py.test.collect.Instance): - Function = AppTestMethod +class AppClassInstance(py.test.collect.Instance): + Function = AppTestMethod - def setup(self): - super(AppClassInstance, self).setup() - instance = self.obj - space = instance.space - w_class = self.parent.w_class + def setup(self): + super(AppClassInstance, self).setup() + instance = self.obj + space = instance.space + w_class = self.parent.w_class if option.runappdirect: self.w_instance = instance else: self.w_instance = space.call_function(w_class) -class AppClassCollector(PyPyClassCollector): - Instance = AppClassInstance +class AppClassCollector(PyPyClassCollector): + Instance = AppClassInstance def _haskeyword(self, keyword): return keyword == 'applevel' or \ @@ -503,11 +513,11 @@ class AppClassCollector(PyPyClassCollector): def _keywords(self): return super(AppClassCollector, self)._keywords() + ['applevel'] - def setup(self): - super(AppClassCollector, self).setup() - cls = self.obj - space = cls.space - clsname = cls.__name__ + def setup(self): + super(AppClassCollector, self).setup() + cls = self.obj + space = cls.space + clsname = cls.__name__ if option.runappdirect: w_class = cls else: @@ -515,7 +525,7 @@ class AppClassCollector(PyPyClassCollector): space.wrap(clsname), space.newtuple([]), space.newdict()) - self.w_class = w_class + self.w_class = w_class class ExpectTestMethod(py.test.collect.Function): def safe_name(target): diff --git a/pypy/doc/confrest.py b/pypy/doc/confrest.py index 0f0fd0f1e5..0782587fa1 100644 --- a/pypy/doc/confrest.py +++ b/pypy/doc/confrest.py @@ -31,7 +31,7 @@ pageTracker._trackPageview(); html.a("documentation", href=self.get_doclink("docindex.html"), class_="menu"), " ", - html.a("svn", href="https://codespeak.net/viewvc/pypy/trunk/", + html.a("hg", href="https://bitbucket.org/pypy/pypy", class_="menu"), " ", html.a("issues", diff --git a/pypy/doc/faq.txt b/pypy/doc/faq.txt index 8b6c83de1a..ecadecbe5d 100644 --- a/pypy/doc/faq.txt +++ b/pypy/doc/faq.txt @@ -62,13 +62,8 @@ OS X and mostly works under Windows too (but is tested there less extensively). PyPy needs a CPython running on the target platform to bootstrap, as cross compilation is not really meant to work yet. At the moment you need CPython 2.4 (with ctypes) or CPython 2.5 or 2.6 -for the translation process. +for the translation process. PyPy's JIT requires an x86 or x86_64 CPU. -PyPy also basically works in a 64-bit Linux environment, but -*it requires a 32-bit Intel CPU* for the JIT right now. (It works -fine in a 32-bit chroot of an Intel 64.). There is also an ongoing -summer of code project to provide 64bit support for JIT. It's scheduled -to be finished by end of summer 2010. ------------------------------------------------ Which Python version (2.x?) does PyPy implement? diff --git a/pypy/doc/how-to-release.txt b/pypy/doc/how-to-release.txt index cfa2e99dd0..be7e88eb9e 100644 --- a/pypy/doc/how-to-release.txt +++ b/pypy/doc/how-to-release.txt @@ -19,7 +19,8 @@ Release Steps * at code freeze make a release branch under http://codepeak.net/svn/pypy/release/x.y(.z). IMPORTANT: bump the - pypy version number in module/sys/version.py, notice that the branch + pypy version number in module/sys/version.py and in + module/cpyext/include/patchlevel.h, notice that the branch will capture the revision number of this change for the release; some of the next updates may be done before or after branching; make sure things are ported back to the trunk and to the branch as @@ -41,6 +42,13 @@ Release Steps * write release announcement pypy/doc/release-x.y(.z).txt the release announcement should contain a direct link to the download page * update pypy.org (under extradoc/pypy.org), rebuild and commit + +* update http://codespeak.net/pypy/trunk: + code0> + chmod -R yourname:users /www/codespeak.net/htdocs/pypy/trunk + local> cd ..../pypy/doc && py.test + local> cd ..../pypy + local> rsync -az doc codespeak.net:/www/codespeak.net/htdocs/pypy/trunk/pypy/ + * post announcement on morepypy.blogspot.com * send announcements to pypy-dev, python-list, python-announce, python-dev ... diff --git a/pypy/doc/release-1.4.1.txt b/pypy/doc/release-1.4.1.txt new file mode 100644 index 0000000000..c8af0b9c9f --- /dev/null +++ b/pypy/doc/release-1.4.1.txt @@ -0,0 +1,84 @@ +=============================== +PyPy 1.4.1 +=============================== + +We're pleased to announce the 1.4.1 release of PyPy. This +release consolidates all the bug fixes that occurred since the +previous release. To everyone that took the trouble to report +them, we want to say thank you. + + http://pypy.org/download.html + +What is PyPy +============ + +PyPy is a very compliant Python interpreter, almost a drop-in +replacement for CPython. Note that it still only emulates Python +2.5 by default; the ``fast-forward`` branch with Python 2.7 +support is slowly getting ready but will only be integrated in +the next release. + +In two words, the advantage of trying out PyPy instead of CPython +(the default implementation of Python) is, for now, the +performance. Not all programs are faster in PyPy, but we are +confident that any CPU-intensive task will be much faster, at +least if it runs for long enough (the JIT has a slow warm-up +phase, which can take several seconds or even one minute on the +largest programs). + +Note again that we do support compiling and using C extension +modules from CPython (``pypy setup.py install``). However, this +is still an alpha feature, and the most complex modules typically +fail for various reasons; others work (e.g. ``PIL``) but take a +serious performance hit. Also, for Mac OS X see below. + +Please note also that PyPy's performance was optimized almost +exclusively on Linux. It seems from some reports that on Windows +as well as Mac OS X (probably for different reasons) the +performance might be lower. We did not investigate much so far. + + +More highlights +=============== + +* We migrated to Mercurial (thanks to Ronny Pfannschmidt and + Antonio Cuni) for the effort) and moved to bitbucket. The new + command to check out a copy of PyPy is:: + + hg clone http://bitbucket.org/pypy/pypy + +* In long-running processes, the assembler generated by old + JIT-compilations is now freed. There should be no more leak, + however long the process runs. + +* Improve a lot the performance of the ``binascii`` module, and + of ``hashlib.md5`` and ``hashlib.sha``. + +* Made sys.setrecursionlimit() a no-op. Instead, we rely purely + on the built-in stack overflow detection mechanism, which also + gives you a RuntimeError -- just not at some exact recursion + level. + +* Fix argument processing (now e.g. ``pypy -OScpass`` works like + it does on CPython --- if you have a clue what it does there + ``:-)`` ) + +* cpyext on Mac OS X: it still does not seem to work. I get + systematically a segfault in dlopen(). Contributions welcome. + +* Fix two corner cases in the GC (one in minimark, one in + asmgcc+JIT). This notably prevented "pypy translate.py -Ojit" + from working on Windows, leading to crashes. + +* Fixed a corner case in the JIT's optimizer, leading to "Fatal + RPython error: AssertionError". + +* Added some missing built-in functions into the 'os' module. + +* Fix ctypes (it was not propagating keepalive information from + c_void_p). + + +Cheers, + +Armin Rigo, for the rest of the team diff --git a/pypy/doc/translation.txt b/pypy/doc/translation.txt index f3d0abc491..5cf9d266b1 100644 --- a/pypy/doc/translation.txt +++ b/pypy/doc/translation.txt @@ -519,7 +519,7 @@ which simply iterates over all numbers from 0 to n - 1 is equivalent to the following in Python:: l = range(n) - iterator = iter(n) + iterator = iter(l) try: while 1: i = iterator.next() diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py index 4e48e2384d..69d78b1864 100644 --- a/pypy/interpreter/argument.py +++ b/pypy/interpreter/argument.py @@ -105,14 +105,11 @@ class Arguments(object): make_sure_not_resized(self.arguments_w) if w_stararg is not None: self._combine_starargs_wrapped(w_stararg) - if w_starstararg is not None: - self._combine_starstarargs_wrapped(w_starstararg) - # if we have a call where **args are used at the callsite - # we shouldn't let the JIT see the argument matching - self._dont_jit = True - else: - self._dont_jit = False - + # if we have a call where **args are used at the callsite + # we shouldn't let the JIT see the argument matching + self._dont_jit = (w_starstararg is not None and + self._combine_starstarargs_wrapped(w_starstararg)) + def __repr__(self): """ NOT_RPYTHON """ name = self.__class__.__name__ @@ -160,10 +157,20 @@ class Arguments(object): raise OperationError(space.w_TypeError, space.wrap("argument after ** must be " "a dictionary")) - keywords_w = [None] * space.int_w(space.len(w_starstararg)) - keywords = [None] * space.int_w(space.len(w_starstararg)) + if space.is_true(w_starstararg): + self._do_combine_starstarargs_wrapped(w_starstararg) + return True + else: + return False # empty dict; don't disable the JIT + + def _do_combine_starstarargs_wrapped(self, w_starstararg): + space = self.space + keys_w = space.unpackiterable(w_starstararg) + length = len(keys_w) + keywords_w = [None] * length + keywords = [None] * length i = 0 - for w_key in space.unpackiterable(w_starstararg): + for w_key in keys_w: try: key = space.str_w(w_key) except OperationError, e: diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py index cad62e05dc..1f132106ed 100644 --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -55,7 +55,7 @@ class W_Root(object): return False def setdict(self, space, w_dict): - typename = space.type(self).getname(space, '?') + typename = space.type(self).getname(space) raise operationerrfmt(space.w_TypeError, "attribute '__dict__' of %s objects " "is not writable", typename) @@ -72,7 +72,7 @@ class W_Root(object): raise NotImplementedError("only for interp-level user subclasses " "from typedef.py") - def getname(self, space, default): + def getname(self, space, default='?'): try: return space.str_w(space.getattr(self, space.wrap('__name__'))) except OperationError, e: @@ -117,7 +117,7 @@ class W_Root(object): classname = wrappable_class_name(RequiredClass) msg = "'%s' object expected, got '%s' instead" raise operationerrfmt(space.w_TypeError, msg, - classname, self.getclass(space).getname(space, '?')) + classname, self.getclass(space).getname(space)) # used by _weakref implemenation @@ -125,7 +125,7 @@ class W_Root(object): return None def setweakref(self, space, weakreflifeline): - typename = space.type(self).getname(space, '?') + typename = space.type(self).getname(space) raise operationerrfmt(space.w_TypeError, "cannot create weak reference to '%s' object", typename) @@ -733,7 +733,7 @@ class ObjSpace(object): msg = "'%s' object expected, got '%s' instead" raise operationerrfmt(self.w_TypeError, msg, wrappable_class_name(RequiredClass), - w_obj.getclass(self).getname(self, '?')) + w_obj.getclass(self).getname(self)) return obj interp_w._annspecialcase_ = 'specialize:arg(1)' @@ -1054,7 +1054,7 @@ class ObjSpace(object): raise msg = "%s must be an integer, not %s" raise operationerrfmt(self.w_TypeError, msg, - objdescr, self.type(w_obj).getname(self, '?')) + objdescr, self.type(w_obj).getname(self)) try: index = self.int_w(w_index) except OperationError, err: @@ -1070,7 +1070,7 @@ class ObjSpace(object): raise operationerrfmt( w_exception, "cannot fit '%s' into an index-sized " - "integer", self.type(w_obj).getname(self, '?')) + "integer", self.type(w_obj).getname(self)) else: return index @@ -1126,6 +1126,11 @@ class ObjSpace(object): buffer = self.buffer_w(w_obj) return buffer.as_str() + def str_or_None_w(self, w_obj): + if self.is_w(w_obj, self.w_None): + return None + return self.str_w(w_obj) + def realstr_w(self, w_obj): # Like str_w, but only works if w_obj is really of type 'str'. if not self.is_true(self.isinstance(w_obj, self.w_str)): diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py index f3e19eb2f2..58390ff0f4 100644 --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -212,7 +212,7 @@ class OperationError(Exception): w_inst = w_type w_instclass = space.exception_getclass(w_inst) if not space.exception_is_valid_class_w(w_instclass): - instclassname = w_instclass.getname(space, '?') + instclassname = w_instclass.getname(space) msg = ("exceptions must be classes, or instances, " "or strings (deprecated), not %s") raise operationerrfmt(space.w_TypeError, msg, instclassname) diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py index 003222e993..72f2c328d0 100644 --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -444,7 +444,7 @@ class Method(Wrappable): pre = "bound" else: pre = "unbound" - return "%s method %s" % (pre, self.w_function.getname(self.space, '?')) + return "%s method %s" % (pre, self.w_function.getname(self.space)) def call_args(self, args): space = self.space @@ -493,13 +493,13 @@ class Method(Wrappable): def descr_method_repr(self): space = self.space - name = self.w_function.getname(self.space, '?') + name = self.w_function.getname(self.space) # XXX do we handle all cases sanely here? if space.is_w(self.w_class, space.w_None): w_class = space.type(self.w_instance) else: w_class = self.w_class - typename = w_class.getname(self.space, '?') + typename = w_class.getname(self.space) if self.w_instance is None: s = "<unbound method %s.%s>" % (typename, name) return space.wrap(s) @@ -591,7 +591,7 @@ class ClassMethod(Wrappable): def descr_classmethod__new__(space, w_subtype, w_function): if not space.is_true(space.callable(w_function)): - typename = space.type(w_function).getname(space, '?') + typename = space.type(w_function).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object is not callable", typename) instance = space.allocate_instance(ClassMethod, w_subtype) diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py index 978a92d41e..c25d1783d8 100644 --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -125,6 +125,9 @@ class UnwrapSpec_Check(UnwrapSpecRecipe): def visit_bufferstr(self, el, app_sig): self.checked_space_method(el, app_sig) + def visit_str_or_None(self, el, app_sig): + self.checked_space_method(el, app_sig) + def visit_nonnegint(self, el, app_sig): self.checked_space_method(el, app_sig) @@ -238,6 +241,9 @@ class UnwrapSpec_EmitRun(UnwrapSpecEmit): def visit_bufferstr(self, typ): self.run_args.append("space.bufferstr_w(%s)" % (self.scopenext(),)) + def visit_str_or_None(self, typ): + self.run_args.append("space.str_or_None_w(%s)" % (self.scopenext(),)) + def visit_nonnegint(self, typ): self.run_args.append("space.nonnegint_w(%s)" % (self.scopenext(),)) @@ -365,6 +371,9 @@ class UnwrapSpec_FastFunc_Unwrap(UnwrapSpecEmit): def visit_bufferstr(self, typ): self.unwrap.append("space.bufferstr_w(%s)" % (self.nextarg(),)) + def visit_str_or_None(self, typ): + self.unwrap.append("space.str_or_None_w(%s)" % (self.nextarg(),)) + def visit_nonnegint(self, typ): self.unwrap.append("space.nonnegint_w(%s)" % (self.nextarg(),)) diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py index c01a634a70..051d366871 100644 --- a/pypy/interpreter/test/test_gateway.py +++ b/pypy/interpreter/test/test_gateway.py @@ -267,10 +267,12 @@ class TestGateway: space = self.space w = space.wrap def g3_ss(space, s0, s1): + if s1 is None: + return space.wrap(42) return space.wrap(s0+s1) app_g3_ss = gateway.interp2app_temp(g3_ss, unwrap_spec=[gateway.ObjSpace, - str,str]) + str, 'str_or_None']) w_app_g3_ss = space.wrap(app_g3_ss) assert self.space.eq_w( space.call(w_app_g3_ss, @@ -280,6 +282,11 @@ class TestGateway: assert self.space.eq_w( space.call_function(w_app_g3_ss, w('foo'), w('bar')), w('foobar')) + assert self.space.eq_w( + space.call_function(w_app_g3_ss, w('foo'), space.w_None), + w(42)) + space.raises_w(space.w_TypeError, space.call_function, + w_app_g3_ss, space.w_None, w('bar')) def test_interp2app_unwrap_spec_int_float(self): space = self.space diff --git a/pypy/interpreter/test/test_interpreter.py b/pypy/interpreter/test/test_interpreter.py index 8cb291d95e..d3d1ed25d3 100644 --- a/pypy/interpreter/test/test_interpreter.py +++ b/pypy/interpreter/test/test_interpreter.py @@ -165,11 +165,14 @@ class TestInterpreter: assert not arg_w or not space.eq_w(arg_w[-1], space.wrap(-1)) return real_call_function(w_obj, *arg_w) self.space.call_function = safe_call_function - code = ''' - def f(): - import sys - ''' - self.codetest(code, 'f', []) + try: + code = ''' + def f(): + import sys + ''' + self.codetest(code, 'f', []) + finally: + del self.space.call_function def test_call_star_starstar(self): code = '''\ diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py index b6e6ede1ab..da304b37fa 100644 --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -51,7 +51,7 @@ def default_identity_hash(space, w_obj): return space.wrap(compute_identity_hash(w_obj)) def descr__hash__unhashable(space, w_obj): - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' objects are unhashable", typename) @@ -512,7 +512,7 @@ class Member(Wrappable): " objects doesn't apply to '%s' object", self.name, self.w_cls.name, - space.type(w_obj).getname(space, '?')) + space.type(w_obj).getname(space)) def descr_member_get(space, member, w_obj, w_w_cls=None): """member.__get__(obj[, type]) -> value @@ -578,7 +578,7 @@ from pypy.interpreter.special import NotImplemented, Ellipsis def descr_get_dict(space, w_obj): w_dict = w_obj.getdict() if w_dict is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "descriptor '__dict__' doesn't apply to" " '%s' objects", typename) diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py index b2a3bd3ccd..0a56ac259a 100644 --- a/pypy/jit/backend/llgraph/llimpl.py +++ b/pypy/jit/backend/llgraph/llimpl.py @@ -373,6 +373,13 @@ def compile_add_jump_target(loop, loop_target): else: log.info("compiling new bridge") +def compile_add_guard_jump_target(loop, loop_target): + loop = _from_opaque(loop) + loop_target = _from_opaque(loop_target) + op = loop.operations[-1] + assert op.is_guard() + op.jump_target = loop_target + def compile_add_fail(loop, fail_index): loop = _from_opaque(loop) index = len(loop.operations)-1 @@ -1634,6 +1641,7 @@ setannotation(compile_add_int_result, annmodel.SomeInteger()) setannotation(compile_add_ref_result, annmodel.SomeInteger()) setannotation(compile_add_float_result, annmodel.SomeInteger()) setannotation(compile_add_jump_target, annmodel.s_None) +setannotation(compile_add_guard_jump_target, annmodel.s_None) setannotation(compile_add_fail, annmodel.SomeInteger()) setannotation(compile_add_fail_arg, annmodel.s_None) setannotation(compile_redirect_fail, annmodel.s_None) diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py index 87cc5af328..43ab590b46 100644 --- a/pypy/jit/backend/llgraph/runner.py +++ b/pypy/jit/backend/llgraph/runner.py @@ -202,6 +202,7 @@ class BaseCPU(model.AbstractCPU): llimpl.compile_add_fail_arg(c, var2index[box]) else: llimpl.compile_add_fail_arg(c, -1) + x = op.result if x is not None: if isinstance(x, history.BoxInt): diff --git a/pypy/jit/backend/x86/jump.py b/pypy/jit/backend/x86/jump.py index 4886257053..fc080e8b62 100644 --- a/pypy/jit/backend/x86/jump.py +++ b/pypy/jit/backend/x86/jump.py @@ -7,7 +7,9 @@ def remap_frame_layout(assembler, src_locations, dst_locations, tmpreg): srccount = {} # maps dst_locations to how many times the same # location appears in src_locations for dst in dst_locations: - srccount[dst._getregkey()] = 0 + key = dst._getregkey() + assert key not in srccount, "duplicate value in dst_locations!" + srccount[key] = 0 for i in range(len(dst_locations)): src = src_locations[i] if isinstance(src, ImmedLoc): diff --git a/pypy/jit/backend/x86/test/test_loop_spec.py b/pypy/jit/backend/x86/test/test_loop_unroll.py index 617aa96fdc..312deff9e5 100644 --- a/pypy/jit/backend/x86/test/test_loop_spec.py +++ b/pypy/jit/backend/x86/test/test_loop_unroll.py @@ -1,8 +1,8 @@ import py from pypy.jit.backend.x86.test.test_basic import Jit386Mixin -from pypy.jit.metainterp.test import test_loop_spec +from pypy.jit.metainterp.test import test_loop_unroll -class TestLoopSpec(Jit386Mixin, test_loop_spec.LoopSpecTest): +class TestLoopSpec(Jit386Mixin, test_loop_unroll.LoopUnrollTest): # for the individual tests see # ====> ../../../metainterp/test/test_loop.py pass diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py index bb9d7eaf4a..2cc839a9b8 100644 --- a/pypy/jit/codewriter/support.py +++ b/pypy/jit/codewriter/support.py @@ -178,12 +178,12 @@ def _ll_1_jit_force_virtual(inst): def _ll_2_int_floordiv_ovf_zer(x, y): if y == 0: raise ZeroDivisionError - if ((x + sys.maxint) & y) == -1: # detect "x = -sys.maxint-1, y = -1". + if x == -sys.maxint - 1 and y == -1: raise OverflowError return llop.int_floordiv(lltype.Signed, x, y) def _ll_2_int_floordiv_ovf(x, y): - if ((x + sys.maxint) & y) == -1: # detect "x = -sys.maxint-1, y = -1". + if x == -sys.maxint - 1 and y == -1: raise OverflowError return llop.int_floordiv(lltype.Signed, x, y) @@ -195,12 +195,12 @@ def _ll_2_int_floordiv_zer(x, y): def _ll_2_int_mod_ovf_zer(x, y): if y == 0: raise ZeroDivisionError - if ((x + sys.maxint) & y) == -1: # detect "x = -sys.maxint-1, y = -1". + if x == -sys.maxint - 1 and y == -1: raise OverflowError return llop.int_mod(lltype.Signed, x, y) def _ll_2_int_mod_ovf(x, y): - if ((x + sys.maxint) & y) == -1: # detect "x = -sys.maxint-1, y = -1". + if x == -sys.maxint - 1 and y == -1: raise OverflowError return llop.int_mod(lltype.Signed, x, y) diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py index e770b3fd01..e88099fe29 100644 --- a/pypy/jit/metainterp/compile.py +++ b/pypy/jit/metainterp/compile.py @@ -12,7 +12,6 @@ from pypy.jit.metainterp.history import TreeLoop, Box, History, LoopToken from pypy.jit.metainterp.history import AbstractFailDescr, BoxInt from pypy.jit.metainterp.history import BoxPtr, BoxObj, BoxFloat, Const from pypy.jit.metainterp import history -from pypy.jit.metainterp.specnode import NotSpecNode, more_general_specnodes from pypy.jit.metainterp.typesystem import llhelper, oohelper from pypy.jit.metainterp.optimizeutil import InvalidLoop from pypy.jit.metainterp.resume import NUMBERING @@ -38,23 +37,24 @@ def show_loop(metainterp_sd, loop=None, error=None): extraloops = [loop] metainterp_sd.stats.view(errmsg=errmsg, extraloops=extraloops) -def create_empty_loop(metainterp): +def create_empty_loop(metainterp, name_prefix=''): name = metainterp.staticdata.stats.name_for_new_loop() - return TreeLoop(name) + return TreeLoop(name_prefix + name) def make_loop_token(nb_args, jitdriver_sd): loop_token = LoopToken() - loop_token.specnodes = [prebuiltNotSpecNode] * nb_args loop_token.outermost_jitdriver_sd = jitdriver_sd return loop_token -def record_loop_or_bridge(loop): +def record_loop_or_bridge(metainterp_sd, loop): """Do post-backend recordings and cleanups on 'loop'. """ # get the original loop token (corresponding to 'loop', or if that is # a bridge, to the loop that this bridge belongs to) looptoken = loop.token assert looptoken is not None + if metainterp_sd.warmrunnerdesc is not None: # for tests + assert looptoken.generation > 0 # has been registered with memmgr wref = weakref.ref(looptoken) for op in loop.operations: descr = op.getdescr() @@ -71,14 +71,17 @@ def record_loop_or_bridge(loop): if descr is not looptoken: looptoken.record_jump_to(descr) op.setdescr(None) # clear reference, mostly for tests + if not we_are_translated(): + op._jumptarget_number = descr.number # mostly for tests: make sure we don't keep a reference to the LoopToken loop.token = None if not we_are_translated(): - loop._number = looptoken.number + loop._looptoken_number = looptoken.number # ____________________________________________________________ -def compile_new_loop(metainterp, old_loop_tokens, start): +def compile_new_loop(metainterp, old_loop_tokens, greenkey, start, + full_preamble_needed=True): """Try to compile a new loop by closing the current history back to the first operation. """ @@ -95,6 +98,11 @@ def compile_new_loop(metainterp, old_loop_tokens, start): loop_token = make_loop_token(len(loop.inputargs), jitdriver_sd) loop.token = loop_token loop.operations[-1].setdescr(loop_token) # patch the target of the JUMP + + loop.preamble = create_empty_loop(metainterp, 'Preamble ') + loop.preamble.inputargs = loop.inputargs + loop.preamble.token = make_loop_token(len(loop.inputargs), jitdriver_sd) + try: old_loop_token = jitdriver_sd.warmstate.optimize_loop( metainterp_sd, old_loop_tokens, loop) @@ -103,23 +111,33 @@ def compile_new_loop(metainterp, old_loop_tokens, start): if old_loop_token is not None: metainterp.staticdata.log("reusing old loop") return old_loop_token - send_loop_to_backend(metainterp_sd, loop, "loop") - insert_loop_token(old_loop_tokens, loop_token) - record_loop_or_bridge(loop) - return loop_token + + if loop.preamble.operations is not None: + send_loop_to_backend(metainterp_sd, loop, "loop") + record_loop_or_bridge(metainterp_sd, loop) + token = loop.preamble.token + if full_preamble_needed or not loop.preamble.token.short_preamble: + send_loop_to_backend(metainterp_sd, loop.preamble, "entry bridge") + insert_loop_token(old_loop_tokens, loop.preamble.token) + jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp( + greenkey, loop.preamble.token) + record_loop_or_bridge(metainterp_sd, loop.preamble) + return token + else: + send_loop_to_backend(metainterp_sd, loop, "loop") + insert_loop_token(old_loop_tokens, loop_token) + jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp( + greenkey, loop.token) + record_loop_or_bridge(metainterp_sd, loop) + return loop_token def insert_loop_token(old_loop_tokens, loop_token): # Find where in old_loop_tokens we should insert this new loop_token. # The following algo means "as late as possible, but before another # loop token that would be more general and so completely mask off # the new loop_token". - for i in range(len(old_loop_tokens)): - if more_general_specnodes(old_loop_tokens[i].specnodes, - loop_token.specnodes): - old_loop_tokens.insert(i, loop_token) - break - else: - old_loop_tokens.append(loop_token) + # XXX do we still need a list? + old_loop_tokens.append(loop_token) def send_loop_to_backend(metainterp_sd, loop, type): globaldata = metainterp_sd.globaldata @@ -128,6 +146,11 @@ def send_loop_to_backend(metainterp_sd, loop, type): globaldata.loopnumbering += 1 metainterp_sd.logger_ops.log_loop(loop.inputargs, loop.operations, n, type) + short = loop.token.short_preamble + if short: + metainterp_sd.logger_ops.log_short_preamble(short[-1].inputargs, + short[-1].operations) + if not we_are_translated(): show_loop(metainterp_sd, loop) loop.check_consistency() @@ -209,13 +232,10 @@ class ExitFrameWithExceptionDescrRef(_DoneWithThisFrameDescr): raise metainterp_sd.ExitFrameWithExceptionRef(cpu, value) -prebuiltNotSpecNode = NotSpecNode() - class TerminatingLoopToken(LoopToken): terminating = True def __init__(self, nargs, finishdescr): - self.specnodes = [prebuiltNotSpecNode]*nargs self.finishdescr = finishdescr def make_done_loop_tokens(): @@ -568,14 +588,40 @@ def compile_new_bridge(metainterp, old_loop_tokens, resumekey): # know exactly what we must do (ResumeGuardDescr/ResumeFromInterpDescr) prepare_last_operation(new_loop, target_loop_token) resumekey.compile_and_attach(metainterp, new_loop) - record_loop_or_bridge(new_loop) + compile_known_target_bridges(metainterp, new_loop) + record_loop_or_bridge(metainterp_sd, new_loop) return target_loop_token +# For backends that not supports emitting guards with preset jump +# targets, emit mini-bridges containing the jump +def compile_known_target_bridges(metainterp, bridge): + for op in bridge.operations: + if op.is_guard(): + target = op.getjumptarget() + if target: + mini = create_empty_loop(metainterp, 'fallback') + mini.inputargs = op.getfailargs()[:] + jmp = ResOperation(rop.JUMP, mini.inputargs[:], None, target) + mini.operations = [jmp] + descr = op.getdescr() + assert isinstance(descr, ResumeGuardDescr) + mini.token = bridge.token + + #descr.compile_and_attach(metainterp, mini) + if not we_are_translated(): + descr._debug_suboperations = mini.operations + send_bridge_to_backend(metainterp.staticdata, descr, + mini.inputargs, mini.operations, + bridge.token) + record_loop_or_bridge(metainterp.staticdata, mini) + + def prepare_last_operation(new_loop, target_loop_token): op = new_loop.operations[-1] if not isinstance(target_loop_token, TerminatingLoopToken): # normal case - op.setdescr(target_loop_token) # patch the jump target + #op.setdescr(target_loop_token) # patch the jump target + pass else: # The target_loop_token is a pseudo loop token, # e.g. loop_tokens_done_with_this_frame_void[0] diff --git a/pypy/jit/metainterp/graphpage.py b/pypy/jit/metainterp/graphpage.py index 291881fd60..87791fe075 100644 --- a/pypy/jit/metainterp/graphpage.py +++ b/pypy/jit/metainterp/graphpage.py @@ -33,8 +33,8 @@ class ResOpGraphPage(GraphPage): for graph, highlight in graphs: if getattr(graph, 'token', None) is not None: resopgen.jumps_to_graphs[graph.token] = graph - if getattr(graph, '_number', None) is not None: - resopgen.jumps_to_graphs[graph._number] = graph + if getattr(graph, '_looptoken_number', None) is not None: + resopgen.jumps_to_graphs[graph._looptoken_number] = graph for graph, highlight in graphs: resopgen.add_graph(graph, highlight) @@ -170,17 +170,21 @@ class ResOpGen(object): (graphindex, opindex)) break if op.getopnum() == rop.JUMP: - tgt = op.getdescr() tgt_g = -1 - if tgt is None: - tgt_g = graphindex + tgt = None + tgt_number = getattr(op, '_jumptarget_number', None) + if tgt_number is not None: + tgt = self.jumps_to_graphs.get(tgt_number) else: - if tgt in self.jumps_to_graphs: - tgt = self.jumps_to_graphs[tgt] + tgt_descr = op.getdescr() + if tgt_descr is None: + tgt_g = graphindex else: - tgt = self.jumps_to_graphs.get(tgt.number) - if tgt is not None: - tgt_g = self.graphs.index(tgt) + tgt = self.jumps_to_graphs.get(tgt_descr.number) + if tgt is None: + tgt = self.jumps_to_graphs.get(tgt_descr) + if tgt is not None: + tgt_g = self.graphs.index(tgt) if tgt_g != -1: self.genedge((graphindex, opstartindex), (tgt_g, 0), diff --git a/pypy/jit/metainterp/greenfield.py b/pypy/jit/metainterp/greenfield.py index e2410436a3..cd5acef41d 100644 --- a/pypy/jit/metainterp/greenfield.py +++ b/pypy/jit/metainterp/greenfield.py @@ -15,7 +15,7 @@ class GreenFieldInfo(object): seen.add(objname) assert len(seen) == 1, ( "Current limitation: you can only give one instance with green " - "fields. Found %r" % seen.keys()) + "fields. Found %r" % list(seen)) self.red_index = jd.jitdriver.reds.index(objname) # # a list of (GTYPE, fieldname) diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py index 42cf8f373b..9389b3be34 100644 --- a/pypy/jit/metainterp/history.py +++ b/pypy/jit/metainterp/history.py @@ -489,6 +489,9 @@ class Box(AbstractValue): def _get_str(self): # for debugging only return self.constbox()._get_str() + def forget_value(self): + raise NotImplementedError + class BoxInt(Box): type = INT _attrs_ = ('value',) @@ -501,6 +504,9 @@ class BoxInt(Box): assert isinstance(value, Symbolic) self.value = value + def forget_value(self): + self.value = 0 + def clonebox(self): return BoxInt(self.value) @@ -536,6 +542,9 @@ class BoxFloat(Box): assert isinstance(floatval, float) self.value = floatval + def forget_value(self): + self.value = 0.0 + def clonebox(self): return BoxFloat(self.value) @@ -568,6 +577,9 @@ class BoxPtr(Box): assert lltype.typeOf(value) == llmemory.GCREF self.value = value + def forget_value(self): + self.value = lltype.nullptr(llmemory.GCREF.TO) + def clonebox(self): return BoxPtr(self.value) @@ -612,6 +624,9 @@ class BoxObj(Box): assert ootype.typeOf(value) is ootype.Object self.value = value + def forget_value(self): + self.value = ootype.NULL + def clonebox(self): return BoxObj(self.value) @@ -729,9 +744,9 @@ class LoopToken(AbstractDescr): was compiled; but the LoopDescr remains alive and points to the generated assembler. """ + short_preamble = None terminating = False # see TerminatingLoopToken in compile.py outermost_jitdriver_sd = None - # specnodes = ... # and more data specified by the backend when the loop is compiled number = -1 generation = r_int64(0) diff --git a/pypy/jit/metainterp/jitprof.py b/pypy/jit/metainterp/jitprof.py index 55497c9df4..c3b605354a 100644 --- a/pypy/jit/metainterp/jitprof.py +++ b/pypy/jit/metainterp/jitprof.py @@ -21,6 +21,7 @@ OPT_FORCINGS ABORT_TOO_LONG ABORT_BRIDGE ABORT_ESCAPE +ABORT_BAD_LOOP NVIRTUALS NVHOLES NVREUSED @@ -177,6 +178,7 @@ class Profiler(BaseProfiler): self._print_intline("abort: trace too long", cnt[ABORT_TOO_LONG]) self._print_intline("abort: compiling", cnt[ABORT_BRIDGE]) self._print_intline("abort: vable escape", cnt[ABORT_ESCAPE]) + self._print_intline("abort: bad loop", cnt[ABORT_BAD_LOOP]) self._print_intline("nvirtuals", cnt[NVIRTUALS]) self._print_intline("nvholes", cnt[NVHOLES]) self._print_intline("nvreused", cnt[NVREUSED]) diff --git a/pypy/jit/metainterp/logger.py b/pypy/jit/metainterp/logger.py index 898e1b4ff8..f41ec09b8c 100644 --- a/pypy/jit/metainterp/logger.py +++ b/pypy/jit/metainterp/logger.py @@ -38,6 +38,11 @@ class Logger(object): self._log_operations(inputargs, operations) debug_stop("jit-log-opt-bridge") + def log_short_preamble(self, inputargs, operations): + debug_start("jit-log-short-preamble") + self._log_operations(inputargs, operations) + debug_stop("jit-log-short-preamble") + def repr_of_descr(self, descr): return descr.repr_of_descr() diff --git a/pypy/jit/metainterp/optimize_nopspec.py b/pypy/jit/metainterp/nounroll_optimize.py index 79b5fa838c..938d64e154 100644 --- a/pypy/jit/metainterp/optimize_nopspec.py +++ b/pypy/jit/metainterp/nounroll_optimize.py @@ -1,8 +1,6 @@ from pypy.rlib.debug import debug_start, debug_stop from pypy.jit.metainterp.optimizeopt import optimize_loop_1, optimize_bridge_1 -from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder -from pypy.jit.metainterp.optimizefindnode import BridgeSpecializationFinder def optimize_loop(metainterp_sd, old_loop_tokens, loop): debug_start("jit-optimize") @@ -14,14 +12,9 @@ def optimize_loop(metainterp_sd, old_loop_tokens, loop): def _optimize_loop(metainterp_sd, old_loop_tokens, loop): cpu = metainterp_sd.cpu metainterp_sd.logger_noopt.log_loop(loop.inputargs, loop.operations) - # XXX the following lines are probably still needed, to discard invalid - # loops. bit silly to run a full perfect specialization and throw the - # result away. - finder = PerfectSpecializationFinder(cpu) - finder.find_nodes_loop(loop, False) if old_loop_tokens: return old_loop_tokens[0] - optimize_loop_1(metainterp_sd, loop) + optimize_loop_1(metainterp_sd, loop, False) return None def optimize_bridge(metainterp_sd, old_loop_tokens, bridge): @@ -34,9 +27,6 @@ def optimize_bridge(metainterp_sd, old_loop_tokens, bridge): def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge): cpu = metainterp_sd.cpu metainterp_sd.logger_noopt.log_loop(bridge.inputargs, bridge.operations) - # XXX same comment as above applies - finder = BridgeSpecializationFinder(cpu) - finder.find_nodes_bridge(bridge) if old_loop_tokens: old_loop_token = old_loop_tokens[0] bridge.operations[-1].setdescr(old_loop_token) # patch jump target diff --git a/pypy/jit/metainterp/optimize.py b/pypy/jit/metainterp/optimize.py index b0f666efc5..ba2bd52c82 100644 --- a/pypy/jit/metainterp/optimize.py +++ b/pypy/jit/metainterp/optimize.py @@ -2,9 +2,7 @@ from pypy.rlib.debug import debug_start, debug_stop # ____________________________________________________________ -from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder -from pypy.jit.metainterp.optimizeopt import optimize_loop_1 -from pypy.jit.metainterp.specnode import equals_specnodes +from pypy.jit.metainterp.optimizeopt import optimize_loop_1, optimize_bridge_1 def optimize_loop(metainterp_sd, old_loop_tokens, loop): debug_start("jit-optimize") @@ -16,19 +14,14 @@ def optimize_loop(metainterp_sd, old_loop_tokens, loop): def _optimize_loop(metainterp_sd, old_loop_tokens, loop): cpu = metainterp_sd.cpu metainterp_sd.logger_noopt.log_loop(loop.inputargs, loop.operations) - finder = PerfectSpecializationFinder(cpu) - finder.find_nodes_loop(loop) - for old_loop_token in old_loop_tokens: - if equals_specnodes(old_loop_token.specnodes, loop.token.specnodes): - return old_loop_token + # XXX do we really still need a list? + if old_loop_tokens: + return old_loop_tokens[0] optimize_loop_1(metainterp_sd, loop) return None # ____________________________________________________________ -from pypy.jit.metainterp.optimizefindnode import BridgeSpecializationFinder -from pypy.jit.metainterp.optimizeopt import optimize_bridge_1 - def optimize_bridge(metainterp_sd, old_loop_tokens, bridge): debug_start("jit-optimize") try: @@ -37,15 +30,14 @@ def optimize_bridge(metainterp_sd, old_loop_tokens, bridge): debug_stop("jit-optimize") def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge): - cpu = metainterp_sd.cpu + cpu = metainterp_sd.cpu metainterp_sd.logger_noopt.log_loop(bridge.inputargs, bridge.operations) - finder = BridgeSpecializationFinder(cpu) - finder.find_nodes_bridge(bridge) - for old_loop_token in old_loop_tokens: - if finder.bridge_matches(old_loop_token.specnodes): - bridge.operations[-1].setdescr(old_loop_token) # patch jump target - optimize_bridge_1(metainterp_sd, bridge) - return old_loop_token + if old_loop_tokens: + old_loop_token = old_loop_tokens[0] + bridge.operations[-1].setdescr(old_loop_token) # patch jump target + optimize_bridge_1(metainterp_sd, bridge) + return old_loop_tokens[0] + #return bridge.operations[-1].getdescr() return None # ____________________________________________________________ diff --git a/pypy/jit/metainterp/optimizefindnode.py b/pypy/jit/metainterp/optimizefindnode.py deleted file mode 100644 index 063de35fbb..0000000000 --- a/pypy/jit/metainterp/optimizefindnode.py +++ /dev/null @@ -1,576 +0,0 @@ -from pypy.jit.metainterp.specnode import SpecNode -from pypy.jit.metainterp.specnode import NotSpecNode, prebuiltNotSpecNode -from pypy.jit.metainterp.specnode import ConstantSpecNode -from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode -from pypy.jit.metainterp.specnode import VirtualArraySpecNode -from pypy.jit.metainterp.specnode import VirtualStructSpecNode -from pypy.jit.metainterp.history import AbstractValue, ConstInt, Const -from pypy.jit.metainterp.resoperation import rop -from pypy.jit.metainterp.executor import execute_nonspec -from pypy.jit.metainterp.optimizeutil import _findall, sort_descrs -from pypy.jit.metainterp.optimizeutil import InvalidLoop - -# ____________________________________________________________ - -UNIQUE_UNKNOWN = '\x00' -UNIQUE_NO = '\x01' -UNIQUE_INST = '\x02' -UNIQUE_ARRAY = '\x03' -UNIQUE_STRUCT = '\x04' - -class InstanceNode(object): - """An instance of this class is used to match the start and - the end of the loop, so it contains both 'origfields' that represents - the field's status at the start and 'curfields' that represents it - at the current point (== the end when optimizefindnode is complete). - """ - escaped = False # if True, then all the rest of the info is pointless - unique = UNIQUE_UNKNOWN # for find_unique_nodes() - - # fields used to store the shape of the potential VirtualInstance - knownclsbox = None # set only on freshly-allocated or fromstart structures - origfields = None # optimization; equivalent to an empty dict - curfields = None # optimization; equivalent to an empty dict - - knownvaluebox = None # a Const with the value of this box, if constant - - # fields used to store the shape of the potential VirtualList - arraydescr = None # set only on freshly-allocated or fromstart arrays - #arraysize = .. # valid if and only if arraydescr is not None - origitems = None # optimization; equivalent to an empty dict - curitems = None # optimization; equivalent to an empty dict - - # fields used to store the shape of the potential VirtualStruct - structdescr = None # set only on freshly-allocated or fromstart structs - #origfields = .. # same as above - #curfields = .. # same as above - - dependencies = None - - def __init__(self, fromstart=False): - self.fromstart = fromstart # for loops only: present since the start - - def is_constant(self): - return self.knownvaluebox is not None - - def add_escape_dependency(self, other): - assert not self.escaped - if self.dependencies is None: - self.dependencies = [] - self.dependencies.append(other) - - def mark_escaped(self): - # invariant: if escaped=True, then dependencies is None - if not self.escaped: - self.escaped = True - self.unique = UNIQUE_NO - # ^^^ always set unique to UNIQUE_NO when we set escaped to True. - # See for example test_find_nodes_store_into_loop_constant_2. - if self.dependencies is not None: - deps = self.dependencies - self.dependencies = None - for box in deps: - box.mark_escaped() - - def set_unique_nodes(self): - if self.fromstart: - self.mark_escaped() - if self.escaped or self.unique != UNIQUE_UNKNOWN: - # this node is not suitable for being a virtual, or we - # encounter it more than once when doing the recursion - self.unique = UNIQUE_NO - elif self.knownclsbox is not None: - self.unique = UNIQUE_INST - if self.curfields is not None: - for subnode in self.curfields.itervalues(): - subnode.set_unique_nodes() - elif self.arraydescr is not None: - self.unique = UNIQUE_ARRAY - if self.curitems is not None: - for subnode in self.curitems.itervalues(): - subnode.set_unique_nodes() - elif self.structdescr is not None: - self.unique = UNIQUE_STRUCT - if self.curfields is not None: - for subnode in self.curfields.itervalues(): - subnode.set_unique_nodes() - else: - assert 0, "most probably unreachable" - - def __repr__(self): - flags = '' - if self.escaped: flags += 'e' - if self.fromstart: flags += 's' - if self.knownclsbox: flags += 'c' - if self.arraydescr: flags += str(self.arraysize) - if self.structdescr: flags += 'S' - return "<InstanceNode (%s)>" % (flags,) - -# ____________________________________________________________ -# General find_nodes_xxx() interface, for both loops and bridges - -class NodeFinder(object): - """Abstract base class.""" - node_escaped = InstanceNode() - node_escaped.unique = UNIQUE_NO - node_escaped.escaped = True - - def __init__(self, cpu): - self.cpu = cpu - self.nodes = {} # Box -> InstanceNode - - def getnode(self, box): - if isinstance(box, Const): - return self.set_constant_node(box, box) - return self.nodes.get(box, self.node_escaped) - - def set_constant_node(self, box, constbox): - assert isinstance(constbox, Const) - node = InstanceNode() - node.unique = UNIQUE_NO - node.escaped = True - node.knownvaluebox = constbox - self.nodes[box] = node - return node - - def get_constant_box(self, box): - if isinstance(box, Const): - return box - try: - node = self.nodes[box] - except KeyError: - return None - else: - return node.knownvaluebox - - def find_nodes(self, operations): - for op in operations: - opnum = op.getopnum() - for value, func in find_nodes_ops: - if opnum == value: - func(self, op) - break - else: - self.find_nodes_default(op) - - def find_nodes_default(self, op): - if op.is_always_pure(): - for i in range(op.numargs()): - arg = op.getarg(i) - if self.get_constant_box(arg) is None: - break - else: - # all constant arguments: we can constant-fold - argboxes = [self.get_constant_box(op.getarg(i)) - for i in range(op.numargs())] - resbox = execute_nonspec(self.cpu, None, - op.getopnum(), argboxes, op.getdescr()) - self.set_constant_node(op.result, resbox.constbox()) - # default case: mark the arguments as escaping - for i in range(op.numargs()): - self.getnode(op.getarg(i)).mark_escaped() - - def find_nodes_no_escape(self, op): - pass # for operations that don't escape their arguments - - find_nodes_PTR_EQ = find_nodes_no_escape - find_nodes_PTR_NE = find_nodes_no_escape - ##find_nodes_INSTANCEOF = find_nodes_no_escape - find_nodes_GUARD_NONNULL = find_nodes_no_escape - find_nodes_GUARD_ISNULL = find_nodes_no_escape - - def find_nodes_NEW_WITH_VTABLE(self, op): - instnode = InstanceNode() - box = op.getarg(0) - assert isinstance(box, Const) - instnode.knownclsbox = box - self.nodes[op.result] = instnode - - def find_nodes_NEW(self, op): - instnode = InstanceNode() - instnode.structdescr = op.getdescr() - self.nodes[op.result] = instnode - - def find_nodes_NEW_ARRAY(self, op): - lengthbox = op.getarg(0) - lengthbox = self.get_constant_box(lengthbox) - if lengthbox is None: - return # var-sized arrays are not virtual - arraynode = InstanceNode() - arraynode.arraysize = lengthbox.getint() - arraynode.arraydescr = op.getdescr() - self.nodes[op.result] = arraynode - - def find_nodes_ARRAYLEN_GC(self, op): - arraynode = self.getnode(op.getarg(0)) - if arraynode.arraydescr is not None: - resbox = ConstInt(arraynode.arraysize) - self.set_constant_node(op.result, resbox) - - def find_nodes_GUARD_CLASS(self, op): - instnode = self.getnode(op.getarg(0)) - if instnode.fromstart: # only useful (and safe) in this case - box = op.getarg(1) - assert isinstance(box, Const) - instnode.knownclsbox = box - - def find_nodes_GUARD_VALUE(self, op): - instnode = self.getnode(op.getarg(0)) - if instnode.fromstart: # only useful (and safe) in this case - box = op.getarg(1) - assert isinstance(box, Const) - instnode.knownvaluebox = box - - def find_nodes_SETFIELD_GC(self, op): - instnode = self.getnode(op.getarg(0)) - fieldnode = self.getnode(op.getarg(1)) - if instnode.escaped: - fieldnode.mark_escaped() - return # nothing to be gained from tracking the field - field = op.getdescr() - assert isinstance(field, AbstractValue) - if instnode.curfields is None: - instnode.curfields = {} - instnode.curfields[field] = fieldnode - instnode.add_escape_dependency(fieldnode) - - def find_nodes_GETFIELD_GC(self, op): - instnode = self.getnode(op.getarg(0)) - if instnode.escaped: - return # nothing to be gained from tracking the field - field = op.getdescr() - assert isinstance(field, AbstractValue) - if instnode.curfields is not None and field in instnode.curfields: - fieldnode = instnode.curfields[field] - elif instnode.origfields is not None and field in instnode.origfields: - fieldnode = instnode.origfields[field] - elif instnode.fromstart: - fieldnode = InstanceNode(fromstart=True) - instnode.add_escape_dependency(fieldnode) - if instnode.origfields is None: - instnode.origfields = {} - instnode.origfields[field] = fieldnode - else: - return # nothing to be gained from tracking the field - self.nodes[op.result] = fieldnode - - find_nodes_GETFIELD_GC_PURE = find_nodes_GETFIELD_GC - - def find_nodes_SETARRAYITEM_GC(self, op): - indexbox = op.getarg(1) - indexbox = self.get_constant_box(indexbox) - if indexbox is None: - self.find_nodes_default(op) # not a Const index - return - arraynode = self.getnode(op.getarg(0)) - itemnode = self.getnode(op.getarg(2)) - if arraynode.escaped: - itemnode.mark_escaped() - return # nothing to be gained from tracking the item - if arraynode.curitems is None: - arraynode.curitems = {} - arraynode.curitems[indexbox.getint()] = itemnode - arraynode.add_escape_dependency(itemnode) - - def find_nodes_GETARRAYITEM_GC(self, op): - indexbox = op.getarg(1) - indexbox = self.get_constant_box(indexbox) - if indexbox is None: - self.find_nodes_default(op) # not a Const index - return - arraynode = self.getnode(op.getarg(0)) - if arraynode.escaped: - return # nothing to be gained from tracking the item - index = indexbox.getint() - if arraynode.curitems is not None and index in arraynode.curitems: - itemnode = arraynode.curitems[index] - elif arraynode.origitems is not None and index in arraynode.origitems: - itemnode = arraynode.origitems[index] - elif arraynode.fromstart: - itemnode = InstanceNode(fromstart=True) - arraynode.add_escape_dependency(itemnode) - if arraynode.origitems is None: - arraynode.origitems = {} - arraynode.origitems[index] = itemnode - else: - return # nothing to be gained from tracking the item - self.nodes[op.result] = itemnode - - find_nodes_GETARRAYITEM_GC_PURE = find_nodes_GETARRAYITEM_GC - - def find_nodes_JUMP(self, op): - # only set up the 'unique' field of the InstanceNodes; - # real handling comes later (build_result_specnodes() for loops). - for i in range(op.numargs()): - box = op.getarg(i) - self.getnode(box).set_unique_nodes() - - def find_nodes_FINISH(self, op): - # only for bridges, and only for the ones that end in a 'return' - # or 'raise'; all other cases end with a JUMP. - for i in range(op.numargs()): - box = op.getarg(i) - self.getnode(box).unique = UNIQUE_NO - -find_nodes_ops = _findall(NodeFinder, 'find_nodes_') - -# ____________________________________________________________ -# Perfect specialization -- for loops only - -class PerfectSpecializationFinder(NodeFinder): - node_fromstart = InstanceNode(fromstart=True) - - def find_nodes_loop(self, loop, build_specnodes=True): - self._loop = loop - self.setup_input_nodes(loop.inputargs) - self.find_nodes(loop.operations) - if build_specnodes: - self.build_result_specnodes(loop) - - def show(self): - from pypy.jit.metainterp.viewnode import viewnodes, view - op = self._loop.operations[-1] - assert op.getopnum() == rop.JUMP - exitnodes = [self.getnode(arg) for arg in op.args] - viewnodes(self.inputnodes, exitnodes) - if hasattr(self._loop.token, "specnodes"): - view(*self._loop.token.specnodes) - - - def setup_input_nodes(self, inputargs): - inputnodes = [] - for box in inputargs: - instnode = InstanceNode(fromstart=True) - inputnodes.append(instnode) - self.nodes[box] = instnode - self.inputnodes = inputnodes - - def build_result_specnodes(self, loop): - # Build the list of specnodes based on the result - # computed by NodeFinder.find_nodes(). - op = loop.operations[-1] - assert op.getopnum() == rop.JUMP - assert len(self.inputnodes) == op.numargs() - while True: - self.restart_needed = False - specnodes = [] - for i in range(op.numargs()): - inputnode = self.inputnodes[i] - exitnode = self.getnode(op.getarg(i)) - specnodes.append(self.intersect(inputnode, exitnode)) - if not self.restart_needed: - break - loop.token.specnodes = specnodes - - def intersect(self, inputnode, exitnode): - assert inputnode.fromstart - if inputnode.is_constant() and \ - exitnode.is_constant(): - if inputnode.knownvaluebox.same_constant(exitnode.knownvaluebox): - return ConstantSpecNode(inputnode.knownvaluebox) - else: - raise InvalidLoop - if inputnode.escaped: - return prebuiltNotSpecNode - unique = exitnode.unique - if unique == UNIQUE_NO: - if inputnode is not self.node_fromstart: - # Mark the input node as escaped, and schedule a complete - # restart of intersect(). This is needed because there is - # an order dependency: calling inputnode.mark_escaped() - # might set the field exitnode.unique to UNIQUE_NO in some - # other node. If inputnode is node_fromstart, there is no - # problem (and it must not be mutated by mark_escaped() then). - inputnode.mark_escaped() - self.restart_needed = True - return prebuiltNotSpecNode - if unique == UNIQUE_INST: - return self.intersect_instance(inputnode, exitnode) - if unique == UNIQUE_ARRAY: - return self.intersect_array(inputnode, exitnode) - if unique == UNIQUE_STRUCT: - return self.intersect_struct(inputnode, exitnode) - assert 0, "unknown value for exitnode.unique: %d" % ord(unique) - - def compute_common_fields(self, orig, d): - fields = [] - if orig is not None: - if d is not None: - d = d.copy() - else: - d = {} - for ofs in orig: - d.setdefault(ofs, self.node_escaped) - if d is not None: - lst = d.keys() - # we always use the "standardized" order of fields - sort_descrs(lst) - for ofs in lst: - try: - if orig is None: - raise KeyError - node = orig[ofs] - except KeyError: - # field stored at exit, but not read at input. Must - # still be allocated, otherwise it will be incorrectly - # uninitialized after a guard failure. - node = self.node_fromstart - specnode = self.intersect(node, d[ofs]) - fields.append((ofs, specnode)) - return fields - - def intersect_instance(self, inputnode, exitnode): - if (inputnode.knownclsbox is not None and - not inputnode.knownclsbox.same_constant(exitnode.knownclsbox)): - # unique match, but the class is known to be a mismatch - raise InvalidLoop - # - fields = self.compute_common_fields(inputnode.origfields, - exitnode.curfields) - return VirtualInstanceSpecNode(exitnode.knownclsbox, fields) - - def intersect_array(self, inputnode, exitnode): - assert inputnode.arraydescr is None - # - items = [] - for i in range(exitnode.arraysize): - if exitnode.curitems is None: - exitsubnode = self.node_escaped - else: - exitsubnode = exitnode.curitems.get(i, self.node_escaped) - if inputnode.origitems is None: - node = self.node_fromstart - else: - node = inputnode.origitems.get(i, self.node_fromstart) - specnode = self.intersect(node, exitsubnode) - items.append(specnode) - return VirtualArraySpecNode(exitnode.arraydescr, items) - - def intersect_struct(self, inputnode, exitnode): - assert inputnode.structdescr is None - # - fields = self.compute_common_fields(inputnode.origfields, - exitnode.curfields) - return VirtualStructSpecNode(exitnode.structdescr, fields) - -# ____________________________________________________________ -# A subclass of NodeFinder for bridges only - -class __extend__(SpecNode): - def make_instance_node(self): - raise NotImplementedError - def matches_instance_node(self, exitnode): - raise NotImplementedError - -class __extend__(NotSpecNode): - def make_instance_node(self): - return NodeFinder.node_escaped - def matches_instance_node(self, exitnode): - return True - -class __extend__(ConstantSpecNode): - def make_instance_node(self): - raise AssertionError, "not implemented (but not used actually)" - def matches_instance_node(self, exitnode): - if exitnode.knownvaluebox is None: - return False - return self.constbox.same_constant(exitnode.knownvaluebox) - -class __extend__(VirtualInstanceSpecNode): - def make_instance_node(self): - instnode = InstanceNode() - instnode.knownclsbox = self.known_class - instnode.curfields = {} - for ofs, subspecnode in self.fields: - instnode.curfields[ofs] = subspecnode.make_instance_node() - return instnode - - def matches_instance_node(self, exitnode): - if exitnode.unique == UNIQUE_NO: - return False - # - assert exitnode.unique == UNIQUE_INST - if not self.known_class.same_constant(exitnode.knownclsbox): - # unique match, but the class is known to be a mismatch - return False - # - return matches_fields(self.fields, exitnode.curfields) - -def matches_fields(fields, d): - seen = 0 - for ofs, subspecnode in fields: - try: - if d is None: - raise KeyError - instnode = d[ofs] - seen += 1 - except KeyError: - instnode = NodeFinder.node_escaped - if not subspecnode.matches_instance_node(instnode): - return False - if d is not None and len(d) > seen: - return False # some key is in d but not in fields - return True - -class __extend__(VirtualArraySpecNode): - def make_instance_node(self): - raise AssertionError, "not implemented (but not used actually)" - def matches_instance_node(self, exitnode): - if exitnode.unique == UNIQUE_NO: - return False - # - assert exitnode.unique == UNIQUE_ARRAY - assert self.arraydescr == exitnode.arraydescr - if len(self.items) != exitnode.arraysize: - # the size is known to be a mismatch - return False - # - d = exitnode.curitems - for i in range(exitnode.arraysize): - try: - if d is None: - raise KeyError - itemnode = d[i] - except KeyError: - itemnode = NodeFinder.node_escaped - subspecnode = self.items[i] - if not subspecnode.matches_instance_node(itemnode): - return False - return True - -class __extend__(VirtualStructSpecNode): - def make_instance_node(self): - raise AssertionError, "not implemented (but not used actually)" - def matches_instance_node(self, exitnode): - if exitnode.unique == UNIQUE_NO: - return False - # - assert exitnode.unique == UNIQUE_STRUCT - assert self.typedescr == exitnode.structdescr - # - return matches_fields(self.fields, exitnode.curfields) - - -class BridgeSpecializationFinder(NodeFinder): - - def find_nodes_bridge(self, bridge, specnodes=None): - if specnodes is not None: # not used actually - self.setup_bridge_input_nodes(specnodes, bridge.inputargs) - self.find_nodes(bridge.operations) - self.jump_op = bridge.operations[-1] - - def setup_bridge_input_nodes(self, specnodes, inputargs): - assert len(specnodes) == len(inputargs) - for i in range(len(inputargs)): - instnode = specnodes[i].make_instance_node() - box = inputargs[i] - self.nodes[box] = instnode - - def bridge_matches(self, nextloop_specnodes): - jump_op = self.jump_op - assert jump_op.numargs() == len(nextloop_specnodes) - for i in range(len(nextloop_specnodes)): - exitnode = self.getnode(jump_op.getarg(i)) - if not nextloop_specnodes[i].matches_instance_node(exitnode): - return False - return True diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py index b1f7092b53..389508f56b 100644 --- a/pypy/jit/metainterp/optimizeopt/__init__.py +++ b/pypy/jit/metainterp/optimizeopt/__init__.py @@ -4,17 +4,17 @@ from pypy.jit.metainterp.optimizeopt.intbounds import OptIntBounds from pypy.jit.metainterp.optimizeopt.virtualize import OptVirtualize from pypy.jit.metainterp.optimizeopt.heap import OptHeap from pypy.jit.metainterp.optimizeopt.string import OptString +from pypy.jit.metainterp.optimizeopt.unroll import optimize_unroll, OptInlineShortPreamble -def optimize_loop_1(metainterp_sd, loop, virtuals=True): - """Optimize loop.operations to make it match the input of loop.specnodes - and to remove internal overheadish operations. Note that loop.specnodes - must be applicable to the loop; you will probably get an AssertionError - if not. +def optimize_loop_1(metainterp_sd, loop, unroll=True): + """Optimize loop.operations to remove internal overheadish operations. """ - optimizations = [OptIntBounds(), + opt_str = OptString() + optimizations = [OptInlineShortPreamble(), + OptIntBounds(), OptRewrite(), OptVirtualize(), - OptString(), + opt_str, OptHeap(), ] if metainterp_sd.jit_ffi: @@ -22,11 +22,15 @@ def optimize_loop_1(metainterp_sd, loop, virtuals=True): optimizations = optimizations + [ OptFfiCall(), ] - optimizer = Optimizer(metainterp_sd, loop, optimizations, virtuals) - optimizer.propagate_all_forward() + + if unroll: + opt_str.enabled = False # FIXME: Workaround to disable string optimisation + # during preamble but to keep it during the loop + optimize_unroll(metainterp_sd, loop, optimizations) + else: + optimizer = Optimizer(metainterp_sd, loop, optimizations) + optimizer.propagate_all_forward() def optimize_bridge_1(metainterp_sd, bridge): - """The same, but for a bridge. The only difference is that we don't - expect 'specnodes' on the bridge. - """ + """The same, but for a bridge. """ optimize_loop_1(metainterp_sd, bridge, False) diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py index f3be2ec3dc..833bf60b40 100644 --- a/pypy/jit/metainterp/optimizeopt/fficall.py +++ b/pypy/jit/metainterp/optimizeopt/fficall.py @@ -73,6 +73,10 @@ class OptFfiCall(Optimization): def __init__(self): self.funcinfo = None + def reconstruct_for_next_iteration(self, optimizer, valuemap): + return OptFfiCall() + # FIXME: Should any status be saved for next iteration? + def begin_optimization(self, funcval, op): self.rollback_maybe() self.funcinfo = FuncInfo(funcval, self.optimizer.cpu, op) diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py index a564071126..c2488661d6 100644 --- a/pypy/jit/metainterp/optimizeopt/heap.py +++ b/pypy/jit/metainterp/optimizeopt/heap.py @@ -23,6 +23,42 @@ class OptHeap(Optimization): self.lazy_setfields = {} self.lazy_setfields_descrs = [] # keys (at least) of previous dict + def reconstruct_for_next_iteration(self, optimizer, valuemap): + new = OptHeap() + + if True: + self.force_all_lazy_setfields() + assert not self.lazy_setfields_descrs + assert not self.lazy_setfields + else: + new.lazy_setfields_descrs = self.lazy_setfields_descrs + new.lazy_setfields = self.lazy_setfields + + for descr, d in self.cached_fields.items(): + newd = {} + new.cached_fields[descr] = newd + for value, fieldvalue in d.items(): + newd[value.get_reconstructed(optimizer, valuemap)] = \ + fieldvalue.get_reconstructed(optimizer, valuemap) + + new.cached_arrayitems = {} + for descr, d in self.cached_arrayitems.items(): + newd = {} + new.cached_arrayitems[descr] = newd + for value, cache in d.items(): + newcache = CachedArrayItems() + newd[value.get_reconstructed(optimizer, valuemap)] = newcache + if cache.var_index_item: + newcache.var_index_item = \ + cache.var_index_item.get_reconstructed(optimizer, valuemap) + if newcache.var_index_indexvalue: + newcache.var_index_indexvalue = \ + cache.var_index_indexvalue.get_reconstructed(optimizer, valuemap) + for index, fieldvalue in cache.fixed_index_items.items(): + newcache.fixed_index_items[index] = \ + fieldvalue.get_reconstructed(optimizer, valuemap) + return new + def clean_caches(self): self.cached_fields.clear() self.cached_arrayitems.clear() @@ -97,7 +133,7 @@ class OptHeap(Optimization): self.emitting_operation(op) self.next_optimization.propagate_forward(op) - def emitting_operation(self, op): + def emitting_operation(self, op): if op.has_no_side_effect(): return if op.is_ovf(): @@ -107,7 +143,9 @@ class OptHeap(Optimization): return opnum = op.getopnum() if (opnum == rop.SETFIELD_GC or + opnum == rop.SETFIELD_RAW or opnum == rop.SETARRAYITEM_GC or + opnum == rop.SETARRAYITEM_RAW or opnum == rop.DEBUG_MERGE_POINT): return assert opnum != rop.CALL_PURE @@ -147,6 +185,16 @@ class OptHeap(Optimization): self.clean_caches() + def turned_constant(self, value): + assert value.is_constant() + newvalue = self.getvalue(value.box) + if value is not newvalue: + for d in self.cached_fields.values(): + if value in d: + d[newvalue] = d[value] + # FIXME: Update the other caches too? + + def force_lazy_setfield(self, descr, before_guard=False): try: op = self.lazy_setfields[descr] diff --git a/pypy/jit/metainterp/optimizeopt/intbounds.py b/pypy/jit/metainterp/optimizeopt/intbounds.py index 201517f718..f15c884527 100644 --- a/pypy/jit/metainterp/optimizeopt/intbounds.py +++ b/pypy/jit/metainterp/optimizeopt/intbounds.py @@ -6,17 +6,35 @@ from pypy.jit.metainterp.history import Const, ConstInt from pypy.jit.metainterp.resoperation import rop, ResOperation class OptIntBounds(Optimization): - """Keeps track of the bounds placed on integers by the guards and - remove redundant guards""" + """Keeps track of the bounds placed on integers by guards and remove + redundant guards""" + + def setup(self): + self.posponedop = None + self.nextop = None + + def reconstruct_for_next_iteration(self, optimizer, valuemap): + assert self.posponedop is None + return self def propagate_forward(self, op): + if op.is_ovf(): + self.posponedop = op + return + if self.posponedop: + self.nextop = op + op = self.posponedop + self.posponedop = None + opnum = op.getopnum() for value, func in optimize_ops: if opnum == value: func(self, op) break else: + assert not op.is_ovf() self.emit_operation(op) + def propagate_bounds_backward(self, box): # FIXME: This takes care of the instruction where box is the reuslt @@ -44,6 +62,18 @@ class OptIntBounds(Optimization): optimize_GUARD_FALSE = optimize_GUARD_TRUE optimize_GUARD_VALUE = optimize_GUARD_TRUE + def optimize_INT_XOR(self, op): + v1 = self.getvalue(op.getarg(0)) + v2 = self.getvalue(op.getarg(1)) + if v1 is v2: + self.make_constant_int(op.result, 0) + return + self.emit_operation(op) + if v1.intbound.known_ge(IntBound(0, 0)) and \ + v2.intbound.known_ge(IntBound(0, 0)): + r = self.getvalue(op.result) + r.intbound.make_ge(IntLowerBound(0)) + def optimize_INT_AND(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) @@ -80,50 +110,71 @@ class OptIntBounds(Optimization): r = self.getvalue(op.result) r.intbound.intersect(v1.intbound.mul_bound(v2.intbound)) + def optimize_INT_FLOORDIV(self, op): + v1 = self.getvalue(op.getarg(0)) + v2 = self.getvalue(op.getarg(1)) + self.emit_operation(op) + r = self.getvalue(op.result) + r.intbound.intersect(v1.intbound.div_bound(v2.intbound)) + + def optimize_INT_LSHIFT(self, op): + v1 = self.getvalue(op.getarg(0)) + v2 = self.getvalue(op.getarg(1)) + self.emit_operation(op) + r = self.getvalue(op.result) + r.intbound.intersect(v1.intbound.lshift_bound(v2.intbound)) + + def optimize_INT_RSHIFT(self, op): + v1 = self.getvalue(op.getarg(0)) + v2 = self.getvalue(op.getarg(1)) + self.emit_operation(op) + r = self.getvalue(op.result) + r.intbound.intersect(v1.intbound.rshift_bound(v2.intbound)) + def optimize_INT_ADD_OVF(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) resbound = v1.intbound.add_bound(v2.intbound) if resbound.has_lower and resbound.has_upper and \ - self.nextop().getopnum() == rop.GUARD_NO_OVERFLOW: + self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: # Transform into INT_ADD and remove guard op = op.copy_and_change(rop.INT_ADD) - self.skip_nextop() self.optimize_INT_ADD(op) # emit the op else: self.emit_operation(op) r = self.getvalue(op.result) r.intbound.intersect(resbound) + self.emit_operation(self.nextop) def optimize_INT_SUB_OVF(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) resbound = v1.intbound.sub_bound(v2.intbound) if resbound.has_lower and resbound.has_upper and \ - self.nextop().getopnum() == rop.GUARD_NO_OVERFLOW: + self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: # Transform into INT_SUB and remove guard op = op.copy_and_change(rop.INT_SUB) - self.skip_nextop() self.optimize_INT_SUB(op) # emit the op else: self.emit_operation(op) r = self.getvalue(op.result) r.intbound.intersect(resbound) - + self.emit_operation(self.nextop) + def optimize_INT_MUL_OVF(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) resbound = v1.intbound.mul_bound(v2.intbound) if resbound.has_lower and resbound.has_upper and \ - self.nextop().getopnum() == rop.GUARD_NO_OVERFLOW: + self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: # Transform into INT_MUL and remove guard op = op.copy_and_change(rop.INT_MUL) - self.skip_nextop() self.optimize_INT_MUL(op) # emit the op else: self.emit_operation(op) r = self.getvalue(op.result) r.intbound.intersect(resbound) + self.emit_operation(self.nextop) def optimize_INT_LT(self, op): v1 = self.getvalue(op.getarg(0)) @@ -302,6 +353,14 @@ class OptIntBounds(Optimization): if v2.intbound.intersect(b): self.propagate_bounds_backward(op.getarg(1)) + def propagate_bounds_INT_LSHIFT(self, op): + v1 = self.getvalue(op.getarg(0)) + v2 = self.getvalue(op.getarg(1)) + r = self.getvalue(op.result) + b = r.intbound.rshift_bound(v2.intbound) + if v1.intbound.intersect(b): + self.propagate_bounds_backward(op.getarg(0)) + propagate_bounds_INT_ADD_OVF = propagate_bounds_INT_ADD propagate_bounds_INT_SUB_OVF = propagate_bounds_INT_SUB propagate_bounds_INT_MUL_OVF = propagate_bounds_INT_MUL diff --git a/pypy/jit/metainterp/optimizeopt/intutils.py b/pypy/jit/metainterp/optimizeopt/intutils.py index f4d10c57a4..260db4f8a3 100644 --- a/pypy/jit/metainterp/optimizeopt/intutils.py +++ b/pypy/jit/metainterp/optimizeopt/intutils.py @@ -155,6 +155,37 @@ class IntBound(object): else: return IntUnbounded() + def lshift_bound(self, other): + if self.has_upper and self.has_lower and \ + other.has_upper and other.has_lower and \ + other.known_ge(IntBound(0, 0)): + try: + vals = (ovfcheck(self.upper * pow2(other.upper)), + ovfcheck(self.upper * pow2(other.lower)), + ovfcheck(self.lower * pow2(other.upper)), + ovfcheck(self.lower * pow2(other.lower))) + return IntBound(min4(vals), max4(vals)) + except OverflowError: + return IntUnbounded() + else: + return IntUnbounded() + + def rshift_bound(self, other): + if self.has_upper and self.has_lower and \ + other.has_upper and other.has_lower and \ + other.known_ge(IntBound(0, 0)): + try: + vals = (ovfcheck(self.upper / pow2(other.upper)), + ovfcheck(self.upper / pow2(other.lower)), + ovfcheck(self.lower / pow2(other.upper)), + ovfcheck(self.lower / pow2(other.lower))) + return IntBound(min4(vals), max4(vals)) + except OverflowError: + return IntUnbounded() + else: + return IntUnbounded() + + def contains(self, val): if self.has_lower and val < self.lower: return False @@ -205,3 +236,11 @@ def min4(t): def max4(t): return max(max(t[0], t[1]), max(t[2], t[3])) + +def pow2(x): + y = 1 << x + if y < 1: + raise OverflowError, "pow2 did overflow" + return y + + diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py index 338ab8ed8f..5d3acacba1 100644 --- a/pypy/jit/metainterp/optimizeopt/optimizer.py +++ b/pypy/jit/metainterp/optimizeopt/optimizer.py @@ -45,6 +45,26 @@ class OptValue(object): def get_key_box(self): return self.box + def enum_forced_boxes(self, boxes, already_seen): + key = self.get_key_box() + if key not in already_seen: + boxes.append(self.force_box()) + already_seen[self.get_key_box()] = None + + def get_reconstructed(self, optimizer, valuemap): + if self in valuemap: + return valuemap[self] + new = self.reconstruct_for_next_iteration(optimizer) + valuemap[self] = new + self.reconstruct_childs(new, valuemap) + return new + + def reconstruct_for_next_iteration(self, optimizer): + return self + + def reconstruct_childs(self, new, valuemap): + pass + def get_args_for_fail(self, modifier): pass @@ -100,6 +120,12 @@ class OptValue(object): box = self.box assert isinstance(box, Const) return box.nonnull() + elif self.intbound: + if self.intbound.known_gt(IntBound(0, 0)) or \ + self.intbound.known_lt(IntBound(0, 0)): + return True + else: + return False else: return False @@ -142,12 +168,20 @@ llhelper.CVAL_NULLREF = ConstantValue(llhelper.CONST_NULL) oohelper.CVAL_NULLREF = ConstantValue(oohelper.CONST_NULL) class Optimization(object): + next_optimization = None + def propagate_forward(self, op): raise NotImplementedError def emit_operation(self, op): self.next_optimization.propagate_forward(op) + def test_emittable(self, op): + return self.is_emittable(op) + + def is_emittable(self, op): + return self.next_optimization.test_emittable(op) + # FIXME: Move some of these here? def getvalue(self, box): return self.optimizer.getvalue(box) @@ -180,18 +214,23 @@ class Optimization(object): op = ResOperation(opnum, args, result) self.optimizer.pure_operations[self.optimizer.make_args_key(op)] = op - def nextop(self): - return self.optimizer.loop.operations[self.optimizer.i + 1] + def setup(self): + pass - def skip_nextop(self): - self.optimizer.i += 1 + def force_at_end_of_preamble(self): + pass - def setup(self, virtuals): + def turned_constant(self, value): pass + def reconstruct_for_next_iteration(self, optimizer=None, valuemap=None): + #return self.__class__() + raise NotImplementedError + + class Optimizer(Optimization): - def __init__(self, metainterp_sd, loop, optimizations=None, virtuals=True): + def __init__(self, metainterp_sd, loop, optimizations=None): self.metainterp_sd = metainterp_sd self.cpu = metainterp_sd.cpu self.loop = loop @@ -203,7 +242,13 @@ class Optimizer(Optimization): self.pure_operations = args_dict() self.producer = {} self.pendingfields = [] + self.posponedop = None + self.exception_might_have_happened = False + self.newoperations = [] + self.set_optimizations(optimizations) + + def set_optimizations(self, optimizations): if optimizations: self.first_optimization = optimizations[0] for i in range(1, len(optimizations)): @@ -211,9 +256,50 @@ class Optimizer(Optimization): optimizations[-1].next_optimization = self for o in optimizations: o.optimizer = self - o.setup(virtuals) + o.setup() else: + optimizations = [] self.first_optimization = self + + self.optimizations = optimizations + + def force_at_end_of_preamble(self): + self.resumedata_memo = resume.ResumeDataLoopMemo(self.metainterp_sd) + for o in self.optimizations: + o.force_at_end_of_preamble() + + def reconstruct_for_next_iteration(self, optimizer=None, valuemap=None): + assert optimizer is None + assert valuemap is None + valuemap = {} + new = Optimizer(self.metainterp_sd, self.loop) + optimizations = [o.reconstruct_for_next_iteration(new, valuemap) for o in + self.optimizations] + new.set_optimizations(optimizations) + + new.values = {} + for box, value in self.values.items(): + new.values[box] = value.get_reconstructed(new, valuemap) + new.interned_refs = self.interned_refs + new.bool_boxes = {} + for value in new.bool_boxes.keys(): + new.bool_boxes[value.get_reconstructed(new, valuemap)] = None + + # FIXME: Move to rewrite.py + new.loop_invariant_results = {} + for key, value in self.loop_invariant_results.items(): + new.loop_invariant_results[key] = \ + value.get_reconstructed(new, valuemap) + + new.pure_operations = self.pure_operations + new.producer = self.producer + assert self.posponedop is None + + return new + + def turned_constant(self, value): + for o in self.optimizations: + o.turned_constant(value) def forget_numberings(self, virtualbox): self.metainterp_sd.profiler.count(jitprof.OPT_FORCINGS) @@ -252,9 +338,9 @@ class Optimizer(Optimization): return constbox return None - def make_equal_to(self, box, value): + def make_equal_to(self, box, value, replace=False): assert isinstance(value, OptValue) - assert box not in self.values + assert replace or box not in self.values self.values[box] = value def make_constant(self, box, constbox): @@ -306,7 +392,6 @@ class Optimizer(Optimization): self.i = 0 while self.i < len(self.loop.operations): op = self.loop.operations[self.i] - #print "OP: %s" % op self.first_optimization.propagate_forward(op) self.i += 1 self.loop.operations = self.newoperations @@ -327,7 +412,9 @@ class Optimizer(Optimization): self.optimize_default(op) #print '\n'.join([str(o) for o in self.newoperations]) + '\n---\n' - + def test_emittable(self, op): + return True + def emit_operation(self, op): ###self.heap_op_optimizer.emitting_operation(op) self._emit_operation(op) @@ -350,6 +437,8 @@ class Optimizer(Optimization): def store_final_boxes_in_guard(self, op): ###pendingfields = self.heap_op_optimizer.force_lazy_setfields_for_guard() + if op.getjumptarget(): + return op descr = op.getdescr() assert isinstance(descr, compile.ResumeGuardDescr) modifier = resume.ResumeDataVirtualAdder(descr, self.resumedata_memo) @@ -380,22 +469,33 @@ class Optimizer(Optimization): return op def make_args_key(self, op): - args = [] - for i in range(op.numargs()): + n = op.numargs() + args = [None] * (n + 1) + for i in range(n): arg = op.getarg(i) - if arg in self.values: - args.append(self.values[arg].get_key_box()) + try: + value = self.values[arg] + except KeyError: + pass else: - args.append(arg) - args.append(ConstInt(op.getopnum())) + arg = value.get_key_box() + args[i] = arg + args[n] = ConstInt(op.getopnum()) return args def optimize_default(self, op): canfold = op.is_always_pure() - is_ovf = op.is_ovf() - if is_ovf: - nextop = self.loop.operations[self.i + 1] + if op.is_ovf(): + self.posponedop = op + return + if self.posponedop: + nextop = op + op = self.posponedop + self.posponedop = None canfold = nextop.getopnum() == rop.GUARD_NO_OVERFLOW + else: + nextop = None + if canfold: for i in range(op.numargs()): if self.get_constant_box(op.getarg(i)) is None: @@ -406,9 +506,8 @@ class Optimizer(Optimization): for i in range(op.numargs())] resbox = execute_nonspec(self.cpu, None, op.getopnum(), argboxes, op.getdescr()) + # FIXME: Don't we need to check for an overflow here? self.make_constant(op.result, resbox.constbox()) - if is_ovf: - self.i += 1 # skip next operation, it is the unneeded guard return # did we do the exact same operation already? @@ -416,20 +515,22 @@ class Optimizer(Optimization): oldop = self.pure_operations.get(args, None) if oldop is not None and oldop.getdescr() is op.getdescr(): assert oldop.getopnum() == op.getopnum() - self.make_equal_to(op.result, self.getvalue(oldop.result)) - if is_ovf: - self.i += 1 # skip next operation, it is the unneeded guard + self.make_equal_to(op.result, self.getvalue(oldop.result), + True) return else: self.pure_operations[args] = op # otherwise, the operation remains self.emit_operation(op) - - def optimize_GUARD_NO_OVERFLOW(self, op): - # otherwise the default optimizer will clear fields, which is unwanted - # in this case - self.emit_operation(op) + if nextop: + self.emit_operation(nextop) + + #def optimize_GUARD_NO_OVERFLOW(self, op): + # # otherwise the default optimizer will clear fields, which is unwanted + # # in this case + # self.emit_operation(op) + # FIXME: Is this still needed? def optimize_DEBUG_MERGE_POINT(self, op): self.emit_operation(op) diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py index 05db94b676..3f2e9afa20 100644 --- a/pypy/jit/metainterp/optimizeopt/rewrite.py +++ b/pypy/jit/metainterp/optimizeopt/rewrite.py @@ -9,6 +9,9 @@ class OptRewrite(Optimization): """Rewrite operations into equivalent, cheaper operations. This includes already executed operations and constants. """ + + def reconstruct_for_next_iteration(self, optimizer, valuemap): + return self def propagate_forward(self, op): args = self.optimizer.make_args_key(op) @@ -22,6 +25,18 @@ class OptRewrite(Optimization): break else: self.emit_operation(op) + + def test_emittable(self, op): + opnum = op.getopnum() + for value, func in optimize_guards: + if opnum == value: + try: + func(self, op, dryrun=True) + return self.is_emittable(op) + except InvalidLoop: + return False + return self.is_emittable(op) + def try_boolinvers(self, op, targs): oldop = self.optimizer.pure_operations.get(targs, None) @@ -142,7 +157,7 @@ class OptRewrite(Optimization): self.emit_operation(ResOperation(rop.CALL, args, op.result, op.getdescr())) - def optimize_guard(self, op, constbox, emit_operation=True): + def optimize_guard(self, op, constbox, emit_operation=True, dryrun=False): value = self.getvalue(op.getarg(0)) if value.is_constant(): box = value.box @@ -150,32 +165,36 @@ class OptRewrite(Optimization): if not box.same_constant(constbox): raise InvalidLoop return + if dryrun: return if emit_operation: self.emit_operation(op) value.make_constant(constbox) + self.optimizer.turned_constant(value) - def optimize_GUARD_ISNULL(self, op): + def optimize_GUARD_ISNULL(self, op, dryrun=False): value = self.getvalue(op.getarg(0)) if value.is_null(): return elif value.is_nonnull(): raise InvalidLoop + if dryrun: return self.emit_operation(op) value.make_constant(self.optimizer.cpu.ts.CONST_NULL) - def optimize_GUARD_NONNULL(self, op): + def optimize_GUARD_NONNULL(self, op, dryrun=False): value = self.getvalue(op.getarg(0)) if value.is_nonnull(): return elif value.is_null(): raise InvalidLoop + if dryrun: return self.emit_operation(op) value.make_nonnull(len(self.optimizer.newoperations) - 1) - def optimize_GUARD_VALUE(self, op): + def optimize_GUARD_VALUE(self, op, dryrun=False): value = self.getvalue(op.getarg(0)) emit_operation = True - if value.last_guard_index != -1: + if not dryrun and value.last_guard_index != -1: # there already has been a guard_nonnull or guard_class or # guard_nonnull_class on this value, which is rather silly. # replace the original guard with a guard_value @@ -193,25 +212,24 @@ class OptRewrite(Optimization): emit_operation = False constbox = op.getarg(1) assert isinstance(constbox, Const) - self.optimize_guard(op, constbox, emit_operation) + self.optimize_guard(op, constbox, emit_operation, dryrun) - def optimize_GUARD_TRUE(self, op): - self.optimize_guard(op, CONST_1) + def optimize_GUARD_TRUE(self, op, dryrun=False): + self.optimize_guard(op, CONST_1, dryrun=dryrun) - def optimize_GUARD_FALSE(self, op): - self.optimize_guard(op, CONST_0) + def optimize_GUARD_FALSE(self, op, dryrun=False): + self.optimize_guard(op, CONST_0, dryrun=dryrun) - def optimize_GUARD_CLASS(self, op): + def optimize_GUARD_CLASS(self, op, dryrun=False): value = self.getvalue(op.getarg(0)) expectedclassbox = op.getarg(1) assert isinstance(expectedclassbox, Const) realclassbox = value.get_constant_class(self.optimizer.cpu) if realclassbox is not None: - # the following assert should always be true for now, - # because invalid loops that would fail it are detected - # earlier, in optimizefindnode.py. - assert realclassbox.same_constant(expectedclassbox) - return + if realclassbox.same_constant(expectedclassbox): + return + raise InvalidLoop + if dryrun: return emit_operation = True if value.last_guard_index != -1: # there already has been a guard_nonnull or guard_class or @@ -237,7 +255,12 @@ class OptRewrite(Optimization): last_guard_index = value.last_guard_index value.make_constant_class(expectedclassbox, last_guard_index) - def optimize_GUARD_NO_EXCEPTION(self, op): + def optimize_GUARD_NONNULL_CLASS(self, op, dryrun=False): + self.optimize_GUARD_NONNULL(op, True) + self.optimize_GUARD_CLASS(op, dryrun) + + def optimize_GUARD_NO_EXCEPTION(self, op, dryrun=False): + if dryrun: return if not self.optimizer.exception_might_have_happened: return self.emit_operation(op) @@ -357,4 +380,4 @@ class OptRewrite(Optimization): return False optimize_ops = _findall(OptRewrite, 'optimize_') - +optimize_guards = _findall(OptRewrite, 'optimize_', 'GUARD') diff --git a/pypy/jit/metainterp/optimizeopt/string.py b/pypy/jit/metainterp/optimizeopt/string.py index 52fc934650..6680885490 100644 --- a/pypy/jit/metainterp/optimizeopt/string.py +++ b/pypy/jit/metainterp/optimizeopt/string.py @@ -162,6 +162,17 @@ class VStringPlainValue(VAbstractStringValue): for value in self._chars: value.get_args_for_fail(modifier) + def FIXME_enum_forced_boxes(self, boxes, already_seen): + key = self.get_key_box() + if key in already_seen: + return + already_seen[key] = None + if self.box is None: + for box in self._chars: + box.enum_forced_boxes(boxes, already_seen) + else: + boxes.append(self.box) + def _make_virtual(self, modifier): return modifier.make_vstrplain(self.mode is mode_unicode) @@ -169,12 +180,22 @@ class VStringPlainValue(VAbstractStringValue): class VStringConcatValue(VAbstractStringValue): """The concatenation of two other strings.""" - def setup(self, left, right, lengthbox): + lengthbox = None # or the computed length + + def setup(self, left, right): self.left = left self.right = right - self.lengthbox = lengthbox - def getstrlen(self, _, mode): + def getstrlen(self, newoperations, mode): + if self.lengthbox is None: + len1box = self.left.getstrlen(newoperations, mode) + if len1box is None: + return None + len2box = self.right.getstrlen(newoperations, mode) + if len2box is None: + return None + self.lengthbox = _int_add(newoperations, len1box, len2box) + # ^^^ may still be None, if newoperations is None return self.lengthbox @specialize.arg(1) @@ -204,6 +225,18 @@ class VStringConcatValue(VAbstractStringValue): self.left.get_args_for_fail(modifier) self.right.get_args_for_fail(modifier) + def FIXME_enum_forced_boxes(self, boxes, already_seen): + key = self.get_key_box() + if key in already_seen: + return + already_seen[key] = None + if self.box is None: + self.left.enum_forced_boxes(boxes, already_seen) + self.right.enum_forced_boxes(boxes, already_seen) + self.lengthbox = None + else: + boxes.append(self.box) + def _make_virtual(self, modifier): return modifier.make_vstrconcat(self.mode is mode_unicode) @@ -250,6 +283,18 @@ class VStringSliceValue(VAbstractStringValue): self.vstart.get_args_for_fail(modifier) self.vlength.get_args_for_fail(modifier) + def FIXME_enum_forced_boxes(self, boxes, already_seen): + key = self.get_key_box() + if key in already_seen: + return + already_seen[key] = None + if self.box is None: + self.vstr.enum_forced_boxes(boxes, already_seen) + self.vstart.enum_forced_boxes(boxes, already_seen) + self.vlength.enum_forced_boxes(boxes, already_seen) + else: + boxes.append(self.box) + def _make_virtual(self, modifier): return modifier.make_vstrslice(self.mode is mode_unicode) @@ -288,6 +333,8 @@ def _int_add(newoperations, box1, box2): return ConstInt(box1.value + box2.value) elif isinstance(box2, ConstInt) and box2.value == 0: return box1 + if newoperations is None: + return None resbox = BoxInt() newoperations.append(ResOperation(rop.INT_ADD, [box1, box2], resbox)) return resbox @@ -318,7 +365,12 @@ def _strgetitem(newoperations, strbox, indexbox, mode): class OptString(optimizer.Optimization): "Handling of strings and unicodes." + enabled = True + def reconstruct_for_next_iteration(self, optimizer, valuemap): + self.enabled = True + return self + def make_vstring_plain(self, box, source_op, mode): vvalue = VStringPlainValue(self.optimizer, box, source_op, mode) self.make_equal_to(box, vvalue) @@ -447,11 +499,8 @@ class OptString(optimizer.Optimization): vleft.ensure_nonnull() vright.ensure_nonnull() newoperations = self.optimizer.newoperations - len1box = vleft.getstrlen(newoperations, mode) - len2box = vright.getstrlen(newoperations, mode) - lengthbox = _int_add(newoperations, len1box, len2box) value = self.make_vstring_concat(op.result, op, mode) - value.setup(vleft, vright, lengthbox) + value.setup(vleft, vright) return True def opt_call_stroruni_STR_SLICE(self, op, mode): @@ -600,6 +649,10 @@ class OptString(optimizer.Optimization): self.optimizer.newoperations.append(op) def propagate_forward(self, op): + if not self.enabled: + self.emit_operation(op) + return + opnum = op.getopnum() for value, func in optimize_ops: if opnum == value: @@ -608,6 +661,7 @@ class OptString(optimizer.Optimization): else: self.emit_operation(op) + optimize_ops = _findall(OptString, 'optimize_') def _findall_call_oopspec(): diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py new file mode 100644 index 0000000000..aa745b54ed --- /dev/null +++ b/pypy/jit/metainterp/optimizeopt/unroll.py @@ -0,0 +1,471 @@ +from pypy.jit.metainterp.optimizeopt.optimizer import * +from pypy.jit.metainterp.resoperation import rop, ResOperation +from pypy.jit.metainterp.compile import ResumeGuardDescr +from pypy.jit.metainterp.resume import Snapshot +from pypy.jit.metainterp.history import TreeLoop, LoopToken +from pypy.rlib.debug import debug_start, debug_stop, debug_print +from pypy.jit.metainterp.optimizeutil import InvalidLoop, RetraceLoop +from pypy.jit.metainterp.jitexc import JitException + +# FIXME: Introduce some VirtualOptimizer super class instead + +def optimize_unroll(metainterp_sd, loop, optimizations): + opt = UnrollOptimizer(metainterp_sd, loop, optimizations) + opt.propagate_all_forward() + +class Inliner(object): + def __init__(self, inputargs, jump_args): + assert len(inputargs) == len(jump_args) + self.argmap = {} + for i in range(len(inputargs)): + self.argmap[inputargs[i]] = jump_args[i] + self.snapshot_map = {None: None} + + def inline_op(self, newop, ignore_result=False, clone=True): + if clone: + newop = newop.clone() + args = newop.getarglist() + newop.initarglist([self.inline_arg(a) for a in args]) + + if newop.is_guard(): + args = newop.getfailargs() + if args: + newop.setfailargs([self.inline_arg(a) for a in args]) + + if newop.result and not ignore_result: + old_result = newop.result + newop.result = newop.result.clonebox() + self.argmap[old_result] = newop.result + + descr = newop.getdescr() + if isinstance(descr, ResumeGuardDescr): + descr.rd_snapshot = self.inline_snapshot(descr.rd_snapshot) + + return newop + + def inline_arg(self, arg): + if arg is None: + return None + if isinstance(arg, Const): + return arg + return self.argmap[arg] + + def inline_snapshot(self, snapshot): + if snapshot in self.snapshot_map: + return self.snapshot_map[snapshot] + boxes = [self.inline_arg(a) for a in snapshot.boxes] + new_snapshot = Snapshot(self.inline_snapshot(snapshot.prev), boxes) + self.snapshot_map[snapshot] = new_snapshot + return new_snapshot + + +class UnrollOptimizer(Optimization): + """Unroll the loop into two iterations. The first one will + become the preamble or entry bridge (don't think there is a + distinction anymore)""" + + def __init__(self, metainterp_sd, loop, optimizations): + self.optimizer = Optimizer(metainterp_sd, loop, optimizations) + self.cloned_operations = [] + for op in self.optimizer.loop.operations: + newop = op.clone() + self.cloned_operations.append(newop) + + def propagate_all_forward(self): + loop = self.optimizer.loop + jumpop = loop.operations[-1] + if jumpop.getopnum() == rop.JUMP: + loop.operations = loop.operations[:-1] + else: + loopop = None + + self.optimizer.propagate_all_forward() + + + if jumpop: + assert jumpop.getdescr() is loop.token + loop.preamble.operations = self.optimizer.newoperations + + self.optimizer = self.optimizer.reconstruct_for_next_iteration() + + jump_args = jumpop.getarglist() + jumpop.initarglist([]) + inputargs = self.inline(self.cloned_operations, + loop.inputargs, jump_args) + loop.inputargs = inputargs + jmp = ResOperation(rop.JUMP, loop.inputargs[:], None) + jmp.setdescr(loop.token) + loop.preamble.operations.append(jmp) + + loop.operations = self.optimizer.newoperations + + short = self.create_short_preamble(loop.preamble, loop) + if short: + if False: + # FIXME: This should save some memory but requires + # a lot of tests to be fixed... + loop.preamble.operations = short[:] + + # Turn guards into conditional jumps to the preamble + for i in range(len(short)): + op = short[i] + if op.is_guard(): + op = op.clone() + op.setfailargs(loop.preamble.inputargs) + op.setjumptarget(loop.preamble.token) + short[i] = op + + short_loop = TreeLoop('short preamble') + short_loop.inputargs = loop.preamble.inputargs[:] + short_loop.operations = short + + assert isinstance(loop.preamble.token, LoopToken) + if loop.preamble.token.short_preamble: + loop.preamble.token.short_preamble.append(short_loop) + else: + loop.preamble.token.short_preamble = [short_loop] + + # Clone ops and boxes to get private versions and + newargs = [a.clonebox() for a in short_loop.inputargs] + inliner = Inliner(short_loop.inputargs, newargs) + short_loop.inputargs = newargs + ops = [inliner.inline_op(op) for op in short_loop.operations] + short_loop.operations = ops + + # Forget the values to allow them to be freed + for box in short_loop.inputargs: + box.forget_value() + for op in short_loop.operations: + if op.result: + op.result.forget_value() + + if False: + boxmap = {} + for i in range(len(short_loop.inputargs)): + box = short_loop.inputargs[i] + newbox = box.clonebox() + boxmap[box] = newbox + newbox.forget_value() + short_loop.inputargs[i] = newbox + for i in range(len(short)): + oldop = short[i] + op = oldop.clone() + args = [] + for a in op.getarglist(): + if not isinstance(a, Const): + a = boxmap[a] + args.append(a) + op.initarglist(args) + if op.is_guard(): + args = [] + for a in op.getfailargs(): + if not isinstance(a, Const): + a = boxmap[a] + args.append(a) + op.setfailargs(args) + box = op.result + if box: + newbox = box.clonebox() + boxmap[box] = newbox + newbox.forget_value() + op.result = newbox + short[i] = op + + + def inline(self, loop_operations, loop_args, jump_args): + self.inliner = inliner = Inliner(loop_args, jump_args) + + for v in self.optimizer.values.values(): + v.last_guard_index = -1 # FIXME: Are there any more indexes stored? + + inputargs = [] + seen_inputargs = {} + for arg in jump_args: + boxes = [] + self.getvalue(arg).enum_forced_boxes(boxes, seen_inputargs) + for a in boxes: + if not isinstance(a, Const): + inputargs.append(a) + + # This loop is equivalent to the main optimization loop in + # Optimizer.propagate_all_forward + for newop in loop_operations: + if newop.getopnum() == rop.JUMP: + newop.initarglist(inputargs) + newop = inliner.inline_op(newop, clone=False) + + self.optimizer.first_optimization.propagate_forward(newop) + + # Remove jump to make sure forced code are placed before it + newoperations = self.optimizer.newoperations + jmp = newoperations[-1] + assert jmp.getopnum() == rop.JUMP + self.optimizer.newoperations = newoperations[:-1] + + boxes_created_this_iteration = {} + jumpargs = jmp.getarglist() + + # FIXME: Should also loop over operations added by forcing things in this loop + for op in newoperations: + boxes_created_this_iteration[op.result] = True + args = op.getarglist() + if op.is_guard(): + args = args + op.getfailargs() + + for a in args: + if not isinstance(a, Const) and not a in boxes_created_this_iteration: + if a not in inputargs: + inputargs.append(a) + box = inliner.inline_arg(a) + if box in self.optimizer.values: + box = self.optimizer.values[box].force_box() + jumpargs.append(box) + + jmp.initarglist(jumpargs) + self.optimizer.newoperations.append(jmp) + return inputargs + + def sameop(self, op1, op2): + if op1.getopnum() != op2.getopnum(): + return False + + args1 = op1.getarglist() + args2 = op2.getarglist() + if len(args1) != len(args2): + return False + for i in range(len(args1)): + box1, box2 = args1[i], args2[i] + val1 = self.optimizer.getvalue(box1) + val2 = self.optimizer.getvalue(box2) + if val1 is not val2: + return False + + if not op1.is_guard(): + descr1 = op1.getdescr() + descr2 = op2.getdescr() + if descr1 is not descr2: + return False + + return True + + def create_short_preamble(self, preamble, loop): + #return None # Dissable + + preamble_ops = preamble.operations + loop_ops = loop.operations + + boxmap = BoxMap() + state = ExeState() + short_preamble = [] + loop_i = preamble_i = 0 + while preamble_i < len(preamble_ops): + + op = preamble_ops[preamble_i] + try: + newop = self.inliner.inline_op(op, True) + except KeyError: + debug_print("create_short_preamble failed due to", + "new boxes created during optimization.", + "op:", op.getopnum(), + "at position: ", preamble_i) + return None + + if self.sameop(newop, loop_ops[loop_i]) \ + and loop_i < len(loop_ops): + try: + boxmap.link_ops(op, loop_ops[loop_i]) + except ImpossibleLink: + debug_print("create_short_preamble failed due to", + "impossible link of " + "op:", op.getopnum(), + "at position: ", preamble_i) + return None + loop_i += 1 + else: + if not state.safe_to_move(op): + debug_print("create_short_preamble failed due to", + "unsafe op:", op.getopnum(), + "at position: ", preamble_i) + return None + short_preamble.append(op) + + state.update(op) + preamble_i += 1 + + if loop_i < len(loop_ops): + debug_print("create_short_preamble failed due to", + "loop contaning ops not in preamble" + "at position", loop_i) + return None + + + jumpargs = [] + for i in range(len(loop.inputargs)): + try: + jumpargs.append(boxmap.get_preamblebox(loop.inputargs[i])) + except KeyError: + debug_print("create_short_preamble failed due to", + "input arguments not located") + return None + + jmp = ResOperation(rop.JUMP, jumpargs[:], None) + jmp.setdescr(loop.token) + short_preamble.append(jmp) + + # Check that boxes used as arguemts are produced. + seen = {} + for box in preamble.inputargs: + seen[box] = True + for op in short_preamble: + for box in op.getarglist(): + if isinstance(box, Const): + continue + if box not in seen: + debug_print("create_short_preamble failed due to", + "op arguments not produced") + return None + if op.result: + seen[op.result] = True + + return short_preamble + +class ExeState(object): + def __init__(self): + self.heap_dirty = False + self.unsafe_getitem = {} + + # Make sure it is safe to move the instrucions in short_preamble + # to the top making short_preamble followed by loop equvivalent + # to preamble + def safe_to_move(self, op): + opnum = op.getopnum() + if op.is_always_pure() or op.is_foldable_guard(): + return True + elif opnum == rop.JUMP: + return True + elif (opnum == rop.GETFIELD_GC or + opnum == rop.GETFIELD_RAW): + if self.heap_dirty: + return False + descr = op.getdescr() + if descr in self.unsafe_getitem: + return False + return True + return False + + def update(self, op): + if (op.has_no_side_effect() or + op.is_ovf() or + op.is_guard()): + return + opnum = op.getopnum() + if (opnum == rop.DEBUG_MERGE_POINT): + return + if (opnum == rop.SETFIELD_GC or + opnum == rop.SETFIELD_RAW): + descr = op.getdescr() + self.unsafe_getitem[descr] = True + return + self.heap_dirty = True + +class ImpossibleLink(JitException): + pass + +class BoxMap(object): + def __init__(self): + self.map = {} + + + def link_ops(self, preambleop, loopop): + pargs = preambleop.getarglist() + largs = loopop.getarglist() + if len(pargs) != len(largs): + raise ImpossibleLink + for i in range(len(largs)): + pbox, lbox = pargs[i], largs[i] + self.link_boxes(pbox, lbox) + + if preambleop.result: + if not loopop.result: + raise ImpossibleLink + self.link_boxes(preambleop.result, loopop.result) + + + def link_boxes(self, pbox, lbox): + if lbox in self.map: + if self.map[lbox] is not pbox: + raise ImpossibleLink + else: + if isinstance(lbox, Const): + if not isinstance(pbox, Const) or not pbox.same_constant(lbox): + raise ImpossibleLink + else: + self.map[lbox] = pbox + + + def get_preamblebox(self, loopbox): + return self.map[loopbox] + +class OptInlineShortPreamble(Optimization): + def reconstruct_for_next_iteration(self, optimizer, valuemap): + return self + + def propagate_forward(self, op): + if op.getopnum() == rop.JUMP: + descr = op.getdescr() + assert isinstance(descr, LoopToken) + # FIXME: Use a tree, similar to the tree formed by the full + # preamble and it's bridges, instead of a list to save time and + # memory + short = descr.short_preamble + if short: + for sh in short: + if self.inline(sh.operations, sh.inputargs, + op.getarglist(), dryrun=True): + self.inline(sh.operations, sh.inputargs, + op.getarglist()) + return + + raise RetraceLoop + self.emit_operation(op) + + + + def inline(self, loop_operations, loop_args, jump_args, dryrun=False): + inliner = Inliner(loop_args, jump_args) + + for op in loop_operations: + newop = inliner.inline_op(op) + + if not dryrun: + # FIXME: Emit a proper guard instead to move these + # forceings into the the small bridge back to the preamble + if newop.is_guard(): + failargs = newop.getfailargs() + for i in range(len(failargs)): + box = failargs[i] + if box in self.optimizer.values: + value = self.optimizer.values[box] + if value.is_constant(): + newbox = box.clonebox() + op = ResOperation(rop.SAME_AS, + [value.force_box()], newbox) + self.optimizer.newoperations.append(op) + box = newbox + else: + box = value.force_box() + failargs[i] = box + newop.setfailargs(failargs) + + + self.emit_operation(newop) + else: + if not self.is_emittable(newop): + return False + + return True + + def inline_arg(self, arg): + if isinstance(arg, Const): + return arg + return self.argmap[arg] diff --git a/pypy/jit/metainterp/optimizeopt/virtualize.py b/pypy/jit/metainterp/optimizeopt/virtualize.py index da1ea634ce..fe3241839a 100644 --- a/pypy/jit/metainterp/optimizeopt/virtualize.py +++ b/pypy/jit/metainterp/optimizeopt/virtualize.py @@ -1,18 +1,15 @@ -from pypy.jit.metainterp.specnode import SpecNode, NotSpecNode, ConstantSpecNode -from pypy.jit.metainterp.specnode import AbstractVirtualStructSpecNode -from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode -from pypy.jit.metainterp.specnode import VirtualArraySpecNode -from pypy.jit.metainterp.specnode import VirtualStructSpecNode +from pypy.jit.metainterp.history import Const, ConstInt, BoxInt from pypy.jit.metainterp.resoperation import rop, ResOperation -from pypy.jit.metainterp.optimizeutil import _findall +from pypy.jit.metainterp.optimizeutil import _findall, sort_descrs +from pypy.jit.metainterp.optimizeutil import descrlist_dict from pypy.rlib.objectmodel import we_are_translated -from pypy.jit.metainterp.optimizeopt.optimizer import * +from pypy.jit.metainterp.optimizeopt import optimizer -class AbstractVirtualValue(OptValue): +class AbstractVirtualValue(optimizer.OptValue): _attrs_ = ('optimizer', 'keybox', 'source_op', '_cached_vinfo') box = None - level = LEVEL_NONNULL + level = optimizer.LEVEL_NONNULL _cached_vinfo = None def __init__(self, optimizer, keybox, source_op=None): @@ -47,6 +44,9 @@ class AbstractVirtualValue(OptValue): def _really_force(self): raise NotImplementedError("abstract base") + def reconstruct_for_next_iteration(self, _optimizer): + return optimizer.OptValue(self.force_box()) + def get_fielddescrlist_cache(cpu): if not hasattr(cpu, '_optimizeopt_fielddescrlist_cache'): result = descrlist_dict() @@ -67,7 +67,7 @@ class AbstractVirtualStructValue(AbstractVirtualValue): return self._fields.get(ofs, default) def setfield(self, ofs, fieldvalue): - assert isinstance(fieldvalue, OptValue) + assert isinstance(fieldvalue, optimizer.OptValue) self._fields[ofs] = fieldvalue def _really_force(self): @@ -123,9 +123,32 @@ class AbstractVirtualStructValue(AbstractVirtualValue): fieldvalue = self._fields[ofs] fieldvalue.get_args_for_fail(modifier) + def enum_forced_boxes(self, boxes, already_seen): + key = self.get_key_box() + if key in already_seen: + return + already_seen[key] = None + if self.box is None: + lst = self._get_field_descr_list() + for ofs in lst: + self._fields[ofs].enum_forced_boxes(boxes, already_seen) + else: + boxes.append(self.box) + + def reconstruct_for_next_iteration(self, optimizer): + self.optimizer = optimizer + return self + + def reconstruct_childs(self, new, valuemap): + assert isinstance(new, AbstractVirtualStructValue) + if new.box is None: + lst = self._get_field_descr_list() + for ofs in lst: + new._fields[ofs] = \ + self._fields[ofs].get_reconstructed(new.optimizer, valuemap) class VirtualValue(AbstractVirtualStructValue): - level = LEVEL_KNOWNCLASS + level = optimizer.LEVEL_KNOWNCLASS def __init__(self, optimizer, known_class, keybox, source_op=None): AbstractVirtualStructValue.__init__(self, optimizer, keybox, source_op) @@ -138,6 +161,8 @@ class VirtualValue(AbstractVirtualStructValue): def __repr__(self): cls_name = self.known_class.value.adr.ptr._obj._TYPE._name + if self._fields is None: + return '<VirtualValue FORCED cls=%s>' % (cls_name,) field_names = [field.name for field in self._fields] return "<VirtualValue cls=%s fields=%s>" % (cls_name, field_names) @@ -167,7 +192,7 @@ class VArrayValue(AbstractVirtualValue): return res def setitem(self, index, itemvalue): - assert isinstance(itemvalue, OptValue) + assert isinstance(itemvalue, optimizer.OptValue) self._items[index] = itemvalue def _really_force(self): @@ -202,79 +227,33 @@ class VArrayValue(AbstractVirtualValue): def _make_virtual(self, modifier): return modifier.make_varray(self.arraydescr) + def enum_forced_boxes(self, boxes, already_seen): + key = self.get_key_box() + if key in already_seen: + return + already_seen[key] = None + if self.box is None: + for itemvalue in self._items: + itemvalue.enum_forced_boxes(boxes, already_seen) + else: + boxes.append(self.box) -class __extend__(SpecNode): - def setup_virtual_node(self, optimizer, box, newinputargs): - raise NotImplementedError - def teardown_virtual_node(self, optimizer, value, newexitargs): - raise NotImplementedError - -class __extend__(NotSpecNode): - def setup_virtual_node(self, optimizer, box, newinputargs): - newinputargs.append(box) - def teardown_virtual_node(self, optimizer, value, newexitargs): - newexitargs.append(value.force_box()) - -class __extend__(ConstantSpecNode): - def setup_virtual_node(self, optimizer, box, newinputargs): - optimizer.make_constant(box, self.constbox) - def teardown_virtual_node(self, optimizer, value, newexitargs): - pass - -class __extend__(AbstractVirtualStructSpecNode): - def setup_virtual_node(self, optimizer, box, newinputargs): - vvalue = self._setup_virtual_node_1(optimizer, box) - for ofs, subspecnode in self.fields: - subbox = optimizer.new_box(ofs) - subspecnode.setup_virtual_node(optimizer, subbox, newinputargs) - vvaluefield = optimizer.getvalue(subbox) - vvalue.setfield(ofs, vvaluefield) - def _setup_virtual_node_1(self, optimizer, box): - raise NotImplementedError - def teardown_virtual_node(self, optimizer, value, newexitargs): - assert value.is_virtual() - for ofs, subspecnode in self.fields: - subvalue = value.getfield(ofs, optimizer.new_const(ofs)) - subspecnode.teardown_virtual_node(optimizer, subvalue, newexitargs) - -class __extend__(VirtualInstanceSpecNode): - def _setup_virtual_node_1(self, optimizer, box): - return optimizer.make_virtual(self.known_class, box) - -class __extend__(VirtualStructSpecNode): - def _setup_virtual_node_1(self, optimizer, box): - return optimizer.make_vstruct(self.typedescr, box) - -class __extend__(VirtualArraySpecNode): - def setup_virtual_node(self, optimizer, box, newinputargs): - vvalue = optimizer.make_varray(self.arraydescr, len(self.items), box) - for index in range(len(self.items)): - subbox = optimizer.new_box_item(self.arraydescr) - subspecnode = self.items[index] - subspecnode.setup_virtual_node(optimizer, subbox, newinputargs) - vvalueitem = optimizer.getvalue(subbox) - vvalue.setitem(index, vvalueitem) - def teardown_virtual_node(self, optimizer, value, newexitargs): - assert value.is_virtual() - for index in range(len(self.items)): - subvalue = value.getitem(index) - subspecnode = self.items[index] - subspecnode.teardown_virtual_node(optimizer, subvalue, newexitargs) - -class OptVirtualize(Optimization): - "Virtualize objects until they escape." + def reconstruct_for_next_iteration(self, optimizer): + self.optimizer = optimizer + return self - def setup(self, virtuals): - if not virtuals: - return + def reconstruct_childs(self, new, valuemap): + assert isinstance(new, VArrayValue) + if new.box is None: + for i in range(len(self._items)): + new._items[i] = self._items[i].get_reconstructed(new.optimizer, + valuemap) + +class OptVirtualize(optimizer.Optimization): + "Virtualize objects until they escape." - inputargs = self.optimizer.loop.inputargs - specnodes = self.optimizer.loop.token.specnodes - assert len(inputargs) == len(specnodes) - newinputargs = [] - for i in range(len(inputargs)): - specnodes[i].setup_virtual_node(self, inputargs[i], newinputargs) - self.optimizer.loop.inputargs = newinputargs + def reconstruct_for_next_iteration(self, optimizer, valuemap): + return self def make_virtual(self, known_class, box, source_op=None): vvalue = VirtualValue(self.optimizer, known_class, box, source_op) @@ -291,19 +270,6 @@ class OptVirtualize(Optimization): self.make_equal_to(box, vvalue) return vvalue - def optimize_JUMP(self, op): - orgop = self.optimizer.loop.operations[-1] - exitargs = [] - target_loop_token = orgop.getdescr() - assert isinstance(target_loop_token, LoopToken) - specnodes = target_loop_token.specnodes - assert op.numargs() == len(specnodes) - for i in range(len(specnodes)): - value = self.getvalue(op.getarg(i)) - specnodes[i].teardown_virtual_node(self, value, exitargs) - op = op.copy_and_change(op.getopnum(), args=exitargs[:]) - self.emit_operation(op) - def optimize_VIRTUAL_REF(self, op): indexbox = op.getarg(1) # @@ -350,10 +316,10 @@ class OptVirtualize(Optimization): def optimize_GETFIELD_GC(self, op): value = self.getvalue(op.getarg(0)) if value.is_virtual(): - # optimizefindnode should ensure that fieldvalue is found assert isinstance(value, AbstractVirtualValue) fieldvalue = value.getfield(op.getdescr(), None) - assert fieldvalue is not None + if fieldvalue is None: + fieldvalue = self.optimizer.new_const(op.getdescr()) self.make_equal_to(op.result, fieldvalue) else: value.ensure_nonnull() diff --git a/pypy/jit/metainterp/optimizeutil.py b/pypy/jit/metainterp/optimizeutil.py index 5c34c2cc9f..855a9a006e 100644 --- a/pypy/jit/metainterp/optimizeutil.py +++ b/pypy/jit/metainterp/optimizeutil.py @@ -3,16 +3,24 @@ from pypy.rlib.rarithmetic import intmask from pypy.rlib.unroll import unrolling_iterable from pypy.jit.metainterp import resoperation, history from pypy.jit.metainterp.jitexc import JitException +from pypy.rlib.debug import make_sure_not_resized class InvalidLoop(JitException): """Raised when the optimize*.py detect that the loop that we are trying to build cannot possibly make sense as a long-running loop (e.g. it cannot run 2 complete iterations).""" +class RetraceLoop(JitException): + """ Raised when inlining a short preamble resulted in an + InvalidLoop. This means the optimized loop is too specialized + to be useful here, so we trace it again and produced a second + copy specialized in some different way. + """ + # ____________________________________________________________ # Misc. utilities -def _findall(Class, name_prefix): +def _findall(Class, name_prefix, op_prefix=None): result = [] for name in dir(Class): if name.startswith(name_prefix): @@ -20,6 +28,8 @@ def _findall(Class, name_prefix): if opname.isupper(): assert hasattr(resoperation.rop, opname) for value, name in resoperation.opname.items(): + if op_prefix and not name.startswith(op_prefix): + continue if hasattr(Class, name_prefix + name): result.append((value, getattr(Class, name_prefix + name))) return unrolling_iterable(result) @@ -68,6 +78,8 @@ def descrlist_dict(): # ____________________________________________________________ def args_eq(args1, args2): + make_sure_not_resized(args1) + make_sure_not_resized(args2) if len(args1) != len(args2): return False for i in range(len(args1)): @@ -84,6 +96,7 @@ def args_eq(args1, args2): return True def args_hash(args): + make_sure_not_resized(args) res = 0x345678 for arg in args: if isinstance(arg, history.Const): @@ -95,5 +108,3 @@ def args_hash(args): def args_dict(): return r_dict(args_eq, args_hash) - - diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py index 85e20f90be..6325ab3c7e 100644 --- a/pypy/jit/metainterp/pyjitpl.py +++ b/pypy/jit/metainterp/pyjitpl.py @@ -14,12 +14,14 @@ from pypy.jit.metainterp import executor from pypy.jit.metainterp.logger import Logger from pypy.jit.metainterp.jitprof import EmptyProfiler from pypy.jit.metainterp.jitprof import GUARDS, RECORDED_OPS, ABORT_ESCAPE -from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG, ABORT_BRIDGE +from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG, ABORT_BRIDGE, \ + ABORT_BAD_LOOP from pypy.jit.metainterp.jitexc import JitException, get_llexception from pypy.rlib.rarithmetic import intmask from pypy.rlib.objectmodel import specialize from pypy.jit.codewriter.jitcode import JitCode, SwitchDictDescr from pypy.jit.codewriter import heaptracker +from pypy.jit.metainterp.optimizeutil import RetraceLoop # ____________________________________________________________ @@ -1384,6 +1386,11 @@ class MetaInterpGlobalData(object): # ____________________________________________________________ +class RetraceState(object): + def __init__(self, metainterp, live_arg_boxes): + self.merge_point = len(metainterp.current_merge_points) - 1 + self.live_arg_boxes = live_arg_boxes + class MetaInterp(object): in_recursion = 0 @@ -1397,6 +1404,7 @@ class MetaInterp(object): self.portal_trace_positions = [] self.free_frames_list = [] self.last_exc_value_box = None + self.retracing_loop_from = None def perform_call(self, jitcode, boxes, greenkey=None): # causes the metainterp to enter the given subfunction @@ -1747,7 +1755,15 @@ class MetaInterp(object): # that failed; # - if self.resumekey is a ResumeFromInterpDescr, it starts directly # from the interpreter. - self.compile_bridge(live_arg_boxes) + if not self.retracing_loop_from: + try: + self.compile_bridge(live_arg_boxes) + except RetraceLoop: + start = len(self.history.operations) + self.current_merge_points.append((live_arg_boxes, start)) + self.retracing_loop_from = RetraceState(self, live_arg_boxes) + return + # raises in case it works -- which is the common case, hopefully, # at least for bridges starting from a guard. @@ -1768,9 +1784,18 @@ class MetaInterp(object): else: # Found! Compile it as a loop. # raises in case it works -- which is the common case - self.compile(original_boxes, live_arg_boxes, start) + if self.retracing_loop_from and \ + self.retracing_loop_from.merge_point == j: + bridge_arg_boxes = self.retracing_loop_from.live_arg_boxes + self.compile_bridge_and_loop(original_boxes, \ + live_arg_boxes, start, + bridge_arg_boxes) + else: + self.compile(original_boxes, live_arg_boxes, start) # creation of the loop was cancelled! - self.staticdata.log('cancelled, going on...') + #self.staticdata.log('cancelled, tracing more...') + self.staticdata.log('cancelled, stopping tracing') + raise SwitchToBlackhole(ABORT_BAD_LOOP) # Otherwise, no loop found so far, so continue tracing. start = len(self.history.operations) @@ -1779,8 +1804,7 @@ class MetaInterp(object): def designate_target_loop(self, gmp): loop_token = gmp.target_loop_token num_green_args = self.jitdriver_sd.num_green_args - residual_args = self.get_residual_args(loop_token.specnodes, - gmp.argboxes[num_green_args:]) + residual_args = gmp.argboxes[num_green_args:] history.set_future_values(self.cpu, residual_args) return loop_token @@ -1836,11 +1860,13 @@ class MetaInterp(object): greenkey = original_boxes[:num_green_args] old_loop_tokens = self.get_compiled_merge_points(greenkey) self.history.record(rop.JUMP, live_arg_boxes[num_green_args:], None) - loop_token = compile.compile_new_loop(self, old_loop_tokens, start) + loop_token = compile.compile_new_loop(self, old_loop_tokens, + greenkey, start) if loop_token is not None: # raise if it *worked* correctly self.set_compiled_merge_points(greenkey, old_loop_tokens) raise GenerateMergePoint(live_arg_boxes, loop_token) self.history.operations.pop() # remove the JUMP + # FIXME: Why is self.history.inputargs not restored? def compile_bridge(self, live_arg_boxes): num_green_args = self.jitdriver_sd.num_green_args @@ -1848,12 +1874,51 @@ class MetaInterp(object): old_loop_tokens = self.get_compiled_merge_points(greenkey) if len(old_loop_tokens) == 0: return + #if self.resumekey.guard_opnum == rop.GUARD_CLASS: + # return # Kepp tracing for another iteration self.history.record(rop.JUMP, live_arg_boxes[num_green_args:], None) - target_loop_token = compile.compile_new_bridge(self, old_loop_tokens, - self.resumekey) - if target_loop_token is not None: # raise if it *worked* correctly - raise GenerateMergePoint(live_arg_boxes, target_loop_token) + try: + target_loop_token = compile.compile_new_bridge(self, + old_loop_tokens, + self.resumekey) + if target_loop_token is not None: # raise if it *worked* correctly + raise GenerateMergePoint(live_arg_boxes, target_loop_token) + finally: + self.history.operations.pop() # remove the JUMP + + def compile_bridge_and_loop(self, original_boxes, live_arg_boxes, start, + bridge_arg_boxes): + num_green_args = self.jitdriver_sd.num_green_args + original_inputargs = self.history.inputargs + greenkey = original_boxes[:num_green_args] + old_loop_tokens = self.get_compiled_merge_points(greenkey) + original_operations = self.history.operations + self.history.inputargs = original_boxes[num_green_args:] + greenkey = original_boxes[:num_green_args] + self.history.record(rop.JUMP, live_arg_boxes[num_green_args:], None) + loop_token = compile.compile_new_loop(self, [], greenkey, start) self.history.operations.pop() # remove the JUMP + if loop_token is None: + return + + if loop_token.short_preamble: + old_loop_tokens[0].short_preamble.extend(loop_token.short_preamble) + + self.history.inputargs = original_inputargs + self.history.operations = self.history.operations[:start] + live_arg_boxes = bridge_arg_boxes + + self.history.record(rop.JUMP, live_arg_boxes[num_green_args:], None) + try: + target_loop_token = compile.compile_new_bridge(self, + [loop_token], + self.resumekey) + except RetraceLoop: + assert False + assert target_loop_token is not None + + self.history.operations = original_operations + raise GenerateMergePoint(live_arg_boxes, old_loop_tokens[0]) def compile_done_with_this_frame(self, exitbox): self.gen_store_back_in_virtualizable() @@ -1892,16 +1957,6 @@ class MetaInterp(object): if target_loop_token is not loop_tokens[0]: compile.giveup() - def get_residual_args(self, specnodes, args): - if specnodes is None: # it is None only for tests - return args - assert len(specnodes) == len(args) - expanded_args = [] - for i in range(len(specnodes)): - specnode = specnodes[i] - specnode.extract_runtime_data(self.cpu, args[i], expanded_args) - return expanded_args - @specialize.arg(1) def initialize_original_boxes(self, jitdriver_sd, *args): original_boxes = [] diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py index c226436af6..c1a9942494 100644 --- a/pypy/jit/metainterp/resoperation.py +++ b/pypy/jit/metainterp/resoperation.py @@ -84,7 +84,7 @@ class AbstractResOp(object): descr = self.getdescr() if descr is not None: descr = descr.clone_if_mutable() - op = ResOperation(self.getopnum(), args, self.result, descr) + op = ResOperation(self.getopnum(), args[:], self.result, descr) if not we_are_translated(): op.name = self.name op.pc = self.pc @@ -198,6 +198,7 @@ class ResOpWithDescr(AbstractResOp): class GuardResOp(ResOpWithDescr): _fail_args = None + _jump_target = None def getfailargs(self): return self._fail_args @@ -205,14 +206,22 @@ class GuardResOp(ResOpWithDescr): def setfailargs(self, fail_args): self._fail_args = fail_args + def getjumptarget(self): + return self._jump_target + + def setjumptarget(self, jump_target): + self._jump_target = jump_target + def copy_and_change(self, opnum, args=None, result=None, descr=None): newop = AbstractResOp.copy_and_change(self, opnum, args, result, descr) newop.setfailargs(self.getfailargs()) + newop.setjumptarget(self.getjumptarget()) return newop def clone(self): newop = AbstractResOp.clone(self) newop.setfailargs(self.getfailargs()) + newop.setjumptarget(self.getjumptarget()) return newop diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py index 203c809ebf..89a5d28354 100644 --- a/pypy/jit/metainterp/resume.py +++ b/pypy/jit/metainterp/resume.py @@ -307,8 +307,9 @@ class ResumeDataVirtualAdder(object): storage = self.storage # make sure that nobody attached resume data to this guard yet assert not storage.rd_numb - numb, liveboxes_from_env, v = self.memo.number(values, - storage.rd_snapshot) + snapshot = storage.rd_snapshot + assert snapshot is not None # is that true? + numb, liveboxes_from_env, v = self.memo.number(values, snapshot) self.liveboxes_from_env = liveboxes_from_env self.liveboxes = {} storage.rd_numb = numb diff --git a/pypy/jit/metainterp/simple_optimize.py b/pypy/jit/metainterp/simple_optimize.py index f1f45ad51d..7ccaf6c443 100644 --- a/pypy/jit/metainterp/simple_optimize.py +++ b/pypy/jit/metainterp/simple_optimize.py @@ -42,8 +42,14 @@ def optimize_loop(metainterp_sd, old_loops, loop): descr.store_final_boxes(op, newboxes) newoperations.extend(transform(op)) loop.operations = newoperations + jumpop = newoperations[-1] + if jumpop.getopnum() == rop.JUMP: + jumpop.setdescr(loop.token) return None def optimize_bridge(metainterp_sd, old_loops, loop): optimize_loop(metainterp_sd, [], loop) + jumpop = loop.operations[-1] + if jumpop.getopnum() == rop.JUMP: + jumpop.setdescr(old_loops[0]) return old_loops[0] diff --git a/pypy/jit/metainterp/specnode.py b/pypy/jit/metainterp/specnode.py deleted file mode 100644 index c6316718cc..0000000000 --- a/pypy/jit/metainterp/specnode.py +++ /dev/null @@ -1,127 +0,0 @@ -from pypy.tool.pairtype import extendabletype -from pypy.jit.metainterp.history import Const - - -class SpecNode(object): - __metaclass__ = extendabletype # extended in optimizefindnode.py - __slots__ = () - - def equals(self, other, ge): # 'ge' stands for greater-or-equal; - raise NotImplementedError # if false, the default is 'equal'. - - def extract_runtime_data(self, cpu, valuebox, resultlist): - raise NotImplementedError - - -class NotSpecNode(SpecNode): - __slots__ = () - - def equals(self, other, ge): - return isinstance(other, NotSpecNode) or ge - - def extract_runtime_data(self, cpu, valuebox, resultlist): - resultlist.append(valuebox) - - -prebuiltNotSpecNode = NotSpecNode() - - -class ConstantSpecNode(SpecNode): - def __init__(self, constbox): - assert isinstance(constbox, Const) - self.constbox = constbox - - def equals(self, other, ge): - return isinstance(other, ConstantSpecNode) and \ - self.constbox.same_constant(other.constbox) - - def extract_runtime_data(self, cpu, valuebox, resultlist): - pass - - -class AbstractVirtualStructSpecNode(SpecNode): - def __init__(self, fields): - self.fields = fields # list: [(fieldofs, subspecnode)] - - def equal_fields(self, other, ge): - if len(self.fields) != len(other.fields): - return False - for i in range(len(self.fields)): - o1, s1 = self.fields[i] - o2, s2 = other.fields[i] - if not (o1 is o2 and s1.equals(s2, ge)): - return False - return True - - def extract_runtime_data(self, cpu, valuebox, resultlist): - from pypy.jit.metainterp import executor, history, resoperation - for ofs, subspecnode in self.fields: - assert isinstance(ofs, history.AbstractDescr) - fieldbox = executor.execute(cpu, None, - resoperation.rop.GETFIELD_GC, - ofs, valuebox) - subspecnode.extract_runtime_data(cpu, fieldbox, resultlist) - - -class VirtualInstanceSpecNode(AbstractVirtualStructSpecNode): - def __init__(self, known_class, fields): - AbstractVirtualStructSpecNode.__init__(self, fields) - assert isinstance(known_class, Const) - self.known_class = known_class - - def equals(self, other, ge): - if not (isinstance(other, VirtualInstanceSpecNode) and - self.known_class.same_constant(other.known_class)): - return False - return self.equal_fields(other, ge) - - -class VirtualArraySpecNode(SpecNode): - def __init__(self, arraydescr, items): - self.arraydescr = arraydescr - self.items = items # list of subspecnodes - - def equals(self, other, ge): - if not (isinstance(other, VirtualArraySpecNode) and - len(self.items) == len(other.items)): - return False - assert self.arraydescr == other.arraydescr - for i in range(len(self.items)): - s1 = self.items[i] - s2 = other.items[i] - if not s1.equals(s2, ge): - return False - return True - - def extract_runtime_data(self, cpu, valuebox, resultlist): - from pypy.jit.metainterp import executor, history, resoperation - for i in range(len(self.items)): - itembox = executor.execute(cpu, None, - resoperation.rop.GETARRAYITEM_GC, - self.arraydescr, - valuebox, history.ConstInt(i)) - subspecnode = self.items[i] - subspecnode.extract_runtime_data(cpu, itembox, resultlist) - - -class VirtualStructSpecNode(AbstractVirtualStructSpecNode): - def __init__(self, typedescr, fields): - AbstractVirtualStructSpecNode.__init__(self, fields) - self.typedescr = typedescr - - def equals(self, other, ge): - if not isinstance(other, VirtualStructSpecNode): - return False - assert self.typedescr == other.typedescr - return self.equal_fields(other, ge) - - -def equals_specnodes(specnodes1, specnodes2, ge=False): - assert len(specnodes1) == len(specnodes2) - for i in range(len(specnodes1)): - if not specnodes1[i].equals(specnodes2[i], ge): - return False - return True - -def more_general_specnodes(specnodes1, specnodes2): - return equals_specnodes(specnodes1, specnodes2, ge=True) diff --git a/pypy/jit/metainterp/test/test_basic.py b/pypy/jit/metainterp/test/test_basic.py index 2777922a28..c7b1c944b0 100644 --- a/pypy/jit/metainterp/test/test_basic.py +++ b/pypy/jit/metainterp/test/test_basic.py @@ -309,6 +309,123 @@ class BasicTests: found += 1 assert found == 1 + def test_loop_invariant_mul1(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x']) + def f(x, y): + res = 0 + while y > 0: + myjitdriver.can_enter_jit(x=x, y=y, res=res) + myjitdriver.jit_merge_point(x=x, y=y, res=res) + res += x * x + y -= 1 + return res + res = self.meta_interp(f, [6, 7]) + assert res == 252 + self.check_loop_count(1) + self.check_loops({'guard_true': 1, + 'int_add': 1, 'int_sub': 1, 'int_gt': 1, + 'jump': 1}) + + def test_loop_invariant_mul_ovf(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x']) + def f(x, y): + res = 0 + while y > 0: + myjitdriver.can_enter_jit(x=x, y=y, res=res) + myjitdriver.jit_merge_point(x=x, y=y, res=res) + b = y * 2 + res += ovfcheck(x * x) + b + y -= 1 + return res + res = self.meta_interp(f, [6, 7]) + assert res == 308 + self.check_loop_count(1) + self.check_loops({'guard_true': 1, + 'int_add': 2, 'int_sub': 1, 'int_gt': 1, + 'int_mul': 1, + 'jump': 1}) + + def test_loop_invariant_mul_bridge1(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x']) + def f(x, y): + res = 0 + while y > 0: + myjitdriver.can_enter_jit(x=x, y=y, res=res) + myjitdriver.jit_merge_point(x=x, y=y, res=res) + res += x * x + if y<16: + x += 1 + y -= 1 + return res + res = self.meta_interp(f, [6, 32]) + assert res == 3427 + self.check_loop_count(3) + + def test_loop_invariant_mul_bridge_maintaining1(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x']) + def f(x, y): + res = 0 + while y > 0: + myjitdriver.can_enter_jit(x=x, y=y, res=res) + myjitdriver.jit_merge_point(x=x, y=y, res=res) + res += x * x + if y<16: + res += 1 + y -= 1 + return res + res = self.meta_interp(f, [6, 32]) + assert res == 1167 + self.check_loop_count(3) + self.check_loops({'int_add': 2, 'int_lt': 1, + 'int_sub': 2, 'guard_false': 1, + 'jump': 2, + 'int_gt': 1, 'guard_true': 1, 'int_mul': 1}) + + + def test_loop_invariant_mul_bridge_maintaining2(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x']) + def f(x, y): + res = 0 + while y > 0: + myjitdriver.can_enter_jit(x=x, y=y, res=res) + myjitdriver.jit_merge_point(x=x, y=y, res=res) + z = x * x + res += z + if y<16: + res += z + y -= 1 + return res + res = self.meta_interp(f, [6, 32]) + assert res == 1692 + self.check_loop_count(3) + self.check_loops({'int_add': 2, 'int_lt': 1, + 'int_sub': 2, 'guard_false': 1, + 'jump': 2, + 'int_gt': 1, 'guard_true': 1, 'int_mul': 1}) + + def test_loop_invariant_intbox(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x']) + class I: + __slots__ = 'intval' + _immutable_ = True + def __init__(self, intval): + self.intval = intval + def f(i, y): + res = 0 + x = I(i) + while y > 0: + myjitdriver.can_enter_jit(x=x, y=y, res=res) + myjitdriver.jit_merge_point(x=x, y=y, res=res) + res += x.intval * x.intval + y -= 1 + return res + res = self.meta_interp(f, [6, 7]) + assert res == 252 + self.check_loop_count(1) + self.check_loops({'guard_true': 1, + 'int_add': 1, 'int_sub': 1, 'int_gt': 1, + 'jump': 1}) + def test_loops_are_transient(self): import gc, weakref myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'res']) @@ -336,7 +453,9 @@ class BasicTests: assert res == f(6, 15) gc.collect() - assert not [wr for wr in wr_loops if wr()] + #assert not [wr for wr in wr_loops if wr()] + for loop in [wr for wr in wr_loops if wr()]: + assert loop().name == 'short preamble' def test_string(self): def f(n): @@ -484,7 +603,7 @@ class BasicTests: res = self.meta_interp(f, [21, 5]) assert res == -1 # the CALL_PURE is constant-folded away by optimizeopt.py - self.check_loops(int_sub=1, call=0, call_pure=0, getfield_gc=1) + self.check_loops(int_sub=1, call=0, call_pure=0, getfield_gc=0) def test_constant_across_mp(self): myjitdriver = JitDriver(greens = [], reds = ['n']) @@ -857,10 +976,9 @@ class BasicTests: self.meta_interp(f, [20], repeat=7) self.check_tree_loop_count(2) # the loop and the entry path # we get: - # ENTER - compile the new loop - # ENTER - compile the entry bridge + # ENTER - compile the new loop and the entry bridge # ENTER - compile the leaving path - self.check_enter_count(3) + self.check_enter_count(2) def test_bridge_from_interpreter_2(self): # one case for backend - computing of framesize on guard failure @@ -1238,7 +1356,7 @@ class BasicTests: res = self.meta_interp(f, [10, 3]) assert res == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 - self.check_tree_loop_count(1) + self.check_tree_loop_count(2) res = self.meta_interp(f, [10, 13]) assert res == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 @@ -1343,7 +1461,8 @@ class BasicTests: return x res = self.meta_interp(f, [299], listops=True) assert res == f(299) - self.check_loops(guard_class=0, guard_value=3) + self.check_loops(guard_class=0, guard_value=3) + self.check_loops(guard_class=0, guard_value=6, everywhere=True) def test_merge_guardnonnull_guardclass(self): from pypy.rlib.objectmodel import instantiate @@ -1373,6 +1492,9 @@ class BasicTests: assert res == f(299) self.check_loops(guard_class=0, guard_nonnull=0, guard_nonnull_class=2, guard_isnull=1) + self.check_loops(guard_class=0, guard_nonnull=0, + guard_nonnull_class=4, guard_isnull=2, + everywhere=True) def test_merge_guardnonnull_guardvalue(self): from pypy.rlib.objectmodel import instantiate @@ -1401,6 +1523,9 @@ class BasicTests: assert res == f(299) self.check_loops(guard_class=0, guard_nonnull=0, guard_value=2, guard_nonnull_class=0, guard_isnull=1) + self.check_loops(guard_class=0, guard_nonnull=0, guard_value=4, + guard_nonnull_class=0, guard_isnull=2, + everywhere=True) def test_merge_guardnonnull_guardvalue_2(self): from pypy.rlib.objectmodel import instantiate @@ -1429,6 +1554,9 @@ class BasicTests: assert res == f(299) self.check_loops(guard_class=0, guard_nonnull=0, guard_value=2, guard_nonnull_class=0, guard_isnull=1) + self.check_loops(guard_class=0, guard_nonnull=0, guard_value=4, + guard_nonnull_class=0, guard_isnull=2, + everywhere=True) def test_merge_guardnonnull_guardclass_guardvalue(self): from pypy.rlib.objectmodel import instantiate @@ -1460,6 +1588,9 @@ class BasicTests: assert res == f(399) self.check_loops(guard_class=0, guard_nonnull=0, guard_value=3, guard_nonnull_class=0, guard_isnull=1) + self.check_loops(guard_class=0, guard_nonnull=0, guard_value=6, + guard_nonnull_class=0, guard_isnull=2, + everywhere=True) def test_residual_call_doesnt_lose_info(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'l']) @@ -1485,7 +1616,8 @@ class BasicTests: y.v = g(y.v) - y.v/y.v + lc/l[0] - 1 return y.v res = self.meta_interp(f, [20], listops=True) - self.check_loops(getfield_gc=1, getarrayitem_gc=0) + self.check_loops(getfield_gc=0, getarrayitem_gc=0) + self.check_loops(getfield_gc=1, getarrayitem_gc=0, everywhere=True) def test_guard_isnull_nonnull(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'res']) @@ -1515,7 +1647,7 @@ class BasicTests: assert res == 42 self.check_loops(guard_nonnull=1, guard_isnull=1) - def test_loop_invariant(self): + def test_loop_invariant1(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'res']) class A(object): pass @@ -1540,7 +1672,8 @@ class BasicTests: return res res = self.meta_interp(g, [21]) assert res == 3 * 21 - self.check_loops(call=1) + self.check_loops(call=0) + self.check_loops(call=1, everywhere=True) def test_bug_optimizeopt_mutates_ops(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'res', 'const', 'a']) @@ -1676,6 +1809,171 @@ class BasicTests: assert res == 8 py.test.raises(AssertGreenFailed, self.interp_operations, f, [8, 0]) + def test_multiple_specialied_versions1(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'res']) + class Base: + def __init__(self, val): + self.val = val + class A(Base): + def binop(self, other): + return A(self.val + other.val) + class B(Base): + def binop(self, other): + return B(self.val * other.val) + def f(x, y): + res = x + while y > 0: + myjitdriver.can_enter_jit(y=y, x=x, res=res) + myjitdriver.jit_merge_point(y=y, x=x, res=res) + res = res.binop(x) + y -= 1 + return res + def g(x, y): + a1 = f(A(x), y) + a2 = f(A(x), y) + b1 = f(B(x), y) + b2 = f(B(x), y) + assert a1.val == a2.val + assert b1.val == b2.val + return a1.val + b1.val + res = self.meta_interp(g, [6, 7]) + assert res == 6*8 + 6**8 + self.check_loop_count(5) + self.check_loops({'guard_true': 2, + 'int_add': 1, 'int_mul': 1, 'int_sub': 2, + 'int_gt': 2, 'jump': 2}) + + def test_multiple_specialied_versions_bridge(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'z', 'res']) + class Base: + def __init__(self, val): + self.val = val + def getval(self): + return self.val + class A(Base): + def binop(self, other): + return A(self.getval() + other.getval()) + class B(Base): + def binop(self, other): + return B(self.getval() * other.getval()) + def f(x, y, z): + res = x + while y > 0: + myjitdriver.can_enter_jit(y=y, x=x, z=z, res=res) + myjitdriver.jit_merge_point(y=y, x=x, z=z, res=res) + res = res.binop(x) + y -= 1 + if y < 7: + x = z + return res + def g(x, y): + a1 = f(A(x), y, A(x)) + a2 = f(A(x), y, A(x)) + b1 = f(B(x), y, B(x)) + b2 = f(B(x), y, B(x)) + c1 = f(B(x), y, A(x)) + c2 = f(B(x), y, A(x)) + d1 = f(A(x), y, B(x)) + d2 = f(A(x), y, B(x)) + assert a1.val == a2.val + assert b1.val == b2.val + assert c1.val == c2.val + assert d1.val == d2.val + return a1.val + b1.val + c1.val + d1.val + res = self.meta_interp(g, [3, 14]) + assert res == g(3, 14) + + def test_specialied_bridge(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'res']) + class A: + def __init__(self, val): + self.val = val + def binop(self, other): + return A(self.val + other.val) + def f(x, y): + res = A(0) + while y > 0: + myjitdriver.can_enter_jit(y=y, x=x, res=res) + myjitdriver.jit_merge_point(y=y, x=x, res=res) + res = res.binop(A(y)) + if y<7: + res = x + y -= 1 + return res + def g(x, y): + a1 = f(A(x), y) + a2 = f(A(x), y) + assert a1.val == a2.val + return a1.val + res = self.meta_interp(g, [6, 14]) + assert res == g(6, 14) + + def test_specialied_bridge_const(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'const', 'x', 'res']) + class A: + def __init__(self, val): + self.val = val + def binop(self, other): + return A(self.val + other.val) + def f(x, y): + res = A(0) + const = 7 + while y > 0: + myjitdriver.can_enter_jit(y=y, x=x, res=res, const=const) + myjitdriver.jit_merge_point(y=y, x=x, res=res, const=const) + const = hint(const, promote=True) + res = res.binop(A(const)) + if y<7: + res = x + y -= 1 + return res + def g(x, y): + a1 = f(A(x), y) + a2 = f(A(x), y) + assert a1.val == a2.val + return a1.val + res = self.meta_interp(g, [6, 14]) + assert res == g(6, 14) + + def test_multiple_specialied_zigzag(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'res']) + class Base: + def __init__(self, val): + self.val = val + class A(Base): + def binop(self, other): + return A(self.val + other.val) + def switch(self): + return B(self.val) + class B(Base): + def binop(self, other): + return B(self.val * other.val) + def switch(self): + return A(self.val) + def f(x, y): + res = x + while y > 0: + myjitdriver.can_enter_jit(y=y, x=x, res=res) + myjitdriver.jit_merge_point(y=y, x=x, res=res) + if y % 4 == 0: + res = res.switch() + res = res.binop(x) + y -= 1 + return res + def g(x, y): + a1 = f(A(x), y) + a2 = f(A(x), y) + b1 = f(B(x), y) + b2 = f(B(x), y) + assert a1.val == a2.val + assert b1.val == b2.val + return a1.val + b1.val + res = self.meta_interp(g, [3, 23]) + assert res == 7068153 + self.check_loop_count(6) + self.check_loops(guard_true=4, guard_class=0, int_add=2, int_mul=2, + guard_false=2) + def test_current_trace_length(self): myjitdriver = JitDriver(greens = ['g'], reds = ['x']) @dont_look_inside diff --git a/pypy/jit/metainterp/test/test_blackhole.py b/pypy/jit/metainterp/test/test_blackhole.py index d406aebb9c..3c7d9f0cbe 100644 --- a/pypy/jit/metainterp/test/test_blackhole.py +++ b/pypy/jit/metainterp/test/test_blackhole.py @@ -145,22 +145,32 @@ def test_convert_and_run_from_pyjitpl(): class TestBlackhole(LLJitMixin): - def test_blackholeinterp_cache(self): + def test_blackholeinterp_cache_basic(self): + class FakeJitcode: + def num_regs_r(self): + return 0 + interp1 = getblackholeinterp({}) + interp1.jitcode = FakeJitcode() + builder = interp1.builder + interp2 = builder.acquire_interp() + builder.release_interp(interp1) + interp3 = builder.acquire_interp() + assert builder.num_interpreters == 2 + + def test_blackholeinterp_cache_normal(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'y']) def choices(x): - if x == 2: return 10 - if x == 3: return 199 - if x == 4: return 124 - if x == 5: return -521 - if x == 6: return 8917 - if x == 7: return -387 - return 34871 + if x == 0: # <- this is the test that eventually succeeds, + return 0 # requiring a blackhole interp in a call stack + return 34871 # of two functions (hence num_interpreters==2) def f(x): y = 0 - while x > 0: + cont = 1 + while cont: myjitdriver.can_enter_jit(x=x, y=y) myjitdriver.jit_merge_point(x=x, y=y) - y += choices(x) + cont = choices(x) + y += cont x -= 1 return y # @@ -186,21 +196,19 @@ class TestBlackhole(LLJitMixin): def __init__(self, num): self.num = num def choices(x): - if x == 2: raise FooError(10) - if x == 3: raise FooError(199) - if x == 4: raise FooError(124) - if x == 5: raise FooError(-521) - if x == 6: raise FooError(8917) - if x == 7: raise FooError(-387) + if x == 0: + raise FooError(0) raise FooError(34871) def f(x): y = 0 - while x > 0: + while True: myjitdriver.can_enter_jit(x=x, y=y) myjitdriver.jit_merge_point(x=x, y=y) try: choices(x) except FooError, e: + if e.num == 0: + break y += e.num x -= 1 return y diff --git a/pypy/jit/metainterp/test/test_compile.py b/pypy/jit/metainterp/test/test_compile.py index 668f46ec22..02974ca891 100644 --- a/pypy/jit/metainterp/test/test_compile.py +++ b/pypy/jit/metainterp/test/test_compile.py @@ -1,32 +1,29 @@ from pypy.jit.metainterp.history import LoopToken, ConstInt, History, Stats from pypy.jit.metainterp.history import BoxInt, INT -from pypy.jit.metainterp.specnode import NotSpecNode, ConstantSpecNode from pypy.jit.metainterp.compile import insert_loop_token, compile_new_loop from pypy.jit.metainterp.compile import ResumeGuardDescr from pypy.jit.metainterp.compile import ResumeGuardCountersInt from pypy.jit.metainterp.compile import compile_tmp_callback -from pypy.jit.metainterp import optimize, jitprof, typesystem, compile -from pypy.jit.metainterp.test.test_optimizefindnode import LLtypeMixin +from pypy.jit.metainterp import nounroll_optimize, jitprof, typesystem, compile +from pypy.jit.metainterp.test.test_optimizeutil import LLtypeMixin from pypy.jit.tool.oparser import parse def test_insert_loop_token(): + # XXX this test is a bit useless now that there are no specnodes lst = [] # tok1 = LoopToken() - tok1.specnodes = [NotSpecNode()] insert_loop_token(lst, tok1) assert lst == [tok1] # tok2 = LoopToken() - tok2.specnodes = [ConstantSpecNode(ConstInt(8))] insert_loop_token(lst, tok2) - assert lst == [tok2, tok1] + assert lst == [tok1, tok2] # tok3 = LoopToken() - tok3.specnodes = [ConstantSpecNode(ConstInt(-13))] insert_loop_token(lst, tok3) - assert lst == [tok2, tok3, tok1] + assert lst == [tok1, tok2, tok3] class FakeCPU: @@ -41,7 +38,10 @@ class FakeLogger: pass class FakeState: - optimize_loop = staticmethod(optimize.optimize_loop) + optimize_loop = staticmethod(nounroll_optimize.optimize_loop) + + def attach_unoptimized_bridge_from_interp(*args): + pass class FakeGlobalData: loopnumbering = 0 @@ -86,7 +86,7 @@ def test_compile_new_loop(): metainterp.history.inputargs = loop.inputargs[:] # loop_tokens = [] - loop_token = compile_new_loop(metainterp, loop_tokens, 0) + loop_token = compile_new_loop(metainterp, loop_tokens, [], 0) assert loop_tokens == [loop_token] assert loop_token.number == 1 assert staticdata.globaldata.loopnumbering == 2 @@ -102,11 +102,11 @@ def test_compile_new_loop(): metainterp.history.operations = loop.operations[:] metainterp.history.inputargs = loop.inputargs[:] # - loop_token_2 = compile_new_loop(metainterp, loop_tokens, 0) + loop_token_2 = compile_new_loop(metainterp, loop_tokens, [], 0) assert loop_token_2 is loop_token assert loop_tokens == [loop_token] assert len(cpu.seen) == 0 - assert staticdata.globaldata.loopnumbering == 2 + assert staticdata.globaldata.loopnumbering == 2 def test_resume_guard_counters(): diff --git a/pypy/jit/metainterp/test/test_exception.py b/pypy/jit/metainterp/test/test_exception.py index bcba20c70d..7317deabf0 100644 --- a/pypy/jit/metainterp/test/test_exception.py +++ b/pypy/jit/metainterp/test/test_exception.py @@ -516,10 +516,9 @@ class ExceptionTests: assert res == -1 self.check_tree_loop_count(2) # the loop and the entry path # we get: - # ENTER - compile the new loop - # ENTER - compile the entry bridge + # ENTER - compile the new loop and the entry bridge # ENTER - compile the leaving path (raising MyError) - self.check_enter_count(3) + self.check_enter_count(2) def test_bridge_from_interpreter_exc_2(self): diff --git a/pypy/jit/metainterp/test/test_greenfield.py b/pypy/jit/metainterp/test/test_greenfield.py index b9fbbf50a8..f7f292291c 100644 --- a/pypy/jit/metainterp/test/test_greenfield.py +++ b/pypy/jit/metainterp/test/test_greenfield.py @@ -49,7 +49,7 @@ class GreenFieldsTests: # res = self.meta_interp(g, [7]) assert res == -22 - self.check_loop_count(4) + self.check_loop_count(6) self.check_loops(guard_value=0) diff --git a/pypy/jit/metainterp/test/test_intbound.py b/pypy/jit/metainterp/test/test_intbound.py index dde7801140..84376013cb 100644 --- a/pypy/jit/metainterp/test/test_intbound.py +++ b/pypy/jit/metainterp/test/test_intbound.py @@ -210,6 +210,17 @@ def test_mul_bound(): assert not a.contains(4) assert not a.contains(-3) +def test_shift_bound(): + for _, _, b1 in some_bounds(): + for _, _, b2 in some_bounds(): + bleft = b1.lshift_bound(b2) + bright = b1.rshift_bound(b2) + for n1 in nbr: + for n2 in range(10): + if b1.contains(n1) and b2.contains(n2): + assert bleft.contains(n1 << n2) + assert bright.contains(n1 >> n2) + def test_div_bound(): for _, _, b1 in some_bounds(): for _, _, b2 in some_bounds(): diff --git a/pypy/jit/metainterp/test/test_jitdriver.py b/pypy/jit/metainterp/test/test_jitdriver.py index 900c0dc6b5..b85d28a74b 100644 --- a/pypy/jit/metainterp/test/test_jitdriver.py +++ b/pypy/jit/metainterp/test/test_jitdriver.py @@ -42,7 +42,7 @@ class MultipleJitDriversTests: # at the generated machine code self.check_loop_count(5) self.check_tree_loop_count(4) # 2 x loop, 2 x enter bridge - self.check_enter_count(7) + self.check_enter_count(5) def test_inline(self): # this is not an example of reasonable code: loop1() is unrolled diff --git a/pypy/jit/metainterp/test/test_jitprof.py b/pypy/jit/metainterp/test/test_jitprof.py index 528226a29a..3c51fd60b0 100644 --- a/pypy/jit/metainterp/test/test_jitprof.py +++ b/pypy/jit/metainterp/test/test_jitprof.py @@ -55,6 +55,8 @@ class TestProfile(ProfilerMixin): TRACING, BACKEND, ~ BACKEND, + BACKEND, + ~ BACKEND, ~ TRACING, RUNNING, ~ RUNNING, @@ -62,9 +64,9 @@ class TestProfile(ProfilerMixin): ~ BLACKHOLE ] assert profiler.events == expected - assert profiler.times == [2, 1, 1, 1] - assert profiler.counters == [1, 1, 1, 1, 3, 3, 1, 7, 1, 0, 0, 0, - 0, 0, 0, 0] + assert profiler.times == [3, 2, 1, 1] + assert profiler.counters == [1, 2, 1, 1, 3, 3, 1, 13, 2, 0, 0, 0, + 0, 0, 0, 0, 0] def test_simple_loop_with_call(self): @dont_look_inside diff --git a/pypy/jit/metainterp/test/test_list.py b/pypy/jit/metainterp/test/test_list.py index 79ae40b04c..30e9658bc1 100644 --- a/pypy/jit/metainterp/test/test_list.py +++ b/pypy/jit/metainterp/test/test_list.py @@ -236,4 +236,4 @@ class TestLLtype(ListTests, LLJitMixin): return a * b res = self.meta_interp(f, [37]) assert res == f(37) - self.check_loops(getfield_gc=1) + self.check_loops(getfield_gc=1, everywhere=True) diff --git a/pypy/jit/metainterp/test/test_loop.py b/pypy/jit/metainterp/test/test_loop.py index fc719abe55..373e014acf 100644 --- a/pypy/jit/metainterp/test/test_loop.py +++ b/pypy/jit/metainterp/test/test_loop.py @@ -87,7 +87,10 @@ class LoopTest(object): return res * 2 res = self.meta_interp(f, [6, 33], policy=StopAtXPolicy(l)) assert res == f(6, 33) - self.check_loop_count(2) + if self.optimizer == OPTIMIZER_FULL: + self.check_loop_count(3) + else: + self.check_loop_count(2) def test_alternating_loops(self): myjitdriver = JitDriver(greens = [], reds = ['pattern']) @@ -101,8 +104,11 @@ class LoopTest(object): pass pattern >>= 1 return 42 - self.meta_interp(f, [0xF0F0]) - self.check_loop_count(2) + self.meta_interp(f, [0xF0F0F0]) + if self.optimizer == OPTIMIZER_FULL: + self.check_loop_count(3) + else: + self.check_loop_count(2) def test_interp_simple(self): myjitdriver = JitDriver(greens = ['i'], reds = ['x', 'y']) @@ -295,6 +301,105 @@ class LoopTest(object): res = self.meta_interp(f, [2, 3]) assert res == expected + def test_loop_in_bridge1(self): + myjitdriver = JitDriver(greens = ['i'], reds = ['x', 'y', 'res']) + bytecode = "abs>cxXyY" + def f(y): + res = x = 0 + i = 0 + op = '-' + while i < len(bytecode): + myjitdriver.jit_merge_point(i=i, x=x, y=y, res=res) + op = bytecode[i] + if op == 'a': + res += 1 + elif op == 'b': + res += 10 + elif op == 'c': + res += 10000 + elif op == 's': + x = y + elif op == 'y': + y -= 1 + elif op == 'Y': + if y: + i = 1 + myjitdriver.can_enter_jit(i=i, x=x, y=y, res=res) + continue + elif op == 'x': + x -= 1 + elif op == 'X': + if x > 0: + i -= 2 + myjitdriver.can_enter_jit(i=i, x=x, y=y, res=res) + continue + elif op == '>': + if y > 6: + i += 4 + continue + i += 1 + return res + + expected = f(12) + res = self.meta_interp(f, [12]) + print res + assert res == expected + + def test_nested_loops_discovered_by_bridge(self): + # This is an bytecode implementation of the loop below. With + # threshold=3 the first trace produced will start with a failing + # test j <= i from the inner loop followed by one iteration of the + # outer loop followed by one iteration of the inner loop. A bridge + # is then created by tracing the inner loop again. + # + # i = j = x = 0 + # while i < n: + # j = 0 + # while j <= i: + # j = j + 1 + # x = x + (i&j) + # i = i + 1 + + myjitdriver = JitDriver(greens = ['pos'], reds = ['i', 'j', 'n', 'x']) + bytecode = "IzJxji" + def f(n, threshold): + myjitdriver.set_param('threshold', threshold) + i = j = x = 0 + pos = 0 + op = '-' + while pos < len(bytecode): + myjitdriver.jit_merge_point(pos=pos, i=i, j=j, n=n, x=x) + op = bytecode[pos] + if op == 'z': + j = 0 + elif op == 'i': + i += 1 + pos = 0 + myjitdriver.can_enter_jit(pos=pos, i=i, j=j, n=n, x=x) + continue + elif op == 'j': + j += 1 + pos = 2 + myjitdriver.can_enter_jit(pos=pos, i=i, j=j, n=n, x=x) + continue + elif op == 'I': + if not (i < n): + pos = 5 + elif op == 'J': + if not (j <= i): + pos = 4 + elif op == 'x': + x = x + (i&j) + + pos += 1 + + return x + + for th in (3, 1, 2, 4, 5): # Start with the interesting case + expected = f(25, th) + res = self.meta_interp(f, [25, th]) + assert res == expected + def test_three_nested_loops(self): myjitdriver = JitDriver(greens = ['i'], reds = ['x']) bytecode = ".+357" @@ -397,8 +502,12 @@ class LoopTest(object): res = self.meta_interp(f, [100, 5], policy=StopAtXPolicy(externfn)) assert res == expected - self.check_loop_count(2) - self.check_tree_loop_count(2) # 1 loop, 1 bridge from interp + if self.optimizer == OPTIMIZER_FULL: + self.check_loop_count(2) + self.check_tree_loop_count(2) # 1 loop, 1 bridge from interp + else: + self.check_loop_count(2) + self.check_tree_loop_count(1) # 1 loop, callable from the interp def test_example(self): myjitdriver = JitDriver(greens = ['i'], diff --git a/pypy/jit/metainterp/test/test_loop_spec.py b/pypy/jit/metainterp/test/test_loop_unroll.py index 0cc7403144..e7a2221177 100644 --- a/pypy/jit/metainterp/test/test_loop_spec.py +++ b/pypy/jit/metainterp/test/test_loop_unroll.py @@ -3,17 +3,16 @@ from pypy.rlib.jit import OPTIMIZER_FULL from pypy.jit.metainterp.test import test_loop from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin -class LoopSpecTest(test_loop.LoopTest): +class LoopUnrollTest(test_loop.LoopTest): optimizer = OPTIMIZER_FULL automatic_promotion_result = { 'int_add' : 3, 'int_gt' : 1, 'guard_false' : 1, 'jump' : 1, - 'guard_value' : 1 } # ====> test_loop.py -class TestLLtype(LoopSpecTest, LLJitMixin): +class TestLLtype(LoopUnrollTest, LLJitMixin): pass -class TestOOtype(LoopSpecTest, OOJitMixin): +class TestOOtype(LoopUnrollTest, OOJitMixin): pass diff --git a/pypy/jit/metainterp/test/test_memmgr.py b/pypy/jit/metainterp/test/test_memmgr.py index 8caedcf676..81a1b926f5 100644 --- a/pypy/jit/metainterp/test/test_memmgr.py +++ b/pypy/jit/metainterp/test/test_memmgr.py @@ -132,7 +132,7 @@ class _TestIntegration(LLJitMixin): res = self.meta_interp(f, [], loop_longevity=1) assert res == 42 # we should see a loop for each call to g() - self.check_tree_loop_count(8 + 20*2) + self.check_tree_loop_count(8 + 20*2*2) def test_throw_away_old_loops(self): myjitdriver = JitDriver(greens=['m'], reds=['n']) @@ -157,7 +157,7 @@ class _TestIntegration(LLJitMixin): res = self.meta_interp(f, [], loop_longevity=3) assert res == 42 - self.check_tree_loop_count(2 + 10*4) # 42 :-) + self.check_tree_loop_count(2 + 10*4*2) def test_call_assembler_keep_alive(self): myjitdriver1 = JitDriver(greens=['m'], reds=['n']) @@ -191,7 +191,7 @@ class _TestIntegration(LLJitMixin): res = self.meta_interp(f, [1], loop_longevity=4, inline=True) assert res == 42 - self.check_tree_loop_count(8) + self.check_tree_loop_count(12) # ____________________________________________________________ diff --git a/pypy/jit/metainterp/test/test_optimizebasic.py b/pypy/jit/metainterp/test/test_optimizebasic.py new file mode 100644 index 0000000000..40170c8535 --- /dev/null +++ b/pypy/jit/metainterp/test/test_optimizebasic.py @@ -0,0 +1,4593 @@ +import py +from pypy.rlib.objectmodel import instantiate +from pypy.jit.metainterp.test.test_optimizeutil import (LLtypeMixin, + #OOtypeMixin, + BaseTest) +import pypy.jit.metainterp.optimizeopt.optimizer as optimizeopt +import pypy.jit.metainterp.optimizeopt.virtualize as virtualize +from pypy.jit.metainterp.optimizeutil import InvalidLoop +from pypy.jit.metainterp.history import AbstractDescr, ConstInt, BoxInt +from pypy.jit.metainterp.jitprof import EmptyProfiler +from pypy.jit.metainterp import executor, compile, resume, history +from pypy.jit.metainterp.resoperation import rop, opname, ResOperation +from pypy.jit.tool.oparser import pure_parse + +##class FakeFrame(object): +## parent_resumedata_snapshot = None +## parent_resumedata_frame_info_list = None + +## def __init__(self, code="", pc=0): +## self.jitcode = code +## self.pc = pc + +class Fake(object): + failargs_limit = 1000 + storedebug = None + +class FakeMetaInterpStaticData(object): + + def __init__(self, cpu): + self.cpu = cpu + self.profiler = EmptyProfiler() + self.options = Fake() + self.globaldata = Fake() + +def test_store_final_boxes_in_guard(): + from pypy.jit.metainterp.compile import ResumeGuardDescr + from pypy.jit.metainterp.resume import tag, TAGBOX + b0 = BoxInt() + b1 = BoxInt() + opt = optimizeopt.Optimizer(FakeMetaInterpStaticData(LLtypeMixin.cpu), + None) + fdescr = ResumeGuardDescr() + op = ResOperation(rop.GUARD_TRUE, ['dummy'], None, descr=fdescr) + # setup rd data + fi0 = resume.FrameInfo(None, "code0", 11) + fdescr.rd_frame_info_list = resume.FrameInfo(fi0, "code1", 33) + snapshot0 = resume.Snapshot(None, [b0]) + fdescr.rd_snapshot = resume.Snapshot(snapshot0, [b1]) + # + opt.store_final_boxes_in_guard(op) + if op.getfailargs() == [b0, b1]: + assert list(fdescr.rd_numb.nums) == [tag(1, TAGBOX)] + assert list(fdescr.rd_numb.prev.nums) == [tag(0, TAGBOX)] + else: + assert op.getfailargs() == [b1, b0] + assert list(fdescr.rd_numb.nums) == [tag(0, TAGBOX)] + assert list(fdescr.rd_numb.prev.nums) == [tag(1, TAGBOX)] + assert fdescr.rd_virtuals is None + assert fdescr.rd_consts == [] + +def test_sharing_field_lists_of_virtual(): + class FakeOptimizer(object): + class cpu(object): + pass + opt = FakeOptimizer() + virt1 = virtualize.AbstractVirtualStructValue(opt, None) + lst1 = virt1._get_field_descr_list() + assert lst1 == [] + lst2 = virt1._get_field_descr_list() + assert lst1 is lst2 + virt1.setfield(LLtypeMixin.valuedescr, optimizeopt.OptValue(None)) + lst3 = virt1._get_field_descr_list() + assert lst3 == [LLtypeMixin.valuedescr] + lst4 = virt1._get_field_descr_list() + assert lst3 is lst4 + + virt2 = virtualize.AbstractVirtualStructValue(opt, None) + lst5 = virt2._get_field_descr_list() + assert lst5 is lst1 + virt2.setfield(LLtypeMixin.valuedescr, optimizeopt.OptValue(None)) + lst6 = virt1._get_field_descr_list() + assert lst6 is lst3 + +def test_reuse_vinfo(): + class FakeVInfo(object): + def set_content(self, fieldnums): + self.fieldnums = fieldnums + def equals(self, fieldnums): + return self.fieldnums == fieldnums + class FakeVirtualValue(virtualize.AbstractVirtualValue): + def _make_virtual(self, *args): + return FakeVInfo() + v1 = FakeVirtualValue(None, None, None) + vinfo1 = v1.make_virtual_info(None, [1, 2, 4]) + vinfo2 = v1.make_virtual_info(None, [1, 2, 4]) + assert vinfo1 is vinfo2 + vinfo3 = v1.make_virtual_info(None, [1, 2, 6]) + assert vinfo3 is not vinfo2 + vinfo4 = v1.make_virtual_info(None, [1, 2, 6]) + assert vinfo3 is vinfo4 + +def test_descrlist_dict(): + from pypy.jit.metainterp import optimizeutil + h1 = optimizeutil.descrlist_hash([]) + h2 = optimizeutil.descrlist_hash([LLtypeMixin.valuedescr]) + h3 = optimizeutil.descrlist_hash( + [LLtypeMixin.valuedescr, LLtypeMixin.nextdescr]) + assert h1 != h2 + assert h2 != h3 + assert optimizeutil.descrlist_eq([], []) + assert not optimizeutil.descrlist_eq([], [LLtypeMixin.valuedescr]) + assert optimizeutil.descrlist_eq([LLtypeMixin.valuedescr], + [LLtypeMixin.valuedescr]) + assert not optimizeutil.descrlist_eq([LLtypeMixin.valuedescr], + [LLtypeMixin.nextdescr]) + assert optimizeutil.descrlist_eq([LLtypeMixin.valuedescr, LLtypeMixin.nextdescr], + [LLtypeMixin.valuedescr, LLtypeMixin.nextdescr]) + assert not optimizeutil.descrlist_eq([LLtypeMixin.nextdescr, LLtypeMixin.valuedescr], + [LLtypeMixin.valuedescr, LLtypeMixin.nextdescr]) + + # descrlist_eq should compare by identity of the descrs, not by the result + # of sort_key + class FakeDescr(object): + def sort_key(self): + return 1 + + assert not optimizeutil.descrlist_eq([FakeDescr()], [FakeDescr()]) + + +# ____________________________________________________________ + +def equaloplists(oplist1, oplist2, strict_fail_args=True, remap={}, + text_right=None): + # try to use the full width of the terminal to display the list + # unfortunately, does not work with the default capture method of py.test + # (which is fd), you you need to use either -s or --capture=sys, else you + # get the standard 80 columns width + totwidth = py.io.get_terminal_width() + width = totwidth / 2 - 1 + print ' Comparing lists '.center(totwidth, '-') + text_right = text_right or 'expected' + print '%s| %s' % ('optimized'.center(width), text_right.center(width)) + for op1, op2 in zip(oplist1, oplist2): + txt1 = str(op1) + txt2 = str(op2) + while txt1 or txt2: + print '%s| %s' % (txt1[:width].ljust(width), txt2[:width]) + txt1 = txt1[width:] + txt2 = txt2[width:] + assert op1.getopnum() == op2.getopnum() + assert op1.numargs() == op2.numargs() + for i in range(op1.numargs()): + x = op1.getarg(i) + y = op2.getarg(i) + assert x == remap.get(y, y) + if op2.result in remap: + assert op1.result == remap[op2.result] + else: + remap[op2.result] = op1.result + if op1.getopnum() != rop.JUMP: # xxx obscure + assert op1.getdescr() == op2.getdescr() + if op1.getfailargs() or op2.getfailargs(): + assert len(op1.getfailargs()) == len(op2.getfailargs()) + if strict_fail_args: + for x, y in zip(op1.getfailargs(), op2.getfailargs()): + assert x == remap.get(y, y) + else: + fail_args1 = set(op1.getfailargs()) + fail_args2 = set([remap.get(y, y) for y in op2.getfailargs()]) + assert fail_args1 == fail_args2 + assert len(oplist1) == len(oplist2) + print '-'*totwidth + return True + +def test_equaloplists(): + ops = """ + [i0] + i1 = int_add(i0, 1) + i2 = int_add(i1, 1) + guard_true(i1) [i2] + jump(i1) + """ + namespace = {} + loop1 = pure_parse(ops, namespace=namespace) + loop2 = pure_parse(ops, namespace=namespace) + loop3 = pure_parse(ops.replace("i2 = int_add", "i2 = int_sub"), + namespace=namespace) + assert equaloplists(loop1.operations, loop2.operations) + py.test.raises(AssertionError, + "equaloplists(loop1.operations, loop3.operations)") + +def test_equaloplists_fail_args(): + ops = """ + [i0] + i1 = int_add(i0, 1) + i2 = int_add(i1, 1) + guard_true(i1) [i2, i1] + jump(i1) + """ + namespace = {} + loop1 = pure_parse(ops, namespace=namespace) + loop2 = pure_parse(ops.replace("[i2, i1]", "[i1, i2]"), + namespace=namespace) + py.test.raises(AssertionError, + "equaloplists(loop1.operations, loop2.operations)") + assert equaloplists(loop1.operations, loop2.operations, + strict_fail_args=False) + loop3 = pure_parse(ops.replace("[i2, i1]", "[i2, i0]"), + namespace=namespace) + py.test.raises(AssertionError, + "equaloplists(loop1.operations, loop3.operations)") + +# ____________________________________________________________ + +class Storage(compile.ResumeGuardDescr): + "for tests." + def __init__(self, metainterp_sd=None, original_greenkey=None): + self.metainterp_sd = metainterp_sd + self.original_greenkey = original_greenkey + def store_final_boxes(self, op, boxes): + op.setfailargs(boxes) + def __eq__(self, other): + return type(self) is type(other) # xxx obscure + +def _sortboxes(boxes): + _kind2count = {history.INT: 1, history.REF: 2, history.FLOAT: 3} + return sorted(boxes, key=lambda box: _kind2count[box.type]) + +class BaseTestBasic(BaseTest): + + def invent_fail_descr(self, fail_args): + if fail_args is None: + return None + descr = Storage() + descr.rd_frame_info_list = resume.FrameInfo(None, "code", 11) + descr.rd_snapshot = resume.Snapshot(None, _sortboxes(fail_args)) + return descr + + def assert_equal(self, optimized, expected): + assert len(optimized.inputargs) == len(expected.inputargs) + remap = {} + for box1, box2 in zip(optimized.inputargs, expected.inputargs): + assert box1.__class__ == box2.__class__ + remap[box2] = box1 + assert equaloplists(optimized.operations, + expected.operations, False, remap) + + def optimize_loop(self, ops, optops): + loop = self.parse(ops) + # + self.loop = loop + metainterp_sd = FakeMetaInterpStaticData(self.cpu) + if hasattr(self, 'vrefinfo'): + metainterp_sd.virtualref_info = self.vrefinfo + if hasattr(self, 'callinfocollection'): + metainterp_sd.callinfocollection = self.callinfocollection + # + # XXX list the exact optimizations that are needed for each test + from pypy.jit.metainterp.optimizeopt import (OptIntBounds, + OptRewrite, + OptVirtualize, + OptString, + OptHeap, + Optimizer) + from pypy.jit.metainterp.optimizeopt.fficall import OptFfiCall + + optimizations = [OptIntBounds(), + OptRewrite(), + OptVirtualize(), + OptString(), + OptHeap(), + OptFfiCall(), + ] + optimizer = Optimizer(metainterp_sd, loop, optimizations) + optimizer.propagate_all_forward() + # + expected = self.parse(optops) + print '\n'.join([str(o) for o in loop.operations]) + self.assert_equal(loop, expected) + + +class BaseTestOptimizeBasic(BaseTestBasic): + + def test_simple(self): + ops = """ + [i] + i0 = int_sub(i, 1) + guard_value(i0, 0) [i0] + jump(i) + """ + expected = """ + [i] + i0 = int_sub(i, 1) + guard_value(i0, 0) [i0] + jump(1) + """ + self.optimize_loop(ops, expected) + + def test_constant_propagate(self): + ops = """ + [] + i0 = int_add(2, 3) + i1 = int_is_true(i0) + guard_true(i1) [] + i2 = int_is_zero(i1) + guard_false(i2) [] + guard_value(i0, 5) [] + jump() + """ + expected = """ + [] + jump() + """ + self.optimize_loop(ops, expected) + + def test_constant_propagate_ovf(self): + ops = """ + [] + i0 = int_add_ovf(2, 3) + guard_no_overflow() [] + i1 = int_is_true(i0) + guard_true(i1) [] + i2 = int_is_zero(i1) + guard_false(i2) [] + guard_value(i0, 5) [] + jump() + """ + expected = """ + [] + jump() + """ + self.optimize_loop(ops, expected) + + def test_constfold_all(self): + from pypy.jit.backend.llgraph.llimpl import TYPES # xxx fish + from pypy.jit.metainterp.executor import execute_nonspec + from pypy.jit.metainterp.history import BoxInt + import random + for opnum in range(rop.INT_ADD, rop.SAME_AS+1): + try: + op = opname[opnum] + except KeyError: + continue + if 'FLOAT' in op: + continue + argtypes, restype = TYPES[op.lower()] + args = [] + for argtype in argtypes: + assert argtype in ('int', 'bool') + args.append(random.randrange(1, 20)) + assert restype in ('int', 'bool') + ops = """ + [] + i1 = %s(%s) + escape(i1) + jump() + """ % (op.lower(), ', '.join(map(str, args))) + argboxes = [BoxInt(a) for a in args] + expected_value = execute_nonspec(self.cpu, None, opnum, + argboxes).getint() + expected = """ + [] + escape(%d) + jump() + """ % expected_value + self.optimize_loop(ops, expected) + + # ---------- + + def test_remove_guard_class_1(self): + ops = """ + [p0] + guard_class(p0, ConstClass(node_vtable)) [] + guard_class(p0, ConstClass(node_vtable)) [] + jump(p0) + """ + expected = """ + [p0] + guard_class(p0, ConstClass(node_vtable)) [] + jump(p0) + """ + self.optimize_loop(ops, expected) + + def test_remove_guard_class_2(self): + ops = """ + [i0] + p0 = new_with_vtable(ConstClass(node_vtable)) + escape(p0) + guard_class(p0, ConstClass(node_vtable)) [] + jump(i0) + """ + expected = """ + [i0] + p0 = new_with_vtable(ConstClass(node_vtable)) + escape(p0) + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_remove_guard_class_constant(self): + ops = """ + [i0] + p0 = same_as(ConstPtr(myptr)) + guard_class(p0, ConstClass(node_vtable)) [] + jump(i0) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_constant_boolrewrite_lt(self): + ops = """ + [i0] + i1 = int_lt(i0, 0) + guard_true(i1) [] + i2 = int_ge(i0, 0) + guard_false(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 0) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_constant_boolrewrite_gt(self): + ops = """ + [i0] + i1 = int_gt(i0, 0) + guard_true(i1) [] + i2 = int_le(i0, 0) + guard_false(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_gt(i0, 0) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_constant_boolrewrite_reflex(self): + ops = """ + [i0] + i1 = int_gt(i0, 0) + guard_true(i1) [] + i2 = int_lt(0, i0) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_gt(i0, 0) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_constant_boolrewrite_reflex_invers(self): + ops = """ + [i0] + i1 = int_gt(i0, 0) + guard_true(i1) [] + i2 = int_ge(0, i0) + guard_false(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_gt(i0, 0) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_remove_consecutive_guard_value_constfold(self): + ops = """ + [] + i0 = escape() + guard_value(i0, 0) [] + i1 = int_add(i0, 1) + guard_value(i1, 1) [] + i2 = int_add(i1, 2) + escape(i2) + jump() + """ + expected = """ + [] + i0 = escape() + guard_value(i0, 0) [] + escape(3) + jump() + """ + self.optimize_loop(ops, expected) + + def test_remove_guard_value_if_constant(self): + ops = """ + [p1] + guard_value(p1, ConstPtr(myptr)) [] + jump(ConstPtr(myptr)) + """ + expected = """ + [] + jump() + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Constant(myptr)', expected) + + def test_ooisnull_oononnull_1(self): + ops = """ + [p0] + guard_class(p0, ConstClass(node_vtable)) [] + guard_nonnull(p0) [] + jump(p0) + """ + expected = """ + [p0] + guard_class(p0, ConstClass(node_vtable)) [] + jump(p0) + """ + self.optimize_loop(ops, expected) + + def test_int_is_true_1(self): + ops = """ + [i0] + i1 = int_is_true(i0) + guard_true(i1) [] + i2 = int_is_true(i0) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_is_true(i0) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_int_is_true_is_zero(self): + py.test.skip("XXX implement me") + ops = """ + [i0] + i1 = int_is_true(i0) + guard_true(i1) [] + i2 = int_is_zero(i0) + guard_false(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_is_true(i0) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_ooisnull_oononnull_2(self): + ops = """ + [p0] + guard_nonnull(p0) [] + guard_nonnull(p0) [] + jump(p0) + """ + expected = """ + [p0] + guard_nonnull(p0) [] + jump(p0) + """ + self.optimize_loop(ops, expected) + + def test_ooisnull_on_null_ptr_1(self): + ops = """ + [] + p0 = escape() + guard_isnull(p0) [] + guard_isnull(p0) [] + jump() + """ + expected = """ + [] + p0 = escape() + guard_isnull(p0) [] + jump() + """ + self.optimize_loop(ops, expected) + + def test_ooisnull_oononnull_via_virtual(self): + ops = """ + [p0] + pv = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(pv, p0, descr=valuedescr) + guard_nonnull(p0) [] + p1 = getfield_gc(pv, descr=valuedescr) + guard_nonnull(p1) [] + jump(p0) + """ + expected = """ + [p0] + guard_nonnull(p0) [] + jump(p0) + """ + self.optimize_loop(ops, expected) + + def test_oois_1(self): + ops = """ + [p0] + guard_class(p0, ConstClass(node_vtable)) [] + i0 = ptr_ne(p0, NULL) + guard_true(i0) [] + i1 = ptr_eq(p0, NULL) + guard_false(i1) [] + i2 = ptr_ne(NULL, p0) + guard_true(i0) [] + i3 = ptr_eq(NULL, p0) + guard_false(i1) [] + jump(p0) + """ + expected = """ + [p0] + guard_class(p0, ConstClass(node_vtable)) [] + jump(p0) + """ + self.optimize_loop(ops, expected) + + def test_nonnull_1(self): + ops = """ + [p0] + setfield_gc(p0, 5, descr=valuedescr) # forces p0 != NULL + i0 = ptr_ne(p0, NULL) + guard_true(i0) [] + i1 = ptr_eq(p0, NULL) + guard_false(i1) [] + i2 = ptr_ne(NULL, p0) + guard_true(i0) [] + i3 = ptr_eq(NULL, p0) + guard_false(i1) [] + guard_nonnull(p0) [] + jump(p0) + """ + expected = """ + [p0] + setfield_gc(p0, 5, descr=valuedescr) + jump(p0) + """ + self.optimize_loop(ops, expected) + + def test_const_guard_value(self): + ops = """ + [] + i = int_add(5, 3) + guard_value(i, 8) [] + jump() + """ + expected = """ + [] + jump() + """ + self.optimize_loop(ops, expected) + + def test_constptr_guard_value(self): + ops = """ + [] + p1 = escape() + guard_value(p1, ConstPtr(myptr)) [] + jump() + """ + self.optimize_loop(ops, ops) + + def test_guard_value_to_guard_true(self): + ops = """ + [i] + i1 = int_lt(i, 3) + guard_value(i1, 1) [i] + jump(i) + """ + expected = """ + [i] + i1 = int_lt(i, 3) + guard_true(i1) [i] + jump(i) + """ + self.optimize_loop(ops, expected) + + def test_guard_value_to_guard_false(self): + ops = """ + [i] + i1 = int_is_true(i) + guard_value(i1, 0) [i] + jump(i) + """ + expected = """ + [i] + i1 = int_is_true(i) + guard_false(i1) [i] + jump(i) + """ + self.optimize_loop(ops, expected) + + def test_guard_value_on_nonbool(self): + ops = """ + [i] + i1 = int_add(i, 3) + guard_value(i1, 0) [i] + jump(i) + """ + expected = """ + [i] + i1 = int_add(i, 3) + guard_value(i1, 0) [i] + jump(-3) + """ + self.optimize_loop(ops, expected) + + def test_int_is_true_of_bool(self): + ops = """ + [i0, i1] + i2 = int_gt(i0, i1) + i3 = int_is_true(i2) + i4 = int_is_true(i3) + guard_value(i4, 0) [i0, i1] + jump(i0, i1) + """ + expected = """ + [i0, i1] + i2 = int_gt(i0, i1) + guard_false(i2) [i0, i1] + jump(i0, i1) + """ + self.optimize_loop(ops, expected) + + + + + def test_p123_simple(self): + ops = """ + [i1, p2, p3] + i3 = getfield_gc(p3, descr=valuedescr) + escape(i3) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i1, descr=valuedescr) + jump(i1, p1, p2) + """ + # We cannot track virtuals that survive for more than two iterations. + self.optimize_loop(ops, ops) + + def test_p123_nested(self): + ops = """ + [i1, p2, p3] + i3 = getfield_gc(p3, descr=valuedescr) + escape(i3) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i1, descr=valuedescr) + p1sub = new_with_vtable(ConstClass(node_vtable2)) + setfield_gc(p1sub, i1, descr=valuedescr) + setfield_gc(p1, p1sub, descr=nextdescr) + jump(i1, p1, p2) + """ + # The same as test_p123_simple, but with a virtual containing another + # virtual. + self.optimize_loop(ops, ops) + + def test_p123_anti_nested(self): + ops = """ + [i1, p2, p3] + p3sub = getfield_gc(p3, descr=nextdescr) + i3 = getfield_gc(p3sub, descr=valuedescr) + escape(i3) + p2sub = new_with_vtable(ConstClass(node_vtable2)) + setfield_gc(p2sub, i1, descr=valuedescr) + setfield_gc(p2, p2sub, descr=nextdescr) + p1 = new_with_vtable(ConstClass(node_vtable)) + jump(i1, p1, p2) + """ + # The same as test_p123_simple, but in the end the "old" p2 contains + # a "young" virtual p2sub. Make sure it is all forced. + self.optimize_loop(ops, ops) + + # ---------- + + def test_fold_guard_no_exception(self): + ops = """ + [i] + guard_no_exception() [] + i1 = int_add(i, 3) + guard_no_exception() [] + i2 = call(i1, descr=nonwritedescr) + guard_no_exception() [i1, i2] + guard_no_exception() [] + i3 = call(i2, descr=nonwritedescr) + jump(i1) # the exception is considered lost when we loop back + """ + expected = """ + [i] + guard_no_exception() [] + i1 = int_add(i, 3) + i2 = call(i1, descr=nonwritedescr) + guard_no_exception() [i1, i2] + i3 = call(i2, descr=nonwritedescr) + jump(i1) + """ + self.optimize_loop(ops, expected) + + # ---------- + + def test_call_loopinvariant(self): + ops = """ + [i1] + i2 = call_loopinvariant(1, i1, descr=nonwritedescr) + guard_no_exception() [] + guard_value(i2, 1) [] + i3 = call_loopinvariant(1, i1, descr=nonwritedescr) + guard_no_exception() [] + guard_value(i3, 1) [] + i4 = call_loopinvariant(1, i1, descr=nonwritedescr) + guard_no_exception() [] + guard_value(i4, 1) [] + jump(i1) + """ + expected = """ + [i1] + i2 = call(1, i1, descr=nonwritedescr) + guard_no_exception() [] + guard_value(i2, 1) [] + jump(i1) + """ + self.optimize_loop(ops, expected) + + + # ---------- + + def test_virtual_1(self): + ops = """ + [i, p0] + i0 = getfield_gc(p0, descr=valuedescr) + i1 = int_add(i0, i) + setfield_gc(p0, i1, descr=valuedescr) + jump(i, p0) + """ + expected = """ + [i, i2] + i1 = int_add(i2, i) + jump(i, i1) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)', + expected) + + def test_virtual_float(self): + ops = """ + [f, p0] + f0 = getfield_gc(p0, descr=floatdescr) + f1 = float_add(f0, f) + setfield_gc(p0, f1, descr=floatdescr) + jump(f, p0) + """ + expected = """ + [f, f2] + f1 = float_add(f2, f) + jump(f, f1) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Not, Virtual(node_vtable, floatdescr=Not)', + expected) + + def test_virtual_2(self): + ops = """ + [i, p0] + i0 = getfield_gc(p0, descr=valuedescr) + i1 = int_add(i0, i) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i1, descr=valuedescr) + jump(i, p1) + """ + expected = """ + [i, i2] + i1 = int_add(i2, i) + jump(i, i1) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)', + expected) + + def test_virtual_oois(self): + ops = """ + [p0, p1, p2] + guard_nonnull(p0) [] + i3 = ptr_ne(p0, NULL) + guard_true(i3) [] + i4 = ptr_eq(p0, NULL) + guard_false(i4) [] + i5 = ptr_ne(NULL, p0) + guard_true(i5) [] + i6 = ptr_eq(NULL, p0) + guard_false(i6) [] + i7 = ptr_ne(p0, p1) + guard_true(i7) [] + i8 = ptr_eq(p0, p1) + guard_false(i8) [] + i9 = ptr_ne(p0, p2) + guard_true(i9) [] + i10 = ptr_eq(p0, p2) + guard_false(i10) [] + i11 = ptr_ne(p2, p1) + guard_true(i11) [] + i12 = ptr_eq(p2, p1) + guard_false(i12) [] + jump(p0, p1, p2) + """ + expected = """ + [p2] + # all constant-folded :-) + jump(p2) + """ + py.test.skip("XXX") + self.optimize_loop(ops, '''Virtual(node_vtable), + Virtual(node_vtable), + Not''', + expected) + # + # to be complete, we also check the no-opt case where most comparisons + # are not removed. The exact set of comparisons removed depends on + # the details of the algorithm... + expected2 = """ + [p0, p1, p2] + guard_nonnull(p0) [] + i7 = ptr_ne(p0, p1) + guard_true(i7) [] + i9 = ptr_ne(p0, p2) + guard_true(i9) [] + i11 = ptr_ne(p2, p1) + guard_true(i11) [] + jump(p0, p1, p2) + """ + self.optimize_loop(ops, expected2) + + def test_virtual_default_field(self): + ops = """ + [p0] + i0 = getfield_gc(p0, descr=valuedescr) + guard_value(i0, 0) [] + p1 = new_with_vtable(ConstClass(node_vtable)) + # the field 'value' has its default value of 0 + jump(p1) + """ + expected = """ + [i] + guard_value(i, 0) [] + jump(0) + """ + # the 'expected' is sub-optimal, but it should be done by another later + # optimization step. See test_find_nodes_default_field() for why. + py.test.skip("XXX") + self.optimize_loop(ops, 'Virtual(node_vtable, valuedescr=Not)', + expected) + + def test_virtual_3(self): + ops = """ + [i] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i, descr=valuedescr) + i0 = getfield_gc(p1, descr=valuedescr) + i1 = int_add(i0, 1) + jump(i1) + """ + expected = """ + [i] + i1 = int_add(i, 1) + jump(i1) + """ + self.optimize_loop(ops, expected) + + def test_virtual_4(self): + ops = """ + [i0, p0] + guard_class(p0, ConstClass(node_vtable)) [] + i1 = getfield_gc(p0, descr=valuedescr) + i2 = int_sub(i1, 1) + i3 = int_add(i0, i1) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i2, descr=valuedescr) + jump(i3, p1) + """ + expected = """ + [i0, i1] + i2 = int_sub(i1, 1) + i3 = int_add(i0, i1) + jump(i3, i2) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)', + expected) + + def test_virtual_5(self): + ops = """ + [i0, p0] + guard_class(p0, ConstClass(node_vtable)) [] + i1 = getfield_gc(p0, descr=valuedescr) + i2 = int_sub(i1, 1) + i3 = int_add(i0, i1) + p2 = new_with_vtable(ConstClass(node_vtable2)) + setfield_gc(p2, i1, descr=valuedescr) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i2, descr=valuedescr) + setfield_gc(p1, p2, descr=nextdescr) + jump(i3, p1) + """ + expected = """ + [i0, i1, i1bis] + i2 = int_sub(i1, 1) + i3 = int_add(i0, i1) + jump(i3, i2, i1) + """ + py.test.skip("XXX") + self.optimize_loop(ops, + '''Not, Virtual(node_vtable, + valuedescr=Not, + nextdescr=Virtual(node_vtable2, + valuedescr=Not))''', + expected) + + def test_virtual_constant_isnull(self): + ops = """ + [i0] + p0 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p0, NULL, descr=nextdescr) + p2 = getfield_gc(p0, descr=nextdescr) + i1 = ptr_eq(p2, NULL) + jump(i1) + """ + expected = """ + [i0] + jump(1) + """ + self.optimize_loop(ops, expected) + + + def test_virtual_constant_isnonnull(self): + ops = """ + [i0] + p0 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p0, ConstPtr(myptr), descr=nextdescr) + p2 = getfield_gc(p0, descr=nextdescr) + i1 = ptr_eq(p2, NULL) + jump(i1) + """ + expected = """ + [i0] + jump(0) + """ + self.optimize_loop(ops, expected) + + def test_nonvirtual_1(self): + ops = """ + [i] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i, descr=valuedescr) + i0 = getfield_gc(p1, descr=valuedescr) + i1 = int_add(i0, 1) + escape(p1) + escape(p1) + jump(i1) + """ + expected = """ + [i] + i1 = int_add(i, 1) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i, descr=valuedescr) + escape(p1) + escape(p1) + jump(i1) + """ + self.optimize_loop(ops, expected) + + def test_nonvirtual_2(self): + ops = """ + [i, p0] + i0 = getfield_gc(p0, descr=valuedescr) + escape(p0) + i1 = int_add(i0, i) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i1, descr=valuedescr) + jump(i, p1) + """ + expected = ops + self.optimize_loop(ops, expected) + + def test_nonvirtual_later(self): + ops = """ + [i] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i, descr=valuedescr) + i1 = getfield_gc(p1, descr=valuedescr) + escape(p1) + i2 = getfield_gc(p1, descr=valuedescr) + i3 = int_add(i1, i2) + jump(i3) + """ + expected = """ + [i] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i, descr=valuedescr) + escape(p1) + i2 = getfield_gc(p1, descr=valuedescr) + i3 = int_add(i, i2) + jump(i3) + """ + self.optimize_loop(ops, expected) + + def test_nonvirtual_dont_write_null_fields_on_force(self): + ops = """ + [i] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i, descr=valuedescr) + i1 = getfield_gc(p1, descr=valuedescr) + setfield_gc(p1, 0, descr=valuedescr) + escape(p1) + i2 = getfield_gc(p1, descr=valuedescr) + jump(i2) + """ + expected = """ + [i] + p1 = new_with_vtable(ConstClass(node_vtable)) + escape(p1) + i2 = getfield_gc(p1, descr=valuedescr) + jump(i2) + """ + self.optimize_loop(ops, expected) + + def test_getfield_gc_pure_1(self): + ops = """ + [i] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i, descr=valuedescr) + i1 = getfield_gc_pure(p1, descr=valuedescr) + jump(i1) + """ + expected = """ + [i] + jump(i) + """ + self.optimize_loop(ops, expected) + + def test_getfield_gc_pure_2(self): + ops = """ + [i] + i1 = getfield_gc_pure(ConstPtr(myptr), descr=valuedescr) + jump(i1) + """ + expected = """ + [i] + jump(5) + """ + self.node.value = 5 + self.optimize_loop(ops, expected) + + def test_getfield_gc_nonpure_2(self): + ops = """ + [i] + i1 = getfield_gc(ConstPtr(myptr), descr=valuedescr) + jump(i1) + """ + expected = ops + self.optimize_loop(ops, expected) + + def test_varray_1(self): + ops = """ + [i1] + p1 = new_array(3, descr=arraydescr) + i3 = arraylen_gc(p1, descr=arraydescr) + guard_value(i3, 3) [] + setarrayitem_gc(p1, 1, i1, descr=arraydescr) + setarrayitem_gc(p1, 0, 25, descr=arraydescr) + i2 = getarrayitem_gc(p1, 1, descr=arraydescr) + jump(i2) + """ + expected = """ + [i1] + jump(i1) + """ + self.optimize_loop(ops, expected) + + def test_varray_alloc_and_set(self): + ops = """ + [i1] + p1 = new_array(2, descr=arraydescr) + setarrayitem_gc(p1, 0, 25, descr=arraydescr) + i2 = getarrayitem_gc(p1, 1, descr=arraydescr) + jump(i2) + """ + expected = """ + [i1] + jump(0) + """ + self.optimize_loop(ops, expected) + + def test_varray_float(self): + ops = """ + [f1] + p1 = new_array(3, descr=floatarraydescr) + i3 = arraylen_gc(p1, descr=floatarraydescr) + guard_value(i3, 3) [] + setarrayitem_gc(p1, 1, f1, descr=floatarraydescr) + setarrayitem_gc(p1, 0, 3.5, descr=floatarraydescr) + f2 = getarrayitem_gc(p1, 1, descr=floatarraydescr) + jump(f2) + """ + expected = """ + [f1] + jump(f1) + """ + self.optimize_loop(ops, expected) + + def test_array_non_optimized(self): + ops = """ + [i1, p0] + setarrayitem_gc(p0, 0, i1, descr=arraydescr) + guard_nonnull(p0) [] + p1 = new_array(i1, descr=arraydescr) + jump(i1, p1) + """ + expected = """ + [i1, p0] + setarrayitem_gc(p0, 0, i1, descr=arraydescr) + p1 = new_array(i1, descr=arraydescr) + jump(i1, p1) + """ + self.optimize_loop(ops, expected) + + def test_nonvirtual_array_dont_write_null_fields_on_force(self): + ops = """ + [i1] + p1 = new_array(5, descr=arraydescr) + setarrayitem_gc(p1, 0, i1, descr=arraydescr) + setarrayitem_gc(p1, 1, 0, descr=arraydescr) + escape(p1) + jump(i1) + """ + expected = """ + [i1] + p1 = new_array(5, descr=arraydescr) + setarrayitem_gc(p1, 0, i1, descr=arraydescr) + escape(p1) + jump(i1) + """ + self.optimize_loop(ops, expected) + + def test_varray_2(self): + ops = """ + [i0, p1] + i1 = getarrayitem_gc(p1, 0, descr=arraydescr) + i2 = getarrayitem_gc(p1, 1, descr=arraydescr) + i3 = int_sub(i1, i2) + guard_value(i3, 15) [] + p2 = new_array(2, descr=arraydescr) + setarrayitem_gc(p2, 1, i0, descr=arraydescr) + setarrayitem_gc(p2, 0, 20, descr=arraydescr) + jump(i0, p2) + """ + expected = """ + [i0, i1, i2] + i3 = int_sub(i1, i2) + guard_value(i3, 15) [] + jump(i0, 20, i0) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Not, VArray(arraydescr, Not, Not)', expected) + + def test_p123_array(self): + ops = """ + [i1, p2, p3] + i3 = getarrayitem_gc(p3, 0, descr=arraydescr) + escape(i3) + p1 = new_array(1, descr=arraydescr) + setarrayitem_gc(p1, 0, i1, descr=arraydescr) + jump(i1, p1, p2) + """ + # We cannot track virtuals that survive for more than two iterations. + self.optimize_loop(ops, ops) + + def test_varray_forced_1(self): + ops = """ + [] + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, 3, descr=valuedescr) + i1 = getfield_gc(p2, descr=valuedescr) # i1 = const 3 + p1 = new_array(i1, descr=arraydescr) + escape(p1) + i2 = arraylen_gc(p1) + escape(i2) + jump() + """ + expected = """ + [] + p1 = new_array(3, descr=arraydescr) + escape(p1) + i2 = arraylen_gc(p1) + escape(i2) + jump() + """ + self.optimize_loop(ops, expected) + + def test_vstruct_1(self): + ops = """ + [i1, p2] + i2 = getfield_gc(p2, descr=adescr) + escape(i2) + p3 = new(descr=ssize) + setfield_gc(p3, i1, descr=adescr) + jump(i1, p3) + """ + expected = """ + [i1, i2] + escape(i2) + jump(i1, i1) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Not, VStruct(ssize, adescr=Not)', expected) + + def test_p123_vstruct(self): + ops = """ + [i1, p2, p3] + i3 = getfield_gc(p3, descr=adescr) + escape(i3) + p1 = new(descr=ssize) + setfield_gc(p1, i1, descr=adescr) + jump(i1, p1, p2) + """ + # We cannot track virtuals that survive for more than two iterations. + self.optimize_loop(ops, ops) + + def test_duplicate_getfield_1(self): + ops = """ + [p1, p2] + i1 = getfield_gc(p1, descr=valuedescr) + i2 = getfield_gc(p2, descr=valuedescr) + i3 = getfield_gc(p1, descr=valuedescr) + i4 = getfield_gc(p2, descr=valuedescr) + escape(i1) + escape(i2) + escape(i3) + escape(i4) + jump(p1, p2) + """ + expected = """ + [p1, p2] + i1 = getfield_gc(p1, descr=valuedescr) + i2 = getfield_gc(p2, descr=valuedescr) + escape(i1) + escape(i2) + escape(i1) + escape(i2) + jump(p1, p2) + """ + self.optimize_loop(ops, expected) + + def test_getfield_after_setfield(self): + ops = """ + [p1, i1] + setfield_gc(p1, i1, descr=valuedescr) + i2 = getfield_gc(p1, descr=valuedescr) + escape(i2) + jump(p1, i1) + """ + expected = """ + [p1, i1] + setfield_gc(p1, i1, descr=valuedescr) + escape(i1) + jump(p1, i1) + """ + self.optimize_loop(ops, expected) + + def test_setfield_of_different_type_does_not_clear(self): + ops = """ + [p1, p2, i1] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p2, p1, descr=nextdescr) + i2 = getfield_gc(p1, descr=valuedescr) + escape(i2) + jump(p1, p2, i1) + """ + expected = """ + [p1, p2, i1] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p2, p1, descr=nextdescr) + escape(i1) + jump(p1, p2, i1) + """ + self.optimize_loop(ops, expected) + + def test_setfield_of_same_type_clears(self): + ops = """ + [p1, p2, i1, i2] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p2, i2, descr=valuedescr) + i3 = getfield_gc(p1, descr=valuedescr) + escape(i3) + jump(p1, p2, i1, i3) + """ + self.optimize_loop(ops, ops) + + def test_duplicate_getfield_mergepoint_has_no_side_effects(self): + ops = """ + [p1] + i1 = getfield_gc(p1, descr=valuedescr) + debug_merge_point(15, 0) + i2 = getfield_gc(p1, descr=valuedescr) + escape(i1) + escape(i2) + jump(p1) + """ + expected = """ + [p1] + i1 = getfield_gc(p1, descr=valuedescr) + debug_merge_point(15, 0) + escape(i1) + escape(i1) + jump(p1) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_getfield_ovf_op_does_not_clear(self): + ops = """ + [p1] + i1 = getfield_gc(p1, descr=valuedescr) + i2 = int_add_ovf(i1, 14) + guard_no_overflow() [] + i3 = getfield_gc(p1, descr=valuedescr) + escape(i2) + escape(i3) + jump(p1) + """ + expected = """ + [p1] + i1 = getfield_gc(p1, descr=valuedescr) + i2 = int_add_ovf(i1, 14) + guard_no_overflow() [] + escape(i2) + escape(i1) + jump(p1) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_getfield_setarrayitem_does_not_clear(self): + ops = """ + [p1, p2] + i1 = getfield_gc(p1, descr=valuedescr) + setarrayitem_gc(p2, 0, p1, descr=arraydescr2) + i3 = getfield_gc(p1, descr=valuedescr) + escape(i1) + escape(i3) + jump(p1, p2) + """ + expected = """ + [p1, p2] + i1 = getfield_gc(p1, descr=valuedescr) + setarrayitem_gc(p2, 0, p1, descr=arraydescr2) + escape(i1) + escape(i1) + jump(p1, p2) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_getfield_constant(self): + ops = """ + [] + i1 = getfield_gc(ConstPtr(myptr), descr=valuedescr) + i2 = getfield_gc(ConstPtr(myptr), descr=valuedescr) + escape(i1) + escape(i2) + jump() + """ + expected = """ + [] + i1 = getfield_gc(ConstPtr(myptr), descr=valuedescr) + escape(i1) + escape(i1) + jump() + """ + self.optimize_loop(ops, expected) + + def test_duplicate_getfield_guard_value_const(self): + ops = """ + [p1] + guard_value(p1, ConstPtr(myptr)) [] + i1 = getfield_gc(p1, descr=valuedescr) + i2 = getfield_gc(ConstPtr(myptr), descr=valuedescr) + escape(i1) + escape(i2) + jump(p1) + """ + expected = """ + [] + i1 = getfield_gc(ConstPtr(myptr), descr=valuedescr) + escape(i1) + escape(i1) + jump() + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Constant(myptr)', expected) + + def test_duplicate_getfield_sideeffects_1(self): + ops = """ + [p1] + i1 = getfield_gc(p1, descr=valuedescr) + escape() + i2 = getfield_gc(p1, descr=valuedescr) + escape(i1) + escape(i2) + jump(p1) + """ + self.optimize_loop(ops, ops) + + def test_duplicate_getfield_sideeffects_2(self): + ops = """ + [p1, i1] + setfield_gc(p1, i1, descr=valuedescr) + escape() + i2 = getfield_gc(p1, descr=valuedescr) + escape(i2) + jump(p1, i1) + """ + self.optimize_loop(ops, ops) + + def test_duplicate_setfield_1(self): + ops = """ + [p1, i1, i2] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2) + """ + expected = """ + [p1, i1, i2] + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_setfield_2(self): + ops = """ + [p1, i1, i3] + setfield_gc(p1, i1, descr=valuedescr) + i2 = getfield_gc(p1, descr=valuedescr) + setfield_gc(p1, i3, descr=valuedescr) + escape(i2) + jump(p1, i1, i3) + """ + expected = """ + [p1, i1, i3] + setfield_gc(p1, i3, descr=valuedescr) + escape(i1) + jump(p1, i1, i3) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_setfield_3(self): + ops = """ + [p1, p2, i1, i3] + setfield_gc(p1, i1, descr=valuedescr) + i2 = getfield_gc(p2, descr=valuedescr) + setfield_gc(p1, i3, descr=valuedescr) + escape(i2) + jump(p1, p2, i1, i3) + """ + # potential aliasing of p1 and p2 means that we cannot kill the + # the setfield_gc + self.optimize_loop(ops, ops) + + def test_duplicate_setfield_4(self): + ops = """ + [p1, i1, i2, p3] + setfield_gc(p1, i1, descr=valuedescr) + # + # some operations on which the above setfield_gc cannot have effect + i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr) + i4 = getarrayitem_gc(p3, i3, descr=arraydescr) + i5 = int_add(i3, i4) + setarrayitem_gc(p3, 0, i5, descr=arraydescr) + setfield_gc(p1, i4, descr=nextdescr) + # + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2, p3) + """ + expected = """ + [p1, i1, i2, p3] + # + i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr) + i4 = getarrayitem_gc(p3, i3, descr=arraydescr) + i5 = int_add(i3, i4) + setarrayitem_gc(p3, 0, i5, descr=arraydescr) + # + setfield_gc(p1, i2, descr=valuedescr) + setfield_gc(p1, i4, descr=nextdescr) + jump(p1, i1, i2, p3) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_setfield_5(self): + ops = """ + [p0, i1] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p0, p1, descr=nextdescr) + setfield_raw(i1, i1, descr=valuedescr) # random op with side-effects + p2 = getfield_gc(p0, descr=nextdescr) + i2 = getfield_gc(p2, descr=valuedescr) + setfield_gc(p0, NULL, descr=nextdescr) + escape(i2) + jump(p0, i1) + """ + expected = """ + [p0, i1] + setfield_raw(i1, i1, descr=valuedescr) + setfield_gc(p0, NULL, descr=nextdescr) + escape(i1) + jump(p0, i1) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_setfield_sideeffects_1(self): + ops = """ + [p1, i1, i2] + setfield_gc(p1, i1, descr=valuedescr) + escape() + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2) + """ + self.optimize_loop(ops, ops) + + def test_duplicate_setfield_residual_guard_1(self): + ops = """ + [p1, i1, i2, i3] + setfield_gc(p1, i1, descr=valuedescr) + guard_true(i3) [] + i4 = int_neg(i2) + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2, i4) + """ + self.optimize_loop(ops, ops) + + def test_duplicate_setfield_residual_guard_2(self): + # the difference with the previous test is that the field value is + # a virtual, which we try hard to keep virtual + ops = """ + [p1, i2, i3] + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, p2, descr=nextdescr) + guard_true(i3) [] + i4 = int_neg(i2) + setfield_gc(p1, NULL, descr=nextdescr) + jump(p1, i2, i4) + """ + expected = """ + [p1, i2, i3] + guard_true(i3) [p1] + i4 = int_neg(i2) + setfield_gc(p1, NULL, descr=nextdescr) + jump(p1, i2, i4) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_setfield_residual_guard_3(self): + ops = """ + [p1, i2, i3] + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, i2, descr=valuedescr) + setfield_gc(p1, p2, descr=nextdescr) + guard_true(i3) [] + i4 = int_neg(i2) + setfield_gc(p1, NULL, descr=nextdescr) + jump(p1, i2, i4) + """ + expected = """ + [p1, i2, i3] + guard_true(i3) [i2, p1] + i4 = int_neg(i2) + setfield_gc(p1, NULL, descr=nextdescr) + jump(p1, i2, i4) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_setfield_residual_guard_4(self): + # test that the setfield_gc does not end up between int_eq and + # the following guard_true + ops = """ + [p1, i1, i2, i3] + setfield_gc(p1, i1, descr=valuedescr) + i5 = int_eq(i3, 5) + guard_true(i5) [] + i4 = int_neg(i2) + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2, i4) + """ + self.optimize_loop(ops, ops) + + def test_duplicate_setfield_aliasing(self): + # a case where aliasing issues (and not enough cleverness) mean + # that we fail to remove any setfield_gc + ops = """ + [p1, p2, i1, i2, i3] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p2, i2, descr=valuedescr) + setfield_gc(p1, i3, descr=valuedescr) + jump(p1, p2, i1, i2, i3) + """ + self.optimize_loop(ops, ops) + + def test_duplicate_setfield_guard_value_const(self): + ops = """ + [p1, i1, i2] + guard_value(p1, ConstPtr(myptr)) [] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(ConstPtr(myptr), i2, descr=valuedescr) + jump(p1, i1, i2) + """ + expected = """ + [i1, i2] + setfield_gc(ConstPtr(myptr), i2, descr=valuedescr) + jump(i1, i2) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Constant(myptr), Not, Not', expected) + + def test_duplicate_getarrayitem_1(self): + ops = """ + [p1] + p2 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p3 = getarrayitem_gc(p1, 1, descr=arraydescr2) + p4 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p5 = getarrayitem_gc(p1, 1, descr=arraydescr2) + escape(p2) + escape(p3) + escape(p4) + escape(p5) + jump(p1) + """ + expected = """ + [p1] + p2 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p3 = getarrayitem_gc(p1, 1, descr=arraydescr2) + escape(p2) + escape(p3) + escape(p2) + escape(p3) + jump(p1) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_getarrayitem_after_setarrayitem_1(self): + ops = """ + [p1, p2] + setarrayitem_gc(p1, 0, p2, descr=arraydescr2) + p3 = getarrayitem_gc(p1, 0, descr=arraydescr2) + escape(p3) + jump(p1, p3) + """ + expected = """ + [p1, p2] + setarrayitem_gc(p1, 0, p2, descr=arraydescr2) + escape(p2) + jump(p1, p2) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_getarrayitem_after_setarrayitem_2(self): + ops = """ + [p1, p2, p3, i1] + setarrayitem_gc(p1, 0, p2, descr=arraydescr2) + setarrayitem_gc(p1, i1, p3, descr=arraydescr2) + p4 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p5 = getarrayitem_gc(p1, i1, descr=arraydescr2) + escape(p4) + escape(p5) + jump(p1, p2, p3, i1) + """ + expected = """ + [p1, p2, p3, i1] + setarrayitem_gc(p1, 0, p2, descr=arraydescr2) + setarrayitem_gc(p1, i1, p3, descr=arraydescr2) + p4 = getarrayitem_gc(p1, 0, descr=arraydescr2) + escape(p4) + escape(p3) + jump(p1, p2, p3, i1) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_getarrayitem_after_setarrayitem_3(self): + ops = """ + [p1, p2, p3, p4, i1] + setarrayitem_gc(p1, i1, p2, descr=arraydescr2) + setarrayitem_gc(p1, 0, p3, descr=arraydescr2) + setarrayitem_gc(p1, 1, p4, descr=arraydescr2) + p5 = getarrayitem_gc(p1, i1, descr=arraydescr2) + p6 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p7 = getarrayitem_gc(p1, 1, descr=arraydescr2) + escape(p5) + escape(p6) + escape(p7) + jump(p1, p2, p3, p4, i1) + """ + expected = """ + [p1, p2, p3, p4, i1] + setarrayitem_gc(p1, i1, p2, descr=arraydescr2) + setarrayitem_gc(p1, 0, p3, descr=arraydescr2) + setarrayitem_gc(p1, 1, p4, descr=arraydescr2) + p5 = getarrayitem_gc(p1, i1, descr=arraydescr2) + escape(p5) + escape(p3) + escape(p4) + jump(p1, p2, p3, p4, i1) + """ + self.optimize_loop(ops, expected) + + def test_getarrayitem_pure_does_not_invalidate(self): + ops = """ + [p1, p2] + p3 = getarrayitem_gc(p1, 0, descr=arraydescr2) + i4 = getfield_gc_pure(ConstPtr(myptr), descr=valuedescr) + p5 = getarrayitem_gc(p1, 0, descr=arraydescr2) + escape(p3) + escape(i4) + escape(p5) + jump(p1, p2) + """ + expected = """ + [p1, p2] + p3 = getarrayitem_gc(p1, 0, descr=arraydescr2) + escape(p3) + escape(5) + escape(p3) + jump(p1, p2) + """ + self.optimize_loop(ops, expected) + + def test_duplicate_getarrayitem_after_setarrayitem_two_arrays(self): + ops = """ + [p1, p2, p3, p4, i1] + setarrayitem_gc(p1, 0, p3, descr=arraydescr2) + setarrayitem_gc(p2, 1, p4, descr=arraydescr2) + p5 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p6 = getarrayitem_gc(p2, 1, descr=arraydescr2) + escape(p5) + escape(p6) + jump(p1, p2, p3, p4, i1) + """ + expected = """ + [p1, p2, p3, p4, i1] + setarrayitem_gc(p1, 0, p3, descr=arraydescr2) + setarrayitem_gc(p2, 1, p4, descr=arraydescr2) + escape(p3) + escape(p4) + jump(p1, p2, p3, p4, i1) + """ + self.optimize_loop(ops, expected) + + def test_bug_1(self): + ops = """ + [i0, p1] + p4 = getfield_gc(p1, descr=nextdescr) + guard_nonnull(p4) [] + escape(p4) + # + p2 = new_with_vtable(ConstClass(node_vtable)) + p3 = escape() + setfield_gc(p2, p3, descr=nextdescr) + jump(i0, p2) + """ + expected = """ + [i0, p4] + guard_nonnull(p4) [] + escape(p4) + # + p3 = escape() + jump(i0, p3) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Not, Virtual(node_vtable, nextdescr=Not)', + expected) + + def test_bug_2(self): + ops = """ + [i0, p1] + p4 = getarrayitem_gc(p1, 0, descr=arraydescr2) + guard_nonnull(p4) [] + escape(p4) + # + p2 = new_array(1, descr=arraydescr2) + p3 = escape() + setarrayitem_gc(p2, 0, p3, descr=arraydescr2) + jump(i0, p2) + """ + expected = """ + [i0, p4] + guard_nonnull(p4) [] + escape(p4) + # + p3 = escape() + jump(i0, p3) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Not, VArray(arraydescr2, Not)', + expected) + + def test_bug_3(self): + ops = """ + [p1] + guard_nonnull(p1) [] + guard_class(p1, ConstClass(node_vtable2)) [] + p2 = getfield_gc(p1, descr=nextdescr) + guard_nonnull(12) [] + guard_class(p2, ConstClass(node_vtable)) [] + p3 = getfield_gc(p1, descr=otherdescr) + guard_nonnull(12) [] + guard_class(p3, ConstClass(node_vtable)) [] + setfield_gc(p3, p2, descr=otherdescr) + p1a = new_with_vtable(ConstClass(node_vtable2)) + p2a = new_with_vtable(ConstClass(node_vtable)) + p3a = new_with_vtable(ConstClass(node_vtable)) + escape(p3a) + setfield_gc(p1a, p2a, descr=nextdescr) + setfield_gc(p1a, p3a, descr=otherdescr) + jump(p1a) + """ + expected = """ + [p2, p3] + guard_class(p2, ConstClass(node_vtable)) [] + guard_class(p3, ConstClass(node_vtable)) [] + setfield_gc(p3, p2, descr=otherdescr) + p3a = new_with_vtable(ConstClass(node_vtable)) + escape(p3a) + p2a = new_with_vtable(ConstClass(node_vtable)) + jump(p2a, p3a) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Virtual(node_vtable2, nextdescr=Not, otherdescr=Not)', expected) + + def test_bug_3bis(self): + ops = """ + [p1] + guard_nonnull(p1) [] + guard_class(p1, ConstClass(node_vtable2)) [] + p2 = getfield_gc(p1, descr=nextdescr) + guard_nonnull(12) [] + guard_class(p2, ConstClass(node_vtable)) [] + p3 = getfield_gc(p1, descr=otherdescr) + guard_nonnull(12) [] + guard_class(p3, ConstClass(node_vtable)) [] + p1a = new_with_vtable(ConstClass(node_vtable2)) + p2a = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3, p2a, descr=otherdescr) + p3a = new_with_vtable(ConstClass(node_vtable)) + escape(p3a) + setfield_gc(p1a, p2a, descr=nextdescr) + setfield_gc(p1a, p3a, descr=otherdescr) + jump(p1a) + """ + expected = """ + [p2, p3] + guard_class(p2, ConstClass(node_vtable)) [] + guard_class(p3, ConstClass(node_vtable)) [] + p2a = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3, p2a, descr=otherdescr) + p3a = new_with_vtable(ConstClass(node_vtable)) + escape(p3a) + jump(p2a, p3a) + """ + py.test.skip("XXX") + self.optimize_loop(ops, 'Virtual(node_vtable2, nextdescr=Not, otherdescr=Not)', expected) + + def test_invalid_loop_1(self): + ops = """ + [p1] + guard_isnull(p1) [] + # + p2 = new_with_vtable(ConstClass(node_vtable)) + jump(p2) + """ + py.test.skip("XXX") + py.test.raises(InvalidLoop, self.optimize_loop, + ops, 'Virtual(node_vtable)', None) + + def test_invalid_loop_2(self): + py.test.skip("this would fail if we had Fixed again in the specnodes") + ops = """ + [p1] + guard_class(p1, ConstClass(node_vtable2)) [] + # + p2 = new_with_vtable(ConstClass(node_vtable)) + escape(p2) # prevent it from staying Virtual + jump(p2) + """ + py.test.raises(InvalidLoop, self.optimize_loop, + ops, '...', None) + + def test_invalid_loop_3(self): + ops = """ + [p1] + p2 = getfield_gc(p1, descr=nextdescr) + guard_isnull(p2) [] + # + p3 = new_with_vtable(ConstClass(node_vtable)) + p4 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3, p4, descr=nextdescr) + jump(p3) + """ + py.test.skip("XXX") + py.test.raises(InvalidLoop, self.optimize_loop, ops, + 'Virtual(node_vtable, nextdescr=Virtual(node_vtable))', + None) + + def test_merge_guard_class_guard_value(self): + ops = """ + [p1, i0, i1, i2, p2] + guard_class(p1, ConstClass(node_vtable)) [i0] + i3 = int_add(i1, i2) + guard_value(p1, ConstPtr(myptr)) [i1] + jump(p2, i0, i1, i3, p2) + """ + expected = """ + [p1, i0, i1, i2, p2] + guard_value(p1, ConstPtr(myptr)) [i0] + i3 = int_add(i1, i2) + jump(p2, i0, i1, i3, p2) + """ + self.optimize_loop(ops, expected) + + def test_merge_guard_nonnull_guard_class(self): + self.make_fail_descr() + ops = """ + [p1, i0, i1, i2, p2] + guard_nonnull(p1, descr=fdescr) [i0] + i3 = int_add(i1, i2) + guard_class(p1, ConstClass(node_vtable)) [i1] + jump(p2, i0, i1, i3, p2) + """ + expected = """ + [p1, i0, i1, i2, p2] + guard_nonnull_class(p1, ConstClass(node_vtable), descr=fdescr) [i0] + i3 = int_add(i1, i2) + jump(p2, i0, i1, i3, p2) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr("i0", rop.GUARD_NONNULL_CLASS) + + def test_merge_guard_nonnull_guard_value(self): + self.make_fail_descr() + ops = """ + [p1, i0, i1, i2, p2] + guard_nonnull(p1, descr=fdescr) [i0] + i3 = int_add(i1, i2) + guard_value(p1, ConstPtr(myptr)) [i1] + jump(p2, i0, i1, i3, p2) + """ + expected = """ + [p1, i0, i1, i2, p2] + guard_value(p1, ConstPtr(myptr), descr=fdescr) [i0] + i3 = int_add(i1, i2) + jump(p2, i0, i1, i3, p2) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr("i0", rop.GUARD_VALUE) + + def test_merge_guard_nonnull_guard_class_guard_value(self): + self.make_fail_descr() + ops = """ + [p1, i0, i1, i2, p2] + guard_nonnull(p1, descr=fdescr) [i0] + i3 = int_add(i1, i2) + guard_class(p1, ConstClass(node_vtable)) [i2] + i4 = int_sub(i3, 1) + guard_value(p1, ConstPtr(myptr)) [i1] + jump(p2, i0, i1, i4, p2) + """ + expected = """ + [p1, i0, i1, i2, p2] + guard_value(p1, ConstPtr(myptr), descr=fdescr) [i0] + i3 = int_add(i1, i2) + i4 = int_sub(i3, 1) + jump(p2, i0, i1, i4, p2) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr("i0", rop.GUARD_VALUE) + + def test_guard_class_oois(self): + ops = """ + [p1] + guard_class(p1, ConstClass(node_vtable2)) [] + i = ptr_ne(ConstPtr(myptr), p1) + guard_true(i) [] + jump(p1) + """ + expected = """ + [p1] + guard_class(p1, ConstClass(node_vtable2)) [] + jump(p1) + """ + self.optimize_loop(ops, expected) + + def test_oois_of_itself(self): + ops = """ + [p0] + p1 = getfield_gc(p0, descr=nextdescr) + p2 = getfield_gc(p0, descr=nextdescr) + i1 = ptr_eq(p1, p2) + guard_true(i1) [] + i2 = ptr_ne(p1, p2) + guard_false(i2) [] + jump(p0) + """ + expected = """ + [p0] + p1 = getfield_gc(p0, descr=nextdescr) + jump(p0) + """ + self.optimize_loop(ops, expected) + + def test_remove_duplicate_pure_op(self): + ops = """ + [p1, p2] + i1 = ptr_eq(p1, p2) + i2 = ptr_eq(p1, p2) + i3 = int_add(i1, 1) + i3b = int_is_true(i3) + guard_true(i3b) [] + i4 = int_add(i2, 1) + i4b = int_is_true(i4) + guard_true(i4b) [] + escape(i3) + escape(i4) + guard_true(i1) [] + guard_true(i2) [] + jump(p1, p2) + """ + expected = """ + [p1, p2] + i1 = ptr_eq(p1, p2) + i3 = int_add(i1, 1) + i3b = int_is_true(i3) + guard_true(i3b) [] + escape(i3) + escape(i3) + guard_true(i1) [] + jump(p1, p2) + """ + self.optimize_loop(ops, expected) + + def test_remove_duplicate_pure_op_with_descr(self): + ops = """ + [p1] + i0 = arraylen_gc(p1, descr=arraydescr) + i1 = int_gt(i0, 0) + guard_true(i1) [] + i2 = arraylen_gc(p1, descr=arraydescr) + i3 = int_gt(i0, 0) + guard_true(i3) [] + jump(p1) + """ + expected = """ + [p1] + i0 = arraylen_gc(p1, descr=arraydescr) + i1 = int_gt(i0, 0) + guard_true(i1) [] + jump(p1) + """ + self.optimize_loop(ops, expected) + + def test_remove_duplicate_pure_op_ovf(self): + ops = """ + [i1] + i3 = int_add_ovf(i1, 1) + guard_no_overflow() [] + i3b = int_is_true(i3) + guard_true(i3b) [] + i4 = int_add_ovf(i1, 1) + guard_no_overflow() [] + i4b = int_is_true(i4) + guard_true(i4b) [] + escape(i3) + escape(i4) + jump(i1) + """ + expected = """ + [i1] + i3 = int_add_ovf(i1, 1) + guard_no_overflow() [] + i3b = int_is_true(i3) + guard_true(i3b) [] + escape(i3) + escape(i3) + jump(i1) + """ + self.optimize_loop(ops, expected) + + def test_int_and_or_with_zero(self): + ops = """ + [i0, i1] + i2 = int_and(i0, 0) + i3 = int_and(0, i2) + i4 = int_or(i2, i1) + i5 = int_or(i0, i3) + jump(i4, i5) + """ + expected = """ + [i0, i1] + jump(i1, i0) + """ + self.optimize_loop(ops, expected) + + def test_fold_partially_constant_ops(self): + ops = """ + [i0] + i1 = int_sub(i0, 0) + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + ops = """ + [i0] + i1 = int_add(i0, 0) + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + ops = """ + [i0] + i1 = int_add(0, i0) + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_fold_partially_constant_ops_ovf(self): + ops = """ + [i0] + i1 = int_sub_ovf(i0, 0) + guard_no_overflow() [] + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + ops = """ + [i0] + i1 = int_add_ovf(i0, 0) + guard_no_overflow() [] + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + ops = """ + [i0] + i1 = int_add_ovf(0, i0) + guard_no_overflow() [] + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected) + + # ---------- + + def make_fail_descr(self): + class FailDescr(compile.ResumeGuardDescr): + oparse = None + def _oparser_uses_descr_of_guard(self, oparse, fail_args): + # typically called twice, before and after optimization + if self.oparse is None: + fdescr.rd_frame_info_list = resume.FrameInfo(None, + "code", 11) + fdescr.rd_snapshot = resume.Snapshot(None, fail_args) + self.oparse = oparse + # + fdescr = instantiate(FailDescr) + self.namespace['fdescr'] = fdescr + + def teardown_method(self, meth): + self.namespace.pop('fdescr', None) + + def _verify_fail_args(self, boxes, oparse, text): + import re + r = re.compile(r"\bwhere\s+(\w+)\s+is a\s+(\w+)") + parts = list(r.finditer(text)) + ends = [match.start() for match in parts] + [len(text)] + # + virtuals = {} + for match, end in zip(parts, ends[1:]): + pvar = match.group(1) + fieldstext = text[match.end():end] + if match.group(2) == 'varray': + arrayname, fieldstext = fieldstext.split(':', 1) + tag = ('varray', self.namespace[arrayname.strip()]) + elif match.group(2) == 'vstruct': + if ',' in fieldstext: + structname, fieldstext = fieldstext.split(',', 1) + else: + structname, fieldstext = fieldstext, '' + tag = ('vstruct', self.namespace[structname.strip()]) + else: + tag = ('virtual', self.namespace[match.group(2)]) + virtuals[pvar] = (tag, None, fieldstext) + # + r2 = re.compile(r"([\w\d()]+)[.](\w+)\s*=\s*([\w\d()]+)") + pendingfields = [] + for match in r2.finditer(text): + pvar = match.group(1) + pfieldname = match.group(2) + pfieldvar = match.group(3) + pendingfields.append((pvar, pfieldname, pfieldvar)) + # + def _variables_equal(box, varname, strict): + if varname not in virtuals: + if strict: + assert box == oparse.getvar(varname) + else: + assert box.value == oparse.getvar(varname).value + else: + tag, resolved, fieldstext = virtuals[varname] + if tag[0] == 'virtual': + assert self.get_class_of_box(box) == tag[1] + elif tag[0] == 'varray': + pass # xxx check arraydescr + elif tag[0] == 'vstruct': + pass # xxx check typedescr + else: + assert 0 + if resolved is not None: + assert resolved.value == box.value + else: + virtuals[varname] = tag, box, fieldstext + # + basetext = text.splitlines()[0] + varnames = [s.strip() for s in basetext.split(',')] + if varnames == ['']: + varnames = [] + assert len(boxes) == len(varnames) + for box, varname in zip(boxes, varnames): + _variables_equal(box, varname, strict=True) + for pvar, pfieldname, pfieldvar in pendingfields: + box = oparse.getvar(pvar) + fielddescr = self.namespace[pfieldname.strip()] + fieldbox = executor.execute(self.cpu, None, + rop.GETFIELD_GC, + fielddescr, + box) + _variables_equal(fieldbox, pfieldvar, strict=True) + # + for match in parts: + pvar = match.group(1) + tag, resolved, fieldstext = virtuals[pvar] + assert resolved is not None + index = 0 + for fieldtext in fieldstext.split(','): + fieldtext = fieldtext.strip() + if not fieldtext: + continue + if tag[0] in ('virtual', 'vstruct'): + fieldname, fieldvalue = fieldtext.split('=') + fielddescr = self.namespace[fieldname.strip()] + fieldbox = executor.execute(self.cpu, None, + rop.GETFIELD_GC, + fielddescr, + resolved) + elif tag[0] == 'varray': + fieldvalue = fieldtext + fieldbox = executor.execute(self.cpu, None, + rop.GETARRAYITEM_GC, + tag[1], + resolved, ConstInt(index)) + else: + assert 0 + _variables_equal(fieldbox, fieldvalue.strip(), strict=False) + index += 1 + + def check_expanded_fail_descr(self, expectedtext, guard_opnum): + from pypy.jit.metainterp.test.test_resume import ResumeDataFakeReader + from pypy.jit.metainterp.test.test_resume import MyMetaInterp + guard_op, = [op for op in self.loop.operations if op.is_guard()] + fail_args = guard_op.getfailargs() + fdescr = guard_op.getdescr() + assert fdescr.guard_opnum == guard_opnum + reader = ResumeDataFakeReader(fdescr, fail_args, + MyMetaInterp(self.cpu)) + boxes = reader.consume_boxes() + self._verify_fail_args(boxes, fdescr.oparse, expectedtext) + + def test_expand_fail_1(self): + self.make_fail_descr() + ops = """ + [i1, i3] + # first rename i3 into i4 + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i3, descr=valuedescr) + i4 = getfield_gc(p1, descr=valuedescr) + # + i2 = int_add(10, 5) + guard_true(i1, descr=fdescr) [i2, i4] + jump(i1, i4) + """ + expected = """ + [i1, i3] + guard_true(i1, descr=fdescr) [i3] + jump(1, i3) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr('15, i3', rop.GUARD_TRUE) + + def test_expand_fail_2(self): + self.make_fail_descr() + ops = """ + [i1, i2] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i2, descr=valuedescr) + setfield_gc(p1, p1, descr=nextdescr) + guard_true(i1, descr=fdescr) [p1] + jump(i1, i2) + """ + expected = """ + [i1, i2] + guard_true(i1, descr=fdescr) [i2] + jump(1, i2) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr('''ptr + where ptr is a node_vtable, valuedescr=i2 + ''', rop.GUARD_TRUE) + + def test_expand_fail_3(self): + self.make_fail_descr() + ops = """ + [i1, i2, i3, p3] + p1 = new_with_vtable(ConstClass(node_vtable)) + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, 1, descr=valuedescr) + setfield_gc(p1, p2, descr=nextdescr) + setfield_gc(p2, i2, descr=valuedescr) + setfield_gc(p2, p3, descr=nextdescr) + guard_true(i1, descr=fdescr) [i3, p1] + jump(i2, i1, i3, p3) + """ + expected = """ + [i1, i2, i3, p3] + guard_true(i1, descr=fdescr) [i3, i2, p3] + jump(i2, 1, i3, p3) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr('''i3, p1 + where p1 is a node_vtable, valuedescr=1, nextdescr=p2 + where p2 is a node_vtable, valuedescr=i2, nextdescr=p3 + ''', rop.GUARD_TRUE) + + def test_expand_fail_4(self): + for arg in ['p1', 'i2,p1', 'p1,p2', 'p2,p1', + 'i2,p1,p2', 'i2,p2,p1']: + self.make_fail_descr() + ops = """ + [i1, i2, i3] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i3, descr=valuedescr) + i4 = getfield_gc(p1, descr=valuedescr) # copy of i3 + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i2, descr=valuedescr) + setfield_gc(p1, p2, descr=nextdescr) + setfield_gc(p2, i2, descr=valuedescr) + guard_true(i1, descr=fdescr) [i4, i3, %s] + jump(i1, i2, i3) + """ + expected = """ + [i1, i2, i3] + guard_true(i1, descr=fdescr) [i3, i2] + jump(1, i2, i3) + """ + self.optimize_loop(ops % arg, expected) + self.check_expanded_fail_descr('''i3, i3, %s + where p1 is a node_vtable, valuedescr=i2, nextdescr=p2 + where p2 is a node_vtable, valuedescr=i2''' % arg, + rop.GUARD_TRUE) + + def test_expand_fail_5(self): + self.make_fail_descr() + ops = """ + [i1, i2, i3, i4] + p1 = new_with_vtable(ConstClass(node_vtable)) + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i4, descr=valuedescr) + setfield_gc(p1, p2, descr=nextdescr) + setfield_gc(p2, i2, descr=valuedescr) + setfield_gc(p2, p1, descr=nextdescr) # a cycle + guard_true(i1, descr=fdescr) [i3, i4, p1, p2] + jump(i2, i1, i3, i4) + """ + expected = """ + [i1, i2, i3, i4] + guard_true(i1, descr=fdescr) [i3, i4, i2] + jump(i2, 1, i3, i4) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr('''i3, i4, p1, p2 + where p1 is a node_vtable, valuedescr=i4, nextdescr=p2 + where p2 is a node_vtable, valuedescr=i2, nextdescr=p1 + ''', rop.GUARD_TRUE) + + def test_expand_fail_6(self): + self.make_fail_descr() + ops = """ + [p0, i0, i1] + guard_true(i0, descr=fdescr) [p0] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i1, descr=valuedescr) + jump(p1, i1, i1) + """ + expected = """ + [i1b, i0, i1] + guard_true(i0, descr=fdescr) [i1b] + jump(i1, i1, i1) + """ + py.test.skip("XXX") + self.optimize_loop(ops, '''Virtual(node_vtable, valuedescr=Not), + Not, Not''', expected) + self.check_expanded_fail_descr('''p0 + where p0 is a node_vtable, valuedescr=i1b + ''', rop.GUARD_TRUE) + + def test_expand_fail_varray(self): + self.make_fail_descr() + ops = """ + [i1] + p1 = new_array(3, descr=arraydescr) + setarrayitem_gc(p1, 1, i1, descr=arraydescr) + setarrayitem_gc(p1, 0, 25, descr=arraydescr) + guard_true(i1, descr=fdescr) [p1] + i2 = getarrayitem_gc(p1, 1, descr=arraydescr) + jump(i2) + """ + expected = """ + [i1] + guard_true(i1, descr=fdescr) [i1] + jump(1) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr('''p1 + where p1 is a varray arraydescr: 25, i1 + ''', rop.GUARD_TRUE) + + def test_expand_fail_vstruct(self): + self.make_fail_descr() + ops = """ + [i1, p1] + p2 = new(descr=ssize) + setfield_gc(p2, i1, descr=adescr) + setfield_gc(p2, p1, descr=bdescr) + guard_true(i1, descr=fdescr) [p2] + i3 = getfield_gc(p2, descr=adescr) + p3 = getfield_gc(p2, descr=bdescr) + jump(i3, p3) + """ + expected = """ + [i1, p1] + guard_true(i1, descr=fdescr) [i1, p1] + jump(1, p1) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr('''p2 + where p2 is a vstruct ssize, adescr=i1, bdescr=p1 + ''', rop.GUARD_TRUE) + + def test_expand_fail_v_all_1(self): + self.make_fail_descr() + ops = """ + [i1, p1a, i2] + p6s = getarrayitem_gc(p1a, 0, descr=arraydescr2) + p7v = getfield_gc(p6s, descr=bdescr) + p5s = new(descr=ssize) + setfield_gc(p5s, i2, descr=adescr) + setfield_gc(p5s, p7v, descr=bdescr) + setarrayitem_gc(p1a, 1, p5s, descr=arraydescr2) + guard_true(i1, descr=fdescr) [p1a] + p2s = new(descr=ssize) + p3v = new_with_vtable(ConstClass(node_vtable)) + p4a = new_array(2, descr=arraydescr2) + setfield_gc(p2s, i1, descr=adescr) + setfield_gc(p2s, p3v, descr=bdescr) + setfield_gc(p3v, i2, descr=valuedescr) + setarrayitem_gc(p4a, 0, p2s, descr=arraydescr2) + jump(i1, p4a, i2) + """ + expected = """ + [i1, ia, iv, pnull, i2] + guard_true(i1, descr=fdescr) [ia, iv, i2] + jump(1, 1, i2, NULL, i2) + """ + py.test.skip("XXX") + self.optimize_loop(ops, ''' + Not, + VArray(arraydescr2, + VStruct(ssize, + adescr=Not, + bdescr=Virtual(node_vtable, + valuedescr=Not)), + Not), + Not''', expected) + self.check_expanded_fail_descr('''p1a + where p1a is a varray arraydescr2: p6s, p5s + where p6s is a vstruct ssize, adescr=ia, bdescr=p7v + where p5s is a vstruct ssize, adescr=i2, bdescr=p7v + where p7v is a node_vtable, valuedescr=iv + ''', rop.GUARD_TRUE) + + def test_expand_fail_lazy_setfield_1(self): + self.make_fail_descr() + ops = """ + [p1, i2, i3] + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, i2, descr=valuedescr) + setfield_gc(p1, p2, descr=nextdescr) + guard_true(i3, descr=fdescr) [] + i4 = int_neg(i2) + setfield_gc(p1, NULL, descr=nextdescr) + jump(p1, i2, i4) + """ + expected = """ + [p1, i2, i3] + guard_true(i3, descr=fdescr) [p1, i2] + i4 = int_neg(i2) + setfield_gc(p1, NULL, descr=nextdescr) + jump(p1, i2, i4) + """ + self.optimize_loop(ops, expected) + self.loop.inputargs[0].value = self.nodebox.value + self.check_expanded_fail_descr(''' + p1.nextdescr = p2 + where p2 is a node_vtable, valuedescr=i2 + ''', rop.GUARD_TRUE) + + def test_expand_fail_lazy_setfield_2(self): + self.make_fail_descr() + ops = """ + [i2, i3] + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, i2, descr=valuedescr) + setfield_gc(ConstPtr(myptr), p2, descr=nextdescr) + guard_true(i3, descr=fdescr) [] + i4 = int_neg(i2) + setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr) + jump(i2, i4) + """ + expected = """ + [i2, i3] + guard_true(i3, descr=fdescr) [i2] + i4 = int_neg(i2) + setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr) + jump(i2, i4) + """ + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr(''' + ConstPtr(myptr).nextdescr = p2 + where p2 is a node_vtable, valuedescr=i2 + ''', rop.GUARD_TRUE) + + +class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin): + + def test_residual_call_does_not_invalidate_caches(self): + ops = """ + [p1, p2] + i1 = getfield_gc(p1, descr=valuedescr) + i2 = call(i1, descr=nonwritedescr) + i3 = getfield_gc(p1, descr=valuedescr) + escape(i1) + escape(i3) + jump(p1, p2) + """ + expected = """ + [p1, p2] + i1 = getfield_gc(p1, descr=valuedescr) + i2 = call(i1, descr=nonwritedescr) + escape(i1) + escape(i1) + jump(p1, p2) + """ + self.optimize_loop(ops, expected) + + def test_residual_call_invalidate_some_caches(self): + ops = """ + [p1, p2] + i1 = getfield_gc(p1, descr=adescr) + i2 = getfield_gc(p1, descr=bdescr) + i3 = call(i1, descr=writeadescr) + i4 = getfield_gc(p1, descr=adescr) + i5 = getfield_gc(p1, descr=bdescr) + escape(i1) + escape(i2) + escape(i4) + escape(i5) + jump(p1, p2) + """ + expected = """ + [p1, p2] + i1 = getfield_gc(p1, descr=adescr) + i2 = getfield_gc(p1, descr=bdescr) + i3 = call(i1, descr=writeadescr) + i4 = getfield_gc(p1, descr=adescr) + escape(i1) + escape(i2) + escape(i4) + escape(i2) + jump(p1, p2) + """ + self.optimize_loop(ops, expected) + + def test_residual_call_invalidate_arrays(self): + ops = """ + [p1, p2, i1] + p3 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p4 = getarrayitem_gc(p2, 1, descr=arraydescr2) + i3 = call(i1, descr=writeadescr) + p5 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p6 = getarrayitem_gc(p2, 1, descr=arraydescr2) + escape(p3) + escape(p4) + escape(p5) + escape(p6) + jump(p1, p2, i1) + """ + expected = """ + [p1, p2, i1] + p3 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p4 = getarrayitem_gc(p2, 1, descr=arraydescr2) + i3 = call(i1, descr=writeadescr) + escape(p3) + escape(p4) + escape(p3) + escape(p4) + jump(p1, p2, i1) + """ + self.optimize_loop(ops, expected) + + def test_residual_call_invalidate_some_arrays(self): + ops = """ + [p1, p2, i1] + p3 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p4 = getarrayitem_gc(p2, 1, descr=arraydescr2) + i2 = getarrayitem_gc(p1, 1, descr=arraydescr) + i3 = call(i1, descr=writearraydescr) + p5 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p6 = getarrayitem_gc(p2, 1, descr=arraydescr2) + i4 = getarrayitem_gc(p1, 1, descr=arraydescr) + escape(p3) + escape(p4) + escape(p5) + escape(p6) + escape(i2) + escape(i4) + jump(p1, p2, i1) + """ + expected = """ + [p1, p2, i1] + p3 = getarrayitem_gc(p1, 0, descr=arraydescr2) + p4 = getarrayitem_gc(p2, 1, descr=arraydescr2) + i2 = getarrayitem_gc(p1, 1, descr=arraydescr) + i3 = call(i1, descr=writearraydescr) + i4 = getarrayitem_gc(p1, 1, descr=arraydescr) + escape(p3) + escape(p4) + escape(p3) + escape(p4) + escape(i2) + escape(i4) + jump(p1, p2, i1) + """ + self.optimize_loop(ops, expected) + + def test_residual_call_invalidates_some_read_caches_1(self): + ops = """ + [p1, i1, p2, i2] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p2, i2, descr=adescr) + i3 = call(i1, descr=readadescr) + setfield_gc(p1, i3, descr=valuedescr) + setfield_gc(p2, i3, descr=adescr) + jump(p1, i1, p2, i2) + """ + expected = """ + [p1, i1, p2, i2] + setfield_gc(p2, i2, descr=adescr) + i3 = call(i1, descr=readadescr) + setfield_gc(p1, i3, descr=valuedescr) + setfield_gc(p2, i3, descr=adescr) + jump(p1, i1, p2, i2) + """ + self.optimize_loop(ops, expected) + + def test_residual_call_invalidates_some_read_caches_2(self): + ops = """ + [p1, i1, p2, i2] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p2, i2, descr=adescr) + i3 = call(i1, descr=writeadescr) + setfield_gc(p1, i3, descr=valuedescr) + setfield_gc(p2, i3, descr=adescr) + jump(p1, i1, p2, i2) + """ + expected = """ + [p1, i1, p2, i2] + setfield_gc(p2, i2, descr=adescr) + i3 = call(i1, descr=writeadescr) + setfield_gc(p1, i3, descr=valuedescr) + setfield_gc(p2, i3, descr=adescr) + jump(p1, i1, p2, i2) + """ + self.optimize_loop(ops, expected) + + def test_residual_call_invalidates_some_read_caches_3(self): + ops = """ + [p1, i1, p2, i2] + setfield_gc(p1, i1, descr=valuedescr) + setfield_gc(p2, i2, descr=adescr) + i3 = call(i1, descr=plaincalldescr) + setfield_gc(p1, i3, descr=valuedescr) + setfield_gc(p2, i3, descr=adescr) + jump(p1, i1, p2, i2) + """ + self.optimize_loop(ops, ops) + + def test_call_assembler_invalidates_caches(self): + ops = ''' + [p1, i1] + setfield_gc(p1, i1, descr=valuedescr) + i3 = call_assembler(i1, descr=asmdescr) + setfield_gc(p1, i3, descr=valuedescr) + jump(p1, i3) + ''' + self.optimize_loop(ops, ops) + + def test_call_pure_invalidates_caches(self): + # CALL_PURE should still force the setfield_gc() to occur before it + ops = ''' + [p1, i1] + setfield_gc(p1, i1, descr=valuedescr) + i3 = call_pure(42, p1, descr=plaincalldescr) + setfield_gc(p1, i3, descr=valuedescr) + jump(p1, i3) + ''' + expected = ''' + [p1, i1] + setfield_gc(p1, i1, descr=valuedescr) + i3 = call(p1, descr=plaincalldescr) + setfield_gc(p1, i3, descr=valuedescr) + jump(p1, i3) + ''' + self.optimize_loop(ops, expected) + + def test_call_pure_constant_folding(self): + # CALL_PURE is not marked as is_always_pure(), because it is wrong + # to call the function arbitrary many times at arbitrary points in + # time. Check that it is either constant-folded (and replaced by + # the result of the call, recorded as the first arg), or turned into + # a regular CALL. + ops = ''' + [i0, i1, i2] + escape(i1) + escape(i2) + i3 = call_pure(42, 123456, 4, 5, 6, descr=plaincalldescr) + i4 = call_pure(43, 123456, 4, i0, 6, descr=plaincalldescr) + jump(i0, i3, i4) + ''' + expected = ''' + [i0, i1, i2] + escape(i1) + escape(i2) + i4 = call(123456, 4, i0, 6, descr=plaincalldescr) + jump(i0, 42, i4) + ''' + self.optimize_loop(ops, expected) + + def test_vref_nonvirtual_nonescape(self): + ops = """ + [p1] + p2 = virtual_ref(p1, 5) + virtual_ref_finish(p2, p1) + jump(p1) + """ + expected = """ + [p1] + i0 = force_token() + jump(p1) + """ + self.optimize_loop(ops, expected) + + def test_vref_nonvirtual_escape(self): + ops = """ + [p1] + p2 = virtual_ref(p1, 5) + escape(p2) + virtual_ref_finish(p2, p1) + jump(p1) + """ + expected = """ + [p1] + i0 = force_token() + p2 = new_with_vtable(ConstClass(jit_virtual_ref_vtable)) + setfield_gc(p2, i0, descr=virtualtokendescr) + setfield_gc(p2, 5, descr=virtualrefindexdescr) + escape(p2) + setfield_gc(p2, p1, descr=virtualforceddescr) + setfield_gc(p2, -3, descr=virtualtokendescr) + jump(p1) + """ + # XXX we should optimize a bit more the case of a nonvirtual. + # in theory it is enough to just do 'p2 = p1'. + self.optimize_loop(ops, expected) + + def test_vref_virtual_1(self): + ops = """ + [p0, i1] + # + p1 = new_with_vtable(ConstClass(node_vtable)) + p1b = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1b, 252, descr=valuedescr) + setfield_gc(p1, p1b, descr=nextdescr) + # + p2 = virtual_ref(p1, 3) + setfield_gc(p0, p2, descr=nextdescr) + call_may_force(i1, descr=mayforcevirtdescr) + guard_not_forced() [i1] + virtual_ref_finish(p2, p1) + setfield_gc(p0, NULL, descr=nextdescr) + jump(p0, i1) + """ + expected = """ + [p0, i1] + i3 = force_token() + # + p2 = new_with_vtable(ConstClass(jit_virtual_ref_vtable)) + setfield_gc(p2, i3, descr=virtualtokendescr) + setfield_gc(p2, 3, descr=virtualrefindexdescr) + setfield_gc(p0, p2, descr=nextdescr) + # + call_may_force(i1, descr=mayforcevirtdescr) + guard_not_forced() [i1] + # + setfield_gc(p0, NULL, descr=nextdescr) + p1 = new_with_vtable(ConstClass(node_vtable)) + p1b = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1b, 252, descr=valuedescr) + setfield_gc(p1, p1b, descr=nextdescr) + setfield_gc(p2, p1, descr=virtualforceddescr) + setfield_gc(p2, -3, descr=virtualtokendescr) + jump(p0, i1) + """ + self.optimize_loop(ops, expected) + + def test_vref_virtual_2(self): + self.make_fail_descr() + ops = """ + [p0, i1] + # + p1 = new_with_vtable(ConstClass(node_vtable)) + p1b = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1b, i1, descr=valuedescr) + setfield_gc(p1, p1b, descr=nextdescr) + # + p2 = virtual_ref(p1, 2) + setfield_gc(p0, p2, descr=nextdescr) + call_may_force(i1, descr=mayforcevirtdescr) + guard_not_forced(descr=fdescr) [p2, p1] + virtual_ref_finish(p2, p1) + setfield_gc(p0, NULL, descr=nextdescr) + jump(p0, i1) + """ + expected = """ + [p0, i1] + i3 = force_token() + # + p2 = new_with_vtable(ConstClass(jit_virtual_ref_vtable)) + setfield_gc(p2, i3, descr=virtualtokendescr) + setfield_gc(p2, 2, descr=virtualrefindexdescr) + setfield_gc(p0, p2, descr=nextdescr) + # + call_may_force(i1, descr=mayforcevirtdescr) + guard_not_forced(descr=fdescr) [p2, i1] + # + setfield_gc(p0, NULL, descr=nextdescr) + p1 = new_with_vtable(ConstClass(node_vtable)) + p1b = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1b, i1, descr=valuedescr) + setfield_gc(p1, p1b, descr=nextdescr) + setfield_gc(p2, p1, descr=virtualforceddescr) + setfield_gc(p2, -3, descr=virtualtokendescr) + jump(p0, i1) + """ + # the point of this test is that 'i1' should show up in the fail_args + # of 'guard_not_forced', because it was stored in the virtual 'p1b'. + self.optimize_loop(ops, expected) + self.check_expanded_fail_descr('''p2, p1 + where p1 is a node_vtable, nextdescr=p1b + where p1b is a node_vtable, valuedescr=i1 + ''', rop.GUARD_NOT_FORCED) + + def test_vref_virtual_and_lazy_setfield(self): + self.make_fail_descr() + ops = """ + [p0, i1] + # + p1 = new_with_vtable(ConstClass(node_vtable)) + p1b = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1b, i1, descr=valuedescr) + setfield_gc(p1, p1b, descr=nextdescr) + # + p2 = virtual_ref(p1, 2) + setfield_gc(p0, p2, descr=refdescr) + call(i1, descr=nonwritedescr) + guard_no_exception(descr=fdescr) [p2, p1] + virtual_ref_finish(p2, p1) + setfield_gc(p0, NULL, descr=refdescr) + jump(p0, i1) + """ + expected = """ + [p0, i1] + i3 = force_token() + call(i1, descr=nonwritedescr) + guard_no_exception(descr=fdescr) [i3, i1, p0] + setfield_gc(p0, NULL, descr=refdescr) + jump(p0, i1) + """ + self.optimize_loop(ops, expected) + # the fail_args contain [i3, i1, p0]: + # - i3 is from the virtual expansion of p2 + # - i1 is from the virtual expansion of p1 + # - p0 is from the extra pendingfields + self.loop.inputargs[0].value = self.nodeobjvalue + self.check_expanded_fail_descr('''p2, p1 + p0.refdescr = p2 + where p2 is a jit_virtual_ref_vtable, virtualtokendescr=i3, virtualrefindexdescr=2 + where p1 is a node_vtable, nextdescr=p1b + where p1b is a node_vtable, valuedescr=i1 + ''', rop.GUARD_NO_EXCEPTION) + + def test_vref_virtual_after_finish(self): + self.make_fail_descr() + ops = """ + [i1] + p1 = new_with_vtable(ConstClass(node_vtable)) + p2 = virtual_ref(p1, 7) + escape(p2) + virtual_ref_finish(p2, p1) + call_may_force(i1, descr=mayforcevirtdescr) + guard_not_forced() [] + jump(i1) + """ + expected = """ + [i1] + i3 = force_token() + p2 = new_with_vtable(ConstClass(jit_virtual_ref_vtable)) + setfield_gc(p2, i3, descr=virtualtokendescr) + setfield_gc(p2, 7, descr=virtualrefindexdescr) + escape(p2) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, p1, descr=virtualforceddescr) + setfield_gc(p2, -3, descr=virtualtokendescr) + call_may_force(i1, descr=mayforcevirtdescr) + guard_not_forced() [] + jump(i1) + """ + self.optimize_loop(ops, expected) + + def test_vref_nonvirtual_and_lazy_setfield(self): + self.make_fail_descr() + ops = """ + [i1, p1] + p2 = virtual_ref(p1, 23) + escape(p2) + virtual_ref_finish(p2, p1) + call_may_force(i1, descr=mayforcevirtdescr) + guard_not_forced() [i1] + jump(i1, p1) + """ + expected = """ + [i1, p1] + i3 = force_token() + p2 = new_with_vtable(ConstClass(jit_virtual_ref_vtable)) + setfield_gc(p2, i3, descr=virtualtokendescr) + setfield_gc(p2, 23, descr=virtualrefindexdescr) + escape(p2) + setfield_gc(p2, p1, descr=virtualforceddescr) + setfield_gc(p2, -3, descr=virtualtokendescr) + call_may_force(i1, descr=mayforcevirtdescr) + guard_not_forced() [i1] + jump(i1, p1) + """ + self.optimize_loop(ops, expected) + + def test_arraycopy_1(self): + ops = ''' + [i0] + p1 = new_array(3, descr=arraydescr) + setarrayitem_gc(p1, 1, 1, descr=arraydescr) + p2 = new_array(3, descr=arraydescr) + setarrayitem_gc(p2, 1, 3, descr=arraydescr) + call(0, p1, p2, 1, 1, 2, descr=arraycopydescr) + i2 = getarrayitem_gc(p2, 1, descr=arraydescr) + jump(i2) + ''' + expected = ''' + [i0] + jump(1) + ''' + self.optimize_loop(ops, expected) + + def test_arraycopy_2(self): + ops = ''' + [i0] + p1 = new_array(3, descr=arraydescr) + p2 = new_array(3, descr=arraydescr) + setarrayitem_gc(p1, 0, i0, descr=arraydescr) + setarrayitem_gc(p2, 0, 3, descr=arraydescr) + call(0, p1, p2, 1, 1, 2, descr=arraycopydescr) + i2 = getarrayitem_gc(p2, 0, descr=arraydescr) + jump(i2) + ''' + expected = ''' + [i0] + jump(3) + ''' + self.optimize_loop(ops, expected) + + def test_arraycopy_not_virtual(self): + ops = ''' + [p0] + p1 = new_array(3, descr=arraydescr) + p2 = new_array(3, descr=arraydescr) + setarrayitem_gc(p1, 2, 10, descr=arraydescr) + setarrayitem_gc(p2, 2, 13, descr=arraydescr) + call(0, p1, p2, 0, 0, 3, descr=arraycopydescr) + jump(p2) + ''' + expected = ''' + [p0] + p2 = new_array(3, descr=arraydescr) + setarrayitem_gc(p2, 2, 10, descr=arraydescr) + jump(p2) + ''' + self.optimize_loop(ops, expected) + + def test_arraycopy_no_elem(self): + """ this was actually observed in the wild + """ + ops = ''' + [p1] + p0 = new_array(0, descr=arraydescr) + call(0, p0, p1, 0, 0, 0, descr=arraycopydescr) + jump(p1) + ''' + expected = ''' + [p1] + jump(p1) + ''' + self.optimize_loop(ops, expected) + + def test_bound_lt(self): + ops = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + i2 = int_lt(i0, 5) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_noguard(self): + ops = """ + [i0] + i1 = int_lt(i0, 4) + i2 = int_lt(i0, 5) + jump(i2) + """ + expected = """ + [i0] + i1 = int_lt(i0, 4) + i2 = int_lt(i0, 5) + jump(i2) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_noopt(self): + ops = """ + [i0] + i1 = int_lt(i0, 4) + guard_false(i1) [] + i2 = int_lt(i0, 5) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 4) + guard_false(i1) [] + i2 = int_lt(i0, 5) + guard_true(i2) [] + jump(4) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_rev(self): + ops = """ + [i0] + i1 = int_lt(i0, 4) + guard_false(i1) [] + i2 = int_gt(i0, 3) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 4) + guard_false(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_tripple(self): + ops = """ + [i0] + i1 = int_lt(i0, 0) + guard_true(i1) [] + i2 = int_lt(i0, 7) + guard_true(i2) [] + i3 = int_lt(i0, 5) + guard_true(i3) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 0) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_add(self): + ops = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + i2 = int_add(i0, 10) + i3 = int_lt(i2, 15) + guard_true(i3) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + i2 = int_add(i0, 10) + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_add_before(self): + ops = """ + [i0] + i2 = int_add(i0, 10) + i3 = int_lt(i2, 15) + guard_true(i3) [] + i1 = int_lt(i0, 6) + guard_true(i1) [] + jump(i0) + """ + expected = """ + [i0] + i2 = int_add(i0, 10) + i3 = int_lt(i2, 15) + guard_true(i3) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_add_ovf(self): + ops = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + i2 = int_add_ovf(i0, 10) + guard_no_overflow() [] + i3 = int_lt(i2, 15) + guard_true(i3) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + i2 = int_add(i0, 10) + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_add_ovf_before(self): + ops = """ + [i0] + i2 = int_add_ovf(i0, 10) + guard_no_overflow() [] + i3 = int_lt(i2, 15) + guard_true(i3) [] + i1 = int_lt(i0, 6) + guard_true(i1) [] + jump(i0) + """ + expected = """ + [i0] + i2 = int_add_ovf(i0, 10) + guard_no_overflow() [] + i3 = int_lt(i2, 15) + guard_true(i3) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_sub(self): + ops = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + i2 = int_sub(i0, 10) + i3 = int_lt(i2, -5) + guard_true(i3) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + i2 = int_sub(i0, 10) + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lt_sub_before(self): + ops = """ + [i0] + i2 = int_sub(i0, 10) + i3 = int_lt(i2, -5) + guard_true(i3) [] + i1 = int_lt(i0, 5) + guard_true(i1) [] + jump(i0) + """ + expected = """ + [i0] + i2 = int_sub(i0, 10) + i3 = int_lt(i2, -5) + guard_true(i3) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_ltle(self): + ops = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + i2 = int_le(i0, 3) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_lt(i0, 4) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_lelt(self): + ops = """ + [i0] + i1 = int_le(i0, 4) + guard_true(i1) [] + i2 = int_lt(i0, 5) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_le(i0, 4) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_gt(self): + ops = """ + [i0] + i1 = int_gt(i0, 5) + guard_true(i1) [] + i2 = int_gt(i0, 4) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_gt(i0, 5) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_gtge(self): + ops = """ + [i0] + i1 = int_gt(i0, 5) + guard_true(i1) [] + i2 = int_ge(i0, 6) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_gt(i0, 5) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_gegt(self): + ops = """ + [i0] + i1 = int_ge(i0, 5) + guard_true(i1) [] + i2 = int_gt(i0, 4) + guard_true(i2) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_ge(i0, 5) + guard_true(i1) [] + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_ovf(self): + ops = """ + [i0] + i1 = int_ge(i0, 0) + guard_true(i1) [] + i2 = int_lt(i0, 10) + guard_true(i2) [] + i3 = int_add_ovf(i0, 1) + guard_no_overflow() [] + jump(i3) + """ + expected = """ + [i0] + i1 = int_ge(i0, 0) + guard_true(i1) [] + i2 = int_lt(i0, 10) + guard_true(i2) [] + i3 = int_add(i0, 1) + jump(i3) + """ + self.optimize_loop(ops, expected) + + def test_bound_arraylen(self): + ops = """ + [i0, p0] + p1 = new_array(i0, descr=arraydescr) + i1 = arraylen_gc(p1) + i2 = int_gt(i1, -1) + guard_true(i2) [] + setarrayitem_gc(p0, 0, p1) + jump(i0, p0) + """ + # The dead arraylen_gc will be eliminated by the backend. + expected = """ + [i0, p0] + p1 = new_array(i0, descr=arraydescr) + i1 = arraylen_gc(p1) + setarrayitem_gc(p0, 0, p1) + jump(i0, p0) + """ + self.optimize_loop(ops, expected) + + def test_bound_strlen(self): + ops = """ + [p0] + i0 = strlen(p0) + i1 = int_ge(i0, 0) + guard_true(i1) [] + jump(p0) + """ + # The dead strlen will be eliminated be the backend. + expected = """ + [p0] + i0 = strlen(p0) + jump(p0) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_addsub_const(self): + ops = """ + [i0] + i1 = int_add(i0, 1) + i2 = int_sub(i1, 1) + i3 = int_add(i2, 1) + i4 = int_mul(i2, i3) + jump(i4) + """ + expected = """ + [i0] + i1 = int_add(i0, 1) + i4 = int_mul(i0, i1) + jump(i4) + """ + self.optimize_loop(ops, expected) + + def test_addsub_int(self): + ops = """ + [i0, i10] + i1 = int_add(i0, i10) + i2 = int_sub(i1, i10) + i3 = int_add(i2, i10) + i4 = int_add(i2, i3) + jump(i4, i10) + """ + expected = """ + [i0, i10] + i1 = int_add(i0, i10) + i4 = int_add(i0, i1) + jump(i4, i10) + """ + self.optimize_loop(ops, expected) + + def test_addsub_int2(self): + ops = """ + [i0, i10] + i1 = int_add(i10, i0) + i2 = int_sub(i1, i10) + i3 = int_add(i10, i2) + i4 = int_add(i2, i3) + jump(i4, i10) + """ + expected = """ + [i0, i10] + i1 = int_add(i10, i0) + i4 = int_add(i0, i1) + jump(i4, i10) + """ + self.optimize_loop(ops, expected) + + def test_framestackdepth_overhead(self): + ops = """ + [p0, i22] + i1 = getfield_gc(p0, descr=valuedescr) + i2 = int_gt(i1, i22) + guard_false(i2) [] + i3 = int_add(i1, 1) + setfield_gc(p0, i3, descr=valuedescr) + i4 = int_sub(i3, 1) + setfield_gc(p0, i4, descr=valuedescr) + i5 = int_gt(i4, i22) + guard_false(i5) [] + i6 = int_add(i4, 1) + i331 = force_token() + i7 = int_sub(i6, 1) + setfield_gc(p0, i7, descr=valuedescr) + jump(p0, i22) + """ + expected = """ + [p0, i22] + i1 = getfield_gc(p0, descr=valuedescr) + i2 = int_gt(i1, i22) + guard_false(i2) [] + i3 = int_add(i1, 1) + i331 = force_token() + setfield_gc(p0, i1, descr=valuedescr) + jump(p0, i22) + """ + self.optimize_loop(ops, expected) + + def test_addsub_ovf(self): + ops = """ + [i0] + i1 = int_add_ovf(i0, 10) + guard_no_overflow() [] + i2 = int_sub_ovf(i1, 5) + guard_no_overflow() [] + jump(i2) + """ + expected = """ + [i0] + i1 = int_add_ovf(i0, 10) + guard_no_overflow() [] + i2 = int_sub(i1, 5) + jump(i2) + """ + self.optimize_loop(ops, expected) + + def test_subadd_ovf(self): + ops = """ + [i0] + i1 = int_sub_ovf(i0, 10) + guard_no_overflow() [] + i2 = int_add_ovf(i1, 5) + guard_no_overflow() [] + jump(i2) + """ + expected = """ + [i0] + i1 = int_sub_ovf(i0, 10) + guard_no_overflow() [] + i2 = int_add(i1, 5) + jump(i2) + """ + self.optimize_loop(ops, expected) + + def test_bound_and(self): + ops = """ + [i0] + i1 = int_and(i0, 255) + i2 = int_lt(i1, 500) + guard_true(i2) [] + i3 = int_le(i1, 255) + guard_true(i3) [] + i4 = int_gt(i1, -1) + guard_true(i4) [] + i5 = int_ge(i1, 0) + guard_true(i5) [] + i6 = int_lt(i1, 0) + guard_false(i6) [] + i7 = int_le(i1, -1) + guard_false(i7) [] + i8 = int_gt(i1, 255) + guard_false(i8) [] + i9 = int_ge(i1, 500) + guard_false(i9) [] + i12 = int_lt(i1, 100) + guard_true(i12) [] + i13 = int_le(i1, 90) + guard_true(i13) [] + i14 = int_gt(i1, 10) + guard_true(i14) [] + i15 = int_ge(i1, 20) + guard_true(i15) [] + jump(i1) + """ + expected = """ + [i0] + i1 = int_and(i0, 255) + i12 = int_lt(i1, 100) + guard_true(i12) [] + i13 = int_le(i1, 90) + guard_true(i13) [] + i14 = int_gt(i1, 10) + guard_true(i14) [] + i15 = int_ge(i1, 20) + guard_true(i15) [] + jump(i1) + """ + self.optimize_loop(ops, expected) + + def test_subsub_ovf(self): + ops = """ + [i0] + i1 = int_sub_ovf(1, i0) + guard_no_overflow() [] + i2 = int_gt(i1, 1) + guard_true(i2) [] + i3 = int_sub_ovf(1, i0) + guard_no_overflow() [] + i4 = int_gt(i3, 1) + guard_true(i4) [] + jump(i0) + """ + expected = """ + [i0] + i1 = int_sub_ovf(1, i0) + guard_no_overflow() [] + i2 = int_gt(i1, 1) + guard_true(i2) [] + i3 = int_sub(1, i0) + jump(i0) + """ + self.optimize_loop(ops, expected) + + def test_bound_eq(self): + ops = """ + [i0, i1] + i2 = int_le(i0, 4) + guard_true(i2) [] + i3 = int_eq(i0, i1) + guard_true(i3) [] + i4 = int_lt(i1, 5) + guard_true(i4) [] + jump(i0, i1) + """ + expected = """ + [i0, i1] + i2 = int_le(i0, 4) + guard_true(i2) [] + i3 = int_eq(i0, i1) + guard_true(i3) [] + jump(i0, i1) + """ + self.optimize_loop(ops, expected) + + def test_bound_eq_const(self): + ops = """ + [i0] + i1 = int_eq(i0, 7) + guard_true(i1) [] + i2 = int_add(i0, 3) + jump(i2) + """ + expected = """ + [i0] + i1 = int_eq(i0, 7) + guard_true(i1) [] + jump(10) + + """ + self.optimize_loop(ops, expected) + + def test_bound_eq_const_not(self): + ops = """ + [i0] + i1 = int_eq(i0, 7) + guard_false(i1) [] + i2 = int_add(i0, 3) + jump(i2) + """ + expected = """ + [i0] + i1 = int_eq(i0, 7) + guard_false(i1) [] + i2 = int_add(i0, 3) + jump(i2) + + """ + self.optimize_loop(ops, expected) + + def test_bound_ne_const(self): + ops = """ + [i0] + i1 = int_ne(i0, 7) + guard_false(i1) [] + i2 = int_add(i0, 3) + jump(i2) + """ + expected = """ + [i0] + i1 = int_ne(i0, 7) + guard_false(i1) [] + jump(10) + + """ + self.optimize_loop(ops, expected) + + def test_bound_ne_const_not(self): + ops = """ + [i0] + i1 = int_ne(i0, 7) + guard_true(i1) [] + i2 = int_add(i0, 3) + jump(i2) + """ + expected = """ + [i0] + i1 = int_ne(i0, 7) + guard_true(i1) [] + i2 = int_add(i0, 3) + jump(i2) + """ + self.optimize_loop(ops, expected) + + def test_bound_ltne(self): + ops = """ + [i0, i1] + i2 = int_lt(i0, 7) + guard_true(i2) [] + i3 = int_ne(i0, 10) + guard_true(i2) [] + jump(i0, i1) + """ + expected = """ + [i0, i1] + i2 = int_lt(i0, 7) + guard_true(i2) [] + jump(i0, i1) + """ + self.optimize_loop(ops, expected) + + def test_bound_lege_const(self): + ops = """ + [i0] + i1 = int_ge(i0, 7) + guard_true(i1) [] + i2 = int_le(i0, 7) + guard_true(i2) [] + i3 = int_add(i0, 3) + jump(i3) + """ + expected = """ + [i0] + i1 = int_ge(i0, 7) + guard_true(i1) [] + i2 = int_le(i0, 7) + guard_true(i2) [] + jump(10) + + """ + self.optimize_loop(ops, expected) + + def test_mul_ovf(self): + ops = """ + [i0, i1] + i2 = int_and(i0, 255) + i3 = int_lt(i1, 5) + guard_true(i3) [] + i4 = int_gt(i1, -10) + guard_true(i4) [] + i5 = int_mul_ovf(i2, i1) + guard_no_overflow() [] + i6 = int_lt(i5, -2550) + guard_false(i6) [] + i7 = int_ge(i5, 1276) + guard_false(i7) [] + i8 = int_gt(i5, 126) + guard_true(i8) [] + jump(i0, i1) + """ + expected = """ + [i0, i1] + i2 = int_and(i0, 255) + i3 = int_lt(i1, 5) + guard_true(i3) [] + i4 = int_gt(i1, -10) + guard_true(i4) [] + i5 = int_mul(i2, i1) + i8 = int_gt(i5, 126) + guard_true(i8) [] + jump(i0, i1) + """ + self.optimize_loop(ops, expected) + + def test_mul_ovf_before(self): + ops = """ + [i0, i1] + i2 = int_and(i0, 255) + i22 = int_add(i2, 1) + i3 = int_mul_ovf(i22, i1) + guard_no_overflow() [] + i4 = int_lt(i3, 10) + guard_true(i4) [] + i5 = int_gt(i3, 2) + guard_true(i5) [] + i6 = int_lt(i1, 0) + guard_false(i6) [] + jump(i0, i1) + """ + expected = """ + [i0, i1] + i2 = int_and(i0, 255) + i22 = int_add(i2, 1) + i3 = int_mul_ovf(i22, i1) + guard_no_overflow() [] + i4 = int_lt(i3, 10) + guard_true(i4) [] + i5 = int_gt(i3, 2) + guard_true(i5) [] + jump(i0, i1) + """ + self.optimize_loop(ops, expected) + + def test_sub_ovf_before(self): + ops = """ + [i0, i1] + i2 = int_and(i0, 255) + i3 = int_sub_ovf(i2, i1) + guard_no_overflow() [] + i4 = int_le(i3, 10) + guard_true(i4) [] + i5 = int_ge(i3, 2) + guard_true(i5) [] + i6 = int_lt(i1, -10) + guard_false(i6) [] + i7 = int_gt(i1, 253) + guard_false(i7) [] + jump(i0, i1) + """ + expected = """ + [i0, i1] + i2 = int_and(i0, 255) + i3 = int_sub_ovf(i2, i1) + guard_no_overflow() [] + i4 = int_le(i3, 10) + guard_true(i4) [] + i5 = int_ge(i3, 2) + guard_true(i5) [] + jump(i0, i1) + """ + self.optimize_loop(ops, expected) + + # ---------- + def optimize_strunicode_loop(self, ops, optops): + # check with the arguments passed in + self.optimize_loop(ops, optops) + # check with replacing 'str' with 'unicode' everywhere + self.optimize_loop(ops.replace('str','unicode').replace('s"', 'u"'), + optops.replace('str','unicode').replace('s"', 'u"')) + + def test_newstr_1(self): + ops = """ + [i0] + p1 = newstr(1) + strsetitem(p1, 0, i0) + i1 = strgetitem(p1, 0) + jump(i1) + """ + expected = """ + [i0] + jump(i0) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_newstr_2(self): + ops = """ + [i0, i1] + p1 = newstr(2) + strsetitem(p1, 0, i0) + strsetitem(p1, 1, i1) + i2 = strgetitem(p1, 1) + i3 = strgetitem(p1, 0) + jump(i2, i3) + """ + expected = """ + [i0, i1] + jump(i1, i0) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_concat_1(self): + ops = """ + [p1, p2] + p3 = call(0, p1, p2, descr=strconcatdescr) + jump(p2, p3) + """ + expected = """ + [p1, p2] + i1 = strlen(p1) + i2 = strlen(p2) + i3 = int_add(i1, i2) + p3 = newstr(i3) + i4 = strlen(p1) + copystrcontent(p1, p3, 0, 0, i4) + i5 = strlen(p2) + i6 = int_add(i4, i5) # will be killed by the backend + copystrcontent(p2, p3, 0, i4, i5) + jump(p2, p3) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_concat_vstr2_str(self): + ops = """ + [i0, i1, p2] + p1 = newstr(2) + strsetitem(p1, 0, i0) + strsetitem(p1, 1, i1) + p3 = call(0, p1, p2, descr=strconcatdescr) + jump(i1, i0, p3) + """ + expected = """ + [i0, i1, p2] + i2 = strlen(p2) + i3 = int_add(2, i2) + p3 = newstr(i3) + strsetitem(p3, 0, i0) + strsetitem(p3, 1, i1) + i4 = strlen(p2) + i5 = int_add(2, i4) # will be killed by the backend + copystrcontent(p2, p3, 0, 2, i4) + jump(i1, i0, p3) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_concat_str_vstr2(self): + ops = """ + [i0, i1, p2] + p1 = newstr(2) + strsetitem(p1, 0, i0) + strsetitem(p1, 1, i1) + p3 = call(0, p2, p1, descr=strconcatdescr) + jump(i1, i0, p3) + """ + expected = """ + [i0, i1, p2] + i2 = strlen(p2) + i3 = int_add(i2, 2) + p3 = newstr(i3) + i4 = strlen(p2) + copystrcontent(p2, p3, 0, 0, i4) + strsetitem(p3, i4, i0) + i5 = int_add(i4, 1) + strsetitem(p3, i5, i1) + i6 = int_add(i5, 1) # will be killed by the backend + jump(i1, i0, p3) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_concat_str_str_str(self): + ops = """ + [p1, p2, p3] + p4 = call(0, p1, p2, descr=strconcatdescr) + p5 = call(0, p4, p3, descr=strconcatdescr) + jump(p2, p3, p5) + """ + expected = """ + [p1, p2, p3] + i1 = strlen(p1) + i2 = strlen(p2) + i12 = int_add(i1, i2) + i3 = strlen(p3) + i123 = int_add(i12, i3) + p5 = newstr(i123) + i1b = strlen(p1) + copystrcontent(p1, p5, 0, 0, i1b) + i2b = strlen(p2) + i12b = int_add(i1b, i2b) + copystrcontent(p2, p5, 0, i1b, i2b) + i3b = strlen(p3) + i123b = int_add(i12b, i3b) # will be killed by the backend + copystrcontent(p3, p5, 0, i12b, i3b) + jump(p2, p3, p5) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_concat_str_cstr1(self): + ops = """ + [p2] + p3 = call(0, p2, s"x", descr=strconcatdescr) + jump(p3) + """ + expected = """ + [p2] + i2 = strlen(p2) + i3 = int_add(i2, 1) + p3 = newstr(i3) + i4 = strlen(p2) + copystrcontent(p2, p3, 0, 0, i4) + strsetitem(p3, i4, 120) # == ord('x') + i5 = int_add(i4, 1) # will be killed by the backend + jump(p3) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_concat_consts(self): + ops = """ + [] + p1 = same_as(s"ab") + p2 = same_as(s"cde") + p3 = call(0, p1, p2, descr=strconcatdescr) + escape(p3) + jump() + """ + expected = """ + [] + escape(s"abcde") + jump() + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_slice_1(self): + ops = """ + [p1, i1, i2] + p2 = call(0, p1, i1, i2, descr=strslicedescr) + jump(p2, i1, i2) + """ + expected = """ + [p1, i1, i2] + i3 = int_sub(i2, i1) + p2 = newstr(i3) + copystrcontent(p1, p2, i1, 0, i3) + jump(p2, i1, i2) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_slice_2(self): + ops = """ + [p1, i2] + p2 = call(0, p1, 0, i2, descr=strslicedescr) + jump(p2, i2) + """ + expected = """ + [p1, i2] + p2 = newstr(i2) + copystrcontent(p1, p2, 0, 0, i2) + jump(p2, i2) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_slice_3(self): + ops = """ + [p1, i1, i2, i3, i4] + p2 = call(0, p1, i1, i2, descr=strslicedescr) + p3 = call(0, p2, i3, i4, descr=strslicedescr) + jump(p3, i1, i2, i3, i4) + """ + expected = """ + [p1, i1, i2, i3, i4] + i0 = int_sub(i2, i1) # killed by the backend + i5 = int_sub(i4, i3) + i6 = int_add(i1, i3) + p3 = newstr(i5) + copystrcontent(p1, p3, i6, 0, i5) + jump(p3, i1, i2, i3, i4) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_slice_getitem1(self): + ops = """ + [p1, i1, i2, i3] + p2 = call(0, p1, i1, i2, descr=strslicedescr) + i4 = strgetitem(p2, i3) + escape(i4) + jump(p1, i1, i2, i3) + """ + expected = """ + [p1, i1, i2, i3] + i6 = int_sub(i2, i1) # killed by the backend + i5 = int_add(i1, i3) + i4 = strgetitem(p1, i5) + escape(i4) + jump(p1, i1, i2, i3) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_slice_plain(self): + ops = """ + [i3, i4] + p1 = newstr(2) + strsetitem(p1, 0, i3) + strsetitem(p1, 1, i4) + p2 = call(0, p1, 1, 2, descr=strslicedescr) + i5 = strgetitem(p2, 0) + escape(i5) + jump(i3, i4) + """ + expected = """ + [i3, i4] + escape(i4) + jump(i3, i4) + """ + self.optimize_strunicode_loop(ops, expected) + + def test_str_slice_concat(self): + ops = """ + [p1, i1, i2, p2] + p3 = call(0, p1, i1, i2, descr=strslicedescr) + p4 = call(0, p3, p2, descr=strconcatdescr) + jump(p4, i1, i2, p2) + """ + expected = """ + [p1, i1, i2, p2] + i3 = int_sub(i2, i1) # length of p3 + i4 = strlen(p2) + i5 = int_add(i3, i4) + p4 = newstr(i5) + copystrcontent(p1, p4, i1, 0, i3) + i4b = strlen(p2) + i6 = int_add(i3, i4b) # killed by the backend + copystrcontent(p2, p4, 0, i3, i4b) + jump(p4, i1, i2, p2) + """ + self.optimize_strunicode_loop(ops, expected) + + # ---------- + def optimize_strunicode_loop_extradescrs(self, ops, optops): + from pypy.jit.metainterp.optimizeopt import string + class FakeCallInfoCollection: + def callinfo_for_oopspec(self, oopspecindex): + calldescrtype = type(LLtypeMixin.strequaldescr) + for value in LLtypeMixin.__dict__.values(): + if isinstance(value, calldescrtype): + extra = value.get_extra_info() + if extra and extra.oopspecindex == oopspecindex: + # returns 0 for 'func' in this test + return value, 0 + raise AssertionError("not found: oopspecindex=%d" % + oopspecindex) + # + self.callinfocollection = FakeCallInfoCollection() + self.optimize_strunicode_loop(ops, optops) + + def test_str_equal_noop1(self): + ops = """ + [p1, p2] + i0 = call(0, p1, p2, descr=strequaldescr) + escape(i0) + jump(p1, p2) + """ + self.optimize_strunicode_loop_extradescrs(ops, ops) + + def test_str_equal_noop2(self): + ops = """ + [p1, p2, p3] + p4 = call(0, p1, p2, descr=strconcatdescr) + i0 = call(0, p3, p4, descr=strequaldescr) + escape(i0) + jump(p1, p2, p3) + """ + expected = """ + [p1, p2, p3] + i1 = strlen(p1) + i2 = strlen(p2) + i3 = int_add(i1, i2) + p4 = newstr(i3) + i4 = strlen(p1) + copystrcontent(p1, p4, 0, 0, i4) + i5 = strlen(p2) + i6 = int_add(i4, i5) # will be killed by the backend + copystrcontent(p2, p4, 0, i4, i5) + i0 = call(0, p3, p4, descr=strequaldescr) + escape(i0) + jump(p1, p2, p3) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_slice1(self): + ops = """ + [p1, i1, i2, p3] + p4 = call(0, p1, i1, i2, descr=strslicedescr) + i0 = call(0, p4, p3, descr=strequaldescr) + escape(i0) + jump(p1, i1, i2, p3) + """ + expected = """ + [p1, i1, i2, p3] + i3 = int_sub(i2, i1) + i0 = call(0, p1, i1, i3, p3, descr=streq_slice_checknull_descr) + escape(i0) + jump(p1, i1, i2, p3) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_slice2(self): + ops = """ + [p1, i1, i2, p3] + p4 = call(0, p1, i1, i2, descr=strslicedescr) + i0 = call(0, p3, p4, descr=strequaldescr) + escape(i0) + jump(p1, i1, i2, p3) + """ + expected = """ + [p1, i1, i2, p3] + i4 = int_sub(i2, i1) + i0 = call(0, p1, i1, i4, p3, descr=streq_slice_checknull_descr) + escape(i0) + jump(p1, i1, i2, p3) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_slice3(self): + ops = """ + [p1, i1, i2, p3] + guard_nonnull(p3) [] + p4 = call(0, p1, i1, i2, descr=strslicedescr) + i0 = call(0, p3, p4, descr=strequaldescr) + escape(i0) + jump(p1, i1, i2, p3) + """ + expected = """ + [p1, i1, i2, p3] + guard_nonnull(p3) [] + i4 = int_sub(i2, i1) + i0 = call(0, p1, i1, i4, p3, descr=streq_slice_nonnull_descr) + escape(i0) + jump(p1, i1, i2, p3) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_slice4(self): + ops = """ + [p1, i1, i2] + p3 = call(0, p1, i1, i2, descr=strslicedescr) + i0 = call(0, p3, s"x", descr=strequaldescr) + escape(i0) + jump(p1, i1, i2) + """ + expected = """ + [p1, i1, i2] + i3 = int_sub(i2, i1) + i0 = call(0, p1, i1, i3, 120, descr=streq_slice_char_descr) + escape(i0) + jump(p1, i1, i2) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_slice5(self): + ops = """ + [p1, i1, i2, i3] + p4 = call(0, p1, i1, i2, descr=strslicedescr) + p5 = newstr(1) + strsetitem(p5, 0, i3) + i0 = call(0, p5, p4, descr=strequaldescr) + escape(i0) + jump(p1, i1, i2, i3) + """ + expected = """ + [p1, i1, i2, i3] + i4 = int_sub(i2, i1) + i0 = call(0, p1, i1, i4, i3, descr=streq_slice_char_descr) + escape(i0) + jump(p1, i1, i2, i3) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_none1(self): + ops = """ + [p1] + i0 = call(0, p1, NULL, descr=strequaldescr) + escape(i0) + jump(p1) + """ + expected = """ + [p1] + i0 = ptr_eq(p1, NULL) + escape(i0) + jump(p1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_none2(self): + ops = """ + [p1] + i0 = call(0, NULL, p1, descr=strequaldescr) + escape(i0) + jump(p1) + """ + expected = """ + [p1] + i0 = ptr_eq(p1, NULL) + escape(i0) + jump(p1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_nonnull1(self): + ops = """ + [p1] + guard_nonnull(p1) [] + i0 = call(0, p1, s"hello world", descr=strequaldescr) + escape(i0) + jump(p1) + """ + expected = """ + [p1] + guard_nonnull(p1) [] + i0 = call(0, p1, s"hello world", descr=streq_nonnull_descr) + escape(i0) + jump(p1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_nonnull2(self): + ops = """ + [p1] + guard_nonnull(p1) [] + i0 = call(0, p1, s"", descr=strequaldescr) + escape(i0) + jump(p1) + """ + expected = """ + [p1] + guard_nonnull(p1) [] + i1 = strlen(p1) + i0 = int_eq(i1, 0) + escape(i0) + jump(p1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_nonnull3(self): + ops = """ + [p1] + guard_nonnull(p1) [] + i0 = call(0, p1, s"x", descr=strequaldescr) + escape(i0) + jump(p1) + """ + expected = """ + [p1] + guard_nonnull(p1) [] + i0 = call(0, p1, 120, descr=streq_nonnull_char_descr) + escape(i0) + jump(p1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_nonnull4(self): + ops = """ + [p1, p2] + p4 = call(0, p1, p2, descr=strconcatdescr) + i0 = call(0, s"hello world", p4, descr=strequaldescr) + escape(i0) + jump(p1, p2) + """ + expected = """ + [p1, p2] + i1 = strlen(p1) + i2 = strlen(p2) + i3 = int_add(i1, i2) + p4 = newstr(i3) + i4 = strlen(p1) + copystrcontent(p1, p4, 0, 0, i4) + i5 = strlen(p2) + i6 = int_add(i4, i5) # will be killed by the backend + copystrcontent(p2, p4, 0, i4, i5) + i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr) + escape(i0) + jump(p1, p2) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_chars0(self): + ops = """ + [i1] + p1 = newstr(0) + i0 = call(0, p1, s"", descr=strequaldescr) + escape(i0) + jump(i1) + """ + expected = """ + [i1] + escape(1) + jump(i1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_chars1(self): + ops = """ + [i1] + p1 = newstr(1) + strsetitem(p1, 0, i1) + i0 = call(0, p1, s"x", descr=strequaldescr) + escape(i0) + jump(i1) + """ + expected = """ + [i1] + i0 = int_eq(i1, 120) # ord('x') + escape(i0) + jump(i1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_chars2(self): + ops = """ + [i1, i2] + p1 = newstr(2) + strsetitem(p1, 0, i1) + strsetitem(p1, 1, i2) + i0 = call(0, p1, s"xy", descr=strequaldescr) + escape(i0) + jump(i1, i2) + """ + expected = """ + [i1, i2] + p1 = newstr(2) + strsetitem(p1, 0, i1) + strsetitem(p1, 1, i2) + i0 = call(0, p1, s"xy", descr=streq_lengthok_descr) + escape(i0) + jump(i1, i2) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_chars3(self): + ops = """ + [p1] + i0 = call(0, s"x", p1, descr=strequaldescr) + escape(i0) + jump(p1) + """ + expected = """ + [p1] + i0 = call(0, p1, 120, descr=streq_checknull_char_descr) + escape(i0) + jump(p1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str_equal_lengthmismatch1(self): + ops = """ + [i1] + p1 = newstr(1) + strsetitem(p1, 0, i1) + i0 = call(0, s"xy", p1, descr=strequaldescr) + escape(i0) + jump(i1) + """ + expected = """ + [i1] + escape(0) + jump(i1) + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str2unicode_constant(self): + ops = """ + [] + p0 = call(0, "xy", descr=s2u_descr) # string -> unicode + escape(p0) + jump() + """ + expected = """ + [] + escape(u"xy") + jump() + """ + self.optimize_strunicode_loop_extradescrs(ops, expected) + + def test_str2unicode_nonconstant(self): + ops = """ + [p0] + p1 = call(0, p0, descr=s2u_descr) # string -> unicode + escape(p1) + jump(p1) + """ + self.optimize_strunicode_loop_extradescrs(ops, ops) + # more generally, supporting non-constant but virtual cases is + # not obvious, because of the exception UnicodeDecodeError that + # can be raised by ll_str2unicode() + + +##class TestOOtype(BaseTestOptimizeBasic, OOtypeMixin): + +## def test_instanceof(self): +## ops = """ +## [i0] +## p0 = new_with_vtable(ConstClass(node_vtable)) +## i1 = instanceof(p0, descr=nodesize) +## jump(i1) +## """ +## expected = """ +## [i0] +## jump(1) +## """ +## self.optimize_loop(ops, expected) + +## def test_instanceof_guard_class(self): +## ops = """ +## [i0, p0] +## guard_class(p0, ConstClass(node_vtable)) [] +## i1 = instanceof(p0, descr=nodesize) +## jump(i1, p0) +## """ +## expected = """ +## [i0, p0] +## guard_class(p0, ConstClass(node_vtable)) [] +## jump(1, p0) +## """ +## self.optimize_loop(ops, expected) diff --git a/pypy/jit/metainterp/test/test_optimizefficall.py b/pypy/jit/metainterp/test/test_optimizefficall.py index 53373b492f..940f4afee4 100644 --- a/pypy/jit/metainterp/test/test_optimizefficall.py +++ b/pypy/jit/metainterp/test/test_optimizefficall.py @@ -2,7 +2,8 @@ from pypy.rpython.lltypesystem import llmemory from pypy.rlib.libffi import Func, types from pypy.jit.metainterp.history import AbstractDescr from pypy.jit.codewriter.effectinfo import EffectInfo -from pypy.jit.metainterp.test.test_optimizeopt import BaseTestOptimizeOpt, LLtypeMixin +from pypy.jit.metainterp.test.test_optimizebasic import BaseTestBasic +from pypy.jit.metainterp.test.test_optimizebasic import LLtypeMixin class MyCallDescr(AbstractDescr): """ @@ -30,7 +31,7 @@ class FakeLLObject(object): return id(self) -class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): +class TestFfiCall(BaseTestBasic, LLtypeMixin): jit_ffi = True class namespace: @@ -46,13 +47,15 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): argtypes=[types.sint, types.double], restype=types.sint) # - def calldescr(cpu, FUNC, oopspecindex): - einfo = EffectInfo([], [], [], oopspecindex=oopspecindex) + def calldescr(cpu, FUNC, oopspecindex, extraeffect=None): + einfo = EffectInfo([], [], [], oopspecindex=oopspecindex, + extraeffect=extraeffect) return cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, einfo) # libffi_prepare = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PREPARE) libffi_push_arg = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PUSH_ARG) - libffi_call = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_CALL) + libffi_call = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_CALL, + EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE) namespace = namespace.__dict__ @@ -78,7 +81,7 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): guard_no_exception() [] jump(i3, f1) """ - loop = self.optimize_loop(ops, 'Not, Not', expected) + loop = self.optimize_loop(ops, expected) def test_ffi_call_nonconst(self): ops = """ @@ -92,7 +95,7 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): jump(i3, f1, p2) """ expected = ops - loop = self.optimize_loop(ops, 'Not, Not, Not', expected) + loop = self.optimize_loop(ops, expected) def test_handle_virtualizables(self): # this test needs an explanation to understand what goes on: see the @@ -118,7 +121,7 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): guard_no_exception() [p2] jump(i3, f1, p2) """ - loop = self.optimize_loop(ops, 'Not, Not, Not', expected) + loop = self.optimize_loop(ops, expected) # ---------------------------------------------------------------------- # in pratice, the situations described in these tests should never happen, @@ -137,7 +140,7 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): jump(i3, f1) """ expected = ops - loop = self.optimize_loop(ops, 'Not, Not', expected) + loop = self.optimize_loop(ops, expected) def test_rollback_multiple_calls(self): ops = """ @@ -160,7 +163,7 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): jump(i3, i4, f1) """ expected = ops - loop = self.optimize_loop(ops, 'Not, Not, Not', expected) + loop = self.optimize_loop(ops, expected) def test_rollback_multiple_prepare(self): ops = """ @@ -183,7 +186,7 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): jump(i3, i4, f1) """ expected = ops - loop = self.optimize_loop(ops, 'Not, Not, Not', expected) + loop = self.optimize_loop(ops, expected) def test_optimize_nested_call(self): ops = """ @@ -221,7 +224,7 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): guard_no_exception() [] jump(i3, i4, f1) """ - loop = self.optimize_loop(ops, 'Not, Not, Not', expected) + loop = self.optimize_loop(ops, expected) def test_rollback_force_token(self): ops = """ @@ -238,4 +241,4 @@ class TestFfiCall(BaseTestOptimizeOpt, LLtypeMixin): jump(i3, f1, p2) """ expected = ops - loop = self.optimize_loop(ops, 'Not, Not, Not', expected) + loop = self.optimize_loop(ops, expected) diff --git a/pypy/jit/metainterp/test/test_optimizefindnode.py b/pypy/jit/metainterp/test/test_optimizefindnode.py deleted file mode 100644 index 15b3046069..0000000000 --- a/pypy/jit/metainterp/test/test_optimizefindnode.py +++ /dev/null @@ -1,1199 +0,0 @@ -import py, random - -from pypy.rpython.lltypesystem import lltype, llmemory, rclass, rstr -from pypy.rpython.ootypesystem import ootype -from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE - -from pypy.jit.backend.llgraph import runner -from pypy.jit.metainterp.history import (BoxInt, BoxPtr, ConstInt, ConstPtr, - Const, TreeLoop, BoxObj, - ConstObj, AbstractDescr) -from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder -from pypy.jit.metainterp.optimizefindnode import BridgeSpecializationFinder -from pypy.jit.metainterp.optimizeutil import sort_descrs, InvalidLoop -from pypy.jit.metainterp.specnode import NotSpecNode, prebuiltNotSpecNode -from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode -from pypy.jit.metainterp.specnode import VirtualArraySpecNode -from pypy.jit.metainterp.specnode import VirtualStructSpecNode -from pypy.jit.metainterp.specnode import ConstantSpecNode -from pypy.jit.codewriter.effectinfo import EffectInfo -from pypy.jit.codewriter.heaptracker import register_known_gctype, adr2int -from pypy.jit.tool.oparser import parse - -def test_sort_descrs(): - class PseudoDescr(AbstractDescr): - def __init__(self, n): - self.n = n - def sort_key(self): - return self.n - for i in range(17): - lst = [PseudoDescr(j) for j in range(i)] - lst2 = lst[:] - random.shuffle(lst2) - sort_descrs(lst2) - assert lst2 == lst - -# ____________________________________________________________ - -class LLtypeMixin(object): - type_system = 'lltype' - - def get_class_of_box(self, box): - return box.getref(rclass.OBJECTPTR).typeptr - - node_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) - node_vtable.name = rclass.alloc_array_name('node') - node_vtable_adr = llmemory.cast_ptr_to_adr(node_vtable) - node_vtable2 = lltype.malloc(OBJECT_VTABLE, immortal=True) - node_vtable2.name = rclass.alloc_array_name('node2') - node_vtable_adr2 = llmemory.cast_ptr_to_adr(node_vtable2) - cpu = runner.LLtypeCPU(None) - - NODE = lltype.GcForwardReference() - NODE.become(lltype.GcStruct('NODE', ('parent', OBJECT), - ('value', lltype.Signed), - ('floatval', lltype.Float), - ('next', lltype.Ptr(NODE)))) - NODE2 = lltype.GcStruct('NODE2', ('parent', NODE), - ('other', lltype.Ptr(NODE))) - node = lltype.malloc(NODE) - node.parent.typeptr = node_vtable - nodebox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node)) - myptr = nodebox.value - myptr2 = lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(NODE)) - nodebox2 = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node)) - nodesize = cpu.sizeof(NODE) - nodesize2 = cpu.sizeof(NODE2) - valuedescr = cpu.fielddescrof(NODE, 'value') - floatdescr = cpu.fielddescrof(NODE, 'floatval') - nextdescr = cpu.fielddescrof(NODE, 'next') - otherdescr = cpu.fielddescrof(NODE2, 'other') - - NODEOBJ = lltype.GcStruct('NODEOBJ', ('parent', OBJECT), - ('ref', lltype.Ptr(OBJECT))) - nodeobj = lltype.malloc(NODEOBJ) - nodeobjvalue = lltype.cast_opaque_ptr(llmemory.GCREF, nodeobj) - refdescr = cpu.fielddescrof(NODEOBJ, 'ref') - - arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Signed)) - floatarraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Float)) - - # a GcStruct not inheriting from OBJECT - S = lltype.GcStruct('TUPLE', ('a', lltype.Signed), ('b', lltype.Ptr(NODE))) - ssize = cpu.sizeof(S) - adescr = cpu.fielddescrof(S, 'a') - bdescr = cpu.fielddescrof(S, 'b') - sbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S))) - arraydescr2 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(S))) - - T = lltype.GcStruct('TUPLE', - ('c', lltype.Signed), - ('d', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE))))) - tsize = cpu.sizeof(T) - cdescr = cpu.fielddescrof(T, 'c') - ddescr = cpu.fielddescrof(T, 'd') - arraydescr3 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(NODE))) - - U = lltype.GcStruct('U', - ('parent', OBJECT), - ('one', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE))))) - u_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) - u_vtable_adr = llmemory.cast_ptr_to_adr(u_vtable) - usize = cpu.sizeof(U) - onedescr = cpu.fielddescrof(U, 'one') - - FUNC = lltype.FuncType([lltype.Signed], lltype.Signed) - plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) - nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [])) - writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [adescr], [])) - writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [adescr], [arraydescr])) - readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([adescr], [], [])) - mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([nextdescr], [], [], - EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE)) - arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY)) - - for _name, _os in [ - ('strconcatdescr', 'OS_STR_CONCAT'), - ('strslicedescr', 'OS_STR_SLICE'), - ('strequaldescr', 'OS_STR_EQUAL'), - ('streq_slice_checknull_descr', 'OS_STREQ_SLICE_CHECKNULL'), - ('streq_slice_nonnull_descr', 'OS_STREQ_SLICE_NONNULL'), - ('streq_slice_char_descr', 'OS_STREQ_SLICE_CHAR'), - ('streq_nonnull_descr', 'OS_STREQ_NONNULL'), - ('streq_nonnull_char_descr', 'OS_STREQ_NONNULL_CHAR'), - ('streq_checknull_char_descr', 'OS_STREQ_CHECKNULL_CHAR'), - ('streq_lengthok_descr', 'OS_STREQ_LENGTHOK'), - ]: - _oopspecindex = getattr(EffectInfo, _os) - locals()[_name] = \ - cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], oopspecindex=_oopspecindex)) - # - _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI')) - locals()[_name.replace('str', 'unicode')] = \ - cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], oopspecindex=_oopspecindex)) - - s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE)) - # - - class LoopToken(AbstractDescr): - pass - asmdescr = LoopToken() # it can be whatever, it's not a descr though - - from pypy.jit.metainterp.virtualref import VirtualRefInfo - class FakeWarmRunnerDesc: - pass - FakeWarmRunnerDesc.cpu = cpu - vrefinfo = VirtualRefInfo(FakeWarmRunnerDesc) - virtualtokendescr = vrefinfo.descr_virtual_token - virtualrefindexdescr = vrefinfo.descr_virtualref_index - virtualforceddescr = vrefinfo.descr_forced - jit_virtual_ref_vtable = vrefinfo.jit_virtual_ref_vtable - jvr_vtable_adr = llmemory.cast_ptr_to_adr(jit_virtual_ref_vtable) - - register_known_gctype(cpu, node_vtable, NODE) - register_known_gctype(cpu, node_vtable2, NODE2) - register_known_gctype(cpu, u_vtable, U) - register_known_gctype(cpu, jit_virtual_ref_vtable,vrefinfo.JIT_VIRTUAL_REF) - - namespace = locals() - -class OOtypeMixin_xxx_disabled(object): - type_system = 'ootype' - -## def get_class_of_box(self, box): -## root = box.getref(ootype.ROOT) -## return ootype.classof(root) - -## cpu = runner.OOtypeCPU(None) -## NODE = ootype.Instance('NODE', ootype.ROOT, {}) -## NODE._add_fields({'value': ootype.Signed, -## 'floatval' : ootype.Float, -## 'next': NODE}) -## NODE2 = ootype.Instance('NODE2', NODE, {'other': NODE}) - -## node_vtable = ootype.runtimeClass(NODE) -## node_vtable_adr = ootype.cast_to_object(node_vtable) -## node_vtable2 = ootype.runtimeClass(NODE2) -## node_vtable_adr2 = ootype.cast_to_object(node_vtable2) - -## node = ootype.new(NODE) -## nodebox = BoxObj(ootype.cast_to_object(node)) -## myptr = nodebox.value -## myptr2 = ootype.cast_to_object(ootype.new(NODE)) -## nodebox2 = BoxObj(ootype.cast_to_object(node)) -## valuedescr = cpu.fielddescrof(NODE, 'value') -## floatdescr = cpu.fielddescrof(NODE, 'floatval') -## nextdescr = cpu.fielddescrof(NODE, 'next') -## otherdescr = cpu.fielddescrof(NODE2, 'other') -## nodesize = cpu.typedescrof(NODE) -## nodesize2 = cpu.typedescrof(NODE2) - -## arraydescr = cpu.arraydescrof(ootype.Array(ootype.Signed)) -## floatarraydescr = cpu.arraydescrof(ootype.Array(ootype.Float)) - -## # a plain Record -## S = ootype.Record({'a': ootype.Signed, 'b': NODE}) -## ssize = cpu.typedescrof(S) -## adescr = cpu.fielddescrof(S, 'a') -## bdescr = cpu.fielddescrof(S, 'b') -## sbox = BoxObj(ootype.cast_to_object(ootype.new(S))) -## arraydescr2 = cpu.arraydescrof(ootype.Array(S)) - -## T = ootype.Record({'c': ootype.Signed, -## 'd': ootype.Array(NODE)}) -## tsize = cpu.typedescrof(T) -## cdescr = cpu.fielddescrof(T, 'c') -## ddescr = cpu.fielddescrof(T, 'd') -## arraydescr3 = cpu.arraydescrof(ootype.Array(NODE)) - -## U = ootype.Instance('U', ootype.ROOT, {'one': ootype.Array(NODE)}) -## usize = cpu.typedescrof(U) -## onedescr = cpu.fielddescrof(U, 'one') -## u_vtable = ootype.runtimeClass(U) -## u_vtable_adr = ootype.cast_to_object(u_vtable) - -## # force a consistent order -## valuedescr.sort_key() -## nextdescr.sort_key() -## adescr.sort_key() -## bdescr.sort_key() - -## FUNC = lltype.FuncType([lltype.Signed], lltype.Signed) -## nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) # XXX fix ootype - -## cpu.class_sizes = {node_vtable_adr: cpu.typedescrof(NODE), -## node_vtable_adr2: cpu.typedescrof(NODE2), -## u_vtable_adr: cpu.typedescrof(U)} -## namespace = locals() - -class BaseTest(object): - invent_fail_descr = None - - def parse(self, s, boxkinds=None): - return parse(s, self.cpu, self.namespace, - type_system=self.type_system, - boxkinds=boxkinds, - invent_fail_descr=self.invent_fail_descr) - - def unpack_specnodes(self, text): - # - def constclass(cls_vtable): - if self.type_system == 'lltype': - return ConstInt(adr2int(llmemory.cast_ptr_to_adr(cls_vtable))) - else: - return ConstObj(ootype.cast_to_object(cls_vtable)) - def constant(value): - if isinstance(lltype.typeOf(value), lltype.Ptr): - return ConstPtr(value) - elif isinstance(ootype.typeOf(value), ootype.OOType): - return ConstObj(ootype.cast_to_object(value)) - else: - return ConstInt(value) - - def parsefields(kwds_fields): - fields = [] - for key, value in kwds_fields.items(): - fields.append((self.namespace[key], value)) - fields.sort(key = lambda (x, _): x.sort_key()) - return fields - def makeConstant(value): - return ConstantSpecNode(constant(value)) - def makeVirtual(cls_vtable, **kwds_fields): - fields = parsefields(kwds_fields) - return VirtualInstanceSpecNode(constclass(cls_vtable), fields) - def makeVirtualArray(arraydescr, *items): - return VirtualArraySpecNode(arraydescr, items) - def makeVirtualStruct(typedescr, **kwds_fields): - fields = parsefields(kwds_fields) - return VirtualStructSpecNode(typedescr, fields) - # - context = {'Not': prebuiltNotSpecNode, - 'Constant': makeConstant, - 'Virtual': makeVirtual, - 'VArray': makeVirtualArray, - 'VStruct': makeVirtualStruct} - lst = eval('[' + text + ']', self.namespace, context) - return lst - - def check_specnodes(self, specnodes, text): - lst = self.unpack_specnodes(text) - assert len(specnodes) == len(lst) - for x, y in zip(specnodes, lst): - assert x.equals(y, ge=False) - return True - -# ____________________________________________________________ - -class BaseTestOptimizeFindNode(BaseTest): - - def find_nodes(self, ops, spectext, boxkinds=None): - assert boxkinds is None or isinstance(boxkinds, dict) - loop = self.parse(ops, boxkinds=boxkinds) - perfect_specialization_finder = PerfectSpecializationFinder(self.cpu) - perfect_specialization_finder.find_nodes_loop(loop) - self.check_specnodes(loop.token.specnodes, spectext) - return (loop.getboxes(), perfect_specialization_finder.getnode) - - def test_find_nodes_simple(self): - ops = """ - [i] - i0 = int_sub(i, 1) - guard_value(i0, 0) [i0] - jump(i0) - """ - boxes, getnode = self.find_nodes(ops, 'Not') - assert getnode(boxes.i).fromstart - assert not getnode(boxes.i0).fromstart - - def test_find_nodes_non_escape(self): - ops = """ - [p0] - p1 = getfield_gc(p0, descr=nextdescr) - i0 = getfield_gc(p1, descr=valuedescr) - i1 = int_sub(i0, 1) - p2 = getfield_gc(p0, descr=nextdescr) - setfield_gc(p2, i1, descr=valuedescr) - p3 = new_with_vtable(ConstClass(node_vtable)) - jump(p3) - """ - boxes, getnode = self.find_nodes(ops, - 'Virtual(node_vtable, nextdescr=Not)') - assert not getnode(boxes.p0).escaped - assert getnode(boxes.p1).escaped - assert getnode(boxes.p2).escaped - assert getnode(boxes.p0).fromstart - assert getnode(boxes.p1).fromstart - assert getnode(boxes.p2).fromstart - - def test_find_nodes_escape(self): - ops = """ - [p0] - p1 = getfield_gc(p0, descr=nextdescr) - p2 = getfield_gc(p1, descr=nextdescr) - i0 = getfield_gc(p2, descr=valuedescr) - i1 = int_sub(i0, 1) - escape(p1) - p3 = getfield_gc(p0, descr=nextdescr) - setfield_gc(p3, i1, descr=valuedescr) - p4 = getfield_gc(p1, descr=nextdescr) - setfield_gc(p4, i1, descr=valuedescr) - p5 = new_with_vtable(ConstClass(node_vtable)) - jump(p5) - """ - boxes, getnode = self.find_nodes(ops, - 'Virtual(node_vtable, nextdescr=Not)') - assert not getnode(boxes.p0).escaped - assert getnode(boxes.p1).escaped - assert getnode(boxes.p2).escaped # forced by p1 - assert getnode(boxes.p3).escaped # forced because p3 == p1 - assert getnode(boxes.p4).escaped # forced by p1 - assert getnode(boxes.p0).fromstart - assert getnode(boxes.p1).fromstart - assert getnode(boxes.p2).fromstart - assert getnode(boxes.p3).fromstart - assert not getnode(boxes.p4).fromstart - - def test_find_nodes_new_1(self): - ops = """ - [p1] - p2 = new_with_vtable(ConstClass(node_vtable)) - jump(p2) - """ - boxes, getnode = self.find_nodes(ops, 'Virtual(node_vtable)') - - boxp1 = getnode(boxes.p1) - boxp2 = getnode(boxes.p2) - assert not boxp1.escaped - assert not boxp2.escaped - - assert not boxp1.origfields - assert not boxp1.curfields - assert not boxp2.origfields - assert not boxp2.curfields - - assert boxp1.fromstart - assert not boxp2.fromstart - - assert boxp1.knownclsbox is None - assert boxp2.knownclsbox.getaddr() == self.node_vtable_adr - - def test_find_nodes_new_2(self): - ops = """ - [i1, p1] - p2 = new_with_vtable(ConstClass(node_vtable)) - p3 = new_with_vtable(ConstClass(node_vtable2)) - setfield_gc(p2, p3, descr=nextdescr) - setfield_gc(p3, i1, descr=valuedescr) - jump(i1, p2) - """ - self.find_nodes(ops, - '''Not, - Virtual(node_vtable, - nextdescr=Virtual(node_vtable2, - valuedescr=Not))''') - - def test_find_nodes_new_3(self): - ops = """ - [sum, p1] - guard_class(p1, ConstClass(node_vtable)) [] - i1 = getfield_gc(p1, descr=valuedescr) - i2 = int_sub(i1, 1) - sum2 = int_add(sum, i1) - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p2, i2, descr=valuedescr) - p3 = new_with_vtable(ConstClass(node_vtable2)) - setfield_gc(p2, p3, descr=nextdescr) - jump(sum2, p2) - """ - boxes, getnode = self.find_nodes( - ops, - '''Not, - Virtual(node_vtable, - valuedescr=Not, - nextdescr=Virtual(node_vtable2))''', - boxkinds={'sum': BoxInt, 'sum2': BoxInt}) - assert getnode(boxes.sum) is not getnode(boxes.sum2) - assert getnode(boxes.p1) is not getnode(boxes.p2) - - boxp1 = getnode(boxes.p1) - boxp2 = getnode(boxes.p2) - boxp3 = getnode(boxes.p3) - assert not boxp1.escaped - assert not boxp2.escaped - assert not boxp3.escaped - - assert not boxp1.curfields - assert boxp1.origfields[self.valuedescr] is getnode(boxes.i1) - assert not boxp2.origfields - assert boxp2.curfields[self.nextdescr] is boxp3 - - assert boxp1.fromstart - assert not boxp2.fromstart - assert not boxp3.fromstart - - assert boxp2.knownclsbox.getaddr() == self.node_vtable_adr - assert boxp3.knownclsbox.getaddr() == self.node_vtable_adr2 - - def test_find_nodes_new_aliasing_0(self): - ops = """ - [p1, p2] - p3 = new_with_vtable(ConstClass(node_vtable)) - jump(p3, p3) - """ - # both p1 and p2 must be NotSpecNodes; it's not possible to pass - # the same Virtual both in p1 and p2 (at least so far). - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_new_aliasing_1(self): - ops = """ - [sum, p1] - guard_class(p1, ConstClass(node_vtable)) [] - p3 = getfield_gc(p1, descr=nextdescr) - guard_class(p3, ConstClass(node_vtable)) [] - i1 = getfield_gc(p1, descr=valuedescr) - i2 = int_sub(i1, 1) - sum2 = int_add(sum, i1) - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p2, i2, descr=valuedescr) - setfield_gc(p2, p2, descr=nextdescr) - jump(sum2, p2) - """ - # the issue is the cycle "p2->p2", which cannot be represented - # with SpecNodes so far - self.find_nodes(ops, 'Not, Not', - boxkinds={'sum': BoxInt, 'sum2': BoxInt}) - - def test_find_nodes_new_aliasing_2(self): - ops = """ - [p1, p2] - escape(p2) - p3 = new_with_vtable(ConstClass(node_vtable)) - jump(p3, p3) - """ - # both p1 and p2 must be NotSpecNodes; it's not possible to pass - # in p1 a Virtual and not in p2, as they both come from the same p3. - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_new_mismatch(self): - ops = """ - [p1] - guard_class(p1, ConstClass(node_vtable)) [] - p2 = new_with_vtable(ConstClass(node_vtable2)) - jump(p2) - """ - # this is not a valid loop at all, because of the mismatch - # between the produced and the consumed class. - py.test.raises(InvalidLoop, self.find_nodes, ops, None) - - def test_find_nodes_new_aliasing_mismatch(self): - ops = """ - [p0, p1] - guard_class(p0, ConstClass(node_vtable)) [] - guard_class(p1, ConstClass(node_vtable2)) [] - p2 = new_with_vtable(ConstClass(node_vtable2)) - jump(p2, p2) - """ - # this is also not really a valid loop, but it's not detected - # because p2 is passed more than once in the jump(). - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_new_escapes(self): - ops = """ - [p0] - escape(p0) - p1 = new_with_vtable(ConstClass(node_vtable)) - jump(p1) - """ - self.find_nodes(ops, 'Not') - - def test_find_nodes_new_unused(self): - ops = """ - [p0] - p1 = new_with_vtable(ConstClass(node_vtable)) - p2 = new_with_vtable(ConstClass(node_vtable)) - p3 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, p2, descr=nextdescr) - setfield_gc(p2, p3, descr=nextdescr) - jump(p1) - """ - self.find_nodes(ops, ''' - Virtual(node_vtable, - nextdescr=Virtual(node_vtable, - nextdescr=Virtual(node_vtable)))''') - - def test_find_nodes_ptr_eq(self): - ops = """ - [p3, p4, p2] - p0 = new_with_vtable(ConstClass(node_vtable)) - p1 = new_with_vtable(ConstClass(node_vtable)) - guard_nonnull(p0) [] - i3 = ptr_ne(p0, NULL) - guard_true(i3) [] - i4 = ptr_eq(p0, NULL) - guard_false(i4) [] - i5 = ptr_ne(NULL, p0) - guard_true(i5) [] - i6 = ptr_eq(NULL, p0) - guard_false(i6) [] - i7 = ptr_ne(p0, p1) - guard_true(i7) [] - i8 = ptr_eq(p0, p1) - guard_false(i8) [] - i9 = ptr_ne(p0, p2) - guard_true(i9) [] - i10 = ptr_eq(p0, p2) - guard_false(i10) [] - i11 = ptr_ne(p2, p1) - guard_true(i11) [] - i12 = ptr_eq(p2, p1) - guard_false(i12) [] - jump(p0, p1, p2) - """ - self.find_nodes(ops, '''Virtual(node_vtable), - Virtual(node_vtable), - Not''') - - def test_find_nodes_call(self): - ops = """ - [i0, p2] - p0 = new_with_vtable(ConstClass(node_vtable)) - i1 = call_pure(i0, p0) # forces p0 to not be virtual - jump(i1, p0) - """ - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_default_field(self): - ops = """ - [p0] - i0 = getfield_gc(p0, descr=valuedescr) - guard_value(i0, 5) [] - p1 = new_with_vtable(ConstClass(node_vtable)) - # the field 'value' has its default value of 0 - jump(p1) - """ - # The answer must contain the 'value' field, because otherwise - # we might get incorrect results: when tracing, i0 was 5. - self.find_nodes(ops, 'Virtual(node_vtable, valuedescr=Not)') - - def test_find_nodes_nonvirtual_guard_class(self): - ops = """ - [p1] - guard_class(p1, ConstClass(node_vtable)) [p1] - jump(p1) - """ - self.find_nodes(ops, 'Not') - - def test_find_nodes_p12_simple(self): - ops = """ - [p1] - i3 = getfield_gc(p1, descr=valuedescr) - escape(i3) - jump(p1) - """ - self.find_nodes(ops, 'Not') - - def test_find_nodes_p123_simple(self): - ops = """ - [i1, p2, p3] - i3 = getfield_gc(p3, descr=valuedescr) - escape(i3) - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i1, descr=valuedescr) - jump(i1, p1, p2) - """ - # We cannot track virtuals that survive for more than two iterations. - self.find_nodes(ops, 'Not, Not, Not') - - def test_find_nodes_p1234_simple(self): - ops = """ - [i1, p2, p3, p4] - i4 = getfield_gc(p4, descr=valuedescr) - escape(i4) - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i1, descr=valuedescr) - jump(i1, p1, p2, p3) - """ - # We cannot track virtuals that survive for more than two iterations. - self.find_nodes(ops, 'Not, Not, Not, Not') - - def test_find_nodes_p123_guard_class(self): - ops = """ - [i1, p2, p3] - guard_class(p3, ConstClass(node_vtable)) [i1, p2, p3] - i3 = getfield_gc(p3, descr=valuedescr) - escape(i3) - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i1, descr=valuedescr) - jump(i1, p1, p2) - """ - # We cannot track virtuals that survive for more than two iterations. - self.find_nodes(ops, 'Not, Not, Not') - - def test_find_nodes_p123_rec(self): - ops = """ - [i1, p2, p0d] - p3 = getfield_gc(p0d, descr=nextdescr) - i3 = getfield_gc(p3, descr=valuedescr) - escape(i3) - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i1, descr=valuedescr) - p0c = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p0c, p2, descr=nextdescr) - jump(i1, p1, p0c) - """ - # We cannot track virtuals that survive for more than two iterations. - self.find_nodes(ops, '''Not, - Not, - Virtual(node_vtable, nextdescr=Not)''') - - def test_find_nodes_setfield_bug(self): - ops = """ - [p1, p2] - escape(p1) - setfield_gc(p1, p2, descr=nextdescr) - p3 = new_with_vtable(ConstClass(node_vtable)) - jump(p1, p3) - """ - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_array_virtual_1(self): - ops = """ - [i1, p2] - i2 = getarrayitem_gc(p2, 1, descr=arraydescr) - escape(i2) - p3 = new_array(3, descr=arraydescr) - setarrayitem_gc(p3, 1, i1, descr=arraydescr) - jump(i1, p3) - """ - self.find_nodes(ops, 'Not, VArray(arraydescr, Not, Not, Not)') - - def test_find_nodes_array_virtual_2(self): - ops = """ - [i1, p2] - i2 = arraylen_gc(p2, descr=arraydescr) - escape(i2) - p3 = new_array(3, descr=arraydescr) - setarrayitem_gc(p3, 1, i1, descr=arraydescr) - jump(i1, p3) - """ - self.find_nodes(ops, 'Not, VArray(arraydescr, Not, Not, Not)') - - def test_find_nodes_array_virtual_3(self): - ops = """ - [pvalue1, p2] - pvalue2 = new_with_vtable(ConstClass(node_vtable2)) - ps2 = getarrayitem_gc(p2, 1, descr=arraydescr) - setfield_gc(ps2, pvalue2, descr=nextdescr) - ps3 = getarrayitem_gc(p2, 1, descr=arraydescr) - pvalue3 = getfield_gc(ps3, descr=nextdescr) - ps1 = new_with_vtable(ConstClass(node_vtable)) - p3 = new_array(3, descr=arraydescr) - setarrayitem_gc(p3, 1, ps1, descr=arraydescr) - jump(pvalue3, p3) - """ - self.find_nodes(ops, 'Virtual(node_vtable2), VArray(arraydescr, Not, Virtual(node_vtable), Not)') - - def test_find_nodes_array_virtual_empty(self): - ops = """ - [i1, p2] - p3 = new_array(3, descr=arraydescr) - jump(i1, p3) - """ - self.find_nodes(ops, 'Not, VArray(arraydescr, Not, Not, Not)') - - def test_find_nodes_array_nonvirtual_1(self): - ops = """ - [i1, p2] - i2 = getarrayitem_gc(p2, i1, descr=arraydescr) - escape(i2) - p3 = new_array(4, descr=arraydescr) - setarrayitem_gc(p3, i1, i2, descr=arraydescr) - jump(i1, p3) - """ - # Does not work because of the variable index, 'i1'. - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_array_forced_1(self): - ops = """ - [p1, i1] - p2 = new_array(1, descr=arraydescr) - setarrayitem_gc(p2, 0, p1, descr=arraydescr) - p3 = getarrayitem_gc(p2, i1, descr=arraydescr) - p4 = new_with_vtable(ConstClass(node_vtable)) - jump(p4, i1) - """ - # escapes because getarrayitem_gc uses a non-constant index - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_arrayitem_forced(self): - ops = """ - [p1] - p2 = new_array(1, descr=arraydescr) - escape(p2) - p4 = new_with_vtable(ConstClass(node_vtable)) - setarrayitem_gc(p2, 0, p4, descr=arraydescr) - jump(p4) - """ - self.find_nodes(ops, 'Not') - - def test_find_nodes_struct_virtual_1(self): - ops = """ - [i1, p2] - i2 = getfield_gc(p2, descr=adescr) - escape(i2) - p3 = new(descr=ssize) - setfield_gc(p3, i1, descr=adescr) - jump(i1, p3) - """ - self.find_nodes(ops, 'Not, VStruct(ssize, adescr=Not)') - - def test_find_nodes_struct_nonvirtual_1(self): - ops = """ - [i1, p2] - i2 = getfield_gc(p2, descr=adescr) - escape(p2) - p3 = new(descr=ssize) - setfield_gc(p3, i1, descr=adescr) - jump(i1, p3) - """ - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_guard_value_constant(self): - ops = """ - [p1] - guard_value(p1, ConstPtr(myptr)) [] - jump(ConstPtr(myptr)) - """ - self.find_nodes(ops, 'Constant(myptr)') - - def test_find_nodes_guard_value_constant_mismatch(self): - ops = """ - [p1] - guard_value(p1, ConstPtr(myptr2)) [] - jump(ConstPtr(myptr)) - """ - py.test.raises(InvalidLoop, self.find_nodes, ops, None) - - def test_find_nodes_guard_value_escaping_constant(self): - ops = """ - [p1] - escape(p1) - guard_value(p1, ConstPtr(myptr)) [] - jump(ConstPtr(myptr)) - """ - self.find_nodes(ops, 'Constant(myptr)') - - def test_find_nodes_guard_value_same_as_constant(self): - ops = """ - [p1] - guard_value(p1, ConstPtr(myptr)) [] - p2 = same_as(ConstPtr(myptr)) - jump(p2) - """ - self.find_nodes(ops, 'Constant(myptr)') - - def test_find_nodes_store_into_loop_constant_1(self): - ops = """ - [i0, p1, p4] - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, p2, descr=nextdescr) - jump(i0, p1, p2) - """ - self.find_nodes(ops, 'Not, Not, Not') - - def test_find_nodes_store_into_loop_constant_2(self): - ops = """ - [i0, p4, p1] - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, p2, descr=nextdescr) - jump(i0, p2, p1) - """ - self.find_nodes(ops, 'Not, Not, Not') - - def test_find_nodes_store_into_loop_constant_3(self): - ops = """ - [i0, p1] - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, p2, descr=nextdescr) - call(i0) - jump(i0, p1) - """ - self.find_nodes(ops, 'Not, Not') - - def test_find_nodes_arithmetic_propagation_bug_0(self): - ops = """ - [p1] - i1 = getarrayitem_gc(p1, 0, descr=arraydescr) - escape(i1) - i2 = int_add(0, 1) - p2 = new_array(i2, descr=arraydescr) - i3 = escape() - setarrayitem_gc(p2, 0, i3, descr=arraydescr) - jump(p2) - """ - self.find_nodes(ops, 'VArray(arraydescr, Not)') - - def test_find_nodes_arithmetic_propagation_bug_1(self): - ops = """ - [p1] - i1 = getarrayitem_gc(p1, 0, descr=arraydescr) - escape(i1) - i2 = same_as(1) - p2 = new_array(i2, descr=arraydescr) - setarrayitem_gc(p2, 0, 5) - jump(p2) - """ - self.find_nodes(ops, 'VArray(arraydescr, Not)') - - def test_find_nodes_arithmetic_propagation_bug_2(self): - ops = """ - [p1] - i0 = int_sub(17, 17) - i1 = getarrayitem_gc(p1, i0, descr=arraydescr) - escape(i1) - i2 = int_add(0, 1) - p2 = new_array(i2, descr=arraydescr) - i3 = escape() - setarrayitem_gc(p2, i0, i3, descr=arraydescr) - jump(p2) - """ - self.find_nodes(ops, 'VArray(arraydescr, Not)') - - def test_find_nodes_arithmetic_propagation_bug_3(self): - ops = """ - [p1] - i1 = getarrayitem_gc(p1, 0, descr=arraydescr) - escape(i1) - p3 = new_array(1, descr=arraydescr) - i2 = arraylen_gc(p3, descr=arraydescr) - p2 = new_array(i2, descr=arraydescr) - i3 = escape() - setarrayitem_gc(p2, 0, i3, descr=arraydescr) - jump(p2) - """ - self.find_nodes(ops, 'VArray(arraydescr, Not)') - - def test_find_nodes_bug_1(self): - ops = """ - [p12] - guard_nonnull(p12) [] - guard_class(p12, ConstClass(node_vtable)) [] - guard_class(p12, ConstClass(node_vtable)) [] - i22 = getfield_gc_pure(p12, descr=valuedescr) - escape(i22) - guard_nonnull(p12) [] - guard_class(p12, ConstClass(node_vtable)) [] - guard_class(p12, ConstClass(node_vtable)) [] - i29 = getfield_gc_pure(p12, descr=valuedescr) - i31 = int_add_ovf(i29, 1) - guard_no_overflow() [] - p33 = new_with_vtable(ConstClass(node_vtable)) # NODE - setfield_gc(p33, i31, descr=valuedescr) - # - p35 = new_array(1, descr=arraydescr3) # Array(NODE) - setarrayitem_gc(p35, 0, p33, descr=arraydescr3) - p38 = new_with_vtable(ConstClass(u_vtable)) # U - setfield_gc(p38, p35, descr=onedescr) - guard_nonnull(p38) [] - guard_nonnull(p38) [] - guard_class(p38, ConstClass(u_vtable)) [] - p42 = getfield_gc(p38, descr=onedescr) # Array(NODE) - i43 = arraylen_gc(p42, descr=arraydescr3) - i45 = int_sub(i43, 0) - p46 = new(descr=tsize) # T - setfield_gc(p46, i45, descr=cdescr) - p47 = new_array(i45, descr=arraydescr3) # Array(NODE) - setfield_gc(p46, p47, descr=ddescr) - i48 = int_lt(0, i43) - guard_true(i48) [] - p49 = getarrayitem_gc(p42, 0, descr=arraydescr3) # NODE - p50 = getfield_gc(p46, descr=ddescr) # Array(NODE) - setarrayitem_gc(p50, 0, p49, descr=arraydescr3) - i52 = int_lt(1, i43) - guard_false(i52) [] - i53 = getfield_gc(p46, descr=cdescr) - i55 = int_ne(i53, 1) - guard_false(i55) [] - p56 = getfield_gc(p46, descr=ddescr) # Array(NODE) - p58 = getarrayitem_gc(p56, 0, descr=arraydescr3) # NODE - guard_nonnull(p38) [] - jump(p58) - """ - self.find_nodes(ops, 'Virtual(node_vtable, valuedescr=Not)') - - # ------------------------------ - # Bridge tests - - def find_bridge(self, ops, inputspectext, outputspectext, boxkinds=None, - mismatch=False): - assert boxkinds is None or isinstance(boxkinds, dict) - inputspecnodes = self.unpack_specnodes(inputspectext) - outputspecnodes = self.unpack_specnodes(outputspectext) - bridge = self.parse(ops, boxkinds=boxkinds) - bridge_specialization_finder = BridgeSpecializationFinder(self.cpu) - bridge_specialization_finder.find_nodes_bridge(bridge, inputspecnodes) - matches = bridge_specialization_finder.bridge_matches(outputspecnodes) - if mismatch: - assert not matches - else: - assert matches - - def test_bridge_simple(self): - ops = """ - [i0] - i1 = int_add(i0, 1) - jump(i1) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Not', 'Virtual(node_vtable)', mismatch=True) - - def test_bridge_simple_known_class(self): - ops = """ - [p0] - setfield_gc(p0, 123, descr=valuedescr) - jump(p0) - """ - self.find_bridge(ops, 'Not', 'Not') - - def test_bridge_simple_constant(self): - ops = """ - [] - jump(ConstPtr(myptr)) - """ - self.find_bridge(ops, '', 'Not') - self.find_bridge(ops, '', 'Constant(myptr)') - self.find_bridge(ops, '', 'Constant(myptr2)', mismatch=True) - - def test_bridge_simple_constant_mismatch(self): - ops = """ - [p0] - jump(p0) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Not', 'Constant(myptr)', mismatch=True) - - def test_bridge_simple_virtual_1(self): - ops = """ - [i0] - p0 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p0, i0, descr=valuedescr) - jump(p0) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Not', 'Virtual(node_vtable, valuedescr=Not)') - self.find_bridge(ops, 'Not', - '''Virtual(node_vtable, - valuedescr=Not, - nextdescr=Not)''') - # - self.find_bridge(ops, 'Not', 'Virtual(node_vtable)', - mismatch=True) # missing valuedescr - self.find_bridge(ops, 'Not', 'Virtual(node_vtable, nextdescr=Not)', - mismatch=True) # missing valuedescr - self.find_bridge(ops, 'Not', 'Virtual(node_vtable2, valuedescr=Not)', - mismatch=True) # bad class - - def test_bridge_simple_virtual_struct(self): - ops = """ - [i0] - p0 = new(descr=ssize) - setfield_gc(p0, i0, descr=adescr) - jump(p0) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Not', 'VStruct(ssize, adescr=Not)') - - def test_bridge_simple_virtual_struct_non_unique(self): - ops = """ - [i0] - p0 = new(descr=ssize) - setfield_gc(p0, i0, descr=adescr) - jump(p0, p0) - """ - self.find_bridge(ops, 'Not', 'Not, Not') - self.find_bridge(ops, 'Not', 'VStruct(ssize), VStruct(ssize)', - mismatch=True) - - - def test_bridge_simple_virtual_2(self): - ops = """ - [p0] - setfield_gc(p0, 123, descr=valuedescr) - jump(p0) - """ - self.find_bridge(ops, 'Virtual(node_vtable)', 'Not') - self.find_bridge(ops, 'Virtual(node_vtable)', - 'Virtual(node_vtable, valuedescr=Not)') - self.find_bridge(ops, 'Virtual(node_vtable, valuedescr=Not)', - 'Virtual(node_vtable, valuedescr=Not)') - self.find_bridge(ops, 'Virtual(node_vtable, valuedescr=Not)', - '''Virtual(node_vtable, - valuedescr=Not, - nextdescr=Not)''') - self.find_bridge(ops, '''Virtual(node_vtable, - valuedescr=Not, - nextdescr=Not)''', - '''Virtual(node_vtable, - valuedescr=Not, - nextdescr=Not)''') - # - self.find_bridge(ops, 'Virtual(node_vtable)', 'Virtual(node_vtable)', - mismatch=True) # because of missing valuedescr - self.find_bridge(ops, 'Virtual(node_vtable)', - 'Virtual(node_vtable2, valuedescr=Not)', - mismatch=True) # bad class - - def test_bridge_virtual_mismatch_1(self): - ops = """ - [i0] - p0 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p0, i0, descr=valuedescr) - jump(p0, p0) - """ - self.find_bridge(ops, 'Not', 'Not, Not') - # - self.find_bridge(ops, 'Not', - '''Virtual(node_vtable, valuedescr=Not), - Virtual(node_vtable, valuedescr=Not)''', - mismatch=True) # duplicate p0 - - def test_bridge_guard_class(self): - ops = """ - [p1] - p2 = getfield_gc(p1, descr=nextdescr) - guard_class(p2, ConstClass(node_vtable)) [] - jump(p2) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Virtual(node_vtable2, nextdescr=Not)', 'Not') - self.find_bridge(ops, - '''Virtual(node_vtable, - nextdescr=Virtual(node_vtable, - nextdescr=Not))''', - '''Virtual(node_vtable, - nextdescr=Not)''') - # - self.find_bridge(ops, 'Not', 'Virtual(node_vtable)', - mismatch=True) - - def test_bridge_unused(self): - ops = """ - [] - p1 = new_with_vtable(ConstClass(node_vtable)) - p2 = new_with_vtable(ConstClass(node_vtable)) - p3 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, p2, descr=nextdescr) - setfield_gc(p2, p3, descr=nextdescr) - jump(p1) - """ - self.find_bridge(ops, '', - '''Not''') - self.find_bridge(ops, '', - '''Virtual(node_vtable, - nextdescr=Not)''') - self.find_bridge(ops, '', - '''Virtual(node_vtable, - nextdescr=Virtual(node_vtable, - nextdescr=Not))''') - self.find_bridge(ops, '', - '''Virtual(node_vtable, - nextdescr=Virtual(node_vtable, - nextdescr=Virtual(node_vtable)))''') - self.find_bridge(ops, '', - '''Virtual(node_vtable, - nextdescr=Virtual(node_vtable, - nextdescr=Virtual(node_vtable, - nextdescr=Not)))''') - - def test_bridge_to_finish(self): - ops = """ - [i1] - i2 = int_add(i1, 5) - finish(i2) - """ - self.find_bridge(ops, 'Not', 'Not') - - def test_bridge_virtual_to_finish(self): - ops = """ - [i1] - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i1, descr=valuedescr) - finish(p1) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Not', - 'Virtual(node_vtable, valuedescr=Not)', - mismatch=True) - - def test_bridge_array_virtual_1(self): - ops = """ - [i1] - p1 = new_array(3, descr=arraydescr) - setarrayitem_gc(p1, 0, i1, descr=arraydescr) - jump(p1) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Not', 'VArray(arraydescr, Not, Not, Not)') - - def test_bridge_array_virtual_size_mismatch(self): - ops = """ - [i1] - p1 = new_array(5, descr=arraydescr) - setarrayitem_gc(p1, 0, i1, descr=arraydescr) - jump(p1) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Not', 'VArray(arraydescr, Not, Not, Not)', - mismatch=True) - - def test_bridge_array_virtual_2(self): - ops = """ - [i1] - p1 = new_array(3, descr=arraydescr) - setarrayitem_gc(p1, 0, i1, descr=arraydescr) - escape(p1) - jump(p1) - """ - self.find_bridge(ops, 'Not', 'Not') - self.find_bridge(ops, 'Not', 'VArray(arraydescr, Not, Not, Not)', - mismatch=True) - - def test_bridge_nested_structs(self): - ops = """ - [] - p1 = new_with_vtable(ConstClass(node_vtable)) - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, p2, descr=nextdescr) - jump(p1) - """ - self.find_bridge(ops, '', 'Not') - self.find_bridge(ops, '', 'Virtual(node_vtable, nextdescr=Not)') - self.find_bridge(ops, '', - 'Virtual(node_vtable, nextdescr=Virtual(node_vtable))') - self.find_bridge(ops, '', - 'Virtual(node_vtable, nextdescr=Virtual(node_vtable2))', - mismatch=True) - - -class TestLLtype(BaseTestOptimizeFindNode, LLtypeMixin): - pass - -##class TestOOtype(BaseTestOptimizeFindNode, OOtypeMixin): -## def test_find_nodes_instanceof(self): -## ops = """ -## [i0] -## p0 = new_with_vtable(ConstClass(node_vtable)) -## i1 = instanceof(p0, descr=nodesize) -## jump(i1) -## """ -## boxes, getnode = self.find_nodes(ops, 'Not') -## assert not getnode(boxes.p0).escaped diff --git a/pypy/jit/metainterp/test/test_optimizeopt.py b/pypy/jit/metainterp/test/test_optimizeopt.py index e4e2627de9..61439a750d 100644 --- a/pypy/jit/metainterp/test/test_optimizeopt.py +++ b/pypy/jit/metainterp/test/test_optimizeopt.py @@ -1,26 +1,19 @@ import py from pypy.rlib.objectmodel import instantiate -from pypy.jit.metainterp.test.test_optimizefindnode import (LLtypeMixin, - #OOtypeMixin, - BaseTest) -from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder +from pypy.jit.metainterp.test.test_optimizeutil import (LLtypeMixin, + #OOtypeMixin, + BaseTest) import pypy.jit.metainterp.optimizeopt.optimizer as optimizeopt import pypy.jit.metainterp.optimizeopt.virtualize as virtualize from pypy.jit.metainterp.optimizeopt import optimize_loop_1 from pypy.jit.metainterp.optimizeutil import InvalidLoop from pypy.jit.metainterp.history import AbstractDescr, ConstInt, BoxInt +from pypy.jit.metainterp.history import TreeLoop, LoopToken from pypy.jit.metainterp.jitprof import EmptyProfiler from pypy.jit.metainterp import executor, compile, resume, history from pypy.jit.metainterp.resoperation import rop, opname, ResOperation from pypy.jit.tool.oparser import pure_parse - -##class FakeFrame(object): -## parent_resumedata_snapshot = None -## parent_resumedata_frame_info_list = None - -## def __init__(self, code="", pc=0): -## self.jitcode = code -## self.pc = pc +from pypy.jit.metainterp.test.test_optimizebasic import equaloplists class Fake(object): failargs_limit = 1000 @@ -129,90 +122,7 @@ def test_descrlist_dict(): assert not optimizeutil.descrlist_eq([FakeDescr()], [FakeDescr()]) - # ____________________________________________________________ - -def equaloplists(oplist1, oplist2, strict_fail_args=True, remap={}): - # try to use the full width of the terminal to display the list - # unfortunately, does not work with the default capture method of py.test - # (which is fd), you you need to use either -s or --capture=sys, else you - # get the standard 80 columns width - totwidth = py.io.get_terminal_width() - width = totwidth / 2 - 1 - print ' Comparing lists '.center(totwidth, '-') - print '%s| %s' % ('optimized'.center(width), 'expected'.center(width)) - for op1, op2 in zip(oplist1, oplist2): - txt1 = str(op1) - txt2 = str(op2) - while txt1 or txt2: - print '%s| %s' % (txt1[:width].ljust(width), txt2[:width]) - txt1 = txt1[width:] - txt2 = txt2[width:] - assert op1.getopnum() == op2.getopnum() - assert op1.numargs() == op2.numargs() - for i in range(op1.numargs()): - x = op1.getarg(i) - y = op2.getarg(i) - assert x == remap.get(y, y) - if op2.result in remap: - assert op1.result == remap[op2.result] - else: - remap[op2.result] = op1.result - if op1.getopnum() != rop.JUMP: # xxx obscure - assert op1.getdescr() == op2.getdescr() - if op1.getfailargs() or op2.getfailargs(): - assert len(op1.getfailargs()) == len(op2.getfailargs()) - if strict_fail_args: - for x, y in zip(op1.getfailargs(), op2.getfailargs()): - assert x == remap.get(y, y) - else: - fail_args1 = set(op1.getfailargs()) - fail_args2 = set([remap.get(y, y) for y in op2.getfailargs()]) - assert fail_args1 == fail_args2 - assert len(oplist1) == len(oplist2) - print '-'*57 - return True - -def test_equaloplists(): - ops = """ - [i0] - i1 = int_add(i0, 1) - i2 = int_add(i1, 1) - guard_true(i1) [i2] - jump(i1) - """ - namespace = {} - loop1 = pure_parse(ops, namespace=namespace) - loop2 = pure_parse(ops, namespace=namespace) - loop3 = pure_parse(ops.replace("i2 = int_add", "i2 = int_sub"), - namespace=namespace) - assert equaloplists(loop1.operations, loop2.operations) - py.test.raises(AssertionError, - "equaloplists(loop1.operations, loop3.operations)") - -def test_equaloplists_fail_args(): - ops = """ - [i0] - i1 = int_add(i0, 1) - i2 = int_add(i1, 1) - guard_true(i1) [i2, i1] - jump(i1) - """ - namespace = {} - loop1 = pure_parse(ops, namespace=namespace) - loop2 = pure_parse(ops.replace("[i2, i1]", "[i1, i2]"), - namespace=namespace) - py.test.raises(AssertionError, - "equaloplists(loop1.operations, loop2.operations)") - assert equaloplists(loop1.operations, loop2.operations, - strict_fail_args=False) - loop3 = pure_parse(ops.replace("[i2, i1]", "[i2, i0]"), - namespace=namespace) - py.test.raises(AssertionError, - "equaloplists(loop1.operations, loop3.operations)") - -# ____________________________________________________________ - class Storage(compile.ResumeGuardDescr): "for tests." def __init__(self, metainterp_sd=None, original_greenkey=None): @@ -222,6 +132,10 @@ class Storage(compile.ResumeGuardDescr): op.setfailargs(boxes) def __eq__(self, other): return type(self) is type(other) # xxx obscure + def clone_if_mutable(self): + res = Storage(self.metainterp_sd, self.original_greenkey) + self.copy_all_attrbutes_into(res) + return res def _sortboxes(boxes): _kind2count = {history.INT: 1, history.REF: 2, history.FLOAT: 3} @@ -238,31 +152,25 @@ class BaseTestOptimizeOpt(BaseTest): descr.rd_snapshot = resume.Snapshot(None, _sortboxes(fail_args)) return descr - def assert_equal(self, optimized, expected): + def assert_equal(self, optimized, expected, text_right=None): assert len(optimized.inputargs) == len(expected.inputargs) remap = {} for box1, box2 in zip(optimized.inputargs, expected.inputargs): assert box1.__class__ == box2.__class__ remap[box2] = box1 assert equaloplists(optimized.operations, - expected.operations, False, remap) + expected.operations, False, remap, text_right) - def optimize_loop(self, ops, spectext, optops, checkspecnodes=True): + def optimize_loop(self, ops, optops, expected_preamble=None): loop = self.parse(ops) - # - if checkspecnodes: - # verify that 'spectext' is indeed what optimizefindnode would - # compute for this loop - cpu = self.cpu - perfect_specialization_finder = PerfectSpecializationFinder(cpu) - perfect_specialization_finder.find_nodes_loop(loop) - self.check_specnodes(loop.token.specnodes, spectext) - else: - # for cases where we want to see how optimizeopt behaves with - # combinations different from the one computed by optimizefindnode - loop.token.specnodes = self.unpack_specnodes(spectext) + expected = self.parse(optops) + if expected_preamble: + expected_preamble = self.parse(expected_preamble) # self.loop = loop + loop.preamble = TreeLoop('preamble') + loop.preamble.inputargs = loop.inputargs + loop.preamble.token = LoopToken() metainterp_sd = FakeMetaInterpStaticData(self.cpu, self.jit_ffi) if hasattr(self, 'vrefinfo'): metainterp_sd.virtualref_info = self.vrefinfo @@ -270,28 +178,70 @@ class BaseTestOptimizeOpt(BaseTest): metainterp_sd.callinfocollection = self.callinfocollection optimize_loop_1(metainterp_sd, loop) # - expected = self.parse(optops) + + print + print loop.preamble.inputargs + print '\n'.join([str(o) for o in loop.preamble.operations]) + print + print loop.inputargs print '\n'.join([str(o) for o in loop.operations]) + print + self.assert_equal(loop, expected) - return loop + if expected_preamble: + self.assert_equal(loop.preamble, expected_preamble, + text_right='expected preamble') + return loop class OptimizeOptTest(BaseTestOptimizeOpt): + def setup_method(self, meth=None): + class FailDescr(compile.ResumeGuardDescr): + oparse = None + def _oparser_uses_descr_of_guard(self, oparse, fail_args): + # typically called 3 times: once when parsing 'ops', + # once when parsing 'preamble', once when parsing 'expected'. + self.oparse = oparse + self.rd_frame_info_list, self.rd_snapshot = snapshot(fail_args) + def _clone_if_mutable(self): + assert self is fdescr + return fdescr2 + def __repr__(self): + if self is fdescr: + return 'fdescr' + if self is fdescr2: + return 'fdescr2' + return compile.ResumeGuardDescr.__repr__(self) + # + def snapshot(fail_args, got=[]): + if not got: # only the first time, i.e. when parsing 'ops' + rd_frame_info_list = resume.FrameInfo(None, "code", 11) + rd_snapshot = resume.Snapshot(None, fail_args) + got.append(rd_frame_info_list) + got.append(rd_snapshot) + return got + # + fdescr = instantiate(FailDescr) + self.namespace['fdescr'] = fdescr + fdescr2 = instantiate(FailDescr) + self.namespace['fdescr2'] = fdescr2 + + def teardown_method(self, meth): + self.namespace.pop('fdescr', None) + self.namespace.pop('fdescr2', None) + + def test_simple(self): ops = """ - [i] - i0 = int_sub(i, 1) - guard_value(i0, 0) [i0] - jump(i) - """ - expected = """ - [i] - i0 = int_sub(i, 1) - guard_value(i0, 0) [i0] - jump(1) + [] + f = escape() + f0 = float_sub(f, 1.0) + guard_value(f0, 0.0) [f0] + escape(f) + jump() """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, ops) def test_constant_propagate(self): ops = """ @@ -308,7 +258,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [] jump() """ - self.optimize_loop(ops, '', expected) + self.optimize_loop(ops, expected) def test_constant_propagate_ovf(self): ops = """ @@ -326,7 +276,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [] jump() """ - self.optimize_loop(ops, '', expected) + self.optimize_loop(ops, expected) def test_constfold_all(self): from pypy.jit.backend.llgraph.llimpl import TYPES # xxx fish @@ -360,7 +310,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(%d) jump() """ % expected_value - self.optimize_loop(ops, '', expected) + self.optimize_loop(ops, expected) # ---------- @@ -371,12 +321,16 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_class(p0, ConstClass(node_vtable)) [] jump(p0) """ - expected = """ + preamble = """ [p0] guard_class(p0, ConstClass(node_vtable)) [] jump(p0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [p0] + jump(p0) + """ + self.optimize_loop(ops, expected, expected_preamble=preamble) def test_remove_guard_class_2(self): ops = """ @@ -392,7 +346,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p0) jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_remove_guard_class_constant(self): ops = """ @@ -405,7 +359,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i0] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_constant_boolrewrite_lt(self): ops = """ @@ -416,13 +370,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_false(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 0) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, expected_preamble=preamble) def test_constant_boolrewrite_gt(self): ops = """ @@ -433,13 +391,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_false(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_gt(i0, 0) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, expected_preamble=preamble) def test_constant_boolrewrite_reflex(self): ops = """ @@ -450,13 +412,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_gt(i0, 0) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, expected_preamble=preamble) def test_constant_boolrewrite_reflex_invers(self): ops = """ @@ -467,13 +433,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_false(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_gt(i0, 0) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, expected_preamble=preamble) def test_remove_consecutive_guard_value_constfold(self): ops = """ @@ -493,19 +463,19 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(3) jump() """ - self.optimize_loop(ops, '', expected) + self.optimize_loop(ops, expected) def test_remove_guard_value_if_constant(self): ops = """ [p1] guard_value(p1, ConstPtr(myptr)) [] - jump(ConstPtr(myptr)) + jump(p1) """ expected = """ [] jump() """ - self.optimize_loop(ops, 'Constant(myptr)', expected) + self.optimize_loop(ops, expected) def test_ooisnull_oononnull_1(self): ops = """ @@ -514,12 +484,52 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_nonnull(p0) [] jump(p0) """ + preamble = """ + [p0] + guard_class(p0, ConstClass(node_vtable)) [] + jump(p0) + """ expected = """ [p0] + jump(p0) + """ + self.optimize_loop(ops, expected, preamble) + + def test_guard_nonnull_class_1(self): + ops = """ + [p0] guard_class(p0, ConstClass(node_vtable)) [] + guard_nonnull(p0) [] + guard_nonnull_class(p0, ConstClass(node_vtable)) [] + jump(p0) + """ + preamble = """ + [p0] + guard_class(p0, ConstClass(node_vtable)) [] + jump(p0) + """ + expected = """ + [p0] + jump(p0) + """ + self.optimize_loop(ops, expected, preamble) + + def test_guard_nonnull_class_2(self): + ops = """ + [p0] + guard_nonnull_class(p0, ConstClass(node_vtable)) [] jump(p0) """ - self.optimize_loop(ops, 'Not', expected) + preamble = """ + [p0] + guard_nonnull_class(p0, ConstClass(node_vtable)) [] + jump(p0) + """ + expected = """ + [p0] + jump(p0) + """ + self.optimize_loop(ops, expected, preamble) def test_int_is_true_1(self): ops = """ @@ -530,13 +540,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_is_true(i0) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_int_is_true_is_zero(self): py.test.skip("XXX implement me") @@ -554,7 +568,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_ooisnull_oononnull_2(self): ops = """ @@ -563,12 +577,16 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_nonnull(p0) [] jump(p0) """ - expected = """ + preamble = """ [p0] guard_nonnull(p0) [] jump(p0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [p0] + jump(p0) + """ + self.optimize_loop(ops, expected, preamble) def test_ooisnull_on_null_ptr_1(self): ops = """ @@ -584,7 +602,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_isnull(p0) [] jump() """ - self.optimize_loop(ops, '', expected) + self.optimize_loop(ops, expected) def test_ooisnull_oononnull_via_virtual(self): ops = """ @@ -596,12 +614,16 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_nonnull(p1) [] jump(p0) """ - expected = """ + preamble = """ [p0] guard_nonnull(p0) [] jump(p0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [p0] + jump(p0) + """ + self.optimize_loop(ops, expected, preamble) def test_oois_1(self): ops = """ @@ -617,12 +639,16 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_false(i1) [] jump(p0) """ - expected = """ + preamble = """ [p0] guard_class(p0, ConstClass(node_vtable)) [] jump(p0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [p0] + jump(p0) + """ + self.optimize_loop(ops, expected, preamble) def test_nonnull_1(self): ops = """ @@ -644,7 +670,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p0, 5, descr=valuedescr) jump(p0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_const_guard_value(self): ops = """ @@ -657,7 +683,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [] jump() """ - self.optimize_loop(ops, '', expected) + self.optimize_loop(ops, expected) def test_constptr_guard_value(self): ops = """ @@ -666,7 +692,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(p1, ConstPtr(myptr)) [] jump() """ - self.optimize_loop(ops, '', ops) + self.optimize_loop(ops, ops) def test_guard_value_to_guard_true(self): ops = """ @@ -675,13 +701,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(i1, 1) [i] jump(i) """ - expected = """ + preamble = """ [i] i1 = int_lt(i, 3) guard_true(i1) [i] jump(i) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i] + jump(i) + """ + self.optimize_loop(ops, expected, preamble) def test_guard_value_to_guard_false(self): ops = """ @@ -690,13 +720,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(i1, 0) [i] jump(i) """ - expected = """ + preamble = """ [i] i1 = int_is_true(i) guard_false(i1) [i] jump(i) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i] + jump(i) + """ + self.optimize_loop(ops, expected, preamble) def test_guard_value_on_nonbool(self): ops = """ @@ -705,13 +739,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(i1, 0) [i] jump(i) """ - expected = """ + preamble = """ [i] i1 = int_add(i, 3) guard_value(i1, 0) [i] - jump(-3) + jump() """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [] + jump() + """ + self.optimize_loop(ops, expected, preamble) def test_int_is_true_of_bool(self): ops = """ @@ -722,13 +760,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(i4, 0) [i0, i1] jump(i0, i1) """ - expected = """ + preamble = """ [i0, i1] i2 = int_gt(i0, i1) guard_false(i2) [i0, i1] jump(i0, i1) """ - self.optimize_loop(ops, 'Not, Not', expected) + expected = """ + [i0, i1] + jump(i0, i1) + """ + self.optimize_loop(ops, expected, preamble) @@ -742,8 +784,22 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i1, descr=valuedescr) jump(i1, p1, p2) """ + preamble = """ + [i1, p2, p3] + i3 = getfield_gc(p3, descr=valuedescr) + escape(i3) + jump(i1, p2) + """ + expected = """ + [i1, p2] + i3 = getfield_gc(p2, descr=valuedescr) + escape(i3) + p3 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3, i1, descr=valuedescr) + jump(i1, p3) + """ # We cannot track virtuals that survive for more than two iterations. - self.optimize_loop(ops, 'Not, Not, Not', ops) + self.optimize_loop(ops, expected, preamble) def test_p123_nested(self): ops = """ @@ -759,7 +815,24 @@ class OptimizeOptTest(BaseTestOptimizeOpt): """ # The same as test_p123_simple, but with a virtual containing another # virtual. - self.optimize_loop(ops, 'Not, Not, Not', ops) + preamble = """ + [i1, p2, p3] + i3 = getfield_gc(p3, descr=valuedescr) + escape(i3) + jump(i1, p2) + """ + expected = """ + [i1, p2] + i3 = getfield_gc(p2, descr=valuedescr) + escape(i3) + p4 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p4, i1, descr=valuedescr) + p1sub = new_with_vtable(ConstClass(node_vtable2)) + setfield_gc(p1sub, i1, descr=valuedescr) + setfield_gc(p4, p1sub, descr=nextdescr) + jump(i1, p4) + """ + self.optimize_loop(ops, expected, preamble) def test_p123_anti_nested(self): ops = """ @@ -775,7 +848,58 @@ class OptimizeOptTest(BaseTestOptimizeOpt): """ # The same as test_p123_simple, but in the end the "old" p2 contains # a "young" virtual p2sub. Make sure it is all forced. - self.optimize_loop(ops, 'Not, Not, Not', ops) + preamble = """ + [i1, p2, p3] + p3sub = getfield_gc(p3, descr=nextdescr) + i3 = getfield_gc(p3sub, descr=valuedescr) + escape(i3) + p2sub = new_with_vtable(ConstClass(node_vtable2)) + setfield_gc(p2sub, i1, descr=valuedescr) + setfield_gc(p2, p2sub, descr=nextdescr) + jump(i1, p2, p2sub) + """ + expected = """ + [i1, p2, p2sub] + i3 = getfield_gc(p2sub, descr=valuedescr) + escape(i3) + p1 = new_with_vtable(ConstClass(node_vtable)) + p3sub = new_with_vtable(ConstClass(node_vtable2)) + setfield_gc(p3sub, i1, descr=valuedescr) + setfield_gc(p1, p3sub, descr=nextdescr) + jump(i1, p1, p3sub) + """ + self.optimize_loop(ops, expected, preamble) + + def test_dont_delay_setfields(self): + ops = """ + [p1, p2] + i1 = getfield_gc(p1, descr=nextdescr) + i2 = int_sub(i1, 1) + i2b = int_is_true(i2) + guard_true(i2b) [] + setfield_gc(p2, i2, descr=nextdescr) + p3 = new_with_vtable(ConstClass(node_vtable)) + jump(p2, p3) + """ + preamble = """ + [p1, p2] + i1 = getfield_gc(p1, descr=nextdescr) + i2 = int_sub(i1, 1) + i2b = int_is_true(i2) + guard_true(i2b) [] + setfield_gc(p2, i2, descr=nextdescr) + jump(p2, i2) + """ + expected = """ + [p2, i1] + i2 = int_sub(i1, 1) + i2b = int_is_true(i2) + guard_true(i2b) [] + p3 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3, i2, descr=nextdescr) + jump(p3, i2) + """ + self.optimize_loop(ops, expected, preamble) # ---------- @@ -794,7 +918,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): # note that 'guard_no_exception' at the very start must be kept # around: bridges may start with one. (In case of loops we could # remove it, but we probably don't care.) - expected = """ + preamble = """ [i] guard_no_exception() [] i1 = int_add(i, 3) @@ -803,7 +927,15 @@ class OptimizeOptTest(BaseTestOptimizeOpt): i3 = call(i2, descr=nonwritedescr) jump(i1) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i] + i1 = int_add(i, 3) + i2 = call(i1, descr=nonwritedescr) + guard_no_exception() [i1, i2] + i3 = call(i2, descr=nonwritedescr) + jump(i1) + """ + self.optimize_loop(ops, expected, preamble) # ---------- @@ -821,14 +953,18 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(i4, 1) [] jump(i1) """ - expected = """ + preamble = """ [i1] i2 = call(1, i1, descr=nonwritedescr) guard_no_exception() [] guard_value(i2, 1) [] jump(i1) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i1] + jump(i1) + """ + self.optimize_loop(ops, expected, preamble) # ---------- @@ -838,49 +974,44 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i, p0] i0 = getfield_gc(p0, descr=valuedescr) i1 = int_add(i0, i) - setfield_gc(p0, i1, descr=valuedescr) - jump(i, p0) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i1, descr=valuedescr) + jump(i, p1) + """ + preamble = """ + [i, p0] + i0 = getfield_gc(p0, descr=valuedescr) + i1 = int_add(i0, i) + jump(i, i1) """ expected = """ [i, i2] i1 = int_add(i2, i) jump(i, i1) """ - self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)', - expected, checkspecnodes=False) + self.optimize_loop(ops, expected, preamble) def test_virtual_float(self): ops = """ [f, p0] f0 = getfield_gc(p0, descr=floatdescr) f1 = float_add(f0, f) - setfield_gc(p0, f1, descr=floatdescr) - jump(f, p0) + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, f1, descr=floatdescr) + jump(f, p1) """ - expected = """ - [f, f2] + preamble = """ + [f, p0] + f2 = getfield_gc(p0, descr=floatdescr) f1 = float_add(f2, f) jump(f, f1) """ - self.optimize_loop(ops, 'Not, Virtual(node_vtable, floatdescr=Not)', - expected, checkspecnodes=False) - - def test_virtual_2(self): - ops = """ - [i, p0] - i0 = getfield_gc(p0, descr=valuedescr) - i1 = int_add(i0, i) - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i1, descr=valuedescr) - jump(i, p1) - """ expected = """ - [i, i2] - i1 = int_add(i2, i) - jump(i, i1) + [f, f2] + f1 = float_add(f2, f) + jump(f, f1) """ - self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)', - expected) + self.optimize_loop(ops, expected, preamble) def test_virtual_oois(self): ops = """ @@ -909,14 +1040,10 @@ class OptimizeOptTest(BaseTestOptimizeOpt): jump(p0, p1, p2) """ expected = """ - [p2] + [p0, p1, p2] # all constant-folded :-) - jump(p2) + jump(p0, p1, p2) """ - self.optimize_loop(ops, '''Virtual(node_vtable), - Virtual(node_vtable), - Not''', - expected, checkspecnodes=False) # # to be complete, we also check the no-opt case where most comparisons # are not removed. The exact set of comparisons removed depends on @@ -932,7 +1059,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i11) [] jump(p0, p1, p2) """ - self.optimize_loop(ops, 'Not, Not, Not', expected2) + self.optimize_loop(ops, expected, expected2) def test_virtual_default_field(self): ops = """ @@ -943,15 +1070,17 @@ class OptimizeOptTest(BaseTestOptimizeOpt): # the field 'value' has its default value of 0 jump(p1) """ + preamble = """ + [p0] + i0 = getfield_gc(p0, descr=valuedescr) + guard_value(i0, 0) [] + jump() + """ expected = """ - [i] - guard_value(i, 0) [] - jump(0) + [] + jump() """ - # the 'expected' is sub-optimal, but it should be done by another later - # optimization step. See test_find_nodes_default_field() for why. - self.optimize_loop(ops, 'Virtual(node_vtable, valuedescr=Not)', - expected) + self.optimize_loop(ops, expected, preamble) def test_virtual_3(self): ops = """ @@ -967,7 +1096,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): i1 = int_add(i, 1) jump(i1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_virtual_4(self): ops = """ @@ -980,14 +1109,21 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i2, descr=valuedescr) jump(i3, p1) """ + preamble = """ + [i0, p0] + guard_class(p0, ConstClass(node_vtable)) [] + i1 = getfield_gc(p0, descr=valuedescr) + i2 = int_sub(i1, 1) + i3 = int_add(i0, i1) + jump(i3, i2) + """ expected = """ [i0, i1] i2 = int_sub(i1, 1) i3 = int_add(i0, i1) jump(i3, i2) """ - self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)', - expected) + self.optimize_loop(ops, expected, preamble) def test_virtual_5(self): ops = """ @@ -1003,18 +1139,21 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, p2, descr=nextdescr) jump(i3, p1) """ + preamble = """ + [i0, p0] + guard_class(p0, ConstClass(node_vtable)) [] + i1 = getfield_gc(p0, descr=valuedescr) + i2 = int_sub(i1, 1) + i3 = int_add(i0, i1) + jump(i3, i2, i1) + """ expected = """ [i0, i1, i1bis] i2 = int_sub(i1, 1) i3 = int_add(i0, i1) jump(i3, i2, i1) """ - self.optimize_loop(ops, - '''Not, Virtual(node_vtable, - valuedescr=Not, - nextdescr=Virtual(node_vtable2, - valuedescr=Not))''', - expected) + self.optimize_loop(ops, expected, preamble) def test_virtual_constant_isnull(self): ops = """ @@ -1025,11 +1164,15 @@ class OptimizeOptTest(BaseTestOptimizeOpt): i1 = ptr_eq(p2, NULL) jump(i1) """ - expected = """ + preamble = """ [i0] - jump(1) + jump() """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [] + jump() + """ + self.optimize_loop(ops, expected, preamble) def test_virtual_constant_isnonnull(self): @@ -1041,11 +1184,15 @@ class OptimizeOptTest(BaseTestOptimizeOpt): i1 = ptr_eq(p2, NULL) jump(i1) """ - expected = """ + preamble = """ [i0] - jump(0) + jump() """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [] + jump() + """ + self.optimize_loop(ops, expected) def test_nonvirtual_1(self): ops = """ @@ -1067,7 +1214,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p1) jump(i1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_nonvirtual_2(self): ops = """ @@ -1079,8 +1226,22 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i1, descr=valuedescr) jump(i, p1) """ - expected = ops - self.optimize_loop(ops, 'Not, Not', expected) + preamble = """ + [i, p0] + i0 = getfield_gc(p0, descr=valuedescr) + escape(p0) + i1 = int_add(i0, i) + jump(i, i1) + """ + expected = """ + [i, i1] + p1 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p1, i1, descr=valuedescr) + escape(p1) + i2 = int_add(i1, i) + jump(i, i2) + """ + self.optimize_loop(ops, expected, preamble) def test_nonvirtual_later(self): ops = """ @@ -1102,7 +1263,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): i3 = int_add(i, i2) jump(i3) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_nonvirtual_dont_write_null_fields_on_force(self): ops = """ @@ -1122,7 +1283,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): i2 = getfield_gc(p1, descr=valuedescr) jump(i2) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_getfield_gc_pure_1(self): ops = """ @@ -1136,7 +1297,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i] jump(i) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_getfield_gc_pure_2(self): ops = """ @@ -1145,11 +1306,11 @@ class OptimizeOptTest(BaseTestOptimizeOpt): jump(i1) """ expected = """ - [i] - jump(5) + [] + jump() """ self.node.value = 5 - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_getfield_gc_nonpure_2(self): ops = """ @@ -1157,8 +1318,12 @@ class OptimizeOptTest(BaseTestOptimizeOpt): i1 = getfield_gc(ConstPtr(myptr), descr=valuedescr) jump(i1) """ - expected = ops - self.optimize_loop(ops, 'Not', expected) + preamble = ops + expected = """ + [i] + jump(i) + """ + self.optimize_loop(ops, expected, preamble) def test_varray_1(self): ops = """ @@ -1175,7 +1340,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i1] jump(i1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_varray_alloc_and_set(self): ops = """ @@ -1185,11 +1350,15 @@ class OptimizeOptTest(BaseTestOptimizeOpt): i2 = getarrayitem_gc(p1, 1, descr=arraydescr) jump(i2) """ - expected = """ + preamble = """ [i1] - jump(0) + jump() + """ + expected = """ + [] + jump() """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected, preamble) def test_varray_float(self): ops = """ @@ -1206,7 +1375,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [f1] jump(f1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_array_non_optimized(self): ops = """ @@ -1222,7 +1391,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): p1 = new_array(i1, descr=arraydescr) jump(i1, p1) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_nonvirtual_array_dont_write_null_fields_on_force(self): ops = """ @@ -1240,7 +1409,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p1) jump(i1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_varray_2(self): ops = """ @@ -1254,13 +1423,21 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setarrayitem_gc(p2, 0, 20, descr=arraydescr) jump(i0, p2) """ - expected = """ - [i0, i1, i2] + preamble = """ + [i0, p1] + i1 = getarrayitem_gc(p1, 0, descr=arraydescr) + i2 = getarrayitem_gc(p1, 1, descr=arraydescr) i3 = int_sub(i1, i2) guard_value(i3, 15) [] - jump(i0, 20, i0) + jump(i0) """ - self.optimize_loop(ops, 'Not, VArray(arraydescr, Not, Not)', expected) + expected = """ + [i0] + i3 = int_sub(20, i0) + guard_value(i3, 15) [] + jump(5) + """ + self.optimize_loop(ops, expected, preamble) def test_p123_array(self): ops = """ @@ -1271,8 +1448,22 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setarrayitem_gc(p1, 0, i1, descr=arraydescr) jump(i1, p1, p2) """ + preamble = """ + [i1, p2, p3] + i3 = getarrayitem_gc(p3, 0, descr=arraydescr) + escape(i3) + jump(i1, p2) + """ + expected = """ + [i1, p2] + i3 = getarrayitem_gc(p2, 0, descr=arraydescr) + escape(i3) + p1 = new_array(1, descr=arraydescr) + setarrayitem_gc(p1, 0, i1, descr=arraydescr) + jump(i1, p1) + """ # We cannot track virtuals that survive for more than two iterations. - self.optimize_loop(ops, 'Not, Not, Not', ops) + self.optimize_loop(ops, expected, preamble) def test_varray_forced_1(self): ops = """ @@ -1294,7 +1485,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i2) jump() """ - self.optimize_loop(ops, '', expected) + self.optimize_loop(ops, expected) def test_vstruct_1(self): ops = """ @@ -1305,12 +1496,18 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p3, i1, descr=adescr) jump(i1, p3) """ - expected = """ - [i1, i2] + preamble = """ + [i1, p2] + i2 = getfield_gc(p2, descr=adescr) escape(i2) - jump(i1, i1) + jump(i1) + """ + expected = """ + [i1] + escape(i1) + jump(i1) """ - self.optimize_loop(ops, 'Not, VStruct(ssize, adescr=Not)', expected) + self.optimize_loop(ops, expected, preamble) def test_p123_vstruct(self): ops = """ @@ -1321,8 +1518,22 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i1, descr=adescr) jump(i1, p1, p2) """ + preamble = """ + [i1, p2, p3] + i3 = getfield_gc(p3, descr=adescr) + escape(i3) + jump(i1, p2) + """ + expected = """ + [i1, p2] + i3 = getfield_gc(p2, descr=adescr) + escape(i3) + p1 = new(descr=ssize) + setfield_gc(p1, i1, descr=adescr) + jump(i1, p1) + """ # We cannot track virtuals that survive for more than two iterations. - self.optimize_loop(ops, 'Not, Not, Not', ops) + self.optimize_loop(ops, expected, preamble) def test_duplicate_getfield_1(self): ops = """ @@ -1347,7 +1558,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i2) jump(p1, p2) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_getfield_after_setfield(self): ops = """ @@ -1363,7 +1574,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump(p1, i1) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_setfield_of_different_type_does_not_clear(self): ops = """ @@ -1381,7 +1592,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump(p1, p2, i1) """ - self.optimize_loop(ops, 'Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_setfield_of_same_type_clears(self): ops = """ @@ -1392,7 +1603,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i3) jump(p1, p2, i1, i3) """ - self.optimize_loop(ops, 'Not, Not, Not, Not', ops) + self.optimize_loop(ops, ops) def test_duplicate_getfield_mergepoint_has_no_side_effects(self): ops = """ @@ -1412,7 +1623,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump(p1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_getfield_ovf_op_does_not_clear(self): ops = """ @@ -1434,7 +1645,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump(p1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_getfield_setarrayitem_does_not_clear(self): ops = """ @@ -1454,7 +1665,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump(p1, p2) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_getfield_constant(self): ops = """ @@ -1472,7 +1683,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump() """ - self.optimize_loop(ops, '', expected) + self.optimize_loop(ops, expected) def test_duplicate_getfield_guard_value_const(self): ops = """ @@ -1491,7 +1702,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump() """ - self.optimize_loop(ops, 'Constant(myptr)', expected) + self.optimize_loop(ops, expected) def test_duplicate_getfield_sideeffects_1(self): ops = """ @@ -1503,7 +1714,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i2) jump(p1) """ - self.optimize_loop(ops, 'Not', ops) + self.optimize_loop(ops, ops) def test_duplicate_getfield_sideeffects_2(self): ops = """ @@ -1514,7 +1725,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i2) jump(p1, i1) """ - self.optimize_loop(ops, 'Not, Not', ops) + self.optimize_loop(ops, ops) def test_duplicate_setfield_1(self): ops = """ @@ -1528,7 +1739,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i2, descr=valuedescr) jump(p1, i1, i2) """ - self.optimize_loop(ops, 'Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_setfield_2(self): ops = """ @@ -1545,7 +1756,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump(p1, i1, i3) """ - self.optimize_loop(ops, 'Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_setfield_3(self): ops = """ @@ -1558,7 +1769,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): """ # potential aliasing of p1 and p2 means that we cannot kill the # the setfield_gc - self.optimize_loop(ops, 'Not, Not, Not, Not', ops) + self.optimize_loop(ops, ops) def test_duplicate_setfield_4(self): ops = """ @@ -1575,7 +1786,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i2, descr=valuedescr) jump(p1, i1, i2, p3) """ - expected = """ + preamble = """ [p1, i1, i2, p3] # i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr) @@ -1585,9 +1796,20 @@ class OptimizeOptTest(BaseTestOptimizeOpt): # setfield_gc(p1, i2, descr=valuedescr) setfield_gc(p1, i4, descr=nextdescr) - jump(p1, i1, i2, p3) + jump(p1, i1, i2, p3, i3) + """ + expected = """ + [p1, i1, i2, p3, i3] + # + i4 = getarrayitem_gc(p3, i3, descr=arraydescr) + i5 = int_add(i3, i4) + setarrayitem_gc(p3, 0, i5, descr=arraydescr) + # + setfield_gc(p1, i2, descr=valuedescr) + setfield_gc(p1, i4, descr=nextdescr) + jump(p1, i1, i2, p3, i3) """ - self.optimize_loop(ops, 'Not, Not, Not, Not', expected) + self.optimize_loop(ops, expected, preamble) def test_duplicate_setfield_5(self): ops = """ @@ -1609,7 +1831,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i1) jump(p0, i1) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_setfield_sideeffects_1(self): ops = """ @@ -1619,7 +1841,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i2, descr=valuedescr) jump(p1, i1, i2) """ - self.optimize_loop(ops, 'Not, Not, Not', ops) + self.optimize_loop(ops, ops) def test_duplicate_setfield_residual_guard_1(self): ops = """ @@ -1630,7 +1852,22 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i2, descr=valuedescr) jump(p1, i1, i2, i4) """ - self.optimize_loop(ops, 'Not, Not, Not, Not', ops) + preamble = """ + [p1, i1, i2, i3] + setfield_gc(p1, i1, descr=valuedescr) + guard_true(i3) [] + i4 = int_neg(i2) + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2, i4) + """ + expected = """ + [p1, i1, i2, i4] + setfield_gc(p1, i1, descr=valuedescr) + guard_true(i4) [] + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2, 1) + """ + self.optimize_loop(ops, expected, preamble) def test_duplicate_setfield_residual_guard_2(self): # the difference with the previous test is that the field value is @@ -1644,14 +1881,20 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, NULL, descr=nextdescr) jump(p1, i2, i4) """ - expected = """ + preamble = """ [p1, i2, i3] guard_true(i3) [p1] i4 = int_neg(i2) setfield_gc(p1, NULL, descr=nextdescr) jump(p1, i2, i4) """ - self.optimize_loop(ops, 'Not, Not, Not', expected) + expected = """ + [p1, i2, i4] + guard_true(i4) [p1] + setfield_gc(p1, NULL, descr=nextdescr) + jump(p1, i2, 1) + """ + self.optimize_loop(ops, expected, preamble) def test_duplicate_setfield_residual_guard_3(self): ops = """ @@ -1664,14 +1907,20 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, NULL, descr=nextdescr) jump(p1, i2, i4) """ - expected = """ + preamble = """ [p1, i2, i3] guard_true(i3) [i2, p1] i4 = int_neg(i2) setfield_gc(p1, NULL, descr=nextdescr) jump(p1, i2, i4) """ - self.optimize_loop(ops, 'Not, Not, Not', expected) + expected = """ + [p1, i2, i4] + guard_true(i4) [i2, p1] + setfield_gc(p1, NULL, descr=nextdescr) + jump(p1, i2, 1) + """ + self.optimize_loop(ops, expected) def test_duplicate_setfield_residual_guard_4(self): # test that the setfield_gc does not end up between int_eq and @@ -1685,7 +1934,16 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i2, descr=valuedescr) jump(p1, i1, i2, i4) """ - self.optimize_loop(ops, 'Not, Not, Not, Not', ops) + preamble = ops + expected = """ + [p1, i1, i2, i4] + setfield_gc(p1, i1, descr=valuedescr) + i5 = int_eq(i4, 5) + guard_true(i5) [] + setfield_gc(p1, i2, descr=valuedescr) + jump(p1, i1, i2, 5) + """ + self.optimize_loop(ops, expected, preamble) def test_duplicate_setfield_aliasing(self): # a case where aliasing issues (and not enough cleverness) mean @@ -1697,7 +1955,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1, i3, descr=valuedescr) jump(p1, p2, i1, i2, i3) """ - self.optimize_loop(ops, 'Not, Not, Not, Not, Not', ops) + self.optimize_loop(ops, ops) def test_duplicate_setfield_guard_value_const(self): ops = """ @@ -1712,7 +1970,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(ConstPtr(myptr), i2, descr=valuedescr) jump(i1, i2) """ - self.optimize_loop(ops, 'Constant(myptr), Not, Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_getarrayitem_1(self): ops = """ @@ -1737,7 +1995,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p3) jump(p1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_getarrayitem_after_setarrayitem_1(self): ops = """ @@ -1753,7 +2011,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p2) jump(p1, p2) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_getarrayitem_after_setarrayitem_2(self): ops = """ @@ -1775,7 +2033,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p3) jump(p1, p2, p3, i1) """ - self.optimize_loop(ops, 'Not, Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_getarrayitem_after_setarrayitem_3(self): ops = """ @@ -1802,7 +2060,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p4) jump(p1, p2, p3, p4, i1) """ - self.optimize_loop(ops, 'Not, Not, Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_getarrayitem_pure_does_not_invalidate(self): ops = """ @@ -1823,7 +2081,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p3) jump(p1, p2) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_duplicate_getarrayitem_after_setarrayitem_two_arrays(self): ops = """ @@ -1844,7 +2102,36 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(p4) jump(p1, p2, p3, p4, i1) """ - self.optimize_loop(ops, 'Not, Not, Not, Not, Not', expected) + self.optimize_loop(ops, expected) + + def test_duplicate_setfield_virtual(self): + ops = """ + [p1, i2, i3, p4] + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, p4, descr=nextdescr) + setfield_gc(p1, p2, descr=nextdescr) + guard_true(i3) [] + i4 = int_neg(i2) + jump(p1, i2, i4, p4) + """ + preamble = """ + [p1, i2, i3, p4] + guard_true(i3) [p1, p4] + i4 = int_neg(i2) + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, p4, descr=nextdescr) + setfield_gc(p1, p2, descr=nextdescr) + jump(p1, i2, i4, p4) + """ + expected = """ + [p1, i2, i4, p4] + guard_true(i4) [p1, p4] + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, p4, descr=nextdescr) + setfield_gc(p1, p2, descr=nextdescr) + jump(p1, i2, 1, p4) + """ + self.optimize_loop(ops, expected, preamble) def test_bug_1(self): ops = """ @@ -1866,8 +2153,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): p3 = escape() jump(i0, p3) """ - self.optimize_loop(ops, 'Not, Virtual(node_vtable, nextdescr=Not)', - expected) + self.optimize_loop(ops, expected) def test_bug_2(self): ops = """ @@ -1889,8 +2175,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): p3 = escape() jump(i0, p3) """ - self.optimize_loop(ops, 'Not, VArray(arraydescr2, Not)', - expected) + self.optimize_loop(ops, expected) def test_bug_3(self): ops = """ @@ -1912,17 +2197,34 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1a, p3a, descr=otherdescr) jump(p1a) """ - expected = """ - [p2, p3] + preamble = """ + [p1] + guard_nonnull_class(p1, ConstClass(node_vtable2)) [] + p2 = getfield_gc(p1, descr=nextdescr) guard_class(p2, ConstClass(node_vtable)) [] + p3 = getfield_gc(p1, descr=otherdescr) guard_class(p3, ConstClass(node_vtable)) [] setfield_gc(p3, p2, descr=otherdescr) p3a = new_with_vtable(ConstClass(node_vtable)) escape(p3a) - p2a = new_with_vtable(ConstClass(node_vtable)) - jump(p2a, p3a) + jump(p3a) """ - self.optimize_loop(ops, 'Virtual(node_vtable2, nextdescr=Not, otherdescr=Not)', expected) + expected = """ + [p3a] + # p1=p1a(next=p2a, other=p3a), p2() + # p2 = getfield_gc(p1, descr=nextdescr) # p2a + # p3 = getfield_gc(p1, descr=otherdescr)# p3a + # setfield_gc(p3, p2, descr=otherdescr) # p3a.other = p2a + # p1a = new_with_vtable(ConstClass(node_vtable2)) + # p2a = new_with_vtable(ConstClass(node_vtable)) + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3a, p2, descr=otherdescr) # p3a.other = p2a + p3anew = new_with_vtable(ConstClass(node_vtable)) + escape(p3anew) + jump(p3anew) + """ + #self.optimize_loop(ops, expected) # XXX Virtual(node_vtable2, nextdescr=Not, otherdescr=Not) + self.optimize_loop(ops, expected, preamble) def test_bug_3bis(self): ops = """ @@ -1944,17 +2246,31 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p1a, p3a, descr=otherdescr) jump(p1a) """ - expected = """ - [p2, p3] + preamble = """ + [p1] + guard_nonnull_class(p1, ConstClass(node_vtable2)) [] + p2 = getfield_gc(p1, descr=nextdescr) guard_class(p2, ConstClass(node_vtable)) [] + p3 = getfield_gc(p1, descr=otherdescr) guard_class(p3, ConstClass(node_vtable)) [] + # p1a = new_with_vtable(ConstClass(node_vtable2)) p2a = new_with_vtable(ConstClass(node_vtable)) setfield_gc(p3, p2a, descr=otherdescr) p3a = new_with_vtable(ConstClass(node_vtable)) escape(p3a) + # setfield_gc(p1a, p2a, descr=nextdescr) + # setfield_gc(p1a, p3a, descr=otherdescr) jump(p2a, p3a) """ - self.optimize_loop(ops, 'Virtual(node_vtable2, nextdescr=Not, otherdescr=Not)', expected) + expected = """ + [p2, p3] + p2a = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p3, p2a, descr=otherdescr) + p3a = new_with_vtable(ConstClass(node_vtable)) + escape(p3a) + jump(p2a, p3a) + """ + self.optimize_loop(ops, expected, preamble) def test_bug_4(self): ops = """ @@ -1963,7 +2279,18 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(ConstPtr(myptr), p9, descr=nextdescr) jump(p30) """ - self.optimize_loop(ops, 'Not', ops) + preamble = """ + [p9] + setfield_gc(ConstPtr(myptr), p9, descr=nextdescr) + jump() + """ + expected = """ + [] + p30 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(ConstPtr(myptr), p30, descr=nextdescr) + jump() + """ + self.optimize_loop(ops, expected, preamble) def test_invalid_loop_1(self): ops = """ @@ -1974,10 +2301,9 @@ class OptimizeOptTest(BaseTestOptimizeOpt): jump(p2) """ py.test.raises(InvalidLoop, self.optimize_loop, - ops, 'Virtual(node_vtable)', None) + ops, ops) def test_invalid_loop_2(self): - py.test.skip("this would fail if we had Fixed again in the specnodes") ops = """ [p1] guard_class(p1, ConstClass(node_vtable2)) [] @@ -1987,7 +2313,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): jump(p2) """ py.test.raises(InvalidLoop, self.optimize_loop, - ops, '...', None) + ops, ops) def test_invalid_loop_3(self): ops = """ @@ -2000,9 +2326,8 @@ class OptimizeOptTest(BaseTestOptimizeOpt): setfield_gc(p3, p4, descr=nextdescr) jump(p3) """ - py.test.raises(InvalidLoop, self.optimize_loop, ops, - 'Virtual(node_vtable, nextdescr=Virtual(node_vtable))', - None) + py.test.raises(InvalidLoop, self.optimize_loop, ops, ops) + def test_merge_guard_class_guard_value(self): ops = """ @@ -2012,16 +2337,21 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(p1, ConstPtr(myptr)) [i1] jump(p2, i0, i1, i3, p2) """ - expected = """ + preamble = """ [p1, i0, i1, i2, p2] guard_value(p1, ConstPtr(myptr)) [i0] i3 = int_add(i1, i2) - jump(p2, i0, i1, i3, p2) + jump(p2, i0, i1, i3) + """ + expected = """ + [p2, i0, i1, i2] + guard_value(p2, ConstPtr(myptr)) [i0] + i3 = int_add(i1, i2) + jump(ConstPtr(myptr), i0, i1, i3) """ - self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected) + self.optimize_loop(ops, expected, preamble) def test_merge_guard_nonnull_guard_class(self): - self.make_fail_descr() ops = """ [p1, i0, i1, i2, p2] guard_nonnull(p1, descr=fdescr) [i0] @@ -2029,17 +2359,22 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_class(p1, ConstClass(node_vtable)) [i1] jump(p2, i0, i1, i3, p2) """ - expected = """ + preamble = """ [p1, i0, i1, i2, p2] guard_nonnull_class(p1, ConstClass(node_vtable), descr=fdescr) [i0] i3 = int_add(i1, i2) - jump(p2, i0, i1, i3, p2) + jump(p2, i0, i1, i3) """ - self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected) - self.check_expanded_fail_descr("i0", rop.GUARD_NONNULL_CLASS) + expected = """ + [p2, i0, i1, i2] + guard_nonnull_class(p2, ConstClass(node_vtable), descr=fdescr2) [i0] + i3 = int_add(i1, i2) + jump(p2, i0, i1, i3) + """ + self.optimize_loop(ops, expected, preamble) + #self.check_expanded_fail_descr("i0", rop.GUARD_NONNULL_CLASS) def test_merge_guard_nonnull_guard_value(self): - self.make_fail_descr() ops = """ [p1, i0, i1, i2, p2] guard_nonnull(p1, descr=fdescr) [i0] @@ -2047,17 +2382,22 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(p1, ConstPtr(myptr)) [i1] jump(p2, i0, i1, i3, p2) """ - expected = """ + preamble = """ [p1, i0, i1, i2, p2] guard_value(p1, ConstPtr(myptr), descr=fdescr) [i0] i3 = int_add(i1, i2) - jump(p2, i0, i1, i3, p2) + jump(p2, i0, i1, i3) """ - self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected) - self.check_expanded_fail_descr("i0", rop.GUARD_VALUE) + expected = """ + [p2, i0, i1, i2] + guard_value(p2, ConstPtr(myptr), descr=fdescr2) [i0] + i3 = int_add(i1, i2) + jump(ConstPtr(myptr), i0, i1, i3) + """ + self.optimize_loop(ops, expected, preamble) + #self.check_expanded_fail_descr("i0", rop.GUARD_VALUE) def test_merge_guard_nonnull_guard_class_guard_value(self): - self.make_fail_descr() ops = """ [p1, i0, i1, i2, p2] guard_nonnull(p1, descr=fdescr) [i0] @@ -2067,15 +2407,22 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_value(p1, ConstPtr(myptr)) [i1] jump(p2, i0, i1, i4, p2) """ - expected = """ + preamble = """ [p1, i0, i1, i2, p2] guard_value(p1, ConstPtr(myptr), descr=fdescr) [i0] i3 = int_add(i1, i2) i4 = int_sub(i3, 1) - jump(p2, i0, i1, i4, p2) + jump(p2, i0, i1, i4) """ - self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected) - self.check_expanded_fail_descr("i0", rop.GUARD_VALUE) + expected = """ + [p2, i0, i1, i2] + guard_value(p2, ConstPtr(myptr), descr=fdescr2) [i0] + i3 = int_add(i1, i2) + i4 = int_sub(i3, 1) + jump(ConstPtr(myptr), i0, i1, i4) + """ + self.optimize_loop(ops, expected, preamble) + #self.check_expanded_fail_descr("i0", rop.GUARD_VALUE) def test_guard_class_oois(self): ops = """ @@ -2085,12 +2432,16 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i) [] jump(p1) """ - expected = """ + preamble = """ [p1] guard_class(p1, ConstClass(node_vtable2)) [] jump(p1) """ - self.optimize_loop(ops, "Not", expected) + expected = """ + [p1] + jump(p1) + """ + self.optimize_loop(ops, expected, preamble) def test_oois_of_itself(self): ops = """ @@ -2103,12 +2454,16 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_false(i2) [] jump(p0) """ - expected = """ + preamble = """ [p0] p1 = getfield_gc(p0, descr=nextdescr) jump(p0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [p0] + jump(p0) + """ + self.optimize_loop(ops, expected, preamble) def test_remove_duplicate_pure_op(self): ops = """ @@ -2127,7 +2482,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i2) [] jump(p1, p2) """ - expected = """ + preamble = """ [p1, p2] i1 = ptr_eq(p1, p2) i3 = int_add(i1, 1) @@ -2138,7 +2493,13 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i1) [] jump(p1, p2) """ - self.optimize_loop(ops, "Not, Not", expected) + expected = """ + [p1, p2] + escape(2) + escape(2) + jump(p1, p2) + """ + self.optimize_loop(ops, expected, preamble) def test_remove_duplicate_pure_op_with_descr(self): ops = """ @@ -2151,14 +2512,18 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i3) [] jump(p1) """ - expected = """ + preamble = """ [p1] i0 = arraylen_gc(p1, descr=arraydescr) i1 = int_gt(i0, 0) guard_true(i1) [] jump(p1) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [p1] + jump(p1) + """ + self.optimize_loop(ops, expected, preamble) def test_remove_duplicate_pure_op_ovf(self): ops = """ @@ -2175,7 +2540,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): escape(i4) jump(i1) """ - expected = """ + preamble = """ [i1] i3 = int_add_ovf(i1, 1) guard_no_overflow() [] @@ -2183,9 +2548,15 @@ class OptimizeOptTest(BaseTestOptimizeOpt): guard_true(i3b) [] escape(i3) escape(i3) - jump(i1) + jump(i1, i3) + """ + expected = """ + [i1, i3] + escape(i3) + escape(i3) + jump(i1, i3) """ - self.optimize_loop(ops, "Not", expected) + self.optimize_loop(ops, expected, preamble) def test_int_and_or_with_zero(self): ops = """ @@ -2200,7 +2571,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i0, i1] jump(i1, i0) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_fold_partially_constant_ops(self): ops = """ @@ -2212,7 +2583,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i0] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) ops = """ [i0] @@ -2223,7 +2594,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i0] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) ops = """ [i0] @@ -2234,7 +2605,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i0] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_fold_partially_constant_ops_ovf(self): ops = """ @@ -2247,7 +2618,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i0] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) ops = """ [i0] @@ -2259,7 +2630,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i0] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) ops = """ [i0] @@ -2271,406 +2642,10 @@ class OptimizeOptTest(BaseTestOptimizeOpt): [i0] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) # ---------- - def make_fail_descr(self): - class FailDescr(compile.ResumeGuardDescr): - oparse = None - def _oparser_uses_descr_of_guard(self, oparse, fail_args): - # typically called twice, before and after optimization - if self.oparse is None: - fdescr.rd_frame_info_list = resume.FrameInfo(None, - "code", 11) - fdescr.rd_snapshot = resume.Snapshot(None, fail_args) - self.oparse = oparse - # - fdescr = instantiate(FailDescr) - self.namespace['fdescr'] = fdescr - - def teardown_method(self, meth): - self.namespace.pop('fdescr', None) - - def _verify_fail_args(self, boxes, oparse, text): - import re - r = re.compile(r"\bwhere\s+(\w+)\s+is a\s+(\w+)") - parts = list(r.finditer(text)) - ends = [match.start() for match in parts] + [len(text)] - # - virtuals = {} - for match, end in zip(parts, ends[1:]): - pvar = match.group(1) - fieldstext = text[match.end():end] - if match.group(2) == 'varray': - arrayname, fieldstext = fieldstext.split(':', 1) - tag = ('varray', self.namespace[arrayname.strip()]) - elif match.group(2) == 'vstruct': - if ',' in fieldstext: - structname, fieldstext = fieldstext.split(',', 1) - else: - structname, fieldstext = fieldstext, '' - tag = ('vstruct', self.namespace[structname.strip()]) - else: - tag = ('virtual', self.namespace[match.group(2)]) - virtuals[pvar] = (tag, None, fieldstext) - # - r2 = re.compile(r"([\w\d()]+)[.](\w+)\s*=\s*([\w\d()]+)") - pendingfields = [] - for match in r2.finditer(text): - pvar = match.group(1) - pfieldname = match.group(2) - pfieldvar = match.group(3) - pendingfields.append((pvar, pfieldname, pfieldvar)) - # - def _variables_equal(box, varname, strict): - if varname not in virtuals: - if strict: - assert box == oparse.getvar(varname) - else: - assert box.value == oparse.getvar(varname).value - else: - tag, resolved, fieldstext = virtuals[varname] - if tag[0] == 'virtual': - assert self.get_class_of_box(box) == tag[1] - elif tag[0] == 'varray': - pass # xxx check arraydescr - elif tag[0] == 'vstruct': - pass # xxx check typedescr - else: - assert 0 - if resolved is not None: - assert resolved.value == box.value - else: - virtuals[varname] = tag, box, fieldstext - # - basetext = text.splitlines()[0] - varnames = [s.strip() for s in basetext.split(',')] - if varnames == ['']: - varnames = [] - assert len(boxes) == len(varnames) - for box, varname in zip(boxes, varnames): - _variables_equal(box, varname, strict=True) - for pvar, pfieldname, pfieldvar in pendingfields: - box = oparse.getvar(pvar) - fielddescr = self.namespace[pfieldname.strip()] - fieldbox = executor.execute(self.cpu, None, - rop.GETFIELD_GC, - fielddescr, - box) - _variables_equal(fieldbox, pfieldvar, strict=True) - # - for match in parts: - pvar = match.group(1) - tag, resolved, fieldstext = virtuals[pvar] - assert resolved is not None - index = 0 - for fieldtext in fieldstext.split(','): - fieldtext = fieldtext.strip() - if not fieldtext: - continue - if tag[0] in ('virtual', 'vstruct'): - fieldname, fieldvalue = fieldtext.split('=') - fielddescr = self.namespace[fieldname.strip()] - fieldbox = executor.execute(self.cpu, None, - rop.GETFIELD_GC, - fielddescr, - resolved) - elif tag[0] == 'varray': - fieldvalue = fieldtext - fieldbox = executor.execute(self.cpu, None, - rop.GETARRAYITEM_GC, - tag[1], - resolved, ConstInt(index)) - else: - assert 0 - _variables_equal(fieldbox, fieldvalue.strip(), strict=False) - index += 1 - - def check_expanded_fail_descr(self, expectedtext, guard_opnum): - from pypy.jit.metainterp.test.test_resume import ResumeDataFakeReader - from pypy.jit.metainterp.test.test_resume import MyMetaInterp - guard_op, = [op for op in self.loop.operations if op.is_guard()] - fail_args = guard_op.getfailargs() - fdescr = guard_op.getdescr() - assert fdescr.guard_opnum == guard_opnum - reader = ResumeDataFakeReader(fdescr, fail_args, - MyMetaInterp(self.cpu)) - boxes = reader.consume_boxes() - self._verify_fail_args(boxes, fdescr.oparse, expectedtext) - - def test_expand_fail_1(self): - self.make_fail_descr() - ops = """ - [i1, i3] - # first rename i3 into i4 - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i3, descr=valuedescr) - i4 = getfield_gc(p1, descr=valuedescr) - # - i2 = int_add(10, 5) - guard_true(i1, descr=fdescr) [i2, i4] - jump(i1, i4) - """ - expected = """ - [i1, i3] - guard_true(i1, descr=fdescr) [i3] - jump(1, i3) - """ - self.optimize_loop(ops, 'Not, Not', expected) - self.check_expanded_fail_descr('15, i3', rop.GUARD_TRUE) - - def test_expand_fail_2(self): - self.make_fail_descr() - ops = """ - [i1, i2] - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i2, descr=valuedescr) - setfield_gc(p1, p1, descr=nextdescr) - guard_true(i1, descr=fdescr) [p1] - jump(i1, i2) - """ - expected = """ - [i1, i2] - guard_true(i1, descr=fdescr) [i2] - jump(1, i2) - """ - self.optimize_loop(ops, 'Not, Not', expected) - self.check_expanded_fail_descr('''ptr - where ptr is a node_vtable, valuedescr=i2 - ''', rop.GUARD_TRUE) - - def test_expand_fail_3(self): - self.make_fail_descr() - ops = """ - [i1, i2, i3, p3] - p1 = new_with_vtable(ConstClass(node_vtable)) - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, 1, descr=valuedescr) - setfield_gc(p1, p2, descr=nextdescr) - setfield_gc(p2, i2, descr=valuedescr) - setfield_gc(p2, p3, descr=nextdescr) - guard_true(i1, descr=fdescr) [i3, p1] - jump(i2, i1, i3, p3) - """ - expected = """ - [i1, i2, i3, p3] - guard_true(i1, descr=fdescr) [i3, i2, p3] - jump(i2, 1, i3, p3) - """ - self.optimize_loop(ops, 'Not, Not, Not, Not', expected) - self.check_expanded_fail_descr('''i3, p1 - where p1 is a node_vtable, valuedescr=1, nextdescr=p2 - where p2 is a node_vtable, valuedescr=i2, nextdescr=p3 - ''', rop.GUARD_TRUE) - - def test_expand_fail_4(self): - for arg in ['p1', 'i2,p1', 'p1,p2', 'p2,p1', - 'i2,p1,p2', 'i2,p2,p1']: - self.make_fail_descr() - ops = """ - [i1, i2, i3] - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i3, descr=valuedescr) - i4 = getfield_gc(p1, descr=valuedescr) # copy of i3 - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i2, descr=valuedescr) - setfield_gc(p1, p2, descr=nextdescr) - setfield_gc(p2, i2, descr=valuedescr) - guard_true(i1, descr=fdescr) [i4, i3, %s] - jump(i1, i2, i3) - """ - expected = """ - [i1, i2, i3] - guard_true(i1, descr=fdescr) [i3, i2] - jump(1, i2, i3) - """ - self.optimize_loop(ops % arg, 'Not, Not, Not', expected) - self.check_expanded_fail_descr('''i3, i3, %s - where p1 is a node_vtable, valuedescr=i2, nextdescr=p2 - where p2 is a node_vtable, valuedescr=i2''' % arg, - rop.GUARD_TRUE) - - def test_expand_fail_5(self): - self.make_fail_descr() - ops = """ - [i1, i2, i3, i4] - p1 = new_with_vtable(ConstClass(node_vtable)) - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i4, descr=valuedescr) - setfield_gc(p1, p2, descr=nextdescr) - setfield_gc(p2, i2, descr=valuedescr) - setfield_gc(p2, p1, descr=nextdescr) # a cycle - guard_true(i1, descr=fdescr) [i3, i4, p1, p2] - jump(i2, i1, i3, i4) - """ - expected = """ - [i1, i2, i3, i4] - guard_true(i1, descr=fdescr) [i3, i4, i2] - jump(i2, 1, i3, i4) - """ - self.optimize_loop(ops, 'Not, Not, Not, Not', expected) - self.check_expanded_fail_descr('''i3, i4, p1, p2 - where p1 is a node_vtable, valuedescr=i4, nextdescr=p2 - where p2 is a node_vtable, valuedescr=i2, nextdescr=p1 - ''', rop.GUARD_TRUE) - - def test_expand_fail_6(self): - self.make_fail_descr() - ops = """ - [p0, i0, i1] - guard_true(i0, descr=fdescr) [p0] - p1 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p1, i1, descr=valuedescr) - jump(p1, i1, i1) - """ - expected = """ - [i1b, i0, i1] - guard_true(i0, descr=fdescr) [i1b] - jump(i1, i1, i1) - """ - self.optimize_loop(ops, '''Virtual(node_vtable, valuedescr=Not), - Not, Not''', expected) - self.check_expanded_fail_descr('''p0 - where p0 is a node_vtable, valuedescr=i1b - ''', rop.GUARD_TRUE) - - def test_expand_fail_varray(self): - self.make_fail_descr() - ops = """ - [i1] - p1 = new_array(3, descr=arraydescr) - setarrayitem_gc(p1, 1, i1, descr=arraydescr) - setarrayitem_gc(p1, 0, 25, descr=arraydescr) - guard_true(i1, descr=fdescr) [p1] - i2 = getarrayitem_gc(p1, 1, descr=arraydescr) - jump(i2) - """ - expected = """ - [i1] - guard_true(i1, descr=fdescr) [i1] - jump(1) - """ - self.optimize_loop(ops, 'Not', expected) - self.check_expanded_fail_descr('''p1 - where p1 is a varray arraydescr: 25, i1 - ''', rop.GUARD_TRUE) - - def test_expand_fail_vstruct(self): - self.make_fail_descr() - ops = """ - [i1, p1] - p2 = new(descr=ssize) - setfield_gc(p2, i1, descr=adescr) - setfield_gc(p2, p1, descr=bdescr) - guard_true(i1, descr=fdescr) [p2] - i3 = getfield_gc(p2, descr=adescr) - p3 = getfield_gc(p2, descr=bdescr) - jump(i3, p3) - """ - expected = """ - [i1, p1] - guard_true(i1, descr=fdescr) [i1, p1] - jump(1, p1) - """ - self.optimize_loop(ops, 'Not, Not', expected) - self.check_expanded_fail_descr('''p2 - where p2 is a vstruct ssize, adescr=i1, bdescr=p1 - ''', rop.GUARD_TRUE) - - def test_expand_fail_v_all_1(self): - self.make_fail_descr() - ops = """ - [i1, p1a, i2] - p6s = getarrayitem_gc(p1a, 0, descr=arraydescr2) - p7v = getfield_gc(p6s, descr=bdescr) - p5s = new(descr=ssize) - setfield_gc(p5s, i2, descr=adescr) - setfield_gc(p5s, p7v, descr=bdescr) - setarrayitem_gc(p1a, 1, p5s, descr=arraydescr2) - guard_true(i1, descr=fdescr) [p1a] - p2s = new(descr=ssize) - p3v = new_with_vtable(ConstClass(node_vtable)) - p4a = new_array(2, descr=arraydescr2) - setfield_gc(p2s, i1, descr=adescr) - setfield_gc(p2s, p3v, descr=bdescr) - setfield_gc(p3v, i2, descr=valuedescr) - setarrayitem_gc(p4a, 0, p2s, descr=arraydescr2) - jump(i1, p4a, i2) - """ - expected = """ - [i1, ia, iv, pnull, i2] - guard_true(i1, descr=fdescr) [ia, iv, i2] - jump(1, 1, i2, NULL, i2) - """ - self.optimize_loop(ops, ''' - Not, - VArray(arraydescr2, - VStruct(ssize, - adescr=Not, - bdescr=Virtual(node_vtable, - valuedescr=Not)), - Not), - Not''', expected) - self.check_expanded_fail_descr('''p1a - where p1a is a varray arraydescr2: p6s, p5s - where p6s is a vstruct ssize, adescr=ia, bdescr=p7v - where p5s is a vstruct ssize, adescr=i2, bdescr=p7v - where p7v is a node_vtable, valuedescr=iv - ''', rop.GUARD_TRUE) - - def test_expand_fail_lazy_setfield_1(self): - self.make_fail_descr() - ops = """ - [p1, i2, i3] - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p2, i2, descr=valuedescr) - setfield_gc(p1, p2, descr=nextdescr) - guard_true(i3, descr=fdescr) [] - i4 = int_neg(i2) - setfield_gc(p1, NULL, descr=nextdescr) - jump(p1, i2, i4) - """ - expected = """ - [p1, i2, i3] - guard_true(i3, descr=fdescr) [p1, i2] - i4 = int_neg(i2) - setfield_gc(p1, NULL, descr=nextdescr) - jump(p1, i2, i4) - """ - self.optimize_loop(ops, 'Not, Not, Not', expected) - self.loop.inputargs[0].value = self.nodebox.value - self.check_expanded_fail_descr(''' - p1.nextdescr = p2 - where p2 is a node_vtable, valuedescr=i2 - ''', rop.GUARD_TRUE) - - def test_expand_fail_lazy_setfield_2(self): - self.make_fail_descr() - ops = """ - [i2, i3] - p2 = new_with_vtable(ConstClass(node_vtable)) - setfield_gc(p2, i2, descr=valuedescr) - setfield_gc(ConstPtr(myptr), p2, descr=nextdescr) - guard_true(i3, descr=fdescr) [] - i4 = int_neg(i2) - setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr) - jump(i2, i4) - """ - expected = """ - [i2, i3] - guard_true(i3, descr=fdescr) [i2] - i4 = int_neg(i2) - setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr) - jump(i2, i4) - """ - self.optimize_loop(ops, 'Not, Not', expected) - self.check_expanded_fail_descr(''' - ConstPtr(myptr).nextdescr = p2 - where p2 is a node_vtable, valuedescr=i2 - ''', rop.GUARD_TRUE) - - class TestLLtype(OptimizeOptTest, LLtypeMixin): def test_residual_call_does_not_invalidate_caches(self): @@ -2691,7 +2666,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i1) jump(p1, p2) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_residual_call_invalidate_some_caches(self): ops = """ @@ -2719,7 +2694,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i2) jump(p1, p2) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_residual_call_invalidate_arrays(self): ops = """ @@ -2746,7 +2721,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(p4) jump(p1, p2, i1) """ - self.optimize_loop(ops, 'Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_residual_call_invalidate_some_arrays(self): ops = """ @@ -2781,7 +2756,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i4) jump(p1, p2, i1) """ - self.optimize_loop(ops, 'Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_residual_call_invalidates_some_read_caches_1(self): ops = """ @@ -2801,7 +2776,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p2, i3, descr=adescr) jump(p1, i1, p2, i2) """ - self.optimize_loop(ops, 'Not, Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_residual_call_invalidates_some_read_caches_2(self): ops = """ @@ -2821,7 +2796,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p2, i3, descr=adescr) jump(p1, i1, p2, i2) """ - self.optimize_loop(ops, 'Not, Not, Not, Not', expected) + self.optimize_loop(ops, expected) def test_residual_call_invalidates_some_read_caches_3(self): ops = """ @@ -2833,7 +2808,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p2, i3, descr=adescr) jump(p1, i1, p2, i2) """ - self.optimize_loop(ops, 'Not, Not, Not, Not', ops) + self.optimize_loop(ops, ops) def test_call_assembler_invalidates_caches(self): ops = ''' @@ -2843,7 +2818,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p1, i3, descr=valuedescr) jump(p1, i3) ''' - self.optimize_loop(ops, 'Not, Not', ops) + self.optimize_loop(ops, ops) def test_call_pure_invalidates_caches(self): # CALL_PURE should still force the setfield_gc() to occur before it @@ -2861,7 +2836,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p1, i3, descr=valuedescr) jump(p1, i3) ''' - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_call_pure_constant_folding(self): # CALL_PURE is not marked as is_always_pure(), because it is wrong @@ -2869,6 +2844,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): # time. Check that it is either constant-folded (and replaced by # the result of the call, recorded as the first arg), or turned into # a regular CALL. + # XXX can this test be improved with unrolling? ops = ''' [i0, i1, i2] escape(i1) @@ -2877,14 +2853,23 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i4 = call_pure(43, 123456, 4, i0, 6, descr=plaincalldescr) jump(i0, i3, i4) ''' - expected = ''' + preamble = ''' [i0, i1, i2] escape(i1) escape(i2) i4 = call(123456, 4, i0, 6, descr=plaincalldescr) - jump(i0, 42, i4) + jump(i0, i4) + ''' + expected = ''' + [i0, i2] + escape(42) + escape(i2) + i4 = call(123456, 4, i0, 6, descr=plaincalldescr) + jump(i0, i4) ''' - self.optimize_loop(ops, 'Not, Not, Not', expected) + self.optimize_loop(ops, expected, preamble) + + # ---------- def test_vref_nonvirtual_nonescape(self): ops = """ @@ -2898,7 +2883,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i0 = force_token() jump(p1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected, expected) def test_vref_nonvirtual_escape(self): ops = """ @@ -2921,7 +2906,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): """ # XXX we should optimize a bit more the case of a nonvirtual. # in theory it is enough to just do 'p2 = p1'. - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected, expected) def test_vref_virtual_1(self): ops = """ @@ -2961,10 +2946,9 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p2, -3, descr=virtualtokendescr) jump(p0, i1) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected, expected) def test_vref_virtual_2(self): - self.make_fail_descr() ops = """ [p0, i1] # @@ -2991,7 +2975,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p0, p2, descr=nextdescr) # call_may_force(i1, descr=mayforcevirtdescr) - guard_not_forced(descr=fdescr) [p2, i1] + guard_not_forced(descr=fdescr2) [p2, i1] # setfield_gc(p0, NULL, descr=nextdescr) p1 = new_with_vtable(ConstClass(node_vtable)) @@ -3004,14 +2988,13 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): """ # the point of this test is that 'i1' should show up in the fail_args # of 'guard_not_forced', because it was stored in the virtual 'p1b'. - self.optimize_loop(ops, 'Not, Not', expected) - self.check_expanded_fail_descr('''p2, p1 - where p1 is a node_vtable, nextdescr=p1b - where p1b is a node_vtable, valuedescr=i1 - ''', rop.GUARD_NOT_FORCED) + self.optimize_loop(ops, expected) + #self.check_expanded_fail_descr('''p2, p1 + # where p1 is a node_vtable, nextdescr=p1b + # where p1b is a node_vtable, valuedescr=i1 + # ''', rop.GUARD_NOT_FORCED) def test_vref_virtual_and_lazy_setfield(self): - self.make_fail_descr() ops = """ [p0, i1] # @@ -3028,7 +3011,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p0, NULL, descr=refdescr) jump(p0, i1) """ - expected = """ + preamble = """ [p0, i1] i3 = force_token() call(i1, descr=nonwritedescr) @@ -3036,21 +3019,28 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setfield_gc(p0, NULL, descr=refdescr) jump(p0, i1) """ - self.optimize_loop(ops, 'Not, Not', expected) + expected = """ + [p0, i1] + i3 = force_token() + call(i1, descr=nonwritedescr) + guard_no_exception(descr=fdescr2) [i3, i1, p0] + setfield_gc(p0, NULL, descr=refdescr) + jump(p0, i1) + """ + self.optimize_loop(ops, expected, preamble) # the fail_args contain [i3, i1, p0]: # - i3 is from the virtual expansion of p2 # - i1 is from the virtual expansion of p1 # - p0 is from the extra pendingfields - self.loop.inputargs[0].value = self.nodeobjvalue - self.check_expanded_fail_descr('''p2, p1 - p0.refdescr = p2 - where p2 is a jit_virtual_ref_vtable, virtualtokendescr=i3, virtualrefindexdescr=2 - where p1 is a node_vtable, nextdescr=p1b - where p1b is a node_vtable, valuedescr=i1 - ''', rop.GUARD_NO_EXCEPTION) + #self.loop.inputargs[0].value = self.nodeobjvalue + #self.check_expanded_fail_descr('''p2, p1 + # p0.refdescr = p2 + # where p2 is a jit_virtual_ref_vtable, virtualtokendescr=i3, virtualrefindexdescr=2 + # where p1 is a node_vtable, nextdescr=p1b + # where p1b is a node_vtable, valuedescr=i1 + # ''', rop.GUARD_NO_EXCEPTION) def test_vref_virtual_after_finish(self): - self.make_fail_descr() ops = """ [i1] p1 = new_with_vtable(ConstClass(node_vtable)) @@ -3075,10 +3065,9 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_not_forced() [] jump(i1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected, expected) def test_vref_nonvirtual_and_lazy_setfield(self): - self.make_fail_descr() ops = """ [i1, p1] p2 = virtual_ref(p1, 23) @@ -3101,7 +3090,9 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_not_forced() [i1] jump(i1, p1) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected, expected) + + # ---------- def test_arraycopy_1(self): ops = ''' @@ -3115,10 +3106,10 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): jump(i2) ''' expected = ''' - [i0] - jump(1) + [] + jump() ''' - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_arraycopy_2(self): ops = ''' @@ -3132,28 +3123,30 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): jump(i2) ''' expected = ''' - [i0] - jump(3) + [] + jump() ''' - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_arraycopy_not_virtual(self): ops = ''' - [p0] + [] p1 = new_array(3, descr=arraydescr) p2 = new_array(3, descr=arraydescr) setarrayitem_gc(p1, 2, 10, descr=arraydescr) setarrayitem_gc(p2, 2, 13, descr=arraydescr) call(0, p1, p2, 0, 0, 3, descr=arraycopydescr) - jump(p2) + escape(p2) + jump() ''' expected = ''' - [p0] + [] p2 = new_array(3, descr=arraydescr) setarrayitem_gc(p2, 2, 10, descr=arraydescr) - jump(p2) + escape(p2) + jump() ''' - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_arraycopy_no_elem(self): """ this was actually observed in the wild @@ -3168,7 +3161,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): [p1] jump(p1) ''' - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_bound_lt(self): ops = """ @@ -3179,13 +3172,18 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 4) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + + self.optimize_loop(ops, expected, preamble) def test_bound_lt_noguard(self): ops = """ @@ -3200,7 +3198,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i2 = int_lt(i0, 5) jump(i2) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected, expected) def test_bound_lt_noopt(self): ops = """ @@ -3211,15 +3209,19 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 4) guard_false(i1) [] i2 = int_lt(i0, 5) guard_true(i2) [] - jump(4) + jump() """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [] + jump() + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lt_rev(self): ops = """ @@ -3230,13 +3232,17 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 4) guard_false(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lt_tripple(self): ops = """ @@ -3249,13 +3255,17 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i3) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 0) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lt_add(self): ops = """ @@ -3267,14 +3277,18 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i3) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 4) guard_true(i1) [] i2 = int_add(i0, 10) jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lt_add_before(self): ops = """ @@ -3286,14 +3300,18 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i1) [] jump(i0) """ - expected = """ + preamble = """ [i0] i2 = int_add(i0, 10) i3 = int_lt(i2, 15) guard_true(i3) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lt_add_ovf(self): ops = """ @@ -3306,14 +3324,18 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i3) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 4) guard_true(i1) [] i2 = int_add(i0, 10) jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lt_add_ovf_before(self): ops = """ @@ -3326,7 +3348,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i1) [] jump(i0) """ - expected = """ + preamble = """ [i0] i2 = int_add_ovf(i0, 10) guard_no_overflow() [] @@ -3334,7 +3356,12 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i3) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + i2 = int_add(i0, 10) + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lt_sub(self): ops = """ @@ -3346,14 +3373,18 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i3) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 4) guard_true(i1) [] i2 = int_sub(i0, 10) jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lt_sub_before(self): ops = """ @@ -3365,14 +3396,18 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i1) [] jump(i0) """ - expected = """ + preamble = """ [i0] i2 = int_sub(i0, 10) i3 = int_lt(i2, -5) guard_true(i3) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_ltle(self): ops = """ @@ -3383,13 +3418,17 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_lt(i0, 4) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lelt(self): ops = """ @@ -3400,13 +3439,17 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_le(i0, 4) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_gt(self): ops = """ @@ -3417,13 +3460,17 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_gt(i0, 5) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_gtge(self): ops = """ @@ -3434,13 +3481,17 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_gt(i0, 5) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_gegt(self): ops = """ @@ -3451,13 +3502,17 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_ge(i0, 5) guard_true(i1) [] jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_ovf(self): ops = """ @@ -3470,7 +3525,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_no_overflow() [] jump(i3) """ - expected = """ + preamble = """ [i0] i1 = int_ge(i0, 0) guard_true(i1) [] @@ -3479,7 +3534,14 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i3 = int_add(i0, 1) jump(i3) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + i2 = int_lt(i0, 10) + guard_true(i2) [] + i3 = int_add(i0, 1) + jump(i3) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_arraylen(self): ops = """ @@ -3499,7 +3561,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): setarrayitem_gc(p0, 0, p1) jump(i0, p0) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_bound_strlen(self): ops = """ @@ -3515,7 +3577,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i0 = strlen(p0) jump(p0) """ - self.optimize_strunicode_loop(ops, 'Not', expected) + self.optimize_strunicode_loop(ops, expected, expected) def test_addsub_const(self): ops = """ @@ -3532,7 +3594,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i4 = int_mul(i0, i1) jump(i4) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_addsub_int(self): ops = """ @@ -3549,7 +3611,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i4 = int_add(i0, i1) jump(i4, i10) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_addsub_int2(self): ops = """ @@ -3566,7 +3628,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i4 = int_add(i0, i1) jump(i4, i10) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_framestackdepth_overhead(self): ops = """ @@ -3587,17 +3649,144 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): jump(p0, i22) """ expected = """ - [p0, i22] - i1 = getfield_gc(p0, descr=valuedescr) - i2 = int_gt(i1, i22) - guard_false(i2) [] - i3 = int_add(i1, 1) + [p0, i22, i1] i331 = force_token() setfield_gc(p0, i1, descr=valuedescr) - jump(p0, i22) + jump(p0, i22, i1) + """ + self.optimize_loop(ops, expected) + + def test_setgetfield_raw(self): + ops = """ + [p4, p7, i30] + p16 = getfield_gc(p4, descr=valuedescr) + p17 = getarrayitem_gc(p4, 1, descr=arraydescr) + guard_value(p16, ConstPtr(myptr), descr=<Guard3>) [] + i1 = getfield_raw(p7, descr=nextdescr) + i2 = int_add(i1, i30) + setfield_raw(p7, 7, descr=nextdescr) + setfield_raw(p7, i2, descr=nextdescr) + jump(p4, p7, i30) + """ + expected = """ + [p4, p7, i30] + i1 = getfield_raw(p7, descr=nextdescr) + i2 = int_add(i1, i30) + setfield_raw(p7, 7, descr=nextdescr) + setfield_raw(p7, i2, descr=nextdescr) + jump(p4, p7, i30) + """ + self.optimize_loop(ops, expected, ops) + + def test_setgetarrayitem_raw(self): + ops = """ + [p4, p7, i30] + p16 = getfield_gc(p4, descr=valuedescr) + guard_value(p16, ConstPtr(myptr), descr=<Guard3>) [] + p17 = getarrayitem_gc(p4, 1, descr=arraydescr) + i1 = getarrayitem_raw(p7, 1, descr=arraydescr) + i2 = int_add(i1, i30) + setarrayitem_raw(p7, 1, 7, descr=arraydescr) + setarrayitem_raw(p7, 1, i2, descr=arraydescr) + jump(p4, p7, i30) + """ + expected = """ + [p4, p7, i30] + i1 = getarrayitem_raw(p7, 1, descr=arraydescr) + i2 = int_add(i1, i30) + setarrayitem_raw(p7, 1, 7, descr=arraydescr) + setarrayitem_raw(p7, 1, i2, descr=arraydescr) + jump(p4, p7, i30) + """ + self.optimize_loop(ops, expected, ops) + + def test_pure(self): + ops = """ + [p42] + p53 = getfield_gc(ConstPtr(myptr), descr=nextdescr) + p59 = getfield_gc_pure(p53, descr=valuedescr) + i61 = call(1, p59, descr=nonwritedescr) + jump(p42) + """ + expected = """ + [p42, p59] + i61 = call(1, p59, descr=nonwritedescr) + jump(p42, p59) + """ - self.optimize_loop(ops, 'Not, Not', expected) + self.node.value = 5 + self.optimize_loop(ops, expected) + + def test_getfield_guard_const(self): + ops = """ + [p0] + p20 = getfield_gc(p0, descr=nextdescr) + guard_nonnull(p20) [] + guard_class(p20, ConstClass(node_vtable)) [] + guard_class(p20, ConstClass(node_vtable)) [] + p23 = getfield_gc(p20, descr=valuedescr) + guard_isnull(p23) [] + guard_class(p20, ConstClass(node_vtable)) [] + guard_value(p20, ConstPtr(myptr)) [] + + p37 = getfield_gc(p0, descr=nextdescr) + guard_nonnull(p37) [] + guard_class(p37, ConstClass(node_vtable)) [] + guard_class(p37, ConstClass(node_vtable)) [] + p40 = getfield_gc(p37, descr=valuedescr) + guard_isnull(p40) [] + guard_class(p37, ConstClass(node_vtable)) [] + guard_value(p37, ConstPtr(myptr)) [] + + p64 = call_may_force(p23, p40, descr=plaincalldescr) + jump(p0) + """ + expected = """ + [p0] + p20 = getfield_gc(p0, descr=nextdescr) + guard_value(p20, ConstPtr(myptr)) [] + p23 = getfield_gc(p20, descr=valuedescr) + guard_isnull(p23) [] + p64 = call_may_force(NULL, NULL, descr=plaincalldescr) + jump(p0) + """ + self.optimize_loop(ops, expected, expected) + def test_getfield_guard_const_preamble(self): + ops = """ + [p0] + p01 = getfield_gc(p0, descr=nextdescr) + p02 = getfield_gc(p01, descr=valuedescr) + guard_value(p01, ConstPtr(myptr)) [] + p11 = getfield_gc(p0, descr=nextdescr) + p12 = getfield_gc(p11, descr=valuedescr) + guard_value(p11, ConstPtr(myptr)) [] + p64 = call_may_force(p02, p12, descr=plaincalldescr) + + p21 = getfield_gc(p0, descr=nextdescr) + p22 = getfield_gc(p21, descr=valuedescr) + guard_value(p21, ConstPtr(myptr)) [] + p31 = getfield_gc(p0, descr=nextdescr) + p32 = getfield_gc(p31, descr=valuedescr) + guard_value(p31, ConstPtr(myptr)) [] + p65 = call_may_force(p22, p32, descr=plaincalldescr) + jump(p0) + """ + expected = """ + [p0] + p01 = getfield_gc(p0, descr=nextdescr) + p02 = getfield_gc(p01, descr=valuedescr) + guard_value(p01, ConstPtr(myptr)) [] + p64 = call_may_force(p02, p02, descr=plaincalldescr) + + p21 = getfield_gc(p0, descr=nextdescr) + p22 = getfield_gc(p21, descr=valuedescr) + guard_value(p21, ConstPtr(myptr)) [] + p65 = call_may_force(p22, p22, descr=plaincalldescr) + jump(p0) + """ + self.optimize_loop(ops, expected, expected) + def test_addsub_ovf(self): ops = """ [i0] @@ -3614,7 +3803,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i2 = int_sub(i1, 5) jump(i2) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_subadd_ovf(self): ops = """ @@ -3632,7 +3821,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i2 = int_add(i1, 5) jump(i2) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_bound_and(self): ops = """ @@ -3677,7 +3866,176 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i15) [] jump(i1) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) + + def test_bound_xor(self): + ops = """ + [i0, i1, i2] + it1 = int_ge(i1, 0) + guard_true(it1) [] + it2 = int_gt(i2, 0) + guard_true(it2) [] + ix1 = int_xor(i0, i0) + ix1t = int_ge(ix1, 0) + guard_true(ix1t) [] + ix2 = int_xor(i0, i1) + ix2t = int_ge(ix2, 0) + guard_true(ix2t) [] + ix3 = int_xor(i1, i0) + ix3t = int_ge(ix3, 0) + guard_true(ix3t) [] + ix4 = int_xor(i1, i2) + ix4t = int_ge(ix4, 0) + guard_true(ix4t) [] + jump(i0, i1, i2) + """ + preamble = """ + [i0, i1, i2] + it1 = int_ge(i1, 0) + guard_true(it1) [] + it2 = int_gt(i2, 0) + guard_true(it2) [] + ix2 = int_xor(i0, i1) + ix2t = int_ge(ix2, 0) + guard_true(ix2t) [] + ix3 = int_xor(i1, i0) + ix3t = int_ge(ix3, 0) + guard_true(ix3t) [] + ix4 = int_xor(i1, i2) + jump(i0, i1, i2) + """ + expected = """ + [i0, i1, i2] + jump(i0, i1, i2) + """ + self.optimize_loop(ops, expected, preamble) + + def test_bound_floordiv(self): + ops = """ + [i0, i1, i2] + it1 = int_ge(i1, 0) + guard_true(it1) [] + it2 = int_gt(i2, 0) + guard_true(it2) [] + ix2 = int_floordiv(i0, i1) + ix2t = int_ge(ix2, 0) + guard_true(ix2t) [] + ix3 = int_floordiv(i1, i0) + ix3t = int_ge(ix3, 0) + guard_true(ix3t) [] + ix4 = int_floordiv(i1, i2) + ix4t = int_ge(ix4, 0) + guard_true(ix4t) [] + jump(i0, i1, i2) + """ + preamble = """ + [i0, i1, i2] + it1 = int_ge(i1, 0) + guard_true(it1) [] + it2 = int_gt(i2, 0) + guard_true(it2) [] + ix2 = int_floordiv(i0, i1) + ix2t = int_ge(ix2, 0) + guard_true(ix2t) [] + ix3 = int_floordiv(i1, i0) + ix3t = int_ge(ix3, 0) + guard_true(ix3t) [] + ix4 = int_floordiv(i1, i2) + jump(i0, i1, i2) + """ + expected = """ + [i0, i1, i2] + jump(i0, i1, i2) + """ + self.optimize_loop(ops, expected, preamble) + + def test_bound_int_is_zero(self): + ops = """ + [i1, i2a, i2b, i2c] + i3 = int_is_zero(i1) + i4 = int_gt(i2a, 7) + guard_true(i4) [] + i5 = int_is_zero(i2a) + guard_false(i5) [] + i6 = int_le(i2b, -7) + guard_true(i6) [] + i7 = int_is_zero(i2b) + guard_false(i7) [] + i8 = int_gt(i2c, -7) + guard_true(i8) [] + i9 = int_is_zero(i2c) + jump(i1, i2a, i2b, i2c) + """ + preamble = """ + [i1, i2a, i2b, i2c] + i3 = int_is_zero(i1) + i4 = int_gt(i2a, 7) + guard_true(i4) [] + i6 = int_le(i2b, -7) + guard_true(i6) [] + i8 = int_gt(i2c, -7) + guard_true(i8) [] + i9 = int_is_zero(i2c) + jump(i1, i2a, i2b, i2c) + """ + expected = """ + [i0, i1, i2, i3] + jump(i0, i1, i2, i3) + """ + self.optimize_loop(ops, expected, preamble) + + def test_division(self): + ops = """ + [i7, i6, i8] + it1 = int_gt(i7, 0) + guard_true(it1) [] + it2 = int_gt(i6, 0) + guard_true(it2) [] + i13 = int_is_zero(i6) + guard_false(i13) [] + i15 = int_and(i8, i6) + i17 = int_eq(i15, -1) + guard_false(i17) [] + i18 = int_floordiv(i7, i6) + i19 = int_xor(i7, i6) + i21 = int_lt(i19, 0) + i22 = int_mod(i7, i6) + i23 = int_is_true(i22) + i24 = int_and(i21, i23) + i25 = int_sub(i18, i24) + jump(i7, i25, i8) + """ + preamble = """ + [i7, i6, i8] + it1 = int_gt(i7, 0) + guard_true(it1) [] + it2 = int_gt(i6, 0) + guard_true(it2) [] + i15 = int_and(i8, i6) + i17 = int_eq(i15, -1) + guard_false(i17) [] + i18 = int_floordiv(i7, i6) + i19 = int_xor(i7, i6) + i22 = int_mod(i7, i6) + i23 = int_is_true(i22) + jump(i7, i18, i8) + """ + expected = """ + [i7, i6, i8] + it2 = int_gt(i6, 0) + guard_true(it2) [] + i15 = int_and(i8, i6) + i17 = int_eq(i15, -1) + guard_false(i17) [] + i18 = int_floordiv(i7, i6) + i19 = int_xor(i7, i6) + i22 = int_mod(i7, i6) + i23 = int_is_true(i22) + jump(i7, i18, i8) + """ + self.optimize_loop(ops, expected, preamble) + + def test_subsub_ovf(self): ops = """ @@ -3692,7 +4050,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i4) [] jump(i0) """ - expected = """ + preamble = """ [i0] i1 = int_sub_ovf(1, i0) guard_no_overflow() [] @@ -3701,45 +4059,56 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i3 = int_sub(1, i0) jump(i0) """ - self.optimize_loop(ops, 'Not', expected) + expected = """ + [i0] + jump(i0) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_eq(self): ops = """ - [i0, i1] + [] + i0 = escape() + i1 = escape() i2 = int_le(i0, 4) guard_true(i2) [] i3 = int_eq(i0, i1) guard_true(i3) [] i4 = int_lt(i1, 5) guard_true(i4) [] - jump(i0, i1) + jump() """ expected = """ - [i0, i1] + [] + i0 = escape() + i1 = escape() i2 = int_le(i0, 4) guard_true(i2) [] i3 = int_eq(i0, i1) guard_true(i3) [] - jump(i0, i1) + jump() """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected) def test_bound_eq_const(self): ops = """ - [i0] + [] + i0 = escape() i1 = int_eq(i0, 7) guard_true(i1) [] i2 = int_add(i0, 3) - jump(i2) + escape(i2) + jump() """ expected = """ - [i0] + [] + i0 = escape() i1 = int_eq(i0, 7) guard_true(i1) [] - jump(10) - + escape(10) + jump() """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_bound_eq_const_not(self): ops = """ @@ -3757,7 +4126,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): jump(i2) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_bound_ne_const(self): ops = """ @@ -3767,14 +4136,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i2 = int_add(i0, 3) jump(i2) """ - expected = """ - [i0] - i1 = int_ne(i0, 7) - guard_false(i1) [] - jump(10) - - """ - self.optimize_loop(ops, 'Not', expected) + py.test.raises(InvalidLoop, self.optimize_loop, ops, ops) def test_bound_ne_const_not(self): ops = """ @@ -3791,7 +4153,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i2 = int_add(i0, 3) jump(i2) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, expected) def test_bound_ltne(self): ops = """ @@ -3802,13 +4164,17 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i2) [] jump(i0, i1) """ - expected = """ + preamble = """ [i0, i1] i2 = int_lt(i0, 7) guard_true(i2) [] jump(i0, i1) """ - self.optimize_loop(ops, 'Not, Not', expected) + expected = """ + [i0, i1] + jump(i0, i1) + """ + self.optimize_loop(ops, expected, preamble) def test_bound_lege_const(self): ops = """ @@ -3820,17 +4186,150 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i3 = int_add(i0, 3) jump(i3) """ + py.test.raises(InvalidLoop, self.optimize_loop, ops, ops) + + def test_bound_lshift(self): + ops = """ + [i0, i1, i1b, i2, i3] + i4 = int_lt(i1, 7) + guard_true(i4) [] + i4b = int_lt(i1b, 7) + guard_true(i4b) [] + i4c = int_ge(i1b, 0) + guard_true(i4c) [] + i5 = int_lt(i3, 2) + guard_true(i5) [] + i6 = int_ge(i3, 0) + guard_true(i6) [] + i7 = int_lshift(i1, i3) + i8 = int_le(i7, 14) + guard_true(i8) [] + i8b = int_lshift(i1, i2) + i9 = int_le(i8b, 14) + guard_true(i9) [] + i10 = int_lshift(i0, i3) + i11 = int_le(i10, 14) + guard_true(i11) [] + i12 = int_lt(i0, 15) + guard_true(i12) [] + i13 = int_lshift(i1b, i3) + i14 = int_le(i13, 14) + guard_true(i14) [] + i15 = int_lshift(i1b, i2) + i16 = int_le(i15, 14) + guard_true(i16) [] + jump(i0, i1, i1b, i2, i3) + """ + preamble = """ + [i0, i1, i1b, i2, i3] + i4 = int_lt(i1, 7) + guard_true(i4) [] + i4b = int_lt(i1b, 7) + guard_true(i4b) [] + i4c = int_ge(i1b, 0) + guard_true(i4c) [] + i5 = int_lt(i3, 2) + guard_true(i5) [] + i6 = int_ge(i3, 0) + guard_true(i6) [] + i7 = int_lshift(i1, i3) + i8 = int_le(i7, 14) + guard_true(i8) [] + i8b = int_lshift(i1, i2) + i9 = int_le(i8b, 14) + guard_true(i9) [] + i10 = int_lshift(i0, i3) + i11 = int_le(i10, 14) + guard_true(i11) [] + i13 = int_lshift(i1b, i3) + i15 = int_lshift(i1b, i2) + i16 = int_le(i15, 14) + guard_true(i16) [] + jump(i0, i1, i1b, i2, i3) + """ expected = """ - [i0] - i1 = int_ge(i0, 7) - guard_true(i1) [] - i2 = int_le(i0, 7) - guard_true(i2) [] - jump(10) + [i0, i1, i1b, i2, i3] + jump(i0, i1, i1b, i2, i3) + """ + self.optimize_loop(ops, expected, preamble) + + def test_bound_rshift(self): + ops = """ + [i0, i1, i1b, i2, i3] + i4 = int_lt(i1, 7) + guard_true(i4) [] + i4b = int_lt(i1b, 7) + guard_true(i4b) [] + i4c = int_ge(i1b, 0) + guard_true(i4c) [] + i5 = int_lt(i3, 2) + guard_true(i5) [] + i6 = int_ge(i3, 0) + guard_true(i6) [] + i7 = int_rshift(i1, i3) + i8 = int_le(i7, 14) + guard_true(i8) [] + i8b = int_rshift(i1, i2) + i9 = int_le(i8b, 14) + guard_true(i9) [] + i10 = int_rshift(i0, i3) + i11 = int_le(i10, 14) + guard_true(i11) [] + i12 = int_lt(i0, 25) + guard_true(i12) [] + i13 = int_rshift(i1b, i3) + i14 = int_le(i13, 14) + guard_true(i14) [] + i15 = int_rshift(i1b, i2) + i16 = int_le(i15, 14) + guard_true(i16) [] + jump(i0, i1, i1b, i2, i3) + """ + preamble = """ + [i0, i1, i1b, i2, i3] + i4 = int_lt(i1, 7) + guard_true(i4) [] + i4b = int_lt(i1b, 7) + guard_true(i4b) [] + i4c = int_ge(i1b, 0) + guard_true(i4c) [] + i5 = int_lt(i3, 2) + guard_true(i5) [] + i6 = int_ge(i3, 0) + guard_true(i6) [] + i7 = int_rshift(i1, i3) + i8b = int_rshift(i1, i2) + i9 = int_le(i8b, 14) + guard_true(i9) [] + i10 = int_rshift(i0, i3) + i11 = int_le(i10, 14) + guard_true(i11) [] + i12 = int_lt(i0, 25) + guard_true(i12) [] + i13 = int_rshift(i1b, i3) + i15 = int_rshift(i1b, i2) + i16 = int_le(i15, 14) + guard_true(i16) [] + jump(i0, i1, i1b, i2, i3) + """ + expected = """ + [i0, i1, i1b, i2, i3] + jump(i0, i1, i1b, i2, i3) + """ + self.optimize_loop(ops, expected, preamble) + def test_bound_dont_backpropagate_rshift(self): + ops = """ + [i0] + i3 = int_rshift(i0, 1) + i5 = int_eq(i3, 1) + guard_true(i5) [] + i11 = int_add(i0, 1) + jump(i11) """ - self.optimize_loop(ops, 'Not', expected) + self.optimize_loop(ops, ops, ops) + def test_mul_ovf(self): ops = """ [i0, i1] @@ -3849,7 +4348,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i8) [] jump(i0, i1) """ - expected = """ + preamble = """ [i0, i1] i2 = int_and(i0, 255) i3 = int_lt(i1, 5) @@ -3861,7 +4360,11 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i8) [] jump(i0, i1) """ - self.optimize_loop(ops, 'Not, Not', expected) + expected = """ + [i0, i1] + jump(i0, i1) + """ + self.optimize_loop(ops, expected, preamble) def test_mul_ovf_before(self): ops = """ @@ -3878,7 +4381,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_false(i6) [] jump(i0, i1) """ - expected = """ + preamble = """ [i0, i1] i2 = int_and(i0, 255) i22 = int_add(i2, 1) @@ -3888,9 +4391,18 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i4) [] i5 = int_gt(i3, 2) guard_true(i5) [] - jump(i0, i1) + jump(i0, i1, i22) + """ + expected = """ + [i0, i1, i22] + i3 = int_mul(i22, i1) + i4 = int_lt(i3, 10) + guard_true(i4) [] + i5 = int_gt(i3, 2) + guard_true(i5) [] + jump(i0, i1, i22) """ - self.optimize_loop(ops, 'Not, Not', expected) + self.optimize_loop(ops, expected, preamble) def test_sub_ovf_before(self): ops = """ @@ -3908,7 +4420,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_false(i7) [] jump(i0, i1) """ - expected = """ + preamble = """ [i0, i1] i2 = int_and(i0, 255) i3 = int_sub_ovf(i2, i1) @@ -3917,18 +4429,101 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): guard_true(i4) [] i5 = int_ge(i3, 2) guard_true(i5) [] - jump(i0, i1) + jump(i0, i1, i2) """ - self.optimize_loop(ops, 'Not, Not', expected) - + expected = """ + [i0, i1, i2] + i3 = int_sub(i2, i1) + i4 = int_le(i3, 10) + guard_true(i4) [] + i5 = int_ge(i3, 2) + guard_true(i5) [] + jump(i0, i1, i2) + """ + self.optimize_loop(ops, expected, preamble) + + def test_value_proven_to_be_constant_after_two_iterations(self): + class FakeDescr(AbstractDescr): + def __init__(self, name): + self.name = name + def sort_key(self): + return id(self) + + + for n in ('inst_w_seq', 'inst_index', 'inst_w_list', 'inst_length', + 'inst_start', 'inst_step'): + self.namespace[n] = FakeDescr(n) + ops = """ + [p0, p1, p2, p3, i4, p5, i6, p7, p8, p9, p14] + guard_value(i4, 3) [] + guard_class(p9, 17278984) [] + guard_class(p9, 17278984) [] + p22 = getfield_gc(p9, descr=inst_w_seq) + guard_nonnull(p22) [] + i23 = getfield_gc(p9, descr=inst_index) + p24 = getfield_gc(p22, descr=inst_w_list) + guard_isnull(p24) [] + i25 = getfield_gc(p22, descr=inst_length) + i26 = int_ge(i23, i25) + guard_true(i26) [] + setfield_gc(p9, ConstPtr(myptr), descr=inst_w_seq) + + guard_nonnull(p14) [] + guard_class(p14, 17273920) [] + guard_class(p14, 17273920) [] + + p75 = new_with_vtable(17278984) + setfield_gc(p75, p14, descr=inst_w_seq) + setfield_gc(p75, 0, descr=inst_index) + guard_class(p75, 17278984) [] + guard_class(p75, 17278984) [] + p79 = getfield_gc(p75, descr=inst_w_seq) + guard_nonnull(p79) [] + i80 = getfield_gc(p75, descr=inst_index) + p81 = getfield_gc(p79, descr=inst_w_list) + guard_isnull(p81) [] + i82 = getfield_gc(p79, descr=inst_length) + i83 = int_ge(i80, i82) + guard_false(i83) [] + i84 = getfield_gc(p79, descr=inst_start) + i85 = getfield_gc(p79, descr=inst_step) + i86 = int_mul(i80, i85) + i87 = int_add(i84, i86) + i91 = int_add(i80, 1) + setfield_gc(p75, i91, descr=inst_index) + + p110 = same_as(ConstPtr(myptr)) + i112 = same_as(3) + i114 = same_as(39) + jump(p0, p1, p110, p3, i112, p5, i114, p7, p8, p75, p14) + """ + expected = """ + [p0, p1, p3, p5, p7, p8, p14, i82] + i115 = int_ge(1, i82) + guard_true(i115) [] + jump(p0, p1, p3, p5, p7, p8, p14, 1) + """ + self.optimize_loop(ops, expected) + + def test_inputargs_added_by_forcing_jumpargs(self): + # FXIME: Can this occur? + ops = """ + [p0, p1, pinv] + i1 = getfield_gc(pinv, descr=valuedescr) + p2 = new_with_vtable(ConstClass(node_vtable)) + setfield_gc(p2, i1, descr=nextdescr) + """ + # ---------- - def optimize_strunicode_loop(self, ops, spectext, optops): + def optimize_strunicode_loop(self, ops, optops, preamble=None): + if not preamble: + preamble = ops # FIXME: Force proper testing of preamble # check with the arguments passed in - self.optimize_loop(ops, spectext, optops) + self.optimize_loop(ops, optops, preamble) # check with replacing 'str' with 'unicode' everywhere - self.optimize_loop(ops.replace('str','unicode').replace('s"', 'u"'), - spectext, - optops.replace('str','unicode').replace('s"', 'u"')) + def r(s): + return s.replace('str','unicode').replace('s"', 'u"') + self.optimize_loop(r(ops), r(optops), r(preamble)) def test_newstr_1(self): ops = """ @@ -3942,7 +4537,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): [i0] jump(i0) """ - self.optimize_strunicode_loop(ops, 'Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_newstr_2(self): ops = """ @@ -3958,7 +4553,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): [i0, i1] jump(i1, i0) """ - self.optimize_strunicode_loop(ops, 'Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_concat_1(self): ops = """ @@ -3979,7 +4574,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): copystrcontent(p2, p3, 0, i4, i5) jump(p2, p3) """ - self.optimize_strunicode_loop(ops, 'Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_concat_vstr2_str(self): ops = """ @@ -4002,7 +4597,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): copystrcontent(p2, p3, 0, 2, i4) jump(i1, i0, p3) """ - self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_concat_str_vstr2(self): ops = """ @@ -4026,7 +4621,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i6 = int_add(i5, 1) # will be killed by the backend jump(i1, i0, p3) """ - self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_concat_str_str_str(self): ops = """ @@ -4053,7 +4648,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): copystrcontent(p3, p5, 0, i12b, i3b) jump(p2, p3, p5) """ - self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_concat_str_cstr1(self): ops = """ @@ -4072,7 +4667,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): i5 = int_add(i4, 1) # will be killed by the backend jump(p3) """ - self.optimize_strunicode_loop(ops, 'Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_concat_consts(self): ops = """ @@ -4083,12 +4678,18 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(p3) jump() """ + preamble = """ + [] + p3 = call(0, s"ab", s"cde", descr=strconcatdescr) + escape(p3) + jump() + """ expected = """ [] escape(s"abcde") jump() """ - self.optimize_strunicode_loop(ops, '', expected) + self.optimize_strunicode_loop(ops, expected, preamble) def test_str_slice_1(self): ops = """ @@ -4103,7 +4704,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): copystrcontent(p1, p2, i1, 0, i3) jump(p2, i1, i2) """ - self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_slice_2(self): ops = """ @@ -4117,7 +4718,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): copystrcontent(p1, p2, 0, 0, i2) jump(p2, i2) """ - self.optimize_strunicode_loop(ops, 'Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_slice_3(self): ops = """ @@ -4135,7 +4736,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): copystrcontent(p1, p3, i6, 0, i5) jump(p3, i1, i2, i3, i4) """ - self.optimize_strunicode_loop(ops, 'Not, Not, Not, Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_slice_getitem1(self): ops = """ @@ -4153,7 +4754,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i4) jump(p1, i1, i2, i3) """ - self.optimize_strunicode_loop(ops, 'Not, Not, Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_slice_plain(self): ops = """ @@ -4171,7 +4772,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i4) jump(i3, i4) """ - self.optimize_strunicode_loop(ops, 'Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) def test_str_slice_concat(self): ops = """ @@ -4192,10 +4793,10 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): copystrcontent(p2, p4, 0, i3, i4b) jump(p4, i1, i2, p2) """ - self.optimize_strunicode_loop(ops, 'Not, Not, Not, Not', expected) + self.optimize_strunicode_loop(ops, expected) # ---------- - def optimize_strunicode_loop_extradescrs(self, ops, spectext, optops): + def optimize_strunicode_loop_extradescrs(self, ops, optops, preamble=None): from pypy.jit.metainterp.optimizeopt import string class FakeCallInfoCollection: def callinfo_for_oopspec(self, oopspecindex): @@ -4210,7 +4811,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): oopspecindex) # self.callinfocollection = FakeCallInfoCollection() - self.optimize_strunicode_loop(ops, spectext, optops) + self.optimize_strunicode_loop(ops, optops, preamble) def test_str_equal_noop1(self): ops = """ @@ -4219,7 +4820,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1, p2) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not', ops) + self.optimize_strunicode_loop_extradescrs(ops, ops) def test_str_equal_noop2(self): ops = """ @@ -4244,7 +4845,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1, p2, p3) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not', + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_slice1(self): @@ -4262,7 +4863,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1, i1, i2, p3) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not, Not', + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_slice2(self): @@ -4280,7 +4881,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1, i1, i2, p3) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not, Not', + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_slice3(self): @@ -4294,14 +4895,13 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): """ expected = """ [p1, i1, i2, p3] - guard_nonnull(p3) [] i4 = int_sub(i2, i1) i0 = call(0, p1, i1, i4, p3, descr=streq_slice_nonnull_descr) escape(i0) jump(p1, i1, i2, p3) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not, Not', - expected) + self.optimize_strunicode_loop_extradescrs(ops, + expected, ops) def test_str_equal_slice4(self): ops = """ @@ -4318,7 +4918,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1, i1, i2) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not', + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_slice5(self): @@ -4338,7 +4938,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1, i1, i2, i3) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not, Not', + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_none1(self): @@ -4354,7 +4954,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_none2(self): ops = """ @@ -4369,7 +4969,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_nonnull1(self): ops = """ @@ -4381,12 +4981,11 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): """ expected = """ [p1] - guard_nonnull(p1) [] i0 = call(0, p1, s"hello world", descr=streq_nonnull_descr) escape(i0) jump(p1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_nonnull2(self): ops = """ @@ -4398,13 +4997,12 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): """ expected = """ [p1] - guard_nonnull(p1) [] i1 = strlen(p1) i0 = int_eq(i1, 0) escape(i0) jump(p1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_nonnull3(self): ops = """ @@ -4416,12 +5014,11 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): """ expected = """ [p1] - guard_nonnull(p1) [] i0 = call(0, p1, 120, descr=streq_nonnull_char_descr) escape(i0) jump(p1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_nonnull4(self): ops = """ @@ -4446,7 +5043,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1, p2) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_chars0(self): ops = """ @@ -4461,7 +5058,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(1) jump(i1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_chars1(self): ops = """ @@ -4478,7 +5075,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(i1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_chars2(self): ops = """ @@ -4499,7 +5096,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(i1, i2) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_chars3(self): ops = """ @@ -4514,7 +5111,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(i0) jump(p1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str_equal_lengthmismatch1(self): ops = """ @@ -4530,7 +5127,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(0) jump(i1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str2unicode_constant(self): ops = """ @@ -4544,7 +5141,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(u"xy") jump() """ - self.optimize_strunicode_loop_extradescrs(ops, '', expected) + self.optimize_strunicode_loop_extradescrs(ops, expected) def test_str2unicode_nonconstant(self): ops = """ @@ -4553,12 +5150,13 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): escape(p1) jump(p1) """ - self.optimize_strunicode_loop_extradescrs(ops, 'Not', ops) + self.optimize_strunicode_loop_extradescrs(ops, ops) # more generally, supporting non-constant but virtual cases is # not obvious, because of the exception UnicodeDecodeError that # can be raised by ll_str2unicode() + ##class TestOOtype(OptimizeOptTest, OOtypeMixin): ## def test_instanceof(self): @@ -4572,7 +5170,7 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): ## [i0] ## jump(1) ## """ -## self.optimize_loop(ops, 'Not', expected) +## self.optimize_loop(ops, expected) ## def test_instanceof_guard_class(self): ## ops = """ @@ -4586,4 +5184,4 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): ## guard_class(p0, ConstClass(node_vtable)) [] ## jump(1, p0) ## """ -## self.optimize_loop(ops, 'Not, Not', expected) +## self.optimize_loop(ops, expected) diff --git a/pypy/jit/metainterp/test/test_optimizeutil.py b/pypy/jit/metainterp/test/test_optimizeutil.py new file mode 100644 index 0000000000..1446fecfe8 --- /dev/null +++ b/pypy/jit/metainterp/test/test_optimizeutil.py @@ -0,0 +1,241 @@ +import py, random + +from pypy.rpython.lltypesystem import lltype, llmemory, rclass, rstr +from pypy.rpython.ootypesystem import ootype +from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE + +from pypy.jit.backend.llgraph import runner +from pypy.jit.metainterp.history import (BoxInt, BoxPtr, ConstInt, ConstPtr, + Const, TreeLoop, BoxObj, + ConstObj, AbstractDescr) +from pypy.jit.metainterp.optimizeutil import sort_descrs, InvalidLoop +from pypy.jit.codewriter.effectinfo import EffectInfo +from pypy.jit.codewriter.heaptracker import register_known_gctype, adr2int +from pypy.jit.tool.oparser import parse + +def test_sort_descrs(): + class PseudoDescr(AbstractDescr): + def __init__(self, n): + self.n = n + def sort_key(self): + return self.n + for i in range(17): + lst = [PseudoDescr(j) for j in range(i)] + lst2 = lst[:] + random.shuffle(lst2) + sort_descrs(lst2) + assert lst2 == lst + +# ____________________________________________________________ + +class LLtypeMixin(object): + type_system = 'lltype' + + def get_class_of_box(self, box): + return box.getref(rclass.OBJECTPTR).typeptr + + node_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) + node_vtable.name = rclass.alloc_array_name('node') + node_vtable_adr = llmemory.cast_ptr_to_adr(node_vtable) + node_vtable2 = lltype.malloc(OBJECT_VTABLE, immortal=True) + node_vtable2.name = rclass.alloc_array_name('node2') + node_vtable_adr2 = llmemory.cast_ptr_to_adr(node_vtable2) + cpu = runner.LLtypeCPU(None) + + NODE = lltype.GcForwardReference() + NODE.become(lltype.GcStruct('NODE', ('parent', OBJECT), + ('value', lltype.Signed), + ('floatval', lltype.Float), + ('next', lltype.Ptr(NODE)))) + NODE2 = lltype.GcStruct('NODE2', ('parent', NODE), + ('other', lltype.Ptr(NODE))) + node = lltype.malloc(NODE) + node.parent.typeptr = node_vtable + nodebox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node)) + myptr = nodebox.value + myptr2 = lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(NODE)) + nodebox2 = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node)) + nodesize = cpu.sizeof(NODE) + nodesize2 = cpu.sizeof(NODE2) + valuedescr = cpu.fielddescrof(NODE, 'value') + floatdescr = cpu.fielddescrof(NODE, 'floatval') + nextdescr = cpu.fielddescrof(NODE, 'next') + otherdescr = cpu.fielddescrof(NODE2, 'other') + + NODEOBJ = lltype.GcStruct('NODEOBJ', ('parent', OBJECT), + ('ref', lltype.Ptr(OBJECT))) + nodeobj = lltype.malloc(NODEOBJ) + nodeobjvalue = lltype.cast_opaque_ptr(llmemory.GCREF, nodeobj) + refdescr = cpu.fielddescrof(NODEOBJ, 'ref') + + arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Signed)) + floatarraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Float)) + + # a GcStruct not inheriting from OBJECT + S = lltype.GcStruct('TUPLE', ('a', lltype.Signed), ('b', lltype.Ptr(NODE))) + ssize = cpu.sizeof(S) + adescr = cpu.fielddescrof(S, 'a') + bdescr = cpu.fielddescrof(S, 'b') + sbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S))) + arraydescr2 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(S))) + + T = lltype.GcStruct('TUPLE', + ('c', lltype.Signed), + ('d', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE))))) + tsize = cpu.sizeof(T) + cdescr = cpu.fielddescrof(T, 'c') + ddescr = cpu.fielddescrof(T, 'd') + arraydescr3 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(NODE))) + + U = lltype.GcStruct('U', + ('parent', OBJECT), + ('one', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE))))) + u_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) + u_vtable_adr = llmemory.cast_ptr_to_adr(u_vtable) + usize = cpu.sizeof(U) + onedescr = cpu.fielddescrof(U, 'one') + + FUNC = lltype.FuncType([lltype.Signed], lltype.Signed) + plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) + nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([], [], [])) + writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([], [adescr], [])) + writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([], [adescr], [arraydescr])) + readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([adescr], [], [])) + mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([nextdescr], [], [], + EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE)) + arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY)) + + for _name, _os in [ + ('strconcatdescr', 'OS_STR_CONCAT'), + ('strslicedescr', 'OS_STR_SLICE'), + ('strequaldescr', 'OS_STR_EQUAL'), + ('streq_slice_checknull_descr', 'OS_STREQ_SLICE_CHECKNULL'), + ('streq_slice_nonnull_descr', 'OS_STREQ_SLICE_NONNULL'), + ('streq_slice_char_descr', 'OS_STREQ_SLICE_CHAR'), + ('streq_nonnull_descr', 'OS_STREQ_NONNULL'), + ('streq_nonnull_char_descr', 'OS_STREQ_NONNULL_CHAR'), + ('streq_checknull_char_descr', 'OS_STREQ_CHECKNULL_CHAR'), + ('streq_lengthok_descr', 'OS_STREQ_LENGTHOK'), + ]: + _oopspecindex = getattr(EffectInfo, _os) + locals()[_name] = \ + cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([], [], [], oopspecindex=_oopspecindex)) + # + _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI')) + locals()[_name.replace('str', 'unicode')] = \ + cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([], [], [], oopspecindex=_oopspecindex)) + + s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, + EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE)) + # + + class LoopToken(AbstractDescr): + pass + asmdescr = LoopToken() # it can be whatever, it's not a descr though + + from pypy.jit.metainterp.virtualref import VirtualRefInfo + class FakeWarmRunnerDesc: + pass + FakeWarmRunnerDesc.cpu = cpu + vrefinfo = VirtualRefInfo(FakeWarmRunnerDesc) + virtualtokendescr = vrefinfo.descr_virtual_token + virtualrefindexdescr = vrefinfo.descr_virtualref_index + virtualforceddescr = vrefinfo.descr_forced + jit_virtual_ref_vtable = vrefinfo.jit_virtual_ref_vtable + jvr_vtable_adr = llmemory.cast_ptr_to_adr(jit_virtual_ref_vtable) + + register_known_gctype(cpu, node_vtable, NODE) + register_known_gctype(cpu, node_vtable2, NODE2) + register_known_gctype(cpu, u_vtable, U) + register_known_gctype(cpu, jit_virtual_ref_vtable,vrefinfo.JIT_VIRTUAL_REF) + + namespace = locals() + +class OOtypeMixin_xxx_disabled(object): + type_system = 'ootype' + +## def get_class_of_box(self, box): +## root = box.getref(ootype.ROOT) +## return ootype.classof(root) + +## cpu = runner.OOtypeCPU(None) +## NODE = ootype.Instance('NODE', ootype.ROOT, {}) +## NODE._add_fields({'value': ootype.Signed, +## 'floatval' : ootype.Float, +## 'next': NODE}) +## NODE2 = ootype.Instance('NODE2', NODE, {'other': NODE}) + +## node_vtable = ootype.runtimeClass(NODE) +## node_vtable_adr = ootype.cast_to_object(node_vtable) +## node_vtable2 = ootype.runtimeClass(NODE2) +## node_vtable_adr2 = ootype.cast_to_object(node_vtable2) + +## node = ootype.new(NODE) +## nodebox = BoxObj(ootype.cast_to_object(node)) +## myptr = nodebox.value +## myptr2 = ootype.cast_to_object(ootype.new(NODE)) +## nodebox2 = BoxObj(ootype.cast_to_object(node)) +## valuedescr = cpu.fielddescrof(NODE, 'value') +## floatdescr = cpu.fielddescrof(NODE, 'floatval') +## nextdescr = cpu.fielddescrof(NODE, 'next') +## otherdescr = cpu.fielddescrof(NODE2, 'other') +## nodesize = cpu.typedescrof(NODE) +## nodesize2 = cpu.typedescrof(NODE2) + +## arraydescr = cpu.arraydescrof(ootype.Array(ootype.Signed)) +## floatarraydescr = cpu.arraydescrof(ootype.Array(ootype.Float)) + +## # a plain Record +## S = ootype.Record({'a': ootype.Signed, 'b': NODE}) +## ssize = cpu.typedescrof(S) +## adescr = cpu.fielddescrof(S, 'a') +## bdescr = cpu.fielddescrof(S, 'b') +## sbox = BoxObj(ootype.cast_to_object(ootype.new(S))) +## arraydescr2 = cpu.arraydescrof(ootype.Array(S)) + +## T = ootype.Record({'c': ootype.Signed, +## 'd': ootype.Array(NODE)}) +## tsize = cpu.typedescrof(T) +## cdescr = cpu.fielddescrof(T, 'c') +## ddescr = cpu.fielddescrof(T, 'd') +## arraydescr3 = cpu.arraydescrof(ootype.Array(NODE)) + +## U = ootype.Instance('U', ootype.ROOT, {'one': ootype.Array(NODE)}) +## usize = cpu.typedescrof(U) +## onedescr = cpu.fielddescrof(U, 'one') +## u_vtable = ootype.runtimeClass(U) +## u_vtable_adr = ootype.cast_to_object(u_vtable) + +## # force a consistent order +## valuedescr.sort_key() +## nextdescr.sort_key() +## adescr.sort_key() +## bdescr.sort_key() + +## FUNC = lltype.FuncType([lltype.Signed], lltype.Signed) +## nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) # XXX fix ootype + +## cpu.class_sizes = {node_vtable_adr: cpu.typedescrof(NODE), +## node_vtable_adr2: cpu.typedescrof(NODE2), +## u_vtable_adr: cpu.typedescrof(U)} +## namespace = locals() + +class BaseTest(object): + invent_fail_descr = None + + def parse(self, s, boxkinds=None): + return parse(s, self.cpu, self.namespace, + type_system=self.type_system, + boxkinds=boxkinds, + invent_fail_descr=self.invent_fail_descr) + +# ____________________________________________________________ + diff --git a/pypy/jit/metainterp/test/test_recursive.py b/pypy/jit/metainterp/test/test_recursive.py index 7d933c8d4c..af234d4ce2 100644 --- a/pypy/jit/metainterp/test/test_recursive.py +++ b/pypy/jit/metainterp/test/test_recursive.py @@ -575,7 +575,7 @@ class RecursiveTests: result += f('-c-----------l-', i+100) self.meta_interp(g, [10], backendopt=True) self.check_aborted_count(1) - self.check_history(call_assembler=1, call=0) + self.check_loops(call_assembler=1, call=0) self.check_tree_loop_count(3) def test_directly_call_assembler(self): @@ -599,19 +599,19 @@ class RecursiveTests: get_printable_location = lambda codeno : str(codeno)) def portal(codeno, j): - i = 0 + i = 1 while 1: driver.jit_merge_point(codeno=codeno, i=i, j=j) - if i == 1: + if (i >> 1) == 1: if j == 0: return portal(2, j - 1) - elif i == 3: + elif i == 5: return i += 1 driver.can_enter_jit(codeno=codeno, i=i, j=j) - portal(2, 50) + portal(2, 5) from pypy.jit.metainterp import compile, pyjitpl pyjitpl._warmrunnerdesc = None @@ -623,8 +623,8 @@ class RecursiveTests: original_ctc = compile.compile_tmp_callback try: compile.compile_tmp_callback = my_ctc - self.meta_interp(portal, [2, 20], inline=True) - self.check_loops(call_assembler=1, call_may_force=0, + self.meta_interp(portal, [2, 5], inline=True) + self.check_loops(call_assembler=2, call_may_force=0, everywhere=True) finally: compile.compile_tmp_callback = original_ctc @@ -652,14 +652,14 @@ class RecursiveTests: get_printable_location = lambda codeno : str(codeno)) def portal(codeno, frame): - i = 0 + i = 1 while 1: driver.jit_merge_point(codeno=codeno, i=i, frame=frame) - if i == 1: + if (i >> 1) == 1: if frame.j == 0: return portal(2, Frame(frame.j - 1)) - elif i == 3: + elif i == 5: return i += 1 driver.can_enter_jit(codeno=codeno, i=i, frame=frame) @@ -667,7 +667,7 @@ class RecursiveTests: def main(codeno, j): portal(codeno, Frame(j)) - main(2, 50) + main(2, 5) from pypy.jit.metainterp import compile, pyjitpl pyjitpl._warmrunnerdesc = None @@ -679,8 +679,8 @@ class RecursiveTests: original_ctc = compile.compile_tmp_callback try: compile.compile_tmp_callback = my_ctc - self.meta_interp(main, [2, 20], inline=True) - self.check_loops(call_assembler=1, call_may_force=0, + self.meta_interp(main, [2, 5], inline=True) + self.check_loops(call_assembler=2, call_may_force=0, everywhere=True) finally: compile.compile_tmp_callback = original_ctc @@ -1020,7 +1020,7 @@ class RecursiveTests: res = self.meta_interp(portal, [2, 0], inline=True, policy=StopAtXPolicy(residual)) assert res == portal(2, 0) - self.check_loops(call_assembler=2) + self.check_loops(call_assembler=4, everywhere=True) def test_inline_without_hitting_the_loop(self): driver = JitDriver(greens = ['codeno'], reds = ['i'], diff --git a/pypy/jit/metainterp/test/test_resume.py b/pypy/jit/metainterp/test/test_resume.py index fdacd374f5..bd891d9ea6 100644 --- a/pypy/jit/metainterp/test/test_resume.py +++ b/pypy/jit/metainterp/test/test_resume.py @@ -1,11 +1,12 @@ import py from pypy.rpython.lltypesystem import lltype, llmemory, rffi -from pypy.jit.metainterp.optimizeopt.virtualize import VirtualValue, OptValue, VArrayValue +from pypy.jit.metainterp.optimizeopt.optimizer import OptValue +from pypy.jit.metainterp.optimizeopt.virtualize import VirtualValue, VArrayValue from pypy.jit.metainterp.optimizeopt.virtualize import VStructValue from pypy.jit.metainterp.resume import * from pypy.jit.metainterp.history import BoxInt, BoxPtr, ConstInt from pypy.jit.metainterp.history import ConstPtr, ConstFloat -from pypy.jit.metainterp.test.test_optimizefindnode import LLtypeMixin +from pypy.jit.metainterp.test.test_optimizeutil import LLtypeMixin from pypy.jit.metainterp import executor from pypy.jit.codewriter import heaptracker diff --git a/pypy/jit/metainterp/test/test_send.py b/pypy/jit/metainterp/test/test_send.py index a69e8da467..aab86a37f4 100644 --- a/pypy/jit/metainterp/test/test_send.py +++ b/pypy/jit/metainterp/test/test_send.py @@ -2,10 +2,11 @@ import py from pypy.rlib.jit import JitDriver, hint, purefunction from pypy.jit.codewriter.policy import StopAtXPolicy from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin - +from pypy.rlib.jit import OPTIMIZER_FULL, OPTIMIZER_SIMPLE class SendTests: - + optimizer=OPTIMIZER_FULL + def test_green_send(self): myjitdriver = JitDriver(greens = ['i'], reds = ['counter']) lst = ["123", "45"] @@ -98,7 +99,7 @@ class SendTests: return res res = self.meta_interp(f, [4, -1]) assert res == 145 - self.check_loops(int_add = 1) + self.check_loops(int_add = 1, everywhere=True) def test_oosend_base(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'w']) @@ -149,9 +150,9 @@ class SendTests: def foo(self): return 3 def externfn(y): - if y % 4 == 0: return W1() - elif y % 4 == 3: return W2() - else: return W3() + lst = [W1, W1, W2, W2, W3, W3, W2, W1, W3] + W = lst[y % len(lst)] + return W() def f(y): while y > 0: myjitdriver.can_enter_jit(y=y) @@ -161,10 +162,16 @@ class SendTests: y -= 1 return 42 policy = StopAtXPolicy(externfn) + for j in range(69, 75): res = self.meta_interp(f, [j], policy=policy) assert res == 42 - self.check_loop_count(3) + if self.optimizer != OPTIMIZER_FULL: + self.check_enter_count(3) + self.check_loop_count(3) + else: + self.check_enter_count_at_most(5) + self.check_loop_count_at_most(5) def test_oosend_guard_failure(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'w']) @@ -203,6 +210,7 @@ class SendTests: # InvalidLoop condition, and was then unrolled, giving two copies # of the body in a single bigger loop with no failing guard except # the final one. + py.test.skip('dissabled "try to trace some more when compile fails"') self.check_loop_count(1) self.check_loops(guard_class=0, int_add=2, int_sub=2) @@ -244,6 +252,7 @@ class SendTests: assert res == f(3, 28) res = self.meta_interp(f, [4, 28]) assert res == f(4, 28) + py.test.skip('dissabled "try to trace some more when compile fails"') self.check_loop_count(1) self.check_loops(guard_class=0, int_add=2, int_sub=2) @@ -347,7 +356,10 @@ class SendTests: assert res == f(198) # we get two TreeLoops: an initial one, and one entering from # the interpreter - self.check_tree_loop_count(2) + if self.optimizer != OPTIMIZER_FULL: + self.check_tree_loop_count(1) + else: + self.check_tree_loop_count(2) def test_two_behaviors(self): py.test.skip("XXX fix me!!!!!!! problem in optimize.py") @@ -400,7 +412,10 @@ class SendTests: # we expect 1 loop, 1 entry bridge, and 1 bridge going from the # loop back to the start of the entry bridge self.check_loop_count(2) # 1 loop + 1 bridge - self.check_tree_loop_count(2) # 1 loop + 1 entry bridge (argh) + if self.optimizer != OPTIMIZER_FULL: + self.check_tree_loop_count(1) # 1 loop + else: + self.check_tree_loop_count(2) # 1 loop + 1 entry bridge (argh) self.check_aborted_count(0) def test_three_cases(self): @@ -421,7 +436,10 @@ class SendTests: return node.x res = self.meta_interp(f, [55]) assert res == f(55) - self.check_tree_loop_count(2) + if self.optimizer != OPTIMIZER_FULL: + self.check_tree_loop_count(1) + else: + self.check_tree_loop_count(2) def test_three_classes(self): class Base: @@ -451,7 +469,10 @@ class SendTests: return n res = self.meta_interp(f, [55], policy=StopAtXPolicy(extern)) assert res == f(55) - self.check_tree_loop_count(2) + if self.optimizer != OPTIMIZER_FULL: + self.check_tree_loop_count(1) + else: + self.check_tree_loop_count(2) def test_bug1(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'node']) diff --git a/pypy/jit/metainterp/test/test_loop_nopspec.py b/pypy/jit/metainterp/test/test_send_nounroll.py index b50ca850d4..e1e5da9134 100644 --- a/pypy/jit/metainterp/test/test_loop_nopspec.py +++ b/pypy/jit/metainterp/test/test_send_nounroll.py @@ -1,12 +1,14 @@ from pypy.jit.metainterp.test import test_loop, test_send from pypy.jit.metainterp.warmspot import ll_meta_interp -from pypy.rlib.jit import OPTIMIZER_NO_PERFECTSPEC +from pypy.rlib.jit import OPTIMIZER_NO_UNROLL from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin class LoopNoPSpecTest(test_send.SendTests): + optimizer=OPTIMIZER_NO_UNROLL + def meta_interp(self, func, args, **kwds): - return ll_meta_interp(func, args, optimizer=OPTIMIZER_NO_PERFECTSPEC, + return ll_meta_interp(func, args, optimizer=self.optimizer, CPUClass=self.CPUClass, type_system=self.type_system, **kwds) @@ -20,6 +22,7 @@ class LoopNoPSpecTest(test_send.SendTests): def check_jumps(self, maxcount): pass + class TestLLtype(LoopNoPSpecTest, LLJitMixin): pass diff --git a/pypy/jit/metainterp/test/test_loop_dummy.py b/pypy/jit/metainterp/test/test_send_simple.py index ca1e189691..53bf9a54a4 100644 --- a/pypy/jit/metainterp/test/test_loop_dummy.py +++ b/pypy/jit/metainterp/test/test_send_simple.py @@ -6,8 +6,9 @@ from pypy.rlib.jit import OPTIMIZER_SIMPLE from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin class LoopDummyTest(test_send.SendTests): + optimizer=OPTIMIZER_SIMPLE def meta_interp(self, func, args, **kwds): - return ll_meta_interp(func, args, optimizer=OPTIMIZER_SIMPLE, + return ll_meta_interp(func, args, optimizer=self.optimizer, CPUClass=self.CPUClass, type_system=self.type_system, **kwds) diff --git a/pypy/jit/metainterp/test/test_specnode.py b/pypy/jit/metainterp/test/test_specnode.py deleted file mode 100644 index 4da76cae0e..0000000000 --- a/pypy/jit/metainterp/test/test_specnode.py +++ /dev/null @@ -1,132 +0,0 @@ -from pypy.rpython.lltypesystem import lltype, llmemory -from pypy.jit.metainterp.history import AbstractDescr, BoxPtr, ConstInt, ConstPtr -from pypy.jit.metainterp.specnode import prebuiltNotSpecNode -from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode -from pypy.jit.metainterp.specnode import VirtualArraySpecNode -from pypy.jit.metainterp.specnode import VirtualStructSpecNode -from pypy.jit.metainterp.specnode import ConstantSpecNode -from pypy.jit.metainterp.specnode import equals_specnodes -from pypy.jit.metainterp.specnode import more_general_specnodes -from pypy.jit.metainterp.test.test_optimizefindnode import LLtypeMixin - -def _get_vspecnode(classnum=123): - return VirtualInstanceSpecNode(ConstInt(classnum), - [(LLtypeMixin.valuedescr, prebuiltNotSpecNode), - (LLtypeMixin.nextdescr, prebuiltNotSpecNode)]) - -def _get_aspecnode(length=2): - return VirtualArraySpecNode(LLtypeMixin.arraydescr, - [prebuiltNotSpecNode] * length) - -def _get_sspecnode(): - return VirtualStructSpecNode(LLtypeMixin.ssize, - [(LLtypeMixin.adescr, prebuiltNotSpecNode), - (LLtypeMixin.bdescr, prebuiltNotSpecNode)]) - -def _get_cspecnode(s): - from pypy.rpython.module.support import LLSupport - llstr = lltype.cast_opaque_ptr(llmemory.GCREF, LLSupport.to_rstr(s)) - box = ConstPtr(llstr) - return ConstantSpecNode(box) - -def test_equals_specnodes(): - assert equals_specnodes([prebuiltNotSpecNode, prebuiltNotSpecNode], - [prebuiltNotSpecNode, prebuiltNotSpecNode]) - vspecnode1 = _get_vspecnode(1) - vspecnode2 = _get_vspecnode(2) - assert equals_specnodes([vspecnode1], [vspecnode1]) - assert not equals_specnodes([vspecnode1], [vspecnode2]) - assert not equals_specnodes([vspecnode1], [prebuiltNotSpecNode]) - assert not equals_specnodes([prebuiltNotSpecNode], [vspecnode2]) - aspecnode1 = _get_aspecnode(1) - aspecnode2 = _get_aspecnode(2) - assert equals_specnodes([aspecnode2], [aspecnode2]) - assert not equals_specnodes([aspecnode1], [aspecnode2]) - assert not equals_specnodes([aspecnode1], [prebuiltNotSpecNode]) - assert not equals_specnodes([prebuiltNotSpecNode], [aspecnode2]) - sspecnode1 = _get_sspecnode() - assert equals_specnodes([sspecnode1], [sspecnode1]) - assert not equals_specnodes([sspecnode1], [prebuiltNotSpecNode]) - assert not equals_specnodes([prebuiltNotSpecNode], [sspecnode1]) - # - foonode = _get_cspecnode('foo') - barnode = _get_cspecnode('bar') - assert equals_specnodes([foonode], [foonode]) - assert not equals_specnodes([foonode], [barnode]) - assert not equals_specnodes([foonode], [prebuiltNotSpecNode]) - -def test_more_general_specnodes(): - assert more_general_specnodes([prebuiltNotSpecNode, prebuiltNotSpecNode], - [prebuiltNotSpecNode, prebuiltNotSpecNode]) - vspecnode1 = _get_vspecnode(1) - vspecnode2 = _get_vspecnode(2) - assert more_general_specnodes([vspecnode1], [vspecnode1]) - assert not more_general_specnodes([vspecnode1], [vspecnode2]) - assert not more_general_specnodes([vspecnode1], [prebuiltNotSpecNode]) - assert more_general_specnodes([prebuiltNotSpecNode], [vspecnode2]) - aspecnode1 = _get_aspecnode(1) - aspecnode2 = _get_aspecnode(2) - assert more_general_specnodes([aspecnode2], [aspecnode2]) - assert not more_general_specnodes([aspecnode1], [aspecnode2]) - assert not more_general_specnodes([aspecnode1], [prebuiltNotSpecNode]) - assert more_general_specnodes([prebuiltNotSpecNode], [aspecnode2]) - sspecnode1 = _get_sspecnode() - assert more_general_specnodes([sspecnode1], [sspecnode1]) - assert not more_general_specnodes([sspecnode1], [prebuiltNotSpecNode]) - assert more_general_specnodes([prebuiltNotSpecNode], [sspecnode1]) - # - foonode = _get_cspecnode('foo') - barnode = _get_cspecnode('bar') - assert more_general_specnodes([foonode], [foonode]) - assert not more_general_specnodes([foonode], [barnode]) - assert not more_general_specnodes([foonode], [prebuiltNotSpecNode]) - assert more_general_specnodes([prebuiltNotSpecNode], [foonode]) - -def test_extract_runtime_data_0(): - res = [] - node = _get_cspecnode('foo') - node.extract_runtime_data("cpu", "box1", res) - assert res == [] - -def test_extract_runtime_data_1(): - res = [] - prebuiltNotSpecNode.extract_runtime_data("cpu", "box1", res) - prebuiltNotSpecNode.extract_runtime_data("cpu", "box2", res) - assert res == ["box1", "box2"] - -def test_extract_runtime_data_2(): - structure = lltype.malloc(LLtypeMixin.NODE) - structure.value = 515 - structure.next = lltype.malloc(LLtypeMixin.NODE) - structbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, structure)) - vspecnode = _get_vspecnode() - res = [] - vspecnode.extract_runtime_data(LLtypeMixin.cpu, structbox, res) - assert len(res) == 2 - assert res[0].value == structure.value - assert res[1].value._obj.container._as_ptr() == structure.next - -def test_extract_runtime_data_3(): - array = lltype.malloc(lltype.GcArray(lltype.Signed), 2) - array[0] = 123 - array[1] = 456 - arraybox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, array)) - aspecnode = _get_aspecnode() - res = [] - aspecnode.extract_runtime_data(LLtypeMixin.cpu, arraybox, res) - assert len(res) == 2 - assert res[0].value == 123 - assert res[1].value == 456 - -def test_extract_runtime_data_4(): - struct = lltype.malloc(LLtypeMixin.S) - struct.a = 123 - struct.b = lltype.malloc(LLtypeMixin.NODE) - structbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, struct)) - sspecnode = _get_sspecnode() - res = [] - sspecnode.extract_runtime_data(LLtypeMixin.cpu, structbox, res) - assert len(res) == 2 - assert res[0].value == 123 - assert (lltype.cast_opaque_ptr(lltype.Ptr(LLtypeMixin.NODE), res[1].value) - == struct.b) diff --git a/pypy/jit/metainterp/test/test_virtual.py b/pypy/jit/metainterp/test/test_virtual.py index a02dad1a7c..6c4641127b 100644 --- a/pypy/jit/metainterp/test/test_virtual.py +++ b/pypy/jit/metainterp/test/test_virtual.py @@ -12,7 +12,7 @@ class VirtualTests: def _freeze_(self): return True - def test_virtualized(self): + def test_virtualized1(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'node']) def f(n): node = self._new() @@ -34,6 +34,54 @@ class VirtualTests: self.check_loops(new=0, new_with_vtable=0, getfield_gc=0, setfield_gc=0) + def test_virtualized2(self): + myjitdriver = JitDriver(greens = [], reds = ['n', 'node1', 'node2']) + def f(n): + node1 = self._new() + node1.value = 0 + node2 = self._new() + node2.value = 0 + while n > 0: + myjitdriver.can_enter_jit(n=n, node1=node1, node2=node2) + myjitdriver.jit_merge_point(n=n, node1=node1, node2=node2) + next1 = self._new() + next1.value = node1.value + n + node2.value + next2 = self._new() + next2.value = next1.value + node1 = next1 + node2 = next2 + n -= 1 + return node1.value * node2.value + assert f(10) == self.meta_interp(f, [10]) + self.check_loops(new=0, new_with_vtable=0, + getfield_gc=0, setfield_gc=0) + + def test_virtualized_circular1(self): + class MyNode(): + pass + myjitdriver = JitDriver(greens = [], reds = ['n', 'node']) + def f(n): + node = MyNode() + node.value = 0 + node.extra = 0 + node.ref = node + while n > 0: + myjitdriver.can_enter_jit(n=n, node=node) + myjitdriver.jit_merge_point(n=n, node=node) + next = MyNode() + next.value = node.ref.value + n + next.extra = node.ref.extra + 1 + next.ref = next + node = next + n -= 1 + return node.value * node.extra + assert f(10) == 55 * 10 + res = self.meta_interp(f, [10]) + assert res == 55 * 10 + self.check_loop_count(1) + self.check_loops(new=0, new_with_vtable=0, + getfield_gc=0, setfield_gc=0) + def test_virtualized_float(self): myjitdriver = JitDriver(greens = [], reds = ['n', 'node']) def f(n): @@ -49,6 +97,24 @@ class VirtualTests: res = self.meta_interp(f, [10]) assert res == f(10) self.check_loop_count(1) + self.check_loops(new=0, float_add=0) + + def test_virtualized_float2(self): + myjitdriver = JitDriver(greens = [], reds = ['n', 'node']) + def f(n): + node = self._new() + node.floatval = 0.0 + while n > 0: + myjitdriver.can_enter_jit(n=n, node=node) + myjitdriver.jit_merge_point(n=n, node=node) + next = self._new() + next.floatval = node.floatval + .5 + node = next + n -= 1 + return node.floatval + res = self.meta_interp(f, [10]) + assert res == f(10) + self.check_loop_count(1) self.check_loops(new=0, float_add=1) def test_virtualized_2(self): @@ -109,7 +175,7 @@ class VirtualTests: next = self._new() next.value = node.value + n next.extra = node.extra + 1 - if next.extra == 4: + if next.extra == 5: next.value += 100 next.extra = 0 node = next @@ -137,16 +203,16 @@ class VirtualTests: next = self._new() next.value = node.value + n next.extra = node.extra + 1 - if next.extra == 4: + if next.extra == 5: next.value = externfn(next) next.extra = 0 node = next n -= 1 return node.value - res = self.meta_interp(f, [11], policy=StopAtXPolicy(externfn)) - assert res == f(11) + res = self.meta_interp(f, [20], policy=StopAtXPolicy(externfn)) + assert res == f(20) self.check_loop_count(2) - self.check_loops(**{self._new_op: 2}) # XXX was 1 + self.check_loops(**{self._new_op: 1}) self.check_loops(int_mul=0, call=1) def test_two_virtuals(self): @@ -172,6 +238,32 @@ class VirtualTests: assert res == 78 self.check_loops(new_with_vtable=0, new=0) + def test_specialied_bridge(self): + myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'res']) + class A: + def __init__(self, val): + self.val = val + def binop(self, other): + return A(self.val + other.val) + def f(x, y): + res = A(0) + while y > 0: + myjitdriver.can_enter_jit(y=y, x=x, res=res) + myjitdriver.jit_merge_point(y=y, x=x, res=res) + res = res.binop(A(y)) + if y<7: + res = x + x = A(1) + y -= 1 + return res + def g(x, y): + a1 = f(A(x), y) + a2 = f(A(x), y) + assert a1.val == a2.val + return a1.val + res = self.meta_interp(g, [6, 14]) + assert res == g(6, 14) + def test_both_virtual_and_field_variable(self): myjitdriver = JitDriver(greens = [], reds = ['n']) class Foo(object): @@ -298,10 +390,9 @@ class VirtualTests: self.check_tree_loop_count(2) # the loop and the entry path # we get: - # ENTER - compile the new loop - # ENTER - compile the entry bridge + # ENTER - compile the new loop and entry bridge # ENTER - compile the leaving path - self.check_enter_count(3) + self.check_enter_count(2) class VirtualMiscTests: diff --git a/pypy/jit/metainterp/test/test_virtualizable.py b/pypy/jit/metainterp/test/test_virtualizable.py index 44edefef59..2bfa14273c 100644 --- a/pypy/jit/metainterp/test/test_virtualizable.py +++ b/pypy/jit/metainterp/test/test_virtualizable.py @@ -11,7 +11,7 @@ from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin from pypy.rpython.rclass import FieldListAccessor from pypy.jit.metainterp.warmspot import get_stats, get_translator from pypy.jit.metainterp import history -from pypy.jit.metainterp.test.test_optimizefindnode import LLtypeMixin +from pypy.jit.metainterp.test.test_optimizeutil import LLtypeMixin def promote_virtualizable(*args): pass @@ -197,7 +197,8 @@ class ExplicitVirtualizableTests: return xy.inst_x res = self.meta_interp(f, [20]) assert res == 134 - self.check_loops(getfield_gc=1, setfield_gc=1) + self.check_loops(getfield_gc=0, setfield_gc=1) + self.check_loops(getfield_gc=1, setfield_gc=2, everywhere=True) # ------------------------------ @@ -1130,7 +1131,7 @@ class ImplicitVirtualizableTests: res = self.meta_interp(f, [10]) assert res == 55 - self.check_loops(new_with_vtable=0, ptr_eq=1) + self.check_loops(new_with_vtable=0, ptr_eq=1, everywhere=True) def test_virtual_child_frame_with_arrays(self): myjitdriver = JitDriver(greens = [], reds = ['frame'], diff --git a/pypy/jit/metainterp/viewnode.py b/pypy/jit/metainterp/viewnode.py deleted file mode 100644 index db5ae8ad86..0000000000 --- a/pypy/jit/metainterp/viewnode.py +++ /dev/null @@ -1,124 +0,0 @@ -import py -from pypy.jit.metainterp import specnode, optimizefindnode -from pypy.tool.pairtype import extendabletype - -class __extend__(specnode.NotSpecNode): - def _dot(self, seen): - if self in seen: - return - seen.add(self) - yield '%s [label="<Not>"]' % (id(self), ) - -class __extend__(specnode.ConstantSpecNode): - def _dot(self, seen): - if self in seen: - return - seen.add(self) - yield '%s [label="<Const: %s>"]' % (id(self), self.constbox) - -class __extend__(specnode.AbstractVirtualStructSpecNode): - def _dot(self, seen): - if self in seen: - return - seen.add(self) - yield '%s [label="<%s>"]' % ( - id(self), - self.__class__.__name__[:-len("SpecNode")]) - for label, node in self.fields: - yield '%s -> %s [label="%s"]' % (id(self), id(node), label.name) - for line in node._dot(seen): - yield line - -class __extend__(specnode.VirtualArraySpecNode): - def _dot(self, seen): - if self in seen: - return - seen.add(self) - yield '%s [label="<Array: %s>"]' % ( - id(self), - len(self.items)) - for i, node in enumerate(self.items): - yield '%s -> %s [label="%s"]' % (id(self), id(node), i) - for line in node._dot(seen): - yield line - - -class __extend__(optimizefindnode.InstanceNode): - __metaclass__ = extendabletype # evil - - def _dot(self, seen): - if self in seen: - return - seen.add(self) - if self.knownclsbox: - name = "Virtual " - if isinstance(self.knownclsbox.value, int): - name += str(self.knownclsbox.value) - else: - name += str(self.knownclsbox.value.adr.ptr).rpartition("_vtable")[0].rpartition('.')[2] - elif self.structdescr: - name = "Struct " + str(self.structdescr) - elif self.arraydescr: - name = "Array" - else: - name = "Not" - if self.escaped: - name = "ESC " + name - if self.fromstart: - name = "START " + name - if self.unique == optimizefindnode.UNIQUE_NO: - color = "blue" - else: - color = "black" - - yield 'orig%s [label="in: [%s]", shape=box, color=%s]' % ( - id(self), name, color) - yield '%s [label="out: [%s]", shape=box, color=%s]' % ( - id(self), name, color) - yield 'orig%s -> %s [color=red]' % (id(self), id(self)) - if self.origfields: - for descr, node in self.origfields.iteritems(): - yield 'orig%s -> orig%s [label="%s"]' % (id(self), id(node), descr.name) - for line in node._dot(seen): - yield line - if self.curfields: - for descr, node in self.curfields.iteritems(): - yield '%s -> %s [label="%s"]' % (id(self), id(node), descr.name) - for line in node._dot(seen): - yield line - if self.origitems: - for i, node in sorted(self.origitems.iteritems()): - yield 'orig%s -> orig%s [label="%s"]' % (id(self), id(node), i) - for line in node._dot(seen): - yield line - if self.curitems: - for i, node in sorted(self.curitems.iteritems()): - yield '%s -> %s [label="%s"]' % (id(self), id(node), i) - for line in node._dot(seen): - yield line - - -def view(*objects): - from dotviewer import graphclient - content = ["digraph G{"] - seen = set() - for obj in objects: - content.extend(obj._dot(seen)) - content.append("}") - p = py.test.ensuretemp("specnodes").join("temp.dot") - p.write("\n".join(content)) - graphclient.display_dot_file(str(p)) - -def viewnodes(l1, l2): - from dotviewer import graphclient - content = ["digraph G{"] - seen = set() - for obj in l1 + l2: - content.extend(obj._dot(seen)) - for i, (o1, o2) in enumerate(zip(l1, l2)): - content.append("%s -> %s [color=green]" % (id(o1), i)) - content.append("%s -> orig%s [color=green]" % (i, id(o2))) - content.append("}") - p = py.test.ensuretemp("specnodes").join("temp.dot") - p.write("\n".join(content)) - graphclient.display_dot_file(str(p)) diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py index c3d28e8984..91a84ab790 100644 --- a/pypy/jit/metainterp/warmspot.py +++ b/pypy/jit/metainterp/warmspot.py @@ -328,7 +328,7 @@ class WarmRunnerDesc(object): return 'DoneWithThisFrameVoid()' class DoneWithThisFrameInt(JitException): - def __init__(self, result): + def __init__(self, result): assert lltype.typeOf(result) is lltype.Signed self.result = result def __str__(self): diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py index 37a6633b81..fda0790e26 100644 --- a/pypy/jit/metainterp/warmstate.py +++ b/pypy/jit/metainterp/warmstate.py @@ -8,7 +8,7 @@ from pypy.rlib.rarithmetic import intmask from pypy.rlib.nonconst import NonConstant from pypy.rlib.unroll import unrolling_iterable from pypy.rlib.jit import (PARAMETERS, OPTIMIZER_SIMPLE, OPTIMIZER_FULL, - OPTIMIZER_NO_PERFECTSPEC) + OPTIMIZER_NO_UNROLL) from pypy.rlib.jit import BaseJitCell from pypy.rlib.debug import debug_start, debug_stop, debug_print from pypy.jit.metainterp import history @@ -224,10 +224,10 @@ class WarmEnterState(object): from pypy.jit.metainterp import simple_optimize self.optimize_loop = simple_optimize.optimize_loop self.optimize_bridge = simple_optimize.optimize_bridge - elif optimizer == OPTIMIZER_NO_PERFECTSPEC: - from pypy.jit.metainterp import optimize_nopspec - self.optimize_loop = optimize_nopspec.optimize_loop - self.optimize_bridge = optimize_nopspec.optimize_bridge + elif optimizer == OPTIMIZER_NO_UNROLL: + from pypy.jit.metainterp import nounroll_optimize + self.optimize_loop = nounroll_optimize.optimize_loop + self.optimize_bridge = nounroll_optimize.optimize_bridge elif optimizer == OPTIMIZER_FULL: from pypy.jit.metainterp import optimize self.optimize_loop = optimize.optimize_loop diff --git a/pypy/jit/tl/pypyjit_child.py b/pypy/jit/tl/pypyjit_child.py index 6f06d48870..ac0d55f392 100644 --- a/pypy/jit/tl/pypyjit_child.py +++ b/pypy/jit/tl/pypyjit_child.py @@ -2,7 +2,7 @@ from pypy.conftest import option from pypy.rpython.lltypesystem import lltype from pypy.jit.metainterp import warmspot from pypy.module.pypyjit.policy import PyPyJitPolicy -from pypy.rlib.jit import OPTIMIZER_FULL +from pypy.rlib.jit import OPTIMIZER_FULL, OPTIMIZER_NO_UNROLL def run_child(glob, loc): @@ -34,6 +34,6 @@ def apply_jit(interp, graph, CPUClass): option.view = True warmspot.jittify_and_run(interp, graph, [], policy=policy, listops=True, CPUClass=CPUClass, - backendopt=True, inline=True, + backendopt=True, inline=False, optimizer=OPTIMIZER_FULL) diff --git a/pypy/jit/tl/pypyjit_demo.py b/pypy/jit/tl/pypyjit_demo.py index 689754cce0..cd65b83d80 100644 --- a/pypy/jit/tl/pypyjit_demo.py +++ b/pypy/jit/tl/pypyjit_demo.py @@ -1,72 +1,18 @@ -## base = object - -## class Number(base): -## __slots__ = ('val', ) -## def __init__(self, val=0): -## self.val = val - -## def __add__(self, other): -## if not isinstance(other, int): -## other = other.val -## return Number(val=self.val + other) - -## def __cmp__(self, other): -## val = self.val -## if not isinstance(other, int): -## other = other.val -## return cmp(val, other) - -## def __nonzero__(self): -## return bool(self.val) - -## def g(x, inc=2): -## return x + inc - -## def f(n, x, inc): -## while x < n: -## x = g(x, inc=1) -## return x - -## import time -## #t1 = time.time() -## #f(10000000, Number(), 1) -## #t2 = time.time() -## #print t2 - t1 -## t1 = time.time() -## f(10000000, 0, 1) -## t2 = time.time() -## print t2 - t1 try: - from array import array - - def coords(w,h): - y = 0 - while y < h: - x = 0 - while x < w: - yield x,y - x += 1 - y += 1 + import pypyjit + pypyjit.set_param(threshold=3, inlining=True) - def f(img): - sa=0 - for x, y in coords(4,4): - sa += x * y - return sa + def main(): + i=a=0 + while i<10: + i+=1 + a+=1 + return a - #img=array('h',(1,2,3,4)) - print f(3) + print main() + except Exception, e: print "Exception: ", type(e) print e -## def f(): -## a=7 -## i=0 -## while i<4: -## if i<0: break -## if i<0: break -## i+=1 - -## f() diff --git a/pypy/jit/tl/spli/test/test_jit.py b/pypy/jit/tl/spli/test/test_jit.py index 27288cb6bc..616fc62fa8 100644 --- a/pypy/jit/tl/spli/test/test_jit.py +++ b/pypy/jit/tl/spli/test/test_jit.py @@ -39,6 +39,7 @@ class TestSPLIJit(JitMixin): self.check_loops(new_with_vtable=0) def test_bridge(self): + py.test.skip('We currently cant virtualize across bridges') def f(a, b): total = 0 i = 0 @@ -54,6 +55,7 @@ class TestSPLIJit(JitMixin): self.check_loops(new_with_vtable=0) def test_bridge_bad_case(self): + py.test.skip('We currently cant virtualize across bridges') def f(a, b): i = 0 while i < 100: diff --git a/pypy/jit/tl/tla/test_tla.py b/pypy/jit/tl/tla/test_tla.py index 642c747d93..1d807331a0 100644 --- a/pypy/jit/tl/tla/test_tla.py +++ b/pypy/jit/tl/tla/test_tla.py @@ -156,7 +156,7 @@ def test_div_float(): # ____________________________________________________________ from pypy.jit.metainterp.test.test_basic import LLJitMixin -from pypy.rlib.jit import OPTIMIZER_FULL +from pypy.rlib.jit import OPTIMIZER_FULL, OPTIMIZER_NO_UNROLL class TestLLtype(LLJitMixin): def test_loop(self): @@ -178,5 +178,5 @@ class TestLLtype(LLJitMixin): assert isinstance(w_result, tla.W_IntObject) return w_result.intvalue res = self.meta_interp(interp_w, [42], listops=True, - optimizer=OPTIMIZER_FULL) + optimizer=OPTIMIZER_NO_UNROLL) assert res == 0 diff --git a/pypy/jit/tool/jitoutput.py b/pypy/jit/tool/jitoutput.py index afe2da613f..43b6ce91e3 100644 --- a/pypy/jit/tool/jitoutput.py +++ b/pypy/jit/tool/jitoutput.py @@ -24,6 +24,7 @@ REGEXES = [ (('abort.trace_too_long',), '^abort: trace too long:\s+(\d+)$'), (('abort.compiling',), '^abort: compiling:\s+(\d+)$'), (('abort.vable_escape',), '^abort: vable escape:\s+(\d+)$'), + (('abort.bad_loop',), '^abort: bad loop:\s+(\d+)$'), (('nvirtuals',), '^nvirtuals:\s+(\d+)$'), (('nvholes',), '^nvholes:\s+(\d+)$'), (('nvreused',), '^nvreused:\s+(\d+)$'), diff --git a/pypy/jit/tool/oparser.py b/pypy/jit/tool/oparser.py index b51fce02fb..9ac74a1617 100644 --- a/pypy/jit/tool/oparser.py +++ b/pypy/jit/tool/oparser.py @@ -31,6 +31,9 @@ class ESCAPE_OP(N_aryOp, ResOpWithDescr): def getopnum(self): return self.OPNUM + def clone(self): + return ESCAPE_OP(self.OPNUM, self.getarglist()[:], self.result, self.getdescr()) + class ExtendedTreeLoop(TreeLoop): def getboxes(self): diff --git a/pypy/jit/tool/test/test_jitoutput.py b/pypy/jit/tool/test/test_jitoutput.py index a9eac1ecf6..351f890580 100644 --- a/pypy/jit/tool/test/test_jitoutput.py +++ b/pypy/jit/tool/test/test_jitoutput.py @@ -36,13 +36,13 @@ def test_really_run(): assert info.tracing_no == 1 assert info.asm_no == 1 assert info.blackhole_no == 1 - assert info.backend_no == 1 + assert info.backend_no == 2 assert info.ops.total == 2 assert info.recorded_ops.total == 2 assert info.recorded_ops.calls == 0 assert info.guards == 1 - assert info.opt_ops == 6 - assert info.opt_guards == 1 + assert info.opt_ops == 11 + assert info.opt_guards == 2 assert info.forcings == 0 DATA = '''Tracing: 1 0.006992 @@ -60,6 +60,7 @@ forcings: 1 abort: trace too long: 10 abort: compiling: 11 abort: vable escape: 12 +abort: bad loop: 135 nvirtuals: 13 nvholes: 14 nvreused: 15 @@ -87,6 +88,7 @@ def test_parse(): assert info.abort.trace_too_long == 10 assert info.abort.compiling == 11 assert info.abort.vable_escape == 12 + assert info.abort.bad_loop == 135 assert info.nvirtuals == 13 assert info.nvholes == 14 assert info.nvreused == 15 diff --git a/pypy/jit/tool/traceviewer.py b/pypy/jit/tool/traceviewer.py index a6386e4fcc..366a799ddf 100755 --- a/pypy/jit/tool/traceviewer.py +++ b/pypy/jit/tool/traceviewer.py @@ -74,7 +74,7 @@ class BasicBlock(object): def generate(self, dotgen, counts): val = counts.get(self.key, 0) - if val > counts.threshold: + if False: #val > counts.threshold: fillcolor = get_gradient_color(self.ratio) else: fillcolor = "white" diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py index 457973e4d4..610c4dc42e 100644 --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -292,7 +292,7 @@ encode.unwrap_spec = [ObjSpace, W_Root, W_Root, str] def buffer_encode(space, s, errors='strict'): return space.newtuple([space.wrap(s), space.wrap(len(s))]) -buffer_encode.unwrap_spec = [ObjSpace, 'bufferstr', str] +buffer_encode.unwrap_spec = [ObjSpace, 'bufferstr', 'str_or_None'] def decode(space, w_obj, w_encoding=NoneNotWrapped, errors='strict'): """decode(obj, [encoding[,errors]]) -> object @@ -380,18 +380,22 @@ def make_encoder_wrapper(name): rname = "unicode_encode_%s" % (name.replace("_encode", ""), ) assert hasattr(runicode, rname) def wrap_encoder(space, uni, errors="strict"): + if errors is None: + errors = 'strict' state = space.fromcache(CodecState) func = getattr(runicode, rname) result = func(uni, len(uni), errors, state.encode_error_handler) return space.newtuple([space.wrap(result), space.wrap(len(uni))]) wrap_encoder.func_name = rname - wrap_encoder.unwrap_spec = [ObjSpace, unicode, str] + wrap_encoder.unwrap_spec = [ObjSpace, unicode, 'str_or_None'] globals()[name] = wrap_encoder def make_decoder_wrapper(name): rname = "str_decode_%s" % (name.replace("_decode", ""), ) assert hasattr(runicode, rname) def wrap_decoder(space, string, errors="strict", w_final=False): + if errors is None: + errors = 'strict' final = space.is_true(w_final) state = space.fromcache(CodecState) func = getattr(runicode, rname) @@ -399,7 +403,7 @@ def make_decoder_wrapper(name): final, state.decode_error_handler) return space.newtuple([space.wrap(result), space.wrap(consumed)]) wrap_decoder.func_name = rname - wrap_decoder.unwrap_spec = [ObjSpace, 'bufferstr', str, W_Root] + wrap_decoder.unwrap_spec = [ObjSpace, 'bufferstr', 'str_or_None', W_Root] globals()[name] = wrap_decoder for encoders in [ @@ -433,6 +437,8 @@ if hasattr(runicode, 'str_decode_mbcs'): make_decoder_wrapper('mbcs_decode') def utf_16_ex_decode(space, data, errors='strict', byteorder=0, w_final=False): + if errors is None: + errors = 'strict' final = space.is_true(w_final) state = space.fromcache(CodecState) if byteorder == 0: @@ -448,7 +454,7 @@ def utf_16_ex_decode(space, data, errors='strict', byteorder=0, w_final=False): data, len(data), errors, final, state.decode_error_handler, byteorder) return space.newtuple([space.wrap(res), space.wrap(consumed), space.wrap(byteorder)]) -utf_16_ex_decode.unwrap_spec = [ObjSpace, str, str, int, W_Root] +utf_16_ex_decode.unwrap_spec = [ObjSpace, str, 'str_or_None', int, W_Root] # ____________________________________________________________ # Charmap @@ -551,8 +557,10 @@ class Charmap_Encode: raise OperationError(space.w_TypeError, space.wrap("invalid mapping")) -@unwrap_spec(ObjSpace, str, str, W_Root) +@unwrap_spec(ObjSpace, str, 'str_or_None', W_Root) def charmap_decode(space, string, errors="strict", w_mapping=None): + if errors is None: + errors = 'strict' if len(string) == 0: return space.newtuple([space.wrap(u''), space.wrap(0)]) @@ -568,8 +576,10 @@ def charmap_decode(space, string, errors="strict", w_mapping=None): final, state.decode_error_handler, mapping) return space.newtuple([space.wrap(result), space.wrap(consumed)]) -@unwrap_spec(ObjSpace, unicode, str, W_Root) +@unwrap_spec(ObjSpace, unicode, 'str_or_None', W_Root) def charmap_encode(space, uni, errors="strict", w_mapping=None): + if errors is None: + errors = 'strict' if space.is_w(w_mapping, space.w_None): mapping = None else: @@ -609,8 +619,10 @@ class UnicodeData_Handler: return -1 return space.int_w(w_code) -@unwrap_spec(ObjSpace, 'bufferstr', str, W_Root) +@unwrap_spec(ObjSpace, 'bufferstr', 'str_or_None', W_Root) def unicode_escape_decode(space, string, errors="strict", w_final=False): + if errors is None: + errors = 'strict' final = space.is_true(w_final) state = space.fromcache(CodecState) errorhandler=state.decode_error_handler @@ -627,8 +639,10 @@ def unicode_escape_decode(space, string, errors="strict", w_final=False): # ____________________________________________________________ # Unicode-internal -@unwrap_spec(ObjSpace, W_Root, str) +@unwrap_spec(ObjSpace, W_Root, 'str_or_None') def unicode_internal_decode(space, w_string, errors="strict"): + if errors is None: + errors = 'strict' # special case for this codec: unicodes are returned as is if space.isinstance_w(w_string, space.w_unicode): return space.newtuple([w_string, space.len(w_string)]) @@ -649,13 +663,13 @@ def unicode_internal_decode(space, w_string, errors="strict"): # support for the "string escape" codec # This is a bytes-to bytes transformation -@unwrap_spec(ObjSpace, W_Root, str) +@unwrap_spec(ObjSpace, W_Root, 'str_or_None') def escape_encode(space, w_string, errors='strict'): w_repr = space.repr(w_string) w_result = space.getslice(w_repr, space.wrap(1), space.wrap(-1)) return space.newtuple([w_result, space.len(w_string)]) -@unwrap_spec(ObjSpace, str, str) +@unwrap_spec(ObjSpace, str, 'str_or_None') def escape_decode(space, data, errors='strict'): from pypy.interpreter.pyparser.parsestring import PyString_DecodeEscape result = PyString_DecodeEscape(space, data, None) diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py index df6f0df3b2..4faac4f5c7 100644 --- a/pypy/module/_file/interp_file.py +++ b/pypy/module/_file/interp_file.py @@ -210,12 +210,14 @@ class W_File(W_AbstractStream): stream.truncate(size) def direct_write(self, data): + self.softspace = 0 self.getstream().write(data) def direct_writelines(self, w_lines): # note: a wrapped list! stream = self.getstream() space = self.space w_iterator = space.iter(w_lines) + self.softspace = 0 while True: try: w_line = space.next(w_iterator) diff --git a/pypy/module/_file/test/test_file.py b/pypy/module/_file/test/test_file.py index a95baf5fc5..80a544745a 100644 --- a/pypy/module/_file/test/test_file.py +++ b/pypy/module/_file/test/test_file.py @@ -171,7 +171,18 @@ Delivered-To: gkj@sundance.gregorykjohnson.com''' raises(ValueError, self.file, self.temppath, "aU") raises(ValueError, self.file, self.temppath, "wU+") raises(ValueError, self.file, self.temppath, "") - + + def test_write_resets_softspace(self): + f = self.file(self.temppath, "w") + print >> f, '.', + f.write(',') + print >> f, '.', + f.close() + f = self.file(self.temppath, "r") + res = f.read() + assert res == ".,." + f.close() + class AppTestConcurrency(object): # these tests only really make sense on top of a translated pypy-c, # because on top of py.py the inner calls to os.write() don't diff --git a/pypy/module/_rawffi/test/test__rawffi.py b/pypy/module/_rawffi/test/test__rawffi.py index 81d60ec085..4670aeea62 100644 --- a/pypy/module/_rawffi/test/test__rawffi.py +++ b/pypy/module/_rawffi/test/test__rawffi.py @@ -863,6 +863,7 @@ class AppTestFfi: assert "Procedure called with too many arguments" in e.message else: assert 0, "Did not raise" + arg.free() def test_struct_byvalue(self): import _rawffi, sys diff --git a/pypy/module/_weakref/interp__weakref.py b/pypy/module/_weakref/interp__weakref.py index 6b09691fcb..b93d82254b 100644 --- a/pypy/module/_weakref/interp__weakref.py +++ b/pypy/module/_weakref/interp__weakref.py @@ -180,9 +180,8 @@ def descr__repr__(space, ref): reprdescr = interp2app(descr__repr__, unwrap_spec=[ObjSpace, W_WeakrefBase]) W_Weakref.typedef = TypeDef("weakref", - __doc__ = """A weak reference to an object 'obj'. A 'callback' can given, -which is called with the weak reference as an argument when 'obj' -is about to be finalized.""", + __doc__ = """A weak reference to an object 'obj'. A 'callback' can be given, +which is called with 'obj' as an argument when it is about to be finalized.""", __new__ = interp2app(descr__new__weakref, unwrap_spec=[ObjSpace, W_Root, W_Root, W_Root, Arguments]), diff --git a/pypy/module/array/benchmark/Makefile b/pypy/module/array/benchmark/Makefile index ac5e2c7ec7..219e5e76f5 100644 --- a/pypy/module/array/benchmark/Makefile +++ b/pypy/module/array/benchmark/Makefile @@ -5,4 +5,20 @@ intimg: intimgtst.o intimg.o loop: loop.o gcc -o $@ $^ sum: sumtst.o sum.o - gcc -o $@ $^
\ No newline at end of file + gcc -o $@ $^ +circular: circulartst.o circular.o + gcc -o $@ $^ + +run: run-intimg run-sum run-circular + +run-%: % + @echo $^ + @echo -n ' C: ' + @/usr/bin/time -f '%e s' ./$^ 2>&1 | tail -1 + @echo -n ' pypy: ' + @/usr/bin/time -f '%e s' ../../../translator/goal/pypy-c $^tst.py 2>&1 | tail -1 + @echo -n 'pypy-trunk: ' + @/usr/bin/time -f '%e s' ../../../../../trunk/pypy/translator/goal/pypy-c $^tst.py 2>&1 | tail -1 + @echo -n ' cpython: ' + @/usr/bin/time -f '%e s' python $^tst.py 2>&1 | tail -1 + @echo diff --git a/pypy/module/array/benchmark/circular.c b/pypy/module/array/benchmark/circular.c new file mode 100644 index 0000000000..e9e263e86c --- /dev/null +++ b/pypy/module/array/benchmark/circular.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +double buf[65536]; + +double circular(double *buf); + +int main() { + double sa = circular(buf); + //printf("%f\n", sa); +} diff --git a/pypy/module/array/benchmark/circulartst.c b/pypy/module/array/benchmark/circulartst.c new file mode 100644 index 0000000000..cdbcdc24b6 --- /dev/null +++ b/pypy/module/array/benchmark/circulartst.c @@ -0,0 +1,14 @@ + +double circular(double *buf) { + int i; + double sa; + for (i=0; i<65536; i++) buf[i] = i; + + i = 10; + sa = 0; + while(i<200000000) { + sa += buf[(i-2)&65535] + buf[(i-1)&65535] + buf[i&65535] + buf[(i+1)&65535] + buf[(i+2)&65535]; + i += 1; + } + return sa; +} diff --git a/pypy/module/array/benchmark/circulartst.py b/pypy/module/array/benchmark/circulartst.py new file mode 100644 index 0000000000..3c0700f286 --- /dev/null +++ b/pypy/module/array/benchmark/circulartst.py @@ -0,0 +1,23 @@ +import sys +sys.setcheckinterval(1<<20) + +from array import array +class Circular(array): + def __new__(cls): + self = array.__new__(cls, 'd', range(65536)) + return self + def __getitem__(self, i): + assert len(self) == 65536 + return array.__getitem__(self, i & 65535) + +import sys +def main(): + buf = Circular() + i = 10 + sa = 0 + while i < 200000000: + sa += buf[i-2] + buf[i-1] + buf[i] + buf[i+1] + buf[i+2] + i += 1 + return sa + +print main() diff --git a/pypy/module/array/benchmark/loop.py b/pypy/module/array/benchmark/looptst.py index 20ca197988..553bc9e789 100644 --- a/pypy/module/array/benchmark/loop.py +++ b/pypy/module/array/benchmark/looptst.py @@ -5,3 +5,5 @@ def h(): s+=i i+=1 return s + +print h() diff --git a/pypy/module/array/benchmark/result.txt b/pypy/module/array/benchmark/result.txt new file mode 100644 index 0000000000..7dfe909818 --- /dev/null +++ b/pypy/module/array/benchmark/result.txt @@ -0,0 +1,18 @@ +intimg + C: 0.29 s + pypy: 0.99 s +pypy-trunk: 1.33 s + cpython: 68.20 s + +sum + C: 0.61 s + pypy: 0.59 s +pypy-trunk: 0.74 s + cpython: 24.77 s + +circular + C: 0.47 s + pypy: 1.48 s +pypy-trunk: 21.13 s + cpython: 869.56 s + diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py index 87d8797a4a..404f087313 100644 --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -939,7 +939,9 @@ def load_extension_module(space, path, name): if os.sep not in path: path = os.curdir + os.sep + path # force a '/' in the path state = space.fromcache(State) - state.package_context = name + if state.find_extension(name, path) is not None: + return + state.package_context = name, path try: from pypy.rlib import rdynload try: @@ -964,7 +966,8 @@ def load_extension_module(space, path, name): generic_cpy_call(space, initfunc) state.check_and_raise_exception() finally: - state.package_context = None + state.package_context = None, None + state.fixup_extension(name, path) @specialize.ll() def generic_cpy_call(space, func, *args): diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h index 0b0c4930dd..4c7ed2483c 100644 --- a/pypy/module/cpyext/include/patchlevel.h +++ b/pypy/module/cpyext/include/patchlevel.h @@ -29,7 +29,7 @@ #define PY_VERSION "2.5.2" /* PyPy version as a string */ -#define PYPY_VERSION "1.4.0" +#define PYPY_VERSION "1.4.1" /* Subversion Revision number of this file (not of the repository) */ #define PY_PATCHLEVEL_REVISION "$Revision: 77872 $" diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py index 360caf9e02..f4bffa814e 100644 --- a/pypy/module/cpyext/modsupport.py +++ b/pypy/module/cpyext/modsupport.py @@ -54,9 +54,10 @@ def _Py_InitPyPyModule(space, name, methods, doc, w_self, apiver): from pypy.module.cpyext.typeobjectdefs import PyTypeObjectPtr modname = rffi.charp2str(name) state = space.fromcache(State) - w_mod = PyImport_AddModule(space, state.package_context) + f_name, f_path = state.package_context + w_mod = PyImport_AddModule(space, f_name) - dict_w = {} + dict_w = {'__file__': space.wrap(f_path)} convert_method_defs(space, dict_w, methods, None, w_self, modname) for key, w_value in dict_w.items(): space.setattr(w_mod, space.wrap(key), w_value) diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py index 7efcb43ad9..bf2fd6d7e8 100644 --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -16,14 +16,19 @@ class State: self.operror = None self.new_method_def = lltype.nullptr(PyMethodDef) - # When importing a package, use this to keep track of its name. This is + # When importing a package, use this to keep track + # of its name and path (as a 2-tuple). This is # necessary because an extension module in a package might not supply # its own fully qualified name to Py_InitModule. If it doesn't, we need # to be able to figure out what module is being initialized. Recursive # imports will clobber this value, which might be confusing, but it # doesn't hurt anything because the code that cares about it will have # already read it by that time. - self.package_context = None + self.package_context = None, None + + # A mapping {filename: copy-of-the-w_dict}, similar to CPython's + # variable 'extensions' in Python/import.c. + self.extensions = {} def set_exception(self, operror): self.clear_exception() @@ -96,3 +101,29 @@ class State: self.programname = rffi.str2charp(progname) lltype.render_immortal(self.programname) return self.programname + + def find_extension(self, name, path): + from pypy.module.cpyext.modsupport import PyImport_AddModule + from pypy.interpreter.module import Module + try: + w_dict = self.extensions[path] + except KeyError: + return None + w_mod = PyImport_AddModule(self.space, name) + assert isinstance(w_mod, Module) + w_mdict = w_mod.getdict() + self.space.call_method(w_mdict, 'update', w_dict) + return w_mod + + def fixup_extension(self, name, path): + from pypy.interpreter.module import Module + space = self.space + w_modules = space.sys.get('modules') + w_mod = space.finditem_str(w_modules, name) + if not isinstance(w_mod, Module): + msg = "fixup_extension: module '%s' not loaded" % name + raise OperationError(space.w_SystemError, + space.wrap(msg)) + w_dict = w_mod.getdict() + w_copy = space.call_method(w_dict, 'copy') + self.extensions[path] = w_copy diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py index d7255330cd..09b1061e58 100644 --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -222,6 +222,12 @@ class AppTestCpythonExtensionBase(LeakCheckingTest): else: return os.path.dirname(mod) + def reimport_module(self, mod, name): + api.load_extension_module(self.space, mod, name) + return self.space.getitem( + self.space.sys.get('modules'), + self.space.wrap(name)) + def import_extension(self, modname, functions, prologue=""): methods_table = [] codes = [] @@ -261,6 +267,7 @@ class AppTestCpythonExtensionBase(LeakCheckingTest): self.imported_module_names = [] self.w_import_module = self.space.wrap(self.import_module) + self.w_reimport_module = self.space.wrap(self.reimport_module) self.w_import_extension = self.space.wrap(self.import_extension) self.w_compile_module = self.space.wrap(self.compile_module) self.w_record_imported_module = self.space.wrap( @@ -709,3 +716,43 @@ class AppTestCpythonExtension(AppTestCpythonExtensionBase): p = mod.get_programname() print p assert 'py' in p + + def test_no_double_imports(self): + import sys, os + try: + init = """ + static int _imported_already = 0; + FILE *f = fopen("_imported_already", "w"); + fprintf(f, "imported_already: %d\\n", _imported_already); + fclose(f); + _imported_already = 1; + if (Py_IsInitialized()) { + Py_InitModule("foo", NULL); + } + """ + self.import_module(name='foo', init=init) + assert 'foo' in sys.modules + + f = open('_imported_already') + data = f.read() + f.close() + assert data == 'imported_already: 0\n' + + f = open('_imported_already', 'w') + f.write('not again!\n') + f.close() + m1 = sys.modules['foo'] + m2 = self.reimport_module(m1.__file__, name='foo') + assert m1 is m2 + assert m1 is sys.modules['foo'] + + f = open('_imported_already') + data = f.read() + f.close() + assert data == 'not again!\n' + + finally: + try: + os.unlink('_imported_already') + except OSError: + pass diff --git a/pypy/module/operator/interp_operator.py b/pypy/module/operator/interp_operator.py index 6beaa5ceab..a7c7bd66a4 100644 --- a/pypy/module/operator/interp_operator.py +++ b/pypy/module/operator/interp_operator.py @@ -17,8 +17,8 @@ def and_(space, w_obj1, w_obj2): def concat(space, w_obj1, w_obj2): 'concat(a, b) -- Same as a + b, for a and b sequences.' - if space.findattr(w_obj1, space.wrap('__getitem__')) is None or \ - space.findattr(w_obj2, space.wrap('__getitem__')) is None: + if (space.lookup(w_obj1, '__getitem__') is None or + space.lookup(w_obj2, '__getitem__') is None): raise OperationError(space.w_TypeError, space.w_None) return space.add(w_obj1, w_obj2) @@ -219,15 +219,15 @@ def ixor(space, w_a, w_b): def iconcat(space, w_obj1, w_obj2): 'iconcat(a, b) -- Same as a += b, for a and b sequences.' - if space.findattr(w_obj1, space.wrap('__getitem__')) is None or \ - space.findattr(w_obj2, space.wrap('__getitem__')) is None: + if (space.lookup(w_obj1, '__getitem__') is None or + space.lookup(w_obj2, '__getitem__') is None): raise OperationError(space.w_TypeError, space.w_None) return space.inplace_add(w_obj1, w_obj2) def irepeat(space, w_obj1, w_obj2): 'irepeat(a, b) -- Same as a *= b, for a and b sequences.' - if space.findattr(w_obj1, space.wrap('__getitem__')) is None: + if space.lookup(w_obj1, '__getitem__') is None: # first arg has to be a sequence raise OperationError(space.w_TypeError, space.wrap("non-sequence object can't be repeated")) diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py index 5a90419f3b..5735a42899 100644 --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -679,8 +679,12 @@ class AppTestPosix: def test_mknod(self): import stat os = self.posix - # not very useful: os.mknod() without specifying 'mode' - os.mknod(self.path2 + 'test_mknod-1') + # os.mknod() may require root priviledges to work at all + try: + # not very useful: os.mknod() without specifying 'mode' + os.mknod(self.path2 + 'test_mknod-1') + except OSError, e: + skip("os.mknod(): got %r" % (e,)) st = os.lstat(self.path2 + 'test_mknod-1') assert stat.S_ISREG(st.st_mode) # os.mknod() with S_IFIFO diff --git a/pypy/module/pypyjit/test/randomized.py b/pypy/module/pypyjit/test/randomized.py index 609c6c9a96..0a5632d29e 100644 --- a/pypy/module/pypyjit/test/randomized.py +++ b/pypy/module/pypyjit/test/randomized.py @@ -32,8 +32,12 @@ class RandomCode(object): ' ' + self.expression() def test(self): - return self.expression() + ' ' + self.sample(self.tests) + \ - ' ' + self.expression() + tst = self.sample(self.tests) + if tst: + return self.expression() + ' ' + tst + \ + ' ' + self.expression() + else: + return self.expression() def constant(self): return str(self.sample(self.constants)) @@ -81,9 +85,9 @@ class RandomCode(object): class IntBounds(RandomCode): - opperators = ('+', '-', '*') - tests = ('<', '>', '<=', '>=', '==', '!=') - constants = range(-3,4) + opperators = ('+', '-', '*', '/', '>>', '<<') + tests = ('<', '>', '<=', '>=', '==', '!=', None) + constants = range(-3,4) varnames = 'abcd' def function(self, name='f'): diff --git a/pypy/module/pypyjit/test/test_pypy_c.py b/pypy/module/pypyjit/test/test_pypy_c.py index c321ff22b7..e42ac5d6a3 100644 --- a/pypy/module/pypyjit/test/test_pypy_c.py +++ b/pypy/module/pypyjit/test/test_pypy_c.py @@ -82,6 +82,8 @@ class PyPyCJITTests(object): def run_source(self, source, expected_max_ops, *testcases, **kwds): assert isinstance(expected_max_ops, int) threshold = kwds.pop('threshold', 3) + self.count_debug_merge_point = \ + kwds.pop('count_debug_merge_point', True) filter_loops = kwds.pop('filter_loops', False) # keep only the loops beginning from case%d.py if kwds: raise TypeError, 'Unsupported keyword arguments: %s' % kwds.keys() @@ -145,10 +147,15 @@ class PyPyCJITTests(object): self.loops, self.all_bytecodes, self.bytecode_by_loop, self.total_ops = \ self.parse_rawloops(self.rawloops, filter_loops) self.check_0_op_bytecodes() + self.rawentrybridges = [part for part in parts + if from_entry_bridge(part, parts)] + _, self.all_bytecodes_entrybridges, _, _ = \ + self.parse_rawloops(self.rawentrybridges, filter_loops) + def parse_rawloops(self, rawloops, filter_loops): from pypy.jit.tool.oparser import parse - loops = [parse(part, no_namespace=True) for part in self.rawloops] + loops = [parse(part, no_namespace=True) for part in rawloops] if filter_loops: self.loops = self.filter_loops(filepath, self.loops) all_bytecodes = [] # contains all bytecodes of all loops @@ -157,7 +164,7 @@ class PyPyCJITTests(object): for loop in loops: loop_bytecodes = [] bytecode_by_loop[loop] = loop_bytecodes - total_ops += len(loop.operations) + total_ops = 0 for op in loop.operations: if op.getopname() == "debug_merge_point": bytecode = BytecodeTrace() @@ -165,8 +172,11 @@ class PyPyCJITTests(object): bytecode.debug_merge_point = op loop_bytecodes.append(bytecode) all_bytecodes.append(bytecode) + if self.count_debug_merge_point: + total_ops += 1 else: bytecode.append(op) + total_ops += 1 return loops, all_bytecodes, bytecode_by_loop, total_ops @@ -187,8 +197,11 @@ class PyPyCJITTests(object): continue assert not bytecodetrace - def get_by_bytecode(self, name, loop=None): - if loop: + def get_by_bytecode(self, name, from_entry_bridge=False, loop=None): + if from_entry_bridge: + assert loop is None + bytecodes = self.all_bytecodes_entrybridges + elif loop: bytecodes = self.bytecode_by_loop[loop] else: bytecodes = self.all_bytecodes @@ -265,9 +278,10 @@ class PyPyCJITTests(object): ''', 98, ([20], 20), ([31], 32)) - ops = self.get_by_bytecode("LOAD_GLOBAL") + ops = self.get_by_bytecode("LOAD_GLOBAL", True) assert len(ops) == 5 - assert ops[0].get_opnames() == ["getfield_gc", "guard_value", + assert ops[0].get_opnames() == ["guard_value", + "getfield_gc", "guard_value", "getfield_gc", "guard_isnull", "getfield_gc", "guard_nonnull_class"] # the second getfield on the same globals is quicker @@ -279,7 +293,7 @@ class PyPyCJITTests(object): assert ops[3].get_opnames() == ["guard_value", "getfield_gc", "guard_isnull"] assert not ops[4] - ops = self.get_by_bytecode("CALL_FUNCTION") + ops = self.get_by_bytecode("CALL_FUNCTION", True) assert len(ops) == 2 for i, bytecode in enumerate(ops): if i == 0: @@ -289,6 +303,17 @@ class PyPyCJITTests(object): assert not bytecode.get_opnames("new") assert len(bytecode.get_opnames("guard")) <= 10 + ops = self.get_by_bytecode("LOAD_GLOBAL") + assert len(ops) == 5 + for bytecode in ops: + assert not bytecode + + ops = self.get_by_bytecode("CALL_FUNCTION") + assert len(ops) == 2 + for bytecode in ops: + assert len(bytecode) <= 1 + + def test_method_call(self): self.run_source(''' class A(object): @@ -306,14 +331,18 @@ class PyPyCJITTests(object): ''', 93, ([20], 20), ([31], 32)) - ops = self.get_by_bytecode("LOOKUP_METHOD") + ops = self.get_by_bytecode("LOOKUP_METHOD", True) assert len(ops) == 2 assert not ops[0].get_opnames("call") assert not ops[0].get_opnames("new") - assert len(ops[0].get_opnames("guard")) <= 2 + assert len(ops[0].get_opnames("guard")) <= 3 assert not ops[1] # second LOOKUP_METHOD folded away - ops = self.get_by_bytecode("CALL_METHOD") + ops = self.get_by_bytecode("LOOKUP_METHOD") + assert not ops[0] # first LOOKUP_METHOD folded away + assert not ops[1] # second LOOKUP_METHOD folded away + + ops = self.get_by_bytecode("CALL_METHOD", True) assert len(ops) == 2 for i, bytecode in enumerate(ops): if i == 0: @@ -324,7 +353,12 @@ class PyPyCJITTests(object): assert len(bytecode.get_opnames("guard")) <= 6 assert len(ops[1]) < len(ops[0]) - ops = self.get_by_bytecode("LOAD_ATTR") + ops = self.get_by_bytecode("CALL_METHOD") + assert len(ops) == 2 + assert len(ops[0]) <= 1 + assert len(ops[1]) <= 1 + + ops = self.get_by_bytecode("LOAD_ATTR", True) assert len(ops) == 2 # With mapdict, we get fast access to (so far) the 5 first # attributes, which means it is done with only the following @@ -334,6 +368,10 @@ class PyPyCJITTests(object): "guard_nonnull_class"] assert not ops[1] # second LOAD_ATTR folded away + ops = self.get_by_bytecode("LOAD_ATTR") + assert not ops[0] # first LOAD_ATTR folded away + assert not ops[1] # second LOAD_ATTR folded away + def test_static_classmethod_call(self): self.run_source(''' class A(object): @@ -378,6 +416,14 @@ class PyPyCJITTests(object): ops = self.get_by_bytecode("CALL_FUNCTION") assert len(ops) == 2 for i, bytecode in enumerate(ops): + assert not bytecode.get_opnames("call") + assert not bytecode.get_opnames("new") + assert len(ops[0].get_opnames("guard")) <= 14 + assert len(ops[1].get_opnames("guard")) <= 3 + + ops = self.get_by_bytecode("CALL_FUNCTION", True) + assert len(ops) == 2 + for i, bytecode in enumerate(ops): if i == 0: assert "call(getexecutioncontext)" in str(bytecode) else: @@ -406,7 +452,7 @@ class PyPyCJITTests(object): ([1000], 49500), ([10000], 495000), ([100000], 4950000)) - assert len(self.loops) == 3 + assert len(self.rawloops) + len(self.rawentrybridges) == 4 op, = self.get_by_bytecode("CALL_FUNCTION_KW") # XXX a bit too many guards, but better than before assert len(op.get_opnames("guard")) <= 12 @@ -568,6 +614,8 @@ class PyPyCJITTests(object): bytecode, = self.get_by_bytecode("CALL_METHOD") assert len(bytecode.get_opnames("new_with_vtable")) == 1 # the forcing of the int assert len(bytecode.get_opnames("call")) == 1 # the call to append + assert len(bytecode.get_opnames("guard")) == 1 # guard for guard_no_exception after the call + bytecode, = self.get_by_bytecode("CALL_METHOD", True) assert len(bytecode.get_opnames("guard")) == 2 # guard for profiling disabledness + guard_no_exception after the call def test_range_iter(self): @@ -581,19 +629,30 @@ class PyPyCJITTests(object): s += g(n)[i] return s ''', 143, ([1000], 1000 * 999 / 2)) - bytecode, = self.get_by_bytecode("BINARY_SUBSCR") + bytecode, = self.get_by_bytecode("BINARY_SUBSCR", True) assert bytecode.get_opnames("guard") == [ "guard_false", # check that the index is >= 0 "guard_false", # check that the index is lower than the current length ] - bytecode, _ = self.get_by_bytecode("FOR_ITER") # second bytecode is the end of the loop + bytecode, _ = self.get_by_bytecode("FOR_ITER", True) # second bytecode is the end of the loop assert bytecode.get_opnames("guard") == [ + "guard_value", "guard_class", # check the class of the iterator "guard_nonnull", # check that the iterator is not finished "guard_isnull", # check that the range list is not forced "guard_false", # check that the index is lower than the current length ] - + + bytecode, = self.get_by_bytecode("BINARY_SUBSCR") + assert bytecode.get_opnames("guard") == [ + "guard_false", # check that the index is >= 0 + "guard_false", # check that the index is lower than the current length + ] + bytecode, _ = self.get_by_bytecode("FOR_ITER") # second bytecode is the end of the loop + assert bytecode.get_opnames("guard") == [ + "guard_false", # check that the index is lower than the current length + ] + def test_exception_inside_loop_1(self): py.test.skip("exceptions: in-progress") self.run_source(''' @@ -825,7 +884,7 @@ class PyPyCJITTests(object): else: sa += 20000 i += 0.25 return sa - '''%(op1, float(a)/4.0, op2, float(b)/4.0), 109, ([], res)) + '''%(op1, float(a)/4.0, op2, float(b)/4.0), 156, ([], res)) def test_boolrewrite_correct_reflex(self): @@ -867,7 +926,7 @@ class PyPyCJITTests(object): else: sa += 20000 i += 0.25 return sa - '''%(op1, float(a)/4.0, float(b)/4.0, op2), 109, ([], res)) + '''%(op1, float(a)/4.0, float(b)/4.0, op2), 156, ([], res)) def test_boolrewrite_ptr(self): # XXX this test is way too imprecise in what it is actually testing @@ -1291,6 +1350,61 @@ class PyPyCJITTests(object): assert call.getarg(1).value == 2.0 assert call.getarg(2).value == 3.0 + def test_xor(self): + values = (-4, -3, -2, -1, 0, 1, 2, 3, 4) + for a in values: + for b in values: + if a^b >= 0: + r = 2000 + else: + r = 0 + ops = 46 + + self.run_source(''' + def main(a, b): + i = sa = 0 + while i < 2000: + if a > 0: # Specialises the loop + pass + if b > 1: + pass + if a^b >= 0: + sa += 1 + i += 1 + return sa + ''', ops, ([a, b], r)) + + def test_shift(self): + from sys import maxint + maxvals = (-maxint-1, -maxint, maxint-1, maxint) + for a in (-4, -3, -2, -1, 0, 1, 2, 3, 4) + maxvals: + for b in (0, 1, 2, 31, 32, 33, 61, 62, 63): + r = 0 + if (a >> b) >= 0: + r += 2000 + if (a << b) > 2: + r += 20000000 + if abs(a) < 10 and b < 5: + ops = 13 + else: + ops = 29 + + self.run_source(''' + def main(a, b): + i = sa = 0 + while i < 2000: + if a > 0: # Specialises the loop + pass + if b < 2 and b > 0: + pass + if (a >> b) >= 0: + sa += 1 + if (a << b) > 2: + sa += 10000 + i += 1 + return sa + ''', ops, ([a, b], r), count_debug_merge_point=False) + def test_ctypes_call(self): py.test.skip('fixme') from pypy.rlib.test.test_libffi import get_libm_name @@ -1316,7 +1430,7 @@ class PyPyCJITTests(object): fabs_addr = int(out.splitlines()[0]) assert len(self.loops) == 2 # the first is the loop, the second is a bridge loop = self.loops[0] - call_functions = self.get_by_bytecode('CALL_FUNCTION', loop) + call_functions = self.get_by_bytecode('CALL_FUNCTION', loop=loop) assert len(call_functions) == 2 # # this is the call "fabs(x)" diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py index aba9e0b16d..3bc6a3c78d 100644 --- a/pypy/module/sys/version.py +++ b/pypy/module/sys/version.py @@ -8,7 +8,7 @@ import os CPYTHON_VERSION = (2, 5, 2, "beta", 42) #XXX # sync patchlevel.h CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h -PYPY_VERSION = (1, 4, 0, "beta", 0) #XXX # sync patchlevel.h +PYPY_VERSION = (1, 4, 1, "beta", 0) #XXX # sync patchlevel.h import pypy diff --git a/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c b/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c index 28ec7a4b74..fccda34d6d 100644 --- a/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c +++ b/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c @@ -533,7 +533,7 @@ EXPORT(UN) ret_un_func(UN inp) return inp; } -int my_unused_function(void) +EXPORT(int) my_unused_function(void) { return 42; } diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py index 5659c32462..a601ff5f00 100644 --- a/pypy/objspace/descroperation.py +++ b/pypy/objspace/descroperation.py @@ -30,7 +30,7 @@ object_delattr._annspecialcase_ = 'specialize:memo' def raiseattrerror(space, w_obj, name, w_descr=None): w_type = space.type(w_obj) - typename = w_type.getname(space, '?') + typename = w_type.getname(space) if w_descr is None: raise operationerrfmt(space.w_AttributeError, "'%s' object has no attribute '%s'", @@ -138,7 +138,7 @@ class DescrOperation(object): return w_obj.call_args(args) w_descr = space.lookup(w_obj, '__call__') if w_descr is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object is not callable", typename) @@ -155,7 +155,7 @@ class DescrOperation(object): def set(space, w_descr, w_obj, w_val): w_set = space.lookup(w_descr, '__set__') if w_set is None: - typename = space.type(w_descr).getname(space, '?') + typename = space.type(w_descr).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object is not a descriptor with set", typename) @@ -164,7 +164,7 @@ class DescrOperation(object): def delete(space, w_descr, w_obj): w_delete = space.lookup(w_descr, '__delete__') if w_delete is None: - typename = space.type(w_descr).getname(space, '?') + typename = space.type(w_descr).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object is not a descriptor with delete", typename) @@ -191,7 +191,7 @@ class DescrOperation(object): def setattr(space, w_obj, w_name, w_val): w_descr = space.lookup(w_obj, '__setattr__') if w_descr is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_AttributeError, "'%s' object is readonly", typename) @@ -200,7 +200,7 @@ class DescrOperation(object): def delattr(space, w_obj, w_name): w_descr = space.lookup(w_obj, '__delattr__') if w_descr is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_AttributeError, "'%s' object does not support attribute removal", typename) @@ -241,7 +241,7 @@ class DescrOperation(object): if w_descr is None: w_descr = space.lookup(w_obj, '__getitem__') if w_descr is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object is not iterable", typename) @@ -251,7 +251,7 @@ class DescrOperation(object): def next(space, w_obj): w_descr = space.lookup(w_obj, 'next') if w_descr is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object is not an iterator", typename) @@ -260,7 +260,7 @@ class DescrOperation(object): def getitem(space, w_obj, w_key): w_descr = space.lookup(w_obj, '__getitem__') if w_descr is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object is not subscriptable", typename) @@ -269,7 +269,7 @@ class DescrOperation(object): def setitem(space, w_obj, w_key, w_val): w_descr = space.lookup(w_obj, '__setitem__') if w_descr is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object does not support item assignment", typename) @@ -278,7 +278,7 @@ class DescrOperation(object): def delitem(space, w_obj, w_key): w_descr = space.lookup(w_obj, '__delitem__') if w_descr is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "'%s' object does not support item deletion", typename) @@ -630,8 +630,8 @@ def _make_binop_impl(symbol, specialnames): w_res = _invoke_binop(space, w_right_impl, w_obj2, w_obj1) if w_res is not None: return w_res - typename1 = w_typ1.getname(space, '?') - typename2 = w_typ2.getname(space, '?') + typename1 = w_typ1.getname(space) + typename2 = w_typ2.getname(space) raise operationerrfmt(space.w_TypeError, errormsg, typename1, typename2) @@ -693,7 +693,7 @@ def _make_unaryop_impl(symbol, specialnames): def unaryop_impl(space, w_obj): w_impl = space.lookup(w_obj, specialname) if w_impl is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, errormsg, typename) return space.get_and_call_function(w_impl, w_obj) return func_with_new_name(unaryop_impl, 'unaryop_%s_impl'%specialname.strip('_')) @@ -715,7 +715,7 @@ for targetname, specialname, checkerspec in [ def %(targetname)s(space, w_obj): w_impl = space.lookup(w_obj, %(specialname)r) if w_impl is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "unsupported operand type for %(targetname)s(): '%%s'", typename) @@ -723,7 +723,7 @@ for targetname, specialname, checkerspec in [ if %(checker)s: return w_result - typename = space.type(w_result).getname(space, '?') + typename = space.type(w_result).getname(space) msg = "%(specialname)s returned non-%(targetname)s (type '%%s')" raise operationerrfmt(space.w_TypeError, msg, typename) assert not hasattr(DescrOperation, %(targetname)r) @@ -742,7 +742,7 @@ for targetname, specialname in [ def %(targetname)s(space, w_obj): w_impl = space.lookup(w_obj, %(specialname)r) if w_impl is None: - typename = space.type(w_obj).getname(space, '?') + typename = space.type(w_obj).getname(space) raise operationerrfmt(space.w_TypeError, "unsupported operand type for %(targetname)s(): '%%s'", typename) @@ -755,7 +755,7 @@ for targetname, specialname in [ except OperationError, e: if not e.match(space, space.w_TypeError): raise - typename = space.type(w_result).getname(space, '?') + typename = space.type(w_result).getname(space) msg = "%(specialname)s returned non-%(targetname)s (type '%%s')" raise operationerrfmt(space.w_TypeError, msg, typename) else: diff --git a/pypy/objspace/std/default.py b/pypy/objspace/std/default.py index 56382296b9..f6858d1d8c 100644 --- a/pypy/objspace/std/default.py +++ b/pypy/objspace/std/default.py @@ -18,7 +18,7 @@ def init__ANY(space, w_obj, __args__): pass def typed_unwrap_error_msg(space, expected, w_obj): - type_name = space.type(w_obj).getname(space, '?') + type_name = space.type(w_obj).getname(space) return space.wrap("expected %s, got %s object" % (expected, type_name)) def int_w__ANY(space,w_obj): diff --git a/pypy/objspace/std/objecttype.py b/pypy/objspace/std/objecttype.py index 425f15878a..8ef2a0f997 100644 --- a/pypy/objspace/std/objecttype.py +++ b/pypy/objspace/std/objecttype.py @@ -9,7 +9,7 @@ from pypy.objspace.std.register_all import register_all def descr__repr__(space, w_obj): w = space.wrap w_type = space.type(w_obj) - classname = w_type.getname(space, '?') + classname = w_type.getname(space) w_module = w_type.lookup("__module__") if w_module is not None: try: @@ -32,7 +32,7 @@ def descr_set___class__(space, w_obj, w_newcls): if not isinstance(w_newcls, W_TypeObject): raise operationerrfmt(space.w_TypeError, "__class__ must be set to new-style class, not '%s' object", - space.type(w_newcls).getname(space, '?')) + space.type(w_newcls).getname(space)) if not w_newcls.is_heaptype(): raise OperationError(space.w_TypeError, space.wrap("__class__ assignment: only for heap types")) @@ -44,7 +44,7 @@ def descr_set___class__(space, w_obj, w_newcls): else: raise operationerrfmt(space.w_TypeError, "__class__ assignment: '%s' object layout differs from '%s'", - w_oldcls.getname(space, '?'), w_newcls.getname(space, '?')) + w_oldcls.getname(space), w_newcls.getname(space)) def descr__new__(space, w_type, __args__): diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py index 09d2eee7ac..2cef159158 100644 --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -344,7 +344,7 @@ class StdObjSpace(ObjSpace, DescrOperation): else: raise operationerrfmt(self.w_TypeError, "%s.__new__(%s): only for the type %s", - w_type.name, w_subtype.getname(self, '?'), w_type.name) + w_type.name, w_subtype.getname(self), w_type.name) return instance allocate_instance._annspecialcase_ = "specialize:arg(1)" diff --git a/pypy/objspace/std/ropeobject.py b/pypy/objspace/std/ropeobject.py index f9e442ea53..2a8cf91c53 100644 --- a/pypy/objspace/std/ropeobject.py +++ b/pypy/objspace/std/ropeobject.py @@ -287,7 +287,7 @@ def str_join__Rope_ANY(space, w_self, w_list): raise operationerrfmt( space.w_TypeError, "sequence item %d: expected string, %s " - "found", i, space.type(w_s).getname(space, "?")) + "found", i, space.type(w_s).getname(space)) assert isinstance(w_s, W_RopeObject) node = w_s._node l.append(node) diff --git a/pypy/objspace/std/ropeunicodeobject.py b/pypy/objspace/std/ropeunicodeobject.py index c5cd1fb262..41024e1a2d 100644 --- a/pypy/objspace/std/ropeunicodeobject.py +++ b/pypy/objspace/std/ropeunicodeobject.py @@ -30,7 +30,7 @@ def unicode_from_string(space, w_str): raise operationerrfmt( space.w_TypeError, "decoder did not return an unicode object (type '%s')", - space.type(w_retval).getname(space, '?')) + space.type(w_retval).getname(space)) assert isinstance(w_retval, W_RopeUnicodeObject) return w_retval diff --git a/pypy/objspace/std/stdtypedef.py b/pypy/objspace/std/stdtypedef.py index d44952ba3a..8c8e520daa 100644 --- a/pypy/objspace/std/stdtypedef.py +++ b/pypy/objspace/std/stdtypedef.py @@ -146,7 +146,7 @@ _gettypeerrormsg._annspecialcase_ = 'specialize:memo' def _gettypenames(space, *args_w): if args_w: - typename = space.type(args_w[-1]).getname(space, '?') + typename = space.type(args_w[-1]).getname(space) return _gettypenames(space, *args_w[:-1]) + (typename,) return () _gettypenames._always_inline_ = True diff --git a/pypy/objspace/std/stringobject.py b/pypy/objspace/std/stringobject.py index ddb4eff97e..aea0d38dd6 100644 --- a/pypy/objspace/std/stringobject.py +++ b/pypy/objspace/std/stringobject.py @@ -341,7 +341,7 @@ def str_join__String_ANY(space, w_self, w_list): raise operationerrfmt( space.w_TypeError, "sequence item %d: expected string, %s " - "found", i, space.type(w_s).getname(space, '?')) + "found", i, space.type(w_s).getname(space)) reslen += len(space.str_w(w_s)) reslen += len(self) * (len(list_w) - 1) sb = StringBuilder(reslen) diff --git a/pypy/objspace/std/transparent.py b/pypy/objspace/std/transparent.py index 84c83e4f0b..ebe9c5bdc6 100644 --- a/pypy/objspace/std/transparent.py +++ b/pypy/objspace/std/transparent.py @@ -63,7 +63,7 @@ completely controlled by the controller.""" return v(space, w_type, w_controller) raise operationerrfmt(space.w_TypeError, "'%s' object could not be wrapped (YET)", - w_type.getname(space, "?")) + w_type.getname(space)) def register_proxyable(space, cls): tpdef = cls.typedef diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py index 4c02612d8d..b51c7cbe46 100644 --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -334,7 +334,7 @@ class W_TypeObject(W_Object): if not isinstance(w_subtype, W_TypeObject): raise operationerrfmt(space.w_TypeError, "X is not a type object ('%s')", - space.type(w_subtype).getname(space, '?')) + space.type(w_subtype).getname(space)) if not space.is_true(space.issubtype(w_subtype, w_self)): raise operationerrfmt(space.w_TypeError, "%s.__new__(%s): %s is not a subtype of %s", @@ -875,7 +875,7 @@ def mro_error(space, orderlists): # explicit error message for this specific case raise operationerrfmt(space.w_TypeError, "duplicate base class '%s'", - candidate.getname(space,"?")) + candidate.getname(space)) while candidate not in cycle: cycle.append(candidate) nextblockinglist = mro_blockinglist(candidate, orderlists) @@ -883,7 +883,7 @@ def mro_error(space, orderlists): del cycle[:cycle.index(candidate)] cycle.append(candidate) cycle.reverse() - names = [cls.getname(space, "?") for cls in cycle] + names = [cls.getname(space) for cls in cycle] raise OperationError(space.w_TypeError, space.wrap("cycle among base classes: " + ' < '.join(names))) diff --git a/pypy/objspace/std/typetype.py b/pypy/objspace/std/typetype.py index 3ee0e4a1de..9486b0fb85 100644 --- a/pypy/objspace/std/typetype.py +++ b/pypy/objspace/std/typetype.py @@ -54,7 +54,7 @@ def _precheck_for_new(space, w_type): if not isinstance(w_type, W_TypeObject): raise operationerrfmt(space.w_TypeError, "X is not a type object (%s)", - space.type(w_type).getname(space, '?')) + space.type(w_type).getname(space)) return w_type # ____________________________________________________________ @@ -114,7 +114,7 @@ def descr_set__bases__(space, w_type, w_value): raise operationerrfmt(space.w_TypeError, "can only assign tuple to %s.__bases__, not %s", w_type.name, - space.type(w_value).getname(space, '?')) + space.type(w_value).getname(space)) newbases_w = space.fixedview(w_value) if len(newbases_w) == 0: raise operationerrfmt(space.w_TypeError, @@ -137,8 +137,8 @@ def descr_set__bases__(space, w_type, w_value): raise operationerrfmt(space.w_TypeError, "__bases__ assignment: '%s' object layout" " differs from '%s'", - w_newbestbase.getname(space, '?'), - w_oldbestbase.getname(space, '?')) + w_newbestbase.getname(space), + w_oldbestbase.getname(space)) # invalidate the version_tag of all the current subclasses w_type.mutated() diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py index e5bc6c1e56..803f250b24 100644 --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -49,7 +49,7 @@ def unicode_to_decimal_w(space, w_unistr): if not isinstance(w_unistr, W_UnicodeObject): raise operationerrfmt(space.w_TypeError, "expected unicode, got '%s'", - space.type(w_unistr).getname(space, '?')) + space.type(w_unistr).getname(space)) unistr = w_unistr._value result = ['\0'] * len(unistr) digits = [ '0', '1', '2', '3', '4', diff --git a/pypy/objspace/std/unicodetype.py b/pypy/objspace/std/unicodetype.py index 7b6a74f4c4..ef3e5593a2 100644 --- a/pypy/objspace/std/unicodetype.py +++ b/pypy/objspace/std/unicodetype.py @@ -238,7 +238,7 @@ def encode_object(space, w_object, encoding, errors): if not space.is_true(space.isinstance(w_retval, space.w_str)): raise operationerrfmt(space.w_TypeError, "encoder did not return an string object (type '%s')", - space.type(w_retval).getname(space, '?')) + space.type(w_retval).getname(space)) return w_retval def decode_object(space, w_obj, encoding, errors): @@ -271,7 +271,7 @@ def unicode_from_encoded_object(space, w_obj, encoding, errors): if not space.is_true(space.isinstance(w_retval, space.w_unicode)): raise operationerrfmt(space.w_TypeError, "decoder did not return an unicode object (type '%s')", - space.type(w_retval).getname(space, '?')) + space.type(w_retval).getname(space)) return w_retval def unicode_from_object(space, w_obj): diff --git a/pypy/objspace/taint.py b/pypy/objspace/taint.py index 94e0805de1..c232945bef 100644 --- a/pypy/objspace/taint.py +++ b/pypy/objspace/taint.py @@ -152,7 +152,7 @@ def taint_look(space, w_obj): """Print some info about the taintedness of an object. For debugging purposes only!""" if isinstance(w_obj, W_Tainted): - info = space.type(w_obj.w_obj).getname(space, '?') + info = space.type(w_obj.w_obj).getname(space) msg = space.str_w(w_obj.w_obj.getrepr(space, info)) msg = 'Taint Box %s\n' % msg os.write(2, msg) diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py index 76f7ad9c0c..48b2b76722 100644 --- a/pypy/rlib/jit.py +++ b/pypy/rlib/jit.py @@ -257,7 +257,7 @@ class JitHintError(Exception): """Inconsistency in the JIT hints.""" OPTIMIZER_SIMPLE = 0 -OPTIMIZER_NO_PERFECTSPEC = 1 +OPTIMIZER_NO_UNROLL = 1 OPTIMIZER_FULL = 2 PARAMETERS = {'threshold': 1000, @@ -387,8 +387,7 @@ class ExtEnterLeaveMarker(ExtRegistryEntry): from pypy.annotation import model as annmodel if self.instance.__name__ == 'jit_merge_point': - if not self.annotate_hooks(**kwds_s): - return None # wrong order, try again later + self.annotate_hooks(**kwds_s) driver = self.instance.im_self keys = kwds_s.keys() @@ -424,13 +423,13 @@ class ExtEnterLeaveMarker(ExtRegistryEntry): driver = self.instance.im_self s_jitcell = self.bookkeeper.valueoftype(BaseJitCell) h = self.annotate_hook - return (h(driver.get_jitcell_at, driver.greens, **kwds_s) - and h(driver.set_jitcell_at, driver.greens, [s_jitcell], **kwds_s) - and h(driver.get_printable_location, driver.greens, **kwds_s)) + h(driver.get_jitcell_at, driver.greens, **kwds_s) + h(driver.set_jitcell_at, driver.greens, [s_jitcell], **kwds_s) + h(driver.get_printable_location, driver.greens, **kwds_s) def annotate_hook(self, func, variables, args_s=[], **kwds_s): if func is None: - return True + return bk = self.bookkeeper s_func = bk.immutablevalue(func) uniquekey = 'jitdriver.%s' % func.func_name @@ -441,12 +440,13 @@ class ExtEnterLeaveMarker(ExtRegistryEntry): else: objname, fieldname = name.split('.') s_instance = kwds_s['s_' + objname] - s_arg = s_instance.classdef.about_attribute(fieldname) - if s_arg is None: - return False # wrong order, try again later + attrdef = s_instance.classdef.find_attribute(fieldname) + position = self.bookkeeper.position_key + attrdef.read_locations[position] = True + s_arg = attrdef.getvalue() + assert s_arg is not None args_s.append(s_arg) bk.emulate_pbc_call(uniquekey, s_func, args_s) - return True def specialize_call(self, hop, **kwds_i): # XXX to be complete, this could also check that the concretetype diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py index 7e16b6fd57..16b9dc040b 100644 --- a/pypy/rlib/rbigint.py +++ b/pypy/rlib/rbigint.py @@ -294,7 +294,6 @@ class rbigint(object): else: result = _x_sub(other, self) result.sign *= other.sign - result._normalize() return result def sub(self, other): @@ -554,7 +553,8 @@ class rbigint(object): while i > 1 and self.digits[i - 1] == 0: i -= 1 assert i >= 1 - self.digits = self.digits[:i] + if i != self._numdigits(): + self.digits = self.digits[:i] if self._numdigits() == 1 and self.digits[0] == 0: self.sign = 0 diff --git a/pypy/rpython/lltypesystem/opimpl.py b/pypy/rpython/lltypesystem/opimpl.py index 03ba9e7571..9a2fedc7a6 100644 --- a/pypy/rpython/lltypesystem/opimpl.py +++ b/pypy/rpython/lltypesystem/opimpl.py @@ -19,6 +19,7 @@ ops_unary = {'is_true': True, 'neg': True, 'abs': True, 'invert': True} # global synonyms for some types from pypy.rlib.rarithmetic import intmask from pypy.rlib.rarithmetic import r_int, r_uint, r_longlong, r_ulonglong +from pypy.rpython.lltypesystem.llmemory import AddressAsInt if r_longlong is r_int: r_longlong_arg = (r_longlong, int) @@ -76,11 +77,13 @@ def get_primitive_op_src(fullopname): else: def op_function(x, y): if not isinstance(x, argtype): - raise TypeError("%r arg 1 must be %s, got %r instead" % ( - fullopname, typname, type(x).__name__)) + if not (isinstance(x, AddressAsInt) and argtype is int): + raise TypeError("%r arg 1 must be %s, got %r instead"% ( + fullopname, typname, type(x).__name__)) if not isinstance(y, argtype): - raise TypeError("%r arg 2 must be %s, got %r instead" % ( - fullopname, typname, type(y).__name__)) + if not (isinstance(y, AddressAsInt) and argtype is int): + raise TypeError("%r arg 2 must be %s, got %r instead"% ( + fullopname, typname, type(y).__name__)) return adjust_result(func(x, y)) return func_with_new_name(op_function, 'op_' + fullopname) diff --git a/pypy/rpython/memory/gc/base.py b/pypy/rpython/memory/gc/base.py index 0c5e5a7484..61c4365cf4 100644 --- a/pypy/rpython/memory/gc/base.py +++ b/pypy/rpython/memory/gc/base.py @@ -3,7 +3,7 @@ from pypy.rlib.debug import ll_assert from pypy.rpython.memory.gcheader import GCHeaderBuilder from pypy.rpython.memory.support import DEFAULT_CHUNK_SIZE from pypy.rpython.memory.support import get_address_stack, get_address_deque -from pypy.rpython.memory.support import AddressDict +from pypy.rpython.memory.support import AddressDict, null_address_dict from pypy.rpython.lltypesystem.llmemory import NULL, raw_malloc_usage TYPEID_MAP = lltype.GcStruct('TYPEID_MAP', ('count', lltype.Signed), @@ -26,6 +26,7 @@ class GCBase(object): self.AddressStack = get_address_stack(chunk_size) self.AddressDeque = get_address_deque(chunk_size) self.AddressDict = AddressDict + self.null_address_dict = null_address_dict self.config = config assert isinstance(translated_to_c, bool) self.translated_to_c = translated_to_c diff --git a/pypy/rpython/memory/gc/minimark.py b/pypy/rpython/memory/gc/minimark.py index 98c4d76173..d249682c04 100644 --- a/pypy/rpython/memory/gc/minimark.py +++ b/pypy/rpython/memory/gc/minimark.py @@ -55,15 +55,29 @@ from pypy.rlib.debug import ll_assert, debug_print, debug_start, debug_stop from pypy.rlib.objectmodel import we_are_translated from pypy.tool.sourcetools import func_with_new_name +# +# Handles the objects in 2 generations: +# +# * young objects: allocated in the nursery if they are not too large, or +# raw-malloced otherwise. The nursery is a fixed-size memory buffer of +# half the size of the L2 cache. When full, we do a minor collection; +# the surviving objects from the nursery are moved outside, and the +# non-surviving raw-malloced objects are freed. All surviving objects +# become old. +# +# * old objects: never move again. These objects are either allocated by +# minimarkpage.py (if they are small), or raw-malloced (if they are not +# small). Collected by regular mark-n-sweep during major collections. +# + WORD = LONG_BIT // 8 NULL = llmemory.NULL first_gcflag = 1 << (LONG_BIT//2) -# The following flag is never set on young objects, i.e. the ones living -# in the nursery. It is initially set on all prebuilt and old objects, -# and gets cleared by the write_barrier() when we write in them a -# pointer to a young object. +# The following flag is never set on young objects. It is initially set +# on all prebuilt and old objects, and gets cleared by the write_barrier() +# when we write in them a pointer to a young object. GCFLAG_NO_YOUNG_PTRS = first_gcflag << 0 # The following flag is set on some prebuilt objects. The flag is set @@ -73,7 +87,8 @@ GCFLAG_NO_YOUNG_PTRS = first_gcflag << 0 # 'prebuilt_root_objects'. GCFLAG_NO_HEAP_PTRS = first_gcflag << 1 -# The following flag is set on surviving objects during a major collection. +# The following flag is set on surviving objects during a major collection, +# and on surviving raw-malloced young objects during a minor collection. GCFLAG_VISITED = first_gcflag << 2 # The following flag is set on nursery objects of which we asked the id @@ -177,14 +192,10 @@ class MiniMarkGC(MovingGCBase): "card_page_indices": 128, # Objects whose total size is at least 'large_object' bytes are - # allocated out of the nursery immediately. If the object - # has GC pointers in its varsized part, we use instead the - # higher limit 'large_object_gcptrs'. The idea is that - # separately allocated objects are allocated immediately "old" - # and it's not good to have too many pointers from old to young - # objects. - "large_object": 1600*WORD, - "large_object_gcptrs": 8250*WORD, + # allocated out of the nursery immediately, as old objects. The + # minimal allocated size of the nursery is 2x the following + # number (by default, at least 132KB on 32-bit and 264KB on 64-bit). + "large_object": (16384+512)*WORD, } def __init__(self, config, @@ -197,7 +208,6 @@ class MiniMarkGC(MovingGCBase): growth_rate_max=2.5, # for tests card_page_indices=0, large_object=8*WORD, - large_object_gcptrs=10*WORD, ArenaCollectionClass=None, **kwds): MovingGCBase.__init__(self, config, **kwds) @@ -219,12 +229,9 @@ class MiniMarkGC(MovingGCBase): while (1 << self.card_page_shift) < self.card_page_indices: self.card_page_shift += 1 # - # 'large_object' and 'large_object_gcptrs' limit how big objects - # can be in the nursery, so they give a lower bound on the allowed - # size of the nursery. + # 'large_object' limit how big objects can be in the nursery, so + # it gives a lower bound on the allowed size of the nursery. self.nonlarge_max = large_object - 1 - self.nonlarge_gcptrs_max = large_object_gcptrs - 1 - assert self.nonlarge_max <= self.nonlarge_gcptrs_max # self.nursery = NULL self.nursery_free = NULL @@ -263,11 +270,12 @@ class MiniMarkGC(MovingGCBase): # we implement differently anyway. So directly call GCBase.setup(). GCBase.setup(self) # - # A list of all raw_malloced objects (the objects too large) - self.rawmalloced_objects = self.AddressStack() + # Two lists of all raw_malloced objects (the objects too large) + self.young_rawmalloced_objects = self.null_address_dict() + self.old_rawmalloced_objects = self.AddressStack() self.rawmalloced_total_size = r_uint(0) # - # A list of all objects with finalizers (never in the nursery). + # A list of all objects with finalizers (these are never young). self.objects_with_finalizers = self.AddressDeque() # # Two lists of the objects with weakrefs. No weakref can be an @@ -280,7 +288,7 @@ class MiniMarkGC(MovingGCBase): # Support for id and identityhash: map nursery objects with # GCFLAG_HAS_SHADOW to their future location at the next # minor collection. - self.young_objects_shadows = self.AddressDict() + self.nursery_objects_shadows = self.AddressDict() # # Allocate a nursery. In case of auto_nursery_size, start by # allocating a very small nursery, enough to do things like look @@ -291,7 +299,7 @@ class MiniMarkGC(MovingGCBase): else: # defaultsize = self.nursery_size - minsize = 2 * (self.nonlarge_gcptrs_max + 1) + minsize = 2 * (self.nonlarge_max + 1) self.nursery_size = minsize self.allocate_nursery() # @@ -303,8 +311,8 @@ class MiniMarkGC(MovingGCBase): # forces a minor collect for every malloc. Useful to debug # external factors, like trackgcroot or the handling of the write # barrier. Implemented by still using 'minsize' for the nursery - # size (needed to handle e.g. mallocs of 8249 words) but hacking - # at the current nursery position in collect_and_reserve(). + # size (needed to handle mallocs just below 'large_objects') but + # hacking at the current nursery position in collect_and_reserve(). if newsize <= 0: newsize = env.estimate_best_nursery_size() if newsize <= 0: @@ -345,7 +353,7 @@ class MiniMarkGC(MovingGCBase): def _nursery_memory_size(self): - extra = self.nonlarge_gcptrs_max + 1 + extra = self.nonlarge_max + 1 return self.nursery_size + extra def _alloc_nursery(self): @@ -441,7 +449,7 @@ class MiniMarkGC(MovingGCBase): if needs_finalizer: ll_assert(not contains_weakptr, "'needs_finalizer' and 'contains_weakptr' both specified") - obj = self.external_malloc(typeid, 0) + obj = self.external_malloc(typeid, 0, can_make_young=False) self.objects_with_finalizers.append(obj) # # If totalsize is greater than nonlarge_max (which should never be @@ -489,16 +497,11 @@ class MiniMarkGC(MovingGCBase): # below 'nonlarge_max'. All the following logic is usually # constant-folded because self.nonlarge_max, size and itemsize # are all constants (the arguments are constant due to - # inlining) and self.has_gcptr_in_varsize() is constant-folded. - if self.has_gcptr_in_varsize(typeid): - nonlarge_max = self.nonlarge_gcptrs_max - else: - nonlarge_max = self.nonlarge_max - + # inlining). if not raw_malloc_usage(itemsize): - too_many_items = raw_malloc_usage(nonvarsize) > nonlarge_max + too_many_items = raw_malloc_usage(nonvarsize) > self.nonlarge_max else: - maxlength = nonlarge_max - raw_malloc_usage(nonvarsize) + maxlength = self.nonlarge_max - raw_malloc_usage(nonvarsize) maxlength = maxlength // raw_malloc_usage(itemsize) too_many_items = length > maxlength @@ -574,7 +577,7 @@ class MiniMarkGC(MovingGCBase): collect_and_reserve._dont_inline_ = True - def external_malloc(self, typeid, length): + def external_malloc(self, typeid, length, can_make_young=True): """Allocate a large object using the ArenaCollection or raw_malloc(), possibly as an object with card marking enabled, if it has gc pointers in its var-sized part. 'length' should be @@ -616,14 +619,19 @@ class MiniMarkGC(MovingGCBase): # Allocate from the ArenaCollection and clear the memory returned. result = self.ac.malloc(totalsize) llmemory.raw_memclear(result, totalsize) - extra_flags = 0 + # + # An object allocated from ArenaCollection is always old, even + # if 'can_make_young'. The interesting case of 'can_make_young' + # is for large objects, bigger than the 'large_objects' threshold, + # which are raw-malloced but still young. + extra_flags = GCFLAG_NO_YOUNG_PTRS # else: # No, so proceed to allocate it externally with raw_malloc(). # Check if we need to introduce the card marker bits area. if (self.card_page_indices <= 0 # <- this check is constant-folded or not self.has_gcptr_in_varsize(typeid) or - raw_malloc_usage(totalsize) <= self.nonlarge_gcptrs_max): + raw_malloc_usage(totalsize) <= self.nonlarge_max): # # In these cases, we don't want a card marker bits area. # This case also includes all fixed-size objects. @@ -635,6 +643,8 @@ class MiniMarkGC(MovingGCBase): extra_words = self.card_marking_words_for_length(length) cardheadersize = WORD * extra_words extra_flags = GCFLAG_HAS_CARDS + # note that if 'can_make_young', then card marking will only + # be used later, after (and if) the object becomes old # # Detect very rare cases of overflows if raw_malloc_usage(totalsize) > (sys.maxint - (WORD-1) @@ -667,11 +677,18 @@ class MiniMarkGC(MovingGCBase): llarena.arena_reserve(result, totalsize) # # Record the newly allocated object and its full malloced size. + # The object is young or old depending on the argument. self.rawmalloced_total_size += allocsize - self.rawmalloced_objects.append(result + size_gc_header) + if can_make_young: + if not self.young_rawmalloced_objects: + self.young_rawmalloced_objects = self.AddressDict() + self.young_rawmalloced_objects.add(result + size_gc_header) + else: + self.old_rawmalloced_objects.append(result + size_gc_header) + extra_flags |= GCFLAG_NO_YOUNG_PTRS # # Common code to fill the header and length of the object. - self.init_gc_object(result, typeid, GCFLAG_NO_YOUNG_PTRS | extra_flags) + self.init_gc_object(result, typeid, extra_flags) if self.is_varsize(typeid): offset_to_length = self.varsize_offset_to_length(typeid) (result + size_gc_header + offset_to_length).signed[0] = length @@ -767,13 +784,35 @@ class MiniMarkGC(MovingGCBase): "odd-valued (i.e. tagged) pointer unexpected here") return self.nursery <= addr < self.nursery_top - def appears_to_be_in_nursery(self, addr): - # same as is_in_nursery(), but may return True accidentally if - # 'addr' is a tagged pointer with just the wrong value. + def appears_to_be_young(self, addr): + # "is a valid addr to a young object?" + # but it's ok to occasionally return True accidentally. + # Maybe the best implementation would be a bloom filter + # of some kind instead of the dictionary lookup that is + # sometimes done below. But the expected common answer + # is "Yes" because addr points to the nursery, so it may + # not be useful to optimize the other case too much. + # + # First, if 'addr' appears to be a pointer to some place within + # the nursery, return True if not self.translated_to_c: + # When non-translated, filter out tagged pointers explicitly. + # When translated, it may occasionally give a wrong answer + # of True if 'addr' is a tagged pointer with just the wrong value. if not self.is_valid_gc_object(addr): return False - return self.nursery <= addr < self.nursery_top + + if self.nursery <= addr < self.nursery_top: + return True # addr is in the nursery + # + # Else, it may be in the set 'young_rawmalloced_objects' + return (bool(self.young_rawmalloced_objects) and + self.young_rawmalloced_objects.contains(addr)) + appears_to_be_young._always_inline_ = True + + def debug_is_old_object(self, addr): + return (self.is_valid_gc_object(addr) + and not self.appears_to_be_young(addr)) def is_forwarded(self, obj): """Returns True if the nursery obj is marked as forwarded. @@ -813,6 +852,14 @@ class MiniMarkGC(MovingGCBase): ((r_uint(length) + ((8 << self.card_page_shift) - 1)) >> (self.card_page_shift + 3))) + def debug_check_consistency(self): + if self.DEBUG: + ll_assert(not self.young_rawmalloced_objects, + "young raw-malloced objects in a major collection") + ll_assert(not self.young_objects_with_weakrefs.non_empty(), + "young objects with weakrefs in a major collection") + MovingGCBase.debug_check_consistency(self) + def debug_check_object(self, obj): # after a minor or major collection, no object should be in the nursery ll_assert(not self.is_in_nursery(obj), @@ -820,10 +867,6 @@ class MiniMarkGC(MovingGCBase): # similarily, all objects should have this flag: ll_assert(self.header(obj).tid & GCFLAG_NO_YOUNG_PTRS, "missing GCFLAG_NO_YOUNG_PTRS") - # if we have GCFLAG_NO_HEAP_PTRS, then we have GCFLAG_NO_YOUNG_PTRS - if self.header(obj).tid & GCFLAG_NO_HEAP_PTRS: - ll_assert(self.header(obj).tid & GCFLAG_NO_YOUNG_PTRS, - "GCFLAG_NO_HEAP_PTRS && !GCFLAG_NO_YOUNG_PTRS") # the GCFLAG_VISITED should not be set between collections ll_assert(self.header(obj).tid & GCFLAG_VISITED == 0, "unexpected GCFLAG_VISITED") @@ -896,17 +939,17 @@ class MiniMarkGC(MovingGCBase): # 'addr_struct' is the address of the object in which we write. # 'newvalue' is the address that we are going to write in there. if DEBUG: # note: PYPY_GC_DEBUG=1 does not enable this - ll_assert(not self.is_in_nursery(addr_struct), - "nursery object with GCFLAG_NO_YOUNG_PTRS") + ll_assert(self.debug_is_old_object(addr_struct), + "young object with GCFLAG_NO_YOUNG_PTRS") # # If it seems that what we are writing is a pointer to the nursery - # (as checked with appears_to_be_in_nursery()), then we need + # (as checked with appears_to_be_young()), then we need # to remove the flag GCFLAG_NO_YOUNG_PTRS and add the old object # to the list 'old_objects_pointing_to_young'. We know that # 'addr_struct' cannot be in the nursery, because nursery objects # never have the flag GCFLAG_NO_YOUNG_PTRS to start with. objhdr = self.header(addr_struct) - if self.appears_to_be_in_nursery(newvalue): + if self.appears_to_be_young(newvalue): self.old_objects_pointing_to_young.append(addr_struct) objhdr.tid &= ~GCFLAG_NO_YOUNG_PTRS # @@ -933,8 +976,8 @@ class MiniMarkGC(MovingGCBase): # which must have an array part; 'index' is the index of the # item that is (or contains) the pointer that we write. if DEBUG: # note: PYPY_GC_DEBUG=1 does not enable this - ll_assert(not self.is_in_nursery(addr_array), - "nursery array with GCFLAG_NO_YOUNG_PTRS") + ll_assert(self.debug_is_old_object(addr_array), + "young array with GCFLAG_NO_YOUNG_PTRS") objhdr = self.header(addr_array) if objhdr.tid & GCFLAG_HAS_CARDS == 0: # @@ -1003,7 +1046,7 @@ class MiniMarkGC(MovingGCBase): # if (source_hdr.tid & GCFLAG_NO_YOUNG_PTRS == 0 or source_hdr.tid & GCFLAG_CARDS_SET != 0): - # there might be an object in source that is in nursery + # there might be in source a pointer to a young object self.old_objects_pointing_to_young.append(dest_addr) dest_hdr.tid &= ~GCFLAG_NO_YOUNG_PTRS # @@ -1023,11 +1066,12 @@ class MiniMarkGC(MovingGCBase): # debug_start("gc-minor") # - # First, find the roots that point to nursery objects. These - # nursery objects are copied out of the nursery. Note that - # references to further nursery objects are not modified by - # this step; only objects directly referenced by roots are - # copied out. They are also added to the list + # First, find the roots that point to young objects. All nursery + # objects found are copied out of the nursery, and the occasional + # young raw-malloced object is flagged with GCFLAG_VISITED. + # Note that during this step, we ignore references to further + # young objects; only objects directly referenced by roots + # are copied out or flagged. They are also added to the list # 'old_objects_pointing_to_young'. self.collect_roots_in_nursery() # @@ -1039,17 +1083,23 @@ class MiniMarkGC(MovingGCBase): # Now trace objects from 'old_objects_pointing_to_young'. # All nursery objects they reference are copied out of the # nursery, and again added to 'old_objects_pointing_to_young'. + # All young raw-malloced object found is flagged GCFLAG_VISITED. # We proceed until 'old_objects_pointing_to_young' is empty. self.collect_oldrefs_to_nursery() # - # Now all live nursery objects should be out. Update the - # young weakrefs' targets. + # Now all live nursery objects should be out. Update the young + # weakrefs' targets. if self.young_objects_with_weakrefs.non_empty(): self.invalidate_young_weakrefs() # # Clear this mapping. - if self.young_objects_shadows.length() > 0: - self.young_objects_shadows.clear() + if self.nursery_objects_shadows.length() > 0: + self.nursery_objects_shadows.clear() + # + # Walk the list of young raw-malloced objects, and either free + # them or make them old. + if self.young_rawmalloced_objects: + self.free_young_rawmalloced_objects() # # All live nursery objects are out, and the rest dies. Fill # the whole nursery with zero and reset the current nursery pointer. @@ -1168,9 +1218,22 @@ class MiniMarkGC(MovingGCBase): def _trace_drag_out(self, root, ignored): obj = root.address[0] + #print '_trace_drag_out(%x: %r)' % (hash(obj.ptr._obj), obj) # - # If 'obj' is not in the nursery, nothing to change. + # If 'obj' is not in the nursery, nothing to change -- expect + # that we must set GCFLAG_VISITED on young raw-malloced objects. if not self.is_in_nursery(obj): + # cache usage trade-off: I think that it is a better idea to + # check if 'obj' is in young_rawmalloced_objects with an access + # to this (small) dictionary, rather than risk a lot of cache + # misses by reading a flag in the header of all the 'objs' that + # arrive here. + if (bool(self.young_rawmalloced_objects) + and self.young_rawmalloced_objects.contains(obj)): + # 'obj' points to a young, raw-malloced object + if (self.header(obj).tid & GCFLAG_VISITED) == 0: + self.header(obj).tid |= GCFLAG_VISITED + self.old_objects_pointing_to_young.append(obj) return # # If 'obj' was already forwarded, change it to its forwarding address. @@ -1190,7 +1253,7 @@ class MiniMarkGC(MovingGCBase): # else: # The object has already a shadow. - newobj = self.young_objects_shadows.get(obj) + newobj = self.nursery_objects_shadows.get(obj) ll_assert(newobj != NULL, "GCFLAG_HAS_SHADOW but no shadow found") newhdr = newobj - size_gc_header # @@ -1247,9 +1310,20 @@ class MiniMarkGC(MovingGCBase): # size_gc_header = self.gcheaderbuilder.size_gc_header self.rawmalloced_total_size += raw_malloc_usage(totalsize) - self.rawmalloced_objects.append(arena + size_gc_header) + self.old_rawmalloced_objects.append(arena + size_gc_header) return arena + def free_young_rawmalloced_objects(self): + self.young_rawmalloced_objects.foreach( + self._free_young_rawmalloced_obj, None) + self.young_rawmalloced_objects.delete() + self.young_rawmalloced_objects = self.null_address_dict() + + def _free_young_rawmalloced_obj(self, obj, ignored1, ignored2): + # If 'obj' has GCFLAG_VISITED, it was seen by _trace_drag_out + # and survives. Otherwise, it dies. + self.free_rawmalloced_object_if_unvisited(obj) + # ---------- # Full collection @@ -1362,37 +1436,39 @@ class MiniMarkGC(MovingGCBase): def _reset_gcflag_visited(self, obj, ignored): self.header(obj).tid &= ~GCFLAG_VISITED + def free_rawmalloced_object_if_unvisited(self, obj): + if self.header(obj).tid & GCFLAG_VISITED: + self.header(obj).tid &= ~GCFLAG_VISITED # survives + self.old_rawmalloced_objects.append(obj) + else: + size_gc_header = self.gcheaderbuilder.size_gc_header + totalsize = size_gc_header + self.get_size(obj) + allocsize = raw_malloc_usage(totalsize) + arena = llarena.getfakearenaaddress(obj - size_gc_header) + # + # Must also include the card marker area, if any + if (self.card_page_indices > 0 # <- this is constant-folded + and self.header(obj).tid & GCFLAG_HAS_CARDS): + # + # Get the length and compute the number of extra bytes + typeid = self.get_type_id(obj) + ll_assert(self.has_gcptr_in_varsize(typeid), + "GCFLAG_HAS_CARDS but not has_gcptr_in_varsize") + offset_to_length = self.varsize_offset_to_length(typeid) + length = (obj + offset_to_length).signed[0] + extra_words = self.card_marking_words_for_length(length) + arena -= extra_words * WORD + allocsize += extra_words * WORD + # + llarena.arena_free(arena) + self.rawmalloced_total_size -= allocsize + def free_unvisited_rawmalloc_objects(self): - size_gc_header = self.gcheaderbuilder.size_gc_header - list = self.rawmalloced_objects - self.rawmalloced_objects = self.AddressStack() + list = self.old_rawmalloced_objects + self.old_rawmalloced_objects = self.AddressStack() # while list.non_empty(): - obj = list.pop() - if self.header(obj).tid & GCFLAG_VISITED: - self.header(obj).tid &= ~GCFLAG_VISITED # survives - self.rawmalloced_objects.append(obj) - else: - totalsize = size_gc_header + self.get_size(obj) - allocsize = raw_malloc_usage(totalsize) - arena = llarena.getfakearenaaddress(obj - size_gc_header) - # - # Must also include the card marker area, if any - if (self.card_page_indices > 0 # <- this is constant-folded - and self.header(obj).tid & GCFLAG_HAS_CARDS): - # - # Get the length and compute the number of extra bytes - typeid = self.get_type_id(obj) - ll_assert(self.has_gcptr_in_varsize(typeid), - "GCFLAG_HAS_CARDS but not has_gcptr_in_varsize") - offset_to_length = self.varsize_offset_to_length(typeid) - length = (obj + offset_to_length).signed[0] - extra_words = self.card_marking_words_for_length(length) - arena -= extra_words * WORD - allocsize += extra_words * WORD - # - llarena.arena_free(arena) - self.rawmalloced_total_size -= allocsize + self.free_rawmalloced_object_if_unvisited(list.pop()) # list.delete() @@ -1475,7 +1551,7 @@ class MiniMarkGC(MovingGCBase): # where the object will be moved by the next minor # collection if self.header(obj).tid & GCFLAG_HAS_SHADOW: - shadow = self.young_objects_shadows.get(obj) + shadow = self.nursery_objects_shadows.get(obj) ll_assert(shadow != NULL, "GCFLAG_HAS_SHADOW but no shadow found") else: @@ -1499,7 +1575,7 @@ class MiniMarkGC(MovingGCBase): (shadow + lenofs).signed[0] = (obj + lenofs).signed[0] # self.header(obj).tid |= GCFLAG_HAS_SHADOW - self.young_objects_shadows.setitem(obj, shadow) + self.nursery_objects_shadows.setitem(obj, shadow) # # The answer is the address of the shadow. obj = shadow @@ -1646,6 +1722,16 @@ class MiniMarkGC(MovingGCBase): else: (obj + offset).address[0] = llmemory.NULL continue # no need to remember this weakref any longer + # + elif (bool(self.young_rawmalloced_objects) and + self.young_rawmalloced_objects.contains(pointing_to)): + # young weakref to a young raw-malloced object + if self.header(pointing_to).tid & GCFLAG_VISITED: + pass # survives, but does not move + else: + (obj + offset).address[0] = llmemory.NULL + continue # no need to remember this weakref any longer + # self.old_objects_with_weakrefs.append(obj) diff --git a/pypy/rpython/memory/support.py b/pypy/rpython/memory/support.py index 4542c4a8a7..ea0945d31f 100644 --- a/pypy/rpython/memory/support.py +++ b/pypy/rpython/memory/support.py @@ -2,6 +2,7 @@ from pypy.rpython.lltypesystem import lltype, llmemory from pypy.rlib.objectmodel import free_non_gc_object, we_are_translated from pypy.rlib.rarithmetic import r_uint, LONG_BIT from pypy.rlib.debug import ll_assert +from pypy.tool.identity_dict import identity_dict DEFAULT_CHUNK_SIZE = 1019 @@ -255,10 +256,14 @@ def AddressDict(length_estimate=0): else: return BasicAddressDict() +def null_address_dict(): + from pypy.rpython.memory import lldict + return lltype.nullptr(lldict.DICT) + class BasicAddressDict(object): def __init__(self): - self.data = {} + self.data = identity_dict() # {_key(addr): value} def _key(self, addr): "NOT_RPYTHON: prebuilt AddressDicts are not supported" diff --git a/pypy/rpython/memory/test/test_transformed_gc.py b/pypy/rpython/memory/test/test_transformed_gc.py index 6411da9de6..6b59286694 100644 --- a/pypy/rpython/memory/test/test_transformed_gc.py +++ b/pypy/rpython/memory/test/test_transformed_gc.py @@ -1482,7 +1482,6 @@ class TestMiniMarkGC(TestHybridGC): 'arena_size': 64*WORD, 'small_request_threshold': 5*WORD, 'large_object': 8*WORD, - 'large_object_gcptrs': 10*WORD, 'card_page_indices': 4, 'translated_to_c': False, } diff --git a/pypy/rpython/rint.py b/pypy/rpython/rint.py index 9694fe23e3..9499dffc7e 100644 --- a/pypy/rpython/rint.py +++ b/pypy/rpython/rint.py @@ -222,7 +222,7 @@ def _rtype_template(hop, func, implicit_excs=[]): # return (x/y) - (((x^y)<0)&((x%y)!=0)); v_xor = hop.genop(prefix + 'xor', vlist, resulttype=repr) - v_xor_le = hop.genop(prefix + 'le', [v_xor, c_zero], + v_xor_le = hop.genop(prefix + 'lt', [v_xor, c_zero], resulttype=Bool) v_xor_le = hop.llops.convertvar(v_xor_le, bool_repr, repr) v_mod = hop.genop(prefix + 'mod', vlist, diff --git a/pypy/tool/logparser.py b/pypy/tool/logparser.py index 73a32bf40f..a409cf5885 100755 --- a/pypy/tool/logparser.py +++ b/pypy/tool/logparser.py @@ -93,6 +93,27 @@ def extract_category(log, catprefix='', toplevel=False): got.insert(0, '\n'.join(resulttext)) return got +def print_log(log): + for entry in log: + if entry[0] == 'debug_print': + print entry[1] + else: + print "{%s" % entry[0] + if len(entry)>3: + print_log(entry[3]) + print "%s}" % entry[0] + +def kill_category(log, catprefix=''): + newlog = [] + for entry in log: + if not entry[0].startswith(catprefix): + if len(entry) > 3: + newlog.append(entry[:3] + + (kill_category(entry[3], catprefix),)) + else: + newlog.append(entry) + return newlog + def getsubcategories(log): return [entry for entry in log if entry[0] != 'debug_print'] diff --git a/pypy/tool/pytest/test/test_pytestsupport.py b/pypy/tool/pytest/test/test_pytestsupport.py index 51ceadda27..148ab34518 100644 --- a/pypy/tool/pytest/test/test_pytestsupport.py +++ b/pypy/tool/pytest/test/test_pytestsupport.py @@ -3,7 +3,8 @@ from pypy.interpreter.gateway import app2interp_temp from pypy.interpreter.argument import Arguments from pypy.interpreter.pycode import PyCode from pypy.interpreter.pyframe import PyFrame -from pypy.tool.pytest.appsupport import AppFrame, build_pytest_assertion, AppExceptionInfo +from pypy.tool.pytest.appsupport import (AppFrame, build_pytest_assertion, + AppExceptionInfo) import py from pypy.tool.udir import udir import os @@ -33,7 +34,7 @@ def test_myexception(space): assert x == 43 t = app2interp_temp(app_test_func) f = t.get_function(space) - space.setitem(space.builtin.w_dict, space.wrap('AssertionError'), + space.setitem(space.builtin.w_dict, space.wrap('AssertionError'), build_pytest_assertion(space)) try: f.call_args(Arguments(None, [])) @@ -43,14 +44,14 @@ def test_myexception(space): else: assert False, "got no exception!" -def app_test_exception(): - try: +def app_test_exception(): + try: raise AssertionError("42") - except AssertionError: - pass - else: + except AssertionError: + pass + else: raise AssertionError, "app level AssertionError mixup!" - + def app_test_exception_with_message(): try: assert 0, "Failed" @@ -58,20 +59,20 @@ def app_test_exception_with_message(): assert e.msg == "Failed" -def test_appexecinfo(space): - try: - space.appexec([], "(): raise ValueError") - except OperationError, e: +def test_appexecinfo(space): + try: + space.appexec([], "(): raise ValueError") + except OperationError, e: appex = AppExceptionInfo(space, e) - else: - py.test.fail("did not raise!") - assert appex.exconly().find('ValueError') != -1 - assert appex.exconly(tryshort=True).find('ValueError') != -1 - assert appex.errisinstance(ValueError) - assert not appex.errisinstance(RuntimeError) - class A: + else: + py.test.fail("did not raise!") + assert appex.exconly().find('ValueError') != -1 + assert appex.exconly(tryshort=True).find('ValueError') != -1 + assert appex.errisinstance(ValueError) + assert not appex.errisinstance(RuntimeError) + class A: pass - assert not appex.errisinstance(A) + assert not appex.errisinstance(A) def test_fakedexception(space): @@ -80,7 +81,7 @@ def test_fakedexception(space): raise PicklingError("SomeMessage") space.setitem(space.builtin.w_dict, space.wrap('raise_error'), space.wrap(raise_error)) - + try: space.appexec([], "(): raise_error()") except OperationError, e: @@ -89,21 +90,27 @@ def test_fakedexception(space): py.test.fail("did not raise!") assert "PicklingError" in appex.exconly() -class AppTestWithWrappedInterplevelAttributes: - def setup_class(cls): +class AppTestWithWrappedInterplevelAttributes: + def setup_class(cls): space = cls.space - cls.w_some1 = space.wrap(42) + cls.w_some1 = space.wrap(42) - def setup_method(self, meth): + def setup_method(self, meth): self.w_some2 = self.space.wrap(23) - def test_values_arrive(self): - assert self.some1 == 42 - assert self.some2 == 23 + def test_values_arrive(self): + assert self.some1 == 42 + assert self.some2 == 23 - def test_values_arrive2(self): + def test_values_arrive2(self): assert self.some1 == 42 + def w_compute(self, x): + return x + 2 + + def test_equal(self): + assert self.compute(3) == 5 + def test_expectcollect(testdir): py.test.importorskip("pexpect") conftestpath.copy(testdir.tmpdir) @@ -132,7 +139,7 @@ def test_safe_filename(testdir): pass """) ev, = sorter.getreports("pytest_runtest_logreport") - assert ev.passed + assert ev.passed sfn = ev.item.safe_filename() print sfn assert sfn == 'test_safe_filename_test_safe_filename_ExpectTestOne_paren_test_one_1.py' @@ -155,4 +162,4 @@ def test_app_test_blow(testdir): ev, = sorter.getreports("pytest_runtest_logreport") assert ev.failed assert 'NameError' in ev.longrepr.reprcrash.message - assert 'blow' in ev.longrepr.reprcrash.message + assert 'blow' in ev.longrepr.reprcrash.message diff --git a/pypy/tool/release/make_release.py b/pypy/tool/release/make_release.py index 9b97888b67..58de7ce72f 100755 --- a/pypy/tool/release/make_release.py +++ b/pypy/tool/release/make_release.py @@ -4,7 +4,8 @@ into release packages. Note: you must run apropriate buildbots first and make sure there are no failures. Use force-builds.py from the same directory. -Usage: make_release.py release/<release name> release_version +Usage: make_release.py <branchname> <version> + e.g.: make_release.py release-1.4.1 1.4.1 """ import autopath @@ -20,6 +21,7 @@ import os import shutil BASEURL = 'http://buildbot.pypy.org/nightly/' +PAUSE = False def browse_nightly(branch, baseurl=BASEURL, @@ -32,12 +34,12 @@ def browse_nightly(branch, dom = minidom.parseString(xml) refs = [node.getAttribute('href') for node in dom.getElementsByTagName('a') if 'pypy' in node.getAttribute('href')] - # all refs are of form: pypy-{type}-{revision}-{platform}.tar.bz2 - r = re.compile('pypy-c-([\w\d]+)-(\d+)-([\w\d]+).tar.bz2$') + # all refs are of form: pypy-c-{type}-{revnum}-{hghash}-{platform}.tar.bz2 + r = re.compile('pypy-c-([\w\d]+)-(\d+)-([0-9a-f]+)-([\w\d]+).tar.bz2$') d = {} for ref in refs: - kind, rev, platform = r.match(ref).groups() - rev = int(rev) + kind, revnum, hghash, platform = r.match(ref).groups() + rev = int(revnum) try: lastrev, _ = d[(kind, platform)] except KeyError: @@ -51,8 +53,10 @@ def main(branch, release): tmpdir = udir.join('download') tmpdir.ensure(dir=True) alltars = [] + olddir = os.getcwd() try: os.chdir(str(tmpdir)) + print 'Using tmpdir', str(tmpdir) for (kind, platform), (rev, name) in to_download.iteritems(): if platform == 'win32': print 'Ignoring %s, windows unsupported' % name @@ -64,20 +68,23 @@ def main(branch, release): t = tarfile.open(str(tmpdir.join(name))) dirname = t.getmembers()[0].name t.extractall(path=str(tmpdir)) - os.system('mv %s %s' % (str(tmpdir.join(dirname)), - str(tmpdir.join('pypy-%s' % release)))) if kind == 'jit': kind = '' else: kind = '-' + kind - olddir = os.getcwd() - name = 'pypy-%s-%s%s.tar.bz2' % (release, platform, kind) + topdirname = 'pypy-%s-%s%s' % (release, platform, kind) + os.system('mv %s %s' % (str(tmpdir.join(dirname)), + str(tmpdir.join(topdirname)))) + if PAUSE: + print 'Pausing, press Enter...' + raw_input() + name = '%s.tar.bz2' % topdirname print "Building %s" % name t = tarfile.open(name, 'w:bz2') - t.add('pypy-%s' % release) + t.add(topdirname) alltars.append(name) t.close() - shutil.rmtree(str(tmpdir.join('pypy-' + release))) + shutil.rmtree(str(tmpdir.join(topdirname))) for name in alltars: print "Uploading %s" % name os.system('scp %s codespeak.net:/www/pypy.org/htdocs/download' % name) diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py index 6bb5a794f5..d2a3052517 100755 --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -19,7 +19,7 @@ from pypy.tool.udir import udir if sys.version_info < (2,6): py.test.skip("requires 2.6 so far") -USE_TARFILE_MODULE = sys.platform == 'win32' +USE_ZIPFILE_MODULE = sys.platform == 'win32' def ignore_patterns(*patterns): """Function that can be used as copytree() ignore parameter. @@ -40,14 +40,26 @@ def package(basedir, name='pypy-nightly', rename_pypy_c='pypy', copy_to_dir = None, override_pypy_c = None): basedir = py.path.local(basedir) if sys.platform == 'win32': - basename = 'pypy-c.exe' + # Can't rename a DLL + if override_pypy_c is not None: + rename_pypy_c = py.path.local(override_pypy_c).purebasename + pypy_c_dir = py.path.local(override_pypy_c).dirname + else: + pypy_c_dir = basedir.join('pypy', 'translator', 'goal') + pypy_c = pypy_c_dir.join(rename_pypy_c + '.exe') + libpypy_c = pypy_c_dir.join('lib' + rename_pypy_c + '.dll') + binaries = [(pypy_c, pypy_c.basename), + (libpypy_c, libpypy_c.basename), + (pypy_c_dir.join('libexpat.dll'), 'libexpat.dll')] else: basename = 'pypy-c' - if override_pypy_c is None: - pypy_c = basedir.join('pypy', 'translator', 'goal', basename) - else: - pypy_c = py.path.local(override_pypy_c) + if override_pypy_c is None: + pypy_c = basedir.join('pypy', 'translator', 'goal', basename) + else: + pypy_c = py.path.local(override_pypy_c) + binaries = [(pypy_c, rename_pypy_c)] if not pypy_c.check(): + print pypy_c raise PyPyCNotFound('Please compile pypy first, using translate.py') builddir = udir.ensure("build", dir=True) pypydir = builddir.ensure(name, dir=True) @@ -72,28 +84,47 @@ def package(basedir, name='pypy-nightly', rename_pypy_c='pypy', spdir = pypydir.ensure('site-packages', dir=True) shutil.copy(str(basedir.join('site-packages', 'README')), str(spdir)) # - pypydir.ensure('bin', dir=True) - archive_pypy_c = pypydir.join('bin', rename_pypy_c) - shutil.copy(str(pypy_c), str(archive_pypy_c)) + if sys.platform == 'win32': + bindir = pypydir + else: + bindir = pypydir.join('bin') + bindir.ensure(dir=True) + for source, target in binaries: + archive = bindir.join(target) + shutil.copy(str(source), str(archive)) old_dir = os.getcwd() try: os.chdir(str(builddir)) - os.system("strip -x " + str(archive_pypy_c)) # ignore errors - if USE_TARFILE_MODULE: - import tarfile - tf = tarfile.open(str(builddir.join(name + '.tar.bz2')), 'w:bz2') - tf.add(name) - tf.close() + # + # 'strip' fun: see https://codespeak.net/issue/pypy-dev/issue587 + for source, target in binaries: + if sys.platform == 'win32': + pass + elif sys.platform == 'darwin': + os.system("strip -x " + str(bindir.join(target))) # ignore errors + else: + os.system("strip " + str(bindir.join(target))) # ignore errors + # + if USE_ZIPFILE_MODULE: + import zipfile + archive = str(builddir.join(name + '.zip')) + zf = zipfile.ZipFile(archive, 'w', + compression=zipfile.ZIP_DEFLATED) + for (dirpath, dirnames, filenames) in os.walk(name): + for fnname in filenames: + filename = os.path.join(dirpath, fnname) + zf.write(filename) + zf.close() else: - e = os.system('tar cvjf ' + str(builddir.join(name + '.tar.bz2')) + - " " + name) + archive = str(builddir.join(name + '.tar.bz2')) + e = os.system('tar cvjf ' + archive + " " + name) if e: raise OSError('"tar" returned exit status %r' % e) finally: os.chdir(old_dir) if copy_to_dir is not None: - print "Copying to %s" % copy_to_dir - shutil.copy(str(builddir.join(name + '.tar.bz2')), copy_to_dir) + print "Copying %s to %s" % (archive, copy_to_dir) + shutil.copy(archive, str(copy_to_dir)) return builddir # for tests if __name__ == '__main__': diff --git a/pypy/tool/release/test/nightly.xml b/pypy/tool/release/test/nightly.xml index d57186b49b..71625b5099 100644 --- a/pypy/tool/release/test/nightly.xml +++ b/pypy/tool/release/test/nightly.xml @@ -1,94 +1,143 @@ - -<html> - <head> - <title>Directory listing for /nightly/release/1.3.x/</title> - <style> - .even-dir { background-color: #efe0ef } - .even { background-color: #eee } - .odd-dir {background-color: #f0d0ef } - .odd { background-color: #dedede } - .icon { text-align: center } - .listing { - margin-left: auto; - margin-right: auto; - width: 50%; - padding: 0.1em; - } - - body { border: 0; padding: 0; margin: 0; background-color: #efefef; } - h1 {padding: 0.1em; background-color: #777; color: white; border-bottom: thin white dashed;} - - </style> - </head> - <body> - <h1>Directory listing for /nightly/release/1.3.x/ - </h1> - <table> - <tr pattern="listHeader"> - <th>Filename</th> - <th>Content type</th> - <th>Content encoding</th> - </tr> - <tr class="even"> - <td><a href="pypy-c-jit-75357-linux.tar.bz2">pypy-c-jit-75357-linux.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="odd"> - <td><a href="pypy-c-jit-75357-win32.tar.bz2">pypy-c-jit-75357-win32.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="even"> - <td><a href="pypy-c-jit-75362-linux.tar.bz2">pypy-c-jit-75362-linux.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="odd"> - <td><a href="pypy-c-jit-75362-win32.tar.bz2">pypy-c-jit-75362-win32.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="even"> - <td><a href="pypy-c-nojit-75357-linux.tar.bz2">pypy-c-nojit-75357-linux.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="odd"> - <td><a href="pypy-c-nojit-75357-linux64.tar.bz2">pypy-c-nojit-75357-linux64.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="even"> - <td><a href="pypy-c-nojit-75357-win32.tar.bz2">pypy-c-nojit-75357-win32.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="odd"> - <td><a href="pypy-c-nojit-75362-linux.tar.bz2">pypy-c-nojit-75362-linux.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="even"> - <td><a href="pypy-c-nojit-75362-linux64.tar.bz2">pypy-c-nojit-75362-linux64.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="odd"> - <td><a href="pypy-c-nojit-75362-win32.tar.bz2">pypy-c-nojit-75362-win32.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="even"> - <td><a href="pypy-c-stackless-75357-linux.tar.bz2">pypy-c-stackless-75357-linux.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - <tr class="odd"> - <td><a href="pypy-c-stackless-75362-linux.tar.bz2">pypy-c-stackless-75362-linux.tar.bz2</a></td> - <td>[application/x-tar]</td> - <td>[bzip2]</td> - </tr> - </table> - </body> + <html> +<head> +<title>Directory listing for /nightly/trunk/</title> +<style> +.even { background-color: #eee } +.odd { background-color: #dedede } +.even-passed { background-color: #caffd8 } +.odd-passed { background-color: #a3feba } +.even-failed { background-color: #ffbbbb } +.odd-failed { background-color: #ff9797 } + +.summary_link { + color: black; + text-decoration: none; +} +.summary_link:hover { + color: blue; + text-decoration: underline; +} + +.icon { text-align: center } +.listing { + margin-left: auto; + margin-right: auto; + width: 50%; + padding: 0.1em; + } + +body { border: 0; padding: 0; margin: 0; background-color: #efefef; } +h1 {padding: 0.1em; background-color: #777; color: white; border-bottom: thin white dashed;} +td,th {padding-left: 0.5em; padding-right: 0.5em; } + +</style> +</head> + +<body> +<h1>Directory listing for /nightly/trunk/</h1> + +<table> + <thead> + <tr> + + <th>Filename</th> + <th>Size</th> + <th>Date</th> + <th><i>own</i> tests</th> + <th><i>applevel</i> tests</th> + + </tr> + </thead> + <tbody> +<tr class="odd"> + <td><a href="pypy-c-jit-40170-c407c9dc5382-linux.tar.bz2">pypy-c-jit-40170-c407c9dc5382-linux.tar.bz2</a></td> + <td>8M</td> + <td>2010-12-22</td> + <td class="odd-failed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">16561, 1 F, 2089 s, 4 x</a></td> + + <td class="odd-passed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">3488, 0 F, 1542 s, 1 x</a></td> +</tr> +<tr class="even"> + <td><a href="pypy-c-jit-40170-c407c9dc5382-linux64.tar.bz2">pypy-c-jit-40170-c407c9dc5382-linux64.tar.bz2</a></td> + <td>9M</td> + <td>2010-12-22</td> + <td class="even-failed"><a class="summary_link" href="/summary?category=linux64&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">16216, 3 F, 2417 s, 4 x</a></td> + + <td class="even-passed"><a class="summary_link" href="/summary?category=linux64&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">3483, 0 F, 1547 s, 1 x</a></td> +</tr> +<tr class="odd"> + <td><a href="pypy-c-jit-40170-c407c9dc5382-osx64.tar.bz2">pypy-c-jit-40170-c407c9dc5382-osx64.tar.bz2</a></td> + <td>10M</td> + <td>2010-12-22</td> + <td class="odd">None</td> + + <td class="odd">None</td> +</tr> +<tr class="even"> + <td><a href="pypy-c-nojit-40170-c407c9dc5382-linux.tar.bz2">pypy-c-nojit-40170-c407c9dc5382-linux.tar.bz2</a></td> + <td>7M</td> + <td>2010-12-22</td> + <td class="even-failed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">16561, 1 F, 2089 s, 4 x</a></td> + + <td class="even-passed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">3377, 0 F, 1542 s, 1 x</a></td> +</tr> +<tr class="odd"> + <td><a href="pypy-c-nojit-40170-c407c9dc5382-linux64.tar.bz2">pypy-c-nojit-40170-c407c9dc5382-linux64.tar.bz2</a></td> + <td>8M</td> + <td>2010-12-22</td> + <td class="odd-failed"><a class="summary_link" href="/summary?category=linux64&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">16216, 3 F, 2417 s, 4 x</a></td> + + <td class="odd-passed"><a class="summary_link" href="/summary?category=linux64&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">3372, 0 F, 1547 s, 1 x</a></td> +</tr> +<tr class="even"> + <td><a href="pypy-c-stackless-40170-c407c9dc5382-linux.tar.bz2">pypy-c-stackless-40170-c407c9dc5382-linux.tar.bz2</a></td> + <td>10M</td> + <td>2010-12-22</td> + <td class="even-failed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">16561, 1 F, 2089 s, 4 x</a></td> + + <td class="even-passed"><a class="summary_link" href="/summary?category=linux32-stackless&branch=%3Ctrunk%3E&recentrev=40170:c407c9dc5382">3221, 0 F, 1401 s, 1 x</a></td> +</tr> +<tr class="odd"> + <td><a href="pypy-c-jit-40149-cd083843b67a-linux.tar.bz2">pypy-c-jit-40149-cd083843b67a-linux.tar.bz2</a></td> + <td>10M</td> + <td>2010-12-21</td> + <td class="odd-passed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">16561, 0 F, 2088 s, 4 x</a></td> + + <td class="odd-passed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">3488, 0 F, 1542 s, 1 x</a></td> +</tr> +<tr class="even"> + <td><a href="pypy-c-jit-40149-cd083843b67a-linux64.tar.bz2">pypy-c-jit-40149-cd083843b67a-linux64.tar.bz2</a></td> + <td>11M</td> + <td>2010-12-21</td> + <td class="even-passed"><a class="summary_link" href="/summary?category=linux64&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">16219, 0 F, 2415 s, 4 x</a></td> + + <td class="even-passed"><a class="summary_link" href="/summary?category=linux64&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">3483, 0 F, 1547 s, 1 x</a></td> +</tr> +<tr class="odd"> + <td><a href="pypy-c-nojit-40149-cd083843b67a-linux.tar.bz2">pypy-c-nojit-40149-cd083843b67a-linux.tar.bz2</a></td> + <td>7M</td> + <td>2010-12-21</td> + <td class="odd-passed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">16561, 0 F, 2088 s, 4 x</a></td> + + <td class="odd-passed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">3377, 0 F, 1542 s, 1 x</a></td> +</tr> +<tr class="even"> + <td><a href="pypy-c-nojit-40149-cd083843b67a-linux64.tar.bz2">pypy-c-nojit-40149-cd083843b67a-linux64.tar.bz2</a></td> + <td>9M</td> + <td>2010-12-21</td> + <td class="even-passed"><a class="summary_link" href="/summary?category=linux64&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">16219, 0 F, 2415 s, 4 x</a></td> + + <td class="even-passed"><a class="summary_link" href="/summary?category=linux64&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">3372, 0 F, 1547 s, 1 x</a></td> +</tr> +<tr class="odd"> + <td><a href="pypy-c-stackless-40149-cd083843b67a-linux.tar.bz2">pypy-c-stackless-40149-cd083843b67a-linux.tar.bz2</a></td> + <td>10M</td> + <td>2010-12-21</td> + <td class="odd-passed"><a class="summary_link" href="/summary?category=linux32&branch=%3Ctrunk%3E&recentrev=40149:cd083843b67a">16561, 0 F, 2088 s, 4 x</a></td> +</tr> + + </tbody> +</table> + +</body> </html> diff --git a/pypy/tool/release/test/test_make_release.py b/pypy/tool/release/test/test_make_release.py index 820f2cae47..6f01751db5 100644 --- a/pypy/tool/release/test/test_make_release.py +++ b/pypy/tool/release/test/test_make_release.py @@ -6,6 +6,6 @@ XML = py.path.local(__file__).join('..', 'nightly.xml').read() def test_browse_nightly(): res = browse_nightly('branch', override_xml=XML) - assert res[('jit', 'linux')] == (75362, 'pypy-c-jit-75362-linux.tar.bz2') + assert res[('jit', 'linux')] == (40170, 'pypy-c-jit-40170-c407c9dc5382-linux.tar.bz2') assert len(res) == 6 - assert res[('nojit', 'linux64')] == (75362, u'pypy-c-nojit-75362-linux64.tar.bz2') + assert res[('nojit', 'linux64')] == (40170, 'pypy-c-nojit-40170-c407c9dc5382-linux64.tar.bz2') diff --git a/pypy/tool/release/test/test_package.py b/pypy/tool/release/test/test_package.py index 2aa3c757ae..694b42d5e7 100644 --- a/pypy/tool/release/test/test_package.py +++ b/pypy/tool/release/test/test_package.py @@ -1,38 +1,56 @@ import py from pypy.tool.autopath import pypydir -from pypy.tool.release.package import package +from pypy.tool.release import package from pypy.module.sys.version import CPYTHON_VERSION -import tarfile, os +import tarfile, zipfile, os, sys def test_dir_structure(test='test'): # make sure we have sort of pypy-c - pypy_c = py.path.local(pypydir).join('translator', 'goal', 'pypy-c') + if sys.platform == 'win32': + basename = 'pypy-c.exe' + rename_pypy_c = 'pypy-c' + else: + basename = 'pypy-c' + rename_pypy_c = 'pypy' + pypy_c = py.path.local(pypydir).join('translator', 'goal', basename) if not pypy_c.check(): os.system("echo faked_pypy_c> %s" % (pypy_c,)) fake_pypy_c = True else: fake_pypy_c = False try: - builddir = package(py.path.local(pypydir).dirpath(), test) + builddir = package.package(py.path.local(pypydir).dirpath(), test, + rename_pypy_c) prefix = builddir.join(test) cpyver = '%d.%d.%d' % CPYTHON_VERSION[:3] assert prefix.join('lib-python', cpyver, 'test').check() - assert prefix.join('bin', 'pypy').check() + if sys.platform == 'win32': + assert prefix.join('pypy-c.exe').check() + else: + assert prefix.join('bin', 'pypy').check() assert prefix.join('lib_pypy', 'syslog.py').check() assert not prefix.join('lib_pypy', 'py').check() assert not prefix.join('lib_pypy', 'ctypes_configure').check() assert prefix.join('LICENSE').check() assert prefix.join('README').check() - th = tarfile.open(str(builddir.join('%s.tar.bz2' % test))) - assert th.getmember('%s/lib_pypy/syslog.py' % test) + if package.USE_ZIPFILE_MODULE: + zh = zipfile.ZipFile(str(builddir.join('%s.zip' % test))) + assert zh.open('%s/lib_pypy/syslog.py' % test) + else: + th = tarfile.open(str(builddir.join('%s.tar.bz2' % test))) + assert th.getmember('%s/lib_pypy/syslog.py' % test) # the headers file could be not there, because they are copied into # trunk/include only during translation includedir = py.path.local(pypydir).dirpath().join('include') def check_include(name): if includedir.join(name).check(file=True): - assert th.getmember('%s/include/%s' % (test, name)) + member = '%s/include/%s' % (test, name) + if package.USE_ZIPFILE_MODULE: + assert zh.open(member) + else: + assert th.getmember(member) check_include('Python.h') check_include('modsupport.inl') check_include('pypy_decl.h') @@ -40,11 +58,11 @@ def test_dir_structure(test='test'): if fake_pypy_c: pypy_c.remove() -def test_with_tarfile_module(): +def test_with_zipfile_module(): from pypy.tool.release import package - prev = package.USE_TARFILE_MODULE + prev = package.USE_ZIPFILE_MODULE try: - package.USE_TARFILE_MODULE = True - test_dir_structure(test='testtarfile') + package.USE_ZIPFILE_MODULE = True + test_dir_structure(test='testzipfile') finally: - package.USE_TARFILE_MODULE = prev + package.USE_ZIPFILE_MODULE = prev diff --git a/pypy/tool/release/win32build.py b/pypy/tool/release/win32build.py new file mode 100644 index 0000000000..6416a317b3 --- /dev/null +++ b/pypy/tool/release/win32build.py @@ -0,0 +1,29 @@ +import autopath +import package +import subprocess +import py, sys +import shutil + +pypydir = py.path.local(autopath.pypydir) +builddir = pypydir.join('translator', 'goal') + +VERSION = "1.4.1" + +def make_pypy(tag, options): + pypy = 'pypy%s' % (tag,) + args = [sys.executable, + str(builddir.join('translate.py')), + '--output=' + pypy, + ] + options + print "Execute", args + p = subprocess.Popen(args, cwd=str(builddir)) + p.wait() + zipfile = 'pypy-%s-win32%s' % (VERSION, tag) + package.package(pypydir.dirpath(), zipfile, pypy, pypydir) + +shutil.copy(str(pypydir.join('..', '..', 'expat-2.0.1', 'win32', 'bin', 'release', 'libexpat.dll')), str(builddir)) + +make_pypy('', ['-Ojit']) +make_pypy('-nojit', []) +#make_pypy('-stackless', [--stackless]) +#make_pypy('-sandbox', [--sandbox]) diff --git a/pypy/tool/win32-build.bat b/pypy/tool/win32-build.bat deleted file mode 100644 index d796a2f866..0000000000 --- a/pypy/tool/win32-build.bat +++ /dev/null @@ -1,38 +0,0 @@ -setlocal
-
-set ROOTDIR=%~dp0..\..
-cd %ROOTDIR%
-
-set ZIPEXE=zip
-set PYTHON=c:\python26\python.exe
-set TRANSLATE=pypy/translator/goal/translate.py
-set TRANSLATEOPTS=--batch
-set TARGET=pypy/translator/goal/targetpypystandalone
-set TARGETOPTS=
-
-copy /y ..\expat-2.0.1\win32\bin\release\libexpat.dll .
-
-call :make_pypy pypy-1.2-win32.zip pypy.exe -Ojit
-call :make_pypy pypy-1.2-win32-nojit.zip pypy-nojit.exe
-call :make_pypy pypy-1.2-win32-stackless.zip pypy-stackless.exe --stackless
-REM call :make_pypy pypy-1.2-win32-sandbox.zip pypy-sandbox.exe --sandbox
-
-goto :EOF
-
-REM =========================================
-:make_pypy
-REM make_pypy subroutine
-REM %1 is the zip filename
-REM %2 is pypy.exe filename
-REM %3 and others are the translation options
-
-set ZIPFILE=%1
-set PYPYEXE=%2
-set EXTRAOPTS=%3 %4 %5 %6 %7 %8 %9
-
-%PYTHON% %TRANSLATE% --output=%PYPYEXE% %TRANSLATEOPTS% %EXTRAOPTS% %TARGET% %TARGETOPTS%
-del %ZIPFILE%
-del /s pypy\lib\*.pyc lib-python\*.pyc
-%ZIPEXE% %ZIPFILE% %PYPYEXE% *.dll
-%ZIPEXE% -r %ZIPFILE% pypy\lib lib-python
-%ZIPEXE% -d %ZIPFILE% lib-python\2.5.2\plat-*
diff --git a/pypy/translator/c/gcc/trackgcroot.py b/pypy/translator/c/gcc/trackgcroot.py index 7938d0068b..fcae854d63 100755 --- a/pypy/translator/c/gcc/trackgcroot.py +++ b/pypy/translator/c/gcc/trackgcroot.py @@ -446,6 +446,7 @@ class FunctionGcRootTracker(object): IGNORE_OPS_WITH_PREFIXES = dict.fromkeys([ 'cmp', 'test', 'set', 'sahf', 'lahf', 'cltd', 'cld', 'std', 'rep', 'movs', 'lods', 'stos', 'scas', 'cwtl', 'cwde', 'prefetch', + 'pslld', # floating-point operations cannot produce GC pointers 'f', 'cvt', 'ucomi', 'comi', 'subs', 'subp' , 'adds', 'addp', 'xorp', @@ -455,9 +456,9 @@ class FunctionGcRootTracker(object): 'inc', 'dec', 'not', 'neg', 'or', 'and', 'sbb', 'adc', 'shl', 'shr', 'sal', 'sar', 'rol', 'ror', 'mul', 'imul', 'div', 'idiv', 'bswap', 'bt', 'rdtsc', - 'punpck', 'pshufd', 'psll', + 'punpck', 'pshufd', # zero-extending moves should not produce GC pointers - 'movz', + 'movz', ]) visit_movb = visit_nop diff --git a/pypy/translator/c/src/asm_gcc_x86_64.h b/pypy/translator/c/src/asm_gcc_x86_64.h index 9657e57999..c07e50baae 100644 --- a/pypy/translator/c/src/asm_gcc_x86_64.h +++ b/pypy/translator/c/src/asm_gcc_x86_64.h @@ -2,7 +2,7 @@ */ #define READ_TIMESTAMP(val) do { \ - unsigned int _eax, _edx; \ - asm volatile("rdtsc" : "=a" (_eax), "=d" (_edx)); \ - val = (((unsigned long) _edx) << 32) | _eax; \ + unsigned long _rax, _rdx; \ + asm volatile("rdtsc" : "=rax"(_rax), "=rdx"(_rdx)); \ + val = (_rdx << 32) | _rax; \ } while (0) diff --git a/pypy/translator/goal/app_main.py b/pypy/translator/goal/app_main.py index 15bacf4b47..457c41ac25 100644 --- a/pypy/translator/goal/app_main.py +++ b/pypy/translator/goal/app_main.py @@ -5,11 +5,11 @@ options: -i inspect interactively after running script -O dummy optimization flag for compatibility with C Python - -c CMD program passed in as CMD (terminates option list) + -c cmd program passed in as CMD (terminates option list) -S do not 'import site' on initialization -u unbuffered binary stdout and stderr -h, --help show this help message and exit - -m library module to be run as a script (terminates option list) + -m mod library module to be run as a script (terminates option list) -W arg warning control (arg is action:message:category:module:lineno) -E ignore environment variables (such as PYTHONPATH) --version print the PyPy version @@ -128,7 +128,8 @@ def print_info(*args): raise SystemExit def print_help(*args): - print 'usage: %s [options]' % (sys.executable,) + print 'usage: %s [options] [-c cmd|-m mod|file.py|-] [arg...]' % ( + sys.executable,) print __doc__.rstrip() if 'pypyjit' in sys.builtin_module_names: _print_jit_help() diff --git a/pypy/translator/goal/sharedpypy.py b/pypy/translator/goal/sharedpypy.py index 6b4bcf30b2..3958fac407 100644 --- a/pypy/translator/goal/sharedpypy.py +++ b/pypy/translator/goal/sharedpypy.py @@ -38,7 +38,7 @@ def main(argv): pycode.exec_code(space, w_dict, w_dict) except OperationError, e: print "OperationError:" - print " operror-type: " + e.w_type.getname(space, '?') + print " operror-type: " + e.w_type.getname(space) print " operror-value: " + space.str_w(space.str(e.get_w_value(space))) return 1 return 0 diff --git a/pypy/translator/goal/targetpreimportedpypy.py b/pypy/translator/goal/targetpreimportedpypy.py index 17a8af8d6a..d4ad9aa7af 100644 --- a/pypy/translator/goal/targetpreimportedpypy.py +++ b/pypy/translator/goal/targetpreimportedpypy.py @@ -72,7 +72,7 @@ def create_entry_point(space, w_dict): ## con.interact() except OperationError, e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space, '?')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.str_w(space.str(e.get_w_value(space)))) return 1 finally: @@ -82,7 +82,7 @@ def create_entry_point(space, w_dict): space.timer.stop("space.finish") except OperationError, e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space, '?')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.str_w(space.str(e.get_w_value(space)))) return 1 space.timer.stop("Entrypoint") diff --git a/pypy/translator/goal/targetpypystandalone.py b/pypy/translator/goal/targetpypystandalone.py index c402b81a96..42352b7de6 100644 --- a/pypy/translator/goal/targetpypystandalone.py +++ b/pypy/translator/goal/targetpypystandalone.py @@ -65,7 +65,7 @@ def create_entry_point(space, w_dict): ## con.interact() except OperationError, e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space, '?')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.str_w(space.str(e.get_w_value(space)))) return 1 finally: @@ -75,7 +75,7 @@ def create_entry_point(space, w_dict): space.timer.stop("space.finish") except OperationError, e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space, '?')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.str_w(space.str(e.get_w_value(space)))) return 1 space.timer.stop("Entrypoint") diff --git a/pypy/translator/tool/make_dot.py b/pypy/translator/tool/make_dot.py index cb3f23b8ba..13501f9abe 100644 --- a/pypy/translator/tool/make_dot.py +++ b/pypy/translator/tool/make_dot.py @@ -49,12 +49,17 @@ class DotGen: color="black", dir="forward", weight="5", + ports=None, ): d = locals() attrs = [('%s="%s"' % (x, d[x].replace('"', '\\"').replace('\n', '\\n'))) for x in ['label', 'style', 'color', 'dir', 'weight']] self.emit('edge [%s];' % ", ".join(attrs)) - self.emit('%s -> %s' % (safename(name1), safename(name2))) + if ports: + self.emit('%s:%s -> %s:%s' % (safename(name1), ports[0], + safename(name2), ports[1])) + else: + self.emit('%s -> %s' % (safename(name1), safename(name2))) def emit_node(self, name, shape="diamond", |