From a5fc2965a8c554c33371521338169788f1573170 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 31 Jan 2017 14:27:48 +0100 Subject: [PATCH 01/52] make shell work after translation --- rsqueakvm/main.py | 3 --- rsqueakvm/util/shell.py | 51 +++++++++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/rsqueakvm/main.py b/rsqueakvm/main.py index 8d2c9817..1a86f8c3 100644 --- a/rsqueakvm/main.py +++ b/rsqueakvm/main.py @@ -446,9 +446,6 @@ def entry_point(argv): create_process(interp, s_frame) context = active_context(space) elif cfg.shell: - if objectmodel.we_are_translated(): - print "Not possible after translation" - return -1 from rsqueakvm.util.shell import Shell cfg = None Shell(interp, space).run() diff --git a/rsqueakvm/util/shell.py b/rsqueakvm/util/shell.py index 86b1bb4a..7e4cbc65 100644 --- a/rsqueakvm/util/shell.py +++ b/rsqueakvm/util/shell.py @@ -1,6 +1,7 @@ import readline import re import sys +import os import inspect from rpython.rlib import objectmodel, unroll from rsqueakvm.error import Exit @@ -65,11 +66,15 @@ def completer(text, state, completions=None): def untranslated_cmd(func): - if objectmodel.we_are_translated(): - def m(s, c): return - return m - else: - return cmd(func) + msg = "The !%s command is not available after translation." % func.__name__ + cmdfunc = cmd(func) + def m(s, c): + if objectmodel.we_are_translated(): + print msg + return + else: + return cmdfunc(s, c) + return m class Shell(object): @@ -101,7 +106,7 @@ def reset_readline(self): @cmd def q(self, code): "!q for quitting" - exit(0) + os._exit(0) @untranslated_cmd def pdb(self, code): @@ -189,7 +194,16 @@ def reload(self, code): def raw_input(self, delim): self.set_readline() try: - return raw_input(delim).strip() + if not objectmodel.we_are_translated(): + return raw_input(delim).strip() + + os.write(1, delim) + line = [] + c = os.read(0, 1) + while c != "\n": + line.append(c) + c = os.read(0, 1) + return "".join(line).strip() finally: self.reset_readline() @@ -203,7 +217,8 @@ def method(self, code): srcline = "" while srcline != "!!": srcline = self.raw_input("%s| " % parts[1]) - if srcline: # don't record method source as history + if srcline and not objectmodel.we_are_translated(): + # don't record method source as history readline.remove_history_item( readline.get_current_history_length() - 1 ) @@ -220,19 +235,24 @@ def run(self): print "You're in a Smalltalk REPL. Type `!exit' to quit, !help for help." while True: code = self.raw_input("$ ") + print code if code.startswith("!"): method = code[1:].split(" ")[0] - for n in unroll.unrolling_iterable(COMMANDS): + for n in UNROLLING_COMMANDS: if n == method: getattr(self, n)(code) else: - import traceback + if not objectmodel.we_are_translated(): + import traceback w_result = None try: w_result = self._execute_code(code) except: - print traceback.format_exc() - import pdb; pdb.set_trace() + if not objectmodel.we_are_translated(): + print traceback.format_exc() + import pdb; pdb.set_trace() + else: + print "Error" if w_result: self.last_result = w_result print w_result.as_repr_string().replace('\r', '\n') @@ -241,14 +261,15 @@ def _execute_code(self, code): from rsqueakvm.main import compile_code, execute_context w_selector = self.methods.get(code, None) if not w_selector: - sys.stdout.write("Compiling code... ") - sys.stdout.flush() + os.write(1, "Compiling code... ") w_selector = self.interp.perform( self.space.wrap_string(compile_code(self.interp, self.w_rcvr, "^ %s" % code)), "asSymbol") self.methods[code] = w_selector print "...done." - sys.stdout.flush() s_frame = self.interp.create_toplevel_context( self.w_rcvr, w_selector=w_selector, w_arguments=[]) return execute_context(self.interp, s_frame) + + +UNROLLING_COMMANDS = unroll.unrolling_iterable(COMMANDS) From c009380d51ce32a259d7479f23a35c02f2439ed4 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 31 Jan 2017 16:47:48 +0100 Subject: [PATCH 02/52] - load files in the shell, allow -r to pass shell line (so we can launch from a script file) - add codeclimate --- .codeclimate.yml | 27 +++++++++++++++++++++++++++ rsqueakvm/main.py | 13 +++++++------ rsqueakvm/util/shell.py | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 .codeclimate.yml diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 00000000..00116e7e --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,27 @@ +--- +engines: + duplication: + enabled: true + config: + languages: + - python + fixme: + enabled: true + radon: + enabled: true + pep8: + enabled: true +ratings: + paths: + - "**.py" +exclude_paths: + - "rsqueakvm/util/**/*" + - "rsqueakvm/util/*" + - "rsqueakvm/tools/**/*" + - "rsqueakvm/tools/*" + - "rsqueakvm/test/**/*" + - "rsqueakvm/test/*" + - "repository/**/*" + - "repository/*" + - "scripts/**/*" + - "scripts/*" diff --git a/rsqueakvm/main.py b/rsqueakvm/main.py index 1a86f8c3..ce553a15 100644 --- a/rsqueakvm/main.py +++ b/rsqueakvm/main.py @@ -431,7 +431,13 @@ def entry_point(argv): print_error("") # Line break after image-loading characters # Create context to be executed - if cfg.code or cfg.selector: + if cfg.shell: + from rsqueakvm.util.shell import Shell + code = cfg.code + cfg = None + Shell(interp, space, code=code).run() + return 0 + elif cfg.code or cfg.selector: if not cfg.have_number: w_receiver = space.w_nil else: @@ -445,11 +451,6 @@ def entry_point(argv): else: create_process(interp, s_frame) context = active_context(space) - elif cfg.shell: - from rsqueakvm.util.shell import Shell - cfg = None - Shell(interp, space).run() - return 0 else: context = active_context(space) diff --git a/rsqueakvm/util/shell.py b/rsqueakvm/util/shell.py index 7e4cbc65..88a864cb 100644 --- a/rsqueakvm/util/shell.py +++ b/rsqueakvm/util/shell.py @@ -78,12 +78,16 @@ def m(s, c): class Shell(object): - def __init__(self, interp, space): + def __init__(self, interp, space, code=None): self.set_interp(interp) self.space = space self.methods = {} self.w_rcvr = self.space.w_nil self.last_result = None + if not code: + self.current_code = [] + else: + self.current_code = [code] space.headless.activate() def set_interp(self, interp): @@ -116,6 +120,10 @@ def pdb(self, code): @cmd def help(self, code): "!help to print this help" + print "Empty lines and lines that start and end with '\"' are skipped." + print "Lines that do not start with ! are run as Smalltalk." + print + print "In addition, the following commands are available:" for h in HELP: print h @@ -130,6 +138,26 @@ def trace(self, code): print "Error in command syntax" return + @cmd + def load(self, code): + "!load Filename to read and execute a file" + from rpython.rlib.streamio import open_file_as_stream + code = code.split(" ", 1) + if len(code) != 2: + print "Error in command syntax" + return + path = code[1] + try: + f = open_file_as_stream(path, buffering=0) + except OSError as e: + os.write(2, "%s -- %s (LoadError)\n" % (os.strerror(e.errno), path)) + return + try: + source = f.readall() + finally: + f.close() + self.current_code = [line.strip() for line in source.split("\n")] + @untranslated_cmd def reload(self, code): "!reload rsqueakvm.abc.xyz... to reload some VM code" @@ -192,6 +220,8 @@ def reload(self, code): print "Reloaded %s" % code def raw_input(self, delim): + if len(self.current_code) > 0: + return self.current_code.pop(0) self.set_readline() try: if not objectmodel.we_are_translated(): @@ -235,12 +265,15 @@ def run(self): print "You're in a Smalltalk REPL. Type `!exit' to quit, !help for help." while True: code = self.raw_input("$ ") - print code if code.startswith("!"): method = code[1:].split(" ")[0] for n in UNROLLING_COMMANDS: if n == method: getattr(self, n)(code) + elif len(code) == 0: + pass + elif code.startswith('"') and code.endswith('"'): + pass else: if not objectmodel.we_are_translated(): import traceback From 69edf19f20e98c06ace6f788780550c9ab32ca12 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 1 Feb 2017 13:59:12 +0100 Subject: [PATCH 03/52] Revert "Revert "split PointersObjects into Fixed and Non-Fixed, implement map transitions for Fixed pointer objects with some inline fields, assume all objects we pass by have a strategy and a class, simplify code and fix tests to honor that"" This reverts commit 78bc8f5a266814c893160ca49fc40fefc6135f33. --- rsqueakvm/constants.py | 56 +- rsqueakvm/display.py | 2 +- rsqueakvm/interpreter_bytecodes.py | 2 +- rsqueakvm/model/base.py | 26 +- rsqueakvm/model/compiled_methods.py | 4 +- rsqueakvm/model/display.py | 6 +- rsqueakvm/model/pointers.py | 162 +-- rsqueakvm/model/variable.py | 5 +- rsqueakvm/objspace.py | 17 +- rsqueakvm/plugins/misc_primitive_plugin.py | 11 +- rsqueakvm/primitives/__init__.py | 2 +- rsqueakvm/primitives/input_output.py | 4 +- rsqueakvm/primitives/misc.py | 5 +- rsqueakvm/primitives/storage.py | 26 +- rsqueakvm/squeakimage.py | 12 +- rsqueakvm/storage.py | 389 +++++- rsqueakvm/storage_classes.py | 20 +- rsqueakvm/storage_contexts.py | 10 +- rsqueakvm/test/conftest.py | 52 +- rsqueakvm/test/jittest/test_basic.py | 52 +- rsqueakvm/test/jittest/test_modern.py | 1220 ++++++++++--------- rsqueakvm/test/test_miniimage_compiling.py | 6 +- rsqueakvm/test/test_model.py | 20 +- rsqueakvm/test/test_primitives.py | 13 +- rsqueakvm/test/test_shadow.py | 4 +- rsqueakvm/test/test_strategies.py | 94 +- rsqueakvm/test/test_zin_squeak_4_5_image.py | 15 +- rsqueakvm/test/util.py | 40 +- rsqueakvm/util/version.py | 10 +- 29 files changed, 1412 insertions(+), 873 deletions(-) diff --git a/rsqueakvm/constants.py b/rsqueakvm/constants.py index c204eceb..35b80445 100644 --- a/rsqueakvm/constants.py +++ b/rsqueakvm/constants.py @@ -126,42 +126,42 @@ SO_JIT_HOOK_RCVR = 59 # really selectorTrap constant_objects_in_special_object_table = { - "nil": (SO_NIL, "POINTERS"), - "true": (SO_TRUE, "POINTERS"), - "false": (SO_FALSE, "POINTERS"), + "nil": (SO_NIL, "FIXED"), + "true": (SO_TRUE, "FIXED"), + "false": (SO_FALSE, "FIXED"), "charactertable": (SO_CHARACTER_TABLE_ARRAY, "POINTERS"), - "schedulerassociationpointer": (SO_SCHEDULERASSOCIATIONPOINTER, "POINTERS"), + "schedulerassociationpointer": (SO_SCHEDULERASSOCIATIONPOINTER, "FIXED"), "special_selectors": (SO_SPECIAL_SELECTORS_ARRAY, "POINTERS"), - "smalltalkdict": (SO_SMALLTALK, "POINTERS"), + "smalltalkdict": (SO_SMALLTALK, "FIXED"), "doesNotUnderstand": (SO_DOES_NOT_UNDERSTAND, "BYTES"), "mustBeBoolean": (SO_MUST_BE_BOOLEAN, "BYTES"), "runWithIn": (SO_RUN_WITH_IN, "BYTES"), "cannotReturn": (SO_CANNOT_RETURN, "BYTES"), # classes - "Bitmap": (SO_BITMAP_CLASS, "POINTERS"), - "SmallInteger": (SO_SMALLINTEGER_CLASS, "POINTERS"), - "String": (SO_STRING_CLASS, "POINTERS"), - "Array": (SO_ARRAY_CLASS, "POINTERS"), - "Float": (SO_FLOAT_CLASS, "POINTERS"), - "MethodContext": (SO_METHODCONTEXT_CLASS, "POINTERS"), - "BlockContext": (SO_BLOCKCONTEXT_CLASS, "POINTERS"), - "BlockClosure": (SO_BLOCKCLOSURE_CLASS, "POINTERS"), - "Point": (SO_POINT_CLASS, "POINTERS"), - "LargePositiveInteger": (SO_LARGEPOSITIVEINTEGER_CLASS, "POINTERS"), - "Message": (SO_MESSAGE_CLASS, "POINTERS"), - "CompiledMethod": (SO_COMPILEDMETHOD_CLASS, "POINTERS"), - "Semaphore": (SO_SEMAPHORE_CLASS, "POINTERS"), - "Character": (SO_CHARACTER_CLASS, "POINTERS"), - "ByteArray": (SO_BYTEARRAY_CLASS, "POINTERS"), - "Process": (SO_PROCESS_CLASS, "POINTERS"), -# "PseudoContext" : (SO_PSEUDOCONTEXT_CLASS, "POINTERS"), -# "TranslatedMethod" : (SO_TRANSLATEDMETHOD_CLASS, "POINTERS"), - "LargeNegativeInteger" : (SO_LARGENEGATIVEINTEGER_CLASS, "POINTERS"), + "Bitmap": (SO_BITMAP_CLASS, "FIXED"), + "SmallInteger": (SO_SMALLINTEGER_CLASS, "FIXED"), + "String": (SO_STRING_CLASS, "FIXED"), + "Array": (SO_ARRAY_CLASS, "FIXED"), + "Float": (SO_FLOAT_CLASS, "FIXED"), + "MethodContext": (SO_METHODCONTEXT_CLASS, "FIXED"), + "BlockContext": (SO_BLOCKCONTEXT_CLASS, "FIXED"), + "BlockClosure": (SO_BLOCKCLOSURE_CLASS, "FIXED"), + "Point": (SO_POINT_CLASS, "FIXED"), + "LargePositiveInteger": (SO_LARGEPOSITIVEINTEGER_CLASS, "FIXED"), + "Message": (SO_MESSAGE_CLASS, "FIXED"), + "CompiledMethod": (SO_COMPILEDMETHOD_CLASS, "FIXED"), + "Semaphore": (SO_SEMAPHORE_CLASS, "FIXED"), + "Character": (SO_CHARACTER_CLASS, "FIXED"), + "ByteArray": (SO_BYTEARRAY_CLASS, "FIXED"), + "Process": (SO_PROCESS_CLASS, "FIXED"), +# "PseudoContext" : (SO_PSEUDOCONTEXT_CLASS, "FIXED"), +# "TranslatedMethod" : (SO_TRANSLATEDMETHOD_CLASS, "FIXED"), + "LargeNegativeInteger" : (SO_LARGENEGATIVEINTEGER_CLASS, "FIXED"), # ours, not in the table, but we'd like it to - "ClassBinding": (SPECIAL_OBJECTS_SIZE + 30, "POINTERS"), - "Metaclass": (SPECIAL_OBJECTS_SIZE + 31, "POINTERS"), - "Processor": (SPECIAL_OBJECTS_SIZE + 32, "POINTERS"), - "ByteSymbol": (SPECIAL_OBJECTS_SIZE + 33, "POINTERS") + "ClassBinding": (SPECIAL_OBJECTS_SIZE + 30, "FIXED"), + "Metaclass": (SPECIAL_OBJECTS_SIZE + 31, "FIXED"), + "Processor": (SPECIAL_OBJECTS_SIZE + 32, "FIXED"), + "ByteSymbol": (SPECIAL_OBJECTS_SIZE + 33, "FIXED") } variables_in_special_object_table = { diff --git a/rsqueakvm/display.py b/rsqueakvm/display.py index 2f75922e..f3126d04 100644 --- a/rsqueakvm/display.py +++ b/rsqueakvm/display.py @@ -216,7 +216,7 @@ def set_title(self, title): RSDL.SetWindowTitle(self.window, title) def get_pixelbuffer(self): - return jit.promote(rffi.cast(RSDL.Uint32P, self.get_plain_pixelbuffer())) + return rffi.cast(RSDL.Uint32P, self.get_plain_pixelbuffer()) def get_plain_pixelbuffer(self): return self.screen_surface.c_pixels diff --git a/rsqueakvm/interpreter_bytecodes.py b/rsqueakvm/interpreter_bytecodes.py index c8a46346..5cda459f 100644 --- a/rsqueakvm/interpreter_bytecodes.py +++ b/rsqueakvm/interpreter_bytecodes.py @@ -365,7 +365,7 @@ def _sendSelfSelectorSpecial(self, selector, numargs, interp): @objectmodel.specialize.arg(3) def _sendSpecialSelector(self, interp, receiver, special_selector, w_args=[]): - space = jit.promote(self.space) + space = self.space w_special_selector = getattr(space, "w_" + special_selector) s_class = receiver.class_shadow(space) diff --git a/rsqueakvm/model/base.py b/rsqueakvm/model/base.py index 1c047db9..24981b35 100644 --- a/rsqueakvm/model/base.py +++ b/rsqueakvm/model/base.py @@ -103,8 +103,8 @@ def invariant(self): def class_shadow(self, space): """Return internal representation of Squeak class.""" - w_class = jit.promote(self.getclass(space)) - return w_class.as_class_get_shadow(space) + w_class = self.getclass(space) + return jit.promote(w_class.as_class_get_shadow(space)) def is_same_object(self, other): """Compare object identity. This should be used instead of directly @@ -137,20 +137,9 @@ def post_become_one_way(self, w_to): def clone(self, space): raise NotImplementedError - def has_class(self): - """All Smalltalk objects should have classes. Unfortuantely for - bootstrapping the metaclass-cycle and during testing, that is not - true for some W_PointersObjects""" - return True - def classname(self, space): """Get the name of the class of the receiver""" - name = None - if self.has_class(): - name = self.class_shadow(space).getname() - if not name: - name = "?" - return name + return self.class_shadow(space).getname() def is_positive(self, space): raise error.UnwrappingError("Got unexpected class in is_positive") @@ -273,7 +262,6 @@ class W_AbstractObjectWithClassReference(W_AbstractObjectWithIdentityHash): """Objects with arbitrary class (ie not CompiledMethod, SmallInteger or Float).""" _attrs_ = ['w_class'] - _immutable_fields_ = ['w_class?'] repr_classname = "W_AbstractObjectWithClassReference" w_class = None @@ -302,12 +290,8 @@ def getclass(self, space): return jit.promote(self.w_class) def guess_classname(self): - if self.getclass(None).has_space(): - class_shadow = self.class_shadow(self.getclass(None).space()) - return class_shadow.name - else: - # We cannot access the class during the initialization sequence. - return "?? (class not initialized)" + class_shadow = self.class_shadow(self.getclass(None).space()) + return class_shadow.name def change_class(self, space, w_class): self.w_class = w_class diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index c7214c99..0a651e08 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -288,7 +288,7 @@ def update_compiledin_class_from_literals(self): literals = self.literals if literals and len(literals) > 0: w_literal = literals[-1] - if isinstance(w_literal, W_PointersObject) and w_literal.has_space(): + if isinstance(w_literal, W_PointersObject): space = w_literal.space() # Not pretty to steal the space from another object. compiledin_class = None if w_literal.is_class(space): @@ -380,7 +380,7 @@ def as_string(self, markBytecode=0): def guess_containing_classname(self): w_class = self.compiled_in() - if w_class and w_class.has_space(): + if w_class: # Not pretty to steal the space from another object. return w_class.as_class_get_shadow(w_class.space()).getname() return "? (no compiledin-info)" diff --git a/rsqueakvm/model/display.py b/rsqueakvm/model/display.py index ab9b589e..7f5b1ae2 100644 --- a/rsqueakvm/model/display.py +++ b/rsqueakvm/model/display.py @@ -54,12 +54,10 @@ def guess_classname(self): # === Object access def at0(self, space, index0): - self = jit.promote(self) val = self.getword(index0) return space.wrap_int(r_uint(val)) def atput0(self, space, index0, w_value): - self = jit.promote(self) word = space.unwrap_uint(w_value) self.setword(index0, word) @@ -90,7 +88,7 @@ def size(self): # === Graphics def display(self): - return jit.promote(self._display) + return self._display def is_headless(self): return self._display.is_headless() @@ -224,7 +222,7 @@ class W_MappingDisplayBitmap(W_DisplayBitmap): repr_classname = "W_MappingDisplayBitmap" _attrs_ = ['words_per_line', 'bits_in_last_word', 'pitch'] - _immutable_fields_ = ['words_per_line?', 'bits_in_last_word?', 'pitch?'] + _immutable_fields_ = ['bits_in_last_word?', 'pitch?'] def __init__(self, space, size, depth): assert depth in [1, 2, 4] diff --git a/rsqueakvm/model/pointers.py b/rsqueakvm/model/pointers.py index 055be276..bfddf120 100644 --- a/rsqueakvm/model/pointers.py +++ b/rsqueakvm/model/pointers.py @@ -3,15 +3,13 @@ from rsqueakvm.model.numeric import W_SmallInteger from rpython.rlib import objectmodel, jit +from rpython.rlib.rarithmetic import intmask from rpython.rlib.rstrategies import rstrategies as rstrat class W_PointersObject(W_AbstractObjectWithIdentityHash): """Common object.""" _attrs_ = ['strategy', '_storage'] - # TODO -- is it viable to have these as pseudo-immutable? - # Measurably increases performance, since they do change rarely. - _immutable_attrs_ = ['strategy?', '_storage?'] strategy = None repr_classname = "W_PointersObject" rstrat.make_accessors(strategy='strategy', storage='_storage') @@ -31,7 +29,7 @@ def fillin(self, space, g_self): for g_obj in g_self.pointers: g_obj.fillin(space) pointers = g_self.get_pointers() - storage_type = space.strategy_factory.strategy_type_for(pointers, weak=False) # do not fill in weak lists, yet + storage_type = space.strategy_factory.strategy_type_for(self, pointers, weak=False) # do not fill in weak lists, yet space.strategy_factory.set_initial_strategy(self, storage_type, g_self.get_class(), len(pointers), pointers) @@ -39,86 +37,61 @@ def fillin(self, space, g_self): def fillin_weak(self, space, g_self): assert g_self.isweak() # when we get here, this is true pointers = self.fetch_all(space) - storage_type = space.strategy_factory.strategy_type_for(pointers, weak=True) + storage_type = space.strategy_factory.strategy_type_for(self, pointers, weak=True) space.strategy_factory.switch_strategy(self, storage_type) def is_weak(self): from rsqueakvm.storage import WeakListStrategy - return isinstance(self._get_strategy(), WeakListStrategy) - - def has_class(self): - return self.getclass(None) is not None + return isinstance(self.strategy, WeakListStrategy) def safe_getclass(self, space): return self.strategy.getclass() def getclass(self, space): - if self._get_strategy() is None: - return None - else: - return self._get_strategy().getclass() + return jit.promote(self.strategy.promoted().getclass()) def is_class(self, space): from rsqueakvm.storage_classes import ClassShadow - if isinstance(self._get_strategy(), ClassShadow): + if isinstance(self.strategy, ClassShadow): + return True + w_Metaclass = space.w_Metaclass + w_class = self.getclass(space) + if w_Metaclass.is_same_object(w_class): return True - elif self.has_class(): - w_Metaclass = space.w_Metaclass - w_class = self.getclass(space) - if w_Metaclass.is_same_object(w_class): - return True - elif w_class.has_class(): - return w_Metaclass.is_same_object(w_class.getclass(space)) - return False + else: + return w_Metaclass.is_same_object(w_class.getclass(space)) def change_class(self, space, w_class): - old_strategy = self._get_strategy() + old_strategy = self.strategy new_strategy = old_strategy.instantiate(self, w_class) self._set_strategy(new_strategy) old_strategy._convert_storage_to(self, new_strategy) new_strategy.strategy_switched(self) def guess_classname(self): - if self.has_class(): - if self.getclass(None).has_space(): - class_shadow = self.class_shadow(self.getclass(None).space()) - return class_shadow.name - else: - # We cannot access the class during the initialization sequence. - return "?? (class not initialized)" - else: - return "? (no class)" + class_shadow = self.class_shadow(self.getclass(None).space()) + return class_shadow.name def invariant(self): from rsqueakvm import storage_classes return (W_AbstractObjectWithIdentityHash.invariant(self) and isinstance(self.getclass(None).strategy, storage_classes.ClassShadow)) - def assert_strategy(self): - # Failing the following assert most likely indicates a bug. The strategy can only be absent during - # the bootstrapping sequence. It will be initialized in the fillin() method. Before that, it should - # not be switched to a specialized strategy, and the space is also not yet available here! - # Otherwise, the specialized strategy will attempt to read information from an uninitialized object. - strategy = self.strategy - assert strategy, "The strategy has not been initialized yet!" - return strategy - def space(self): - return self.assert_strategy().space + return self.strategy.space def __str__(self): - if self.has_strategy() and self.strategy.provides_getname: - return self._get_strategy().getname() + if self.strategy.provides_getname: + return self.strategy.getname() else: return W_AbstractObjectWithIdentityHash.__str__(self) def repr_content(self): strategy_info = "no strategy" name = "" - if self.has_strategy(): - strategy_info = self.strategy.__repr__() - if self.strategy.provides_getname: - name = " [%s]" % self._get_strategy().getname() + strategy_info = self.strategy.__repr__() + if self.strategy.provides_getname: + name = " [%s]" % self.strategy.getname() return '(%s) len=%d%s' % (strategy_info, self.size(), name) def unwrap_char(self, space): @@ -170,30 +143,20 @@ def atput0(self, space, index0, w_value): self.store(space, index0 + self.instsize(), w_value) def fetch(self, space, n0): - return self._get_strategy().fetch(self, n0) + return self.strategy.promoted().fetch(self, n0) def store(self, space, n0, w_value): - return self._get_strategy().store(self, n0, w_value) + return self.strategy.promoted().store(self, n0, w_value) def size(self): - if not self.has_strategy(): - # TODO - this happens only for objects bootstrapped in ObjSpace. - # Think of a way to avoid this check. Usually, self.strategy is never None. - return 0 - return self._get_strategy().size(self) + return self.strategy.size(self) def instsize(self): return self.class_shadow(self.space()).instsize() - def store_strategy(self, strategy): - self.strategy = strategy - - def _get_strategy(self): - return self.strategy.promote_if_neccessary() if self.strategy is not None else None - @objectmodel.specialize.arg(2) def as_special_get_shadow(self, space, TheClass): - shadow = self._get_strategy() + shadow = self.strategy if not isinstance(shadow, TheClass): shadow = space.strategy_factory.switch_strategy(self, TheClass) assert isinstance(shadow, TheClass) @@ -201,7 +164,7 @@ def as_special_get_shadow(self, space, TheClass): def as_class_get_shadow(self, space): from rsqueakvm.storage_classes import ClassShadow - return jit.promote(self.as_special_get_shadow(space, ClassShadow)) + return self.as_special_get_shadow(space, ClassShadow) def as_context_get_shadow(self, space): from rsqueakvm.storage_contexts import ContextPartShadow @@ -219,27 +182,22 @@ def as_observed_get_shadow(self, space): from rsqueakvm.storage import ObserveeShadow return self.as_special_get_shadow(space, ObserveeShadow) - def has_strategy(self): - return self._get_strategy() is not None - - def has_space(self): - # The space is accessed through the strategy. - return self.has_strategy() + def can_become(self, w_other): + return type(w_other) is type(self) def _become(self, w_other): - if not isinstance(w_other, W_PointersObject): - raise error.PrimitiveFailedError + assert isinstance(w_other, W_PointersObject) # Make sure our class shadow is initialized, we will need it - if self.getclass(None) and self.getclass(None).has_space(): + if self.getclass(None): self.class_shadow(self.getclass(None).space()) - if w_other.getclass(None) and w_other.getclass(None).has_space(): + if w_other.getclass(None): w_other.class_shadow(w_other.getclass(None).space()) # Only one strategy will handle the become (or none of them). # The receivers strategy gets the first shot. # If it doesn't want to, let the w_other's strategy handle it. - if self.has_strategy() and self._get_strategy().handles_become(): + if self.strategy.handles_become(): self.strategy.become(w_other) - elif w_other.has_strategy() and w_other._get_strategy().handles_become(): + elif w_other.strategy.handles_become(): w_other.strategy.become(self) self.strategy, w_other.strategy = w_other.strategy, self.strategy self._storage, w_other._storage = w_other._storage, self._storage @@ -260,7 +218,57 @@ def pointers_become_one_way(self, space, from_w, to_w): def clone(self, space): my_pointers = self.fetch_all(space) - w_result = W_PointersObject(space, self.getclass(space), - len(my_pointers)) + w_result = self.__class__(space, self.getclass(space), + len(my_pointers)) w_result.store_all(space, my_pointers) return w_result + + +# when changing this constant, also change the read and __write__ methods in +# IntMapStorageNode in storage.py (or generalize those methods to compile with +# getattrs) +_NUMBER_OF_INT_FIELDS = 3 +_NUMBER_OF_INLINE_FIELDS = 2 +class W_FixedPointersObject(W_PointersObject): + """My instances represent only those pointers objects that have a fixed number + of fields and no variable sized parts""" + _attrs_ = ['_field1', '_field2', '_intField1', '_intField2', '_intField3', '_intFields', '_size'] + _immutable_attrs_ = ['_intFields?', '_size?'] + + def __init__(self, space, w_class, size, weak=False): + self._init_inline_fields() + self._size = size + W_PointersObject.__init__(self, space, w_class, size, weak=False) + + def size(self): + return self._size + + def instsize(self): + return self._size + + def fillin(self, space, g_self): + self._init_inline_fields() + self._size = len(g_self.pointers) + W_PointersObject.fillin(self, space, g_self) + + def _init_inline_fields(self): + self._intField1 = 0 + self._intField2 = 0 + self._intField3 = 0 + self._intFields = None + self._field1 = None + self._field2 = None + + def _swap_inline_fields(self, w_other): + self._intField1, w_other._intField1 = w_other._intField1, self._intField1 + self._intField2, w_other._intField2 = w_other._intField2, self._intField2 + self._intField3, w_other._intField3 = w_other._intField3, self._intField3 + self._intFields, w_other._intFields = w_other._intFields, self._intFields + self._field1, w_other._field1 = w_other._field1, self._field1 + self._field2, w_other._field2 = w_other._field2, self._field2 + + def _become(self, w_other): + assert isinstance(w_other, W_FixedPointersObject) + W_PointersObject._become(self, w_other) + self._swap_inline_fields(w_other) + self._size, w_other._size = w_other._size, self._size diff --git a/rsqueakvm/model/variable.py b/rsqueakvm/model/variable.py index 19772b78..5ee8374f 100644 --- a/rsqueakvm/model/variable.py +++ b/rsqueakvm/model/variable.py @@ -69,9 +69,8 @@ def size(self): return len(self.bytes) def str_content(self): - if self.getclass(None).has_space(): - if self.getclass(None).space().omit_printing_raw_bytes.is_set(): - return "" + if self.getclass(None).space().omit_printing_raw_bytes.is_set(): + return "" return "'%s'" % ''.join([\ char if ord(char) < 128 else (r'\x%s' % hex(ord(char))[2:]) for char in \ (self.unwrap_string(None).replace('\r', '\n'))]) diff --git a/rsqueakvm/objspace.py b/rsqueakvm/objspace.py index 4339606e..420beeb3 100644 --- a/rsqueakvm/objspace.py +++ b/rsqueakvm/objspace.py @@ -3,7 +3,7 @@ from rsqueakvm.error import WrappingError, UnwrappingError from rsqueakvm.model.character import W_Character from rsqueakvm.model.numeric import W_Float, W_SmallInteger, W_LargeIntegerWord, W_LargeIntegerBig -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from rsqueakvm.model.variable import W_BytesObject from rsqueakvm.model.block_closure import W_BlockClosure from rsqueakvm.util.version import Version @@ -13,9 +13,14 @@ from rpython.rlib.objectmodel import instantiate, specialize, import_from_mixin, we_are_translated, always_inline from rpython.rlib.rarithmetic import intmask, r_uint, r_uint32, int_between, is_valid_int, r_ulonglong, r_longlong, r_int64 -def empty_object(): +def empty_variable_object(): return instantiate(W_PointersObject) +def empty_fixed_object(): + w_new = instantiate(W_FixedPointersObject) + w_new._size = 0 # XXX + return w_new + def empty_symbol(): return instantiate(W_BytesObject) @@ -65,11 +70,13 @@ def make_special_objects(self): self.w_zero = W_SmallInteger(0) self.w_one = W_SmallInteger(1) self.w_two = W_SmallInteger(2) - self.w_special_objects = empty_object() + self.w_special_objects = empty_variable_object() # no add all of those special objects that we assume constant while the image is running for name, (idx, t) in constants.constant_objects_in_special_object_table.items(): if t == "POINTERS": - setattr(self, "w_" + name, empty_object()) + setattr(self, "w_" + name, empty_variable_object()) + elif t == "FIXED": + setattr(self, "w_" + name, empty_fixed_object()) elif t == "BYTES": setattr(self, "w_" + name, empty_symbol()) else: @@ -299,7 +306,7 @@ def display(self): self.altf4quit.is_set() ) self._display.set(disp) - return jit.promote(disp) + return disp def smalltalk_at(self, string): """A helper to find a class by name in modern Squeak images""" diff --git a/rsqueakvm/plugins/misc_primitive_plugin.py b/rsqueakvm/plugins/misc_primitive_plugin.py index 9d0f9f83..0e536280 100644 --- a/rsqueakvm/plugins/misc_primitive_plugin.py +++ b/rsqueakvm/plugins/misc_primitive_plugin.py @@ -2,25 +2,18 @@ from rsqueakvm.error import PrimitiveFailedError from rsqueakvm.model.variable import W_BytesObject from rsqueakvm.plugins.plugin import Plugin +from rsqueakvm.util.cells import QuasiConstant from rpython.rlib.rarithmetic import r_uint, intmask from rpython.rlib import jit -class Cell(object): - _attrs_ = ["value"] - _immutable_fields_ = ["value?"] - def __init__(self, value): self.value = value - def set(self, v): self.value = v - def get(self): return self.value - - class MiscPrimitivePluginClass(Plugin): _attrs_ = ["ascii_order"] _immutable_fields_ = ["ascii_order"] def __init__(self): Plugin.__init__(self) - self.ascii_order = Cell(None) + self.ascii_order = QuasiConstant(None, type=W_BytesObject) MiscPrimitivePlugin = MiscPrimitivePluginClass() diff --git a/rsqueakvm/primitives/__init__.py b/rsqueakvm/primitives/__init__.py index c79ece67..7698fbd3 100644 --- a/rsqueakvm/primitives/__init__.py +++ b/rsqueakvm/primitives/__init__.py @@ -235,7 +235,7 @@ def wrapped(interp, s_frame, argument_count, w_method=None): # arguments, an interp and an argument_count # completes, and returns a result, or throws a PrimitiveFailedError. def make_simulation(code): - p_code = jit.promote(code) + p_code = code @wrap_primitive(clean_stack=False, no_result=True, compiled_method=True) def try_simulation(interp, s_frame, argument_count, w_method=None): if interp.space.simulate_numeric_primitives.is_set(): diff --git a/rsqueakvm/primitives/input_output.py b/rsqueakvm/primitives/input_output.py index 9c24e7d3..ae10c7b9 100644 --- a/rsqueakvm/primitives/input_output.py +++ b/rsqueakvm/primitives/input_output.py @@ -3,7 +3,7 @@ from rsqueakvm import display, wrapper from rsqueakvm.error import PrimitiveFailedError from rsqueakvm.model.display import W_DisplayBitmap -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from rsqueakvm.model.variable import W_WordsObject from rsqueakvm.primitives import expose_primitive, assert_class, index1_0 from rsqueakvm.primitives.constants import * @@ -57,7 +57,7 @@ def func(interp, s_frame, _): @expose_primitive(MOUSE_POINT, unwrap_spec=[object]) def func(interp, s_frame, w_rcvr): x, y = interp.space.display().mouse_point() - w_point = W_PointersObject(interp.space, interp.space.w_Point, 2) + w_point = W_FixedPointersObject(interp.space, interp.space.w_Point, 0) w_point.store(interp.space, 0, interp.space.wrap_int(x)) w_point.store(interp.space, 1, interp.space.wrap_int(y)) return w_point diff --git a/rsqueakvm/primitives/misc.py b/rsqueakvm/primitives/misc.py index f49dfd9d..b19242b1 100644 --- a/rsqueakvm/primitives/misc.py +++ b/rsqueakvm/primitives/misc.py @@ -70,8 +70,9 @@ def _become(space, w_rcvr, w_new): w_rights.append(w_right) else: for i in range(len(w_lefts)): - w_lefts[i].become(w_rights[i]) - raise PrimitiveFailedError() + if not w_lefts[i].become(w_rights[i]): + raise PrimitiveFailedError + raise PrimitiveFailedError @expose_primitive(BECOME, unwrap_spec=[object, object]) def func(interp, s_frame, w_rcvr, w_new): diff --git a/rsqueakvm/primitives/storage.py b/rsqueakvm/primitives/storage.py index 8f332a49..1f78e7f2 100644 --- a/rsqueakvm/primitives/storage.py +++ b/rsqueakvm/primitives/storage.py @@ -82,8 +82,7 @@ def func(interp, s_frame, w_from, w_to): raise PrimitiveFailedError for w_obj in get_instances_array(interp, s_frame, store=False): - if w_obj.has_class(): - w_obj.pointers_become_one_way(space, from_w, to_w) + w_obj.pointers_become_one_way(space, from_w, to_w) return w_from @expose_primitive(INST_VAR_AT, unwrap_spec=[object, index1_0]) @@ -142,7 +141,7 @@ def get_instances_array_gc(interp, w_class=None): rgc.toggle_gcflag_extra(gcref) w_obj = rgc.try_cast_gcref_to_instance(W_Object, gcref) - if w_obj is not None and w_obj.has_class(): + if w_obj is not None: w_cls = w_obj.getclass(space) if w_cls is not None: # XXX: should not return SmallFloat64 on Spur64... @@ -167,17 +166,16 @@ def get_instances_array_trace(interp, w_class, some_instance=False): w_obj = pending.pop() if w_obj and not seen_w.get(w_obj, False): seen_w[w_obj] = True - if w_obj.has_class(): - w_cls = w_obj.getclass(space) - if w_cls is not None: - # XXX: should not return SmallFloat64 on Spur64... - if ((not w_cls.is_same_object(space.w_SmallInteger)) and - (not (space.is_spur.is_set() and w_cls.is_same_object(space.w_Character))) and - (w_class is None or w_cls.is_same_object(w_class))): - if some_instance: - return [w_obj] - else: - result_w.append(w_obj) + w_cls = w_obj.getclass(space) + if w_cls is not None: + # XXX: should not return SmallFloat64 on Spur64... + if ((not w_cls.is_same_object(space.w_SmallInteger)) and + (not (space.is_spur.is_set() and w_cls.is_same_object(space.w_Character))) and + (w_class is None or w_cls.is_same_object(w_class))): + if some_instance: + return [w_obj] + else: + result_w.append(w_obj) pending.extend(_trace_pointers(interp.space, w_obj)) return result_w diff --git a/rsqueakvm/squeakimage.py b/rsqueakvm/squeakimage.py index 55ae233f..a086862c 100644 --- a/rsqueakvm/squeakimage.py +++ b/rsqueakvm/squeakimage.py @@ -6,7 +6,7 @@ from rsqueakvm.model.compiled_methods import W_CompiledMethod, W_PreSpurCompiledMethod, W_SpurCompiledMethod from rsqueakvm.model.display import W_DisplayBitmap from rsqueakvm.model.numeric import W_Float, W_SmallInteger, W_LargeIntegerWord, W_LargeIntegerBig, W_LargeInteger -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from rsqueakvm.model.block_closure import W_BlockClosure from rsqueakvm.model.variable import W_BytesObject, W_WordsObject from rsqueakvm.util import stream, system @@ -528,6 +528,8 @@ def instantiate(self, g_object): return objectmodel.instantiate(W_Character) elif self.isblockclosure(g_object): return objectmodel.instantiate(W_BlockClosure) + elif self.isfixed(g_object): + return objectmodel.instantiate(W_FixedPointersObject) elif self.ispointers(g_object): return objectmodel.instantiate(W_PointersObject) elif g_object.format == 5: @@ -565,6 +567,9 @@ def isblockclosure(self, g_object): g_closure = self.special_g_object_safe(constants.SO_BLOCKCLOSURE_CLASS) return self.ispointers(g_object) and g_object.g_class == g_closure + def isfixed(self, g_object): + return g_object.format == 1 + def ispointers(self, g_object): return g_object.format < 5 @@ -744,6 +749,8 @@ def instantiate(self, g_object): return objectmodel.instantiate(W_Character) elif self.isblockclosure(g_object): return objectmodel.instantiate(W_BlockClosure) + elif self.isfixed(g_object): + return objectmodel.instantiate(W_FixedPointersObject) elif self.ispointers(g_object): return objectmodel.instantiate(W_PointersObject) elif self.isfloat(g_object): @@ -773,6 +780,9 @@ def isblockclosure(self, g_object): g_closure = self.special_g_object_safe(constants.SO_BLOCKCLOSURE_CLASS) return self.ispointers(g_object) and g_closure == g_object.g_class + def isfixed(self, g_object): + return g_object.format == 1 + def ispointers(self, g_object): return g_object.format < 6 diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 163df1a1..b952f3b1 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -5,7 +5,7 @@ from rsqueakvm import constants from rsqueakvm.model.character import W_Character from rsqueakvm.model.numeric import W_Float, W_SmallInteger, W_MutableSmallInteger -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, _NUMBER_OF_INT_FIELDS, _NUMBER_OF_INLINE_FIELDS, W_FixedPointersObject from rsqueakvm.model.variable import W_BytesObject from rsqueakvm.model.compiled_methods import W_CompiledMethod from rsqueakvm.util.version import VersionMixin, elidable_for_version @@ -13,7 +13,9 @@ from rpython.rlib import jit from rpython.rlib.objectmodel import import_from_mixin +from rpython.rlib.rarithmetic import int_between from rpython.rlib.rstrategies import rstrategies as rstrat +from rpython.rlib.unroll import unrolling_iterable """ A note on terminology: @@ -32,6 +34,14 @@ in storage_classes.py. """ + +class StrategyMetaclass(rstrat.StrategyMetaclass): + def __new__(self, name, bases, attrs): + new_class = rstrat.StrategyMetaclass.__new__(self, name, bases, attrs) + new_class.instantiate_type = new_class + return new_class + + class AbstractStrategy(object): """Subclasses of this handle the information contained in Smalltalk objects. The common API allows to store and fetch elements from object slots. @@ -44,7 +54,7 @@ class AbstractStrategy(object): _immutable_fields_ = ['space', 'w_class'] provides_getname = False repr_classname = "AbstractStrategy" - __metaclass__ = rstrat.StrategyMetaclass + __metaclass__ = StrategyMetaclass import_from_mixin(rstrat.AbstractStrategy) def __init__(self, space, w_self, size, w_class): @@ -86,9 +96,11 @@ def instantiate(self, w_self, w_class): size = self.size(w_self) new_strategy = self.strategy_factory().instantiate_strategy(self.instantiate_type, w_class, w_self, size) return new_strategy - - def promote_if_neccessary(self): - return jit.promote(self) + def promoted(self): + if self._is_singleton: + return jit.promote(self) + else: + return self # ========== Storage classes implementing storage strategies ========== @@ -105,6 +117,7 @@ class SimpleStorageStrategy(AbstractStrategy): def default_value(self): return self.space.w_nil + class OptimizedConvertFromAllNilMixin(object): @jit.unroll_safe def _better_convert_storage_from(self, w_self, previous_strategy): @@ -150,7 +163,345 @@ def store(self, w_self, index0, w_value): else: storage[index0] = w_value ListStrategy._convert_storage_from = ListStrategy._better_convert_storage_from -ListStrategy.instantiate_type = ListStrategy + + +class MapStrategy(AbstractStrategy): + """This strategy is used for W_FixedPointersObject which only have indexable + fields. We guess these are 'normal' objects with some different named + fields, for which the storage strategies hardly make sense (except maybe if + they are always all nil or things like points where they are all integers). + + This strategy implements class maps. Since Squeak objects are fixed size + during their lifetime and start out with their instance variables all nil, + the base MapStrategy instance is similar to the AllNilStrategy, except that + it does not use the storage to keep the size around (because that would be + overwritten). This also has the effect that if we go back to nil at some + point, we can specialize again, rather than always sticking with ObjectNodes + once something not-unboxed is stored in a field. + + For specialized fields, we search, add, and remove nodes as needed and do + the normal map transition mechanism (add_node, find_node, remove_node). + """ + _attrs_ = [] + repr_classname = "MapStrategy (base)" + import_from_mixin(rstrat.SafeIndexingMixin) + + def _initialize_storage(self, w_self, initial_size): + self.set_storage(w_self, None) + + @jit.unroll_safe + def _convert_storage_from(self, w_self, previous_strategy): + assert isinstance(previous_strategy, MapStrategy) + storage = previous_strategy.fetch_all(w_self) + for i, w_value in enumerate(storage): + w_self._get_strategy().store(w_self, i, w_value) + + def instantiate(self, w_self, w_class): + return self.strategy_factory().strategy_singleton_instance(MapStrategy, w_class) + + def check_index(self, w_self, index0): + if not int_between(0, index0, self.size(w_self)): + raise IndexError + + def fetch(self, w_self, index0): + self.check_index_fetch(w_self, index0) + return self.space.w_nil + + def store(self, w_self, index0, w_value): + self.check_index_store(w_self, index0) + if w_value is self.space.w_nil: + return + storage_node_type = MapStorageNode.type_for(w_self, w_value) + new_node = w_self._get_strategy().add_node(storage_node_type, index0, w_self) + assert isinstance(new_node, MapStorageNode) + w_self._set_strategy(new_node) + new_node.store(w_self, index0, w_value) + + def size(self, w_self): + return w_self.size() + + # maps interface + used_fields = "none" + + def getprev(self): + return None + + def read(self, w_self, index0): + return self.fetch(w_self, index0) + + def write(self, w_self, index0, w_value): + self.store(w_self, index0, w_value) + + @jit.elidable + def find_node(self, index0): + return self + + def remove_node(self, node, w_self, index0): + raise NotImplementedError + + def add_node(self, strategy_cls, index0, w_self): + new_node = self.strategy_factory().get_transition(self, strategy_cls, index0, self.getclass()) + assert isinstance(new_node, MapStorageNode) + new_node.update_storage(w_self) + return new_node + + +MapStrategy._is_singleton = True +class MapNodesMetaclass(StrategyMetaclass): + classes = [] + def __new__(self, name, bases, attrs): + attrs['_is_singleton'] = True + new_node = type.__new__(self, name, bases, attrs) + self.classes.append(new_node) + return new_node + + +NUM_DIGITS = 4 +NUM_DIGITS_POW2 = 1 << NUM_DIGITS + + +class MapStorageNode(MapStrategy): + _attrs_ = ["_size_estimate", "prev", "index", "pos"] + _immutable_fields_ = ["prev", "index", "pos"] + + # disable the custom erase/unerase pairs that normally each strategy + # subclass gets via the custom Metaclass. For maps, all Nodes share the same + # erasing pairs with MapStrategy. This way, they can all access the _storage + # array + __metaclass__ = MapNodesMetaclass + + repr_classname = "MapStrategy (abstract node)" + + @staticmethod + @jit.unroll_safe + def type_for(w_self, w_value): + for c in MapNodesMetaclass.classes: + if c.correct_type(w_self, w_value): + return c + assert False + + def __init__(self, space, prev, index, w_class): + assert isinstance(prev, MapStrategy) + self.space = space + self.prev = prev + self.index = index + self.pos = self.compute_position() + self.w_class = w_class + self._size_estimate = max(self.length() * NUM_DIGITS_POW2, 0) + + def __repr__(self): + return "<%s index:%d;pos:%d>" % (self.repr_classname, self.index, self.pos) + + def getprev(self): + return self.prev + + def length(self): + return self.pos + 1 + + @jit.elidable + def size_estimate(self): + return self._size_estimate >> NUM_DIGITS + + def update_storage(self, w_self): + # we use shifting in the size_estimates to propagate sizes only slowly + # through the map in case there a very few instances with many fields + # set, but many instances that only use a few fields. + if not jit.we_are_jitted(): + self._propagate_size_estimates() + + def _propagate_size_estimates(self): + node = self.getprev() + while node is not None: + if node.used_fields == self.used_fields: + assert isinstance(node, MapStorageNode) + node._size_estimate = self._size_estimate + node.size_estimate() - self.size_estimate() + return + node = node.getprev() + + def read(self, w_self, index0): + raise NotImplementedError + + def __write__(self, w_self, index0, w_value): + raise NotImplementedError + + @staticmethod + def correct_type(w_self, w_value): + return False + + def write(self, w_self, index0, w_value): + assert index0 == self.index + if not self.correct_type(w_self, w_value): + strategy = w_self._get_strategy() + assert isinstance(strategy, MapStrategy) + new_node = strategy.remove_node( + self, w_self, index0 + ).add_node( + MapStorageNode.type_for(w_self, w_value), index0, w_self + ) + w_self._set_strategy(new_node) + new_node.store(w_self, index0, w_value) + else: + self.__write__(w_self, index0, w_value) + + def compute_position(self): + node = self.getprev() + n = 0 + while node is not None: + if self.used_fields == node.used_fields: + n += 1 + node = node.getprev() + return n + + def fetch(self, w_self, index0): + self = jit.promote(self) + index0 = jit.promote(index0) + node = self.find_node(index0) + return node.read(w_self, index0) + + def store(self, w_self, index0, w_value): + self = jit.promote(self) + index0 = jit.promote(index0) + node = self.find_node(index0) + node.write(w_self, index0, w_value) + + @jit.elidable + def find_node(self, index0): + if self.index == index0: + return self + else: + return self.prev.find_node(index0) + + def remove_node(self, node, w_self, index0): + if node is self: + return self.prev + w_cur_val = self.read(w_self, self.index) + new_prev = self.prev.remove_node(node, w_self, index0) + node = new_prev.add_node(MapStorageNode.type_for(w_self, w_cur_val), self.index, w_self) + node.write(w_self, self.index, w_cur_val) + return node + + def add_node(self, strategy_cls, index0, w_self): + if self.index > index0: + w_cur_val = self.read(w_self, self.index) + new_prev = self.prev.add_node(strategy_cls, index0, w_self) + node = new_prev.add_node(MapStorageNode.type_for(w_self, w_cur_val), self.index, w_self) + node.write(w_self, self.index, w_cur_val) + return node + else: + assert self.index < index0 + new_node = self.strategy_factory().get_transition(self, strategy_cls, index0, self.getclass()) + assert isinstance(new_node, MapStorageNode) + new_node.update_storage(w_self) + return new_node + + +class IntMapStorageNode(MapStorageNode): + # we inline up to three integer fields on the object and the rest in a intFields array + used_fields = "intFields" + repr_classname = "MapStrategy (int)" + + @staticmethod + def correct_type(w_self, w_value): + return isinstance(w_value, W_SmallInteger) + + def storage_list_position(self): + p = self.pos - _NUMBER_OF_INT_FIELDS + assert p >= 0 + return p + + def length(self): + return self.pos - (_NUMBER_OF_INT_FIELDS - 1) + + @jit.unroll_safe + def update_storage(self, w_self): + MapStorageNode.update_storage(self, w_self) + assert isinstance(w_self, W_FixedPointersObject) + storage = w_self._intFields + length = self.length() + if length > 0: + if storage is None or len(storage) < length: + new_storage = [0] * self.size_estimate() + if storage is not None: + for i, value in enumerate(storage): + new_storage[i] = value + w_self._intFields = new_storage + + def read(self, w_self, index0): + assert isinstance(w_self, W_FixedPointersObject) + if self.pos == 0: + res = w_self._intField1 + elif self.pos == 1: + res = w_self._intField2 + elif self.pos == 2: + res = w_self._intField3 + else: + res = w_self._intFields[self.storage_list_position()] + return self.space.wrap_smallint_unsafe(res) + + def __write__(self, w_self, index0, w_value): + assert isinstance(w_self, W_FixedPointersObject) + assert index0 == self.index + unwrapped = self.space.unwrap_int(w_value) + if self.pos == 0: + w_self._intField1 = unwrapped + elif self.pos == 1: + w_self._intField2 = unwrapped + elif self.pos == 2: + w_self._intField3 = unwrapped + else: + w_self._intFields[self.storage_list_position()] = unwrapped + + +class ObjectMapStorageNode(MapStorageNode): + used_fields = "erased_storage" + repr_classname = "MapStrategy (object)" + + @staticmethod + def correct_type(w_self, w_value): + return True + + def storage_list_position(self): + p = self.pos - _NUMBER_OF_INLINE_FIELDS + assert p >= 0 + return p + + def length(self): + return self.pos - (_NUMBER_OF_INLINE_FIELDS - 1) + + @jit.unroll_safe + def update_storage(self, w_self): + MapStorageNode.update_storage(self, w_self) + storage = self.get_storage(w_self) + length = self.length() + if storage is None or len(storage) < length: + new_storage = [None] * self.size_estimate() + if storage is not None: + for i, value in enumerate(storage): + new_storage[i] = value + self.set_storage(w_self, new_storage) + + def read(self, w_self, index0): + assert isinstance(w_self, W_FixedPointersObject) + if self.pos == 0: + w_res = w_self._field1 + elif self.pos == 1: + w_res = w_self._field2 + else: + w_res = self.get_storage(w_self)[self.storage_list_position()] + if w_res is None: + return self.space.w_nil + else: + return w_res + + def __write__(self, w_self, index0, w_value): + assert isinstance(w_self, W_FixedPointersObject) + assert index0 == self.index + if self.pos == 0: + w_self._field1 = w_value + elif self.pos == 1: + w_self._field2 = w_value + else: + self.get_storage(w_self)[self.storage_list_position()] = w_value class ListEntry(object): @@ -248,7 +599,6 @@ def delete(self, w_self, start, end): self.check_index_range(w_self, start, end) assert start >= 0 and end >= 0 del self.get_storage(w_self)[start : end] -WeakListStrategy.instantiate_type = WeakListStrategy @rstrat.strategy(generalize=[ListStrategy]) class SmallIntegerOrNilStrategy(SimpleStorageStrategy): @@ -261,7 +611,6 @@ def unwrap(self, w_val): return self.space.unwrap_int(w_val) def wrapped_tagged_value(self): return self.space.w_nil def unwrapped_tagged_value(self): return constants.MAXINT SmallIntegerOrNilStrategy._convert_storage_from = SmallIntegerOrNilStrategy._better_convert_storage_from -SmallIntegerOrNilStrategy.instantiate_type = SmallIntegerOrNilStrategy @rstrat.strategy(generalize=[ListStrategy]) class CharacterOrNilStrategy(SimpleStorageStrategy): @@ -277,7 +626,6 @@ def unwrap(self, w_val): def wrapped_tagged_value(self): return self.space.w_nil def unwrapped_tagged_value(self): return constants.MAXINT CharacterOrNilStrategy._convert_storage_from = CharacterOrNilStrategy._better_convert_storage_from -CharacterOrNilStrategy.instantiate_type = CharacterOrNilStrategy @rstrat.strategy(generalize=[ListStrategy]) class FloatOrNilStrategy(SimpleStorageStrategy): @@ -291,7 +639,6 @@ def unwrap(self, w_val): return self.space.unwrap_float(w_val) def wrapped_tagged_value(self): return self.space.w_nil def unwrapped_tagged_value(self): return self.tag_float FloatOrNilStrategy._convert_storage_from = FloatOrNilStrategy._better_convert_storage_from -FloatOrNilStrategy.instantiate_type = FloatOrNilStrategy @rstrat.strategy(generalize=[ SmallIntegerOrNilStrategy, @@ -301,7 +648,6 @@ class AllNilStrategy(SimpleStorageStrategy): repr_classname = "AllNilStrategy" import_from_mixin(rstrat.SingleValueStrategy) def value(self): return self.space.w_nil -AllNilStrategy.instantiate_type = AllNilStrategy class StrategyFactory(rstrat.StrategyFactory): _immutable_fields_ = ["space", "no_specialized_storage"] @@ -309,6 +655,7 @@ def __init__(self, space): self.space = space self.no_specialized_storage = QuasiConstant(False) self.singleton_nodes = {} + self.transitions = {} rstrat.StrategyFactory.__init__(self, AbstractStrategy) # XXX: copied and slightly modified to not set a singleton field on the strategy class @@ -367,14 +714,24 @@ def strategy_singleton_instance(self, strategy_class, w_class=None): def strategy_singleton_instance_from_cache(self, strategy_class, w_class): return self.singleton_nodes.get((strategy_class, w_class), None) + @jit.elidable + def get_transition(self, prev, strategy_node_class, index0, w_class): + key = (prev, strategy_node_class, index0) + node = self.transitions.get(key, None) + if node is None: + self.transitions[key] = node = strategy_node_class(self.space, prev, index0, w_class) + return node + def instantiate_strategy(self, strategy_type, w_class, w_self=None, initial_size=0): return strategy_type(self.space, w_self, initial_size, w_class) - def strategy_type_for(self, objects, weak=False): + def strategy_type_for(self, w_self, objects, weak=False): if weak: return WeakListStrategy if self.no_specialized_storage.is_set(): return ListStrategy + if isinstance(w_self, W_FixedPointersObject): + return MapStrategy return rstrat.StrategyFactory.strategy_type_for(self, objects) def empty_storage_type(self, w_self, size, weak=False): @@ -382,6 +739,8 @@ def empty_storage_type(self, w_self, size, weak=False): return WeakListStrategy if self.no_specialized_storage.is_set(): return ListStrategy + if isinstance(w_self, W_FixedPointersObject): + return MapStrategy return AllNilStrategy def log(self, w_self, new_strategy, old_strategy=None, new_element=None): @@ -414,7 +773,7 @@ def is_shadow(self): def become(self, w_other): w_self = self._w_self self.onesided_become(w_other) - if w_other.has_strategy() and w_other._get_strategy().is_shadow(): + if w_other._get_strategy().is_shadow(): w_other._get_strategy().onesided_become(w_self) def onesided_become(self, w_other): self._w_self = w_other @@ -424,8 +783,6 @@ def own_store(self, i, val): self.store(self._w_self, i, val) def own_fetch(self, i): return self.fetch(self._w_self, i) - def promote_if_neccessary(self): - return self class AbstractGenericShadow(ListStrategy): """ @@ -472,7 +829,6 @@ def fetch(self, w_self, n0): def store(self, w_self, n0, w_value): AbstractCachingShadow.store(self, w_self, n0, w_value) self.changed() -CachedObjectShadow.instantiate_type = CachedObjectShadow class ObserveeShadow(AbstractGenericShadow): @@ -494,4 +850,3 @@ def set_observer(self, observer): if self.observer is not None and observer is not self.observer: raise RuntimeError('Meant to be observed by only one observer, so far') self.observer = observer -ObserveeShadow.instantiate_type = ObserveeShadow diff --git a/rsqueakvm/storage_classes.py b/rsqueakvm/storage_classes.py index b67f5888..4951abe5 100644 --- a/rsqueakvm/storage_classes.py +++ b/rsqueakvm/storage_classes.py @@ -2,7 +2,7 @@ from rsqueakvm.model.base import W_Object from rsqueakvm.model.compiled_methods import W_CompiledMethod, W_PreSpurCompiledMethod, W_SpurCompiledMethod from rsqueakvm.model.numeric import W_MutableFloat, W_SmallInteger, W_LargeIntegerWord, W_LargeIntegerBig -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from rsqueakvm.model.variable import W_BytesObject, W_WordsObject from rsqueakvm.storage import AbstractCachingShadow, AbstractGenericShadow from rsqueakvm.util.version import elidable_for_version, Version @@ -10,8 +10,6 @@ from rpython.rlib import jit from rpython.rlib.rbigint import NULLRBIGINT -import pdb - POINTERS = 0 BYTES = 1 WORDS = 2 @@ -55,7 +53,7 @@ def store(self, w_self, n0, w_val): else: if w_self.getclass(self.space).is_same_object(self.space.w_Metaclass): # In case of Metaclasses, the "instance" class is stored in the last field. - if n0 == self.size(w_self) - 1 and isinstance(w_val, W_PointersObject): + if n0 == self.size(w_self) - 1 and isinstance(w_val, W_FixedPointersObject): cl_shadow = w_val.as_class_get_shadow(self.space) self.name = "%s class" % cl_shadow.getname() self.changed() @@ -173,7 +171,7 @@ def store_w_superclass(self, w_class): self._s_superclass = None self.changed() else: - assert isinstance(w_class, W_PointersObject) + assert isinstance(w_class, W_FixedPointersObject) s_new_superclass = w_class.as_class_get_shadow(self.space) if superclass is s_new_superclass: return @@ -253,7 +251,11 @@ def new(self, extrasize=0): return w_new def make_pointers_object(self, w_cls, size): - return W_PointersObject(self.space, w_cls, size) + if self.isvariable(): + return W_PointersObject(self.space, w_cls, size) + else: + assert size == self.instsize() + return W_FixedPointersObject(self.space, w_cls, size) @elidable_for_version(0) def get_instance_kind(self): @@ -330,8 +332,8 @@ def inherits_from(self, s_superclass): def initialize_methoddict(self): "NOT_RPYTHON" # this is only for testing. if self._s_methoddict is None: - w_methoddict = W_PointersObject(self.space, None, 2) - w_methoddict.store(self.space, constants.METHODDICT_VALUES_INDEX, W_PointersObject(self.space, None, 0)) + w_methoddict = W_PointersObject(self.space, self.space.w_Array, 2) + w_methoddict.store(self.space, constants.METHODDICT_VALUES_INDEX, W_PointersObject(self.space, self.space.w_Array, 0)) self.store_s_methoddict(w_methoddict.as_methoddict_get_shadow(self.space)) def installmethod(self, w_selector, w_method): @@ -341,7 +343,6 @@ def installmethod(self, w_selector, w_method): self.s_methoddict().methoddict[w_selector] = w_method if isinstance(w_method, W_CompiledMethod): w_method.compiledin_class = self.w_self() -ClassShadow.instantiate_type = ClassShadow class MethodDictionaryShadow(AbstractGenericShadow): @@ -429,4 +430,3 @@ def sync_method_cache(self): w_compiledmethod.set_lookup_class_and_name(self.s_class.w_self(), selector) if self.s_class: self.s_class.changed() -MethodDictionaryShadow.instantiate_type = MethodDictionaryShadow diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 6f78f7cb..7700f88d 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -2,7 +2,7 @@ from rsqueakvm.model.compiled_methods import W_CompiledMethod from rsqueakvm.model.pointers import W_PointersObject from rsqueakvm.model.block_closure import W_BlockClosure -from rsqueakvm.storage import AbstractStrategy, ShadowMixin +from rsqueakvm.storage import AbstractStrategy, ShadowMixin, StrategyMetaclass from rpython.rlib import jit, objectmodel, unroll from rpython.rlib.objectmodel import import_from_mixin, always_inline @@ -33,7 +33,7 @@ def num(self): ActiveContext = ContextState("ActiveContext") DirtyContext = ContextState("DirtyContext") -class ExtendableStrategyMetaclass(extendabletype, rstrat.StrategyMetaclass): +class ExtendableStrategyMetaclass(extendabletype, StrategyMetaclass): pass @@ -686,7 +686,7 @@ def build_block_context(space, s_home, argcnt, pc): ctx.store_w_home(s_home.w_self()) ctx.store_initialip(pc) ctx.store_pc(pc) - w_self.store_strategy(ctx) + w_self._set_strategy(ctx) ctx.init_temps_and_stack() return ctx @@ -939,7 +939,7 @@ def w_self(self): else: space = self.space w_self = W_PointersObject(space, space.w_MethodContext, self._w_self_size) - w_self.store_strategy(self) + w_self._set_strategy(self) self._w_self = w_self return w_self @@ -960,5 +960,3 @@ def w_arguments_method_context(self): def method_str_method_context(self): block = '[] in ' if self.is_closure_context() else '' return '%s%s' % (block, self.w_method().get_identifier_string()) - -ContextPartShadow.instantiate_type = ContextPartShadow diff --git a/rsqueakvm/test/conftest.py b/rsqueakvm/test/conftest.py index 7454159f..88e2ca3f 100644 --- a/rsqueakvm/test/conftest.py +++ b/rsqueakvm/test/conftest.py @@ -2,6 +2,13 @@ def pytest_addoption(parser): group = parser.getgroup("RSqueak test options") + group.addoption( + "--update", "-U", + dest="update-jit-tests", + action="store_true", + default=False, + help="Ask if we should update the JIT fixtures" + ) group.addoption( "--quick", "-Q", dest="execute-quick-tests", @@ -43,12 +50,16 @@ def system(self, *args): def popen(self, *args, **kwargs): return subprocess.Popen(self.all_arguments(list(args)), **kwargs) +UPDATE_JIT_TESTS = False + # The 'jit' parameter is used in tests under jittest/ def pytest_funcarg__spy(request): val = request.config.getvalue("rsqueak-binary") if not val: py.test.skip("Provide --jit parameter to execute jit tests") jitarg = request.config.getvalue("jitargs") + global UPDATE_JIT_TESTS + UPDATE_JIT_TESTS = request.config.getvalue("update-jit-tests") if not jitarg: return _Executor_(py.path.local(val)) else: @@ -60,13 +71,14 @@ def pytest_assertrepr_compare(op, left, right): ltrace = [lop.name for lop in left.trace] rtrace = [rop.name for rop in right.trace] import difflib - answer = (['Comparing Traces failed (set UPDATE_JITTESTS=1 in your env and run again to auto-update):'] + - list(difflib.unified_diff(ltrace, rtrace)) + + answer = (['Comparing Traces failed (use -U / --update when running to auto-update):'] + ["-------OLD------"] + [str(op) for op in left.trace] + ["-------NEW------"] + - [str(op) for op in right.trace]) - if os.environ.get("UPDATE_JITTESTS", None) == "1": + [str(op) for op in right.trace] + + ["-------DIFF-----"] + + list(difflib.unified_diff(ltrace, rtrace))) + if UPDATE_JIT_TESTS: print "\n".join(answer) import sys sys.stdout.write("Should we accept the new version (y/N)? ") @@ -76,21 +88,25 @@ def pytest_assertrepr_compare(op, left, right): stk.reverse() for filename, lineno, funcname, text in stk: if re.search("jittest/test_", filename): - f = open(filename) - contents = f.readlines() - newline = "\r\n" if contents[0].endswith("\r\n") else "\n" - while contents[lineno].strip() != str(left.trace[0]).strip(): - lineno += 1 - indent = (len(contents[lineno]) - len(contents[lineno].lstrip())) * " " - contents[lineno:lineno + len(left.trace)] = [(indent + str(op) + newline) for op in right.trace] - f.close() - f = open(filename, "w") - f.truncate(0) - f.writelines(contents) - f.close() - break + try: + lno = lineno + f = open(filename) + contents = f.readlines() + newline = "\r\n" if contents[0].endswith("\r\n") else "\n" + while contents[lno].strip() != str(left.trace[0]).strip(): + lno += 1 + indent = (len(contents[lno]) - len(contents[lno].lstrip())) * " " + contents[lno:lno + len(left.trace)] = [(indent + str(op) + newline) for op in right.trace] + f.close() + f = open(filename, "w") + f.truncate(0) + f.writelines(contents) + f.close() + break + except Exception: + import pdb; pdb.set_trace() del sys.exitfunc - sys.exit(os.system("%s \"%s\"" % (sys.executable, "\" \"".join(sys.argv)))) + os._exit(os.system("%s \"%s\"" % (sys.executable, "\" \"".join(sys.argv)))) else: return answer else: diff --git a/rsqueakvm/test/jittest/test_basic.py b/rsqueakvm/test/jittest/test_basic.py index 8245a4e9..92ab2b64 100644 --- a/rsqueakvm/test/jittest/test_basic.py +++ b/rsqueakvm/test/jittest/test_basic.py @@ -10,7 +10,7 @@ def test_while_loop(self, spy, tmpdir): 0 to: 1000000000 do: [:t|nil]. """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i59 = int_le(i51, 1000000000), guard_true(i59, descr=), i60 = int_add(i51, 1), @@ -29,7 +29,7 @@ def test_constant_string(self, spy, tmpdir): ^ i """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i83 = int_le(i77, 10000), guard_true(i83, descr=), i85 = int_add_ovf(i77, 1), @@ -54,7 +54,7 @@ def test_constant_string_equal2(self, spy, tmpdir): ^ i """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i71 = int_le(i64, 100000), guard_true(i71, descr=), i72 = int_add(i64, 1), @@ -82,7 +82,7 @@ def test_constant_string_var_equal(self, spy, tmpdir): ^ i """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i72 = int_le(i64, 100000), guard_true(i72, descr=), i73 = int_add(i64, 1), @@ -104,7 +104,7 @@ def test_bitblt_fillWhite(self, spy, tmpdir): Display beDisplay. 1 to: 10000 do: [:i | Display fillWhite]. """) self.assert_matches(traces[0].loop, """ - i598 = int_le(2, i153), + i598 = int_le(2, i153) guard_false(i598, descr=), i599 = getfield_gc_pure(p589, descr=), i600 = int_add_ovf(i599, i162), @@ -358,7 +358,7 @@ def test_bytecode_prim_add(self, spy, tmpdir): 1 to: 10000 do: [:i | p + p]. """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i68 = int_le(i61, 10000), guard_true(i68, descr=), i69 = int_add(i61, 1), @@ -376,7 +376,7 @@ def test_bytecode_prim_add_fail(self, spy, tmpdir): 1 to: 10000 do: [:i | p + p]. """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i129 = int_le(i123, 10000), guard_true(i129, descr=), p98 = force_token() @@ -387,7 +387,6 @@ def test_bytecode_prim_add_fail(self, spy, tmpdir): setfield_gc(ConstPtr(ptr71), i70, descr=), i73 = int_le(i70, 0), guard_false(i73, descr=), - i144 = arraylen_gc(p62, descr=), jump(p0, p3, p4, i5, i6, p7, i8, i9, p11, p12, p13, p16, i141, p24, p26, p28, p30, p32, p34, p36, p38, p40, p42, p44, p46, p62, p84, i142, p114, descr=TargetToken(154312720))] """) @@ -559,7 +558,7 @@ def test_large_integer_xor(self, spy, tmpdir): 1 to: 100000 do: [:i | li bitXorLarge: i]. """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i97 = int_le(i90, 100000), guard_true(i97, descr=), i100 = int_xor(i83, i90), @@ -581,7 +580,7 @@ def test_large_integer_and(self, spy, tmpdir): """) if not IS_64BIT: self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i72 = int_le(i65, 100000), guard_true(i72, descr=), i73 = int_and(i58, i65), @@ -616,7 +615,7 @@ def test_large_integer_or(self, spy, tmpdir): """) if not IS_64BIT: self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), + guard_not_invalidated(descr=) i72 = int_le(i65, 100000), guard_true(i72, descr=), i73 = int_or(i58, i65), @@ -683,24 +682,21 @@ def test_named_object_access(self, spy, tmpdir): 1 to: 10000 do: [:i | o extent ]. """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=) - i135 = int_le(i134, 10000) - guard_true(i135, descr=) - p98 = force_token() - enter_portal_frame(4, 0) + guard_not_invalidated(descr=) + i95 = int_le(i86, 10000) + guard_true(i95, descr=) + p96 = force_token() + enter_portal_frame(1, 0) p99 = force_token() - enter_portal_frame(4, 0) - leave_portal_frame(4) - leave_portal_frame(4) - i136 = int_add(i134, 1) - i70 = int_sub(i61, 1), - setfield_gc(ConstPtr(ptr71), i70, descr=), - i73 = int_le(i70, 0), - guard_false(i73, descr=), - i137 = arraylen_gc(p71, descr=) - i138 = arraylen_gc(p81, descr=) - i139 = arraylen_gc(p98, descr=) - jump(p0, p1, i2, p3, p6, p7, i8, i9, p10, p11, i13, p14, p17, i136, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p45, p47, p71, p81, p73, p75, p98, p106, descr=TargetToken(160421980)) + enter_portal_frame(1, 0) + leave_portal_frame(1) + leave_portal_frame(1) + i105 = int_add(i86, 1) + i107 = int_sub(i90, 1) + setfield_gc(ConstPtr(ptr108), i107, descr=) + i110 = int_le(i107, 0) + guard_false(i110, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i105, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p60, p62, i107, descr=TargetToken(47188672)) """) def test_very_large_integer_mul(self, spy, tmpdir): diff --git a/rsqueakvm/test/jittest/test_modern.py b/rsqueakvm/test/jittest/test_modern.py index 295a2485..87211d9f 100644 --- a/rsqueakvm/test/jittest/test_modern.py +++ b/rsqueakvm/test/jittest/test_modern.py @@ -57,20 +57,19 @@ def test_ivar_access(self, spy, tmpdir): [ o i < 10000 ] whileTrue: [ o i: o i + 1 ]. """) self.assert_matches(traces[-1].loop, """ - guard_not_invalidated(descr=) - i78 = int_lt(i61, 10000) - guard_true(i78, descr=) - i80 = int_add(i61, 1) - p81 = force_token() - enter_portal_frame(4, 0) - leave_portal_frame(4) - i86 = int_sub(i72, 1) - setfield_gc(ConstPtr(ptr87), i86, descr=) - setarrayitem_gc(p53, 0, i80, descr=) - i90 = int_le(i86, 0) - guard_false(i90, descr=) - i91 = arraylen_gc(p53, descr=) - jump(p0, p1, i2, p3, p4, p7, p8, p10, p13, p15, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p53, i80, i86, descr=TargetToken(170868656)) + guard_not_invalidated(descr=) + i70 = int_lt(i54, 10000) + guard_true(i70, descr=) + i72 = int_add(i54, 1) + p73 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + i78 = int_sub(i65, 1) + setfield_gc(p13, i72, descr=) + setfield_gc(ConstPtr(ptr79), i78, descr=) + i81 = int_le(i78, 0) + guard_false(i81, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, p13, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, i72, i78, descr=TargetToken(105649344)) """) def test_named_access(self, spy, tmpdir): @@ -80,16 +79,15 @@ def test_named_access(self, spy, tmpdir): 1 to: 100000 do: [:i | m bounds ]. """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=) - i69 = int_le(i69, 100000) - guard_true(i69, descr=) - i71 = int_add(i69, 1) - i70 = int_sub(i61, 1), - setfield_gc(ConstPtr(ptr71), i70, descr=), - i73 = int_le(i70, 0), - guard_false(i73, descr=), - i72 = arraylen_gc(p65, descr=) - jump(p0, p1, i2, p3, p6, p7, i8, i9, p10, p11, i13, p14, p17, i71, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p45, p47, p65, descr=TargetToken(231309508)) + guard_not_invalidated(descr=) + i65 = int_le(i56, 100000) + guard_true(i65, descr=) + i67 = int_add(i56, 1) + i69 = int_sub(i60, 1) + setfield_gc(ConstPtr(ptr70), i69, descr=) + i72 = int_le(i69, 0) + guard_false(i72, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i67, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, i69, descr=TargetToken(97209184)) """) def test_named_access_in_array(self, spy, tmpdir): @@ -99,19 +97,17 @@ def test_named_access_in_array(self, spy, tmpdir): 1 to: 100000 do: [:i | (o at: 1) bounds ]. """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=) - i70 = int_le(i69, 100000) - guard_true(i70, descr=) - i71 = int_add(i69, 1) - i73 = int_sub(i61, 1), - setfield_gc(ConstPtr(ptr71), i70, descr=), - i74 = int_le(i73, 0), - guard_false(i74, descr=), - i72 = arraylen_gc(p65, descr=) - jump(p0, p1, i2, p3, p6, p7, i8, i9, p10, p11, i13, p14, p17, i71, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p45, p47, p65, descr=TargetToken(231309508)) + guard_not_invalidated(descr=) + i80 = int_le(i71, 100000) + guard_true(i80, descr=) + i82 = int_add(i71, 1) + i84 = int_sub(i75, 1) + setfield_gc(ConstPtr(ptr85), i84, descr=) + i87 = int_le(i84, 0) + guard_false(i87, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i82, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p57, p62, i84, descr=TargetToken(107351760)) """) - @py.test.mark.skipif("'Not ready'") def test_named_access_in_do_block(self, spy, tmpdir): traces = self.run(spy, tmpdir, """ | o | @@ -119,13 +115,107 @@ def test_named_access_in_do_block(self, spy, tmpdir): 1 to: 100000 do: [:i | o do: [:m | m bounds ] ]. """) self.assert_matches(traces[1].loop, """ - guard_not_invalidated(descr=) - i70 = int_le(i69, 100000) - guard_true(i70, descr=) - i71 = int_add(i69, 1) - i72 = arraylen_gc(p65, descr=) - i73 = arraylen_gc(p67, descr=) - jump(p0, p1, i2, p3, p6, p7, i8, i9, p10, p11, i13, p14, p17, i71, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p45, p47, p65, descr=TargetToken(231309508)) + guard_not_invalidated(descr=) + i150 = int_le(i141, 100000) + guard_true(i150, descr=) + p151 = getfield_gc_r(p1, descr=) + guard_nonnull_class(p151, ConstClass(ContextPartShadow), descr=) + i153 = ptr_eq(p151, p0) + guard_true(i153, descr=) + p155 = getfield_gc_r(ConstPtr(ptr154), descr=) + guard_value(p155, ConstPtr(ptr156), descr=) + p157 = getfield_gc_r(p11, descr=) + guard_value(p157, ConstPtr(ptr158), descr=) + p160 = getfield_gc_r(ConstPtr(ptr159), descr=) + guard_value(p160, ConstPtr(ptr161), descr=) + p162 = force_token() + enter_portal_frame(1, 0) + p165 = getfield_gc_r(p11, descr=) + i166 = arraylen_gc(p165, descr=) + i168 = int_le(1, i166) + guard_true(i168, descr=) + i170 = uint_lt(0, i166) + guard_true(i170, descr=) + p172 = getarrayitem_gc_r(p165, 0, descr=) + guard_nonnull_class(p172, ConstClass(W_FixedPointersObject), descr=) + p174 = force_token() + enter_portal_frame(1, 0) + p177 = getfield_gc_r(p172, descr=) + guard_value(p177, ConstPtr(ptr178), descr=) + p180 = getfield_gc_r(ConstPtr(ptr179), descr=) + guard_value(p180, ConstPtr(ptr181), descr=) + p182 = getfield_gc_r(p172, descr=) + guard_nonnull_class(p182, ConstClass(W_FixedPointersObject), descr=) + leave_portal_frame(1) + i186 = int_sub(i145, 1) + setfield_gc(ConstPtr(ptr187), i186, descr=) + i189 = int_le(i186, 0) + guard_false(i189, descr=) + p190 = force_token() + p191 = new_with_vtable(descr=) + p192 = new_with_vtable(descr=) + setfield_gc(p192, p162, descr=) + setfield_gc(p192, ConstPtr(null), descr=) + p194 = new_array_clear(17, descr=) + p195 = new_with_vtable(descr=) + setfield_gc(p195, 0, descr=) + setfield_gc(p195, ConstPtr(ptr197), descr=) + setfield_gc(p195, 268435510, descr=) + setfield_gc(p195, ConstPtr(ptr199), descr=) + setfield_gc(p195, p1, descr=) + setfield_gc(p195, p6, descr=) + setfield_gc(p195, ConstPtr(ptr200), descr=) + setarrayitem_gc(p194, 0, p195, descr=) + p202 = new_with_vtable(descr=) + setfield_gc(p202, 2, descr=) + setarrayitem_gc(p194, 1, p202, descr=) + p205 = new_with_vtable(descr=) + setfield_gc(p205, i166, descr=) + setarrayitem_gc(p194, 2, p205, descr=) + setarrayitem_gc(p194, 3, p202, descr=) + setarrayitem_gc(p194, 4, ConstPtr(ptr209), descr=) + setarrayitem_gc(p194, 5, ConstPtr(ptr211), descr=) + setfield_gc(p0, p190, descr=) + setfield_gc(p191, ConstPtr(ptr212), descr=) + setfield_gc(p191, p192, descr=) + setfield_gc(p191, 1086324741, descr=) + setfield_gc(p191, p194, descr=) + setfield_gc(p191, ConstPtr(ptr214), descr=) + setfield_gc(p191, p11, descr=) + setfield_gc(p191, ConstPtr(null), descr=) + setfield_gc(p191, 23, descr=) + setfield_gc(p191, ConstPtr(null), descr=) + setfield_gc(p191, ConstPtr(null), descr=) + call_assembler_n(p191, descr=) + guard_not_forced(descr=) + keepalive(p191) + p220 = guard_exception(13757968, descr=) + i221 = ptr_eq(p191, p0) + guard_false(i221, descr=) + p222 = getfield_gc_r(p191, descr=) + i224 = ptr_ne(p222, ConstPtr(null)) + cond_call(i224, 6399472, p191, descr=) + i226 = getfield_gc_i(p191, descr=) + guard_value(i226, 1086324759, descr=) + guard_not_invalidated(descr=) + p228 = getfield_gc_r(p191, descr=) + guard_isnull(p228, descr=) + p229 = getfield_gc_r(p191, descr=) + guard_value(p229, ConstPtr(ptr230), descr=) + setfield_gc(p191, ConstPtr(null), descr=) + setfield_gc(p191, 1090519039, descr=) + guard_class(p220, 13757968, descr=) + p234 = getfield_gc_r(p220, descr=) + setfield_gc(p191, 16777215, descr=) + setfield_gc(p192, ConstPtr(null), descr=) + guard_nonnull(p234, descr=) + i237 = int_add(i141, 1) + i239 = getfield_gc_i(ConstPtr(ptr238), descr=) + i241 = int_sub(i239, 1) + setfield_gc(ConstPtr(ptr242), i241, descr=) + i244 = int_le(i241, 0) + guard_false(i244, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i237, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, i241, descr=TargetToken(106207040)) """) def test_named_access_fresh(self, spy, tmpdir): @@ -133,44 +223,39 @@ def test_named_access_fresh(self, spy, tmpdir): 1 to: 100000 do: [:i | Morph new bounds ]. """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=) - i169 = int_le(i162, 100000) - guard_true(i169, descr=) - p98 = force_token() - enter_portal_frame(0, 0) - p99 = force_token() - enter_portal_frame(0, 0) - p100 = force_token() - enter_portal_frame(0, 0) - p101 = force_token() - enter_portal_frame(0, 0) - p102 = force_token() - enter_portal_frame(0, 0) - p103 = force_token() - enter_portal_frame(0, 0) - leave_portal_frame(0) - leave_portal_frame(0) - leave_portal_frame(0) - leave_portal_frame(0) - p104 = force_token() - enter_portal_frame(0, 0) - p105 = force_token() - enter_portal_frame(0, 0) - leave_portal_frame(0) - leave_portal_frame(0) - leave_portal_frame(0) - leave_portal_frame(0) - i170 = int_add(i162, 1) - i171 = int_sub(i166, 1) - setfield_gc(ConstPtr(ptr163), i171, descr=) - i172 = int_le(i171, 0) - guard_false(i172, descr=) - i174 = arraylen_gc(p61, descr=) - i175 = arraylen_gc(p68, descr=) - i176 = arraylen_gc(p94, descr=) - i177 = arraylen_gc(p112, descr=) - i178 = arraylen_gc(p140, descr=) - jump(p0, p1, i2, p3, p6, p7, i8, i9, p10, p11, i13, p14, i170, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p45, p47, p61, p68, p63, p94, p112, p114, p140, p142, p154, i171, descr=TargetToken(241143860)) + guard_not_invalidated(descr=) + i163 = int_le(i153, 100000) + guard_true(i163, descr=) + p164 = force_token() + enter_portal_frame(1, 0) + p167 = force_token() + enter_portal_frame(1, 0) + p170 = force_token() + enter_portal_frame(1, 0) + p173 = force_token() + enter_portal_frame(1, 0) + p176 = force_token() + enter_portal_frame(1, 0) + p179 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + leave_portal_frame(1) + leave_portal_frame(1) + leave_portal_frame(1) + p186 = force_token() + enter_portal_frame(1, 0) + p189 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + leave_portal_frame(1) + leave_portal_frame(1) + leave_portal_frame(1) + i197 = int_add(i153, 1) + i199 = int_sub(i157, 1) + setfield_gc(ConstPtr(ptr200), i199, descr=) + i202 = int_le(i199, 0) + guard_false(i202, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, i197, p17, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p52, p99, p128, p130, i199, descr=TargetToken(92509376)) """) def test_named_access_and_send(self, spy, tmpdir): @@ -180,51 +265,46 @@ def test_named_access_and_send(self, spy, tmpdir): 1 to: 100000 do: [:i | m bounds outsetBy: 10 ]. """) self.assert_matches(traces[3].loop, """ - guard_not_invalidated(descr=), - i234 = int_le(i227, 100000) - guard_true(i234, descr=) - p98 = force_token() - enter_portal_frame(4, 0) - p99 = force_token() - enter_portal_frame(4, 0) - p100 = force_token() - enter_portal_frame(4, 0) - p101 = force_token() - enter_portal_frame(4, 0) - leave_portal_frame(4) - leave_portal_frame(4) - leave_portal_frame(4) - p102 = force_token() - enter_portal_frame(4, 0) - p103 = force_token() - enter_portal_frame(4, 0) - p104 = force_token() - enter_portal_frame(4, 0) - leave_portal_frame(4) - leave_portal_frame(4) - leave_portal_frame(4) - p105 = force_token() - enter_portal_frame(4, 0) - p106 = force_token() - enter_portal_frame(4, 0) - leave_portal_frame(4) - leave_portal_frame(4) - leave_portal_frame(4) - i235 = int_add(i227, 1) - i236 = int_sub(i231, 3) - setfield_gc(ConstPtr(ptr228), i236, descr=) - i237 = int_le(i236, 0) - guard_false(i237, descr=) - i240 = arraylen_gc(p86, descr=) - i241 = arraylen_gc(p89, descr=) - i242 = arraylen_gc(p97, descr=) - i243 = arraylen_gc(p119, descr=) - i245 = int_sub_ovf(i147, 10) - guard_no_overflow(descr=) - i246 = int_sub_ovf(i156, 10) - guard_no_overflow(descr=) - i244 = arraylen_gc(p145, descr=) - jump(p0, p1, i2, p3, p6, p7, i8, i9, p10, p11, i13, p14, p17, i235, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p45, p47, p65, p67, p86, p89, p97, p91, p119, p121, p145, p169, p189, p88, i236, i147, i156, descr=TargetToken(231130940)) + guard_not_invalidated(descr=) + i149 = int_le(i140, 100000) + guard_true(i149, descr=) + p150 = force_token() + enter_portal_frame(1, 0) + p153 = force_token() + enter_portal_frame(1, 0) + p156 = force_token() + enter_portal_frame(1, 0) + p159 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + leave_portal_frame(1) + leave_portal_frame(1) + p165 = force_token() + enter_portal_frame(1, 0) + p168 = force_token() + enter_portal_frame(1, 0) + p171 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + leave_portal_frame(1) + leave_portal_frame(1) + p177 = force_token() + enter_portal_frame(1, 0) + p180 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + leave_portal_frame(1) + leave_portal_frame(1) + i187 = int_add(i140, 1) + i189 = int_sub(i144, 3) + setfield_gc(ConstPtr(ptr190), i189, descr=) + i192 = int_le(i189, 0) + guard_false(i192, descr=) + i194 = int_sub_ovf(i92, 10) + guard_no_overflow(descr=) + i195 = int_sub_ovf(i95, 10) + guard_no_overflow(descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i187, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p54, p73, p101, p71, i189, i92, i95, descr=TargetToken(91217200)) """) def test_simple_loop_with_closure(self, spy, tmpdir): @@ -328,25 +408,25 @@ def test_mixed_stack(self, spy, tmpdir): """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=) - i102 = int_lt(i66, i49) - guard_true(i102, descr=) - i103 = int_mul_ovf(i66, i60) - guard_no_overflow(descr=) - i104 = int_add_ovf(i56, i103) - guard_no_overflow(descr=) - i106 = int_add(i66, 1) - p107 = force_token() + guard_not_invalidated(descr=) + i83 = int_lt(i59, i49) + guard_true(i83, descr=) + i84 = int_mul_ovf(i59, i55) + guard_no_overflow(descr=) + i85 = int_add_ovf(i54, i84) + guard_no_overflow(descr=) + i87 = int_add(i59, 1) + p88 = force_token() enter_portal_frame(1, 0) - i110 = int_add_ovf(i104, i92) - guard_no_overflow(descr=) + i91 = int_add_ovf(i85, i73) + guard_no_overflow(descr=) leave_portal_frame(1) - i113 = int_sub(i98, 1) - setfield_gc(ConstPtr(ptr114), i113, descr=) - i116 = int_le(i113, 0) - guard_false(i116, descr=) - i118 = arraylen_gc(p54, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, p11, i104, i106, p17, i110, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, p54, i60, i56, p77, p87, i92, i113, descr=TargetToken(106653920)) + i94 = int_sub(i79, 1) + setfield_gc(ConstPtr(ptr95), i94, descr=) + i97 = int_le(i94, 0) + guard_false(i97, descr=) + i99 = arraylen_gc(p66, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i85, i87, p17, i91, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, i55, i54, p66, p68, i73, i94, descr=TargetToken(100984816)) """) def test_global_class_access(self, spy, tmpdir): @@ -359,34 +439,32 @@ def test_global_class_access(self, spy, tmpdir): """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=), - i146 = int_lt(i69, i52), - guard_true(i146, descr=), - i147 = int_mul_ovf(i69, i63), - guard_no_overflow(descr=), - i148 = int_add_ovf(i59, i147), - guard_no_overflow(descr=), - i150 = int_add(i69, 1), - p153 = getarrayitem_gc_r(p88, 0, descr=), - guard_nonnull_class(p153, ConstClass(W_PointersObject), descr=) - p98 = force_token() - enter_portal_frame(4, 0), - p99 = force_token() - enter_portal_frame(4, 0), - leave_portal_frame(4), - p161 = getfield_gc_r(p153, descr=), - guard_value(p161, ConstPtr(ptr162), descr=), - p163 = getfield_gc_r(p153, descr=), - leave_portal_frame(4), - i166 = int_sub(i140, 1), - setfield_gc(ConstPtr(ptr167), i166, descr=), - setarrayitem_gc(p163, 0, p145, descr=), - i170 = int_le(i166, 0), - guard_false(i170, descr=), - i171 = arraylen_gc(p57, descr=), - i172 = arraylen_gc(p106, descr=), - i173 = arraylen_gc(p124, descr=), - jump(p0, p1, i2, p3, p4, p7, p8, p10, p13, i148, i150, p19, p145, p27, p29, p31, p33, p35, p37, p39, p41, p43, p45, i52, p57, i63, i59, p74, p75, i80, p88, p106, p108, p111, p124, p145, p83, i166, descr=TargetToken(166580560)) + guard_not_invalidated(descr=) + i112 = int_lt(i59, i49) + guard_true(i112, descr=) + i113 = int_mul_ovf(i59, i55) + guard_no_overflow(descr=) + i114 = int_add_ovf(i54, i113) + guard_no_overflow(descr=) + i116 = int_add(i59, 1) + p118 = getarrayitem_gc_r(p66, 0, descr=) + guard_nonnull_class(p118, 13692960, descr=) + p120 = force_token() + enter_portal_frame(1, 0) + p123 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + p127 = getfield_gc_r(p118, descr=) + guard_value(p127, ConstPtr(ptr128), descr=) + p129 = getfield_gc_r(p118, descr=) + leave_portal_frame(1) + i132 = int_sub(i106, 1) + setfield_gc(ConstPtr(ptr133), i132, descr=) + setarrayitem_gc(p129, 0, p111, descr=) + i136 = int_le(i132, 0) + guard_false(i136, descr=) + i138 = arraylen_gc(p66, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i114, i116, p17, p111, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, i55, i54, p66, p77, p79, i132, p111, descr=TargetToken(114551088)) """) @py.test.mark.skipif("'Flaky, check with pypy devs'") @@ -396,7 +474,7 @@ def test_benchFib(self, spy, tmpdir): 25 benchFib """) self.assert_matches(traces[0].bridges[-1], """ - guard_value(i7, 0, descr=), + guard_value(i7, 0, descr=) guard_not_invalidated(descr=), guard_value(p1, ConstPtr(ptr28), descr=), guard_value(i2, 0, descr=), @@ -558,46 +636,45 @@ def test_make_float(self, spy, tmpdir): if IS_64BIT: self.assert_matches(traces[1].loop, """ - guard_not_invalidated(descr=) - i124 = int_le(i115, 1000000) - guard_true(i124, descr=) - p125 = force_token() - enter_portal_frame(4, 0) - i129 = int_and(i115, 4294967295) - leave_portal_frame(4) - p131 = force_token() - enter_portal_frame(4, 0) - leave_portal_frame(4) - i136 = int_lshift(i129, 32) - i137 = int_or(i136, i129) - i139 = uint_rshift(i137, 63) - i141 = int_and(i137, 9218868437227405312) - i143 = uint_rshift(i141, 52) - i145 = int_and(i137, 4503599627370495) - guard_value(i143, 0, descr=) - f148 = call_f(ConstClass(_ll_1_cast_uint_to_float__Unsigned), i145, descr=) - i150 = float_eq(f148, 0.000000) - guard_false(i150, descr=) - f152 = float_sub(f148, f148) - i154 = float_eq(f152, 0.000000) - guard_true(i154, descr=) - f157 = call_f(ConstClass(ccall_ldexp), f148, -1074, descr=) - i160 = call_i(ConstClass(_ll_1_threadlocalref_get__INTLlT_Signed), 48, descr=) - f162 = float_add(f157, 11235582092889474423308157442431404585112356118389416079589380072358292237843810195794279832650471001320007117491962084853674360550901038905802964414967132773610493339054092829768888725077880882465817684505312860552384417646403930092119569408801702322709406917786643639996702871154982269052209770601514008576.000000) - i163 = float_eq(f162, f157) - guard_false(i163, descr=) - i164 = int_is_true(i160) - guard_false(i164, descr=) - i165 = int_is_true(i139) - guard_false(i165, descr=) - f167 = float_add(f157, 2.000000) - i169 = int_add(i115, 1) - i171 = int_sub(i119, 1) - setfield_gc(ConstPtr(ptr172), i171, descr=) - i174 = int_le(i171, 0) - guard_false(i174, descr=) - i175 = arraylen_gc(p55, descr=) - jump(p0, p1, i2, p3, p4, p7, p8, p10, i129, i169, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p55, p57, i171, descr=TargetToken(175413136)) + guard_not_invalidated(descr=) + i116 = int_le(i107, 1000000) + guard_true(i116, descr=) + p117 = force_token() + enter_portal_frame(1, 0) + i121 = int_and(i107, 4294967295) + leave_portal_frame(1) + p123 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + i128 = int_lshift(i121, 32) + i129 = int_or(i128, i121) + i131 = uint_rshift(i129, 63) + i133 = int_and(i129, 9218868437227405312) + i135 = uint_rshift(i133, 52) + i137 = int_and(i129, 4503599627370495) + guard_value(i135, 0, descr=) + f140 = call_f(ConstClass(_ll_1_cast_uint_to_float__Unsigned), i137, descr=) + i142 = float_eq(f140, 0.000000) + guard_false(i142, descr=) + f144 = float_sub(f140, f140) + i146 = float_eq(f144, 0.000000) + guard_true(i146, descr=) + f149 = call_f(ConstClass(ccall_ldexp), f140, -1074, descr=) + i152 = call_i(ConstClass(_ll_1_threadlocalref_get__INTLlT_Signed), 48, descr=) + f154 = float_add(f149, 11235582092889474423308157442431404585112356118389416079589380072358292237843810195794279832650471001320007117491962084853674360550901038905802964414967132773610493339054092829768888725077880882465817684505312860552384417646403930092119569408801702322709406917786643639996702871154982269052209770601514008576.000000) + i155 = float_eq(f154, f149) + guard_false(i155, descr=) + i156 = int_is_true(i152) + guard_false(i156, descr=) + i157 = int_is_true(i131) + guard_false(i157, descr=) + f159 = float_add(f149, 2.000000) + i161 = int_add(i107, 1) + i163 = int_sub(i111, 1) + setfield_gc(ConstPtr(ptr164), i163, descr=) + i166 = int_le(i163, 0) + guard_false(i166, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, i121, i161, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p52, i163, descr=TargetToken(97308768)) """) else: self.assert_matches(traces[1].loop, @@ -642,7 +719,6 @@ def test_make_float(self, spy, tmpdir): setfield_gc(ConstPtr(ptr192), i191, descr=) i194 = int_le(i191, 0) guard_false(i194, descr=) - i195 = arraylen_gc(p55, descr=) jump(p0, p1, i2, p3, p4, p7, p8, p10, i125, i189, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p55, p57, i191, descr=TargetToken(155445900)) """) @@ -657,38 +733,36 @@ def test_new_large_int(self, spy, tmpdir): if IS_64BIT: self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=) - i130 = int_le(i120, 1000000) - guard_true(i130, descr=) - p131 = force_token() - enter_portal_frame(4, 0) - p134 = force_token() - enter_portal_frame(4, 0) - leave_portal_frame(4) - leave_portal_frame(4) - p140 = new_array_clear(8, descr=) - p144 = call_r(ConstClass(frombytes), p140, ConstPtr(ptr142), 0, descr=) - guard_no_exception(descr=) - p147 = call_r(ConstClass(rbigint.add), p144, ConstPtr(ptr146), descr=) - guard_no_exception(descr=) - i148 = getfield_gc_i(p147, descr=) - i150 = int_ge(i148, 0) - guard_true(i150, descr=) - i151 = getfield_gc_i(p147, descr=) - i153 = int_le(i151, 2) - guard_true(i153, descr=) - i155 = call_i(ConstClass(rbigint._touint_helper), p147, descr=) - guard_no_exception(descr=) - i157 = int_ge(i155, 0) - guard_true(i157, descr=) - i159 = int_add(i120, 1) - i161 = int_sub(i124, 1) - setfield_gc(ConstPtr(ptr162), i161, descr=) - i164 = int_le(i161, 0) - guard_false(i164, descr=) - i165 = arraylen_gc(p55, descr=) - i166 = arraylen_gc(p76, descr=) - jump(p0, p1, i2, p3, p4, p7, p8, p10, i155, i159, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p55, p57, p59, p76, p78, i161, descr=TargetToken(176747472)) + guard_not_invalidated(descr=) + i118 = int_le(i108, 1000000) + guard_true(i118, descr=) + p119 = force_token() + enter_portal_frame(1, 0) + p122 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + leave_portal_frame(1) + p128 = new_array_clear(8, descr=) + p132 = call_r(ConstClass(frombytes), p128, ConstPtr(ptr130), 0, descr=) + guard_no_exception(descr=) + p135 = call_r(ConstClass(rbigint.add), p132, ConstPtr(ptr134), descr=) + guard_no_exception(descr=) + i136 = getfield_gc_i(p135, descr=) + i138 = int_ge(i136, 0) + guard_true(i138, descr=) + i139 = getfield_gc_i(p135, descr=) + i141 = int_le(i139, 2) + guard_true(i141, descr=) + i143 = call_i(ConstClass(rbigint._touint_helper), p135, descr=) + guard_no_exception(descr=) + i145 = int_ge(i143, 0) + guard_true(i145, descr=) + i147 = int_add(i108, 1) + i149 = int_sub(i112, 1) + setfield_gc(ConstPtr(ptr150), i149, descr=) + i152 = int_le(i149, 0) + guard_false(i152, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, i143, i147, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p52, p54, p68, i149, descr=TargetToken(100027392)) """) else: self.assert_matches(traces[0].loop, @@ -707,8 +781,6 @@ def test_new_large_int(self, spy, tmpdir): setfield_gc(ConstPtr(ptr123), i122, descr=) i125 = int_le(i122, 0) guard_false(i125, descr=) - i127 = arraylen_gc(p55, descr=) - i128 = arraylen_gc(p76, descr=) jump(p0, p1, i2, p3, p4, p7, p8, p10, i120, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, p55, p57, p59, p76, p78, i122, descr=TargetToken(156580140)) """) @@ -722,73 +794,70 @@ def test_large_negation0(self, spy, tmpdir): """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=) - i129 = int_le(i120, 1000000) - guard_true(i129, descr=) + guard_not_invalidated(descr=) + i121 = int_le(i112, 1000000) + guard_true(i121, descr=) + p122 = getfield_gc_r(p11, descr=) + guard_value(p122, ConstPtr(ptr123), descr=) + p125 = getfield_gc_r(ConstPtr(ptr124), descr=) + guard_value(p125, ConstPtr(ptr126), descr=) + p127 = force_token() + enter_portal_frame(1, 0) p131 = getfield_gc_r(ConstPtr(ptr130), descr=) - guard_value(p131, ConstPtr(ptr132), descr=) - p134 = getfield_gc_r(ConstPtr(ptr133), descr=) - guard_value(p134, ConstPtr(ptr135), descr=) - p136 = force_token() - enter_portal_frame(4, 0) + guard_value(p131, ConstPtr(ptr132), descr=) + p134 = getfield_gc_r(ConstPtr(ptr133), descr=) + guard_nonnull_class(p134, ConstClass(W_FixedPointersObject), descr=) + i136 = getfield_gc_i(p11, descr=) + i138 = cond_call_value_i(i136, ConstClass(calculate_exposed_size_for_big_int), p64, descr=) + guard_no_exception(descr=) p140 = getfield_gc_r(ConstPtr(ptr139), descr=) - guard_value(p140, ConstPtr(ptr141), descr=) - p143 = getfield_gc_r(ConstPtr(ptr142), descr=) - p145 = getarrayitem_gc_r(p143, 1, descr=) - guard_nonnull_class(p145, ConstClass(W_PointersObject), descr=) - i146 = getfield_gc_i(p11, descr=) - i148 = cond_call_value_i(i146, ConstClass(calculate_exposed_size_for_big_int), p69, descr=) - guard_no_exception(descr=) - p150 = getfield_gc_r(ConstPtr(ptr149), descr=) - setfield_gc(p11, i148, descr=) - guard_value(p150, ConstPtr(ptr151), descr=) - p152 = getfield_gc_r(p150, descr=) - guard_value(p152, ConstPtr(ptr153), descr=) - p154 = getfield_gc_r(p145, descr=) - guard_value(p154, ConstPtr(ptr155), descr=) - p156 = getfield_gc_r(p154, descr=) - guard_value(p156, ConstPtr(ptr157), descr=) - p159 = getfield_gc_r(ConstPtr(ptr158), descr=) - guard_value(p159, ConstPtr(ptr160), descr=) - p161 = force_token() - enter_portal_frame(4, 0) - i165 = int_lt(i148, 0) - guard_false(i165, descr=) - p166 = new_array_clear(i148, descr=) - p168 = getfield_gc_r(ConstPtr(ptr167), descr=) - guard_value(p168, ConstPtr(ptr169), descr=) - leave_portal_frame(4) - p171 = force_token() - enter_portal_frame(4, 0) - p175 = getfield_gc_r(ConstPtr(ptr174), descr=) - guard_value(p175, ConstPtr(ptr176), descr=) - p177 = force_token() - enter_portal_frame(4, 0) - leave_portal_frame(4) - i182 = int_sub(i148, 1) - i183 = int_le(i148, i182) - guard_false(i183, descr=) - p184 = force_token() - p185 = new_with_vtable(descr=) - p186 = new_with_vtable(descr=) - setfield_gc(p185, ConstPtr(ptr187), descr=) - setfield_gc(p0, p184, descr=) - setfield_gc(p185, 9223372036854775807, descr=) - setfield_gc(p185, p166, descr=) - setfield_gc(p185, p186, descr=) - call_may_force_n(ConstClass(_replace_from_to_trampoline__v77___simple_call__function__r), 0, i182, 0, p185, p13, descr=) - guard_not_forced(descr=) - guard_no_exception(descr=) - guard_not_invalidated(descr=) - leave_portal_frame(4) - leave_portal_frame(4) - i195 = int_add(i120, 1) - i197 = getfield_gc_i(ConstPtr(ptr196), descr=) - i199 = int_sub(i197, 1) - setfield_gc(ConstPtr(ptr200), i199, descr=) - i202 = int_le(i199, 0) - guard_false(i202, descr=) - jump(p0, p1, i2, p3, p4, p7, p8, p10, p13, i195, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, descr=TargetToken(193745952)) + setfield_gc(p11, i138, descr=) + guard_value(p140, ConstPtr(ptr141), descr=) + p142 = getfield_gc_r(p134, descr=) + guard_value(p142, ConstPtr(ptr143), descr=) + p144 = getfield_gc_r(p142, descr=) + guard_value(p144, ConstPtr(ptr145), descr=) + p147 = getfield_gc_r(ConstPtr(ptr146), descr=) + guard_value(p147, ConstPtr(ptr148), descr=) + p149 = force_token() + enter_portal_frame(1, 0) + i153 = int_lt(i138, 0) + guard_false(i153, descr=) + p154 = new_array_clear(i138, descr=) + p156 = getfield_gc_r(ConstPtr(ptr155), descr=) + guard_value(p156, ConstPtr(ptr157), descr=) + leave_portal_frame(1) + p159 = force_token() + enter_portal_frame(1, 0) + p163 = getfield_gc_r(ConstPtr(ptr162), descr=) + guard_value(p163, ConstPtr(ptr164), descr=) + p165 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + i170 = int_sub(i138, 1) + i171 = int_le(i138, i170) + guard_false(i171, descr=) + p172 = force_token() + p173 = new_with_vtable(descr=) + p174 = new_with_vtable(descr=) + setfield_gc(p173, ConstPtr(ptr175), descr=) + setfield_gc(p0, p172, descr=) + setfield_gc(p173, 0, descr=) + setfield_gc(p173, p154, descr=) + setfield_gc(p173, p174, descr=) + call_may_force_n(ConstClass(_replace_from_to_trampoline__v133___simple_call__function__), 0, i170, 0, p173, p11, descr=) + guard_not_forced(descr=) + guard_no_exception(descr=) + guard_not_invalidated(descr=) + leave_portal_frame(1) + leave_portal_frame(1) + i183 = int_add(i112, 1) + i185 = getfield_gc_i(ConstPtr(ptr184), descr=) + i187 = int_sub(i185, 1) + setfield_gc(ConstPtr(ptr188), i187, descr=) + i190 = int_le(i187, 0) + guard_false(i190, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i183, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p64, descr=TargetToken(88378128)) """) # This is a bit full, unfortunately, but there shouldn't be any BlockClosure accesses in this loop @@ -802,102 +871,93 @@ def test_interval_do(self, spy, tmpdir): """) self.assert_matches(traces[0].loop, """ - guard_not_invalidated(descr=) - i176 = int_lt(i66, i49) - guard_true(i176, descr=) - p177 = getfield_gc_r(p6, descr=) - guard_value(p177, ConstPtr(ptr178), descr=) - p179 = getfield_gc_r(p6, descr=) - i181 = getarrayitem_gc_i(p179, 0, descr=) - i183 = int_eq(i181, 9223372036854775807) - guard_false(i183, descr=) - i185 = getarrayitem_gc_i(p179, 2, descr=) - i187 = int_eq(i185, 9223372036854775807) - guard_false(i187, descr=) - i188 = int_mul_ovf(i66, i185) - guard_no_overflow(descr=) - i189 = int_add_ovf(i181, i188) - guard_no_overflow(descr=) - i191 = int_add(i66, 1) - p193 = getfield_gc_r(ConstPtr(ptr192), descr=) - guard_value(p193, ConstPtr(ptr194), descr=) - p196 = getarrayitem_gc_r(p77, 0, descr=) - guard_nonnull_class(p196, ConstClass(W_LargeIntegerBig), descr=) - p199 = getarrayitem_gc_r(p77, 1, descr=) - guard_nonnull_class(p199, ConstClass(W_PointersObject), descr=) - p201 = force_token() + guard_not_invalidated(descr=) + i153 = int_lt(i59, i49) + guard_true(i153, descr=) + p154 = getfield_gc_r(p6, descr=) + guard_value(p154, ConstPtr(ptr155), descr=) + i156 = getfield_gc_i(p6, descr=) + i157 = getfield_gc_i(p6, descr=) + i158 = int_mul_ovf(i59, i157) + guard_no_overflow(descr=) + i159 = int_add_ovf(i156, i158) + guard_no_overflow(descr=) + i161 = int_add(i59, 1) + p163 = getarrayitem_gc_r(p66, 0, descr=) + guard_nonnull_class(p163, ConstClass(W_LargeIntegerBig), descr=) + p166 = getarrayitem_gc_r(p66, 1, descr=) + guard_nonnull_class(p166, 13692960, descr=) + p168 = force_token() enter_portal_frame(1, 0) - p204 = getfield_gc_r(p196, descr=) - guard_value(p204, ConstPtr(ptr205), descr=) - p207 = getfield_gc_r(ConstPtr(ptr206), descr=) - guard_value(p207, ConstPtr(ptr208), descr=) - p209 = force_token() + p171 = getfield_gc_r(p163, descr=) + guard_value(p171, ConstPtr(ptr172), descr=) + p174 = getfield_gc_r(ConstPtr(ptr173), descr=) + guard_value(p174, ConstPtr(ptr175), descr=) + p176 = force_token() enter_portal_frame(1, 0) - p213 = getfield_gc_r(ConstPtr(ptr212), descr=) - guard_value(p213, ConstPtr(ptr214), descr=) - p216 = getfield_gc_r(ConstPtr(ptr215), descr=) - p218 = getarrayitem_gc_r(p216, 1, descr=) - guard_nonnull_class(p218, ConstClass(W_PointersObject), descr=) - i220 = getfield_gc_i(p196, descr=) - p221 = getfield_gc_r(p196, descr=) - i223 = cond_call_value_i(i220, ConstClass(calculate_exposed_size_for_big_int), p221, descr=) - guard_no_exception(descr=) - p225 = getfield_gc_r(ConstPtr(ptr224), descr=) - setfield_gc(p196, i223, descr=) - guard_value(p225, ConstPtr(ptr226), descr=) - p227 = getfield_gc_r(p225, descr=) - guard_value(p227, ConstPtr(ptr228), descr=) - p229 = getfield_gc_r(p218, descr=) - guard_value(p229, ConstPtr(ptr230), descr=) - p231 = getfield_gc_r(p229, descr=) - guard_value(p231, ConstPtr(ptr232), descr=) - p234 = getfield_gc_r(ConstPtr(ptr233), descr=) - guard_value(p234, ConstPtr(ptr235), descr=) - p236 = force_token() + p180 = getfield_gc_r(ConstPtr(ptr179), descr=) + guard_value(p180, ConstPtr(ptr181), descr=) + p183 = getfield_gc_r(ConstPtr(ptr182), descr=) + guard_nonnull_class(p183, ConstClass(W_FixedPointersObject), descr=) + i185 = getfield_gc_i(p163, descr=) + p186 = getfield_gc_r(p163, descr=) + i188 = cond_call_value_i(i185, ConstClass(calculate_exposed_size_for_big_int), p186, descr=) + guard_no_exception(descr=) + p190 = getfield_gc_r(ConstPtr(ptr189), descr=) + setfield_gc(p163, i188, descr=) + guard_value(p190, ConstPtr(ptr191), descr=) + p192 = getfield_gc_r(p183, descr=) + guard_value(p192, ConstPtr(ptr193), descr=) + p194 = getfield_gc_r(p192, descr=) + guard_value(p194, ConstPtr(ptr195), descr=) + p197 = getfield_gc_r(ConstPtr(ptr196), descr=) + guard_value(p197, ConstPtr(ptr198), descr=) + p199 = force_token() enter_portal_frame(1, 0) - i240 = int_lt(i223, 0) - guard_false(i240, descr=) - p241 = new_array_clear(i223, descr=) - p243 = getfield_gc_r(ConstPtr(ptr242), descr=) - guard_value(p243, ConstPtr(ptr244), descr=) + i203 = int_lt(i188, 0) + guard_false(i203, descr=) + p204 = new_array_clear(i188, descr=) + p206 = getfield_gc_r(ConstPtr(ptr205), descr=) + guard_value(p206, ConstPtr(ptr207), descr=) leave_portal_frame(1) - p246 = force_token() + p209 = force_token() enter_portal_frame(1, 0) - p250 = getfield_gc_r(ConstPtr(ptr249), descr=) - guard_value(p250, ConstPtr(ptr251), descr=) - p252 = force_token() + p213 = getfield_gc_r(ConstPtr(ptr212), descr=) + guard_value(p213, ConstPtr(ptr214), descr=) + p215 = force_token() enter_portal_frame(1, 0) leave_portal_frame(1) - i257 = int_sub(i223, 1) - i258 = int_le(i223, i257) - guard_false(i258, descr=) - p259 = force_token() - p260 = new_with_vtable(descr=) - p261 = new_with_vtable(descr=) - setfield_gc(p260, p261, descr=) - setfield_gc(p260, ConstPtr(ptr262), descr=) - setfield_gc(p0, p259, descr=) - setfield_gc(p260, 0, descr=) - setfield_gc(p260, p241, descr=) - call_may_force_n(ConstClass(_replace_from_to_trampoline__v124___simple_call__function__), 0, i257, 0, p260, p196, descr=) - guard_not_forced(descr=) - guard_no_exception(descr=) - guard_not_invalidated(descr=) + i220 = int_sub(i188, 1) + i221 = int_le(i188, i220) + guard_false(i221, descr=) + p222 = force_token() + p223 = new_with_vtable(descr=) + p224 = new_with_vtable(descr=) + setfield_gc(p223, p224, descr=) + setfield_gc(p223, 0, descr=) + setfield_gc(p223, ConstPtr(ptr226), descr=) + setfield_gc(p0, p222, descr=) + setfield_gc(p223, p204, descr=) + call_may_force_n(ConstClass(_replace_from_to_trampoline__v133___simple_call__function__), 0, i220, 0, p223, p163, descr=) + guard_not_forced(descr=) + guard_no_exception(descr=) + guard_not_invalidated(descr=) leave_portal_frame(1) leave_portal_frame(1) - p269 = getfield_gc_r(p199, descr=) - guard_value(p269, ConstPtr(ptr270), descr=) - p272 = getfield_gc_r(ConstPtr(ptr271), descr=) - guard_value(p272, ConstPtr(ptr273), descr=) - p274 = getfield_gc_r(p199, descr=) + p232 = getfield_gc_r(p166, descr=) + guard_value(p232, ConstPtr(ptr233), descr=) + p235 = getfield_gc_r(ConstPtr(ptr234), descr=) + guard_value(p235, ConstPtr(ptr236), descr=) + p237 = getfield_gc_r(p166, descr=) leave_portal_frame(1) - i277 = getfield_gc_i(ConstPtr(ptr276), descr=) - i279 = int_sub(i277, 1) - setfield_gc(ConstPtr(ptr280), i279, descr=) - setarrayitem_gc(p274, 0, p260, descr=) - i283 = int_le(i279, 0) - guard_false(i283, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, p11, i189, i191, p17, p260, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, p77, p72, descr=TargetToken(104558688)) + i240 = getfield_gc_i(ConstPtr(ptr239), descr=) + i242 = int_sub(i240, 1) + setfield_gc(ConstPtr(ptr243), i242, descr=) + setarrayitem_gc(p237, 0, p223, descr=) + i246 = int_le(i242, 0) + guard_false(i246, descr=) + i247 = arraylen_gc(p66, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i159, i161, p17, p223, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, p66, p64, descr=TargetToken(108721024)) """) def test_dnu(self, spy, tmpdir): @@ -1044,37 +1104,36 @@ def test_identity_hash_fresh_object(self, spy, tmpdir): """) # important: there shouldn't be a setfield inst_hash or an abort, just a getfield for the hash value and a cond_call self.assert_matches(traces[-1].loop, """ - guard_not_invalidated(descr=) - i116 = int_le(i107, 10000) - guard_true(i116, descr=) - p117 = force_token() + guard_not_invalidated(descr=) + i103 = int_le(i94, 10000) + guard_true(i103, descr=) + p104 = force_token() enter_portal_frame(1, 0) - i120 = int_add_ovf(i107, i71) - guard_no_overflow(descr=) - i122 = int_sub(i120, 1) - i123 = int_gt(i122, i78) - guard_false(i123, descr=) - i125 = int_sub(i122, 1) - i126 = uint_lt(i125, i91) - guard_true(i126, descr=) - i128 = int_lt(i125, 0) - guard_false(i128, descr=) - p129 = getarrayitem_gc_r(p90, i125, descr=) - guard_nonnull_class(p129, ConstClass(W_PointersObject), descr=) + i107 = int_add_ovf(i94, i62) + guard_no_overflow(descr=) + i109 = int_sub(i107, 1) + i110 = int_gt(i109, i66) + guard_false(i110, descr=) + i112 = int_sub(i109, 1) + i113 = uint_lt(i112, i78) + guard_true(i113, descr=) + i115 = int_lt(i112, 0) + guard_false(i115, descr=) + p116 = getarrayitem_gc_r(p77, i112, descr=) + guard_nonnull_class(p116, ConstClass(W_FixedPointersObject), descr=) leave_portal_frame(1) - p132 = getfield_gc_r(p129, descr=) - guard_value(p132, ConstPtr(ptr133), descr=) - i134 = getfield_gc_i(p129, descr=) - i136 = cond_call_value_i(i134, ConstClass(calculate_and_cache), p129, descr=) - guard_no_exception(descr=) - guard_not_invalidated(descr=) - i138 = int_add(i107, 1) - i140 = int_sub(i111, 1) - setfield_gc(ConstPtr(ptr141), i140, descr=) - i143 = int_le(i140, 0) - guard_false(i143, descr=) - i145 = arraylen_gc(p67, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, p11, p13, p15, p17, i138, p25, p27, p29, p31, p33, p35, p37, p39, p41, p67, p69, i71, p76, i78, p81, i91, p90, i140, descr=TargetToken(90449984)) + p119 = getfield_gc_r(p116, descr=) + guard_value(p119, ConstPtr(ptr120), descr=) + i121 = getfield_gc_i(p116, descr=) + i123 = cond_call_value_i(i121, ConstClass(calculate_and_cache), p116, descr=) + guard_no_exception(descr=) + guard_not_invalidated(descr=) + i125 = int_add(i94, 1) + i127 = int_sub(i98, 1) + setfield_gc(ConstPtr(ptr128), i127, descr=) + i130 = int_le(i127, 0) + guard_false(i130, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, p13, p15, p17, i125, p25, p27, p29, p31, p33, p35, p37, p39, p41, i62, i66, p68, i78, p77, i127, descr=TargetToken(78064480)) """) def test_nested_closure_loop_call(self, spy, tmpdir): @@ -1087,170 +1146,187 @@ def test_nested_closure_loop_call(self, spy, tmpdir): """) # important: there should be two loops, with the outer calling the inner, and in between an entry bridge to the inner loop abs call self.assert_matches(traces[-3].loop, """ - guard_not_invalidated(descr=) - i106 = int_lt(i66, i49) - guard_true(i106, descr=) - i107 = int_mul_ovf(i66, i60) - guard_no_overflow(descr=) - i108 = int_add_ovf(i56, i107) - guard_no_overflow(descr=) - i110 = int_add(i66, 1) - p111 = force_token() + guard_not_invalidated(descr=) + i99 = int_lt(i59, i49) + guard_true(i99, descr=) + i100 = int_mul_ovf(i59, i55) + guard_no_overflow(descr=) + i101 = int_add_ovf(i54, i100) + guard_no_overflow(descr=) + i103 = int_add(i59, 1) + p104 = force_token() enter_portal_frame(1, 0) - p114 = force_token() + p107 = force_token() enter_portal_frame(1, 0) - i118 = int_lt(i108, 0) - guard_false(i118, descr=) + i111 = int_lt(i101, 0) + guard_false(i111, descr=) leave_portal_frame(1) leave_portal_frame(1) - i122 = int_sub(i102, 1) - setfield_gc(ConstPtr(ptr123), i122, descr=) - i125 = int_le(i122, 0) - guard_false(i125, descr=) - i127 = arraylen_gc(p54, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, p11, i108, i110, p17, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, p54, i60, i56, i122, descr=TargetToken(111727328)) + i115 = int_sub(i95, 1) + setfield_gc(ConstPtr(ptr116), i115, descr=) + i118 = int_le(i115, 0) + guard_false(i118, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i101, i103, p17, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, i55, i54, i115, descr=TargetToken(108261712)) """) assert len(traces[-2].setup) > 0 self.assert_matches(traces[-1].loop, """ - guard_not_invalidated(descr=) - i212 = int_le(i203, 2000) - guard_true(i212, descr=) - p214 = getfield_gc_r(ConstPtr(ptr213), descr=) - guard_value(p214, ConstPtr(ptr215), descr=) - p217 = getfield_gc_r(ConstPtr(ptr216), descr=) - guard_value(p217, ConstPtr(ptr218), descr=) - p219 = force_token() + guard_not_invalidated(descr=) + i184 = int_le(i175, 2000) + guard_true(i184, descr=) + p186 = getfield_gc_r(ConstPtr(ptr185), descr=) + guard_value(p186, ConstPtr(ptr187), descr=) + p188 = force_token() enter_portal_frame(1, 0) - p223 = getfield_gc_r(ConstPtr(ptr222), descr=) - guard_value(p223, ConstPtr(ptr224), descr=) - p226 = getfield_gc_r(ConstPtr(ptr225), descr=) - p228 = getarrayitem_gc_r(p226, 1, descr=) - guard_nonnull_class(p228, ConstClass(W_PointersObject), descr=) - p230 = getfield_gc_r(p228, descr=) - guard_value(p230, ConstPtr(ptr231), descr=) - p232 = getfield_gc_r(p230, descr=) - guard_value(p232, ConstPtr(ptr233), descr=) - p235 = getfield_gc_r(ConstPtr(ptr234), descr=) - guard_value(p235, ConstPtr(ptr236), descr=) - p237 = force_token() + p192 = getfield_gc_r(ConstPtr(ptr191), descr=) + guard_value(p192, ConstPtr(ptr193), descr=) + p195 = getfield_gc_r(ConstPtr(ptr194), descr=) + guard_nonnull_class(p195, ConstClass(W_FixedPointersObject), descr=) + p197 = getfield_gc_r(p195, descr=) + guard_value(p197, ConstPtr(ptr198), descr=) + p199 = getfield_gc_r(p197, descr=) + guard_value(p199, ConstPtr(ptr200), descr=) + p202 = getfield_gc_r(ConstPtr(ptr201), descr=) + guard_value(p202, ConstPtr(ptr203), descr=) + p204 = force_token() enter_portal_frame(1, 0) - p241 = getfield_gc_r(ConstPtr(ptr240), descr=) - guard_value(p241, ConstPtr(ptr242), descr=) - p243 = getfield_gc_r(p241, descr=) - guard_value(p243, ConstPtr(ptr244), descr=) - p246 = getfield_gc_r(ConstPtr(ptr245), descr=) - guard_value(p246, ConstPtr(ptr247), descr=) - p248 = force_token() + p208 = getfield_gc_r(ConstPtr(ptr207), descr=) + guard_value(p208, ConstPtr(ptr209), descr=) + p211 = getfield_gc_r(ConstPtr(ptr210), descr=) + guard_value(p211, ConstPtr(ptr212), descr=) + p213 = force_token() enter_portal_frame(1, 0) leave_portal_frame(1) leave_portal_frame(1) leave_portal_frame(1) - p254 = getfield_gc_r(p1, descr=) - guard_nonnull_class(p254, ConstClass(ContextPartShadow), descr=) - i256 = ptr_eq(p254, p0) - guard_true(i256, descr=) - p257 = force_token() + p219 = getfield_gc_r(p1, descr=) + guard_nonnull_class(p219, ConstClass(ContextPartShadow), descr=) + i221 = ptr_eq(p219, p0) + guard_true(i221, descr=) + p222 = force_token() + enter_portal_frame(1, 0) + p225 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + p229 = force_token() enter_portal_frame(1, 0) - p260 = force_token() + p232 = force_token() enter_portal_frame(1, 0) leave_portal_frame(1) - p264 = force_token() + leave_portal_frame(1) + i238 = int_sub(i179, 1) + setfield_gc(ConstPtr(ptr239), i238, descr=) + i241 = int_le(i238, 0) + guard_false(i241, descr=) + p242 = force_token() + p243 = new_with_vtable(descr=) + p244 = new_with_vtable(descr=) + setfield_gc(p244, p222, descr=) + setfield_gc(p244, ConstPtr(null), descr=) + p246 = new_array_clear(17, descr=) + p247 = new_with_vtable(descr=) + setfield_gc(p247, 0, descr=) + setfield_gc(p247, ConstPtr(ptr249), descr=) + setfield_gc(p247, 268435503, descr=) + setfield_gc(p247, ConstPtr(ptr251), descr=) + setfield_gc(p247, p1, descr=) + setfield_gc(p247, p6, descr=) + setfield_gc(p247, ConstPtr(ptr252), descr=) + setarrayitem_gc(p246, 0, p247, descr=) + p254 = new_with_vtable(descr=) + setfield_gc(p254, 1, descr=) + setarrayitem_gc(p246, 1, p254, descr=) + p257 = new_with_vtable(descr=) + setfield_gc(p257, 1, descr=) + setarrayitem_gc(p246, 2, p257, descr=) + p260 = new_with_vtable(descr=) + setfield_gc(p260, 1000, descr=) + setarrayitem_gc(p246, 3, p260, descr=) + p263 = new_with_vtable(descr=) + setfield_gc(p263, 1, descr=) + setarrayitem_gc(p246, 4, p263, descr=) + setarrayitem_gc(p246, 5, ConstPtr(ptr267), descr=) + setarrayitem_gc(p246, 6, ConstPtr(ptr269), descr=) + p270 = new_with_vtable(descr=) + setfield_gc(p270, 0, descr=) + setfield_gc(p270, ConstPtr(null), descr=) + setfield_gc(p270, ConstPtr(ptr273), descr=) + setfield_gc(p270, ConstPtr(null), descr=) + setfield_gc(p270, ConstPtr(null), descr=) + setfield_gc(p270, 1, descr=) + setfield_gc(p270, 1000, descr=) + setfield_gc(p270, 1, descr=) + setfield_gc(p270, ConstPtr(null), descr=) + setfield_gc(p270, 3, descr=) + setfield_gc(p243, ConstPtr(ptr281), descr=) + setfield_gc(p0, p242, descr=) + setfield_gc(p243, p244, descr=) + setfield_gc(p243, 1090519045, descr=) + setfield_gc(p243, p246, descr=) + setfield_gc(p243, ConstPtr(ptr283), descr=) + setfield_gc(p243, p270, descr=) + setfield_gc(p243, ConstPtr(null), descr=) + setfield_gc(p243, 23, descr=) + setfield_gc(p243, ConstPtr(null), descr=) + setfield_gc(p243, ConstPtr(null), descr=) + call_assembler_n(p243, descr=) + guard_not_forced(descr=) + keepalive(p243) + p289 = guard_exception(13757968, descr=) + i290 = ptr_eq(p243, p0) + guard_false(i290, descr=) + p291 = getfield_gc_r(p243, descr=) + i293 = ptr_ne(p291, ConstPtr(null)) + cond_call(i293, 6399472, p243, descr=) + i295 = getfield_gc_i(p243, descr=) + guard_value(i295, 1090519067, descr=) + guard_not_invalidated(descr=) + p297 = getfield_gc_r(p243, descr=) + guard_isnull(p297, descr=) + p298 = getfield_gc_r(p243, descr=) + guard_value(p298, ConstPtr(ptr299), descr=) + setfield_gc(p243, ConstPtr(null), descr=) + setfield_gc(p243, 1094713343, descr=) + guard_class(p289, 13757968, descr=) + p303 = getfield_gc_r(p289, descr=) + setfield_gc(p243, 20971519, descr=) + setfield_gc(p244, ConstPtr(null), descr=) + guard_nonnull(p303, descr=) + i306 = int_add(i175, 1) + i308 = getfield_gc_i(ConstPtr(ptr307), descr=) + i310 = int_sub(i308, 1) + setfield_gc(ConstPtr(ptr311), i310, descr=) + i313 = int_le(i310, 0) + guard_false(i313, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, i306, p17, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, i310, descr=TargetToken(104224336)) + """) + + def test_object_access_with_integer(self, spy, tmpdir): + traces = self.run(spy, tmpdir, """ + | o | + o := OrderedCollection new. + o add: 'foo'; add: 'bar'. + 1 to: 10000 do: [:i | o first]. + """) + self.assert_matches(traces[0].loop, """ + guard_not_invalidated(descr=) + i96 = int_le(i87, 10000) + guard_true(i96, descr=) + p97 = force_token() enter_portal_frame(1, 0) - p267 = force_token() + p100 = force_token() enter_portal_frame(1, 0) + p103 = getarrayitem_gc_r(p77, i76, descr=) + guard_nonnull_class(p103, ConstClass(W_BytesObject), descr=) leave_portal_frame(1) leave_portal_frame(1) - i273 = int_sub(i207, 1) - setfield_gc(ConstPtr(ptr274), i273, descr=) - i276 = int_le(i273, 0) - guard_false(i276, descr=) - p277 = force_token() - p278 = new_with_vtable(descr=) - p279 = new_with_vtable(descr=) - setfield_gc(p279, p257, descr=) - setfield_gc(p279, ConstPtr(null), descr=) - p281 = new_array_clear(17, descr=) - p282 = new_with_vtable(descr=) - setfield_gc(p282, 0, descr=) - setfield_gc(p282, 1, descr=) - setfield_gc(p282, ConstPtr(ptr285), descr=) - setfield_gc(p282, 47, descr=) - setfield_gc(p282, ConstPtr(ptr287), descr=) - setfield_gc(p282, p1, descr=) - setfield_gc(p282, p6, descr=) - setfield_gc(p282, ConstPtr(ptr288), descr=) - setarrayitem_gc(p281, 0, p282, descr=) - p290 = new_with_vtable(descr=) - setfield_gc(p290, 1, descr=) - setarrayitem_gc(p281, 1, p290, descr=) - p293 = new_with_vtable(descr=) - setfield_gc(p293, 1, descr=) - setarrayitem_gc(p281, 2, p293, descr=) - p296 = new_with_vtable(descr=) - setfield_gc(p296, 1000, descr=) - setarrayitem_gc(p281, 3, p296, descr=) - p299 = new_with_vtable(descr=) - setfield_gc(p299, 1, descr=) - setarrayitem_gc(p281, 4, p299, descr=) - setarrayitem_gc(p281, 5, ConstPtr(ptr303), descr=) - setarrayitem_gc(p281, 6, ConstPtr(ptr305), descr=) - setarrayitem_gc(p281, 7, ConstPtr(ptr307), descr=) - setarrayitem_gc(p281, 8, ConstPtr(ptr309), descr=) - setarrayitem_gc(p281, 9, ConstPtr(ptr311), descr=) - setarrayitem_gc(p281, 10, ConstPtr(ptr313), descr=) - setarrayitem_gc(p281, 11, ConstPtr(ptr315), descr=) - setarrayitem_gc(p281, 12, ConstPtr(ptr317), descr=) - setarrayitem_gc(p281, 13, ConstPtr(ptr319), descr=) - setarrayitem_gc(p281, 14, ConstPtr(ptr321), descr=) - setarrayitem_gc(p281, 15, ConstPtr(ptr323), descr=) - setarrayitem_gc(p281, 16, ConstPtr(ptr325), descr=) - p326 = new_with_vtable(descr=) - setfield_gc(p326, 0, descr=) - p329 = new_array(3, descr=) - setarrayitem_gc(p329, 0, 1, descr=) - setarrayitem_gc(p329, 1, 1000, descr=) - setarrayitem_gc(p329, 2, 1, descr=) - setfield_gc(p326, p329, descr=) - setfield_gc(p326, ConstPtr(ptr336), descr=) - setfield_gc(p278, ConstPtr(ptr337), descr=) - setfield_gc(p0, p277, descr=) - setfield_gc(p278, p279, descr=) - setfield_gc(p278, 1090519045, descr=) - setfield_gc(p278, p281, descr=) - setfield_gc(p278, ConstPtr(ptr339), descr=) - setfield_gc(p278, p326, descr=) - setfield_gc(p278, ConstPtr(null), descr=) - setfield_gc(p278, 23, descr=) - setfield_gc(p278, ConstPtr(null), descr=) - setfield_gc(p278, ConstPtr(null), descr=) - call_assembler_n(p278, descr=) - guard_not_forced(descr=) - keepalive(p278) - p345 = guard_exception(13592624, descr=) - i346 = ptr_eq(p278, p0) - guard_false(i346, descr=) - p347 = getfield_gc_r(p278, descr=) - i349 = ptr_ne(p347, ConstPtr(null)) - cond_call(i349, 6397552, p278, descr=) - i351 = getfield_gc_i(p278, descr=) - guard_value(i351, 1090519067, descr=) - guard_not_invalidated(descr=) - p353 = getfield_gc_r(p278, descr=) - guard_isnull(p353, descr=) - p354 = getfield_gc_r(p278, descr=) - guard_value(p354, ConstPtr(ptr355), descr=) - setfield_gc(p278, ConstPtr(null), descr=) - setfield_gc(p278, 1094713343, descr=) - guard_class(p345, 13592624, descr=) - p359 = getfield_gc_r(p345, descr=) - setfield_gc(p278, 20971519, descr=) - setfield_gc(p279, ConstPtr(null), descr=) - guard_nonnull(p359, descr=) - i362 = int_add(i203, 1) - i364 = getfield_gc_i(ConstPtr(ptr363), descr=) - i366 = int_sub(i364, 1) - setfield_gc(ConstPtr(ptr367), i366, descr=) - i369 = int_le(i366, 0) - guard_false(i369, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, i362, p17, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, i366, descr=TargetToken(92840368)) + i108 = int_add(i87, 1) + i110 = int_sub(i91, 1) + setfield_gc(ConstPtr(ptr111), i110, descr=) + i113 = int_le(i110, 0) + guard_false(i113, descr=) + i115 = int_add(1, i63) + i116 = int_sub(i63, 1) + i117 = uint_lt(i116, i78) + guard_true(i117, descr=) + jump(p0, p1, i2, p4, p5, p6, p8, p11, i108, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p68, p77, i116, i110, i63, i78, descr=TargetToken(93564304)) """) diff --git a/rsqueakvm/test/test_miniimage_compiling.py b/rsqueakvm/test/test_miniimage_compiling.py index c10c1827..c1da1faf 100644 --- a/rsqueakvm/test/test_miniimage_compiling.py +++ b/rsqueakvm/test/test_miniimage_compiling.py @@ -113,10 +113,12 @@ def test_become(): testBecome | p1 p2 a | p1 := 1@2. - p2 := #(3 4 5). + p2 := OrderedCollection new. + p2 add: 3; add: 4; add: 5. a := p1 -> p2. (1@2 = a key) ifFalse: [^1]. - (#(3 4 5) = a value) ifFalse: [^2]. + (#(3 4 5) = a value asArray) + ifFalse: [^2]. (p1 -> p2 = a) ifFalse: [^3]. (p1 == a key) ifFalse: [^4]. (p2 == a value) ifFalse: [^5]. diff --git a/rsqueakvm/test/test_model.py b/rsqueakvm/test/test_model.py index afb50837..cd81f96e 100644 --- a/rsqueakvm/test/test_model.py +++ b/rsqueakvm/test/test_model.py @@ -115,7 +115,7 @@ def test_compiledin_class(): assert classshadow.lookup(w_foo).compiled_in() is w_super def new_object(size=0): - return W_PointersObject(space, None, size) + return W_PointersObject(space, space.w_Array, size) def test_compiledin_class_assoc(): val = bootstrap_class(0) @@ -189,7 +189,7 @@ def test_compiledmethod_atput0_not_aligned(): def test_is_same_object(w_o1=None, w_o2=None): if w_o1 is None: - w_o1 = W_PointersObject(space, None, 0) + w_o1 = W_PointersObject(space, space.w_Array, 0) if w_o2 is None: w_o2 = w_o1 assert w_o1.is_same_object(w_o2) @@ -197,9 +197,9 @@ def test_is_same_object(w_o1=None, w_o2=None): def test_not_is_same_object(w_o1=None,w_o2=None): if w_o1 is None: - w_o1 = W_PointersObject(space, None, 0) + w_o1 = W_PointersObject(space, space.w_Array, 0) if w_o2 is None: - w_o2 = W_PointersObject(space, None,0) + w_o2 = W_PointersObject(space, space.w_Array, 0) assert not w_o1.is_same_object(w_o2) assert not w_o2.is_same_object(w_o1) w_o2 = W_SmallInteger(2) @@ -267,7 +267,7 @@ def test_become_with_shadow(): def test_word_atput(): i = W_SmallInteger(100) - b = W_WordsObject(space, None, 1) + b = W_WordsObject(space, space.w_Array, 1) b.atput0(space, 0, i) assert 100 == b.getword(0) i = space.w_LargePositiveInteger.as_class_get_shadow(space).new(4) @@ -276,7 +276,7 @@ def test_word_atput(): assert b.getword(0) == 3221225472 def test_word_at(): - b = W_WordsObject(space, None, 1) + b = W_WordsObject(space, space.w_Array, 1) b.setword(0, 100) r = b.at0(space, 0) assert isinstance(r, W_SmallInteger) @@ -354,7 +354,7 @@ def test_large_positive_integer_1word_at_put(): assert hex(r_uint(target.unwrap_long_untranslated(space))) == hex(r_uint(source.unwrap_long_untranslated(space))) def test_BytesObject_short_at(): - target = W_BytesObject(space, None, 4) + target = W_BytesObject(space, space.w_Array, 4) target.setchar(0, chr(0x00)) target.setchar(1, chr(0x01)) target.setchar(2, chr(0x10)) @@ -363,7 +363,7 @@ def test_BytesObject_short_at(): assert target.short_at0(space, 1).value == intmask(0xffff8110) def test_BytesObject_short_atput(): - target = W_BytesObject(space, None, 4) + target = W_BytesObject(space, space.w_Array, 4) target.short_atput0(space, 0, space.wrap_int(0x0100)) if not constants.IS_64BIT: target.short_atput0(space, 1, space.wrap_int(intmask(0xffff8110))) @@ -376,7 +376,7 @@ def test_BytesObject_short_atput(): assert target.getchar(3) == chr(0x81) def test_WordsObject_short_at(): - target = W_WordsObject(space, None, 2) + target = W_WordsObject(space, space.w_Array, 2) target.setword(0, r_uint(0x00018000)) target.setword(1, r_uint(0x80010111)) assert target.short_at0(space, 0).value == intmask(0xffff8000) @@ -385,7 +385,7 @@ def test_WordsObject_short_at(): assert target.short_at0(space, 3).value == intmask(0xffff8001) def test_WordsObject_short_atput(): - target = W_WordsObject(space, None, 2) + target = W_WordsObject(space, space.w_Array, 2) target.short_atput0(space, 0, space.wrap_int(0x0100)) if not constants.IS_64BIT: target.short_atput0(space, 1, space.wrap_int(-1)) diff --git a/rsqueakvm/test/test_primitives.py b/rsqueakvm/test/test_primitives.py index 93df73be..ed14e5f8 100644 --- a/rsqueakvm/test/test_primitives.py +++ b/rsqueakvm/test/test_primitives.py @@ -10,7 +10,7 @@ from rsqueakvm.model.display import W_DisplayBitmap from rsqueakvm.model.numeric import (W_Float, W_SmallInteger, W_LargeIntegerWord, W_LargeIntegerBig) -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from rsqueakvm.model.variable import W_BytesObject, W_WordsObject from rsqueakvm.error import PrimitiveFailedError from rsqueakvm import primitives @@ -43,7 +43,10 @@ class MockFrame(W_PointersObject): def __init__(self, space, stack): size = 6 + len(stack) + 6 self._initialize_storage(space, space.w_BlockContext, size) - self.store_all(space, [None] * 6 + stack + [space.w_nil] * 6) + for i, v in enumerate(stack): + if not isinstance(v, W_Object): + stack[i] = space.w(v) + self.store_all(space, [space.w_nil] * 6 + stack + [space.w_nil] * 6) s_self = self.as_context_get_shadow(space) s_self.reset_stack() s_self.push_all(stack) @@ -857,7 +860,7 @@ def step(s_frame): def test_primitive_be_display(): assert space.w_display() is space.w_nil - mock_display = W_PointersObject(space, space.w_Point, 4) + mock_display = W_FixedPointersObject(space, space.w_Point, 4) w_wordbmp = W_WordsObject(space, space.w_Bitmap, 10) mock_display.store(space, 0, w_wordbmp) # bitmap mock_display.store(space, 1, space.wrap_int(32)) # width @@ -871,7 +874,7 @@ def test_primitive_be_display(): sdldisplay = w_bitmap.display() assert isinstance(sdldisplay, display.SDLDisplay) - mock_display2 = W_PointersObject(space, space.w_Point, 4) + mock_display2 = W_FixedPointersObject(space, space.w_Point, 4) mock_display2.store(space, 0, W_WordsObject(space, space.w_Bitmap, 10)) # bitmap mock_display2.store(space, 1, space.wrap_int(32)) # width mock_display2.store(space, 2, space.wrap_int(10)) # height @@ -889,7 +892,7 @@ def test_primitive_be_display(): assert mock_display.fetch(space, 0) is w_bitmap # def test_primitive_force_display_update(monkeypatch): -# mock_display = W_PointersObject(space, space.w_Point, 4) +# mock_display = W_FixedPointersObject(space, space.w_Point, 4) # w_wordbmp = W_WordsObject(space, space.w_Array, 10) # mock_display.store(space, 0, w_wordbmp) # bitmap # mock_display.store(space, 1, space.wrap_int(32)) # width diff --git a/rsqueakvm/test/test_shadow.py b/rsqueakvm/test/test_shadow.py index 98ce38f0..3c8ffeee 100644 --- a/rsqueakvm/test/test_shadow.py +++ b/rsqueakvm/test/test_shadow.py @@ -3,7 +3,7 @@ from rsqueakvm import storage_classes, constants, wrapper from rsqueakvm.model.compiled_methods import W_PreSpurCompiledMethod, W_SpurCompiledMethod -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from rsqueakvm.model.variable import W_BytesObject from .util import create_space, copy_to_module, cleanup_module @@ -60,7 +60,7 @@ def build_smalltalk_class(name, format, w_superclass=None, w_Metaclass) w_methoddict = build_methoddict(methods) size = constants.CLASS_NAME_INDEX + 1 - w_class = W_PointersObject(space, w_classofclass, size) + w_class = W_FixedPointersObject(space, w_classofclass, size) w_class.store(space, constants.CLASS_SUPERCLASS_INDEX, w_superclass) w_class.store(space, constants.CLASS_METHODDICT_INDEX, w_methoddict) w_class.store(space, constants.CLASS_FORMAT_INDEX, space.wrap_int(format)) diff --git a/rsqueakvm/test/test_strategies.py b/rsqueakvm/test/test_strategies.py index f0faefb4..107a5c62 100644 --- a/rsqueakvm/test/test_strategies.py +++ b/rsqueakvm/test/test_strategies.py @@ -1,6 +1,6 @@ from rsqueakvm import storage from rsqueakvm.model.numeric import W_Float, W_SmallInteger -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from .util import create_space_interp, copy_to_module, cleanup_module @@ -31,11 +31,19 @@ def float_arr(size): a.store(space, 0, space.wrap_float(1.2)) return a +def pt(x, y): + w_point = W_FixedPointersObject(space, space.w_Point, 2) + w_point.store(interp.space, 0, space.w(x)) + w_point.store(interp.space, 1, space.w(y)) + return w_point + def check_arr(arr, expected): for i in range(arr.size()): w_val = arr.fetch(space, i) if expected[i] == w_nil: assert w_val.is_nil(space) + elif expected[i] == None: + assert w_val.is_nil(space) elif isinstance(expected[i], int): assert isinstance(w_val, W_SmallInteger) assert space.unwrap_int(w_val) == expected[i] @@ -200,3 +208,87 @@ def test_Float_store_SmallInt_to_List(): a.store(space, 1, space.wrap_int(2)) assert isinstance(a.strategy, storage.ListStrategy) check_arr(a, [1.2, 2, w_nil, w_nil, w_nil]) + +def test_maps_are_constant(): + a = pt(None, None) + check_arr(a, [None, None]) + b = pt(None, None) + check_arr(b, [None, None]) + assert a.strategy is b.strategy + a.store(space, 1, space.wrap_int(2)) + b.store(space, 1, space.wrap_int(24)) + assert a.strategy is b.strategy + a.store(space, 1, space.w_nil) + assert a.strategy is not b.strategy + b.store(space, 1, space.wrap_float(1.1)) + assert a.strategy is b.strategy + a.store(space, 1, space.wrap_int(12)) + assert a.strategy is b.strategy + +def test_store_SmallInt_uses_smallint_map(): + a = pt(None, None) + check_arr(a, [None, None]) + assert isinstance(a.strategy, storage.MapStrategy) + assert a.strategy.getprev() is None + a.store(space, 1, space.wrap_int(2)) + check_arr(a, [None, 2]) + assert isinstance(a.strategy, storage.IntMapStorageNode) + assert a.strategy.index == 1 + assert isinstance(a.strategy.prev, storage.MapStrategy) + assert a.strategy.prev.getprev() is None + +def test_object_map_does_not_change_to_int(): + a = pt(1.2, 2.2) + check_arr(a, [1.2, 2.2]) + assert isinstance(a.strategy, storage.ObjectMapStorageNode) + assert isinstance(a.strategy.prev, storage.ObjectMapStorageNode) + assert isinstance(a.strategy.prev.prev, storage.MapStrategy) + a.store(space, 1, space.wrap_int(2)) + check_arr(a, [1.2, 2]) + assert isinstance(a.strategy, storage.ObjectMapStorageNode) + assert isinstance(a.strategy.prev, storage.ObjectMapStorageNode) + assert isinstance(a.strategy.prev.prev, storage.MapStrategy) + assert a.strategy.index == 1 + +def test_store_SmallInt_uses_sorted_smallint_map_then_removes_it(): + a = pt(None, None) + a.store(space, 1, space.wrap_int(2)) + a.store(space, 0, space.wrap_int(3)) + check_arr(a, [3, 2]) + assert isinstance(a.strategy, storage.IntMapStorageNode) + assert a.strategy.index == 1 + assert isinstance(a.strategy.prev, storage.IntMapStorageNode) + assert a.strategy.prev.index == 0 + assert isinstance(a.strategy.prev.prev, storage.MapStrategy) + assert a.strategy.prev.prev.getprev() is None + a.store(space, 1, space.w_nil) + assert isinstance(a.strategy, storage.ObjectMapStorageNode) + assert a.strategy.index == 1 + assert isinstance(a.strategy.prev, storage.IntMapStorageNode) + assert a.strategy.prev.index == 0 + assert isinstance(a.strategy.prev.prev, storage.MapStrategy) + assert a.strategy.prev.prev.getprev() is None + a.store(space, 1, space.wrap_int(2)) + assert isinstance(a.strategy, storage.ObjectMapStorageNode) + assert a.strategy.index == 1 + assert isinstance(a.strategy.prev, storage.IntMapStorageNode) + assert a.strategy.prev.index == 0 + assert isinstance(a.strategy.prev.prev, storage.MapStrategy) + assert a.strategy.prev.prev.getprev() is None + a.store(space, 1, space.wrap_float(1.1)) + assert isinstance(a.strategy, storage.ObjectMapStorageNode) + assert a.strategy.index == 1 + assert isinstance(a.strategy.prev, storage.IntMapStorageNode) + assert a.strategy.prev.index == 0 + assert a.strategy.prev.prev.getprev() is None + a.store(space, 0, space.wrap_float(3.3)) + assert isinstance(a.strategy, storage.ObjectMapStorageNode) + assert a.strategy.index == 1 + assert isinstance(a.strategy.prev, storage.ObjectMapStorageNode) + assert a.strategy.prev.index == 0 + assert a.strategy.prev.prev.getprev() is None + +def test_map_strategies_are_singletons(): + assert storage.MapStrategy._is_singleton + for c in space.strategy_factory._collect_subclasses(storage.MapStrategy): + assert c._is_singleton diff --git a/rsqueakvm/test/test_zin_squeak_4_5_image.py b/rsqueakvm/test/test_zin_squeak_4_5_image.py index 605dc43f..5e062915 100644 --- a/rsqueakvm/test/test_zin_squeak_4_5_image.py +++ b/rsqueakvm/test/test_zin_squeak_4_5_image.py @@ -1,7 +1,7 @@ import sys from rsqueakvm.model.numeric import W_SmallInteger -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from .util import read_image, copy_to_module, cleanup_module, slow_test, very_slow_test, skip @@ -82,7 +82,7 @@ def test_ContextPart_jump(): 0xc9, 0x82, 0xc0, 0x40, 0x7c] # Send value and return Association = space.w_Point # Wrong class, doesn't matter. - assoc = W_PointersObject(space, Association, 2) + assoc = W_FixedPointersObject(space, Association, 2) assoc.store(space, 0, w('a')) assoc.store(space, 1, w(3)) w_method = space.make_method(bytes, [assoc, w(5), push, sender, jump, w(10)]) @@ -116,10 +116,10 @@ def test_ContextPart_jump_nonlocal(): 0xc9, 0x82, 0xc0, 0x40, 0x7c] # Send value and return Association = space.w_Point # Wrong class, doesn't matter. - assoc = W_PointersObject(space, Association, 2) + assoc = W_FixedPointersObject(space, Association, 2) assoc.store(space, 0, w('a')) assoc.store(space, 1, space.w_nil) - assoc2 = W_PointersObject(space, Association, 2) + assoc2 = W_FixedPointersObject(space, Association, 2) assoc2.store(space, 0, w('outer')) assoc2.store(space, 1, space.w_nil) w_method = space.make_method(bytes, [assoc, w(5), assoc2, push, jump, w(10)]) @@ -147,13 +147,13 @@ def test_contextOn_do_(): ] Association = space.w_Point # Wrong class, doesn't matter. - ctxAssoc = W_PointersObject(space, Association, 2) + ctxAssoc = W_FixedPointersObject(space, Association, 2) ctxAssoc.store(space, 0, w('ctx')) ctxAssoc.store(space, 1, space.w_nil) - contextPartAssoc = W_PointersObject(space, Association, 2) + contextPartAssoc = W_FixedPointersObject(space, Association, 2) contextPartAssoc.store(space, 0, w('ContextPart')) contextPartAssoc.store(space, 1, ContextPart) - errorAssoc = W_PointersObject(space, Association, 2) + errorAssoc = W_FixedPointersObject(space, Association, 2) errorAssoc.store(space, 0, w('Point')) errorAssoc.store(space, 1, Association) w_method = space.make_method(bytes, [ctxAssoc, contextOnDo, contextPartAssoc, errorAssoc, w('nothing')]) @@ -203,5 +203,4 @@ def test_semaphore(): w_method = space.make_method(bytes, [semaAssoc, w_fork, w_wait, w_yield, w_processor, w_suspPrimOFail, w('nothing')]) result = interp.execute_method(w_method) - import pdb; pdb.set_trace() assert isinstance(result, W_PointersObject) diff --git a/rsqueakvm/test/util.py b/rsqueakvm/test/util.py index 253c1048..04fd873c 100644 --- a/rsqueakvm/test/util.py +++ b/rsqueakvm/test/util.py @@ -1,10 +1,10 @@ import py import sys -from rsqueakvm import storage_classes, interpreter, objspace, util, constants, squeakimage, interpreter_bytecodes +from rsqueakvm import storage_classes, interpreter, objspace, util, constants, squeakimage, interpreter_bytecodes, storage from rsqueakvm.model.base import W_Object from rsqueakvm.model.compiled_methods import W_PreSpurCompiledMethod -from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject from rpython.rlib import jit from rpython.rlib.objectmodel import instantiate @@ -210,13 +210,10 @@ def define_core_cls(name, w_superclass, w_metaclass): for nm, idx in constants.constant_objects_in_special_object_table_wo_types.items(): w_cls_obj = getattr(self, "w_" + nm) if nm[0].isupper(): - if w_cls_obj.getclass(None) is None: - if w_cls_obj.strategy is None: - w_cls_obj._initialize_storage(self, self.w_Metaclass, 0) - elif w_cls_obj.strategy.w_class is None: - w_cls_obj.strategy.w_class = self.w_Metaclass - else: - import pdb; pdb.set_trace() + if w_cls_obj.strategy is None: + w_cls_obj._initialize_storage(self, self.w_Metaclass, 0) + elif w_cls_obj.strategy.w_class is None: + w_cls_obj.strategy.w_class = self.w_Metaclass def patch_bootstrap_classes(self): # Create all classes in the class hierarchies of the classes in the special objects array. @@ -302,15 +299,18 @@ def patch_bootstrap_object(obj, cls, size): patch_bootstrap_object(self.w_special_selectors, self.w_Array, len(constants.SPECIAL_SELECTORS) * 2) special_objects_w = [self.w_nil] * (constants.SPECIAL_OBJECTS_SIZE * 2) for name, idx in constants.constant_objects_in_special_object_table_wo_types.items(): - special_objects_w[idx] = getattr(self, "w_" + name) + w_sp_obj = getattr(self, "w_" + name) + if (not isinstance(w_sp_obj, W_PointersObject)) or (getattr(w_sp_obj, "strategy") is not None): + special_objects_w[idx] = w_sp_obj self.w_special_objects = self.wrap_list(special_objects_w) + self.w_Processor = self.wrap_list([ # scheduler + self.w_nil, + self.wrap_list([self.w_nil, self.w_nil]) # active proc + ]) self.w_schedulerassociationpointer = ( self.wrap_list([ # assoc self.w_nil, - self.wrap_list([ # scheduler - self.w_nil, - self.wrap_list([self.w_nil, self.w_nil]) # active proc - ]) + self.w_Processor ]) ) @@ -318,24 +318,24 @@ def patch_class(self, w_class, instsize, w_superclass=None, w_metaclass=None, name='?', format=storage_classes.POINTERS, varsized=False): s = instantiate(storage_classes.ClassShadow) s.space = self - s.w_class = w_metaclass + s.w_class = w_metaclass or self.w_nil s.version = util.version.Version() s._w_self = w_class s.subclass_s = {} s._s_superclass = None - s.store_w_superclass(w_superclass) + s.store_w_superclass(w_superclass or self.w_nil) s.name = name s._instance_size = instsize s.instance_kind = format s._s_methoddict = None s.instance_varsized = varsized or format != storage_classes.POINTERS - w_class.store_strategy(s) + w_class._set_strategy(s) s._initialize_storage(w_class, 6) def bootstrap_class(self, instsize, w_superclass=None, w_metaclass=None, name='?', format=storage_classes.POINTERS, varsized=False): - w_class = W_PointersObject(self, w_metaclass, 6) - self.patch_class(w_class, instsize, w_superclass, w_metaclass, name, format, varsized) + w_class = W_FixedPointersObject(self, w_metaclass or self.w_nil, 6) + self.patch_class(w_class, instsize, w_superclass or self.w_nil, w_metaclass or self.w_nil, name, format, varsized) return w_class def w(self, any): @@ -367,7 +367,7 @@ def initialize_class(self, w_class, interp): interp.perform(w_class, w_selector=initialize_symbol) def find_symbol_in_methoddict(self, string, cls, fail=True): - if isinstance(cls, W_PointersObject): + if isinstance(cls, W_FixedPointersObject): cls = cls.as_class_get_shadow(self) s_methoddict = cls.s_methoddict() s_methoddict.sync_method_cache() diff --git a/rsqueakvm/util/version.py b/rsqueakvm/util/version.py index 6cc379f0..6600c4d6 100644 --- a/rsqueakvm/util/version.py +++ b/rsqueakvm/util/version.py @@ -21,10 +21,14 @@ def decorator(func): else: elidable_func = jit.elidable(versioned_func) code = [ - "def meth(self %s):" % argstr, - " return elidable_func(self, self.version %s)" % argstr + "def meth(self %s):" % argstr ] - d = {"elidable_func": elidable_func} + if promote and (promote == 'all' or ("0" in promote.split(","))): + code.append(" self = jit.promote(self)") + code.append( + " return elidable_func(self, self.version %s)" % argstr + ) + d = {"elidable_func": elidable_func, "jit": jit} exec "\n".join(code) in d meth = d["meth"] meth.func_name = "constant_meth_" + func.func_name From 588e9e68093f0d4ab0a761c1da07ae515cef35d4 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 09:56:57 +0100 Subject: [PATCH 04/52] fix translation with immutability plugin --- rsqueakvm/model/pointers.py | 11 +++++++++-- rsqueakvm/plugins/immutability/pointers.py | 12 +++++++++--- rsqueakvm/storage_classes.py | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/rsqueakvm/model/pointers.py b/rsqueakvm/model/pointers.py index bfddf120..3c714950 100644 --- a/rsqueakvm/model/pointers.py +++ b/rsqueakvm/model/pointers.py @@ -218,8 +218,8 @@ def pointers_become_one_way(self, space, from_w, to_w): def clone(self, space): my_pointers = self.fetch_all(space) - w_result = self.__class__(space, self.getclass(space), - len(my_pointers)) + w_result = W_PointersObject(space, self.getclass(space), + len(my_pointers)) w_result.store_all(space, my_pointers) return w_result @@ -272,3 +272,10 @@ def _become(self, w_other): W_PointersObject._become(self, w_other) self._swap_inline_fields(w_other) self._size, w_other._size = w_other._size, self._size + + def clone(self, space): + my_pointers = self.fetch_all(space) + w_result = W_FixedPointersObject(space, self.getclass(space), + len(my_pointers)) + w_result.store_all(space, my_pointers) + return w_result diff --git a/rsqueakvm/plugins/immutability/pointers.py b/rsqueakvm/plugins/immutability/pointers.py index 9716350e..56227193 100644 --- a/rsqueakvm/plugins/immutability/pointers.py +++ b/rsqueakvm/plugins/immutability/pointers.py @@ -39,7 +39,7 @@ class W_AbstractImmutable_PointersObject(W_PointersObject): repr_classname = ('%s_AbstractImmutable' % W_PointersObject.repr_classname) - def __init__(self, space, w_cls): + def __init__(self, space, w_cls, pointers_w): """ Initialize immutable pointers object, but avoid initializing storage by calling `W_AbstractObjectWithIdentityHash.__init__(self)` instead of @@ -68,6 +68,12 @@ def fetch(self, space, n0): """:raises: NotImplementedError""" raise NotImplementedError('abstract base class') + def clone(self, space): + my_pointers = self.fetch_all(space) + w_result = self.__class__(space, self.getclass(space), my_pointers) + w_result.store_all(space, my_pointers) + return w_result + class W_Immutable_PointersObject(W_AbstractImmutable_PointersObject): """`W_PointersObject` subclass with immutable storage of variable size.""" @@ -77,7 +83,7 @@ class W_Immutable_PointersObject(W_AbstractImmutable_PointersObject): staticmethod, rerased.new_erasing_pair('storage_eraser')) def __init__(self, space, w_cls, pointers_w): - W_AbstractImmutable_PointersObject.__init__(self, space, w_cls) + W_AbstractImmutable_PointersObject.__init__(self, space, w_cls, pointers_w) self._storage = self.erase(pointers_w) def size(self): @@ -105,7 +111,7 @@ class W_FixedImmutable_PointersObject(W_AbstractImmutable_PointersObject): repr_classname = cls_name def __init__(self, space, w_cls, pointers_w): - W_AbstractImmutable_PointersObject.__init__(self, space, w_cls) + W_AbstractImmutable_PointersObject.__init__(self, space, w_cls, pointers_w) for x in storage_iter: setattr(self, STORAGE_ATTR_TEMPLATE % x, pointers_w[x]) diff --git a/rsqueakvm/storage_classes.py b/rsqueakvm/storage_classes.py index 1f2d1f36..f60fa8a5 100644 --- a/rsqueakvm/storage_classes.py +++ b/rsqueakvm/storage_classes.py @@ -251,7 +251,7 @@ def new(self, extrasize=0): return w_new def make_pointers_object(self, w_cls, size): - if self.isvariable(): + if self.isvariable() or size > 16: return W_PointersObject(self.space, w_cls, size) else: assert size == self.instsize() From b604699c7c484988d12df72498454e383a6d9598 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 10:02:18 +0100 Subject: [PATCH 05/52] promote perform selectors --- rsqueakvm/primitives/control.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rsqueakvm/primitives/control.py b/rsqueakvm/primitives/control.py index d61d7ed0..adda9b1c 100644 --- a/rsqueakvm/primitives/control.py +++ b/rsqueakvm/primitives/control.py @@ -258,7 +258,7 @@ def func(interp, s_frame, argcount): w_selector = s_frame.pop() w_rcvr = s_frame.top() # rcvr is removed in _sendSelector return s_frame._sendSelector( - w_selector, argcount - 1, interp, w_rcvr, + jit.promote(w_selector), argcount - 1, interp, w_rcvr, w_rcvr.class_shadow(interp.space), w_arguments=arguments_w) @expose_primitive(PERFORM_WITH_ARGS, @@ -266,7 +266,8 @@ def func(interp, s_frame, argcount): no_result=True, clean_stack=False) def func(interp, s_frame, w_rcvr, w_selector, w_arguments): s_frame.pop_n(2) # removing our arguments, rcvr is removed in _sendSelector - return s_frame._sendSelector(w_selector, len(w_arguments), interp, w_rcvr, + return s_frame._sendSelector(jit.promote(w_selector), len(w_arguments), + interp, w_rcvr, w_rcvr.class_shadow(interp.space), w_arguments=w_arguments) From 41bc04cded33178663eb8c941db814f549a54b94 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 11:46:47 +0100 Subject: [PATCH 06/52] don't allocate _storage field if map doesn't need it --- rsqueakvm/storage.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index b952f3b1..57e345a7 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -473,12 +473,13 @@ def update_storage(self, w_self): MapStorageNode.update_storage(self, w_self) storage = self.get_storage(w_self) length = self.length() - if storage is None or len(storage) < length: - new_storage = [None] * self.size_estimate() - if storage is not None: - for i, value in enumerate(storage): - new_storage[i] = value - self.set_storage(w_self, new_storage) + if length > 0: + if storage is None or len(storage) < length: + new_storage = [None] * self.size_estimate() + if storage is not None: + for i, value in enumerate(storage): + new_storage[i] = value + self.set_storage(w_self, new_storage) def read(self, w_self, index0): assert isinstance(w_self, W_FixedPointersObject) From 165ada5d6f0367f1744a97328468884385a7d2de Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 15:03:35 +0100 Subject: [PATCH 07/52] try to avoid some allocation overhead when switching strategies --- rsqueakvm/storage.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 57e345a7..30e6a567 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -138,7 +138,9 @@ class ListStrategy(SimpleStorageStrategy): import_from_mixin(OptimizedConvertFromAllNilMixin) def _wrap(self, w_value): - if isinstance(w_value, W_SmallInteger): + if w_value is None: + return self.space.w_nil + elif isinstance(w_value, W_SmallInteger): assert isinstance(w_value, W_MutableSmallInteger) return self.space.wrap_smallint_unsafe(w_value.value) else: @@ -147,6 +149,8 @@ def _wrap(self, w_value): def _unwrap(self, w_value): if isinstance(w_value, W_SmallInteger): return W_MutableSmallInteger(w_value.value) + elif w_value is self.space.w_nil: + return None else: return w_value From c599ed5b3aa327100d7d39d502f663119916aa81 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 13:08:01 +0100 Subject: [PATCH 08/52] update the singleton nodes for MapStrategy, so that we'll always get the latest strategy in a loop --- rsqueakvm/storage.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 30e6a567..38d39050 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -190,16 +190,27 @@ class MapStrategy(AbstractStrategy): repr_classname = "MapStrategy (base)" import_from_mixin(rstrat.SafeIndexingMixin) + @jit.unroll_safe def _initialize_storage(self, w_self, initial_size): self.set_storage(w_self, None) + node = self + while node is not None: + node.update_storage(w_self) + node = node.getprev() @jit.unroll_safe def _convert_storage_from(self, w_self, previous_strategy): assert isinstance(previous_strategy, MapStrategy) storage = previous_strategy.fetch_all(w_self) - for i, w_value in enumerate(storage): + self.store_all(w_self, storage) + + def store_all(self, w_self, elements): + for i, w_value in enumerate(elements): w_self._get_strategy().store(w_self, i, w_value) + def update_storage(self, w_self): + pass + def instantiate(self, w_self, w_class): return self.strategy_factory().strategy_singleton_instance(MapStrategy, w_class) @@ -725,6 +736,7 @@ def get_transition(self, prev, strategy_node_class, index0, w_class): node = self.transitions.get(key, None) if node is None: self.transitions[key] = node = strategy_node_class(self.space, prev, index0, w_class) + self.singleton_nodes[(MapStrategy, w_class)] = node return node def instantiate_strategy(self, strategy_type, w_class, w_self=None, initial_size=0): From d134857ef37446e5cd88bc5a5a4b3b577d0a7bfb Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 16:03:32 +0100 Subject: [PATCH 09/52] use nan-tagging for nil in float and integer storages --- rsqueakvm/model/pointers.py | 6 +-- rsqueakvm/storage.py | 74 +++++++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/rsqueakvm/model/pointers.py b/rsqueakvm/model/pointers.py index 3c714950..2c710b04 100644 --- a/rsqueakvm/model/pointers.py +++ b/rsqueakvm/model/pointers.py @@ -252,9 +252,9 @@ def fillin(self, space, g_self): W_PointersObject.fillin(self, space, g_self) def _init_inline_fields(self): - self._intField1 = 0 - self._intField2 = 0 - self._intField3 = 0 + self._intField1 = 0.0 + self._intField2 = 0.0 + self._intField3 = 0.0 self._intFields = None self._field1 = None self._field2 = None diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 38d39050..b5d1cc77 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -13,9 +13,12 @@ from rpython.rlib import jit from rpython.rlib.objectmodel import import_from_mixin -from rpython.rlib.rarithmetic import int_between +from rpython.rlib.rarithmetic import int_between, intmask from rpython.rlib.rstrategies import rstrategies as rstrat from rpython.rlib.unroll import unrolling_iterable +from rpython.rlib import longlong2float +from rpython.rtyper.lltypesystem import rffi, lltype + """ A note on terminology: @@ -410,14 +413,12 @@ def add_node(self, strategy_cls, index0, w_self): return new_node -class IntMapStorageNode(MapStorageNode): - # we inline up to three integer fields on the object and the rest in a intFields array +class UnboxedAttributeNode(MapStorageNode): + # we inline up to three long fields on the object and the rest in a + # intFields array used_fields = "intFields" - repr_classname = "MapStrategy (int)" - - @staticmethod - def correct_type(w_self, w_value): - return isinstance(w_value, W_SmallInteger) + repr_classname = "MapStrategy (unboxed)" + nan_tag = longlong2float.longlong2float(longlong2float.nan_encoded_zero + 42) def storage_list_position(self): p = self.pos - _NUMBER_OF_INT_FIELDS @@ -435,7 +436,7 @@ def update_storage(self, w_self): length = self.length() if length > 0: if storage is None or len(storage) < length: - new_storage = [0] * self.size_estimate() + new_storage = [0.0] * self.size_estimate() if storage is not None: for i, value in enumerate(storage): new_storage[i] = value @@ -451,12 +452,12 @@ def read(self, w_self, index0): res = w_self._intField3 else: res = w_self._intFields[self.storage_list_position()] - return self.space.wrap_smallint_unsafe(res) + return self.map_wrap(res) def __write__(self, w_self, index0, w_value): assert isinstance(w_self, W_FixedPointersObject) assert index0 == self.index - unwrapped = self.space.unwrap_int(w_value) + unwrapped = self.map_unwrap(w_value) if self.pos == 0: w_self._intField1 = unwrapped elif self.pos == 1: @@ -467,6 +468,42 @@ def __write__(self, w_self, index0, w_value): w_self._intFields[self.storage_list_position()] = unwrapped +class IntMapStorageNode(UnboxedAttributeNode): + repr_classname = "MapStrategy (int)" + + @staticmethod + def correct_type(w_self, w_value): + return isinstance(w_value, W_SmallInteger) + + def map_unwrap(self, w_value): + return longlong2float.longlong2float( + rffi.cast(lltype.SignedLongLong, self.space.unwrap_int(w_value)) + ) + + def map_wrap(self, value): + if value is self.nan_tag: + return self.space.w_nil + return self.space.wrap_smallint_unsafe( + intmask(longlong2float.float2longlong(value)) + ) + + +class FloatMapStorageNode(UnboxedAttributeNode): + repr_classname = "MapStrategy (int)" + + @staticmethod + def correct_type(w_self, w_value): + return isinstance(w_value, W_Float) + + def map_unwrap(self, w_value): + return self.space.unwrap_float(w_value) + + def map_wrap(self, value): + if value is self.nan_tag: + return self.space.w_nil + return self.space.wrap_float(value) + + class ObjectMapStorageNode(MapStorageNode): used_fields = "erased_storage" repr_classname = "MapStrategy (object)" @@ -622,10 +659,17 @@ class SmallIntegerOrNilStrategy(SimpleStorageStrategy): import_from_mixin(rstrat.TaggingStrategy) import_from_mixin(OptimizedConvertFromAllNilMixin) contained_type = W_SmallInteger - def wrap(self, val): return self.space.wrap_smallint_unsafe(val) - def unwrap(self, w_val): return self.space.unwrap_int(w_val) + tag_float = longlong2float.longlong2float(longlong2float.nan_encoded_zero + 42) + def wrap(self, val): + return self.space.wrap_smallint_unsafe( + intmask(longlong2float.float2longlong(val)) + ) + def unwrap(self, w_val): + return longlong2float.longlong2float( + rffi.cast(lltype.SignedLongLong, self.space.unwrap_int(w_val)) + ) def wrapped_tagged_value(self): return self.space.w_nil - def unwrapped_tagged_value(self): return constants.MAXINT + def unwrapped_tagged_value(self): return self.tag_float SmallIntegerOrNilStrategy._convert_storage_from = SmallIntegerOrNilStrategy._better_convert_storage_from @rstrat.strategy(generalize=[ListStrategy]) @@ -649,7 +693,7 @@ class FloatOrNilStrategy(SimpleStorageStrategy): import_from_mixin(rstrat.TaggingStrategy) import_from_mixin(OptimizedConvertFromAllNilMixin) contained_type = W_Float - tag_float = sys.float_info.max + tag_float = longlong2float.longlong2float(longlong2float.nan_encoded_zero + 42) def wrap(self, val): return self.space.wrap_float(val) def unwrap(self, w_val): return self.space.unwrap_float(w_val) def wrapped_tagged_value(self): return self.space.w_nil From 141029f18a99915adbdead997874e218beb25b30 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 16:35:30 +0100 Subject: [PATCH 10/52] Revert "use nan-tagging for nil in float and integer storages" This reverts commit d134857ef37446e5cd88bc5a5a4b3b577d0a7bfb. --- rsqueakvm/model/pointers.py | 6 +-- rsqueakvm/storage.py | 74 ++++++++----------------------------- 2 files changed, 18 insertions(+), 62 deletions(-) diff --git a/rsqueakvm/model/pointers.py b/rsqueakvm/model/pointers.py index 2c710b04..3c714950 100644 --- a/rsqueakvm/model/pointers.py +++ b/rsqueakvm/model/pointers.py @@ -252,9 +252,9 @@ def fillin(self, space, g_self): W_PointersObject.fillin(self, space, g_self) def _init_inline_fields(self): - self._intField1 = 0.0 - self._intField2 = 0.0 - self._intField3 = 0.0 + self._intField1 = 0 + self._intField2 = 0 + self._intField3 = 0 self._intFields = None self._field1 = None self._field2 = None diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index b5d1cc77..38d39050 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -13,12 +13,9 @@ from rpython.rlib import jit from rpython.rlib.objectmodel import import_from_mixin -from rpython.rlib.rarithmetic import int_between, intmask +from rpython.rlib.rarithmetic import int_between from rpython.rlib.rstrategies import rstrategies as rstrat from rpython.rlib.unroll import unrolling_iterable -from rpython.rlib import longlong2float -from rpython.rtyper.lltypesystem import rffi, lltype - """ A note on terminology: @@ -413,12 +410,14 @@ def add_node(self, strategy_cls, index0, w_self): return new_node -class UnboxedAttributeNode(MapStorageNode): - # we inline up to three long fields on the object and the rest in a - # intFields array +class IntMapStorageNode(MapStorageNode): + # we inline up to three integer fields on the object and the rest in a intFields array used_fields = "intFields" - repr_classname = "MapStrategy (unboxed)" - nan_tag = longlong2float.longlong2float(longlong2float.nan_encoded_zero + 42) + repr_classname = "MapStrategy (int)" + + @staticmethod + def correct_type(w_self, w_value): + return isinstance(w_value, W_SmallInteger) def storage_list_position(self): p = self.pos - _NUMBER_OF_INT_FIELDS @@ -436,7 +435,7 @@ def update_storage(self, w_self): length = self.length() if length > 0: if storage is None or len(storage) < length: - new_storage = [0.0] * self.size_estimate() + new_storage = [0] * self.size_estimate() if storage is not None: for i, value in enumerate(storage): new_storage[i] = value @@ -452,12 +451,12 @@ def read(self, w_self, index0): res = w_self._intField3 else: res = w_self._intFields[self.storage_list_position()] - return self.map_wrap(res) + return self.space.wrap_smallint_unsafe(res) def __write__(self, w_self, index0, w_value): assert isinstance(w_self, W_FixedPointersObject) assert index0 == self.index - unwrapped = self.map_unwrap(w_value) + unwrapped = self.space.unwrap_int(w_value) if self.pos == 0: w_self._intField1 = unwrapped elif self.pos == 1: @@ -468,42 +467,6 @@ def __write__(self, w_self, index0, w_value): w_self._intFields[self.storage_list_position()] = unwrapped -class IntMapStorageNode(UnboxedAttributeNode): - repr_classname = "MapStrategy (int)" - - @staticmethod - def correct_type(w_self, w_value): - return isinstance(w_value, W_SmallInteger) - - def map_unwrap(self, w_value): - return longlong2float.longlong2float( - rffi.cast(lltype.SignedLongLong, self.space.unwrap_int(w_value)) - ) - - def map_wrap(self, value): - if value is self.nan_tag: - return self.space.w_nil - return self.space.wrap_smallint_unsafe( - intmask(longlong2float.float2longlong(value)) - ) - - -class FloatMapStorageNode(UnboxedAttributeNode): - repr_classname = "MapStrategy (int)" - - @staticmethod - def correct_type(w_self, w_value): - return isinstance(w_value, W_Float) - - def map_unwrap(self, w_value): - return self.space.unwrap_float(w_value) - - def map_wrap(self, value): - if value is self.nan_tag: - return self.space.w_nil - return self.space.wrap_float(value) - - class ObjectMapStorageNode(MapStorageNode): used_fields = "erased_storage" repr_classname = "MapStrategy (object)" @@ -659,17 +622,10 @@ class SmallIntegerOrNilStrategy(SimpleStorageStrategy): import_from_mixin(rstrat.TaggingStrategy) import_from_mixin(OptimizedConvertFromAllNilMixin) contained_type = W_SmallInteger - tag_float = longlong2float.longlong2float(longlong2float.nan_encoded_zero + 42) - def wrap(self, val): - return self.space.wrap_smallint_unsafe( - intmask(longlong2float.float2longlong(val)) - ) - def unwrap(self, w_val): - return longlong2float.longlong2float( - rffi.cast(lltype.SignedLongLong, self.space.unwrap_int(w_val)) - ) + def wrap(self, val): return self.space.wrap_smallint_unsafe(val) + def unwrap(self, w_val): return self.space.unwrap_int(w_val) def wrapped_tagged_value(self): return self.space.w_nil - def unwrapped_tagged_value(self): return self.tag_float + def unwrapped_tagged_value(self): return constants.MAXINT SmallIntegerOrNilStrategy._convert_storage_from = SmallIntegerOrNilStrategy._better_convert_storage_from @rstrat.strategy(generalize=[ListStrategy]) @@ -693,7 +649,7 @@ class FloatOrNilStrategy(SimpleStorageStrategy): import_from_mixin(rstrat.TaggingStrategy) import_from_mixin(OptimizedConvertFromAllNilMixin) contained_type = W_Float - tag_float = longlong2float.longlong2float(longlong2float.nan_encoded_zero + 42) + tag_float = sys.float_info.max def wrap(self, val): return self.space.wrap_float(val) def unwrap(self, w_val): return self.space.unwrap_float(w_val) def wrapped_tagged_value(self): return self.space.w_nil From 8c263aa932dc1820a6f73c6d4b6953ace2324166 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 16:35:38 +0100 Subject: [PATCH 11/52] Revert "update the singleton nodes for MapStrategy, so that we'll always get the latest strategy in a loop" This reverts commit c599ed5b3aa327100d7d39d502f663119916aa81. --- rsqueakvm/storage.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 38d39050..30e6a567 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -190,27 +190,16 @@ class MapStrategy(AbstractStrategy): repr_classname = "MapStrategy (base)" import_from_mixin(rstrat.SafeIndexingMixin) - @jit.unroll_safe def _initialize_storage(self, w_self, initial_size): self.set_storage(w_self, None) - node = self - while node is not None: - node.update_storage(w_self) - node = node.getprev() @jit.unroll_safe def _convert_storage_from(self, w_self, previous_strategy): assert isinstance(previous_strategy, MapStrategy) storage = previous_strategy.fetch_all(w_self) - self.store_all(w_self, storage) - - def store_all(self, w_self, elements): - for i, w_value in enumerate(elements): + for i, w_value in enumerate(storage): w_self._get_strategy().store(w_self, i, w_value) - def update_storage(self, w_self): - pass - def instantiate(self, w_self, w_class): return self.strategy_factory().strategy_singleton_instance(MapStrategy, w_class) @@ -736,7 +725,6 @@ def get_transition(self, prev, strategy_node_class, index0, w_class): node = self.transitions.get(key, None) if node is None: self.transitions[key] = node = strategy_node_class(self.space, prev, index0, w_class) - self.singleton_nodes[(MapStrategy, w_class)] = node return node def instantiate_strategy(self, strategy_type, w_class, w_self=None, initial_size=0): From 96d3f227bb735af54f6a23e6f611adcef2975c7e Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 17:22:26 +0100 Subject: [PATCH 12/52] if w_self is constant, promote our strategy --- rsqueakvm/model/pointers.py | 6 +++--- rsqueakvm/storage.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rsqueakvm/model/pointers.py b/rsqueakvm/model/pointers.py index 3c714950..56cca8cd 100644 --- a/rsqueakvm/model/pointers.py +++ b/rsqueakvm/model/pointers.py @@ -48,7 +48,7 @@ def safe_getclass(self, space): return self.strategy.getclass() def getclass(self, space): - return jit.promote(self.strategy.promoted().getclass()) + return jit.promote(self.strategy.promoted(self).getclass()) def is_class(self, space): from rsqueakvm.storage_classes import ClassShadow @@ -143,10 +143,10 @@ def atput0(self, space, index0, w_value): self.store(space, index0 + self.instsize(), w_value) def fetch(self, space, n0): - return self.strategy.promoted().fetch(self, n0) + return self.strategy.promoted(self).fetch(self, n0) def store(self, space, n0, w_value): - return self.strategy.promoted().store(self, n0, w_value) + return self.strategy.promoted(self).store(self, n0, w_value) def size(self): return self.strategy.size(self) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 30e6a567..f442193f 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -96,9 +96,11 @@ def instantiate(self, w_self, w_class): size = self.size(w_self) new_strategy = self.strategy_factory().instantiate_strategy(self.instantiate_type, w_class, w_self, size) return new_strategy - def promoted(self): + def promoted(self, w_self): if self._is_singleton: return jit.promote(self) + elif jit.isconstant(w_self): + return jit.promote(self) else: return self From a91b67f1711650526564a39ae694a280bb21c226 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Mar 2017 17:33:09 +0100 Subject: [PATCH 13/52] if self is constant, call an elidable strategy accessor --- rsqueakvm/model/pointers.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/rsqueakvm/model/pointers.py b/rsqueakvm/model/pointers.py index 56cca8cd..533621d7 100644 --- a/rsqueakvm/model/pointers.py +++ b/rsqueakvm/model/pointers.py @@ -44,11 +44,21 @@ def is_weak(self): from rsqueakvm.storage import WeakListStrategy return isinstance(self.strategy, WeakListStrategy) + def elidable_strategy(self): + if jit.isconstant(self): + return self._pure_strategy() + else: + return self.strategy.promoted(self) + + @jit.elidable + def _pure_strategy(self): + return self.strategy + def safe_getclass(self, space): - return self.strategy.getclass() + return self.strategy.promoted(self).getclass() def getclass(self, space): - return jit.promote(self.strategy.promoted(self).getclass()) + return jit.promote(self.elidable_strategy().getclass()) def is_class(self, space): from rsqueakvm.storage_classes import ClassShadow @@ -78,7 +88,7 @@ def invariant(self): isinstance(self.getclass(None).strategy, storage_classes.ClassShadow)) def space(self): - return self.strategy.space + return self.elidable_strategy().space def __str__(self): if self.strategy.provides_getname: @@ -143,20 +153,20 @@ def atput0(self, space, index0, w_value): self.store(space, index0 + self.instsize(), w_value) def fetch(self, space, n0): - return self.strategy.promoted(self).fetch(self, n0) + return self.elidable_strategy().fetch(self, n0) def store(self, space, n0, w_value): - return self.strategy.promoted(self).store(self, n0, w_value) + return self.elidable_strategy().store(self, n0, w_value) def size(self): - return self.strategy.size(self) + return self.elidable_strategy().size(self) def instsize(self): return self.class_shadow(self.space()).instsize() @objectmodel.specialize.arg(2) def as_special_get_shadow(self, space, TheClass): - shadow = self.strategy + shadow = self.elidable_strategy() if not isinstance(shadow, TheClass): shadow = space.strategy_factory.switch_strategy(self, TheClass) assert isinstance(shadow, TheClass) From 547c288f6d81e22ca81c19410f5ea7942247174a Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 7 Mar 2017 14:44:19 +0100 Subject: [PATCH 14/52] compute the actually required frame size --- rsqueakvm/interpreter.py | 19 +- rsqueakvm/interpreter_bytecodes.py | 290 +++++++++++++++++++--------- rsqueakvm/model/compiled_methods.py | 25 ++- rsqueakvm/squeakimage.py | 2 +- rsqueakvm/storage_contexts.py | 7 +- 5 files changed, 240 insertions(+), 103 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index 9abf651b..d25995c6 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -410,17 +410,26 @@ def step(self, context): if not objectmodel.we_are_translated(): if USE_SIGUSR1: self.check_sigusr(context) + stackdepth_pre = context.stackdepth() bytecode = context.fetch_next_bytecode() for entry in UNROLLING_BYTECODE_RANGES: - if len(entry) == 2: - bc, methname = entry + if len(entry) == 3: + bc, methname, _ = entry if bytecode == bc: - return getattr(context, methname)(self, bytecode) + result = getattr(context, methname)(self, bytecode) + if not objectmodel.we_are_translated(): + stackdepth_after = context.stackdepth() + assert context.last_effect == (stackdepth_after - stackdepth_pre) + return result else: - start, stop, methname = entry + start, stop, methname, _ = entry if start <= bytecode <= stop: - return getattr(context, methname)(self, bytecode) + result = getattr(context, methname)(self, bytecode) + if not objectmodel.we_are_translated(): + stackdepth_after = context.stackdepth() + assert context.last_effect == (stackdepth_after - stackdepth_pre) + return result assert 0, "unreachable" # ============== Methods for handling user interrupts ============== diff --git a/rsqueakvm/interpreter_bytecodes.py b/rsqueakvm/interpreter_bytecodes.py index 5cda459f..7a33083e 100644 --- a/rsqueakvm/interpreter_bytecodes.py +++ b/rsqueakvm/interpreter_bytecodes.py @@ -37,9 +37,10 @@ def bytecode_implementation_wrapper(self, interp, current_bytecode): i = i + 1 # This is a good place to step through bytecodes. - self.debug_bytecode(interp) + self.debug_bytecode(interp, current_bytecode, parameters) return actual_implementation_method(self, interp, current_bytecode, *parameters) bytecode_implementation_wrapper.func_name = actual_implementation_method.func_name + bytecode_implementation_wrapper.parameter_bytes = parameter_bytes return bytecode_implementation_wrapper return bytecode_implementation_decorator @@ -658,93 +659,105 @@ def longJumpIfFalseBytecode(self, interp, current_bytecode, parameter): bytecodePrimPointX = make_send_selector_bytecode("x", 0) bytecodePrimPointY = make_send_selector_bytecode("y", 0) - def debug_bytecode(self, interp): - # Hook used in interpreter_debugging - pass - + def debug_bytecode(self, interp, current_bytecode, parameters): + if not objectmodel.we_are_translated(): + self.last_effect = _compute_effect_of_bytecode( + current_bytecode, + BYTECODE_EFFECTS[current_bytecode], + parameters[0] if len(parameters) > 0 else 0 + ) + + +SEND_EFFECT_LITERAL = 0xff +SEND_EFFECT_SHIFT5 = 0xfe +SEND_EFFECT_SHIFT6 = 0xfd +ARRAY_EFFECT = 0xfc +CLOSURE_EFFECT = 0xfb +ANYTHING_EFFECT = 0xfa +# bytecode or bytecode range, name, stack effect, arguments BYTECODE_RANGES = [ - ( 0, 15, "pushReceiverVariableBytecode"), - ( 16, 31, "pushTemporaryVariableBytecode"), - ( 32, 63, "pushLiteralConstantBytecode"), - ( 64, 95, "pushLiteralVariableBytecode"), - ( 96, 103, "storeAndPopReceiverVariableBytecode"), - (104, 111, "storeAndPopTemporaryVariableBytecode"), - (112, "pushReceiverBytecode"), - (113, "pushConstantTrueBytecode"), - (114, "pushConstantFalseBytecode"), - (115, "pushConstantNilBytecode"), - (116, "pushConstantMinusOneBytecode"), - (117, "pushConstantZeroBytecode"), - (118, "pushConstantOneBytecode"), - (119, "pushConstantTwoBytecode"), - (120, "returnReceiverBytecode"), - (121, "returnTrueBytecode"), - (122, "returnFalseBytecode"), - (123, "returnNilBytecode"), - (124, "returnTopFromMethodBytecode"), - (125, "returnTopFromBlockBytecode"), - (126, "unknownBytecode"), - (127, "unknownBytecode"), - (128, "extendedPushBytecode"), - (129, "extendedStoreBytecode"), - (130, "extendedStoreAndPopBytecode"), - (131, "singleExtendedSendBytecode"), - (132, "doubleExtendedDoAnythingBytecode"), - (133, "singleExtendedSuperBytecode"), - (134, "secondExtendedSendBytecode"), - (135, "popStackBytecode"), - (136, "duplicateTopBytecode"), - (137, "pushActiveContextBytecode"), - (138, "pushNewArrayBytecode"), - (139, "callPrimitiveBytecode"), - (140, "pushRemoteTempLongBytecode"), - (141, "storeRemoteTempLongBytecode"), - (142, "storeAndPopRemoteTempLongBytecode"), - (143, "pushClosureCopyCopiedValuesBytecode"), - (144, 151, "shortUnconditionalJumpBytecode"), - (152, 159, "shortConditionalJumpBytecode"), - (160, 167, "longUnconditionalJumpBytecode"), - (168, 171, "longJumpIfTrueBytecode"), - (172, 175, "longJumpIfFalseBytecode"), - (176, "bytecodePrimAdd"), - (177, "bytecodePrimSubtract"), - (178, "bytecodePrimLessThan"), - (179, "bytecodePrimGreaterThan"), - (180, "bytecodePrimLessOrEqual"), - (181, "bytecodePrimGreaterOrEqual"), - (182, "bytecodePrimEqual"), - (183, "bytecodePrimNotEqual"), - (184, "bytecodePrimMultiply"), - (185, "bytecodePrimDivide"), - (186, "bytecodePrimMod"), - (187, "bytecodePrimMakePoint"), - (188, "bytecodePrimBitShift"), - (189, "bytecodePrimDiv"), - (190, "bytecodePrimBitAnd"), - (191, "bytecodePrimBitOr"), - (192, "bytecodePrimAt"), - (193, "bytecodePrimAtPut"), - (194, "bytecodePrimSize"), - (195, "bytecodePrimNext"), - (196, "bytecodePrimNextPut"), - (197, "bytecodePrimAtEnd"), - (198, "bytecodePrimEquivalent"), - (199, "bytecodePrimClass"), - (200, "bytecodePrimBlockCopy"), - (201, "bytecodePrimValue"), - (202, "bytecodePrimValueWithArg"), - (203, "bytecodePrimDo"), - (204, "bytecodePrimNew"), - (205, "bytecodePrimNewWithArg"), - (206, "bytecodePrimPointX"), - (207, "bytecodePrimPointY"), - (208, 255, "sendLiteralSelectorBytecode"), + ( 0, 15, "pushReceiverVariableBytecode", +1), + ( 16, 31, "pushTemporaryVariableBytecode", +1), + ( 32, 63, "pushLiteralConstantBytecode", +1), + ( 64, 95, "pushLiteralVariableBytecode", +1), + ( 96, 103, "storeAndPopReceiverVariableBytecode", -1), + (104, 111, "storeAndPopTemporaryVariableBytecode", -1), + (112, "pushReceiverBytecode", +1), + (113, "pushConstantTrueBytecode", +1), + (114, "pushConstantFalseBytecode", +1), + (115, "pushConstantNilBytecode", +1), + (116, "pushConstantMinusOneBytecode", +1), + (117, "pushConstantZeroBytecode", +1), + (118, "pushConstantOneBytecode", +1), + (119, "pushConstantTwoBytecode", +1), + (120, "returnReceiverBytecode", 0), + (121, "returnTrueBytecode", 0), + (122, "returnFalseBytecode", 0), + (123, "returnNilBytecode", 0), + (124, "returnTopFromMethodBytecode", -1), + (125, "returnTopFromBlockBytecode", -1), + (126, "unknownBytecode", 0), + (127, "unknownBytecode", 0), + (128, "extendedPushBytecode", +1), + (129, "extendedStoreBytecode", 0), + (130, "extendedStoreAndPopBytecode", -1), + (131, "singleExtendedSendBytecode", SEND_EFFECT_SHIFT5), + (132, "doubleExtendedDoAnythingBytecode", ANYTHING_EFFECT), + (133, "singleExtendedSuperBytecode", SEND_EFFECT_SHIFT5), + (134, "secondExtendedSendBytecode", SEND_EFFECT_SHIFT6), + (135, "popStackBytecode", -1), + (136, "duplicateTopBytecode", +1), + (137, "pushActiveContextBytecode", +1), + (138, "pushNewArrayBytecode", ARRAY_EFFECT), + (139, "callPrimitiveBytecode", 0), + (140, "pushRemoteTempLongBytecode", +1), + (141, "storeRemoteTempLongBytecode", 0), + (142, "storeAndPopRemoteTempLongBytecode", -1), + (143, "pushClosureCopyCopiedValuesBytecode", CLOSURE_EFFECT), + (144, 151, "shortUnconditionalJumpBytecode", 0), + (152, 159, "shortConditionalJumpBytecode", -1), + (160, 167, "longUnconditionalJumpBytecode", 0), + (168, 171, "longJumpIfTrueBytecode", -1), + (172, 175, "longJumpIfFalseBytecode", -1), + (176, "bytecodePrimAdd", -1), + (177, "bytecodePrimSubtract", -1), + (178, "bytecodePrimLessThan", -1), + (179, "bytecodePrimGreaterThan", -1), + (180, "bytecodePrimLessOrEqual", -1), + (181, "bytecodePrimGreaterOrEqual", -1), + (182, "bytecodePrimEqual", -1), + (183, "bytecodePrimNotEqual", -1), + (184, "bytecodePrimMultiply", -1), + (185, "bytecodePrimDivide", -1), + (186, "bytecodePrimMod", -1), + (187, "bytecodePrimMakePoint", -1), + (188, "bytecodePrimBitShift", -1), + (189, "bytecodePrimDiv", -1), + (190, "bytecodePrimBitAnd", -1), + (191, "bytecodePrimBitOr", -1), + (192, "bytecodePrimAt", -1), + (193, "bytecodePrimAtPut", -2), + (194, "bytecodePrimSize", 0), + (195, "bytecodePrimNext", 0), + (196, "bytecodePrimNextPut", -1), + (197, "bytecodePrimAtEnd", 0), + (198, "bytecodePrimEquivalent", -1), + (199, "bytecodePrimClass", 0), + (200, "bytecodePrimBlockCopy", -1), + (201, "bytecodePrimValue", 0), + (202, "bytecodePrimValueWithArg", -1), + (203, "bytecodePrimDo", -1), + (204, "bytecodePrimNew", 0), + (205, "bytecodePrimNewWithArg", -1), + (206, "bytecodePrimPointX", 0), + (207, "bytecodePrimPointY", 0), + (208, 255, "sendLiteralSelectorBytecode", SEND_EFFECT_LITERAL), ] def initialize_bytecode_names(): result = [None] * 256 for entry in BYTECODE_RANGES: - if len(entry) == 2: + if len(entry) == 3: result[entry[0]] = entry[1] else: for arg, pos in enumerate(range(entry[0], entry[1]+1)): @@ -756,23 +769,28 @@ def initialize_bytecode_names(): def initialize_bytecode_table(): result = [None] * 256 + args = [None] * 256 for entry in BYTECODE_RANGES: - if len(entry) == 2: + if len(entry) == 3: + idx = 1 positions = [entry[0]] else: + idx = 2 positions = range(entry[0], entry[1]+1) for pos in positions: - result[pos] = getattr(ContextPartShadow, entry[-1]) + result[pos] = getattr(ContextPartShadow, entry[idx]) + args[pos] = result[pos].parameter_bytes assert None not in result - return result + assert None not in args + return result, args # this table is only used for creating named bytecodes in tests and printing -BYTECODE_TABLE = initialize_bytecode_table() +BYTECODE_TABLE, BYTECODE_ARGUMENT_COUNT = initialize_bytecode_table() def initialize_return_bytecodes(): result = [] for entry in BYTECODE_RANGES: - if len(entry) == 2: + if len(entry) == 3: if entry[1].startswith('return'): result.append(entry[0]) else: @@ -782,3 +800,101 @@ def initialize_return_bytecodes(): return result RETURN_BYTECODES = initialize_return_bytecodes() + +def initialize_conditional_jump_bytecodes(): + result = [] + cond_jump_bytecodes = ( "shortConditionalJumpBytecode", + "longJumpIfTrueBytecode", + "longJumpIfFalseBytecode" ) + for entry in BYTECODE_RANGES: + if len(entry) == 3: + if entry[1] in cond_jump_bytecodes: + result.append(entry[0]) + else: + if entry[2] in cond_jump_bytecodes: + result.extend(range(entry[0], entry[1]+1)) + assert len(result) > 0 + return result + +CONDITIONAL_JUMP_BYTECODES = initialize_conditional_jump_bytecodes() + + +def initialize_bytecode_effects(): + result = [None] * 256 + for entry in BYTECODE_RANGES: + if len(entry) == 3: + positions = [entry[0]] + else: + positions = range(entry[0], entry[1]+1) + for pos in positions: + result[pos] = entry[-1] + assert None not in result + return result + +BYTECODE_EFFECTS = initialize_bytecode_effects() + + +def compute_frame_size(bytecode): + return _compute_frame_size(bytecode, 0) + + +def _compute_frame_size(bytecode, offset): + size = 0 + max_size = 0 + idx = offset + while idx < len(bytecode): + byte = ord(bytecode[idx]) + parameters = BYTECODE_ARGUMENT_COUNT[byte] + if parameters > 0: + parameter_1 = ord(bytecode[idx + 1]) + else: + parameter_1 = 0 + if byte in CONDITIONAL_JUMP_BYTECODES: + if parameters == 0: + jump = (byte & 7) + 1 + elif parameters == 1: + jump = ((byte & 3) << 8) + parameter_1 + else: + assert False + szA = _compute_frame_size(bytecode, idx + parameters + 1) + szB = _compute_frame_size(bytecode, idx + parameters + 1 + jump) + return max_size + max(szA, szB) + elif byte in RETURN_BYTECODES: + return max_size + else: + effect = BYTECODE_EFFECTS[byte] + size += _compute_effect_of_bytecode(byte, effect, parameter_1) + max_size = max(size, max_size) + idx += parameters + 1 + return size + + +def _compute_effect_of_bytecode(byte, effect, parameter_1): + if effect == ARRAY_EFFECT: + arraySize, popIntoArray = splitter[7, 1](parameter_1) + if popIntoArray == 1: + return -arraySize + 1 + elif effect == SEND_EFFECT_SHIFT5: + argcount = parameter_1 >> 5 + return -argcount - 1 + 1 + elif effect == SEND_EFFECT_SHIFT6: + argcount = parameter_1 >> 6 + return -argcount - 1 + 1 + elif effect == SEND_EFFECT_LITERAL: + argcount = ((byte >> 4) & 3) - 1 + return -argcount - 1 + 1 + elif effect == CLOSURE_EFFECT: + numArgs, numCopied = splitter[4, 4](descriptor) + return -numCopied + 1 + elif effect == ANYTHING_EFFECT: + opType = parameter_1 >> 5 + if opType in (0, 1): # send + return -(parameter_1 & 31) - 1 + 1 + elif opType in (2, 3, 4): # push + return 1 + elif opType in (5, 7): # top + return 0 + elif opType == 6: # pop + return -1 + else: + return effect diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index 1872e1b2..9524ee01 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -58,7 +58,7 @@ class W_CompiledMethod(W_AbstractObjectWithIdentityHash): # Main method content "bytes", "literals", # Additional info about the method - "lookup_selector", "compiledin_class", "lookup_class" ] + "lookup_selector", "compiledin_class", "lookup_class", "_frame_size" ] lookup_selector = "" lookup_class = None @@ -95,6 +95,7 @@ def __init__(self, space, bytecount=0, header=0): W_AbstractObjectWithIdentityHash.__init__(self) self.lookup_selector = "unknown%d" % self.gethash() self.bytes = ["\x00"] * bytecount + self._frame_size = 0 self.setheader(space, header, initializing=True) self.post_init() @@ -140,10 +141,12 @@ def set_lookup_class_and_name(self, w_class, selector): def setbytes(self, bytes): self.bytes = bytes + self.update_frame_size() def setchar(self, index0, character): assert index0 >= 0 self.bytes[index0] = character + self.update_frame_size() # === Getters === @@ -185,7 +188,14 @@ def primitive(self): return self._primitive @jit.elidable_promote() - def compute_frame_size(self): + def frame_size(self): + return self._frame_size + + def update_frame_size(self): + from rsqueakvm.interpreter_bytecodes import compute_frame_size + self._frame_size = compute_frame_size(self.getbytes()) + self.argsize + self._tempsize + + def squeak_frame_size(self): # From blue book: normal mc have place for 12 temps+maxstack # mc for methods with islarge flag turned on 32 return 16 + self.islarge * 40 + self.argsize @@ -309,6 +319,7 @@ def _become(self, w_other): self._primitive, w_other._primitive = w_other._primitive, self._primitive self.literals, w_other.literals = w_other.literals, self.literals self._tempsize, w_other._tempsize = w_other._tempsize, self._tempsize + self._frame_size, w_other._frame_size = w_other._frame_size, self._frame_size self.bytes, w_other.bytes = w_other.bytes, self.bytes self.header, w_other.header = w_other.header, self.header self.literalsize, w_other.literalsize = w_other.literalsize, self.literalsize @@ -326,6 +337,7 @@ def _become(self, w_other): def clone(self, space): copy = self.__class__(space, 0, self.getheader()) copy.bytes = list(self.bytes) + copy._frame_size = self._frame_size copy.literals = list(self.literals) copy.compiledin_class = self.compiledin_class copy.lookup_selector = self.lookup_selector @@ -362,14 +374,17 @@ def str_content(self): return self.get_identifier_string() def bytecode_string(self, markBytecode=0): - from rsqueakvm.interpreter_bytecodes import BYTECODE_TABLE + from rsqueakvm.interpreter_bytecodes import BYTECODE_TABLE, BYTECODE_ARGUMENT_COUNT retval = "Bytecode:------------" j = 1 - for i in self.bytes: + idx = 0 + while idx < len(self.bytes): + i = self.bytes[idx] retval += '\n' retval += '->' if j is markBytecode else ' ' retval += ('%0.2i: 0x%0.2x(%0.3i) ' % (j, ord(i), ord(i))) + BYTECODE_TABLE[ord(i)].__name__ - j += 1 + idx += BYTECODE_ARGUMENT_COUNT[ord(i)] + 1 + j += BYTECODE_ARGUMENT_COUNT[ord(i)] + 1 retval += "\n---------------------" return retval diff --git a/rsqueakvm/squeakimage.py b/rsqueakvm/squeakimage.py index 52bb6ac5..0b1e4d7b 100644 --- a/rsqueakvm/squeakimage.py +++ b/rsqueakvm/squeakimage.py @@ -1050,7 +1050,7 @@ def frame_size_for(self, obj): if obj.getclass(self.space).is_same_object(self.space.w_MethodContext): w_method = obj.fetch(self.space, constants.MTHDCTX_METHOD) if not w_method.is_nil(self.space): - w_method.compute_frame_size() + w_method.squeak_frame_size() elif obj.getclass(self.space).is_same_object(self.space.w_BlockContext): w_home = obj.fetch(self.space, constants.BLKCTX_HOME_INDEX) return self.frame_size_for(w_home) diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 9157006a..d3bf0d33 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -472,13 +472,10 @@ def stacksize(self): def full_stacksize(self): if not self.pure_is_block_context(): - # Magic numbers... Takes care of cases where reflective - # code writes more than actual stack size method = self.w_method() assert isinstance(method, W_CompiledMethod), "context method is not a W_CompiledMethod" - stacksize = method.compute_frame_size() + stacksize = method.frame_size() else: - # TODO why not use method.compute_frame_size for BlockContext too? stacksize = self.stacksize() # no temps return stacksize @@ -804,7 +801,7 @@ class __extend__(ContextPartShadow): def build_method_context(space, w_method, w_receiver, arguments=[], closure=None, s_fallback=None): w_method = jit.promote(w_method) - size = w_method.compute_frame_size() + constants.MTHDCTX_TEMP_FRAME_START + size = w_method.frame_size() + constants.MTHDCTX_TEMP_FRAME_START ctx = ContextPartShadow(space, None, size, space.w_MethodContext) if s_fallback is not None: From 89687c3609c07dc9bb8f1091e5a88aca42ff2ef5 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 7 Mar 2017 16:11:44 +0100 Subject: [PATCH 15/52] fix tests and frame size calculation, as well as a couple of stack errors this exposed in primitives --- rsqueakvm/interpreter.py | 1 + rsqueakvm/interpreter_bytecodes.py | 38 ++++++++++++++++------------ rsqueakvm/model/compiled_methods.py | 3 +++ rsqueakvm/primitives/control.py | 2 +- rsqueakvm/primitives/input_output.py | 2 +- rsqueakvm/test/test_primitives.py | 14 +++++----- rsqueakvm/test/util.py | 8 +++--- 7 files changed, 39 insertions(+), 29 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index d25995c6..103b9435 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -571,6 +571,7 @@ def create_toplevel_context(self, w_receiver, selector="", w_selector=None, w_ar w_method.setbytes([chr(131), chr(len(w_arguments) << 5 + 0), chr(124)]) #returnTopFromMethodBytecode w_method.set_lookup_class_and_name(w_receiver.getclass(self.space), "Interpreter.perform") + w_method.set_frame_size(len(w_arguments) + 1) s_frame = ContextPartShadow.build_method_context(self.space, w_method, w_receiver) s_frame.push(w_receiver) diff --git a/rsqueakvm/interpreter_bytecodes.py b/rsqueakvm/interpreter_bytecodes.py index 7a33083e..f00c18bd 100644 --- a/rsqueakvm/interpreter_bytecodes.py +++ b/rsqueakvm/interpreter_bytecodes.py @@ -801,22 +801,19 @@ def initialize_return_bytecodes(): RETURN_BYTECODES = initialize_return_bytecodes() -def initialize_conditional_jump_bytecodes(): +def initialize_jump_bytecodes(): result = [] - cond_jump_bytecodes = ( "shortConditionalJumpBytecode", - "longJumpIfTrueBytecode", - "longJumpIfFalseBytecode" ) for entry in BYTECODE_RANGES: if len(entry) == 3: - if entry[1] in cond_jump_bytecodes: + if "Jump" in entry[1]: result.append(entry[0]) else: - if entry[2] in cond_jump_bytecodes: + if "Jump" in entry[2]: result.extend(range(entry[0], entry[1]+1)) assert len(result) > 0 return result -CONDITIONAL_JUMP_BYTECODES = initialize_conditional_jump_bytecodes() +JUMP_BYTECODES = initialize_jump_bytecodes() def initialize_bytecode_effects(): @@ -845,28 +842,33 @@ def _compute_frame_size(bytecode, offset): while idx < len(bytecode): byte = ord(bytecode[idx]) parameters = BYTECODE_ARGUMENT_COUNT[byte] + effect = BYTECODE_EFFECTS[byte] if parameters > 0: parameter_1 = ord(bytecode[idx + 1]) else: parameter_1 = 0 - if byte in CONDITIONAL_JUMP_BYTECODES: + if byte in JUMP_BYTECODES: if parameters == 0: jump = (byte & 7) + 1 elif parameters == 1: - jump = ((byte & 3) << 8) + parameter_1 + if effect == 0: + # UnconditionalJump + jump = (((byte & 7) - 4) << 8) + parameter_1 + else: + jump = ((byte & 3) << 8) + parameter_1 else: assert False - szA = _compute_frame_size(bytecode, idx + parameters + 1) - szB = _compute_frame_size(bytecode, idx + parameters + 1 + jump) - return max_size + max(szA, szB) + if jump > 0: + szA = _compute_frame_size(bytecode, idx + parameters + 1) + szB = _compute_frame_size(bytecode, idx + parameters + 1 + jump) + return max_size + max(szA, szB) elif byte in RETURN_BYTECODES: return max_size else: - effect = BYTECODE_EFFECTS[byte] size += _compute_effect_of_bytecode(byte, effect, parameter_1) max_size = max(size, max_size) - idx += parameters + 1 - return size + idx += parameters + 1 + return max_size def _compute_effect_of_bytecode(byte, effect, parameter_1): @@ -874,6 +876,8 @@ def _compute_effect_of_bytecode(byte, effect, parameter_1): arraySize, popIntoArray = splitter[7, 1](parameter_1) if popIntoArray == 1: return -arraySize + 1 + else: + return 1 elif effect == SEND_EFFECT_SHIFT5: argcount = parameter_1 >> 5 return -argcount - 1 + 1 @@ -884,7 +888,7 @@ def _compute_effect_of_bytecode(byte, effect, parameter_1): argcount = ((byte >> 4) & 3) - 1 return -argcount - 1 + 1 elif effect == CLOSURE_EFFECT: - numArgs, numCopied = splitter[4, 4](descriptor) + numArgs, numCopied = splitter[4, 4](parameter_1) return -numCopied + 1 elif effect == ANYTHING_EFFECT: opType = parameter_1 >> 5 @@ -896,5 +900,7 @@ def _compute_effect_of_bytecode(byte, effect, parameter_1): return 0 elif opType == 6: # pop return -1 + else: + assert False else: return effect diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index 9524ee01..b4fbbf01 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -191,6 +191,9 @@ def primitive(self): def frame_size(self): return self._frame_size + def set_frame_size(self, size): + self._frame_size = size + def update_frame_size(self): from rsqueakvm.interpreter_bytecodes import compute_frame_size self._frame_size = compute_frame_size(self.getbytes()) + self.argsize + self._tempsize diff --git a/rsqueakvm/primitives/control.py b/rsqueakvm/primitives/control.py index adda9b1c..3f92d025 100644 --- a/rsqueakvm/primitives/control.py +++ b/rsqueakvm/primitives/control.py @@ -53,7 +53,7 @@ def func(interp, s_frame, w_rcvr): from rpython.rlib.debug import attach_gdb attach_gdb() -@expose_primitive(CHANGE_CLASS, unwrap_spec=[object, object], no_result=True) +@expose_primitive(CHANGE_CLASS, unwrap_spec=[object, object]) def func(interp, s_frame, w_rcvr, w_arg): w_arg_class = w_arg.getclass(interp.space) w_rcvr_class = w_rcvr.getclass(interp.space) diff --git a/rsqueakvm/primitives/input_output.py b/rsqueakvm/primitives/input_output.py index ae10c7b9..8002c792 100644 --- a/rsqueakvm/primitives/input_output.py +++ b/rsqueakvm/primitives/input_output.py @@ -97,7 +97,7 @@ def func(interp, s_frame, argcount, w_method): @expose_primitive(SNAPSHOT, clean_stack=False, no_result=True) def func(interp, s_frame, argcount): - s_frame.pop_n(argcount) + s_frame.pop_n(argcount + 1) s_frame.push(interp.space.w_true) # leaving true on the frame as return value for resuming image from rsqueakvm.squeakimage import SpurImageWriter diff --git a/rsqueakvm/test/test_primitives.py b/rsqueakvm/test/test_primitives.py index ed14e5f8..485d59b7 100644 --- a/rsqueakvm/test/test_primitives.py +++ b/rsqueakvm/test/test_primitives.py @@ -696,8 +696,8 @@ def test_directory_delimitor(): assert space.unwrap_char_as_byte(w_c) == os.path.sep def test_primitive_closure_copyClosure(): - w_frame, s_frame = new_frame("") - w_outer_frame, s_initial_context = new_frame("") + w_frame, s_frame = new_frame("pppppppp") + w_outer_frame, s_initial_context = new_frame("pppppppp") w_block = prim(CLOSURE_COPY_WITH_COPIED_VALUES, map(wrap, [w_outer_frame, 2, [wrap(1), wrap(2)]]), w_frame) assert not w_block.is_nil(space) @@ -721,7 +721,7 @@ def test_primitive_closure_copyClosure(): # prim_fails(STRING_REPLACE, [['a', 'b'], 1, 4, "ccccc", 1]) def build_up_closure_environment(args, copiedValues=[]): - w_frame, s_initial_context = new_frame("") + w_frame, s_initial_context = new_frame("pppppppp") size_arguments = len(args) closure = space.newClosure(w_frame, 4, #pc @@ -775,7 +775,7 @@ def test_primitive_some_object(): def test_primitive_next_object(): someInstances = map(space.wrap_list, [[2], [3]]) - w_frame, s_context = new_frame("") + w_frame, s_context = new_frame("pppppppp") s_context.push(space.w_nil) interp = InterpreterForTest(space) @@ -791,7 +791,7 @@ def test_primitive_next_object(): def test_primitive_next_instance(): someInstances = map(space.wrap_list, [[2], [3]]) - w_frame, s_context = new_frame("") + w_frame, s_context = new_frame("pppppppp") s_context.push(space.w_Array) interp = InterpreterForTest(space) @@ -807,7 +807,7 @@ def test_primitive_next_instance(): def test_primitive_next_instance_wo_some_instance_in_same_frame(): someInstances = map(space.wrap_list, [[2], [3]]) - w_frame, s_context = new_frame("") + w_frame, s_context = new_frame("pppppppp") s_context.push(space.w_Array) interp = InterpreterForTest(space) @@ -831,7 +831,7 @@ def quick_check_for_interrupt(s_frame, dec=1): def step(s_frame): raise Stepping - w_frame, s_initial_context = new_frame("") + w_frame, s_initial_context = new_frame("pppppppp") closure = space.newClosure(w_frame, 4, 0, []) s_frame = w_frame.as_context_get_shadow(space) diff --git a/rsqueakvm/test/util.py b/rsqueakvm/test/util.py index a566d225..83977242 100644 --- a/rsqueakvm/test/util.py +++ b/rsqueakvm/test/util.py @@ -132,11 +132,10 @@ def get_opcode_chr(n): opcode = entry[0] + n assert entry[0] <= opcode <= entry[1] return chr(opcode) - setattr(mod, name, get_opcode_chr) + setattr(mod, entry[2], get_opcode_chr) for entry in interpreter_bytecodes.BYTECODE_RANGES: - name = entry[-1] - if len(entry) == 2: # no range - setattr(mod, name, chr(entry[0])) + if len(entry) == 3: # no range + setattr(mod, entry[1], chr(entry[0])) else: make_getter(entry) @@ -435,6 +434,7 @@ def make_method(self, bytes, literals=None, numargs=0): if literals is None: literals = [W_PointersObject(self, None, 2)] w_method.setliterals(literals) + w_method.update_frame_size() return w_method def make_frame(self, bytes, literals=None, receiver=None, args=[]): From c8a58d2c04f769f3b6d5737192278586b9a84f53 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 7 Mar 2017 16:23:16 +0100 Subject: [PATCH 16/52] skip two bytes for Spur prim bytecode 139 as params --- rsqueakvm/interpreter_bytecodes.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rsqueakvm/interpreter_bytecodes.py b/rsqueakvm/interpreter_bytecodes.py index f00c18bd..061a1a0b 100644 --- a/rsqueakvm/interpreter_bytecodes.py +++ b/rsqueakvm/interpreter_bytecodes.py @@ -561,14 +561,10 @@ def _activate_unwind_context(self, interp): def unknownBytecode(self, interp, current_bytecode): raise error.MissingBytecode("unknownBytecode") - @bytecode_implementation() + @bytecode_implementation(parameter_bytes=2) def callPrimitiveBytecode(self, interp, current_bytecode): if not interp.image.version.is_spur: raise error.MissingBytecode("unknownBytecode") - else: - # skip next two bytes which belong to this bytecode - # then continue with the next bytecodes (fallback code) - self._jump(2) # ====== Jump bytecodes ====== From 1f6bf27b81bf121f41ad0bf4f95b35d1074fb0ca Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 8 Mar 2017 10:07:12 +0100 Subject: [PATCH 17/52] fix tests and extend stack size computation --- rsqueakvm/interpreter_bytecodes.py | 47 +++++++++++++++++++++++------ rsqueakvm/model/compiled_methods.py | 7 +++-- rsqueakvm/storage_contexts.py | 5 +-- rsqueakvm/test/test_interpreter.py | 47 ++++++++++++++++++++--------- rsqueakvm/test/test_shadow.py | 1 + rsqueakvm/test/util.py | 6 ++-- 6 files changed, 82 insertions(+), 31 deletions(-) diff --git a/rsqueakvm/interpreter_bytecodes.py b/rsqueakvm/interpreter_bytecodes.py index 061a1a0b..451e222c 100644 --- a/rsqueakvm/interpreter_bytecodes.py +++ b/rsqueakvm/interpreter_bytecodes.py @@ -562,7 +562,7 @@ def unknownBytecode(self, interp, current_bytecode): raise error.MissingBytecode("unknownBytecode") @bytecode_implementation(parameter_bytes=2) - def callPrimitiveBytecode(self, interp, current_bytecode): + def callPrimitiveBytecode(self, interp, current_bytecode, i, j): if not interp.image.version.is_spur: raise error.MissingBytecode("unknownBytecode") @@ -828,14 +828,18 @@ def initialize_bytecode_effects(): def compute_frame_size(bytecode): - return _compute_frame_size(bytecode, 0) + return _compute_frame_size(bytecode, 0)[0] -def _compute_frame_size(bytecode, offset): +def _compute_frame_size(bytecode, offset, stop=-1): size = 0 max_size = 0 idx = offset - while idx < len(bytecode): + if stop == -1: + stop = len(bytecode) + else: + stop = min(stop, len(bytecode)) + while idx < stop: byte = ord(bytecode[idx]) parameters = BYTECODE_ARGUMENT_COUNT[byte] effect = BYTECODE_EFFECTS[byte] @@ -843,6 +847,7 @@ def _compute_frame_size(bytecode, offset): parameter_1 = ord(bytecode[idx + 1]) else: parameter_1 = 0 + idx += parameters if byte in JUMP_BYTECODES: if parameters == 0: jump = (byte & 7) + 1 @@ -855,16 +860,38 @@ def _compute_frame_size(bytecode, offset): else: assert False if jump > 0: - szA = _compute_frame_size(bytecode, idx + parameters + 1) - szB = _compute_frame_size(bytecode, idx + parameters + 1 + jump) - return max_size + max(szA, szB) + nextpc = idx + 1 + szA, idxA = _compute_frame_size(bytecode, nextpc, stop=nextpc + jump) + if idxA >= nextpc + jump: + # branch A ran over to branch B. just continue + max_size = max_size + szA + idx = idxA + else: + # Branch A returned before reaching branch B's jump target. + # We continue calculating with branch B. + szB, idxB = _compute_frame_size(bytecode, nextpc + jump) + return max_size + max(szA, szB), idxB elif byte in RETURN_BYTECODES: - return max_size + return max_size, idx + elif effect == CLOSURE_EFFECT: + size += _compute_effect_of_bytecode(byte, effect, parameter_1) + if idx > len(bytecode) - 2: + # parameter bytes missing, 0 size closure + max_size = max(size, max_size) + else: + assert parameters == 3 + j, i = ord(bytecode[idx - 1]), ord(bytecode[idx]) + numArgs, numCopied = splitter[4, 4](parameter_1) + blockSize = (j << 8) | i + nextpc = idx + 1 + szBlock, _ = _compute_frame_size(bytecode, nextpc, stop=nextpc + blockSize) + szBlock += numArgs + numCopied + max_size = max(max(size, max_size), szBlock) else: size += _compute_effect_of_bytecode(byte, effect, parameter_1) max_size = max(size, max_size) - idx += parameters + 1 - return max_size + idx += 1 + return max_size, idx def _compute_effect_of_bytecode(byte, effect, parameter_1): diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index b4fbbf01..c98e725a 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -218,8 +218,11 @@ def end_pc(self): @jit.elidable_promote() def fetch_bytecode(self, pc): - assert pc >= 0 and pc < len(self.bytes) - return self.bytes[pc] + # assert pc >= 0 and pc < len(self.bytes) + try: + return self.bytes[pc] + except IndexError: + return chr(0) def compiled_in(self): # This method cannot be constant/elidable. Looking up the compiledin-class from diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index d3bf0d33..d732f89f 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -311,8 +311,9 @@ def store_stackpointer(self, size): assert size >= 0, "trying to store negative stackpointer" self.pop_n(depth - size) else: - for i in range(depth, size): - self.push(self.space.w_nil) + self.store_stack_ptr(size) + # for i in range(depth, size): + # self.push(self.space.w_nil) def stackdepth(self): return self.stack_ptr() diff --git a/rsqueakvm/test/test_interpreter.py b/rsqueakvm/test/test_interpreter.py index 4288f11a..617d7582 100644 --- a/rsqueakvm/test/test_interpreter.py +++ b/rsqueakvm/test/test_interpreter.py @@ -132,6 +132,7 @@ def test_create_frame(): w_method.islarge = 1 w_method.argsize = 2 w_method._tempsize = 8 + w_method.update_frame_size() s_frame = w_method.create_frame(space, w("receiver"), [w("foo"), w("bar")]) w_frame = s_frame.w_self() assert s_frame.w_receiver().unwrap_string(None) == "receiver" @@ -145,7 +146,7 @@ def test_create_frame(): assert s_frame.fetch_next_bytecode() == ord("l") def test_push_pop(): - _, frame = new_frame("") + _, frame = new_frame("ppp") frame.push(w(12)) frame.push(w(34)) frame.push(w(56)) @@ -449,9 +450,11 @@ def sendBytecodesTest(w_class, w_object, bytecodes): w_method.bytes = pushConstantOneBytecode + bytecode literals = fakeliterals(space, "foo") w_foo = literals[0] + w_method.update_frame_size() shadow.installmethod(w_foo, w_method) - w_frame, s_frame = new_frame(bytecodes) + w_frame, s_frame = new_frame(pushConstantOneBytecode + bytecodes) s_frame.w_method().setliterals(literals) + s_frame.store_pc(1) # skip over the push One bytecode, we push the object next s_frame.push(w_object) w_active_context = step_in_interp(s_frame) s_active_context = w_active_context.as_context_get_shadow(space) @@ -481,6 +484,7 @@ def test_fibWithArgument(): method._tempsize = 1 literals = fakeliterals(space, "fib:") method.setliterals(literals) + method.update_frame_size() shadow.installmethod(literals[0], method) w_object = shadow.new() w_frame, s_frame = new_frame(sendLiteralSelectorBytecode(16) + returnTopFromMethodBytecode) @@ -618,6 +622,7 @@ def test_callPrimitiveAndPush_fallback(): w_method.argsize = 1 w_method._tempsize = 1 w_method.literalsize = 1 + w_method.update_frame_size() w_symbol = fakesymbol("+") shadow.installmethod(w_symbol, w_method) # slightly evil @@ -634,12 +639,15 @@ def test_callPrimitiveAndPush_fallback(): assert s_active_context.stack() == [] def test_bytecodePrimBool(): - w_frame, s_frame = new_frame(bytecodePrimLessThan + - bytecodePrimGreaterThan + - bytecodePrimLessOrEqual + - bytecodePrimGreaterOrEqual + - bytecodePrimEqual + - bytecodePrimNotEqual) + w_frame, s_frame = new_frame( + pushConstantOneBytecode * 7 + # just for frame size + bytecodePrimLessThan + + bytecodePrimGreaterThan + + bytecodePrimLessOrEqual + + bytecodePrimGreaterOrEqual + + bytecodePrimEqual + + bytecodePrimNotEqual) + s_frame.store_pc(7) # skip pushing constants for i in range(6): s_frame.push(space.w_one) s_frame.push(space.w_two) @@ -663,6 +671,7 @@ def test_singleExtendedSuperBytecode(bytecode=singleExtendedSuperBytecode + chr( # which does a call to its super meth1 = W_PreSpurCompiledMethod(space, 2) meth1.bytes = pushReceiverBytecode + bytecode + meth1.update_frame_size() literals = fakeliterals(space, "foo") foo = literals[0] meth1.setliterals(literals) @@ -671,6 +680,7 @@ def test_singleExtendedSuperBytecode(bytecode=singleExtendedSuperBytecode + chr( meth2 = W_PreSpurCompiledMethod(space, 2) meth2.bytes = pushReceiverBytecode + bytecode meth2.setliterals(fakeliterals(space, foo)) + meth2.update_frame_size() w_super.as_class_get_shadow(space).installmethod(foo, meth2) meth3 = W_PreSpurCompiledMethod(space, 0) w_supersuper.as_class_get_shadow(space).installmethod(foo, meth3) @@ -877,7 +887,8 @@ def test(): # Closure Bytecodes def test_bc_pushNewArrayBytecode(bytecode=pushNewArrayBytecode): - w_frame, s_frame = new_frame(bytecode + chr(0x83)) + w_frame, s_frame = new_frame("ppp" + bytecode + chr(0x83)) + s_frame.store_pc(3) # skip over pushes, they are just for frame size s_frame.push(w(fakeliterals(space, "egg"))) s_frame.push(w(fakeliterals(space, "bar"))) s_frame.push(w(fakeliterals(space, "baz"))) @@ -889,7 +900,8 @@ def test_bc_pushNewArrayBytecode(bytecode=pushNewArrayBytecode): assert space.unwrap_array(array.at0(space, 2)) == fakeliterals(space, "baz") def test_bc_pushNewArrayBytecode_noPopIntoArray(bytecode=pushNewArrayBytecode): - w_frame, s_frame = new_frame(bytecode + chr(0x02)) + w_frame, s_frame = new_frame("pp" + bytecode + chr(0x02)) + s_frame.store_pc(2) # skip over pushes, they are just for frame size s_frame.push(w("egg")) s_frame.push(w("bar")) step_in_interp(s_frame) @@ -915,7 +927,8 @@ def test_bc_pushRemoteTempLongBytecode(bytecode=pushRemoteTempLongBytecode): def setupTempArrayAndContext(bytecode): # both indizes are 0-relative - w_frame, s_frame = new_frame(bytecode + chr(2) + chr(1)) + w_frame, s_frame = new_frame("pp" + bytecode + chr(2) + chr(1)) + s_frame.store_pc(2) # skip over pushes, they are just for frame size s_frame.push(w(fakeliterals(space, "english"))) s_frame.push(w(fakeliterals(space, "bar"))) temp_array = space.w_Array.as_class_get_shadow(interp.space).new(3) @@ -950,7 +963,8 @@ def test_bc_pushClosureCopyCopied0ValuesBytecode(bytecode=pushClosureCopyCopiedV assert closure.w_outerContext() is s_frame._w_self def test_bc_pushClosureCopyCopied2ValuesBytecode(bytecode=pushClosureCopyCopiedValuesBytecode): - w_frame, s_frame = new_frame(bytecode + chr(0x23) + chr(0) + chr(0)) + w_frame, s_frame = new_frame("pp" + bytecode + chr(0x23) + chr(0) + chr(0)) + s_frame.store_pc(2) # skip over pushes, they are just for frame size s_frame.push(w("english")) s_frame.push(w("bar")) pc = s_frame.pc() @@ -990,7 +1004,8 @@ def test(): def test_frame_dirty_if_active(): bytes = reduce(operator.add, map(chr, [0x84, 0xc0, 0x00])) - w_frame, s_frame = new_frame(bytes) + w_frame, s_frame = new_frame("p" + bytes) + s_frame.store_pc(1) s_frame.store_w_receiver(w_frame) s_frame.push(w_frame) s_frame.set_state(storage_contexts.ActiveContext) @@ -999,7 +1014,8 @@ def test_frame_dirty_if_active(): def test_frame_not_dirty_if_inactive(): bytes = reduce(operator.add, map(chr, [0x84, 0xc0, 0x00])) - w_frame, s_frame = new_frame(bytes) + w_frame, s_frame = new_frame("p" + bytes) + s_frame.store_pc(1) w_other_frame, s_other_frame = new_frame("") s_frame.store_w_receiver(w_other_frame) s_frame.push(w_frame) @@ -1010,7 +1026,8 @@ def test_frame_not_dirty_if_inactive(): def test_raise_NonVirtualReturn_on_dirty_frame(): bytes = reduce(operator.add, map(chr, [0x84, 0xc0, 0x00])) + returnTopFromMethodBytecode - w_frame, s_frame = new_frame(bytes) + w_frame, s_frame = new_frame("p" + bytes) + s_frame.store_pc(1) s_frame.store_w_receiver(w_frame) s_frame.push(w_frame) diff --git a/rsqueakvm/test/test_shadow.py b/rsqueakvm/test/test_shadow.py index 3c8ffeee..864581f1 100644 --- a/rsqueakvm/test/test_shadow.py +++ b/rsqueakvm/test/test_shadow.py @@ -105,6 +105,7 @@ def create_method(tempsize=3,argsize=2, bytes="abcde"): w_m._tempsize = tempsize w_m.argsize = argsize w_m.literalsize = 2 + w_m.update_frame_size() return w_m def methodcontext(w_sender=None, pc=13, stackpointer=0, stacksize=5, diff --git a/rsqueakvm/test/util.py b/rsqueakvm/test/util.py index 83977242..bff7b7e4 100644 --- a/rsqueakvm/test/util.py +++ b/rsqueakvm/test/util.py @@ -419,8 +419,10 @@ def find_symbol_in_methoddict(self, string, cls, fail=True): # ============ Helpers for executing ============ def wrap_frame(self, s_frame): - # Add a toplevel frame around s_frame to properly return. - toplevel_frame = self.make_method([0x7c]).create_frame(self, self.w(0), []) + # Add a toplevel frame around s_frame to properly return, with one push + # bytecode (that we skip) so we have enough room on the stack + toplevel_frame = self.make_method([112, 0x7c]).create_frame(self, self.w(0), []) + toplevel_frame.store_pc(1) s_frame.store_s_sender(toplevel_frame) def make_method(self, bytes, literals=None, numargs=0): From 360aaff69eed4cd696835878663660c077079f0d Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 13 Mar 2017 10:20:32 +0100 Subject: [PATCH 18/52] switch to dynamic computation of frame size --- rsqueakvm/interpreter.py | 20 +- rsqueakvm/interpreter_bytecodes.py | 331 ++++++++-------------------- rsqueakvm/model/compiled_methods.py | 32 ++- rsqueakvm/storage_contexts.py | 4 +- rsqueakvm/test/test_interpreter.py | 47 ++-- rsqueakvm/test/test_primitives.py | 14 +- rsqueakvm/test/test_shadow.py | 1 - rsqueakvm/test/util.py | 14 +- 8 files changed, 143 insertions(+), 320 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index 103b9435..9abf651b 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -410,26 +410,17 @@ def step(self, context): if not objectmodel.we_are_translated(): if USE_SIGUSR1: self.check_sigusr(context) - stackdepth_pre = context.stackdepth() bytecode = context.fetch_next_bytecode() for entry in UNROLLING_BYTECODE_RANGES: - if len(entry) == 3: - bc, methname, _ = entry + if len(entry) == 2: + bc, methname = entry if bytecode == bc: - result = getattr(context, methname)(self, bytecode) - if not objectmodel.we_are_translated(): - stackdepth_after = context.stackdepth() - assert context.last_effect == (stackdepth_after - stackdepth_pre) - return result + return getattr(context, methname)(self, bytecode) else: - start, stop, methname, _ = entry + start, stop, methname = entry if start <= bytecode <= stop: - result = getattr(context, methname)(self, bytecode) - if not objectmodel.we_are_translated(): - stackdepth_after = context.stackdepth() - assert context.last_effect == (stackdepth_after - stackdepth_pre) - return result + return getattr(context, methname)(self, bytecode) assert 0, "unreachable" # ============== Methods for handling user interrupts ============== @@ -571,7 +562,6 @@ def create_toplevel_context(self, w_receiver, selector="", w_selector=None, w_ar w_method.setbytes([chr(131), chr(len(w_arguments) << 5 + 0), chr(124)]) #returnTopFromMethodBytecode w_method.set_lookup_class_and_name(w_receiver.getclass(self.space), "Interpreter.perform") - w_method.set_frame_size(len(w_arguments) + 1) s_frame = ContextPartShadow.build_method_context(self.space, w_method, w_receiver) s_frame.push(w_receiver) diff --git a/rsqueakvm/interpreter_bytecodes.py b/rsqueakvm/interpreter_bytecodes.py index 451e222c..5cda459f 100644 --- a/rsqueakvm/interpreter_bytecodes.py +++ b/rsqueakvm/interpreter_bytecodes.py @@ -37,10 +37,9 @@ def bytecode_implementation_wrapper(self, interp, current_bytecode): i = i + 1 # This is a good place to step through bytecodes. - self.debug_bytecode(interp, current_bytecode, parameters) + self.debug_bytecode(interp) return actual_implementation_method(self, interp, current_bytecode, *parameters) bytecode_implementation_wrapper.func_name = actual_implementation_method.func_name - bytecode_implementation_wrapper.parameter_bytes = parameter_bytes return bytecode_implementation_wrapper return bytecode_implementation_decorator @@ -561,10 +560,14 @@ def _activate_unwind_context(self, interp): def unknownBytecode(self, interp, current_bytecode): raise error.MissingBytecode("unknownBytecode") - @bytecode_implementation(parameter_bytes=2) - def callPrimitiveBytecode(self, interp, current_bytecode, i, j): + @bytecode_implementation() + def callPrimitiveBytecode(self, interp, current_bytecode): if not interp.image.version.is_spur: raise error.MissingBytecode("unknownBytecode") + else: + # skip next two bytes which belong to this bytecode + # then continue with the next bytecodes (fallback code) + self._jump(2) # ====== Jump bytecodes ====== @@ -655,105 +658,93 @@ def longJumpIfFalseBytecode(self, interp, current_bytecode, parameter): bytecodePrimPointX = make_send_selector_bytecode("x", 0) bytecodePrimPointY = make_send_selector_bytecode("y", 0) - def debug_bytecode(self, interp, current_bytecode, parameters): - if not objectmodel.we_are_translated(): - self.last_effect = _compute_effect_of_bytecode( - current_bytecode, - BYTECODE_EFFECTS[current_bytecode], - parameters[0] if len(parameters) > 0 else 0 - ) - - -SEND_EFFECT_LITERAL = 0xff -SEND_EFFECT_SHIFT5 = 0xfe -SEND_EFFECT_SHIFT6 = 0xfd -ARRAY_EFFECT = 0xfc -CLOSURE_EFFECT = 0xfb -ANYTHING_EFFECT = 0xfa -# bytecode or bytecode range, name, stack effect, arguments + def debug_bytecode(self, interp): + # Hook used in interpreter_debugging + pass + BYTECODE_RANGES = [ - ( 0, 15, "pushReceiverVariableBytecode", +1), - ( 16, 31, "pushTemporaryVariableBytecode", +1), - ( 32, 63, "pushLiteralConstantBytecode", +1), - ( 64, 95, "pushLiteralVariableBytecode", +1), - ( 96, 103, "storeAndPopReceiverVariableBytecode", -1), - (104, 111, "storeAndPopTemporaryVariableBytecode", -1), - (112, "pushReceiverBytecode", +1), - (113, "pushConstantTrueBytecode", +1), - (114, "pushConstantFalseBytecode", +1), - (115, "pushConstantNilBytecode", +1), - (116, "pushConstantMinusOneBytecode", +1), - (117, "pushConstantZeroBytecode", +1), - (118, "pushConstantOneBytecode", +1), - (119, "pushConstantTwoBytecode", +1), - (120, "returnReceiverBytecode", 0), - (121, "returnTrueBytecode", 0), - (122, "returnFalseBytecode", 0), - (123, "returnNilBytecode", 0), - (124, "returnTopFromMethodBytecode", -1), - (125, "returnTopFromBlockBytecode", -1), - (126, "unknownBytecode", 0), - (127, "unknownBytecode", 0), - (128, "extendedPushBytecode", +1), - (129, "extendedStoreBytecode", 0), - (130, "extendedStoreAndPopBytecode", -1), - (131, "singleExtendedSendBytecode", SEND_EFFECT_SHIFT5), - (132, "doubleExtendedDoAnythingBytecode", ANYTHING_EFFECT), - (133, "singleExtendedSuperBytecode", SEND_EFFECT_SHIFT5), - (134, "secondExtendedSendBytecode", SEND_EFFECT_SHIFT6), - (135, "popStackBytecode", -1), - (136, "duplicateTopBytecode", +1), - (137, "pushActiveContextBytecode", +1), - (138, "pushNewArrayBytecode", ARRAY_EFFECT), - (139, "callPrimitiveBytecode", 0), - (140, "pushRemoteTempLongBytecode", +1), - (141, "storeRemoteTempLongBytecode", 0), - (142, "storeAndPopRemoteTempLongBytecode", -1), - (143, "pushClosureCopyCopiedValuesBytecode", CLOSURE_EFFECT), - (144, 151, "shortUnconditionalJumpBytecode", 0), - (152, 159, "shortConditionalJumpBytecode", -1), - (160, 167, "longUnconditionalJumpBytecode", 0), - (168, 171, "longJumpIfTrueBytecode", -1), - (172, 175, "longJumpIfFalseBytecode", -1), - (176, "bytecodePrimAdd", -1), - (177, "bytecodePrimSubtract", -1), - (178, "bytecodePrimLessThan", -1), - (179, "bytecodePrimGreaterThan", -1), - (180, "bytecodePrimLessOrEqual", -1), - (181, "bytecodePrimGreaterOrEqual", -1), - (182, "bytecodePrimEqual", -1), - (183, "bytecodePrimNotEqual", -1), - (184, "bytecodePrimMultiply", -1), - (185, "bytecodePrimDivide", -1), - (186, "bytecodePrimMod", -1), - (187, "bytecodePrimMakePoint", -1), - (188, "bytecodePrimBitShift", -1), - (189, "bytecodePrimDiv", -1), - (190, "bytecodePrimBitAnd", -1), - (191, "bytecodePrimBitOr", -1), - (192, "bytecodePrimAt", -1), - (193, "bytecodePrimAtPut", -2), - (194, "bytecodePrimSize", 0), - (195, "bytecodePrimNext", 0), - (196, "bytecodePrimNextPut", -1), - (197, "bytecodePrimAtEnd", 0), - (198, "bytecodePrimEquivalent", -1), - (199, "bytecodePrimClass", 0), - (200, "bytecodePrimBlockCopy", -1), - (201, "bytecodePrimValue", 0), - (202, "bytecodePrimValueWithArg", -1), - (203, "bytecodePrimDo", -1), - (204, "bytecodePrimNew", 0), - (205, "bytecodePrimNewWithArg", -1), - (206, "bytecodePrimPointX", 0), - (207, "bytecodePrimPointY", 0), - (208, 255, "sendLiteralSelectorBytecode", SEND_EFFECT_LITERAL), + ( 0, 15, "pushReceiverVariableBytecode"), + ( 16, 31, "pushTemporaryVariableBytecode"), + ( 32, 63, "pushLiteralConstantBytecode"), + ( 64, 95, "pushLiteralVariableBytecode"), + ( 96, 103, "storeAndPopReceiverVariableBytecode"), + (104, 111, "storeAndPopTemporaryVariableBytecode"), + (112, "pushReceiverBytecode"), + (113, "pushConstantTrueBytecode"), + (114, "pushConstantFalseBytecode"), + (115, "pushConstantNilBytecode"), + (116, "pushConstantMinusOneBytecode"), + (117, "pushConstantZeroBytecode"), + (118, "pushConstantOneBytecode"), + (119, "pushConstantTwoBytecode"), + (120, "returnReceiverBytecode"), + (121, "returnTrueBytecode"), + (122, "returnFalseBytecode"), + (123, "returnNilBytecode"), + (124, "returnTopFromMethodBytecode"), + (125, "returnTopFromBlockBytecode"), + (126, "unknownBytecode"), + (127, "unknownBytecode"), + (128, "extendedPushBytecode"), + (129, "extendedStoreBytecode"), + (130, "extendedStoreAndPopBytecode"), + (131, "singleExtendedSendBytecode"), + (132, "doubleExtendedDoAnythingBytecode"), + (133, "singleExtendedSuperBytecode"), + (134, "secondExtendedSendBytecode"), + (135, "popStackBytecode"), + (136, "duplicateTopBytecode"), + (137, "pushActiveContextBytecode"), + (138, "pushNewArrayBytecode"), + (139, "callPrimitiveBytecode"), + (140, "pushRemoteTempLongBytecode"), + (141, "storeRemoteTempLongBytecode"), + (142, "storeAndPopRemoteTempLongBytecode"), + (143, "pushClosureCopyCopiedValuesBytecode"), + (144, 151, "shortUnconditionalJumpBytecode"), + (152, 159, "shortConditionalJumpBytecode"), + (160, 167, "longUnconditionalJumpBytecode"), + (168, 171, "longJumpIfTrueBytecode"), + (172, 175, "longJumpIfFalseBytecode"), + (176, "bytecodePrimAdd"), + (177, "bytecodePrimSubtract"), + (178, "bytecodePrimLessThan"), + (179, "bytecodePrimGreaterThan"), + (180, "bytecodePrimLessOrEqual"), + (181, "bytecodePrimGreaterOrEqual"), + (182, "bytecodePrimEqual"), + (183, "bytecodePrimNotEqual"), + (184, "bytecodePrimMultiply"), + (185, "bytecodePrimDivide"), + (186, "bytecodePrimMod"), + (187, "bytecodePrimMakePoint"), + (188, "bytecodePrimBitShift"), + (189, "bytecodePrimDiv"), + (190, "bytecodePrimBitAnd"), + (191, "bytecodePrimBitOr"), + (192, "bytecodePrimAt"), + (193, "bytecodePrimAtPut"), + (194, "bytecodePrimSize"), + (195, "bytecodePrimNext"), + (196, "bytecodePrimNextPut"), + (197, "bytecodePrimAtEnd"), + (198, "bytecodePrimEquivalent"), + (199, "bytecodePrimClass"), + (200, "bytecodePrimBlockCopy"), + (201, "bytecodePrimValue"), + (202, "bytecodePrimValueWithArg"), + (203, "bytecodePrimDo"), + (204, "bytecodePrimNew"), + (205, "bytecodePrimNewWithArg"), + (206, "bytecodePrimPointX"), + (207, "bytecodePrimPointY"), + (208, 255, "sendLiteralSelectorBytecode"), ] def initialize_bytecode_names(): result = [None] * 256 for entry in BYTECODE_RANGES: - if len(entry) == 3: + if len(entry) == 2: result[entry[0]] = entry[1] else: for arg, pos in enumerate(range(entry[0], entry[1]+1)): @@ -765,28 +756,23 @@ def initialize_bytecode_names(): def initialize_bytecode_table(): result = [None] * 256 - args = [None] * 256 for entry in BYTECODE_RANGES: - if len(entry) == 3: - idx = 1 + if len(entry) == 2: positions = [entry[0]] else: - idx = 2 positions = range(entry[0], entry[1]+1) for pos in positions: - result[pos] = getattr(ContextPartShadow, entry[idx]) - args[pos] = result[pos].parameter_bytes + result[pos] = getattr(ContextPartShadow, entry[-1]) assert None not in result - assert None not in args - return result, args + return result # this table is only used for creating named bytecodes in tests and printing -BYTECODE_TABLE, BYTECODE_ARGUMENT_COUNT = initialize_bytecode_table() +BYTECODE_TABLE = initialize_bytecode_table() def initialize_return_bytecodes(): result = [] for entry in BYTECODE_RANGES: - if len(entry) == 3: + if len(entry) == 2: if entry[1].startswith('return'): result.append(entry[0]) else: @@ -796,134 +782,3 @@ def initialize_return_bytecodes(): return result RETURN_BYTECODES = initialize_return_bytecodes() - -def initialize_jump_bytecodes(): - result = [] - for entry in BYTECODE_RANGES: - if len(entry) == 3: - if "Jump" in entry[1]: - result.append(entry[0]) - else: - if "Jump" in entry[2]: - result.extend(range(entry[0], entry[1]+1)) - assert len(result) > 0 - return result - -JUMP_BYTECODES = initialize_jump_bytecodes() - - -def initialize_bytecode_effects(): - result = [None] * 256 - for entry in BYTECODE_RANGES: - if len(entry) == 3: - positions = [entry[0]] - else: - positions = range(entry[0], entry[1]+1) - for pos in positions: - result[pos] = entry[-1] - assert None not in result - return result - -BYTECODE_EFFECTS = initialize_bytecode_effects() - - -def compute_frame_size(bytecode): - return _compute_frame_size(bytecode, 0)[0] - - -def _compute_frame_size(bytecode, offset, stop=-1): - size = 0 - max_size = 0 - idx = offset - if stop == -1: - stop = len(bytecode) - else: - stop = min(stop, len(bytecode)) - while idx < stop: - byte = ord(bytecode[idx]) - parameters = BYTECODE_ARGUMENT_COUNT[byte] - effect = BYTECODE_EFFECTS[byte] - if parameters > 0: - parameter_1 = ord(bytecode[idx + 1]) - else: - parameter_1 = 0 - idx += parameters - if byte in JUMP_BYTECODES: - if parameters == 0: - jump = (byte & 7) + 1 - elif parameters == 1: - if effect == 0: - # UnconditionalJump - jump = (((byte & 7) - 4) << 8) + parameter_1 - else: - jump = ((byte & 3) << 8) + parameter_1 - else: - assert False - if jump > 0: - nextpc = idx + 1 - szA, idxA = _compute_frame_size(bytecode, nextpc, stop=nextpc + jump) - if idxA >= nextpc + jump: - # branch A ran over to branch B. just continue - max_size = max_size + szA - idx = idxA - else: - # Branch A returned before reaching branch B's jump target. - # We continue calculating with branch B. - szB, idxB = _compute_frame_size(bytecode, nextpc + jump) - return max_size + max(szA, szB), idxB - elif byte in RETURN_BYTECODES: - return max_size, idx - elif effect == CLOSURE_EFFECT: - size += _compute_effect_of_bytecode(byte, effect, parameter_1) - if idx > len(bytecode) - 2: - # parameter bytes missing, 0 size closure - max_size = max(size, max_size) - else: - assert parameters == 3 - j, i = ord(bytecode[idx - 1]), ord(bytecode[idx]) - numArgs, numCopied = splitter[4, 4](parameter_1) - blockSize = (j << 8) | i - nextpc = idx + 1 - szBlock, _ = _compute_frame_size(bytecode, nextpc, stop=nextpc + blockSize) - szBlock += numArgs + numCopied - max_size = max(max(size, max_size), szBlock) - else: - size += _compute_effect_of_bytecode(byte, effect, parameter_1) - max_size = max(size, max_size) - idx += 1 - return max_size, idx - - -def _compute_effect_of_bytecode(byte, effect, parameter_1): - if effect == ARRAY_EFFECT: - arraySize, popIntoArray = splitter[7, 1](parameter_1) - if popIntoArray == 1: - return -arraySize + 1 - else: - return 1 - elif effect == SEND_EFFECT_SHIFT5: - argcount = parameter_1 >> 5 - return -argcount - 1 + 1 - elif effect == SEND_EFFECT_SHIFT6: - argcount = parameter_1 >> 6 - return -argcount - 1 + 1 - elif effect == SEND_EFFECT_LITERAL: - argcount = ((byte >> 4) & 3) - 1 - return -argcount - 1 + 1 - elif effect == CLOSURE_EFFECT: - numArgs, numCopied = splitter[4, 4](parameter_1) - return -numCopied + 1 - elif effect == ANYTHING_EFFECT: - opType = parameter_1 >> 5 - if opType in (0, 1): # send - return -(parameter_1 & 31) - 1 + 1 - elif opType in (2, 3, 4): # push - return 1 - elif opType in (5, 7): # top - return 0 - elif opType == 6: # pop - return -1 - else: - assert False - else: - return effect diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index c98e725a..0bdfdc37 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -59,6 +59,7 @@ class W_CompiledMethod(W_AbstractObjectWithIdentityHash): "bytes", "literals", # Additional info about the method "lookup_selector", "compiledin_class", "lookup_class", "_frame_size" ] + _immutable_fields_ = ["_frame_size?"] lookup_selector = "" lookup_class = None @@ -141,12 +142,12 @@ def set_lookup_class_and_name(self, w_class, selector): def setbytes(self, bytes): self.bytes = bytes - self.update_frame_size() + self._frame_size = 0 def setchar(self, index0, character): assert index0 >= 0 self.bytes[index0] = character - self.update_frame_size() + self._frame_size = 0 # === Getters === @@ -189,14 +190,13 @@ def primitive(self): @jit.elidable_promote() def frame_size(self): - return self._frame_size - - def set_frame_size(self, size): - self._frame_size = size + if not jit.we_are_jitted(): + return self.squeak_frame_size() + else: + return self._frame_size - def update_frame_size(self): - from rsqueakvm.interpreter_bytecodes import compute_frame_size - self._frame_size = compute_frame_size(self.getbytes()) + self.argsize + self._tempsize + def update_frame_size(self, size): + self._frame_size = max(self._frame_size, size) def squeak_frame_size(self): # From blue book: normal mc have place for 12 temps+maxstack @@ -380,17 +380,15 @@ def str_content(self): return self.get_identifier_string() def bytecode_string(self, markBytecode=0): - from rsqueakvm.interpreter_bytecodes import BYTECODE_TABLE, BYTECODE_ARGUMENT_COUNT + from rsqueakvm.interpreter_bytecodes import BYTECODE_TABLE retval = "Bytecode:------------" - j = 1 - idx = 0 - while idx < len(self.bytes): + j = 0 + while j < len(self.bytes): i = self.bytes[idx] retval += '\n' - retval += '->' if j is markBytecode else ' ' - retval += ('%0.2i: 0x%0.2x(%0.3i) ' % (j, ord(i), ord(i))) + BYTECODE_TABLE[ord(i)].__name__ - idx += BYTECODE_ARGUMENT_COUNT[ord(i)] + 1 - j += BYTECODE_ARGUMENT_COUNT[ord(i)] + 1 + retval += '->' if j + 1 is markBytecode else ' ' + retval += ('%0.2i: 0x%0.2x(%0.3i) ' % (j + 1, ord(i), ord(i))) + BYTECODE_TABLE[ord(i)].__name__ + j += 1 retval += "\n---------------------" return retval diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index d732f89f..8f1710a0 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -312,8 +312,6 @@ def store_stackpointer(self, size): self.pop_n(depth - size) else: self.store_stack_ptr(size) - # for i in range(depth, size): - # self.push(self.space.w_nil) def stackdepth(self): return self.stack_ptr() @@ -530,6 +528,8 @@ def push(self, w_v): ptr = jit.promote(self.stack_ptr()) self.stack_put(ptr, w_v) self.store_stack_ptr(ptr + 1) + if not jit.we_are_jitted(): + self.w_method().update_frame_size(ptr + 1) @jit.unroll_safe def push_all(self, lst): diff --git a/rsqueakvm/test/test_interpreter.py b/rsqueakvm/test/test_interpreter.py index 617d7582..4288f11a 100644 --- a/rsqueakvm/test/test_interpreter.py +++ b/rsqueakvm/test/test_interpreter.py @@ -132,7 +132,6 @@ def test_create_frame(): w_method.islarge = 1 w_method.argsize = 2 w_method._tempsize = 8 - w_method.update_frame_size() s_frame = w_method.create_frame(space, w("receiver"), [w("foo"), w("bar")]) w_frame = s_frame.w_self() assert s_frame.w_receiver().unwrap_string(None) == "receiver" @@ -146,7 +145,7 @@ def test_create_frame(): assert s_frame.fetch_next_bytecode() == ord("l") def test_push_pop(): - _, frame = new_frame("ppp") + _, frame = new_frame("") frame.push(w(12)) frame.push(w(34)) frame.push(w(56)) @@ -450,11 +449,9 @@ def sendBytecodesTest(w_class, w_object, bytecodes): w_method.bytes = pushConstantOneBytecode + bytecode literals = fakeliterals(space, "foo") w_foo = literals[0] - w_method.update_frame_size() shadow.installmethod(w_foo, w_method) - w_frame, s_frame = new_frame(pushConstantOneBytecode + bytecodes) + w_frame, s_frame = new_frame(bytecodes) s_frame.w_method().setliterals(literals) - s_frame.store_pc(1) # skip over the push One bytecode, we push the object next s_frame.push(w_object) w_active_context = step_in_interp(s_frame) s_active_context = w_active_context.as_context_get_shadow(space) @@ -484,7 +481,6 @@ def test_fibWithArgument(): method._tempsize = 1 literals = fakeliterals(space, "fib:") method.setliterals(literals) - method.update_frame_size() shadow.installmethod(literals[0], method) w_object = shadow.new() w_frame, s_frame = new_frame(sendLiteralSelectorBytecode(16) + returnTopFromMethodBytecode) @@ -622,7 +618,6 @@ def test_callPrimitiveAndPush_fallback(): w_method.argsize = 1 w_method._tempsize = 1 w_method.literalsize = 1 - w_method.update_frame_size() w_symbol = fakesymbol("+") shadow.installmethod(w_symbol, w_method) # slightly evil @@ -639,15 +634,12 @@ def test_callPrimitiveAndPush_fallback(): assert s_active_context.stack() == [] def test_bytecodePrimBool(): - w_frame, s_frame = new_frame( - pushConstantOneBytecode * 7 + # just for frame size - bytecodePrimLessThan + - bytecodePrimGreaterThan + - bytecodePrimLessOrEqual + - bytecodePrimGreaterOrEqual + - bytecodePrimEqual + - bytecodePrimNotEqual) - s_frame.store_pc(7) # skip pushing constants + w_frame, s_frame = new_frame(bytecodePrimLessThan + + bytecodePrimGreaterThan + + bytecodePrimLessOrEqual + + bytecodePrimGreaterOrEqual + + bytecodePrimEqual + + bytecodePrimNotEqual) for i in range(6): s_frame.push(space.w_one) s_frame.push(space.w_two) @@ -671,7 +663,6 @@ def test_singleExtendedSuperBytecode(bytecode=singleExtendedSuperBytecode + chr( # which does a call to its super meth1 = W_PreSpurCompiledMethod(space, 2) meth1.bytes = pushReceiverBytecode + bytecode - meth1.update_frame_size() literals = fakeliterals(space, "foo") foo = literals[0] meth1.setliterals(literals) @@ -680,7 +671,6 @@ def test_singleExtendedSuperBytecode(bytecode=singleExtendedSuperBytecode + chr( meth2 = W_PreSpurCompiledMethod(space, 2) meth2.bytes = pushReceiverBytecode + bytecode meth2.setliterals(fakeliterals(space, foo)) - meth2.update_frame_size() w_super.as_class_get_shadow(space).installmethod(foo, meth2) meth3 = W_PreSpurCompiledMethod(space, 0) w_supersuper.as_class_get_shadow(space).installmethod(foo, meth3) @@ -887,8 +877,7 @@ def test(): # Closure Bytecodes def test_bc_pushNewArrayBytecode(bytecode=pushNewArrayBytecode): - w_frame, s_frame = new_frame("ppp" + bytecode + chr(0x83)) - s_frame.store_pc(3) # skip over pushes, they are just for frame size + w_frame, s_frame = new_frame(bytecode + chr(0x83)) s_frame.push(w(fakeliterals(space, "egg"))) s_frame.push(w(fakeliterals(space, "bar"))) s_frame.push(w(fakeliterals(space, "baz"))) @@ -900,8 +889,7 @@ def test_bc_pushNewArrayBytecode(bytecode=pushNewArrayBytecode): assert space.unwrap_array(array.at0(space, 2)) == fakeliterals(space, "baz") def test_bc_pushNewArrayBytecode_noPopIntoArray(bytecode=pushNewArrayBytecode): - w_frame, s_frame = new_frame("pp" + bytecode + chr(0x02)) - s_frame.store_pc(2) # skip over pushes, they are just for frame size + w_frame, s_frame = new_frame(bytecode + chr(0x02)) s_frame.push(w("egg")) s_frame.push(w("bar")) step_in_interp(s_frame) @@ -927,8 +915,7 @@ def test_bc_pushRemoteTempLongBytecode(bytecode=pushRemoteTempLongBytecode): def setupTempArrayAndContext(bytecode): # both indizes are 0-relative - w_frame, s_frame = new_frame("pp" + bytecode + chr(2) + chr(1)) - s_frame.store_pc(2) # skip over pushes, they are just for frame size + w_frame, s_frame = new_frame(bytecode + chr(2) + chr(1)) s_frame.push(w(fakeliterals(space, "english"))) s_frame.push(w(fakeliterals(space, "bar"))) temp_array = space.w_Array.as_class_get_shadow(interp.space).new(3) @@ -963,8 +950,7 @@ def test_bc_pushClosureCopyCopied0ValuesBytecode(bytecode=pushClosureCopyCopiedV assert closure.w_outerContext() is s_frame._w_self def test_bc_pushClosureCopyCopied2ValuesBytecode(bytecode=pushClosureCopyCopiedValuesBytecode): - w_frame, s_frame = new_frame("pp" + bytecode + chr(0x23) + chr(0) + chr(0)) - s_frame.store_pc(2) # skip over pushes, they are just for frame size + w_frame, s_frame = new_frame(bytecode + chr(0x23) + chr(0) + chr(0)) s_frame.push(w("english")) s_frame.push(w("bar")) pc = s_frame.pc() @@ -1004,8 +990,7 @@ def test(): def test_frame_dirty_if_active(): bytes = reduce(operator.add, map(chr, [0x84, 0xc0, 0x00])) - w_frame, s_frame = new_frame("p" + bytes) - s_frame.store_pc(1) + w_frame, s_frame = new_frame(bytes) s_frame.store_w_receiver(w_frame) s_frame.push(w_frame) s_frame.set_state(storage_contexts.ActiveContext) @@ -1014,8 +999,7 @@ def test_frame_dirty_if_active(): def test_frame_not_dirty_if_inactive(): bytes = reduce(operator.add, map(chr, [0x84, 0xc0, 0x00])) - w_frame, s_frame = new_frame("p" + bytes) - s_frame.store_pc(1) + w_frame, s_frame = new_frame(bytes) w_other_frame, s_other_frame = new_frame("") s_frame.store_w_receiver(w_other_frame) s_frame.push(w_frame) @@ -1026,8 +1010,7 @@ def test_frame_not_dirty_if_inactive(): def test_raise_NonVirtualReturn_on_dirty_frame(): bytes = reduce(operator.add, map(chr, [0x84, 0xc0, 0x00])) + returnTopFromMethodBytecode - w_frame, s_frame = new_frame("p" + bytes) - s_frame.store_pc(1) + w_frame, s_frame = new_frame(bytes) s_frame.store_w_receiver(w_frame) s_frame.push(w_frame) diff --git a/rsqueakvm/test/test_primitives.py b/rsqueakvm/test/test_primitives.py index 485d59b7..ed14e5f8 100644 --- a/rsqueakvm/test/test_primitives.py +++ b/rsqueakvm/test/test_primitives.py @@ -696,8 +696,8 @@ def test_directory_delimitor(): assert space.unwrap_char_as_byte(w_c) == os.path.sep def test_primitive_closure_copyClosure(): - w_frame, s_frame = new_frame("pppppppp") - w_outer_frame, s_initial_context = new_frame("pppppppp") + w_frame, s_frame = new_frame("") + w_outer_frame, s_initial_context = new_frame("") w_block = prim(CLOSURE_COPY_WITH_COPIED_VALUES, map(wrap, [w_outer_frame, 2, [wrap(1), wrap(2)]]), w_frame) assert not w_block.is_nil(space) @@ -721,7 +721,7 @@ def test_primitive_closure_copyClosure(): # prim_fails(STRING_REPLACE, [['a', 'b'], 1, 4, "ccccc", 1]) def build_up_closure_environment(args, copiedValues=[]): - w_frame, s_initial_context = new_frame("pppppppp") + w_frame, s_initial_context = new_frame("") size_arguments = len(args) closure = space.newClosure(w_frame, 4, #pc @@ -775,7 +775,7 @@ def test_primitive_some_object(): def test_primitive_next_object(): someInstances = map(space.wrap_list, [[2], [3]]) - w_frame, s_context = new_frame("pppppppp") + w_frame, s_context = new_frame("") s_context.push(space.w_nil) interp = InterpreterForTest(space) @@ -791,7 +791,7 @@ def test_primitive_next_object(): def test_primitive_next_instance(): someInstances = map(space.wrap_list, [[2], [3]]) - w_frame, s_context = new_frame("pppppppp") + w_frame, s_context = new_frame("") s_context.push(space.w_Array) interp = InterpreterForTest(space) @@ -807,7 +807,7 @@ def test_primitive_next_instance(): def test_primitive_next_instance_wo_some_instance_in_same_frame(): someInstances = map(space.wrap_list, [[2], [3]]) - w_frame, s_context = new_frame("pppppppp") + w_frame, s_context = new_frame("") s_context.push(space.w_Array) interp = InterpreterForTest(space) @@ -831,7 +831,7 @@ def quick_check_for_interrupt(s_frame, dec=1): def step(s_frame): raise Stepping - w_frame, s_initial_context = new_frame("pppppppp") + w_frame, s_initial_context = new_frame("") closure = space.newClosure(w_frame, 4, 0, []) s_frame = w_frame.as_context_get_shadow(space) diff --git a/rsqueakvm/test/test_shadow.py b/rsqueakvm/test/test_shadow.py index 864581f1..3c8ffeee 100644 --- a/rsqueakvm/test/test_shadow.py +++ b/rsqueakvm/test/test_shadow.py @@ -105,7 +105,6 @@ def create_method(tempsize=3,argsize=2, bytes="abcde"): w_m._tempsize = tempsize w_m.argsize = argsize w_m.literalsize = 2 - w_m.update_frame_size() return w_m def methodcontext(w_sender=None, pc=13, stackpointer=0, stacksize=5, diff --git a/rsqueakvm/test/util.py b/rsqueakvm/test/util.py index bff7b7e4..a566d225 100644 --- a/rsqueakvm/test/util.py +++ b/rsqueakvm/test/util.py @@ -132,10 +132,11 @@ def get_opcode_chr(n): opcode = entry[0] + n assert entry[0] <= opcode <= entry[1] return chr(opcode) - setattr(mod, entry[2], get_opcode_chr) + setattr(mod, name, get_opcode_chr) for entry in interpreter_bytecodes.BYTECODE_RANGES: - if len(entry) == 3: # no range - setattr(mod, entry[1], chr(entry[0])) + name = entry[-1] + if len(entry) == 2: # no range + setattr(mod, name, chr(entry[0])) else: make_getter(entry) @@ -419,10 +420,8 @@ def find_symbol_in_methoddict(self, string, cls, fail=True): # ============ Helpers for executing ============ def wrap_frame(self, s_frame): - # Add a toplevel frame around s_frame to properly return, with one push - # bytecode (that we skip) so we have enough room on the stack - toplevel_frame = self.make_method([112, 0x7c]).create_frame(self, self.w(0), []) - toplevel_frame.store_pc(1) + # Add a toplevel frame around s_frame to properly return. + toplevel_frame = self.make_method([0x7c]).create_frame(self, self.w(0), []) s_frame.store_s_sender(toplevel_frame) def make_method(self, bytes, literals=None, numargs=0): @@ -436,7 +435,6 @@ def make_method(self, bytes, literals=None, numargs=0): if literals is None: literals = [W_PointersObject(self, None, 2)] w_method.setliterals(literals) - w_method.update_frame_size() return w_method def make_frame(self, bytes, literals=None, receiver=None, args=[]): From b96acde29ddc248d949968f53c8a091f63c6ae37 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 13 Mar 2017 10:44:39 +0100 Subject: [PATCH 19/52] punt on untranslated for now --- rsqueakvm/storage_contexts.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 8f1710a0..9a8d323b 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -528,8 +528,9 @@ def push(self, w_v): ptr = jit.promote(self.stack_ptr()) self.stack_put(ptr, w_v) self.store_stack_ptr(ptr + 1) - if not jit.we_are_jitted(): - self.w_method().update_frame_size(ptr + 1) + if objectmodel.we_are_translated(): + if not jit.we_are_jitted(): + self.w_method().update_frame_size(ptr + 1) @jit.unroll_safe def push_all(self, lst): From 8a4591cc5ba2fee9670bd4adba967327bbe7f920 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 13 Mar 2017 11:31:12 +0100 Subject: [PATCH 20/52] use the discovered frame size and go through slow path in interpreted mode --- rsqueakvm/model/compiled_methods.py | 7 +++---- rsqueakvm/storage_contexts.py | 10 ++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index 0bdfdc37..64f6c1e4 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -190,10 +190,7 @@ def primitive(self): @jit.elidable_promote() def frame_size(self): - if not jit.we_are_jitted(): - return self.squeak_frame_size() - else: - return self._frame_size + return self._frame_size def update_frame_size(self, size): self._frame_size = max(self._frame_size, size) @@ -430,6 +427,7 @@ def setheader(self, space, header, initializing=False): self.initialize_literals(decoded_header.number_of_literals, space, initializing) self.argsize = decoded_header.number_of_arguments + self._frame_size = self.argsize self._tempsize = decoded_header.number_of_temporaries self.islarge = decoded_header.large_frame self.compiledin_class = None @@ -462,6 +460,7 @@ def setheader(self, space, header, initializing=False): self.initialize_literals( decoded_header.number_of_literals, space, initializing) self.argsize = decoded_header.number_of_arguments + self._frame_size = self.argsize self._tempsize = decoded_header.number_of_temporaries self._primitive = decoded_header.primitive_index self.islarge = decoded_header.large_frame diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 9a8d323b..cbfe2357 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -492,6 +492,16 @@ def stack_get(self, index0): def stack_put(self, index0, w_val): assert w_val is not None, "trying to put None on the stack" assert index0 >= 0, "trying to stack_put at negative index" + if not jit.we_are_jitted(): + self.slow_stack_put(index0, w_val) + else: + self._temps_and_stack[index0] = w_val + + def slow_stack_put(self, index0, w_val): + if self._w_self_size <= index0 + constants.MTHDCTX_TEMP_FRAME_START: + self._w_self_size = index0 + constants.MTHDCTX_TEMP_FRAME_START + while len(self._temps_and_stack) <= index0: + self._temps_and_stack.append(None) self._temps_and_stack[index0] = w_val @objectmodel.not_rpython # this is only for testing. From b1c7eae51eb948419478c2b95cc74a56fabffb31 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 13 Mar 2017 14:05:13 +0100 Subject: [PATCH 21/52] we need to have space at least for args and temps. the size from the POV for squeak is the squeak size --- rsqueakvm/model/compiled_methods.py | 5 +++-- rsqueakvm/storage_contexts.py | 18 ++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index 64f6c1e4..715fb210 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -195,6 +195,7 @@ def frame_size(self): def update_frame_size(self, size): self._frame_size = max(self._frame_size, size) + @jit.elidable_promote() def squeak_frame_size(self): # From blue book: normal mc have place for 12 temps+maxstack # mc for methods with islarge flag turned on 32 @@ -427,7 +428,6 @@ def setheader(self, space, header, initializing=False): self.initialize_literals(decoded_header.number_of_literals, space, initializing) self.argsize = decoded_header.number_of_arguments - self._frame_size = self.argsize self._tempsize = decoded_header.number_of_temporaries self.islarge = decoded_header.large_frame self.compiledin_class = None @@ -435,6 +435,7 @@ def setheader(self, space, header, initializing=False): self.update_primitive_index() else: self._primitive = 0 + self._frame_size = self.argsize + self._tempsize def setbytes(self, bytes): W_CompiledMethod.setbytes(self, bytes) @@ -460,8 +461,8 @@ def setheader(self, space, header, initializing=False): self.initialize_literals( decoded_header.number_of_literals, space, initializing) self.argsize = decoded_header.number_of_arguments - self._frame_size = self.argsize self._tempsize = decoded_header.number_of_temporaries self._primitive = decoded_header.primitive_index self.islarge = decoded_header.large_frame self.compiledin_class = None + self._frame_size = self.argsize + self._tempsize diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index cbfe2357..923e149a 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -487,21 +487,19 @@ def init_temps_and_stack(self): def stack_get(self, index0): assert index0 >= 0, "trying to stack_get negative index" + if not jit.we_are_jitted(): + self.update_stacksize(index0) return self._temps_and_stack[index0] or self.space.w_nil + def update_stacksize(self, index0): + while len(self._temps_and_stack) <= index0: + self._temps_and_stack.append(None) + def stack_put(self, index0, w_val): assert w_val is not None, "trying to put None on the stack" assert index0 >= 0, "trying to stack_put at negative index" if not jit.we_are_jitted(): - self.slow_stack_put(index0, w_val) - else: - self._temps_and_stack[index0] = w_val - - def slow_stack_put(self, index0, w_val): - if self._w_self_size <= index0 + constants.MTHDCTX_TEMP_FRAME_START: - self._w_self_size = index0 + constants.MTHDCTX_TEMP_FRAME_START - while len(self._temps_and_stack) <= index0: - self._temps_and_stack.append(None) + self.update_stacksize(index0) self._temps_and_stack[index0] = w_val @objectmodel.not_rpython # this is only for testing. @@ -813,7 +811,7 @@ class __extend__(ContextPartShadow): def build_method_context(space, w_method, w_receiver, arguments=[], closure=None, s_fallback=None): w_method = jit.promote(w_method) - size = w_method.frame_size() + constants.MTHDCTX_TEMP_FRAME_START + size = w_method.squeak_frame_size() + constants.MTHDCTX_TEMP_FRAME_START ctx = ContextPartShadow(space, None, size, space.w_MethodContext) if s_fallback is not None: From d79c8990f4e6d68303d8d108ee4ed21bf0c62b17 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 13 Mar 2017 14:31:53 +0100 Subject: [PATCH 22/52] dynamically overflow into an 'overflow stack' array on the heap. shouldn't happen after we're jitted, but we abort tracing in that case anyway --- rsqueakvm/storage_contexts.py | 40 ++++++++++++++++++++++--------- rsqueakvm/test/test_primitives.py | 2 ++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 923e149a..7dcaa945 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -43,6 +43,8 @@ class ExtraContextAttributes(object): 'instances_w', # Fallback for failed primitives '_s_fallback', + # Dynamically extended stack space + '_overflow_stack', # From block-context '_w_home', '_initialip', '_eargc'] @@ -52,6 +54,7 @@ def __init__(self): self._w_home = None self._initialip = 0 self._eargc = 0 + self._overflow_stack = [] state_mask = r_uint(0b00111111111111111111111111111111) @@ -487,24 +490,42 @@ def init_temps_and_stack(self): def stack_get(self, index0): assert index0 >= 0, "trying to stack_get negative index" - if not jit.we_are_jitted(): - self.update_stacksize(index0) + if self.update_stacksize(index0): + return self.overflow_stack_get(index0) return self._temps_and_stack[index0] or self.space.w_nil - def update_stacksize(self, index0): - while len(self._temps_and_stack) <= index0: - self._temps_and_stack.append(None) + def overflow_stack_get(self, index0): + overflow_index = index0 - len(self._temps_and_stack) + return self.get_extra_data()._overflow_stack[overflow_index] or self.space.w_nil def stack_put(self, index0, w_val): assert w_val is not None, "trying to put None on the stack" assert index0 >= 0, "trying to stack_put at negative index" - if not jit.we_are_jitted(): - self.update_stacksize(index0) + if self.update_stacksize(index0): + return self.overflow_stack_put(index0, w_val) self._temps_and_stack[index0] = w_val + def overflow_stack_put(self, index0, w_val): + overflow_index = index0 - len(self._temps_and_stack) + self.get_extra_data()._overflow_stack[overflow_index] = w_val + + @jit.unroll_safe + def update_stacksize(self, index0): + stacksize = len(self._temps_and_stack) + if stacksize <= index0: + self.w_method().update_frame_size(index0 + 1) + overflow_stack = self.get_extra_data()._overflow_stack + overflow_index = index0 - stacksize + while len(overflow_stack) <= overflow_index: + overflow_stack.append(None) + return True + return False + @objectmodel.not_rpython # this is only for testing. def stack(self): - return self._temps_and_stack[self.tempsize():self.stack_ptr()] + stacksize = len(self._temps_and_stack) + return (self._temps_and_stack[self.tempsize():stacksize] + + self.get_extra_data()._overflow_stack[self.tempsize() - stacksize:self.stack_ptr()]) def pop(self): # HACK HACK HACK (3 times) @@ -536,9 +557,6 @@ def push(self, w_v): ptr = jit.promote(self.stack_ptr()) self.stack_put(ptr, w_v) self.store_stack_ptr(ptr + 1) - if objectmodel.we_are_translated(): - if not jit.we_are_jitted(): - self.w_method().update_frame_size(ptr + 1) @jit.unroll_safe def push_all(self, lst): diff --git a/rsqueakvm/test/test_primitives.py b/rsqueakvm/test/test_primitives.py index ed14e5f8..099a35fe 100644 --- a/rsqueakvm/test/test_primitives.py +++ b/rsqueakvm/test/test_primitives.py @@ -41,6 +41,7 @@ def teardown_module(): class MockFrame(W_PointersObject): def __init__(self, space, stack): + w_home_frame, s_home_frame = space.make_frame([]) size = 6 + len(stack) + 6 self._initialize_storage(space, space.w_BlockContext, size) for i, v in enumerate(stack): @@ -51,6 +52,7 @@ def __init__(self, space, stack): s_self.reset_stack() s_self.push_all(stack) s_self.store_expected_argument_count(0) + s_self.store_w_home(w_home_frame) def as_context_get_shadow(self, space): if not isinstance(self.strategy, storage_contexts.ContextPartShadow): From 5cd8292e98803e4cb9acb9b4255c4c574ad93680 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 13 Mar 2017 14:54:42 +0100 Subject: [PATCH 23/52] don't store the w_self_size, calculate it if required --- rsqueakvm/storage_contexts.py | 40 ++++++++++++++++++++++--------- rsqueakvm/test/test_primitives.py | 8 +++++-- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 7dcaa945..0d34b4ee 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -80,7 +80,7 @@ class actually represents one of two classes, determined by the is_block_context __metaclass__ = ExtendableStrategyMetaclass import_from_mixin(ShadowMixin) - _attrs_ = ['_w_self', '_w_self_size', + _attrs_ = ['_w_self', '_state_stackptr_pc', # Core context data '_s_sender', '_temps_and_stack', @@ -92,7 +92,7 @@ class actually represents one of two classes, determined by the is_block_context ] _virtualizable_ = [ - "_w_self", "_w_self_size", + "_w_self", '_state_stackptr_pc', '_s_sender', "_temps_and_stack[*]", 'blockmethod', @@ -110,10 +110,6 @@ def __init__(self, space, w_self, size, w_class): self._state_stackptr_pc = r_uint(0) self._s_sender = jit.vref_None - if w_self is not None: - self._w_self_size = w_self.size() - else: - self._w_self_size = size self._w_self = w_self self.set_state(InactiveContext) self.store_pc(0) @@ -203,7 +199,7 @@ def pure_is_block_context(self): return False def size(self, ignored_w_self): - return self._w_self_size + return self.external_size() def fetch(self, ignored_w_self, n0): if self.pure_is_block_context(): @@ -454,7 +450,21 @@ def external_stackpointer(self): def stackend(self): # XXX this is incorrect when there is subclassing - return self._w_self_size + return self.external_size() + + def external_size(self): + """ + The size of this context pointer object, as it would be seen by Squeak. + There is possibly less _temps_and_stack allocated, if the code + dynamically stores more than that, we overflow into + the get_extra_data()._overflow_stack and update the size to use for + _temps_and_stack in the w_method, so the next time _temps_and_stack will + be the right size (see update_stacksize and its callers) + """ + if self.pure_is_block_context(): + return ContextPartShadow.size_context_for_home(self.s_home()) + else: + return ContextPartShadow.size_context_for_method(self.w_method()) def fetch_next_bytecode(self): pc = jit.promote(self.pc()) @@ -701,9 +711,13 @@ class __extend__(ContextPartShadow): # === Initialization === + @staticmethod + def size_context_for_home(s_home): + return s_home.own_size() - s_home.tempsize() + @staticmethod def build_block_context(space, s_home, argcnt, pc): - size = s_home.own_size() - s_home.tempsize() + size = ContextPartShadow.size_context_for_home(s_home) w_self = W_PointersObject(space, space.w_BlockContext, size) ctx = ContextPartShadow(space, w_self, size, space.w_BlockContext) @@ -825,11 +839,15 @@ class __extend__(ContextPartShadow): # === Initialization === + @staticmethod + def size_context_for_method(w_method): + return w_method.squeak_frame_size() + constants.MTHDCTX_TEMP_FRAME_START + @staticmethod def build_method_context(space, w_method, w_receiver, arguments=[], closure=None, s_fallback=None): w_method = jit.promote(w_method) - size = w_method.squeak_frame_size() + constants.MTHDCTX_TEMP_FRAME_START + size = ContextPartShadow.size_context_for_method(w_method) ctx = ContextPartShadow(space, None, size, space.w_MethodContext) if s_fallback is not None: @@ -963,7 +981,7 @@ def w_self(self): return self._w_self else: space = self.space - w_self = W_PointersObject(space, space.w_MethodContext, self._w_self_size) + w_self = W_PointersObject(space, space.w_MethodContext, self.external_size()) w_self._set_strategy(self) self._w_self = w_self return w_self diff --git a/rsqueakvm/test/test_primitives.py b/rsqueakvm/test/test_primitives.py index 099a35fe..22556049 100644 --- a/rsqueakvm/test/test_primitives.py +++ b/rsqueakvm/test/test_primitives.py @@ -41,13 +41,14 @@ def teardown_module(): class MockFrame(W_PointersObject): def __init__(self, space, stack): - w_home_frame, s_home_frame = space.make_frame([]) size = 6 + len(stack) + 6 + w_home_frame, s_home_frame = space.make_frame([], args=[space.w_nil] * size) self._initialize_storage(space, space.w_BlockContext, size) for i, v in enumerate(stack): if not isinstance(v, W_Object): stack[i] = space.w(v) self.store_all(space, [space.w_nil] * 6 + stack + [space.w_nil] * 6) + self.store(space, constants.BLKCTX_HOME_INDEX, w_home_frame) s_self = self.as_context_get_shadow(space) s_self.reset_stack() s_self.push_all(stack) @@ -56,7 +57,10 @@ def __init__(self, space, stack): def as_context_get_shadow(self, space): if not isinstance(self.strategy, storage_contexts.ContextPartShadow): - self.strategy = storage_contexts.ContextPartShadow(space, self, self.size(), space.w_BlockContext) + strategy = storage_contexts.ContextPartShadow(space, self, self.size(), space.w_BlockContext) + strategy.store(self, constants.BLKCTX_HOME_INDEX, + self.fetch(self, constants.BLKCTX_HOME_INDEX)) + self.strategy = strategy self.strategy.init_temps_and_stack() return self.strategy From 795f0546a8599f041b7f059f6ded82c0d11f80c1 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 13 Mar 2017 16:04:58 +0100 Subject: [PATCH 24/52] merge blockmethod and extra_data into a single field --- rsqueakvm/interpreter.py | 2 +- rsqueakvm/storage_contexts.py | 60 +++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index 9abf651b..516d0898 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -304,7 +304,7 @@ def getreceiverclass(self, s_context): return s_context.w_receiver().safe_getclass(self.space) def getblockmethod(self, s_context): - return s_context.blockmethod + return s_context.blockmethod() def loop_bytecodes(self, s_context, may_context_switch=True): old_pc = 0 diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 0d34b4ee..04cf13e8 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -1,4 +1,5 @@ from rsqueakvm import constants, error, wrapper +from rsqueakvm.model.base import W_Object from rsqueakvm.model.compiled_methods import W_CompiledMethod from rsqueakvm.model.pointers import W_PointersObject from rsqueakvm.model.block_closure import W_BlockClosure @@ -37,7 +38,7 @@ class ExtendableStrategyMetaclass(extendabletype, StrategyMetaclass): pass -class ExtraContextAttributes(object): +class ExtraContextAttributes(W_Object): _attrs_ = [ # Cache for allInstances 'instances_w', @@ -45,6 +46,8 @@ class ExtraContextAttributes(object): '_s_fallback', # Dynamically extended stack space '_overflow_stack', + # Home method of first block argument + 'blockmethod', # From block-context '_w_home', '_initialip', '_eargc'] @@ -55,6 +58,7 @@ def __init__(self): self._initialip = 0 self._eargc = 0 self._overflow_stack = [] + self.blockmethod = None state_mask = r_uint(0b00111111111111111111111111111111) @@ -85,19 +89,17 @@ class actually represents one of two classes, determined by the is_block_context # Core context data '_s_sender', '_temps_and_stack', # MethodContext data - 'blockmethod', 'closure', '_w_receiver', '_w_method', # Extra data - 'extra_data' + 'extra_data_or_blockmethod' ] _virtualizable_ = [ "_w_self", '_state_stackptr_pc', '_s_sender', "_temps_and_stack[*]", - 'blockmethod', 'closure', '_w_receiver', '_w_method', - 'extra_data' + 'extra_data_or_blockmethod' ] # ______________________________________________________________________ @@ -115,12 +117,11 @@ def __init__(self, space, w_self, size, w_class): self.store_pc(0) # From MethodContext - self.blockmethod = None self.closure = None self._w_method = None self._w_receiver = None # Extra data - self.extra_data = None + self.extra_data_or_blockmethod = None def _initialize_storage(self, w_self, initial_size): # The context object holds all of its storage itself. @@ -166,15 +167,25 @@ def is_privileged_index(self, n0): return (n0 == self.privileged_method_fields[0]) | (n0 == self.privileged_method_fields[1]) def get_extra_data(self): - if self.extra_data is None: - self.extra_data = ExtraContextAttributes() - return self.extra_data + extra_data = self.extra_data_or_blockmethod + if extra_data is None or not isinstance(extra_data, ExtraContextAttributes): + new_extra_data = ExtraContextAttributes() + new_extra_data.blockmethod = extra_data + extra_data = self.extra_data_or_blockmethod = new_extra_data + assert isinstance(extra_data, ExtraContextAttributes) + return extra_data + + def extra_data(self): + extra_data = self.extra_data_or_blockmethod + if isinstance(extra_data, ExtraContextAttributes): + return extra_data + return None def get_fallback(self): - if self.extra_data is None: + if self.extra_data() is None: return None else: - return self.extra_data._s_fallback + return self.get_extra_data()._s_fallback @always_inline def _get_state_stackptr_pc(self): @@ -859,18 +870,35 @@ def build_method_context(space, w_method, w_receiver, arguments=[], ctx.initialize_temps(space, arguments) return ctx + def blockmethod(self): + w_bm = self.extra_data_or_blockmethod + if isinstance(w_bm, ExtraContextAttributes): + w_bm = w_bm.blockmethod + if w_bm is None: + return None + assert isinstance(w_bm, W_CompiledMethod) + return w_bm + + def set_blockmethod(self, w_obj): + assert isinstance(w_obj, W_CompiledMethod) + w_bm = self.extra_data_or_blockmethod + if isinstance(w_bm, ExtraContextAttributes): + w_bm.blockmethod = w_obj + else: + self.extra_data_or_blockmethod = w_obj + @jit.unroll_safe def initialize_temps(self, space, arguments): argc = len(arguments) for i0 in range(argc): w_arg = arguments[i0] - if isinstance(w_arg, W_BlockClosure) and self.blockmethod is None: + if isinstance(w_arg, W_BlockClosure) and self.blockmethod() is None: # If our first argument is a block, we use its home method to # distinguish the context from others that receive a different # block argument to the same method (e.g. so that all the # collection methods are specialized based on where the block # that was passed in came from - self.blockmethod = w_arg.w_method() + self.set_blockmethod(w_arg.w_method()) self.settemp(i0, w_arg) closure = self.closure if closure: @@ -879,7 +907,7 @@ def initialize_temps(self, space, arguments): self.store_pc(pc) for i0 in range(closure.varsize()): w_obj = closure.at0(space, i0) - if isinstance(w_obj, W_BlockClosure) and self.blockmethod is None: + if isinstance(w_obj, W_BlockClosure) and self.blockmethod() is None: # for some methods (see Collection>>do: or detect:ifNone:) # the user code passes in a block, and the block closure # captures that block and is then called in a loop. So if we @@ -887,7 +915,7 @@ def initialize_temps(self, space, arguments): # loop, we really want to specialize on the first temp of # that block, which represents the block passed in from user # code into detect:ifNone: - self.blockmethod = w_obj.w_method() + self.set_blockmethod(w_obj.w_method()) self.settemp(i0+argc, w_obj) # === Accessing object fields === From 7d822dfd89b0df23667e64fa9969f7c161a9abcd Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 13 Mar 2017 17:00:22 +0100 Subject: [PATCH 25/52] let's ignore all stores behind the stack pointer, even those out of bounds --- rsqueakvm/storage_contexts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 04cf13e8..96fadeb0 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -263,7 +263,7 @@ def store_context_part(self, n0, w_value): return else: # XXX later should store tail out of known context part as well - raise error.WrapperException("Index in context out of bounds") + return # === Sender === From d90f4644c576a298e0eaeffabeb5a7a5766f7ed9 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 14 Mar 2017 09:28:54 +0100 Subject: [PATCH 26/52] implement switches to disable or limit maps --- rsqueakvm/main.py | 9 +++++++++ rsqueakvm/objspace.py | 2 ++ rsqueakvm/primitives/input_output.py | 2 +- rsqueakvm/squeakimage.py | 11 +++++------ rsqueakvm/storage_classes.py | 8 +++++--- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/rsqueakvm/main.py b/rsqueakvm/main.py index 007d898b..cb5506b6 100644 --- a/rsqueakvm/main.py +++ b/rsqueakvm/main.py @@ -99,6 +99,10 @@ def _usage(argv): Disables non-cooperative scheduling. -S|--no-storage - Disable specialized storage strategies. Always use generic ListStrategy. Probably slower. + -M|--no-maps - Disable Self-style maps for small fixed pointer + objects and revert to storage strategies. + --maps-limit - Number of fields an object can have to be + eligible for use with maps. --hacks - Enable Spy hacks. Set display color depth to 8 Logging: @@ -269,6 +273,11 @@ def parse_args(self, argv, skip_bad=False): self.interrupts = False elif arg in ["-S", "--no-storage"]: self.space.strategy_factory.no_specialized_storage.activate() + elif arg in ["-M", "--no-maps"]: + self.space.use_maps.deactivate() + elif arg in ["--maps-limit"]: + limit, idx = get_int_parameter(argv, idx, arg) + self.space.maps_limit.set(limit) # Logging elif arg in ["-t", "--trace"]: self.trace = True diff --git a/rsqueakvm/objspace.py b/rsqueakvm/objspace.py index 83e257a3..f613a952 100644 --- a/rsqueakvm/objspace.py +++ b/rsqueakvm/objspace.py @@ -49,6 +49,8 @@ def __init__(self): self.is_spur = QuasiConstant(False) self.uses_block_contexts = QuasiConstant(False) self.simulate_numeric_primitives = QuasiConstant(False) + self.use_maps = QuasiConstant(True) + self.maps_limit = QuasiConstant(16) self.system_attributes = {} self._system_attribute_version = QuasiConstant(Version()) diff --git a/rsqueakvm/primitives/input_output.py b/rsqueakvm/primitives/input_output.py index 8002c792..75aaa90a 100644 --- a/rsqueakvm/primitives/input_output.py +++ b/rsqueakvm/primitives/input_output.py @@ -57,7 +57,7 @@ def func(interp, s_frame, _): @expose_primitive(MOUSE_POINT, unwrap_spec=[object]) def func(interp, s_frame, w_rcvr): x, y = interp.space.display().mouse_point() - w_point = W_FixedPointersObject(interp.space, interp.space.w_Point, 0) + w_point = interp.space.w_Point.as_class_get_shadow(interp.space).new() w_point.store(interp.space, 0, interp.space.wrap_int(x)) w_point.store(interp.space, 1, interp.space.wrap_int(y)) return w_point diff --git a/rsqueakvm/squeakimage.py b/rsqueakvm/squeakimage.py index 0b1e4d7b..c19674d8 100644 --- a/rsqueakvm/squeakimage.py +++ b/rsqueakvm/squeakimage.py @@ -356,6 +356,11 @@ def get_bytes_of(self, chunk): bytes.append(chr((each >> 24) & 0xff)) return bytes + def isfixed(self, g_object): + return (self.space.use_maps.is_set() and + g_object.format == 1 and + len(g_object.pointers) <= self.space.maps_limit.get()) + def isfloat(self, g_object): return self.iswords(g_object) and self.space.w_Float.is_same_object(g_object.g_class.w_object) @@ -553,9 +558,6 @@ def isblockclosure(self, g_object): g_closure = self.special_g_object_safe(constants.SO_BLOCKCLOSURE_CLASS) return self.ispointers(g_object) and g_object.g_class == g_closure - def isfixed(self, g_object): - return g_object.format == 1 - def ispointers(self, g_object): return g_object.format < 5 @@ -766,9 +768,6 @@ def isblockclosure(self, g_object): g_closure = self.special_g_object_safe(constants.SO_BLOCKCLOSURE_CLASS) return self.ispointers(g_object) and g_closure == g_object.g_class - def isfixed(self, g_object): - return g_object.format == 1 - def ispointers(self, g_object): return g_object.format < 6 diff --git a/rsqueakvm/storage_classes.py b/rsqueakvm/storage_classes.py index f60fa8a5..1e4d18a7 100644 --- a/rsqueakvm/storage_classes.py +++ b/rsqueakvm/storage_classes.py @@ -53,7 +53,7 @@ def store(self, w_self, n0, w_val): else: if w_self.getclass(self.space).is_same_object(self.space.w_Metaclass): # In case of Metaclasses, the "instance" class is stored in the last field. - if n0 == self.size(w_self) - 1 and isinstance(w_val, W_FixedPointersObject): + if n0 == self.size(w_self) - 1 and isinstance(w_val, W_PointersObject): cl_shadow = w_val.as_class_get_shadow(self.space) self.name = "%s class" % cl_shadow.getname() self.changed() @@ -171,7 +171,7 @@ def store_w_superclass(self, w_class): self._s_superclass = None self.changed() else: - assert isinstance(w_class, W_FixedPointersObject) + assert isinstance(w_class, W_PointersObject) s_new_superclass = w_class.as_class_get_shadow(self.space) if superclass is s_new_superclass: return @@ -251,7 +251,9 @@ def new(self, extrasize=0): return w_new def make_pointers_object(self, w_cls, size): - if self.isvariable() or size > 16: + if (not self.space.use_maps.is_set() or + self.space.maps_limit.get() < size or + self.isvariable()): return W_PointersObject(self.space, w_cls, size) else: assert size == self.instsize() From 00612217e8a9897622f9876d4f810917c9269571 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 14 Mar 2017 10:24:11 +0100 Subject: [PATCH 27/52] if we run out of the end of a compiled method, return the receiver --- rsqueakvm/model/compiled_methods.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index 715fb210..0312514a 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -216,11 +216,10 @@ def end_pc(self): @jit.elidable_promote() def fetch_bytecode(self, pc): - # assert pc >= 0 and pc < len(self.bytes) try: return self.bytes[pc] except IndexError: - return chr(0) + return chr(120) # returnReceiverBytecode def compiled_in(self): # This method cannot be constant/elidable. Looking up the compiledin-class from From 5709807ce4d5663af9637366debbc5cd9280f74b Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 14 Mar 2017 10:24:25 +0100 Subject: [PATCH 28/52] log map transitions, too --- rsqueakvm/storage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index f442193f..204180b7 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -247,6 +247,7 @@ def remove_node(self, node, w_self, index0): def add_node(self, strategy_cls, index0, w_self): new_node = self.strategy_factory().get_transition(self, strategy_cls, index0, self.getclass()) + self.strategy_factory().log(w_self, new_node, self) assert isinstance(new_node, MapStorageNode) new_node.update_storage(w_self) return new_node From fe7ce83ac2f946438cca25d34b9ebccb3729d359 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 14 Mar 2017 10:41:33 +0100 Subject: [PATCH 29/52] remove unneeded imports --- rsqueakvm/plugins/misc_primitive_plugin.py | 2 +- rsqueakvm/primitives/input_output.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rsqueakvm/plugins/misc_primitive_plugin.py b/rsqueakvm/plugins/misc_primitive_plugin.py index 2e5187ee..bf38dd6f 100644 --- a/rsqueakvm/plugins/misc_primitive_plugin.py +++ b/rsqueakvm/plugins/misc_primitive_plugin.py @@ -2,7 +2,7 @@ from rsqueakvm.error import PrimitiveFailedError from rsqueakvm.model.variable import W_BytesObject from rsqueakvm.plugins.plugin import Plugin -from rsqueakvm.util.cells import Cell, QuasiConstant +from rsqueakvm.util.cells import QuasiConstant from rpython.rlib.rarithmetic import r_uint, intmask from rpython.rlib import jit diff --git a/rsqueakvm/primitives/input_output.py b/rsqueakvm/primitives/input_output.py index 051b623b..cba7fae8 100644 --- a/rsqueakvm/primitives/input_output.py +++ b/rsqueakvm/primitives/input_output.py @@ -3,7 +3,7 @@ from rsqueakvm import display, wrapper from rsqueakvm.error import PrimitiveFailedError from rsqueakvm.model.display import W_DisplayBitmap -from rsqueakvm.model.pointers import W_PointersObject, W_FixedPointersObject +from rsqueakvm.model.pointers import W_PointersObject from rsqueakvm.model.variable import W_WordsObject from rsqueakvm.primitives import expose_primitive, assert_class, index1_0 from rsqueakvm.primitives.constants import * From d5c4eb7b1fd28d493b90f2e4451c63ba1654bf8e Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 14 Mar 2017 10:41:54 +0100 Subject: [PATCH 30/52] fix merge error --- rsqueakvm/plugins/misc_primitive_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsqueakvm/plugins/misc_primitive_plugin.py b/rsqueakvm/plugins/misc_primitive_plugin.py index bf38dd6f..0071386e 100644 --- a/rsqueakvm/plugins/misc_primitive_plugin.py +++ b/rsqueakvm/plugins/misc_primitive_plugin.py @@ -14,7 +14,7 @@ class MiscPrimitivePlugin(Plugin): def __init__(self): Plugin.__init__(self) - self.ascii_order = QuasiConstant(None, type=W_BytesObject) + self.ascii_order = QuasiConstant(None, cls=W_BytesObject) plugin = MiscPrimitivePlugin() From 02d2696934654208e1d2f1e417495c152234292c Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 14 Mar 2017 11:12:58 +0100 Subject: [PATCH 31/52] fix logging of map transitions --- rsqueakvm/storage.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 204180b7..042840a9 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -189,7 +189,7 @@ class MapStrategy(AbstractStrategy): the normal map transition mechanism (add_node, find_node, remove_node). """ _attrs_ = [] - repr_classname = "MapStrategy (base)" + repr_classname = "BaseMapStrategy" import_from_mixin(rstrat.SafeIndexingMixin) def _initialize_storage(self, w_self, initial_size): @@ -277,7 +277,7 @@ class MapStorageNode(MapStrategy): # array __metaclass__ = MapNodesMetaclass - repr_classname = "MapStrategy (abstract node)" + repr_classname = "AbstractMapStrategyNode" @staticmethod @jit.unroll_safe @@ -397,6 +397,7 @@ def add_node(self, strategy_cls, index0, w_self): else: assert self.index < index0 new_node = self.strategy_factory().get_transition(self, strategy_cls, index0, self.getclass()) + self.strategy_factory().log(w_self, new_node, self) assert isinstance(new_node, MapStorageNode) new_node.update_storage(w_self) return new_node @@ -405,7 +406,7 @@ def add_node(self, strategy_cls, index0, w_self): class IntMapStorageNode(MapStorageNode): # we inline up to three integer fields on the object and the rest in a intFields array used_fields = "intFields" - repr_classname = "MapStrategy (int)" + repr_classname = "IntMapStrategyNode" @staticmethod def correct_type(w_self, w_value): @@ -461,7 +462,7 @@ def __write__(self, w_self, index0, w_value): class ObjectMapStorageNode(MapStorageNode): used_fields = "erased_storage" - repr_classname = "MapStrategy (object)" + repr_classname = "ObjectMapStrategyNode" @staticmethod def correct_type(w_self, w_value): From ecfcd895779fdfafaec8fe964e9ba4b2d55b9391 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 14 Mar 2017 16:12:46 +0100 Subject: [PATCH 32/52] use logname method for strategy logs --- rsqueakvm/storage.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 042840a9..0ef7beaa 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -62,11 +62,13 @@ def __init__(self, space, w_self, size, w_class): self.w_class = w_class def getname(self): raise NotImplementedError("Abstract class") + def logname(self): + return self.repr_classname def __repr__(self): if self.provides_getname: - return "<%s %s>" % (self.repr_classname, self.getname()) + return "<%s %s>" % (self.logname(), self.getname()) else: - return "<%s>" % self.repr_classname + return "<%s>" % self.logname() def strategy_factory(self): return self.space.strategy_factory def _convert_storage_from_AllNilStrategy(self, w_self, all_nil_storage): @@ -296,8 +298,8 @@ def __init__(self, space, prev, index, w_class): self.w_class = w_class self._size_estimate = max(self.length() * NUM_DIGITS_POW2, 0) - def __repr__(self): - return "<%s index:%d;pos:%d>" % (self.repr_classname, self.index, self.pos) + def logname(self): + return "%sIdx%dPos%d" % (self.repr_classname, self.index, self.pos) def getprev(self): return self.prev @@ -757,8 +759,8 @@ def log(self, w_self, new_strategy, old_strategy=None, new_element=None): # Gather information to be logged image_loaded = self.space.image_loaded.is_set() size = new_strategy.size(w_self) - new_strategy_str = new_strategy.repr_classname - old_strategy_str = old_strategy.repr_classname if old_strategy else "" + new_strategy_str = new_strategy.logname() + old_strategy_str = old_strategy.logname() if old_strategy else "" classname = w_self.guess_classname() if image_loaded else "" element_classname = new_element.guess_classname() if new_element and image_loaded else "" if image_loaded: From 97f2513ed06d1873e5167c77433810815426c1c0 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 14 Mar 2017 16:13:15 +0100 Subject: [PATCH 33/52] try to be more precise in how map transitions work, transitioning through the common base each time --- rsqueakvm/storage.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index 0ef7beaa..dd79e04c 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -222,9 +222,19 @@ def store(self, w_self, index0, w_value): storage_node_type = MapStorageNode.type_for(w_self, w_value) new_node = w_self._get_strategy().add_node(storage_node_type, index0, w_self) assert isinstance(new_node, MapStorageNode) + self.log_set_storage(w_self, w_self._get_strategy(), new_node) w_self._set_strategy(new_node) new_node.store(w_self, index0, w_value) + def log_set_storage(self, w_self, old_node, new_node): + if self.strategy_factory().logger.active: + common_node = old_node.common_base(new_node) + if old_node is not common_node: + self.strategy_factory().log(w_self, common_node, old_node) + + def common_base(self, other): + return self + def size(self, w_self): return w_self.size() @@ -301,6 +311,18 @@ def __init__(self, space, prev, index, w_class): def logname(self): return "%sIdx%dPos%d" % (self.repr_classname, self.index, self.pos) + def common_base(self, other): + node = other + while isinstance(node, MapStorageNode) and node.index > self.index: + node = node.getprev() + last_base = base = self + while base: + if base is node: + return base + last_base = base + base = base.getprev() + return last_base + def getprev(self): return self.prev @@ -347,6 +369,7 @@ def write(self, w_self, index0, w_value): ).add_node( MapStorageNode.type_for(w_self, w_value), index0, w_self ) + self.log_set_storage(w_self, strategy, new_node) w_self._set_strategy(new_node) new_node.store(w_self, index0, w_value) else: @@ -763,6 +786,10 @@ def log(self, w_self, new_strategy, old_strategy=None, new_element=None): old_strategy_str = old_strategy.logname() if old_strategy else "" classname = w_self.guess_classname() if image_loaded else "" element_classname = new_element.guess_classname() if new_element and image_loaded else "" + if isinstance(new_strategy, MapStrategy): + new_strategy_str += classname + if isinstance(old_strategy, MapStrategy): + old_strategy_str += classname if image_loaded: cause = "Switched" if old_strategy else "Initialized" else: From 177c202e6b7a5045db0953e492c06b473b1a0510 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Fri, 17 Mar 2017 16:12:28 +0100 Subject: [PATCH 34/52] avoid checking for None on the stack --- rsqueakvm/storage_contexts.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 96fadeb0..43aff98b 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -505,7 +505,7 @@ def full_stacksize(self): def init_temps_and_stack(self): self = fresh_virtualizable(self) stacksize = self.full_stacksize() - self._temps_and_stack = [None] * stacksize + self._temps_and_stack = [self.space.w_nil] * stacksize tempsize = self.tempsize() self.store_stack_ptr(tempsize) # we point after the last element @@ -513,7 +513,7 @@ def stack_get(self, index0): assert index0 >= 0, "trying to stack_get negative index" if self.update_stacksize(index0): return self.overflow_stack_get(index0) - return self._temps_and_stack[index0] or self.space.w_nil + return self._temps_and_stack[index0] def overflow_stack_get(self, index0): overflow_index = index0 - len(self._temps_and_stack) @@ -655,7 +655,6 @@ def __str__(self): argcount = self.w_method().argsize j = 0 for w_obj in self._temps_and_stack[:self.stack_ptr()]: - w_obj = self.space.w_nil if w_obj is None else w_obj if j == argcount: retval += "\nTemps:---------------" if j == self.tempsize(): From b6f7277733f64077924c77404fbaef1471cd9758 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Sun, 19 Mar 2017 15:53:16 +0100 Subject: [PATCH 35/52] try unrolling at least a little --- rsqueakvm/interpreter.py | 67 +++++++++++++++++++++------ rsqueakvm/main.py | 23 ++++++++- rsqueakvm/test/jittest/test_modern.py | 60 ++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 15 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index 9abf651b..ee999aab 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -126,9 +126,17 @@ def __init__(self, s_new_context, forced=False): ContextSwitchException.__init__(self, s_new_context) self.forced = forced +class Optargs(object): + _attrs_ = ["max_squeak_unroll_count", "squeak_unroll_trace_limit"] + _immutable_fields_ = ["max_squeak_unroll_count", "squeak_unroll_trace_limit"] + def __init__(self): + self.max_squeak_unroll_count = 2 + self.squeak_unroll_trace_limit = 32000 + + UNROLLING_BYTECODE_RANGES = unroll.unrolling_iterable(interpreter_bytecodes.BYTECODE_RANGES) -def get_printable_location(pc, self, method, w_class, blockmethod): +def get_printable_location(pc, jump_back_pc, unrollings, self, method, w_class, blockmethod): bc = ord(method.bytes[pc]) name = method.safe_identifier_string() classname = "???" @@ -144,7 +152,7 @@ def get_printable_location(pc, self, method, w_class, blockmethod): return '%s(%s): (%s) [%d]: <%s>%s' % (classname, name, blockname, pc, hex(bc), interpreter_bytecodes.BYTECODE_NAMES[bc]) def resume_get_printable_location(pc, self, method, w_class): - return "resume: %s" % get_printable_location(pc, self, method, w_class, None) + return "resume: %s" % get_printable_location(pc, 0, 0, self, method, w_class, None) # def confirm_enter_jit(pc, self, method, w_class, s_context): # print get_printable_location(pc, self, method, w_class) @@ -161,11 +169,12 @@ class Interpreter(object): "evented", "interrupts", "trace_important", - "trace"] + "trace", + "optargs"] jit_driver = jit.JitDriver( name=jit_driver_name, - greens=['pc', 'self', 'method', 'w_class', 'blockmethod'], + greens=['pc', 'jump_back_pc', 'unrollings', 'self', 'method', 'w_class', 'blockmethod'], reds=['s_context'], virtualizables=['s_context'], get_printable_location=get_printable_location, @@ -182,7 +191,7 @@ class Interpreter(object): ) def __init__(self, space, image=None, trace_important=False, - trace=False, evented=True, interrupts=True): + trace=False, evented=True, interrupts=True, optargs=None): # === Initialize immutable variables self.space = space self.image = image @@ -199,6 +208,7 @@ def __init__(self, space, image=None, trace_important=False, self.interrupt_counter_size = constants.INTERRUPT_COUNTER_SIZE self.last_check = self.time_now() self.trace = trace + self.optargs = optargs # === Initialize mutable variables self.interrupt_check_counter = self.interrupt_counter_size @@ -308,23 +318,52 @@ def getblockmethod(self, s_context): def loop_bytecodes(self, s_context, may_context_switch=True): old_pc = 0 + jump_back_pc = 0 + unrollings = 0 if not jit.we_are_jitted() and may_context_switch: self.quick_check_for_interrupt(s_context) method = s_context.w_method() while True: pc = s_context.pc() if pc < old_pc: - if jit.we_are_jitted(): - # Do the interrupt-check at the end of a loop, don't interrupt loops midway. - self.jitted_check_for_interrupt(s_context) - self.jit_driver.can_enter_jit( - pc=pc, self=self, method=method, - w_class=self.getreceiverclass(s_context), - blockmethod=self.getblockmethod(s_context), - s_context=s_context) + if jump_back_pc == old_pc: + if (unrollings < self.optargs.max_squeak_unroll_count and + jit.current_trace_length() < self.optargs.squeak_unroll_trace_limit): + unrollings += 1 + else: + if jit.we_are_jitted(): + # Do the interrupt-check at the end of a loop, don't + # interrupt loops midway. + self.jitted_check_for_interrupt(s_context) + self.jit_driver.can_enter_jit( + pc=pc, + jump_back_pc=jump_back_pc, + unrollings=unrollings, + self=self, method=method, + w_class=self.getreceiverclass(s_context), + blockmethod=self.getblockmethod(s_context), + s_context=s_context) + else: + jump_back_pc = old_pc + unrollings = 1 + # we jumped back from the end of a loop. Instead of allowing + # to enter the JIT here, we instead wait for the second time + # this loop runs and call can_enter_jit only then + # (effectively unrolling at least two iterations). This is + # because the way the Squeak compiler generates loop + # bytecodes: we get the branch condition in the header and a + # conditional jump forward in case it is false. Then the + # loop body and an unconditional jump back. In the case of 1 + # to: 1 do: or other loops that run for exactly one + # iteration, we will still generate a call_assembler in that + # case, which we work around with this. There are indeed a + # few examples of loops that run exactly one iteration old_pc = pc self.jit_driver.jit_merge_point( - pc=pc, self=self, method=method, + pc=pc, + jump_back_pc=jump_back_pc, + unrollings=unrollings, + self=self, method=method, w_class=self.getreceiverclass(s_context), blockmethod=self.getblockmethod(s_context), s_context=s_context) diff --git a/rsqueakvm/main.py b/rsqueakvm/main.py index d21f8913..643fceb9 100644 --- a/rsqueakvm/main.py +++ b/rsqueakvm/main.py @@ -93,6 +93,14 @@ def _usage(argv): Other: -j|--jit - jitargs will be passed to the jit config. + -o|--opt - custom optimization heuristics for the RSqueak + interpreter. Comma-separated list of: + max_squeak_unroll_count (default: 1) + How many extra times we should try + to unroll loops. + squeak_unroll_trace_limit (default: 32000) + Trace length after which we should not + unroll any more Squeak loops. -p|--poll - Actively poll for events. Try this if the image is not responding well. -i|--no-interrupts - Disable timer interrupt. @@ -201,6 +209,7 @@ def __init__(self, space, argv): self.extra_arguments_idx = len(argv) self.log_image_loading = False self.shell = False + self.optargs = interpreter.Optargs() def parse_args(self, argv, skip_bad=False): idx = 1 @@ -263,6 +272,17 @@ def parse_args(self, argv, skip_bad=False): jit.set_param(interpreter.Interpreter.jit_driver, "trace_limit", int(limit.split("=")[1])) if len(parts) > 0: jit.set_user_param(interpreter.Interpreter.jit_driver, ",".join(parts)) + elif arg in ["-o", "--opt"]: + optarg, idx = get_parameter(argv, idx, arg) + parts = optarg.split(",") + for part in parts: + key, value = part.split("=") + if "max_squeak_unroll_count" == key: + self.optargs.max_squeak_unroll_count = int(value) + elif "squeak_unroll_trace_limit" == key: + self.optargs.squeak_unroll_trace_limit == int(value) + else: + raise Exception("Wrong argument to %s: %s" % (arg, part)) elif arg in ["-p", "--poll"]: self.poll = True elif arg in ["-i", "--no-interrupts"]: @@ -426,7 +446,8 @@ def entry_point(argv): image = squeakimage.ImageReader(space, stream, cfg.log_image_loading).create_image() interp = interpreter.Interpreter(space, image, trace=cfg.trace, trace_important=cfg.trace_important, - evented=not cfg.poll, interrupts=cfg.interrupts) + evented=not cfg.poll, interrupts=cfg.interrupts, + optargs=cfg.optargs) space.runtime_setup(interp, cfg.exepath, argv, cfg.path, cfg.extra_arguments_idx) print_error("") # Line break after image-loading characters diff --git a/rsqueakvm/test/jittest/test_modern.py b/rsqueakvm/test/jittest/test_modern.py index 630c2f55..b6988dd5 100644 --- a/rsqueakvm/test/jittest/test_modern.py +++ b/rsqueakvm/test/jittest/test_modern.py @@ -1251,3 +1251,63 @@ def test_nested_closure_loop_call(self, spy, tmpdir): guard_false(i335, descr=) jump(p0, p1, i2, p4, p6, p7, p9, i328, p18, p20, p22, p24, p26, p28, p30, p32, p34, p36, p38, p40, p42, p324, i332, descr=TargetToken(93835149719440)) """) + + def test_loop_unrolling(self, spy, tmpdir): + traces = self.run(spy, tmpdir, """ + | c instance | + Object + subclass: #MyA + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Test'. + c := Smalltalk at: #MyA. + c compile: 'oneIterLoop +1 to: 1 do: [:i | ].' classified: 'none' withStamp: nil notifying: nil logSource: false. + instance := c new. + 1 to: 100000 do: [:i | instance oneIterLoop ]. + """) + self.assert_matches(traces[-1].loop, """ + guard_not_invalidated(descr=) + i74 = int_le(i65, 100000) + guard_true(i74, descr=) + p75 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + i80 = int_add(i65, 1) + i82 = int_sub(i69, 1) + setfield_gc(ConstPtr(ptr83), i82, descr=) + i85 = int_le(i82, 0) + guard_false(i85, descr=) + jump(p0, p1, i2, p4, p6, p7, p9, p12, p14, i80, p22, p24, p26, p28, p30, p32, p34, p36, p38, p40, p42, p47, i82, descr=TargetToken(83088416)) + """) + + def test_loop_unrolling2(self, spy, tmpdir): + traces = self.run(spy, tmpdir, """ + | c instance | + Object + subclass: #MyA + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Test'. + c := Smalltalk at: #MyA. + c compile: 'twoIterLoop +1 to: 2 do: [:i | ].' classified: 'none' withStamp: nil notifying: nil logSource: false. + instance := c new. + 1 to: 100000 do: [:i | instance twoIterLoop ]. + """) + self.assert_matches(traces[-1].loop, """ + guard_not_invalidated(descr=) + i74 = int_le(i65, 100000) + guard_true(i74, descr=) + p75 = force_token() + enter_portal_frame(1, 0) + leave_portal_frame(1) + i80 = int_add(i65, 1) + i82 = int_sub(i69, 1) + setfield_gc(ConstPtr(ptr83), i82, descr=) + i85 = int_le(i82, 0) + guard_false(i85, descr=) + jump(p0, p1, i2, p4, p6, p7, p9, p12, p14, i80, p22, p24, p26, p28, p30, p32, p34, p36, p38, p40, p42, p47, i82, descr=TargetToken(83088416)) + """) From c582599f849994186b7e45ae7c57e9a66c7226be Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 20 Mar 2017 08:37:13 +0100 Subject: [PATCH 36/52] try harder to avoid an extra guard in bytecodePrimAt --- rsqueakvm/primitives/__init__.py | 1 + rsqueakvm/primitives/array_stream.py | 16 ++++++++-------- rsqueakvm/storage.py | 5 ++++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/rsqueakvm/primitives/__init__.py b/rsqueakvm/primitives/__init__.py index df652c11..970a8334 100644 --- a/rsqueakvm/primitives/__init__.py +++ b/rsqueakvm/primitives/__init__.py @@ -22,6 +22,7 @@ def assert_valid_index(space, n0, w_obj): raise PrimitiveFailedError() # return the index, since from here on the annotator knows that # n0 cannot be negative + assert n0 >= 0 return n0 def assert_valid_inst_index(space, n0, w_obj): diff --git a/rsqueakvm/primitives/array_stream.py b/rsqueakvm/primitives/array_stream.py index cae7a97f..97da21d7 100644 --- a/rsqueakvm/primitives/array_stream.py +++ b/rsqueakvm/primitives/array_stream.py @@ -8,13 +8,13 @@ # Array and Stream Primitives @expose_primitive(AT, unwrap_spec=[object, index1_0]) -def func(interp, s_frame, w_obj, n0): - n0 = assert_valid_index(interp.space, n0, w_obj) +def func(interp, s_frame, w_obj, i0): + n0 = assert_valid_index(interp.space, i0, w_obj) return w_obj.at0(interp.space, n0) @expose_primitive(AT_PUT, unwrap_spec=[object, index1_0, object]) -def func(interp, s_frame, w_obj, n0, w_val): - n0 = assert_valid_index(interp.space, n0, w_obj) +def func(interp, s_frame, w_obj, i0, w_val): + n0 = assert_valid_index(interp.space, i0, w_obj) w_obj.atput0(interp.space, n0, w_val) return w_val @@ -25,8 +25,8 @@ def func(interp, s_frame, w_obj): return interp.space.wrap_int(w_obj.varsize()) @expose_primitive(STRING_AT, unwrap_spec=[object, index1_0]) -def func(interp, s_frame, w_obj, n0): - n0 = assert_valid_index(interp.space, n0, w_obj) +def func(interp, s_frame, w_obj, i0): + n0 = assert_valid_index(interp.space, i0, w_obj) # TODO: This can actually be called on any indexable object... if not (isinstance(w_obj, W_BytesObject) or isinstance(w_obj, W_WordsObject)): @@ -34,9 +34,9 @@ def func(interp, s_frame, w_obj, n0): return interp.space.wrap_char(w_obj.getchar(n0)) @expose_primitive(STRING_AT_PUT, unwrap_spec=[object, index1_0, object]) -def func(interp, s_frame, w_obj, n0, w_val): +def func(interp, s_frame, w_obj, i0, w_val): val = interp.space.unwrap_char_as_byte(w_val) - n0 = assert_valid_index(interp.space, n0, w_obj) + n0 = assert_valid_index(interp.space, i0, w_obj) if not (isinstance(w_obj, W_CompiledMethod) or isinstance(w_obj, W_BytesObject) or isinstance(w_obj, W_WordsObject)): diff --git a/rsqueakvm/storage.py b/rsqueakvm/storage.py index dd79e04c..9fc7979a 100644 --- a/rsqueakvm/storage.py +++ b/rsqueakvm/storage.py @@ -116,8 +116,11 @@ class SimpleStorageStrategy(AbstractStrategy): """ repr_classname = "SimpleStorageStrategy" _attrs_ = [] - import_from_mixin(rstrat.UnsafeIndexingMixin) + def check_index_fetch(self, w_self, index0): + assert index0 >= 0 + def check_index_store(self, w_self, index0): + assert index0 >= 0 def default_value(self): return self.space.w_nil From 999206705b50ab5dd88a8695d8f57871c46751d1 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 20 Mar 2017 09:46:58 +0100 Subject: [PATCH 37/52] test fix --- rsqueakvm/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index 7e54dc24..c569225a 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -208,7 +208,7 @@ def __init__(self, space, image=None, trace_important=False, self.interrupt_counter_size = constants.INTERRUPT_COUNTER_SIZE self.last_check = self.time_now() self.trace = trace - self.optargs = optargs + self.optargs = optargs or Optargs() # === Initialize mutable variables self.interrupt_check_counter = self.interrupt_counter_size From f3c3316b5bf57615a0bb07d87671ea0e1c703bfb Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 20 Mar 2017 11:15:41 +0100 Subject: [PATCH 38/52] only set the framesize if it grows, only let it grow, and make it into a green --- rsqueakvm/interpreter.py | 15 ++++++++++----- rsqueakvm/model/compiled_methods.py | 11 +++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index c569225a..b744e15e 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -136,7 +136,7 @@ def __init__(self): UNROLLING_BYTECODE_RANGES = unroll.unrolling_iterable(interpreter_bytecodes.BYTECODE_RANGES) -def get_printable_location(pc, jump_back_pc, unrollings, self, method, w_class, blockmethod): +def get_printable_location(pc, jump_back_pc, unrollings, frame_size, self, method, w_class, blockmethod): bc = ord(method.bytes[pc]) name = method.safe_identifier_string() classname = "???" @@ -151,8 +151,8 @@ def get_printable_location(pc, jump_back_pc, unrollings, self, method, w_class, blockname = blockmethod.safe_identifier_string() return '%s(%s): (%s) [%d]: <%s>%s' % (classname, name, blockname, pc, hex(bc), interpreter_bytecodes.BYTECODE_NAMES[bc]) -def resume_get_printable_location(pc, self, method, w_class): - return "resume: %s" % get_printable_location(pc, 0, 0, self, method, w_class, None) +def resume_get_printable_location(pc, frame_size, self, method, w_class): + return "resume: %s" % get_printable_location(pc, 0, 0, frame_size, self, method, w_class, None) # def confirm_enter_jit(pc, self, method, w_class, s_context): # print get_printable_location(pc, self, method, w_class) @@ -174,7 +174,8 @@ class Interpreter(object): jit_driver = jit.JitDriver( name=jit_driver_name, - greens=['pc', 'jump_back_pc', 'unrollings', 'self', 'method', 'w_class', 'blockmethod'], + greens=['pc', 'jump_back_pc', 'unrollings', 'frame_size', 'self', + 'method', 'w_class', 'blockmethod'], reds=['s_context'], virtualizables=['s_context'], get_printable_location=get_printable_location, @@ -183,7 +184,7 @@ class Interpreter(object): resume_driver = jit.JitDriver( name=jit_driver_name + "_resume", - greens=['pc', 'self', 'method', 'w_class'], + greens=['pc', 'frame_size', 'self', 'method', 'w_class'], reds=['s_context'], # virtualizables=['s_context'], get_printable_location=resume_get_printable_location, @@ -230,6 +231,7 @@ def loop(self, w_active_context): pc = s_context.pc() self.resume_driver.jit_merge_point( pc=pc, + frame_size=method.frame_size(), self=self, method=method, w_class=self.getreceiverclass(s_context), @@ -249,6 +251,7 @@ def loop(self, w_active_context): self.resume_driver.can_enter_jit( pc=pc, self=self, + frame_size=method.frame_size(), method=method, w_class=self.getreceiverclass(s_context), s_context=s_context) @@ -339,6 +342,7 @@ def loop_bytecodes(self, s_context, may_context_switch=True): pc=pc, jump_back_pc=jump_back_pc, unrollings=unrollings, + frame_size=method.frame_size(), self=self, method=method, w_class=self.getreceiverclass(s_context), blockmethod=self.getblockmethod(s_context), @@ -363,6 +367,7 @@ def loop_bytecodes(self, s_context, may_context_switch=True): pc=pc, jump_back_pc=jump_back_pc, unrollings=unrollings, + frame_size=method.frame_size(), self=self, method=method, w_class=self.getreceiverclass(s_context), blockmethod=self.getblockmethod(s_context), diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index 0312514a..1afb1f64 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -142,12 +142,10 @@ def set_lookup_class_and_name(self, w_class, selector): def setbytes(self, bytes): self.bytes = bytes - self._frame_size = 0 def setchar(self, index0, character): assert index0 >= 0 self.bytes[index0] = character - self._frame_size = 0 # === Getters === @@ -188,12 +186,12 @@ def getliteral(self, index): def primitive(self): return self._primitive - @jit.elidable_promote() def frame_size(self): return self._frame_size def update_frame_size(self, size): - self._frame_size = max(self._frame_size, size) + if self._frame_size < size: + self._frame_size = size @jit.elidable_promote() def squeak_frame_size(self): @@ -323,6 +321,7 @@ def _become(self, w_other): self.literals, w_other.literals = w_other.literals, self.literals self._tempsize, w_other._tempsize = w_other._tempsize, self._tempsize self._frame_size, w_other._frame_size = w_other._frame_size, self._frame_size + self.version, w_other.version = w_other.version, self.version self.bytes, w_other.bytes = w_other.bytes, self.bytes self.header, w_other.header = w_other.header, self.header self.literalsize, w_other.literalsize = w_other.literalsize, self.literalsize @@ -434,7 +433,7 @@ def setheader(self, space, header, initializing=False): self.update_primitive_index() else: self._primitive = 0 - self._frame_size = self.argsize + self._tempsize + self.update_frame_size(self.argsize + self._tempsize) def setbytes(self, bytes): W_CompiledMethod.setbytes(self, bytes) @@ -464,4 +463,4 @@ def setheader(self, space, header, initializing=False): self._primitive = decoded_header.primitive_index self.islarge = decoded_header.large_frame self.compiledin_class = None - self._frame_size = self.argsize + self._tempsize + self.update_frame_size(self.argsize + self._tempsize) From 5ff5b921367719c51e79405ddac1aeeb4380c48b Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 20 Mar 2017 11:25:44 +0100 Subject: [PATCH 39/52] only jit frames that have no overflow stack --- rsqueakvm/interpreter.py | 19 ++++++++++--------- rsqueakvm/storage_contexts.py | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index b744e15e..213c0b3b 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -338,15 +338,16 @@ def loop_bytecodes(self, s_context, may_context_switch=True): # Do the interrupt-check at the end of a loop, don't # interrupt loops midway. self.jitted_check_for_interrupt(s_context) - self.jit_driver.can_enter_jit( - pc=pc, - jump_back_pc=jump_back_pc, - unrollings=unrollings, - frame_size=method.frame_size(), - self=self, method=method, - w_class=self.getreceiverclass(s_context), - blockmethod=self.getblockmethod(s_context), - s_context=s_context) + if not s_context.has_overflow_stack(): + self.jit_driver.can_enter_jit( + pc=pc, + jump_back_pc=jump_back_pc, + unrollings=unrollings, + frame_size=method.frame_size(), + self=self, method=method, + w_class=self.getreceiverclass(s_context), + blockmethod=self.getblockmethod(s_context), + s_context=s_context) else: jump_back_pc = old_pc unrollings = 1 diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 43aff98b..782b3ee6 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -57,7 +57,7 @@ def __init__(self): self._w_home = None self._initialip = 0 self._eargc = 0 - self._overflow_stack = [] + self._overflow_stack = None self.blockmethod = None @@ -530,15 +530,23 @@ def overflow_stack_put(self, index0, w_val): overflow_index = index0 - len(self._temps_and_stack) self.get_extra_data()._overflow_stack[overflow_index] = w_val + def has_overflow_stack(self): + extra_data = self.extra_data() + return extra_data and extra_data._overflow_stack is not None + @jit.unroll_safe def update_stacksize(self, index0): stacksize = len(self._temps_and_stack) if stacksize <= index0: self.w_method().update_frame_size(index0 + 1) - overflow_stack = self.get_extra_data()._overflow_stack + extra_data = self.get_extra_data() + overflow_stack = extra_data._overflow_stack overflow_index = index0 - stacksize - while len(overflow_stack) <= overflow_index: - overflow_stack.append(None) + if overflow_stack is None: + extra_data._overflow_stack = [None] * (overflow_index + 1) + else: + while len(overflow_stack) <= overflow_index: + overflow_stack.append(None) return True return False From 101e3126b875632440340a6d21869d9b5829b9fc Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 20 Mar 2017 12:33:14 +0100 Subject: [PATCH 40/52] forgot to init the frame size on fillin --- rsqueakvm/interpreter.py | 10 ++++++---- rsqueakvm/model/compiled_methods.py | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index 213c0b3b..cc8b6826 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -228,10 +228,11 @@ def loop(self, w_active_context): s_context = w_active_context.as_context_get_shadow(self.space) while True: method = s_context.w_method() + frame_size = method.frame_size() pc = s_context.pc() self.resume_driver.jit_merge_point( pc=pc, - frame_size=method.frame_size(), + frame_size=frame_size, self=self, method=method, w_class=self.getreceiverclass(s_context), @@ -251,7 +252,7 @@ def loop(self, w_active_context): self.resume_driver.can_enter_jit( pc=pc, self=self, - frame_size=method.frame_size(), + frame_size=frame_size, method=method, w_class=self.getreceiverclass(s_context), s_context=s_context) @@ -326,6 +327,7 @@ def loop_bytecodes(self, s_context, may_context_switch=True): if not jit.we_are_jitted() and may_context_switch: self.quick_check_for_interrupt(s_context) method = s_context.w_method() + frame_size = method.frame_size() while True: pc = s_context.pc() if pc < old_pc: @@ -343,7 +345,7 @@ def loop_bytecodes(self, s_context, may_context_switch=True): pc=pc, jump_back_pc=jump_back_pc, unrollings=unrollings, - frame_size=method.frame_size(), + frame_size=frame_size, self=self, method=method, w_class=self.getreceiverclass(s_context), blockmethod=self.getblockmethod(s_context), @@ -368,7 +370,7 @@ def loop_bytecodes(self, s_context, may_context_switch=True): pc=pc, jump_back_pc=jump_back_pc, unrollings=unrollings, - frame_size=method.frame_size(), + frame_size=frame_size, self=self, method=method, w_class=self.getreceiverclass(s_context), blockmethod=self.getblockmethod(s_context), diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index 1afb1f64..c6d153e3 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -104,6 +104,7 @@ def fillin(self, space, g_self): W_AbstractObjectWithIdentityHash.fillin(self, space, g_self) self.lookup_selector = "unknown%d" % self.gethash() self.bytes = [] # make sure the attribute is defined + self._frame_size = 0 # Implicitly sets the header, including self.literalsize for i, w_object in enumerate(g_self.get_pointers()): self.literalatput0(space, i, w_object, initializing=True) From 74bf83445456a9d8fb39722483354062cca30ac7 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 20 Mar 2017 12:35:08 +0100 Subject: [PATCH 41/52] wrong return --- rsqueakvm/storage_contexts.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 782b3ee6..36515491 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -532,7 +532,9 @@ def overflow_stack_put(self, index0, w_val): def has_overflow_stack(self): extra_data = self.extra_data() - return extra_data and extra_data._overflow_stack is not None + if extra_data: + return extra_data._overflow_stack is not None + return False @jit.unroll_safe def update_stacksize(self, index0): From 4e7fd7b8c47daa9818d6b7dd9f21176a752bf4be Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 21 Mar 2017 11:50:03 +0100 Subject: [PATCH 42/52] split safe identification methods on compiled method --- rsqueakvm/model/compiled_methods.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index c6d153e3..fe87b72a 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -404,18 +404,24 @@ def guess_containing_classname(self): def get_identifier_string(self): return "%s >> #%s" % (self.guess_containing_classname(), self.lookup_selector) - def safe_identifier_string(self): - if not we_are_translated(): - return self.get_identifier_string() - # This has the same functionality as get_identifier_string, but without calling any - # methods in order to avoid side effects that prevent translation. + def safe_class_string(self): w_class = self.safe_compiled_in() if isinstance(w_class, W_PointersObject): from rsqueakvm.storage_classes import ClassShadow s_class = w_class.strategy if isinstance(s_class, ClassShadow): - return "%s >> #%s" % (s_class.getname(), self.lookup_selector) - return "#%s" % self.lookup_selector + return s_class.getname() + return "UnknownClass" + + def safe_method_string(self): + return self.lookup_selector + + def safe_identifier_string(self): + if not we_are_translated(): + return self.get_identifier_string() + # This has the same functionality as get_identifier_string, but without calling any + # methods in order to avoid side effects that prevent translation. + return "%s >> #%s" % (self.safe_class_string(), self.safe_method_string()) class W_SpurCompiledMethod(W_CompiledMethod): From c4c98f76c04d7a7001b279e42983d491e4ce048c Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 21 Mar 2017 11:50:25 +0100 Subject: [PATCH 43/52] patch the jitdriver to produce better jitlog and vmprof info --- rsqueakvm/plugins/profiler_plugin.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rsqueakvm/plugins/profiler_plugin.py b/rsqueakvm/plugins/profiler_plugin.py index 6c439c01..da69cab4 100644 --- a/rsqueakvm/plugins/profiler_plugin.py +++ b/rsqueakvm/plugins/profiler_plugin.py @@ -28,6 +28,8 @@ def _get_code(interp, s_frame, s_sender, may_context_switch=True): _decorator = rvmprof.vmprof_execute_code("rsqueak", _get_code) _my_stack_frame = _decorator(Interpreter.stack_frame) Interpreter.stack_frame = _my_stack_frame + Interpreter.jit_driver.get_location = get_location + Interpreter.jit_driver.get_unique_id = get_unique_id print "Interpreter was patched for vmprof" @@ -41,6 +43,7 @@ def _get_full_name(w_cm): # must not be longer than 255 chars return "st:%s:0:/img" % _safe(w_cm.safe_identifier_string()) + rvmprof.register_code_object_class(W_CompiledMethod, _get_full_name) @@ -51,6 +54,25 @@ def _my_post_init(self): W_CompiledMethod.post_init = _my_post_init patch_compiled_method() + +@rjitlog.returns(rjitlog.MP_FILENAME, rjitlog.MP_LINENO, + rjitlog.MP_SCOPE, rjitlog.MP_INDEX, rjitlog.MP_OPCODE) +def get_location(pc, jump_back_pc, unrollings, frame_size, self, method, w_class, blockmethod): + from rsqueakvm.interpreter_bytecodes import BYTECODE_NAMES + classname = "string://%s" % _safe(method.safe_class_string()) + methodname = _safe(method.safe_method_string()) + bc = ord(method.bytes[pc]) + bcname = BYTECODE_NAMES[bc] + return (classname, pc, methodname, bc, bcname) + + +def get_unique_id(pc, jump_back_pc, unrollings, frame_size, self, method, w_class, blockmethod): + # XXX: this is patched too late in the annotation process, so we cannot use + # rvmprof.get_unique_id because that'll call _was_registered which is a + # specialize.memo that then happens too late in the annotation process + return method._vmprof_unique_id + + # ____________________________________________________________ From b412e7f622416c856fbcdb89ada6b0a26bfbf59c Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 21 Mar 2017 14:59:56 +0100 Subject: [PATCH 44/52] try harder to find the selector when filling in compiled methods --- rsqueakvm/model/compiled_methods.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index fe87b72a..d68dc506 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -1,6 +1,7 @@ from rsqueakvm import constants, error from rsqueakvm.model.base import W_Object, W_AbstractObjectWithIdentityHash from rsqueakvm.model.pointers import W_PointersObject +from rsqueakvm.model.variable import W_BytesObject from rpython.rlib import jit from rpython.rlib.objectmodel import not_rpython, we_are_translated @@ -105,9 +106,12 @@ def fillin(self, space, g_self): self.lookup_selector = "unknown%d" % self.gethash() self.bytes = [] # make sure the attribute is defined self._frame_size = 0 + for g_obj in g_self.pointers: + g_obj.fillin(space) # Implicitly sets the header, including self.literalsize for i, w_object in enumerate(g_self.get_pointers()): self.literalatput0(space, i, w_object, initializing=True) + self.update_selector_from_literals() self.setbytes(g_self.get_bytes()[self.bytecodeoffset():]) self.post_init() @@ -294,6 +298,15 @@ def atput0(self, space, index0, w_value): # === Misc === + def update_selector_from_literals(self): + # Second-to-last of the literals is either the symbol selector or an + # object the selector in the second slot + literals = self.literals + if literals and len(literals) > 1: + w_symbol = literals[-2] + if isinstance(w_symbol, W_BytesObject): + self.lookup_selector = "".join(w_symbol.getbytes()) + def update_compiledin_class_from_literals(self): # (Blue book, p 607) Last of the literals is either the containing class # or an association with compiledin as a class From 59f27c67796b95b6d72d0b7c90913dc5107bbc75 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 21 Mar 2017 15:04:49 +0100 Subject: [PATCH 45/52] fix tests --- rsqueakvm/model/compiled_methods.py | 1 - rsqueakvm/storage_contexts.py | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index d68dc506..8d302cda 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -335,7 +335,6 @@ def _become(self, w_other): self.literals, w_other.literals = w_other.literals, self.literals self._tempsize, w_other._tempsize = w_other._tempsize, self._tempsize self._frame_size, w_other._frame_size = w_other._frame_size, self._frame_size - self.version, w_other.version = w_other.version, self.version self.bytes, w_other.bytes = w_other.bytes, self.bytes self.header, w_other.header = w_other.header, self.header self.literalsize, w_other.literalsize = w_other.literalsize, self.literalsize diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 36515491..2d57bba9 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -555,8 +555,10 @@ def update_stacksize(self, index0): @objectmodel.not_rpython # this is only for testing. def stack(self): stacksize = len(self._temps_and_stack) - return (self._temps_and_stack[self.tempsize():stacksize] + - self.get_extra_data()._overflow_stack[self.tempsize() - stacksize:self.stack_ptr()]) + stack = self._temps_and_stack[self.tempsize():stacksize] + if self.has_overflow_stack(): + stack.extend(self.get_extra_data()._overflow_stack[self.tempsize() - stacksize:self.stack_ptr()]) + return stack def pop(self): # HACK HACK HACK (3 times) From e526d1a743652add98e7eb3f46263fe3f1a9f9ab Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 21 Mar 2017 18:27:26 +0100 Subject: [PATCH 46/52] add --run-file argument to support the Squeak-style for running code from files but running the content of the file in headless mode --- rsqueakvm/main.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/rsqueakvm/main.py b/rsqueakvm/main.py index 5415756a..18fcf227 100644 --- a/rsqueakvm/main.py +++ b/rsqueakvm/main.py @@ -79,6 +79,9 @@ def _usage(argv): synthetic high-prio Process. -u|--stop-ui - Only with -m or -r. Try to stop UI-process at startup. Can help benchmarking. + --run-file - Run the .st file supplied as first argument. No + display will be created, but stdin/stdout will + be connected so terminal input/output will work. --shell - Stop after loading the image. Any code typed is compiled an run. --simulate-numeric-primitives @@ -202,6 +205,7 @@ def __init__(self, space, argv): self.got_lone_path = False self.selector = None self.code = "" + self.run_file = False self.number = 0 self.have_number = False self.stringarg = None @@ -242,6 +246,8 @@ def parse_args(self, argv, skip_bad=False): # Execution elif arg in ["-r", "--run"]: self.code, idx = get_parameter(argv, idx, arg) + elif arg in ["--run-file"]: + self.run_file = True elif arg in ["-m", "--method"]: self.selector, idx = get_parameter(argv, idx, arg) elif arg in ["-n", "--number"]: @@ -362,6 +368,30 @@ def ensure_path(self): def sanitize(self): self.ensure_path() + if self.run_file: + if self.code: + raise error.Exit("Cannot handle both --run-file and -r.") + try: + codefile = self.argv[self.extra_arguments_idx] + except IndexError: + raise error.Exit("Cannot handle --run-file without a file argument.") + try: + f = streamio.open_file_as_stream(codefile, mode="r", buffering=0) + except OSError as e: + raise error.Exit("Cannot read %s" % codefile) + try: + source = list(f.readall()) + finally: + f.close() + if len(source) == 0: + raise error.Exit("Empty source file given") + idx = len(source) - 1 + assert idx > 0 + while source[idx].isspace(): + idx -= 1 + if source[idx] == "!": + source[idx] = " " + self.code = "".join(source) if self.code and self.selector: raise error.Exit("Cannot handle both -r and -m.") @@ -473,6 +503,11 @@ def entry_point(argv): else: w_receiver = space.wrap_int(cfg.number) if cfg.code: + if cfg.run_file: # connect the stdio streams + selector = compile_code( + interp, w_receiver, "FileStream startUp: true") + with objspace.ForceHeadless(space): + interp.perform(w_receiver, selector) cfg.selector = compile_code(interp, w_receiver, cfg.code) s_frame = create_context(interp, w_receiver, cfg.selector, cfg.stringarg) if cfg.headless: From ffe009fcf473d3a8f906858f5625d4fcd3132019 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 22 Mar 2017 09:25:38 +0100 Subject: [PATCH 47/52] add a -rr cmdline argument as a hack to run a doit twice, so it gets jitted --- rsqueakvm/main.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rsqueakvm/main.py b/rsqueakvm/main.py index 18fcf227..6df53aa7 100644 --- a/rsqueakvm/main.py +++ b/rsqueakvm/main.py @@ -66,6 +66,8 @@ def _usage(argv): Execution: -r|--run - Code will be compiled and executed in headless mode, result printed. + -rr - Code will be compiled and executed in + headless mode, twice, the second result printed. -m|--method - Selector will be sent to nil in headless mode, result printed. @@ -206,6 +208,7 @@ def __init__(self, space, argv): self.selector = None self.code = "" self.run_file = False + self.run_twice = False self.number = 0 self.have_number = False self.stringarg = None @@ -246,6 +249,9 @@ def parse_args(self, argv, skip_bad=False): # Execution elif arg in ["-r", "--run"]: self.code, idx = get_parameter(argv, idx, arg) + elif arg == "-rr": + self.code, idx = get_parameter(argv, idx, arg) + self.run_twice = True elif arg in ["--run-file"]: self.run_file = True elif arg in ["-m", "--method"]: @@ -508,7 +514,12 @@ def entry_point(argv): interp, w_receiver, "FileStream startUp: true") with objspace.ForceHeadless(space): interp.perform(w_receiver, selector) - cfg.selector = compile_code(interp, w_receiver, cfg.code) + if cfg.run_twice: + selector = compile_code(interp, w_receiver, cfg.code) + cfg.selector = compile_code( + interp, w_receiver, "^ self %s; %s" % (selector, selector)) + else: + cfg.selector = compile_code(interp, w_receiver, cfg.code) s_frame = create_context(interp, w_receiver, cfg.selector, cfg.stringarg) if cfg.headless: space.headless.activate() From f6d331cd61fbeb73ab7998875e0ce40817429bbf Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 22 Mar 2017 10:40:14 +0100 Subject: [PATCH 48/52] avoid creating an infinite recursion when using -rr --- rsqueakvm/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsqueakvm/main.py b/rsqueakvm/main.py index 6df53aa7..1eee5c64 100644 --- a/rsqueakvm/main.py +++ b/rsqueakvm/main.py @@ -543,7 +543,7 @@ def result_string(w_result): def compile_code(interp, w_receiver, code, isclass=False, make_selector=True): if make_selector: - selector = "DoIt%d\r\n" % int(time.time()) + selector = "DoIt%d\r\n" % (int(time.time()) + len(code)) else: selector = "" space = interp.space From 41cb7938e02c36df27cb3b33892c435defe79bff Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 22 Mar 2017 10:40:33 +0100 Subject: [PATCH 49/52] use double-run for jittests as a workaround for our new frame size discovery mechanism --- rsqueakvm/test/jittest/base.py | 2 +- rsqueakvm/test/jittest/test_basic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rsqueakvm/test/jittest/base.py b/rsqueakvm/test/jittest/base.py index 419c1c99..432c05e2 100644 --- a/rsqueakvm/test/jittest/base.py +++ b/rsqueakvm/test/jittest/base.py @@ -52,7 +52,7 @@ def run(self, spy, tmpdir, code): logfile = str(tmpdir.join("x.pypylog")) print logfile proc = spy.popen( - "--silent", "-r", code.replace("\n", "\r\n"), self.test_image, + "--silent", "-rr", code.replace("\n", "\r\n"), self.test_image, cwd=str(tmpdir), env={"PYPYLOG": "jit:%s" % logfile, "SDL_VIDEODRIVER": "dummy"} diff --git a/rsqueakvm/test/jittest/test_basic.py b/rsqueakvm/test/jittest/test_basic.py index 92ab2b64..308c4c51 100644 --- a/rsqueakvm/test/jittest/test_basic.py +++ b/rsqueakvm/test/jittest/test_basic.py @@ -7,7 +7,7 @@ class TestBasic(BaseJITTest): def test_while_loop(self, spy, tmpdir): traces = self.run(spy, tmpdir, """ - 0 to: 1000000000 do: [:t|nil]. + 0 to: 10000 do: [:t|nil]. """) self.assert_matches(traces[0].loop, """ guard_not_invalidated(descr=) From 93fdd5567dd2e97e6868a056e61f0210af43dc10 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 22 Mar 2017 12:03:38 +0100 Subject: [PATCH 50/52] update jittests --- rsqueakvm/test/jittest/test_basic.py | 4 +- rsqueakvm/test/jittest/test_modern.py | 720 +++++++++++--------------- 2 files changed, 311 insertions(+), 413 deletions(-) diff --git a/rsqueakvm/test/jittest/test_basic.py b/rsqueakvm/test/jittest/test_basic.py index 308c4c51..e5e65cd9 100644 --- a/rsqueakvm/test/jittest/test_basic.py +++ b/rsqueakvm/test/jittest/test_basic.py @@ -375,7 +375,7 @@ def test_bytecode_prim_add_fail(self, spy, tmpdir): p := 1@2. 1 to: 10000 do: [:i | p + p]. """) - self.assert_matches(traces[0].loop, """ + self.assert_matches(traces[1].loop, """ guard_not_invalidated(descr=) i129 = int_le(i123, 10000), guard_true(i129, descr=), @@ -681,7 +681,7 @@ def test_named_object_access(self, spy, tmpdir): o := (10@10 corner: 100@100). 1 to: 10000 do: [:i | o extent ]. """) - self.assert_matches(traces[0].loop, """ + self.assert_matches(traces[1].loop, """ guard_not_invalidated(descr=) i95 = int_le(i86, 10000) guard_true(i95, descr=) diff --git a/rsqueakvm/test/jittest/test_modern.py b/rsqueakvm/test/jittest/test_modern.py index de248f24..1464b80e 100644 --- a/rsqueakvm/test/jittest/test_modern.py +++ b/rsqueakvm/test/jittest/test_modern.py @@ -115,114 +115,28 @@ def test_named_access_in_do_block(self, spy, tmpdir): 1 to: 100000 do: [:i | o do: [:m | m bounds ] ]. """) self.assert_matches(traces[1].loop, """ - guard_not_invalidated(descr=) - i150 = int_le(i141, 100000) - guard_true(i150, descr=) - p151 = getfield_gc_r(p1, descr=) - guard_nonnull_class(p151, ConstClass(ContextPartShadow), descr=) - i153 = ptr_eq(p151, p0) - guard_true(i153, descr=) - p155 = getfield_gc_r(ConstPtr(ptr154), descr=) - guard_value(p155, ConstPtr(ptr156), descr=) - p157 = getfield_gc_r(p11, descr=) - guard_value(p157, ConstPtr(ptr158), descr=) - p160 = getfield_gc_r(ConstPtr(ptr159), descr=) - guard_value(p160, ConstPtr(ptr161), descr=) - p162 = force_token() - enter_portal_frame(1, 0) - p165 = getfield_gc_r(p11, descr=) - i166 = arraylen_gc(p165, descr=) - i168 = int_le(1, i166) - guard_true(i168, descr=) - i170 = uint_lt(0, i166) - guard_true(i170, descr=) - p172 = getarrayitem_gc_r(p165, 0, descr=) - guard_nonnull_class(p172, ConstClass(W_FixedPointersObject), descr=) - p174 = force_token() - enter_portal_frame(1, 0) - p177 = getfield_gc_r(p172, descr=) - guard_value(p177, ConstPtr(ptr178), descr=) - p180 = getfield_gc_r(ConstPtr(ptr179), descr=) - guard_value(p180, ConstPtr(ptr181), descr=) - p182 = getfield_gc_r(p172, descr=) - guard_nonnull_class(p182, ConstClass(W_FixedPointersObject), descr=) + guard_not_invalidated(descr=) + i66 = int_le(i57, 100000) + guard_true(i66, descr=) + p67 = force_token() + enter_portal_frame(1, 45812) + p70 = force_token() + enter_portal_frame(1, 132700) leave_portal_frame(1) - i186 = int_sub(i145, 1) - setfield_gc(ConstPtr(ptr187), i186, descr=) - i189 = int_le(i186, 0) - guard_false(i189, descr=) - p190 = force_token() - p191 = new_with_vtable(descr=) - p192 = new_with_vtable(descr=) - setfield_gc(p192, p162, descr=) - setfield_gc(p192, ConstPtr(null), descr=) - p194 = new_array_clear(17, descr=) - p195 = new_with_vtable(descr=) - setfield_gc(p195, 0, descr=) - setfield_gc(p195, ConstPtr(ptr197), descr=) - setfield_gc(p195, 268435510, descr=) - setfield_gc(p195, ConstPtr(ptr199), descr=) - setfield_gc(p195, p1, descr=) - setfield_gc(p195, p6, descr=) - setfield_gc(p195, ConstPtr(ptr200), descr=) - setarrayitem_gc(p194, 0, p195, descr=) - p202 = new_with_vtable(descr=) - setfield_gc(p202, 2, descr=) - setarrayitem_gc(p194, 1, p202, descr=) - p205 = new_with_vtable(descr=) - setfield_gc(p205, i166, descr=) - setarrayitem_gc(p194, 2, p205, descr=) - setarrayitem_gc(p194, 3, p202, descr=) - setarrayitem_gc(p194, 4, ConstPtr(ptr209), descr=) - setarrayitem_gc(p194, 5, ConstPtr(ptr211), descr=) - setfield_gc(p0, p190, descr=) - setfield_gc(p191, ConstPtr(ptr212), descr=) - setfield_gc(p191, p192, descr=) - setfield_gc(p191, 1086324741, descr=) - setfield_gc(p191, p194, descr=) - setfield_gc(p191, ConstPtr(ptr214), descr=) - setfield_gc(p191, p11, descr=) - setfield_gc(p191, ConstPtr(null), descr=) - setfield_gc(p191, 23, descr=) - setfield_gc(p191, ConstPtr(null), descr=) - setfield_gc(p191, ConstPtr(null), descr=) - call_assembler_n(p191, descr=) - guard_not_forced(descr=) - keepalive(p191) - p220 = guard_exception(13757968, descr=) - i221 = ptr_eq(p191, p0) - guard_false(i221, descr=) - p222 = getfield_gc_r(p191, descr=) - i224 = ptr_ne(p222, ConstPtr(null)) - cond_call(i224, 6399472, p191, descr=) - i226 = getfield_gc_i(p191, descr=) - guard_value(i226, 1086324759, descr=) - guard_not_invalidated(descr=) - p228 = getfield_gc_r(p191, descr=) - guard_isnull(p228, descr=) - p229 = getfield_gc_r(p191, descr=) - guard_value(p229, ConstPtr(ptr230), descr=) - setfield_gc(p191, ConstPtr(null), descr=) - setfield_gc(p191, 1090519039, descr=) - guard_class(p220, 13757968, descr=) - p234 = getfield_gc_r(p220, descr=) - setfield_gc(p191, 16777215, descr=) - setfield_gc(p192, ConstPtr(null), descr=) - guard_nonnull(p234, descr=) - i237 = int_add(i141, 1) - i239 = getfield_gc_i(ConstPtr(ptr238), descr=) - i241 = int_sub(i239, 1) - setfield_gc(ConstPtr(ptr242), i241, descr=) - i244 = int_le(i241, 0) - guard_false(i244, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, p11, i237, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, i241, descr=TargetToken(106207040)) + leave_portal_frame(1) + i76 = int_add(i57, 1) + i78 = int_sub(i61, 1) + setfield_gc(ConstPtr(ptr79), i78, descr=) + i81 = int_le(i78, 0) + guard_false(i81, descr=) + jump(p0, p1, p3, p4, p5, p10, i76, p36, p43, i78, descr=TargetToken(93957335638368)) """) def test_named_access_fresh(self, spy, tmpdir): traces = self.run(spy, tmpdir, """ 1 to: 100000 do: [:i | Morph new bounds ]. """) - self.assert_matches(traces[0].loop, """ + self.assert_matches(traces[1].loop, """ guard_not_invalidated(descr=) i163 = int_le(i153, 100000) guard_true(i163, descr=) @@ -264,7 +178,7 @@ def test_named_access_and_send(self, spy, tmpdir): m := Morph new. 1 to: 100000 do: [:i | m bounds outsetBy: 10 ]. """) - self.assert_matches(traces[3].loop, """ + self.assert_matches(traces[6].loop, """ guard_not_invalidated(descr=) i149 = int_le(i140, 100000) guard_true(i149, descr=) @@ -311,7 +225,7 @@ def test_simple_loop_with_closure(self, spy, tmpdir): traces = self.run(spy, tmpdir, """ 1 to: 100000 do: [:i | [i] value + 100]. """) - self.assert_matches(traces[0].loop, """ + self.assert_matches(traces[1].loop, """ guard_not_invalidated(descr=) i91 = int_le(i84, 100000) guard_true(i91, descr=) @@ -349,7 +263,7 @@ def test_block_passing(self, spy, tmpdir): traces = self.run(spy, tmpdir, """ 1 to: 100000 do: [:i | [:blk | blk value: i] value: [:x | x + 100]]. """) - self.assert_matches(traces[0].loop, """ + self.assert_matches(traces[2].loop, """ guard_not_invalidated(descr=) i91 = int_le(i84, 100000) guard_true(i91, descr=) @@ -437,7 +351,7 @@ def test_global_class_access(self, spy, tmpdir): a := OrderedCollection compilerClass. ]. """) - self.assert_matches(traces[0].loop, + self.assert_matches(traces[1].loop, """ guard_not_invalidated(descr=) i112 = int_lt(i59, i49) @@ -731,7 +645,7 @@ def test_new_large_int(self, spy, tmpdir): ]. """) if IS_64BIT: - self.assert_matches(traces[0].loop, + self.assert_matches(traces[1].loop, """ guard_not_invalidated(descr=) i118 = int_le(i108, 1000000) @@ -765,7 +679,7 @@ def test_new_large_int(self, spy, tmpdir): jump(p0, p1, i2, p4, p5, p6, p8, i143, i147, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p52, p54, p68, i149, descr=TargetToken(100027392)) """) else: - self.assert_matches(traces[0].loop, + self.assert_matches(traces[1].loop, """ guard_not_invalidated(descr=) i110 = int_le(i101, 1000000) @@ -792,72 +706,62 @@ def test_large_negation0(self, spy, tmpdir): a negated. ]. """) - self.assert_matches(traces[0].loop, + self.assert_matches(traces[3].loop, """ - guard_not_invalidated(descr=) - i121 = int_le(i112, 1000000) - guard_true(i121, descr=) - p122 = getfield_gc_r(p11, descr=) - guard_value(p122, ConstPtr(ptr123), descr=) - p125 = getfield_gc_r(ConstPtr(ptr124), descr=) - guard_value(p125, ConstPtr(ptr126), descr=) - p127 = force_token() - enter_portal_frame(1, 0) - p131 = getfield_gc_r(ConstPtr(ptr130), descr=) - guard_value(p131, ConstPtr(ptr132), descr=) - p134 = getfield_gc_r(ConstPtr(ptr133), descr=) - guard_nonnull_class(p134, ConstClass(W_FixedPointersObject), descr=) - i136 = getfield_gc_i(p11, descr=) - i138 = cond_call_value_i(i136, ConstClass(calculate_exposed_size_for_big_int), p64, descr=) - guard_no_exception(descr=) - p140 = getfield_gc_r(ConstPtr(ptr139), descr=) - setfield_gc(p11, i138, descr=) - guard_value(p140, ConstPtr(ptr141), descr=) - p142 = getfield_gc_r(p134, descr=) - guard_value(p142, ConstPtr(ptr143), descr=) - p144 = getfield_gc_r(p142, descr=) - guard_value(p144, ConstPtr(ptr145), descr=) - p147 = getfield_gc_r(ConstPtr(ptr146), descr=) - guard_value(p147, ConstPtr(ptr148), descr=) - p149 = force_token() - enter_portal_frame(1, 0) - i153 = int_lt(i138, 0) - guard_false(i153, descr=) - p154 = new_array_clear(i138, descr=) - p156 = getfield_gc_r(ConstPtr(ptr155), descr=) - guard_value(p156, ConstPtr(ptr157), descr=) + guard_not_invalidated(descr=) + i87 = int_le(i78, 1000000) + guard_true(i87, descr=) + p88 = force_token() + enter_portal_frame(1, 3656) + p92 = getfield_gc_r(ConstPtr(ptr91), descr=) + guard_nonnull_class(p92, ConstClass(W_FixedPointersObject), descr=) + i94 = getfield_gc_i(p10, descr=) + i96 = cond_call_value_i(i94, ConstClass(calculate_exposed_size_for_big_int), p38, descr=) + guard_no_exception(descr=) + setfield_gc(p10, i96, descr=) + p97 = getfield_gc_r(p92, descr=) + guard_value(p97, ConstPtr(ptr98), descr=) + p99 = getfield_gc_r(p97, descr=) + guard_value(p99, ConstPtr(ptr100), descr=) + p101 = force_token() + enter_portal_frame(1, 83540) + i105 = int_lt(i96, 0) + guard_false(i105, descr=) + p106 = new_array_clear(i96, descr=) leave_portal_frame(1) - p159 = force_token() - enter_portal_frame(1, 0) - p163 = getfield_gc_r(ConstPtr(ptr162), descr=) - guard_value(p163, ConstPtr(ptr164), descr=) - p165 = force_token() - enter_portal_frame(1, 0) + p108 = force_token() + enter_portal_frame(1, 3144) + p111 = force_token() + enter_portal_frame(1, 364) leave_portal_frame(1) - i170 = int_sub(i138, 1) - i171 = int_le(i138, i170) - guard_false(i171, descr=) - p172 = force_token() - p173 = new_with_vtable(descr=) - p174 = new_with_vtable(descr=) - setfield_gc(p173, ConstPtr(ptr175), descr=) - setfield_gc(p0, p172, descr=) - setfield_gc(p173, 0, descr=) - setfield_gc(p173, p154, descr=) - setfield_gc(p173, p174, descr=) - call_may_force_n(ConstClass(_replace_from_to_trampoline__v133___simple_call__function__), 0, i170, 0, p173, p11, descr=) - guard_not_forced(descr=) - guard_no_exception(descr=) - guard_not_invalidated(descr=) + i116 = int_sub(i96, 1) + i117 = int_le(i96, i116) + guard_false(i117, descr=) + p118 = force_token() + p119 = new_with_vtable(descr=) + p120 = new_with_vtable(descr=) + setfield_gc(p119, ConstPtr(ptr121), descr=) + setfield_gc(p0, p118, descr=) + setfield_gc(p119, 0, descr=) + setfield_gc(p119, p106, descr=) + setfield_gc(p119, p120, descr=) + call_may_force_n(ConstClass(_replace_from_to_trampoline__v135___simple_call__function__), 0, i116, 0, p119, p10, descr=) + guard_not_forced(descr=) + guard_no_exception(descr=) + guard_not_invalidated(descr=) + p126 = getfield_gc_r(p10, descr=) + guard_value(p126, ConstPtr(ptr127), descr=) leave_portal_frame(1) leave_portal_frame(1) - i183 = int_add(i112, 1) - i185 = getfield_gc_i(ConstPtr(ptr184), descr=) - i187 = int_sub(i185, 1) - setfield_gc(ConstPtr(ptr188), i187, descr=) - i190 = int_le(i187, 0) - guard_false(i190, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, p11, i183, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, p64, descr=TargetToken(88378128)) + p130 = getfield_gc_r(p5, descr=) + guard_value(p130, ConstPtr(ptr131), descr=) + i133 = int_add(i78, 1) + i135 = getfield_gc_i(ConstPtr(ptr134), descr=) + i137 = int_sub(i135, 1) + setfield_gc(ConstPtr(ptr138), i137, descr=) + i140 = int_le(i137, 0) + guard_false(i140, descr=) + jump(p0, p1, p3, p4, p5, p10, i133, p18, p38, descr=TargetToken(94579867017968)) """) # This is a bit full, unfortunately, but there shouldn't be any BlockClosure accesses in this loop @@ -869,95 +773,85 @@ def test_interval_do(self, spy, tmpdir): b := a negated. ]. """) - self.assert_matches(traces[0].loop, + self.assert_matches(traces[2].loop, """ - guard_not_invalidated(descr=) - i153 = int_lt(i59, i49) - guard_true(i153, descr=) - p154 = getfield_gc_r(p6, descr=) - guard_value(p154, ConstPtr(ptr155), descr=) - i156 = getfield_gc_i(p6, descr=) - i157 = getfield_gc_i(p6, descr=) - i158 = int_mul_ovf(i59, i157) - guard_no_overflow(descr=) - i159 = int_add_ovf(i156, i158) - guard_no_overflow(descr=) - i161 = int_add(i59, 1) - p163 = getarrayitem_gc_r(p66, 0, descr=) - guard_nonnull_class(p163, ConstClass(W_LargeIntegerBig), descr=) - p166 = getarrayitem_gc_r(p66, 1, descr=) - guard_nonnull_class(p166, 13692960, descr=) - p168 = force_token() - enter_portal_frame(1, 0) - p171 = getfield_gc_r(p163, descr=) - guard_value(p171, ConstPtr(ptr172), descr=) - p174 = getfield_gc_r(ConstPtr(ptr173), descr=) - guard_value(p174, ConstPtr(ptr175), descr=) - p176 = force_token() - enter_portal_frame(1, 0) - p180 = getfield_gc_r(ConstPtr(ptr179), descr=) - guard_value(p180, ConstPtr(ptr181), descr=) - p183 = getfield_gc_r(ConstPtr(ptr182), descr=) - guard_nonnull_class(p183, ConstClass(W_FixedPointersObject), descr=) - i185 = getfield_gc_i(p163, descr=) - p186 = getfield_gc_r(p163, descr=) - i188 = cond_call_value_i(i185, ConstClass(calculate_exposed_size_for_big_int), p186, descr=) - guard_no_exception(descr=) - p190 = getfield_gc_r(ConstPtr(ptr189), descr=) - setfield_gc(p163, i188, descr=) - guard_value(p190, ConstPtr(ptr191), descr=) - p192 = getfield_gc_r(p183, descr=) - guard_value(p192, ConstPtr(ptr193), descr=) - p194 = getfield_gc_r(p192, descr=) - guard_value(p194, ConstPtr(ptr195), descr=) - p197 = getfield_gc_r(ConstPtr(ptr196), descr=) - guard_value(p197, ConstPtr(ptr198), descr=) - p199 = force_token() - enter_portal_frame(1, 0) - i203 = int_lt(i188, 0) - guard_false(i203, descr=) - p204 = new_array_clear(i188, descr=) - p206 = getfield_gc_r(ConstPtr(ptr205), descr=) - guard_value(p206, ConstPtr(ptr207), descr=) + guard_not_invalidated(descr=) + i121 = int_lt(i39, i32) + guard_true(i121, descr=) + i122 = getfield_gc_i(p5, descr=) + i123 = getfield_gc_i(p5, descr=) + i124 = int_mul_ovf(i39, i123) + guard_no_overflow(descr=) + i125 = int_add_ovf(i122, i124) + guard_no_overflow(descr=) + i127 = int_add(i39, 1) + p129 = getarrayitem_gc_r(p46, 0, descr=) + guard_nonnull_class(p129, ConstClass(W_LargeIntegerBig), descr=) + p132 = getarrayitem_gc_r(p46, 1, descr=) + guard_nonnull_class(p132, 94560195322224, descr=) + p134 = force_token() + enter_portal_frame(1, 132700) + p137 = getfield_gc_r(p129, descr=) + guard_value(p137, ConstPtr(ptr138), descr=) + p139 = force_token() + enter_portal_frame(1, 3656) + p143 = getfield_gc_r(ConstPtr(ptr142), descr=) + guard_nonnull_class(p143, ConstClass(W_FixedPointersObject), descr=) + i145 = getfield_gc_i(p129, descr=) + p146 = getfield_gc_r(p129, descr=) + i148 = cond_call_value_i(i145, ConstClass(calculate_exposed_size_for_big_int), p146, descr=) + guard_no_exception(descr=) + setfield_gc(p129, i148, descr=) + p149 = getfield_gc_r(p143, descr=) + guard_value(p149, ConstPtr(ptr150), descr=) + p151 = getfield_gc_r(p149, descr=) + guard_value(p151, ConstPtr(ptr152), descr=) + p153 = force_token() + enter_portal_frame(1, 83540) + i157 = int_lt(i148, 0) + guard_false(i157, descr=) + p158 = new_array_clear(i148, descr=) leave_portal_frame(1) - p209 = force_token() - enter_portal_frame(1, 0) - p213 = getfield_gc_r(ConstPtr(ptr212), descr=) - guard_value(p213, ConstPtr(ptr214), descr=) - p215 = force_token() - enter_portal_frame(1, 0) + p160 = force_token() + enter_portal_frame(1, 3144) + p163 = force_token() + enter_portal_frame(1, 364) leave_portal_frame(1) - i220 = int_sub(i188, 1) - i221 = int_le(i188, i220) - guard_false(i221, descr=) - p222 = force_token() - p223 = new_with_vtable(descr=) - p224 = new_with_vtable(descr=) - setfield_gc(p223, p224, descr=) - setfield_gc(p223, 0, descr=) - setfield_gc(p223, ConstPtr(ptr226), descr=) - setfield_gc(p0, p222, descr=) - setfield_gc(p223, p204, descr=) - call_may_force_n(ConstClass(_replace_from_to_trampoline__v133___simple_call__function__), 0, i220, 0, p223, p163, descr=) - guard_not_forced(descr=) - guard_no_exception(descr=) - guard_not_invalidated(descr=) + i168 = int_sub(i148, 1) + i169 = int_le(i148, i168) + guard_false(i169, descr=) + p170 = force_token() + p171 = new_with_vtable(descr=) + p172 = new_with_vtable(descr=) + setfield_gc(p171, p172, descr=) + setfield_gc(p171, ConstPtr(ptr173), descr=) + setfield_gc(p0, p170, descr=) + setfield_gc(p171, 0, descr=) + setfield_gc(p171, p158, descr=) + call_may_force_n(ConstClass(_replace_from_to_trampoline__v135___simple_call__function__), 0, i168, 0, p171, p129, descr=) + guard_not_forced(descr=) + guard_no_exception(descr=) + guard_not_invalidated(descr=) + p178 = getfield_gc_r(p129, descr=) + guard_value(p178, ConstPtr(ptr179), descr=) leave_portal_frame(1) leave_portal_frame(1) - p232 = getfield_gc_r(p166, descr=) - guard_value(p232, ConstPtr(ptr233), descr=) - p235 = getfield_gc_r(ConstPtr(ptr234), descr=) - guard_value(p235, ConstPtr(ptr236), descr=) - p237 = getfield_gc_r(p166, descr=) + p182 = getfield_gc_r(p44, descr=) + guard_value(p182, ConstPtr(ptr183), descr=) + p184 = getfield_gc_r(p132, descr=) + guard_value(p184, ConstPtr(ptr185), descr=) + p186 = getfield_gc_r(p132, descr=) leave_portal_frame(1) - i240 = getfield_gc_i(ConstPtr(ptr239), descr=) - i242 = int_sub(i240, 1) - setfield_gc(ConstPtr(ptr243), i242, descr=) - setarrayitem_gc(p237, 0, p223, descr=) - i246 = int_le(i242, 0) - guard_false(i246, descr=) - i247 = arraylen_gc(p66, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, p11, i159, i161, p17, p223, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, p66, p64, descr=TargetToken(108721024)) + p188 = getfield_gc_r(p5, descr=) + setarrayitem_gc(p186, 0, p171, descr=) + guard_value(p188, ConstPtr(ptr190), descr=) + i192 = getfield_gc_i(ConstPtr(ptr191), descr=) + i194 = int_sub(i192, 1) + setfield_gc(ConstPtr(ptr195), i194, descr=) + i197 = int_le(i194, 0) + guard_false(i197, descr=) + i198 = arraylen_gc(p46, descr=) + jump(p0, p1, p3, p4, p5, p10, i125, i127, p16, p171, i32, p46, p44, descr=TargetToken(94560264933152)) """) def test_dnu(self, spy, tmpdir): @@ -994,24 +888,26 @@ def test_dnu(self, spy, tmpdir): def test_oam(self, spy, tmpdir): traces = self.run(spy, tmpdir, """ | c i | - Object + Smalltalk at: #MyOaM ifAbsent: [ + Object subclass: #MyOaM instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Test'. - c := Smalltalk at: #MyOaM. - c compile: 'run: aSelector with: someArgs in: aReceiver - ^ {aSelector. someArgs. aReceiver}' classified: 'none' withStamp: nil notifying: nil logSource: false. + c := Smalltalk at: #MyOaM. + c compile: 'run: aSelector with: someArgs in: aReceiver + ^ {aSelector. someArgs. aReceiver}' classified: 'none' withStamp: nil notifying: nil logSource: false. - Object + Object subclass: #MyA instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Test'. + c := Smalltalk at: #MyA. + c methodDict at: #oam put: (Smalltalk at: #MyOaM) new]. c := Smalltalk at: #MyA. - c methodDict at: #oam put: (Smalltalk at: #MyOaM) new. i := c new. 1 to: 100000 do: [:ignored | i oam ] """) @@ -1104,36 +1000,35 @@ def test_identity_hash_fresh_object(self, spy, tmpdir): """) # important: there shouldn't be a setfield inst_hash or an abort, just a getfield for the hash value and a cond_call self.assert_matches(traces[-1].loop, """ - guard_not_invalidated(descr=) - i103 = int_le(i94, 10000) - guard_true(i103, descr=) - p104 = force_token() - enter_portal_frame(1, 0) - i107 = int_add_ovf(i94, i62) - guard_no_overflow(descr=) - i109 = int_sub(i107, 1) - i110 = int_gt(i109, i66) - guard_false(i110, descr=) - i112 = int_sub(i109, 1) - i113 = uint_lt(i112, i78) - guard_true(i113, descr=) - i115 = int_lt(i112, 0) - guard_false(i115, descr=) - p116 = getarrayitem_gc_r(p77, i112, descr=) - guard_nonnull_class(p116, ConstClass(W_FixedPointersObject), descr=) + guard_not_invalidated(descr=) + i85 = int_le(i76, 10000) + guard_true(i85, descr=) + p86 = force_token() + enter_portal_frame(1, 106244) + i89 = int_add_ovf(i76, i50) + guard_no_overflow(descr=) + i91 = int_sub(i89, 1) + i92 = int_gt(i91, i54) + guard_false(i92, descr=) + i94 = int_sub(i91, 1) + i95 = uint_lt(i94, i63) + guard_true(i95, descr=) + i97 = int_lt(i94, 0) + guard_false(i97, descr=) + p98 = getarrayitem_gc_r(p62, i94, descr=) + guard_nonnull_class(p98, ConstClass(W_FixedPointersObject), descr=) leave_portal_frame(1) - p119 = getfield_gc_r(p116, descr=) - guard_value(p119, ConstPtr(ptr120), descr=) - i121 = getfield_gc_i(p116, descr=) - i123 = cond_call_value_i(i121, ConstClass(calculate_and_cache), p116, descr=) - guard_no_exception(descr=) - guard_not_invalidated(descr=) - i125 = int_add(i94, 1) - i127 = int_sub(i98, 1) - setfield_gc(ConstPtr(ptr128), i127, descr=) - i130 = int_le(i127, 0) - guard_false(i130, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, p11, p13, p15, p17, i125, p25, p27, p29, p31, p33, p35, p37, p39, p41, i62, i66, p68, i78, p77, i127, descr=TargetToken(78064480)) + p101 = getfield_gc_r(p98, descr=) + guard_value(p101, ConstPtr(ptr102), descr=) + i103 = getfield_gc_i(p98, descr=) + i105 = cond_call_value_i(i103, ConstClass(calculate_and_cache), p98, descr=) + guard_no_exception(descr=) + i107 = int_add(i76, 1) + i109 = int_sub(i80, 1) + setfield_gc(ConstPtr(ptr110), i109, descr=) + i112 = int_le(i109, 0) + guard_false(i112, descr=) + jump(p0, p1, p3, p4, p5, p10, p12, p14, p16, i107, p24, p26, p28, p30, p35, i50, i54, p56, i63, p62, i109, descr=TargetToken(94409022230064)) """) def test_nested_closure_loop_call(self, spy, tmpdir): @@ -1145,7 +1040,7 @@ def test_nested_closure_loop_call(self, spy, tmpdir): ]. """) # important: there should be two loops, with the outer calling the inner, and in between an entry bridge to the inner loop abs call - self.assert_matches(traces[-3].loop, """ + self.assert_matches(traces[1].loop, """ guard_not_invalidated(descr=) i99 = int_lt(i59, i49) guard_true(i99, descr=) @@ -1168,136 +1063,139 @@ def test_nested_closure_loop_call(self, spy, tmpdir): guard_false(i118, descr=) jump(p0, p1, i2, p4, p5, p6, p8, p11, i101, i103, p17, p25, p27, p29, p31, p33, p35, p37, p39, p41, p43, i49, i55, i54, i115, descr=TargetToken(108261712)) """) - assert len(traces[-2].setup) > 0 - self.assert_matches(traces[-1].loop, """ - guard_not_invalidated(descr=) - i184 = int_le(i175, 2000) - guard_true(i184, descr=) - p186 = getfield_gc_r(ConstPtr(ptr185), descr=) - guard_value(p186, ConstPtr(ptr187), descr=) + assert len(traces[5].setup) > 0 + self.assert_matches(traces[6].loop, """ + guard_not_invalidated(descr=) + i162 = int_le(i153, 10000) + guard_true(i162, descr=) + p163 = force_token() + enter_portal_frame(1, 99400) + p167 = getfield_gc_r(ConstPtr(ptr166), descr=) + guard_nonnull_class(p167, ConstClass(W_FixedPointersObject), descr=) + p169 = getfield_gc_r(p167, descr=) + guard_value(p169, ConstPtr(ptr170), descr=) + p171 = getfield_gc_r(p169, descr=) + guard_value(p171, ConstPtr(ptr172), descr=) + p173 = force_token() + enter_portal_frame(1, 115312) + p176 = force_token() + enter_portal_frame(1, 17536) + leave_portal_frame(1) + leave_portal_frame(1) + leave_portal_frame(1) + p182 = getfield_gc_r(p1, descr=) + guard_class(p182, ConstClass(ContextPartShadow), descr=) + i184 = ptr_eq(p182, p0) + guard_true(i184, descr=) + p185 = force_token() + enter_portal_frame(1, 17620) p188 = force_token() - enter_portal_frame(1, 0) - p192 = getfield_gc_r(ConstPtr(ptr191), descr=) - guard_value(p192, ConstPtr(ptr193), descr=) - p195 = getfield_gc_r(ConstPtr(ptr194), descr=) - guard_nonnull_class(p195, ConstClass(W_FixedPointersObject), descr=) - p197 = getfield_gc_r(p195, descr=) - guard_value(p197, ConstPtr(ptr198), descr=) - p199 = getfield_gc_r(p197, descr=) - guard_value(p199, ConstPtr(ptr200), descr=) - p202 = getfield_gc_r(ConstPtr(ptr201), descr=) - guard_value(p202, ConstPtr(ptr203), descr=) - p204 = force_token() - enter_portal_frame(1, 0) - p208 = getfield_gc_r(ConstPtr(ptr207), descr=) - guard_value(p208, ConstPtr(ptr209), descr=) - p211 = getfield_gc_r(ConstPtr(ptr210), descr=) - guard_value(p211, ConstPtr(ptr212), descr=) - p213 = force_token() - enter_portal_frame(1, 0) + enter_portal_frame(1, 17584) leave_portal_frame(1) + p192 = force_token() + enter_portal_frame(1, 132700) + p195 = force_token() + enter_portal_frame(1, 99628) leave_portal_frame(1) leave_portal_frame(1) - p219 = getfield_gc_r(p1, descr=) - guard_nonnull_class(p219, ConstClass(ContextPartShadow), descr=) - i221 = ptr_eq(p219, p0) - guard_true(i221, descr=) - p222 = force_token() - enter_portal_frame(1, 0) - p225 = force_token() - enter_portal_frame(1, 0) + p200 = force_token() + enter_portal_frame(1, 132700) + p203 = force_token() + enter_portal_frame(1, 99628) leave_portal_frame(1) - p229 = force_token() - enter_portal_frame(1, 0) - p232 = force_token() - enter_portal_frame(1, 0) + leave_portal_frame(1) + p208 = force_token() + enter_portal_frame(1, 132700) + p211 = force_token() + enter_portal_frame(1, 99628) leave_portal_frame(1) leave_portal_frame(1) - i238 = int_sub(i179, 1) - setfield_gc(ConstPtr(ptr239), i238, descr=) - i241 = int_le(i238, 0) - guard_false(i241, descr=) - p242 = force_token() - p243 = new_with_vtable(descr=) - p244 = new_with_vtable(descr=) - setfield_gc(p244, p222, descr=) - setfield_gc(p244, ConstPtr(null), descr=) - p246 = new_array_clear(17, descr=) - p247 = new_with_vtable(descr=) - setfield_gc(p247, 0, descr=) - setfield_gc(p247, ConstPtr(ptr249), descr=) - setfield_gc(p247, 268435503, descr=) - setfield_gc(p247, ConstPtr(ptr251), descr=) - setfield_gc(p247, p1, descr=) - setfield_gc(p247, p6, descr=) - setfield_gc(p247, ConstPtr(ptr252), descr=) - setarrayitem_gc(p246, 0, p247, descr=) - p254 = new_with_vtable(descr=) - setfield_gc(p254, 1, descr=) - setarrayitem_gc(p246, 1, p254, descr=) - p257 = new_with_vtable(descr=) - setfield_gc(p257, 1, descr=) - setarrayitem_gc(p246, 2, p257, descr=) - p260 = new_with_vtable(descr=) - setfield_gc(p260, 1000, descr=) - setarrayitem_gc(p246, 3, p260, descr=) - p263 = new_with_vtable(descr=) - setfield_gc(p263, 1, descr=) - setarrayitem_gc(p246, 4, p263, descr=) - setarrayitem_gc(p246, 5, ConstPtr(ptr267), descr=) - setarrayitem_gc(p246, 6, ConstPtr(ptr269), descr=) - p270 = new_with_vtable(descr=) - setfield_gc(p270, 0, descr=) - setfield_gc(p270, ConstPtr(null), descr=) - setfield_gc(p270, ConstPtr(ptr273), descr=) - setfield_gc(p270, ConstPtr(null), descr=) - setfield_gc(p270, ConstPtr(null), descr=) - setfield_gc(p270, 1, descr=) - setfield_gc(p270, 1000, descr=) - setfield_gc(p270, 1, descr=) - setfield_gc(p270, ConstPtr(null), descr=) - setfield_gc(p270, 3, descr=) - setfield_gc(p243, ConstPtr(ptr281), descr=) - setfield_gc(p0, p242, descr=) - setfield_gc(p243, p244, descr=) - setfield_gc(p243, 1090519045, descr=) - setfield_gc(p243, p246, descr=) - setfield_gc(p243, ConstPtr(ptr283), descr=) - setfield_gc(p243, p270, descr=) - setfield_gc(p243, ConstPtr(null), descr=) - setfield_gc(p243, 23, descr=) - setfield_gc(p243, ConstPtr(null), descr=) - setfield_gc(p243, ConstPtr(null), descr=) - call_assembler_n(p243, descr=) - guard_not_forced(descr=) - keepalive(p243) - p289 = guard_exception(13757968, descr=) - i290 = ptr_eq(p243, p0) - guard_false(i290, descr=) - p291 = getfield_gc_r(p243, descr=) - i293 = ptr_ne(p291, ConstPtr(null)) - cond_call(i293, 6399472, p243, descr=) - i295 = getfield_gc_i(p243, descr=) - guard_value(i295, 1090519067, descr=) - guard_not_invalidated(descr=) - p297 = getfield_gc_r(p243, descr=) - guard_isnull(p297, descr=) - p298 = getfield_gc_r(p243, descr=) - guard_value(p298, ConstPtr(ptr299), descr=) - setfield_gc(p243, ConstPtr(null), descr=) - setfield_gc(p243, 1094713343, descr=) - guard_class(p289, 13757968, descr=) - p303 = getfield_gc_r(p289, descr=) - setfield_gc(p243, 20971519, descr=) - setfield_gc(p244, ConstPtr(null), descr=) - guard_nonnull(p303, descr=) - i306 = int_add(i175, 1) - i308 = getfield_gc_i(ConstPtr(ptr307), descr=) - i310 = int_sub(i308, 1) - setfield_gc(ConstPtr(ptr311), i310, descr=) - i313 = int_le(i310, 0) - guard_false(i313, descr=) - jump(p0, p1, i2, p4, p5, p6, p8, i306, p17, p19, p21, p23, p25, p27, p29, p31, p33, p35, p37, p39, p41, i310, descr=TargetToken(104224336)) + i217 = int_sub(i157, 1) + setfield_gc(ConstPtr(ptr218), i217, descr=) + i220 = int_le(i217, 0) + guard_false(i220, descr=) + p221 = force_token() + p222 = new_with_vtable(descr=) + p223 = new_with_vtable(descr=) + setfield_gc(p223, p185, descr=) + setfield_gc(p223, ConstPtr(null), descr=) + p225 = new_array_clear(7, descr=) + p226 = new_with_vtable(descr=) + setfield_gc(p226, 0, descr=) + setfield_gc(p226, ConstPtr(ptr228), descr=) + setfield_gc(p226, 268435503, descr=) + setfield_gc(p226, ConstPtr(ptr230), descr=) + setfield_gc(p226, p1, descr=) + setfield_gc(p226, p5, descr=) + setfield_gc(p226, ConstPtr(ptr231), descr=) + setarrayitem_gc(p225, 0, p226, descr=) + p233 = new_with_vtable(descr=) + setfield_gc(p233, 3, descr=) + setarrayitem_gc(p225, 1, p233, descr=) + p236 = new_with_vtable(descr=) + setfield_gc(p236, 3, descr=) + setarrayitem_gc(p225, 2, p236, descr=) + p239 = new_with_vtable(descr=) + setfield_gc(p239, 1000, descr=) + setarrayitem_gc(p225, 3, p239, descr=) + p242 = new_with_vtable(descr=) + setfield_gc(p242, 3, descr=) + setarrayitem_gc(p225, 4, p242, descr=) + setarrayitem_gc(p225, 5, ConstPtr(ptr246), descr=) + setarrayitem_gc(p225, 6, ConstPtr(ptr248), descr=) + p249 = new_with_vtable(descr=) + setfield_gc(p249, 0, descr=) + setfield_gc(p249, ConstPtr(null), descr=) + setfield_gc(p249, ConstPtr(ptr252), descr=) + setfield_gc(p249, ConstPtr(null), descr=) + setfield_gc(p249, ConstPtr(null), descr=) + setfield_gc(p249, 1, descr=) + setfield_gc(p249, 1000, descr=) + setfield_gc(p249, 1, descr=) + setfield_gc(p249, ConstPtr(null), descr=) + setfield_gc(p249, 3, descr=) + setfield_gc(p222, ConstPtr(ptr260), descr=) + setfield_gc(p0, p221, descr=) + setfield_gc(p222, p223, descr=) + setfield_gc(p222, 1090519045, descr=) + setfield_gc(p222, p225, descr=) + setfield_gc(p222, ConstPtr(ptr262), descr=) + setfield_gc(p222, p249, descr=) + setfield_gc(p222, ConstPtr(null), descr=) + setfield_gc(p222, ConstPtr(null), descr=) + setfield_gc(p222, ConstPtr(ptr265), descr=) + call_assembler_n(p222, descr=) + guard_not_forced(descr=) + keepalive(p222) + p267 = guard_exception(94582450243584, descr=) + i268 = ptr_eq(p222, p0) + guard_false(i268, descr=) + p269 = getfield_gc_r(p222, descr=) + i271 = ptr_ne(p269, ConstPtr(null)) + cond_call(i271, 94582442480048, p222, descr=) + i273 = getfield_gc_i(p222, descr=) + guard_value(i273, 1090519067, descr=) + guard_not_invalidated(descr=) + p275 = getfield_gc_r(p222, descr=) + guard_isnull(p275, descr=) + p276 = getfield_gc_r(p222, descr=) + guard_value(p276, ConstPtr(ptr277), descr=) + setfield_gc(p222, ConstPtr(null), descr=) + setfield_gc(p222, 1094713343, descr=) + guard_class(p267, 94582450243584, descr=) + p281 = getfield_gc_r(p267, descr=) + setfield_gc(p222, 20971519, descr=) + setfield_gc(p223, ConstPtr(null), descr=) + guard_nonnull(p281, descr=) + p283 = getfield_gc_r(p5, descr=) + guard_value(p283, ConstPtr(ptr284), descr=) + i286 = int_add(i153, 1) + i288 = getfield_gc_i(ConstPtr(ptr287), descr=) + i290 = int_sub(i288, 1) + setfield_gc(ConstPtr(ptr291), i290, descr=) + i293 = int_le(i290, 0) + guard_false(i293, descr=) + jump(p0, p1, p3, p4, p5, i286, i290, descr=TargetToken(94582475493232)) """) def test_object_access_with_integer(self, spy, tmpdir): @@ -1307,7 +1205,7 @@ def test_object_access_with_integer(self, spy, tmpdir): o add: 'foo'; add: 'bar'. 1 to: 10000 do: [:i | o first]. """) - self.assert_matches(traces[0].loop, """ + self.assert_matches(traces[2].loop, """ guard_not_invalidated(descr=) i96 = int_le(i87, 10000) guard_true(i96, descr=) From 7526c30e05370ee8e09b328ea578502c2c823997 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Thu, 23 Mar 2017 14:40:27 +0100 Subject: [PATCH 51/52] only interrupt on keyup --- rsqueakvm/display.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsqueakvm/display.py b/rsqueakvm/display.py index bfcf38ca..a2c889f5 100644 --- a/rsqueakvm/display.py +++ b/rsqueakvm/display.py @@ -726,7 +726,7 @@ def cursor_words_to_bytes(self, bytenum, words): separate_module_sources=[""" int InterruptEventFilter(void* userdata, SDL_Event *event) { int interrupt_key = 15 << 8; - if (event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) { + if (event->type == SDL_KEYUP) { if (((SDL_KeyboardEvent*)event)->keysym.sym == SDLK_PERIOD) { if ((((SDL_KeyboardEvent*)event)->keysym.mod & (KMOD_ALT|KMOD_GUI)) != 0) { if (event->type == SDL_KEYUP) { // only keyup generates the interrupt From bc0162ccb8522a32a11eaee5ce0837b67577412e Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Thu, 23 Mar 2017 14:56:45 +0100 Subject: [PATCH 52/52] address code review concerns --- rsqueakvm/interpreter.py | 10 ++++++++++ rsqueakvm/main.py | 4 ++++ rsqueakvm/model/compiled_methods.py | 11 +++++------ rsqueakvm/model/pointers.py | 11 ++++++++--- rsqueakvm/primitives/__init__.py | 7 +++---- rsqueakvm/storage_contexts.py | 16 ++++++++-------- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/rsqueakvm/interpreter.py b/rsqueakvm/interpreter.py index cc8b6826..0ec4e8bf 100644 --- a/rsqueakvm/interpreter.py +++ b/rsqueakvm/interpreter.py @@ -130,6 +130,16 @@ class Optargs(object): _attrs_ = ["max_squeak_unroll_count", "squeak_unroll_trace_limit"] _immutable_fields_ = ["max_squeak_unroll_count", "squeak_unroll_trace_limit"] def __init__(self): + """ + - max_squeak_unroll_count: How many times to unroll a loop before + JIT'ing. The default (2) was chosen because it "felt right". + - squeak_unroll_trace_limit: After how many operations we should never + unroll. This tries to avoid trace_too_long aborts, and should kind of + be synchronized with the default trace_limit. Imagine you have a long + loop, and you unroll it twice, then we're at 86k ops with the default + of 32k ops. This is alreay quite a long trace, we don't want to blow + it up too much. + """ self.max_squeak_unroll_count = 2 self.squeak_unroll_trace_limit = 32000 diff --git a/rsqueakvm/main.py b/rsqueakvm/main.py index 1eee5c64..b1524d0f 100644 --- a/rsqueakvm/main.py +++ b/rsqueakvm/main.py @@ -68,6 +68,10 @@ def _usage(argv): headless mode, result printed. -rr - Code will be compiled and executed in headless mode, twice, the second result printed. + This is a workaround for making jittests and + benchmarking easier, because do-its are not JIT'ed + right now, due to the dynamic frame size + calculation. -m|--method - Selector will be sent to nil in headless mode, result printed. diff --git a/rsqueakvm/model/compiled_methods.py b/rsqueakvm/model/compiled_methods.py index 8d302cda..5134b340 100644 --- a/rsqueakvm/model/compiled_methods.py +++ b/rsqueakvm/model/compiled_methods.py @@ -392,8 +392,7 @@ def bytecode_string(self, markBytecode=0): from rsqueakvm.interpreter_bytecodes import BYTECODE_TABLE retval = "Bytecode:------------" j = 0 - while j < len(self.bytes): - i = self.bytes[idx] + for i in self.bytes: retval += '\n' retval += '->' if j + 1 is markBytecode else ' ' retval += ('%0.2i: 0x%0.2x(%0.3i) ' % (j + 1, ord(i), ord(i))) + BYTECODE_TABLE[ord(i)].__name__ @@ -408,10 +407,10 @@ def as_string(self, markBytecode=0): def guess_containing_classname(self): w_class = self.compiled_in() - if w_class: - # Not pretty to steal the space from another object. - return w_class.as_class_get_shadow(w_class.space()).getname() - return "? (no compiledin-info)" + if w_class is None: + return "? (no compiledin-info)" + # Not pretty to steal the space from another object. + return w_class.as_class_get_shadow(w_class.space()).getname() def get_identifier_string(self): return "%s >> #%s" % (self.guess_containing_classname(), self.lookup_selector) diff --git a/rsqueakvm/model/pointers.py b/rsqueakvm/model/pointers.py index 533621d7..814387d6 100644 --- a/rsqueakvm/model/pointers.py +++ b/rsqueakvm/model/pointers.py @@ -234,9 +234,14 @@ def clone(self, space): return w_result -# when changing this constant, also change the read and __write__ methods in -# IntMapStorageNode in storage.py (or generalize those methods to compile with -# getattrs) +# When changing these constants, also change the read and __write__ methods in +# IntMapStorageNode in storage.py (or generalize those methods, while you're at +# it). The numbers for these fields where chosen semi-randomly. Ranges (which +# are used in iterations when doing ((1 to: 2) do: []) have 3 integer fields, so +# three seemed like a good number here. Collections often have two integer +# fields and one or two pointer objects. Rectangles have two Points, Points two +# integer fields. I didn't want to go too large to not waste so much +# memory. More measurements would probably be required. _NUMBER_OF_INT_FIELDS = 3 _NUMBER_OF_INLINE_FIELDS = 2 class W_FixedPointersObject(W_PointersObject): diff --git a/rsqueakvm/primitives/__init__.py b/rsqueakvm/primitives/__init__.py index 970a8334..99e24734 100644 --- a/rsqueakvm/primitives/__init__.py +++ b/rsqueakvm/primitives/__init__.py @@ -20,10 +20,9 @@ def assert_class(interp, w_obj, w_class): def assert_valid_index(space, n0, w_obj): if not int_between(0, n0, w_obj.varsize()): raise PrimitiveFailedError() - # return the index, since from here on the annotator knows that - # n0 cannot be negative - assert n0 >= 0 - return n0 + else: # n0 cannot be negative due to the if condition. help the annotator. + assert n0 >= 0 + return n0 def assert_valid_inst_index(space, n0, w_obj): if not int_between(0, n0, w_obj.size()): diff --git a/rsqueakvm/storage_contexts.py b/rsqueakvm/storage_contexts.py index 2d57bba9..759576cc 100644 --- a/rsqueakvm/storage_contexts.py +++ b/rsqueakvm/storage_contexts.py @@ -168,12 +168,13 @@ def is_privileged_index(self, n0): def get_extra_data(self): extra_data = self.extra_data_or_blockmethod - if extra_data is None or not isinstance(extra_data, ExtraContextAttributes): + if isinstance(extra_data, ExtraContextAttributes): + return extra_data + else: new_extra_data = ExtraContextAttributes() new_extra_data.blockmethod = extra_data - extra_data = self.extra_data_or_blockmethod = new_extra_data - assert isinstance(extra_data, ExtraContextAttributes) - return extra_data + self.extra_data_or_blockmethod = new_extra_data + return new_extra_data def extra_data(self): extra_data = self.extra_data_or_blockmethod @@ -885,10 +886,9 @@ def blockmethod(self): w_bm = self.extra_data_or_blockmethod if isinstance(w_bm, ExtraContextAttributes): w_bm = w_bm.blockmethod - if w_bm is None: - return None - assert isinstance(w_bm, W_CompiledMethod) - return w_bm + if isinstance(w_bm, W_CompiledMethod): + return w_bm + return None def set_blockmethod(self, w_obj): assert isinstance(w_obj, W_CompiledMethod)