From 227e7deb1b52eb58c94236a28ff48e7dafa4996c Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 3 Aug 2021 16:58:33 +0100 Subject: [PATCH 01/16] Added inlining of #to:do: messages and handling for block arguments during inling Block arguments are turned into locals during inlining. This might not be perfect for the general case, but works well for to:do:. This commit also adapts the variable write nodes so that I can pass in the value directly, and don't have to have a child node. This requires some adaptation in the node traversal methods in abstract_node.py to handle the case that child nodes may be legitimately None. This also moves the lexical scope into the abstract method. Drawback is that also trivial methods have the extra field. Signed-off-by: Stefan Marr --- src/rtruffle/abstract_node.py | 8 +- src/som/compiler/ast/parser.py | 19 +++++ src/som/compiler/ast/variable.py | 3 +- .../compiler/bc/method_generation_context.py | 4 +- src/som/compiler/method_generation_context.py | 15 ++-- .../ast/nodes/specialized/literal_to_do.py | 84 +++++++++++++++++++ .../interpreter/ast/nodes/variable_node.py | 21 +++-- src/som/vmobjects/method.py | 7 +- src/som/vmobjects/method_ast.py | 5 +- src/som/vmobjects/method_bc.py | 5 +- src/som/vmobjects/method_trivial.py | 8 +- tests/test_ast_inlining.py | 15 +++- 12 files changed, 161 insertions(+), 33 deletions(-) create mode 100644 src/som/interpreter/ast/nodes/specialized/literal_to_do.py diff --git a/src/rtruffle/abstract_node.py b/src/rtruffle/abstract_node.py index 0ef3e080..0827fea3 100644 --- a/src/rtruffle/abstract_node.py +++ b/src/rtruffle/abstract_node.py @@ -62,7 +62,8 @@ def _adapt_after_inlining(node, mgenc): n.adapt_after_inlining(mgenc) else: current = getattr(node, child_slot) - current.adapt_after_inlining(mgenc) + if current is not None: + current.adapt_after_inlining(mgenc) node.handle_inlining(mgenc) cls.adapt_after_inlining = _adapt_after_inlining @@ -83,7 +84,10 @@ def _adapt_after_outer_inlined(node, removed_ctx_level, mgenc_with_inlined): ) else: current = getattr(node, child_slot) - current.adapt_after_outer_inlined(removed_ctx_level, mgenc_with_inlined) + if current is not None: + current.adapt_after_outer_inlined( + removed_ctx_level, mgenc_with_inlined + ) node.handle_outer_inlined(removed_ctx_level, mgenc_with_inlined) cls.adapt_after_outer_inlined = _adapt_after_outer_inlined diff --git a/src/som/compiler/ast/parser.py b/src/som/compiler/ast/parser.py index 5950bd5f..97e4c9ab 100644 --- a/src/som/compiler/ast/parser.py +++ b/src/som/compiler/ast/parser.py @@ -25,6 +25,7 @@ IfInlinedNode, IfElseInlinedNode, ) +from som.interpreter.ast.nodes.specialized.literal_to_do import ToDoInlined from som.interpreter.ast.nodes.specialized.literal_while import WhileInlinedNode from som.vm.symbols import symbol_for @@ -291,6 +292,18 @@ def _try_inlining_or(receiver, arg_expr, source, mgenc): arg_body = arg_expr.get_method().inline(mgenc) return OrInlinedNode(receiver, arg_body, source) + @staticmethod + def _try_inlining_to_do(receiver, arguments, source, mgenc): + if not isinstance(arguments[1], BlockNode): + return None + + block_method = arguments[1].get_method() + do_expr = block_method.inline(mgenc) + idx_arg = block_method.get_argument(1, 0) + return ToDoInlined( + receiver, arguments[0], do_expr, mgenc.get_inlined_local(idx_arg, 0), source + ) + def _keyword_message(self, mgenc, receiver): is_super_send = self._super_send @@ -358,6 +371,12 @@ def _keyword_message(self, mgenc, receiver): ) if inlined is not None: return inlined + elif keyword == "to:do:": + inlined = self._try_inlining_to_do( + receiver, arguments, source, mgenc + ) + if inlined is not None: + return inlined selector = symbol_for(keyword) diff --git a/src/som/compiler/ast/variable.py b/src/som/compiler/ast/variable.py index e54dd5a3..28040b35 100644 --- a/src/som/compiler/ast/variable.py +++ b/src/som/compiler/ast/variable.py @@ -147,7 +147,8 @@ def get_initialized_read_node(self, context_level, source_section): def copy_for_inlining(self, idx): if self._name == "$blockSelf": return None - return Argument(self._name, idx, self.source) + assert self._name != "self" + return Local(self._name, idx, self.source) def __str__(self): return "Argument(" + self._name + " idx: " + str(self.idx) + ")" diff --git a/src/som/compiler/bc/method_generation_context.py b/src/som/compiler/bc/method_generation_context.py index 4114d76c..c0bf3fd3 100644 --- a/src/som/compiler/bc/method_generation_context.py +++ b/src/som/compiler/bc/method_generation_context.py @@ -96,8 +96,8 @@ def add_local(self, local_name, source, parser): self._local_list.append(local) return local - def inline_locals(self, local_vars): - fresh_copies = MethodGenerationContextBase.inline_locals(self, local_vars) + def inline_as_locals(self, variables): + fresh_copies = MethodGenerationContextBase.inline_as_locals(self, variables) if fresh_copies: self._local_list.extend(fresh_copies) return fresh_copies diff --git a/src/som/compiler/method_generation_context.py b/src/som/compiler/method_generation_context.py index e8459e6e..e1f5033a 100644 --- a/src/som/compiler/method_generation_context.py +++ b/src/som/compiler/method_generation_context.py @@ -83,13 +83,13 @@ def add_local(self, local_name, source, parser): self._locals[local_name] = result return result - def inline_locals(self, local_vars): + def inline_as_locals(self, variables): fresh_copies = [] - for local in local_vars: - fresh_copy = local.copy_for_inlining(len(self._locals)) + for var in variables: + fresh_copy = var.copy_for_inlining(len(self._locals)) if fresh_copy: # fresh_copy can be None, because we don't need the $blockSelf - name = local.get_qualified_name() + name = var.get_qualified_name() assert name not in self._locals self._locals[name] = fresh_copy fresh_copies.append(fresh_copy) @@ -212,10 +212,13 @@ def set_block_signature(self, line, column): self.signature = symbol_for(block_sig) def merge_into_scope(self, scope_to_be_inlined): - assert len(scope_to_be_inlined.arguments) == 1 + arg_vars = scope_to_be_inlined.arguments + if len(arg_vars) > 1: + self.inline_as_locals(arg_vars) + local_vars = scope_to_be_inlined.locals if local_vars: - self.inline_locals(local_vars) + self.inline_as_locals(local_vars) def _strip_colons_and_source_location(method_name): diff --git a/src/som/interpreter/ast/nodes/specialized/literal_to_do.py b/src/som/interpreter/ast/nodes/specialized/literal_to_do.py new file mode 100644 index 00000000..49f55865 --- /dev/null +++ b/src/som/interpreter/ast/nodes/specialized/literal_to_do.py @@ -0,0 +1,84 @@ +from rlib.jit import JitDriver + +from som.interpreter.ast.nodes.expression_node import ExpressionNode +from som.vmobjects.double import Double +from som.vmobjects.integer import Integer + + +class ToDoInlined(ExpressionNode): + _immutable_fields_ = [ + "_from_expr?", + "_to_expr?", + "_do_expr?", + "_idx_var", + "_idx_write?", + ] + _child_nodes_ = ["_from_expr", "_to_expr", "_do_expr", "_idx_write"] + + def __init__(self, from_expr, to_expr, do_expr, idx_var, source_section): + ExpressionNode.__init__(self, source_section) + self._from_expr = self.adopt_child(from_expr) + self._to_expr = self.adopt_child(to_expr) + self._do_expr = self.adopt_child(do_expr) + self._idx_var = idx_var + self._idx_write = self.adopt_child(idx_var.get_write_node(0, None)) + + def execute(self, frame): + start = self._from_expr.execute(frame) + assert isinstance(start, Integer) + + end = self._to_expr.execute(frame) + if isinstance(end, Integer): + _to_do_int( + frame, + start.get_embedded_integer(), + end.get_embedded_integer(), + self._idx_write, + self._do_expr, + ) + return start + + assert isinstance(end, Double) + _to_do_dbl( + frame, + start.get_embedded_integer(), + end.get_embedded_double(), + self._idx_write, + self._do_expr, + ) + return start + + +def get_printable_location(do_expr, idx_write): # pylint: disable=unused-argument + assert isinstance(do_expr, ExpressionNode) + return "#to:do: %s" % str(do_expr.source_section) + + +int_driver = JitDriver( + greens=["do_expr", "idx_write"], + reds="auto", + is_recursive=True, + get_printable_location=get_printable_location, +) + + +dbl_driver = JitDriver( + greens=["do_expr", "idx_write"], + reds="auto", + is_recursive=True, + get_printable_location=get_printable_location, +) + + +def _to_do_int(frame, start, end, idx_write, do_expr): + for i in range(start, end + 1): + int_driver.jit_merge_point(do_expr=do_expr, idx_write=idx_write) + idx_write.write_value(frame, Integer(i)) + do_expr.execute(frame) + + +def _to_do_dbl(frame, start, end, idx_write, do_expr): + for i in range(start, end + 1): + dbl_driver.jit_merge_point(do_expr=do_expr, idx_write=idx_write) + idx_write.write_value(frame, Integer(i)) + do_expr.execute(frame) diff --git a/src/som/interpreter/ast/nodes/variable_node.py b/src/som/interpreter/ast/nodes/variable_node.py index 28efe426..dcadc327 100644 --- a/src/som/interpreter/ast/nodes/variable_node.py +++ b/src/som/interpreter/ast/nodes/variable_node.py @@ -29,12 +29,6 @@ def _specialize(self): def handle_inlining(self, mgenc): if self._context_level == 0: - from som.compiler.ast.variable import Local - - # we got inlined - assert isinstance( - self.var, Local - ), "We are not currently inlining any blocks with arguments" self.var = mgenc.get_inlined_local(self.var, 0) else: self._context_level -= 1 @@ -73,6 +67,9 @@ def __init__(self, var, context_level, value_expr, source_section): def execute(self, frame): return self._specialize().execute(frame) + def write_value(self, frame, value): + return self._specialize().write_value(frame, value) + def _specialize(self): return self.replace( self._var.get_initialized_write_node( @@ -161,6 +158,10 @@ def execute(self, frame): self.determine_block(frame).set_outer(self._frame_idx, value) return value + def write_value(self, frame, value): + self.determine_block(frame).set_outer(self._frame_idx, value) + return value + class _LocalVariableNode(ExpressionNode): _immutable_fields_ = ["_frame_idx"] @@ -191,6 +192,10 @@ def execute(self, frame): write_inner(frame, self._frame_idx, val) return val + def write_value(self, frame, value): + write_inner(frame, self._frame_idx, value) + return value + class LocalFrameVarReadNode(_LocalVariableNode): def execute(self, frame): @@ -205,3 +210,7 @@ def execute(self, frame): val = self._expr.execute(frame) write_frame(frame, self._frame_idx, val) return val + + def write_value(self, frame, value): + write_frame(frame, self._frame_idx, value) + return value diff --git a/src/som/vmobjects/method.py b/src/som/vmobjects/method.py index 1d39c237..f32dafe4 100644 --- a/src/som/vmobjects/method.py +++ b/src/som/vmobjects/method.py @@ -6,12 +6,14 @@ class AbstractMethod(AbstractObject): _immutable_fields_ = [ "_signature", "_holder", + "_lexical_scope", ] - def __init__(self, signature): + def __init__(self, signature, lexical_scope): AbstractObject.__init__(self) self._signature = signature self._holder = None + self._lexical_scope = lexical_scope @staticmethod def is_primitive(): @@ -22,6 +24,9 @@ def is_invokable(): """We use this method to identify methods and primitives""" return True + def get_argument(self, idx, ctx_level): + return self._lexical_scope.get_argument(idx, ctx_level) + def get_holder(self): return self._holder diff --git a/src/som/vmobjects/method_ast.py b/src/som/vmobjects/method_ast.py index 5f8493bb..a4d3b456 100644 --- a/src/som/vmobjects/method_ast.py +++ b/src/som/vmobjects/method_ast.py @@ -80,7 +80,6 @@ class AstMethod(AbstractMethod): "_size_inner", "_embedded_block_methods", "source_section", - "_lexical_scope", ] def __init__( @@ -94,7 +93,7 @@ def __init__( source_section, lexical_scope, ): - AbstractMethod.__init__(self, signature) + AbstractMethod.__init__(self, signature, lexical_scope) assert isinstance(arg_inner_access, list) make_sure_not_resized(arg_inner_access) @@ -108,8 +107,6 @@ def __init__( self.invokable = _Invokable(expr_or_sequence) - self._lexical_scope = lexical_scope - def set_holder(self, value): self._holder = value for method in self._embedded_block_methods: diff --git a/src/som/vmobjects/method_bc.py b/src/som/vmobjects/method_bc.py index 6af8c1f3..da18193d 100644 --- a/src/som/vmobjects/method_bc.py +++ b/src/som/vmobjects/method_bc.py @@ -55,7 +55,6 @@ class BcAbstractMethod(AbstractMethod): "_arg_inner_access[*]", "_size_frame", "_size_inner", - "_lexical_scope", "_inlined_loops[*]", ] @@ -72,7 +71,7 @@ def __init__( lexical_scope, inlined_loops, ): - AbstractMethod.__init__(self, signature) + AbstractMethod.__init__(self, signature, lexical_scope) # Set the number of bytecodes in this method self._bytecodes = ["\x00"] * num_bytecodes @@ -89,8 +88,6 @@ def __init__( self._size_frame = size_frame self._size_inner = size_inner - self._lexical_scope = lexical_scope - self._inlined_loops = inlined_loops def get_number_of_locals(self): diff --git a/src/som/vmobjects/method_trivial.py b/src/som/vmobjects/method_trivial.py index 510b92a2..6024913b 100644 --- a/src/som/vmobjects/method_trivial.py +++ b/src/som/vmobjects/method_trivial.py @@ -43,7 +43,7 @@ class LiteralReturn(AbstractTrivialMethod): _immutable_fields_ = ["_value"] def __init__(self, signature, value): - AbstractTrivialMethod.__init__(self, signature) + AbstractTrivialMethod.__init__(self, signature, None) self._value = value def set_holder(self, value): @@ -85,7 +85,7 @@ class GlobalRead(AbstractTrivialMethod): _immutable_fields_ = ["_assoc?", "_global_name", "_context_level", "universe"] def __init__(self, signature, global_name, context_level, universe, assoc=None): - AbstractTrivialMethod.__init__(self, signature) + AbstractTrivialMethod.__init__(self, signature, None) self._assoc = assoc self._global_name = global_name self._context_level = context_level @@ -140,7 +140,7 @@ class FieldRead(AbstractTrivialMethod): _immutable_fields_ = ["_field_idx", "_context_level"] def __init__(self, signature, field_idx, context_level): - AbstractTrivialMethod.__init__(self, signature) + AbstractTrivialMethod.__init__(self, signature, None) self._field_idx = field_idx self._context_level = context_level @@ -185,7 +185,7 @@ class FieldWrite(AbstractTrivialMethod): _immutable_fields_ = ["_field_idx", "_arg_idx"] def __init__(self, signature, field_idx, arg_idx): - AbstractTrivialMethod.__init__(self, signature) + AbstractTrivialMethod.__init__(self, signature, None) self._field_idx = field_idx self._arg_idx = arg_idx diff --git a/tests/test_ast_inlining.py b/tests/test_ast_inlining.py index e9d3c9a2..786745d2 100644 --- a/tests/test_ast_inlining.py +++ b/tests/test_ast_inlining.py @@ -23,6 +23,7 @@ IfInlinedNode, IfElseInlinedNode, ) +from som.interpreter.ast.nodes.specialized.literal_to_do import ToDoInlined from som.interpreter.ast.nodes.specialized.literal_while import WhileInlinedNode from som.interpreter.ast.nodes.variable_node import ( UninitializedReadNode, @@ -493,7 +494,7 @@ def test_to_do_block_block_inlined_self(cgenc, mgenc): b ifTrue: [ l2 := l2 + 1 ] ] ] )""", ) - block_a = ast._exprs[0]._arg_exprs[1]._value.invokable.expr_or_sequence + block_a = ast._exprs[0]._do_expr block_b_if_true = block_a._arg_exprs[0]._value.invokable.expr_or_sequence read_node = block_b_if_true._condition_expr @@ -502,13 +503,13 @@ def test_to_do_block_block_inlined_self(cgenc, mgenc): assert read_node.var.idx == 1 write_node = block_b_if_true._body_expr - assert write_node._context_level == 2 + assert write_node._context_level == 1 assert write_node._var._name == "l2" assert write_node._var.idx == 1 assert isinstance(write_node._value_expr, IntIncrementNode) read_l2_node = write_node._value_expr._rcvr_expr - assert read_l2_node._context_level == 2 + assert read_l2_node._context_level == 1 assert read_l2_node.var._name == "l2" assert read_l2_node.var.idx == 1 @@ -538,3 +539,11 @@ def test_inlining_of_or(mgenc, or_sel): ) assert isinstance(ast._exprs[0], OrInlinedNode) + + +def test_inlining_of_to_do(mgenc): + ast = parse_method(mgenc, "test = ( 1 to: 2 do: [:i | i ] )") + + assert isinstance(ast._exprs[0], ToDoInlined) + to_do = ast._exprs[0] + assert to_do._idx_var._name == "i" From 0c5eec33ff4c7b81716b2f60884456419b3b8f3e Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 3 Aug 2021 21:57:15 +0100 Subject: [PATCH 02/16] Added testing of printable locations, and fix issues There's a failing assertion on logging with PYPYLOG, which is hopefully fixed with this. Signed-off-by: Stefan Marr --- tests/test_printable_locations.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/test_printable_locations.py b/tests/test_printable_locations.py index da0caeb9..ac5b35b3 100644 --- a/tests/test_printable_locations.py +++ b/tests/test_printable_locations.py @@ -2,9 +2,13 @@ import pytest from rtruffle.source_section import SourceCoordinate, SourceSection +from som.interpreter.ast.nodes.expression_node import ExpressionNode from som.interpreter.ast.nodes.specialized.down_to_do_node import ( get_printable_location as pl_dtd, ) +from som.interpreter.ast.nodes.specialized.literal_to_do import ( + get_printable_location as pl_ltd, +) from som.interpreter.ast.nodes.specialized.literal_while import ( get_printable_location_while as pl_while, WhileInlinedNode, @@ -15,7 +19,6 @@ from som.interpreter.ast.nodes.specialized.to_do_node import ( get_printable_location as pl_td, ) -from som.vm.symbols import symbol_for from som.vmobjects.clazz import Class from som.vmobjects.method_ast import AstMethod, get_printable_location as pl_method @@ -41,6 +44,12 @@ def test_pl_dtd(method): assert pl_dtd(method) == "#to:do: Test>>test" +def test_pl_ltd(source_section): + do_expr = ExpressionNode(source_section) + + assert pl_ltd(do_expr, None) == "#to:do: test.som:1:1" + + def test_while(source_section): node = WhileInlinedNode(None, None, None, source_section) assert pl_while(node) == "while test.som:1:1" From fbe63a8ff0abdfe57da79a9cfb680761e6b633da Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 4 Aug 2021 00:03:09 +0100 Subject: [PATCH 03/16] Remove return from write_value Signed-off-by: Stefan Marr --- src/som/interpreter/ast/nodes/variable_node.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/som/interpreter/ast/nodes/variable_node.py b/src/som/interpreter/ast/nodes/variable_node.py index dcadc327..5a9bb42a 100644 --- a/src/som/interpreter/ast/nodes/variable_node.py +++ b/src/som/interpreter/ast/nodes/variable_node.py @@ -68,7 +68,7 @@ def execute(self, frame): return self._specialize().execute(frame) def write_value(self, frame, value): - return self._specialize().write_value(frame, value) + self._specialize().write_value(frame, value) def _specialize(self): return self.replace( @@ -160,7 +160,6 @@ def execute(self, frame): def write_value(self, frame, value): self.determine_block(frame).set_outer(self._frame_idx, value) - return value class _LocalVariableNode(ExpressionNode): @@ -194,7 +193,6 @@ def execute(self, frame): def write_value(self, frame, value): write_inner(frame, self._frame_idx, value) - return value class LocalFrameVarReadNode(_LocalVariableNode): @@ -213,4 +211,3 @@ def execute(self, frame): def write_value(self, frame, value): write_frame(frame, self._frame_idx, value) - return value From 4c2e3ae53acfc5e707e3565ecf858ae3c357cdc4 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 4 Aug 2021 00:06:18 +0100 Subject: [PATCH 04/16] Revise ToDoInlined to prevent unnecessary store to frame The biggest drawback of this design is that the lifetime of the frame is longer than the block activation in the loop, which is a problem for performance, because the compiler materializes the store. Using `if we_are_jitted()` prevents the unnecessary store in the trace, and at the same time, avoids much of the overhead in the interpreter. Signed-off-by: Stefan Marr --- .../ast/nodes/specialized/literal_to_do.py | 89 ++++++++----------- tests/test_printable_locations.py | 7 +- 2 files changed, 41 insertions(+), 55 deletions(-) diff --git a/src/som/interpreter/ast/nodes/specialized/literal_to_do.py b/src/som/interpreter/ast/nodes/specialized/literal_to_do.py index 49f55865..238d44ed 100644 --- a/src/som/interpreter/ast/nodes/specialized/literal_to_do.py +++ b/src/som/interpreter/ast/nodes/specialized/literal_to_do.py @@ -1,10 +1,29 @@ -from rlib.jit import JitDriver +from rlib.jit import JitDriver, we_are_jitted from som.interpreter.ast.nodes.expression_node import ExpressionNode +from som.vm.globals import nilObject from som.vmobjects.double import Double from som.vmobjects.integer import Integer +def get_printable_location(self): + assert isinstance(self, ToDoInlined) + source = self.source_section + return "#to:do: %s:%d:%d" % ( + source.file, + source.coord.start_line, + source.coord.start_column, + ) + + +driver = JitDriver( + greens=["self"], + reds="auto", + is_recursive=True, + get_printable_location=get_printable_location, +) + + class ToDoInlined(ExpressionNode): _immutable_fields_ = [ "_from_expr?", @@ -28,57 +47,23 @@ def execute(self, frame): assert isinstance(start, Integer) end = self._to_expr.execute(frame) + if isinstance(end, Integer): - _to_do_int( - frame, - start.get_embedded_integer(), - end.get_embedded_integer(), - self._idx_write, - self._do_expr, - ) - return start + end_int = end.get_embedded_integer() + else: + assert isinstance(end, Double) + end_int = int(end.get_embedded_double()) + + if we_are_jitted(): + self._idx_write.write_value(frame, nilObject) + + i = start.get_embedded_integer() + while i <= end_int: + driver.jit_merge_point(self=self) + self._idx_write.write_value(frame, Integer(i)) + self._do_expr.execute(frame) + if we_are_jitted(): + self._idx_write.write_value(frame, nilObject) + i += 1 - assert isinstance(end, Double) - _to_do_dbl( - frame, - start.get_embedded_integer(), - end.get_embedded_double(), - self._idx_write, - self._do_expr, - ) return start - - -def get_printable_location(do_expr, idx_write): # pylint: disable=unused-argument - assert isinstance(do_expr, ExpressionNode) - return "#to:do: %s" % str(do_expr.source_section) - - -int_driver = JitDriver( - greens=["do_expr", "idx_write"], - reds="auto", - is_recursive=True, - get_printable_location=get_printable_location, -) - - -dbl_driver = JitDriver( - greens=["do_expr", "idx_write"], - reds="auto", - is_recursive=True, - get_printable_location=get_printable_location, -) - - -def _to_do_int(frame, start, end, idx_write, do_expr): - for i in range(start, end + 1): - int_driver.jit_merge_point(do_expr=do_expr, idx_write=idx_write) - idx_write.write_value(frame, Integer(i)) - do_expr.execute(frame) - - -def _to_do_dbl(frame, start, end, idx_write, do_expr): - for i in range(start, end + 1): - dbl_driver.jit_merge_point(do_expr=do_expr, idx_write=idx_write) - idx_write.write_value(frame, Integer(i)) - do_expr.execute(frame) diff --git a/tests/test_printable_locations.py b/tests/test_printable_locations.py index ac5b35b3..472f0eb7 100644 --- a/tests/test_printable_locations.py +++ b/tests/test_printable_locations.py @@ -2,12 +2,13 @@ import pytest from rtruffle.source_section import SourceCoordinate, SourceSection -from som.interpreter.ast.nodes.expression_node import ExpressionNode +from som.compiler.ast.variable import Local from som.interpreter.ast.nodes.specialized.down_to_do_node import ( get_printable_location as pl_dtd, ) from som.interpreter.ast.nodes.specialized.literal_to_do import ( get_printable_location as pl_ltd, + ToDoInlined, ) from som.interpreter.ast.nodes.specialized.literal_while import ( get_printable_location_while as pl_while, @@ -45,9 +46,9 @@ def test_pl_dtd(method): def test_pl_ltd(source_section): - do_expr = ExpressionNode(source_section) + node = ToDoInlined(None, None, None, Local("", 1, source_section), source_section) - assert pl_ltd(do_expr, None) == "#to:do: test.som:1:1" + assert pl_ltd(node) == "#to:do: test.som:1:1" def test_while(source_section): From d6eb452141627f24a7f68cf98885d92a16c10e8e Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 5 Aug 2021 17:18:26 +0100 Subject: [PATCH 05/16] Inline #to:do: in the bytecode interpreter This requires two new bytecodes DUP_SECOND (in addition to DUP, which duplicates the first/top element of the stack), and JUMP_IF_GREATER Signed-off-by: Stefan Marr --- src/som/compiler/bc/bytecode_generator.py | 11 +++ .../compiler/bc/method_generation_context.py | 46 +++++++++++++ src/som/compiler/bc/parser.py | 1 + src/som/interpreter/bc/bytecodes.py | 14 +++- src/som/interpreter/bc/interpreter.py | 27 ++++++++ src/som/vmobjects/method_ast.py | 2 +- src/som/vmobjects/method_bc.py | 50 ++++++++++++-- src/som/vmobjects/method_trivial.py | 12 ++-- tests/test_bytecode_generation.py | 69 +++++++++++++++++++ tests/test_printable_locations.py | 1 + 10 files changed, 218 insertions(+), 15 deletions(-) diff --git a/src/som/compiler/bc/bytecode_generator.py b/src/som/compiler/bc/bytecode_generator.py index d8a83817..1052d888 100644 --- a/src/som/compiler/bc/bytecode_generator.py +++ b/src/som/compiler/bc/bytecode_generator.py @@ -57,6 +57,10 @@ def emit_dup(mgenc): emit1(mgenc, BC.dup, 1) +def emit_dup_second(mgenc): + emit1(mgenc, BC.dup_second, 1) + + def emit_push_block(mgenc, block_method, with_ctx): idx = mgenc.add_literal_if_absent(block_method) emit2(mgenc, BC.push_block if with_ctx else BC.push_block_no_ctx, idx, 1) @@ -211,6 +215,13 @@ def emit_jump_with_dummy_offset(mgenc): return idx +def emit_jump_if_greater_with_dummy_offset(mgenc): + emit1(mgenc, BC.jump_if_greater, 0) + idx = mgenc.add_bytecode_argument_and_get_index(0) + mgenc.add_bytecode_argument(0) + return idx + + def emit_jump_backward_with_offset(mgenc, offset): emit3( mgenc, diff --git a/src/som/compiler/bc/method_generation_context.py b/src/som/compiler/bc/method_generation_context.py index c0bf3fd3..c8024c07 100644 --- a/src/som/compiler/bc/method_generation_context.py +++ b/src/som/compiler/bc/method_generation_context.py @@ -5,6 +5,11 @@ emit_pop, emit_push_constant, emit_jump_backward_with_offset, + emit_dup, + emit_inc, + emit_dup_second, + emit_jump_if_greater_with_dummy_offset, + emit_pop_local, emit_inc_field_push, emit_return_field, ) @@ -752,6 +757,47 @@ def inline_andor(self, parser, is_or): return True + def inline_to_do(self, parser): + # HACK: We do assume that the receiver on the stack is a integer, + # HACK: similar to the other inlined messages. + # HACK: We don't support anything but integer at the moment. + push_block_candidate = self._last_bytecode_is_one_of(0, PUSH_BLOCK_BYTECODES) + if push_block_candidate == Bytecodes.invalid: + return False + + assert bytecode_length(push_block_candidate) == 2 + block_literal_idx = self._bytecode[-1] + + to_be_inlined = self._literals[block_literal_idx] + + self._remove_last_bytecodes(1) # remove push_block* + + self._is_currently_inlining_a_block = True + emit_dup_second(self) + + loop_begin_idx = self.offset_of_next_instruction() + jump_offset_idx_to_end = emit_jump_if_greater_with_dummy_offset(self) + + emit_dup(self) + + to_be_inlined.merge_scope_into(self) + + block_arg = to_be_inlined.get_argument(1, 0) + emit_pop_local(self, self.get_inlined_local_idx(block_arg, 0), 0) + + to_be_inlined.inline(self, False) + + emit_pop(self) + emit_inc(self) + + self.emit_backwards_jump_offset_to_target(loop_begin_idx, parser) + self.patch_jump_offset_to_point_to_next_instruction( + jump_offset_idx_to_end, parser + ) + + self._is_currently_inlining_a_block = False + return True + def _complete_jumps_and_emit_returning_nil( self, parser, loop_begin_idx, jump_offset_idx_to_skip_loop_body ): diff --git a/src/som/compiler/bc/parser.py b/src/som/compiler/bc/parser.py index bfb3a6aa..bac9d2cd 100644 --- a/src/som/compiler/bc/parser.py +++ b/src/som/compiler/bc/parser.py @@ -290,6 +290,7 @@ def _keyword_message(self, mgenc): keyword == "ifFalse:ifTrue:" and mgenc.inline_if_true_false(self, False) ) + or (keyword == "to:do:" and mgenc.inline_to_do(self)) ): return diff --git a/src/som/interpreter/bc/bytecodes.py b/src/som/interpreter/bc/bytecodes.py index 2fa56bf5..98879d19 100644 --- a/src/som/interpreter/bc/bytecodes.py +++ b/src/som/interpreter/bc/bytecodes.py @@ -5,8 +5,9 @@ class Bytecodes(object): # Bytecodes used by the Simple Object Machine (SOM) halt = 0 dup = halt + 1 + dup_second = dup + 1 - push_frame = dup + 1 + push_frame = dup_second + 1 push_frame_0 = push_frame + 1 push_frame_1 = push_frame_0 + 1 push_frame_2 = push_frame_1 + 1 @@ -76,13 +77,15 @@ class Bytecodes(object): jump_on_false_top_nil = jump_on_true_top_nil + 1 jump_on_true_pop = jump_on_false_top_nil + 1 jump_on_false_pop = jump_on_true_pop + 1 - jump_backward = jump_on_false_pop + 1 + jump_if_greater = jump_on_false_pop + 1 + jump_backward = jump_if_greater + 1 jump2 = jump_backward + 1 jump2_on_true_top_nil = jump2 + 1 jump2_on_false_top_nil = jump2_on_true_top_nil + 1 jump2_on_true_pop = jump2_on_false_top_nil + 1 jump2_on_false_pop = jump2_on_true_pop + 1 - jump2_backward = jump2_on_false_pop + 1 + jump2_if_greater = jump2_on_false_pop + 1 + jump2_backward = jump2_if_greater + 1 q_super_send_1 = jump2_backward + 1 q_super_send_2 = q_super_send_1 + 1 @@ -150,12 +153,14 @@ def is_one_of(bytecode, candidates): Bytecodes.jump_on_true_pop, Bytecodes.jump_on_false_pop, Bytecodes.jump_on_false_top_nil, + Bytecodes.jump_if_greater, Bytecodes.jump_backward, Bytecodes.jump2, Bytecodes.jump2_on_true_top_nil, Bytecodes.jump2_on_true_pop, Bytecodes.jump2_on_false_pop, Bytecodes.jump2_on_false_top_nil, + Bytecodes.jump2_if_greater, Bytecodes.jump2_backward, ] @@ -198,6 +203,7 @@ def is_one_of(bytecode, candidates): _BYTECODE_LENGTH = [ 1, # halt 1, # dup + 1, # dup_second 3, # push_frame 3, # push_frame_0 3, # push_frame_1 @@ -251,12 +257,14 @@ def is_one_of(bytecode, candidates): 3, # jump_on_false_top_nil 3, # jump_on_true_pop 3, # jump_on_false_pop + 3, # jump_if_greater 3, # jump_backward 3, # jump2 3, # jump2_on_true_top_nil 3, # jump2_on_false_top_nil 3, # jump2_on_true_pop 3, # jump2_on_false_pop + 3, # jump2_if_greater 3, # jump2_backward 2, # q_super_send_1 2, # q_super_send_2 diff --git a/src/som/interpreter/bc/interpreter.py b/src/som/interpreter/bc/interpreter.py index 4d983519..ababfa69 100644 --- a/src/som/interpreter/bc/interpreter.py +++ b/src/som/interpreter/bc/interpreter.py @@ -135,6 +135,11 @@ def interpret(method, frame, max_stack_size): stack_ptr += 1 stack[stack_ptr] = val + elif bytecode == Bytecodes.dup_second: + val = stack[stack_ptr - 1] + stack_ptr += 1 + stack[stack_ptr] = val + elif bytecode == Bytecodes.push_frame: stack_ptr += 1 stack[stack_ptr] = read_frame( @@ -545,6 +550,15 @@ def interpret(method, frame, max_stack_size): stack[stack_ptr] = None stack_ptr -= 1 + elif bytecode == Bytecodes.jump_if_greater: + top = stack[stack_ptr] + top_2 = stack[stack_ptr - 1] + if top.get_embedded_integer() > top_2.get_embedded_integer(): + stack[stack_ptr] = None + stack[stack_ptr - 1] = None + stack_ptr -= 2 + next_bc_idx = current_bc_idx + method.get_bytecode(current_bc_idx + 1) + elif bytecode == Bytecodes.jump_backward: next_bc_idx = current_bc_idx - method.get_bytecode(current_bc_idx + 1) jitdriver.can_enter_jit( @@ -614,6 +628,19 @@ def interpret(method, frame, max_stack_size): stack[stack_ptr] = None stack_ptr -= 1 + elif bytecode == Bytecodes.jump2_if_greater: + top = stack[stack_ptr] + top_2 = stack[stack_ptr - 1] + if top.get_embedded_integer() > top_2.get_embedded_integer(): + stack[stack_ptr] = None + stack[stack_ptr - 1] = None + stack_ptr -= 2 + next_bc_idx = ( + current_bc_idx + + method.get_bytecode(current_bc_idx + 1) + + (method.get_bytecode(current_bc_idx + 2) << 8) + ) + elif bytecode == Bytecodes.jump2_backward: next_bc_idx = current_bc_idx - ( method.get_bytecode(current_bc_idx + 1) diff --git a/src/som/vmobjects/method_ast.py b/src/som/vmobjects/method_ast.py index a4d3b456..f4e94358 100644 --- a/src/som/vmobjects/method_ast.py +++ b/src/som/vmobjects/method_ast.py @@ -162,7 +162,7 @@ def invoke_args(node, rcvr, args): # pylint: disable=no-self-argument ) return node.invokable.expr_or_sequence.execute(frame) - def inline(self, mgenc): + def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument mgenc.merge_into_scope(self._lexical_scope) self.invokable.expr_or_sequence.adapt_after_inlining(mgenc) return self.invokable.expr_or_sequence diff --git a/src/som/vmobjects/method_bc.py b/src/som/vmobjects/method_bc.py index da18193d..51f29557 100644 --- a/src/som/vmobjects/method_bc.py +++ b/src/som/vmobjects/method_bc.py @@ -15,6 +15,8 @@ emit_push_field_with_index, emit_pop_field_with_index, emit3_with_dummy, + emit_push_local, + emit_pop_local, compute_offset, ) from som.interpreter.ast.frame import ( @@ -240,8 +242,12 @@ def invoke_n(self, stack, stack_ptr): stack, stack_ptr, self._number_of_arguments, result ) - def inline(self, mgenc): + def merge_scope_into(self, mgenc): mgenc.merge_into_scope(self._lexical_scope) + + def inline(self, mgenc, merge_scope=True): + if merge_scope: + mgenc.merge_into_scope(self._lexical_scope) self._inline_into(mgenc) def _create_back_jump_heap(self): @@ -294,7 +300,7 @@ def _inline_into(self, mgenc): if bytecode == Bytecodes.halt: emit1(mgenc, bytecode, 0) - elif bytecode == Bytecodes.dup: + elif bytecode == Bytecodes.dup or bytecode == Bytecodes.dup_second: emit1(mgenc, bytecode, 1) elif ( @@ -305,8 +311,23 @@ def _inline_into(self, mgenc): ): idx = self.get_bytecode(i + 1) ctx_level = self.get_bytecode(i + 2) - assert ctx_level > 0 - if bytecode == Bytecodes.push_field: + + if ctx_level == 0: + assert ( + bytecode == Bytecodes.push_argument + or bytecode == Bytecodes.pop_argument + ), ( + "This should really be push or pop argument." + + " everything else should have a ctx_level > 0" + ) + + arg = self._lexical_scope.get_argument(idx, 0) + idx = mgenc.get_inlined_local_idx(arg, 0) + if bytecode == Bytecodes.push_argument: + emit_push_local(mgenc, idx, 0) + else: + emit_pop_local(mgenc, idx, 0) + elif bytecode == Bytecodes.push_field: emit_push_field_with_index(mgenc, idx, ctx_level - 1) elif bytecode == Bytecodes.pop_field: emit_pop_field_with_index(mgenc, idx, ctx_level - 1) @@ -422,9 +443,11 @@ def _inline_into(self, mgenc): bytecode == Bytecodes.jump or bytecode == Bytecodes.jump_on_true_top_nil or bytecode == Bytecodes.jump_on_false_top_nil + or bytecode == Bytecodes.jump_if_greater or bytecode == Bytecodes.jump2 or bytecode == Bytecodes.jump2_on_true_top_nil or bytecode == Bytecodes.jump2_on_false_top_nil + or bytecode == Bytecodes.jump2_if_greater ): # emit the jump, but instead of the offset, emit a dummy idx = emit3_with_dummy(mgenc, bytecode, 0) @@ -491,6 +514,7 @@ def adapt_after_outer_inlined(self, removed_ctx_level, mgenc_with_inlined): if ( bytecode == Bytecodes.halt or bytecode == Bytecodes.dup + or bytecode == Bytecodes.dup_second or bytecode == Bytecodes.push_block_no_ctx or bytecode == Bytecodes.push_constant or bytecode == Bytecodes.push_constant_0 @@ -517,12 +541,14 @@ def adapt_after_outer_inlined(self, removed_ctx_level, mgenc_with_inlined): or bytecode == Bytecodes.jump_on_true_pop or bytecode == Bytecodes.jump_on_false_top_nil or bytecode == Bytecodes.jump_on_false_pop + or bytecode == Bytecodes.jump_if_greater or bytecode == Bytecodes.jump_backward or bytecode == Bytecodes.jump2 or bytecode == Bytecodes.jump2_on_true_top_nil or bytecode == Bytecodes.jump2_on_true_pop or bytecode == Bytecodes.jump2_on_false_top_nil or bytecode == Bytecodes.jump2_on_false_pop + or bytecode == Bytecodes.jump2_if_greater or bytecode == Bytecodes.jump2_backward ): # don't use context @@ -539,6 +565,20 @@ def adapt_after_outer_inlined(self, removed_ctx_level, mgenc_with_inlined): ctx_level = self.get_bytecode(i + 2) if ctx_level > removed_ctx_level: self.set_bytecode(i + 2, ctx_level - 1) + elif ctx_level == removed_ctx_level and ( + bytecode == Bytecodes.push_argument + or bytecode == Bytecodes.pop_argument + ): + idx = self.get_bytecode(i + 1) + arg = self._lexical_scope.get_argument(idx, removed_ctx_level) + new_idx = mgenc_with_inlined.get_inlined_local_idx( + arg, removed_ctx_level + ) + if bytecode == Bytecodes.push_argument: + self.set_bytecode(i, Bytecodes.push_local) + else: + self.set_bytecode(i, Bytecodes.pop_local) + self.set_bytecode(i + 1, new_idx) elif bytecode == Bytecodes.push_block: literal_idx = self.get_bytecode(i + 1) @@ -665,7 +705,7 @@ def invoke_n(self, stack, stack_ptr): ) raise e - def inline(self, mgenc): + def inline(self, mgenc, merge_scope=True): raise Exception( "Blocks should never handle non-local returns. " "So, this should not happen." diff --git a/src/som/vmobjects/method_trivial.py b/src/som/vmobjects/method_trivial.py index 6024913b..0c74570d 100644 --- a/src/som/vmobjects/method_trivial.py +++ b/src/som/vmobjects/method_trivial.py @@ -70,14 +70,14 @@ def invoke_n(self, stack, stack_ptr): if is_ast_interpreter(): - def inline(self, _mgenc): + def inline(self, _mgenc, merge_scope=True): # pylint: disable=unused-argument from som.interpreter.ast.nodes.literal_node import LiteralNode return LiteralNode(self._value) else: - def inline(self, mgenc): + def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument emit_push_constant(mgenc, self._value) @@ -125,14 +125,14 @@ def invoke_n(self, stack, stack_ptr): if is_ast_interpreter(): - def inline(self, mgenc): + def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument from som.interpreter.ast.nodes.global_read_node import create_global_node return create_global_node(self._global_name, self.universe, mgenc, None) else: - def inline(self, mgenc): + def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument emit_push_global(mgenc, self._global_name) @@ -170,14 +170,14 @@ def invoke_n(self, stack, stack_ptr): if is_ast_interpreter(): - def inline(self, mgenc): + def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument from som.interpreter.ast.nodes.field_node import FieldReadNode return FieldReadNode(mgenc.get_self_read(), self._field_idx, None) else: - def inline(self, mgenc): + def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument emit_push_field_with_index(mgenc, self._field_idx, self._context_level - 1) diff --git a/tests/test_bytecode_generation.py b/tests/test_bytecode_generation.py index 6290b020..80e1d2a8 100644 --- a/tests/test_bytecode_generation.py +++ b/tests/test_bytecode_generation.py @@ -1377,3 +1377,72 @@ def test_field_read_inlining(cgenc, mgenc): Bytecodes.return_self, ], ) + + +def test_inlining_of_to_do(mgenc): + bytecodes = method_to_bytecodes(mgenc, "test = ( 1 to: 2 do: [:i | i ] )") + + assert len(bytecodes) == 19 + check( + bytecodes, + [ + Bytecodes.push_1, + Bytecodes.push_constant_0, + Bytecodes.dup_second, # stack: Top[1, 2, 1] + BC(Bytecodes.jump_if_greater, 15), # consume only on jump + Bytecodes.dup, + BC( + Bytecodes.pop_local, 0, 0 + ), # store the i into the local (arg becomes local after inlining) + BC( + Bytecodes.push_local, 0, 0 + ), # push the local on the stack as part of the block's code + Bytecodes.pop, # cleanup after block. + Bytecodes.inc, # increment top, the iteration counter + BC( + Bytecodes.jump_backward, 12 + ), # jump back to the jump_if_greater bytecode + # jump_if_greater target + Bytecodes.return_self, + ], + ) + + +def test_to_do_block_block_inlined_self(cgenc, mgenc): + add_field(cgenc, "field") + bytecodes = method_to_bytecodes( + mgenc, + """ + test = ( + | l1 l2 | + 1 to: 2 do: [:a | + l1 do: [:b | + b ifTrue: [ + a. + l2 := l2 + 1 ] ] ] + )""", + ) + + assert len(bytecodes) == 23 + check( + bytecodes, + [ + Bytecodes.push_1, + Bytecodes.push_constant_0, + Bytecodes.dup_second, + BC(Bytecodes.jump_if_greater, 19), + Bytecodes.dup, + BC(Bytecodes.pop_local, 2, 0), + BC(Bytecodes.push_local, 0, 0), + BC(Bytecodes.push_block, 2), + Bytecodes.send_2, + Bytecodes.pop, + Bytecodes.inc, + BC(Bytecodes.jump_backward, 16), + Bytecodes.return_self, + ], + ) + + block_method = mgenc._literals[2] # pylint: disable=protected-access + block_bcs = block_method.get_bytecodes() + check(block_bcs, [(6, BC(Bytecodes.push_local, 2, 1)), Bytecodes.pop]) diff --git a/tests/test_printable_locations.py b/tests/test_printable_locations.py index 472f0eb7..c0fed1b1 100644 --- a/tests/test_printable_locations.py +++ b/tests/test_printable_locations.py @@ -20,6 +20,7 @@ from som.interpreter.ast.nodes.specialized.to_do_node import ( get_printable_location as pl_td, ) +from som.vm.symbols import symbol_for from som.vmobjects.clazz import Class from som.vmobjects.method_ast import AstMethod, get_printable_location as pl_method From 11ab84b3115d73f05e4e71f010207b6b806c57c1 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 5 Aug 2021 20:49:51 +0100 Subject: [PATCH 06/16] Added bytecodes that nils out the loop variable in jitted mode Signed-off-by: Stefan Marr --- src/som/compiler/bc/bytecode_generator.py | 4 ++++ src/som/compiler/bc/disassembler.py | 8 +++++++- .../compiler/bc/method_generation_context.py | 6 +++++- src/som/interpreter/bc/bytecodes.py | 13 ++++++++++-- src/som/interpreter/bc/interpreter.py | 14 +++++++++++++ src/som/vmobjects/method_bc.py | 20 +++++++++++++++++++ tests/test_bytecode_generation.py | 14 +++++++------ 7 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/som/compiler/bc/bytecode_generator.py b/src/som/compiler/bc/bytecode_generator.py index 1052d888..5c4513e9 100644 --- a/src/som/compiler/bc/bytecode_generator.py +++ b/src/som/compiler/bc/bytecode_generator.py @@ -24,6 +24,10 @@ def emit_push_argument(mgenc, idx, ctx): emit3(mgenc, BC.push_argument, idx, ctx, 1) +def emit_nil_local(mgenc, idx): + emit2(mgenc, BC.nil_local, idx) + + def emit_return_self(mgenc): mgenc.optimize_dup_pop_pop_sequence() emit1(mgenc, BC.return_self, 0) diff --git a/src/som/compiler/bc/disassembler.py b/src/som/compiler/bc/disassembler.py index 67df56f3..42360b8d 100644 --- a/src/som/compiler/bc/disassembler.py +++ b/src/som/compiler/bc/disassembler.py @@ -78,7 +78,13 @@ def dump_bytecode(m, b, indent=""): + ", context " + str(m.get_bytecode(b + 2)) ) - elif bytecode == Bytecodes.push_frame or bytecode == Bytecodes.pop_frame: + elif ( + bytecode == Bytecodes.push_frame + or bytecode == Bytecodes.pop_frame + or bytecode == Bytecodes.nil_inner + or bytecode == Bytecodes.nil_frame + or bytecode == Bytecodes.nil_local + ): error_println("idx: " + str(m.get_bytecode(b + 1))) elif bytecode == Bytecodes.push_inner or bytecode == Bytecodes.pop_inner: error_println( diff --git a/src/som/compiler/bc/method_generation_context.py b/src/som/compiler/bc/method_generation_context.py index c8024c07..5e4fa92c 100644 --- a/src/som/compiler/bc/method_generation_context.py +++ b/src/som/compiler/bc/method_generation_context.py @@ -10,6 +10,7 @@ emit_dup_second, emit_jump_if_greater_with_dummy_offset, emit_pop_local, + emit_nil_local, emit_inc_field_push, emit_return_field, ) @@ -783,14 +784,17 @@ def inline_to_do(self, parser): to_be_inlined.merge_scope_into(self) block_arg = to_be_inlined.get_argument(1, 0) - emit_pop_local(self, self.get_inlined_local_idx(block_arg, 0), 0) + i_var_idx = self.get_inlined_local_idx(block_arg, 0) + emit_pop_local(self, i_var_idx, 0) to_be_inlined.inline(self, False) emit_pop(self) emit_inc(self) + emit_nil_local(self, i_var_idx) self.emit_backwards_jump_offset_to_target(loop_begin_idx, parser) + self.patch_jump_offset_to_point_to_next_instruction( jump_offset_idx_to_end, parser ) diff --git a/src/som/interpreter/bc/bytecodes.py b/src/som/interpreter/bc/bytecodes.py index 98879d19..0659967b 100644 --- a/src/som/interpreter/bc/bytecodes.py +++ b/src/som/interpreter/bc/bytecodes.py @@ -51,7 +51,10 @@ class Bytecodes(object): pop_field_0 = pop_field + 1 pop_field_1 = pop_field_0 + 1 - send_1 = pop_field_1 + 1 + nil_frame = pop_field_1 + 1 + nil_inner = nil_frame + 1 + + send_1 = nil_inner + 1 send_2 = send_1 + 1 send_3 = send_2 + 1 send_n = send_3 + 1 @@ -96,8 +99,9 @@ class Bytecodes(object): push_argument = push_local + 1 pop_local = push_argument + 1 pop_argument = pop_local + 1 + nil_local = pop_argument + 1 - invalid = pop_argument + 1 + invalid = nil_local + 1 def is_one_of(bytecode, candidates): @@ -182,6 +186,8 @@ def is_one_of(bytecode, candidates): Bytecodes.pop_inner_0, Bytecodes.pop_inner_1, Bytecodes.pop_inner_2, + Bytecodes.nil_frame, + Bytecodes.nil_inner, Bytecodes.q_super_send_1, Bytecodes.q_super_send_2, Bytecodes.q_super_send_3, @@ -237,6 +243,8 @@ def is_one_of(bytecode, candidates): 3, # pop_field 1, # pop_field_0 1, # pop_field_1 + 2, # nil_frame + 2, # nil_inner 2, # send_1 2, # send_2 2, # send_3 @@ -275,6 +283,7 @@ def is_one_of(bytecode, candidates): 3, # push_argument 3, # pop_local 3, # pop_argument + 2, # nil_local ] diff --git a/src/som/interpreter/bc/interpreter.py b/src/som/interpreter/bc/interpreter.py index ababfa69..a506749f 100644 --- a/src/som/interpreter/bc/interpreter.py +++ b/src/som/interpreter/bc/interpreter.py @@ -319,6 +319,16 @@ def interpret(method, frame, max_stack_size): write_inner(frame, FRAME_AND_INNER_RCVR_IDX + 2, value) + elif bytecode == Bytecodes.nil_frame: + if we_are_jitted(): + idx = method.get_bytecode(current_bc_idx + 1) + write_frame(frame, idx, nilObject) + + elif bytecode == Bytecodes.nil_inner: + if we_are_jitted(): + idx = method.get_bytecode(current_bc_idx + 1) + write_inner(frame, idx, nilObject) + elif bytecode == Bytecodes.pop_field: field_idx = method.get_bytecode(current_bc_idx + 1) ctx_level = method.get_bytecode(current_bc_idx + 2) @@ -696,6 +706,10 @@ def interpret(method, frame, max_stack_size): method.patch_variable_access(current_bc_idx) # retry bytecode after patching next_bc_idx = current_bc_idx + elif bytecode == Bytecodes.nil_local: + method.patch_variable_access(current_bc_idx) + # retry bytecode after patching + next_bc_idx = current_bc_idx else: _unknown_bytecode(bytecode, current_bc_idx, method) diff --git a/src/som/vmobjects/method_bc.py b/src/som/vmobjects/method_bc.py index 51f29557..20f7e54a 100644 --- a/src/som/vmobjects/method_bc.py +++ b/src/som/vmobjects/method_bc.py @@ -17,6 +17,7 @@ emit3_with_dummy, emit_push_local, emit_pop_local, + emit_nil_local, compute_offset, ) from som.interpreter.ast.frame import ( @@ -177,6 +178,13 @@ def patch_variable_access(self, bytecode_index): elif bc == Bytecodes.pop_local: var = self._lexical_scope.get_local(idx, ctx_level) self.set_bytecode(bytecode_index, var.get_pop_bytecode(ctx_level)) + elif bc == Bytecodes.nil_local: + var = self._lexical_scope.get_local(idx, 0) + if var.is_accessed_out_of_context(): + bytecode = Bytecodes.nil_inner + else: + bytecode = Bytecodes.nil_frame + self.set_bytecode(bytecode_index, bytecode) else: raise Exception("Unsupported bytecode?") assert ( @@ -362,6 +370,12 @@ def _inline_into(self, mgenc): else: emit3(mgenc, bytecode, idx, ctx_level, -1) + elif bytecode == Bytecodes.nil_local: + idx = self.get_bytecode(i + 1) + var = self._lexical_scope.get_local(idx, 0) + idx = mgenc.get_inlined_local_idx(var, 0) + emit_nil_local(mgenc, idx) + elif bytecode == Bytecodes.push_block: literal_idx = self.get_bytecode(i + 1) block_method = self._literals[literal_idx] @@ -603,6 +617,12 @@ def adapt_after_outer_inlined(self, removed_ctx_level, mgenc_with_inlined): elif ctx_level > removed_ctx_level: self.set_bytecode(i + 2, ctx_level - 1) + elif bytecode == Bytecodes.nil_local: + assert removed_ctx_level > 0, ( + "Don't need to adjust this bytecode, " + + "because it only operates on ctx_level==0" + ) + elif bytecode == Bytecodes.return_non_local: ctx_level = self.get_bytecode(i + 1) self.set_bytecode(i + 1, ctx_level - 1) diff --git a/tests/test_bytecode_generation.py b/tests/test_bytecode_generation.py index 80e1d2a8..1d467209 100644 --- a/tests/test_bytecode_generation.py +++ b/tests/test_bytecode_generation.py @@ -1382,14 +1382,14 @@ def test_field_read_inlining(cgenc, mgenc): def test_inlining_of_to_do(mgenc): bytecodes = method_to_bytecodes(mgenc, "test = ( 1 to: 2 do: [:i | i ] )") - assert len(bytecodes) == 19 + assert len(bytecodes) == 21 check( bytecodes, [ Bytecodes.push_1, Bytecodes.push_constant_0, Bytecodes.dup_second, # stack: Top[1, 2, 1] - BC(Bytecodes.jump_if_greater, 15), # consume only on jump + BC(Bytecodes.jump_if_greater, 17), # consume only on jump Bytecodes.dup, BC( Bytecodes.pop_local, 0, 0 @@ -1399,8 +1399,9 @@ def test_inlining_of_to_do(mgenc): ), # push the local on the stack as part of the block's code Bytecodes.pop, # cleanup after block. Bytecodes.inc, # increment top, the iteration counter + Bytecodes.nil_local, BC( - Bytecodes.jump_backward, 12 + Bytecodes.jump_backward, 14 ), # jump back to the jump_if_greater bytecode # jump_if_greater target Bytecodes.return_self, @@ -1423,14 +1424,14 @@ def test_to_do_block_block_inlined_self(cgenc, mgenc): )""", ) - assert len(bytecodes) == 23 + assert len(bytecodes) == 25 check( bytecodes, [ Bytecodes.push_1, Bytecodes.push_constant_0, Bytecodes.dup_second, - BC(Bytecodes.jump_if_greater, 19), + BC(Bytecodes.jump_if_greater, 21), Bytecodes.dup, BC(Bytecodes.pop_local, 2, 0), BC(Bytecodes.push_local, 0, 0), @@ -1438,7 +1439,8 @@ def test_to_do_block_block_inlined_self(cgenc, mgenc): Bytecodes.send_2, Bytecodes.pop, Bytecodes.inc, - BC(Bytecodes.jump_backward, 16), + Bytecodes.nil_local, + BC(Bytecodes.jump_backward, 18), Bytecodes.return_self, ], ) From c5176b57538d908b4ced2b380c751bf484e8e09e Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 6 Aug 2021 12:44:20 +0100 Subject: [PATCH 07/16] Nil var before loop Signed-off-by: Stefan Marr --- src/som/compiler/bc/method_generation_context.py | 10 ++++++---- tests/test_bytecode_generation.py | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/som/compiler/bc/method_generation_context.py b/src/som/compiler/bc/method_generation_context.py index 5e4fa92c..92b197b4 100644 --- a/src/som/compiler/bc/method_generation_context.py +++ b/src/som/compiler/bc/method_generation_context.py @@ -770,21 +770,23 @@ def inline_to_do(self, parser): block_literal_idx = self._bytecode[-1] to_be_inlined = self._literals[block_literal_idx] + to_be_inlined.merge_scope_into(self) + + block_arg = to_be_inlined.get_argument(1, 0) + i_var_idx = self.get_inlined_local_idx(block_arg, 0) self._remove_last_bytecodes(1) # remove push_block* self._is_currently_inlining_a_block = True emit_dup_second(self) + emit_nil_local(self, i_var_idx) + loop_begin_idx = self.offset_of_next_instruction() jump_offset_idx_to_end = emit_jump_if_greater_with_dummy_offset(self) emit_dup(self) - to_be_inlined.merge_scope_into(self) - - block_arg = to_be_inlined.get_argument(1, 0) - i_var_idx = self.get_inlined_local_idx(block_arg, 0) emit_pop_local(self, i_var_idx, 0) to_be_inlined.inline(self, False) diff --git a/tests/test_bytecode_generation.py b/tests/test_bytecode_generation.py index 1d467209..7127f86d 100644 --- a/tests/test_bytecode_generation.py +++ b/tests/test_bytecode_generation.py @@ -1382,13 +1382,14 @@ def test_field_read_inlining(cgenc, mgenc): def test_inlining_of_to_do(mgenc): bytecodes = method_to_bytecodes(mgenc, "test = ( 1 to: 2 do: [:i | i ] )") - assert len(bytecodes) == 21 + assert len(bytecodes) == 23 check( bytecodes, [ Bytecodes.push_1, Bytecodes.push_constant_0, Bytecodes.dup_second, # stack: Top[1, 2, 1] + Bytecodes.nil_local, BC(Bytecodes.jump_if_greater, 17), # consume only on jump Bytecodes.dup, BC( @@ -1424,13 +1425,14 @@ def test_to_do_block_block_inlined_self(cgenc, mgenc): )""", ) - assert len(bytecodes) == 25 + assert len(bytecodes) == 27 check( bytecodes, [ Bytecodes.push_1, Bytecodes.push_constant_0, Bytecodes.dup_second, + Bytecodes.nil_local, BC(Bytecodes.jump_if_greater, 21), Bytecodes.dup, BC(Bytecodes.pop_local, 2, 0), From 2e5e16dfe823161a8f105d372bf29e9f9e98925f Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 24 Aug 2021 10:41:58 +0100 Subject: [PATCH 08/16] Fix emitting new bytecodes Signed-off-by: Stefan Marr --- src/som/compiler/bc/bytecode_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/som/compiler/bc/bytecode_generator.py b/src/som/compiler/bc/bytecode_generator.py index 5c4513e9..642a550b 100644 --- a/src/som/compiler/bc/bytecode_generator.py +++ b/src/som/compiler/bc/bytecode_generator.py @@ -25,7 +25,7 @@ def emit_push_argument(mgenc, idx, ctx): def emit_nil_local(mgenc, idx): - emit2(mgenc, BC.nil_local, idx) + emit2(mgenc, BC.nil_local, idx, 0) def emit_return_self(mgenc): From 816559fe605b423b3da1750b1edb96911ac03766 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 9 Feb 2023 02:11:51 +0000 Subject: [PATCH 09/16] The trivial LiteralReturn methods need a source section to enable inlining for them Signed-off-by: Stefan Marr --- src/som/compiler/bc/method_generation_context.py | 16 +++++++++------- src/som/interpreter/ast/nodes/literal_node.py | 2 +- src/som/vmobjects/method_trivial.py | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/som/compiler/bc/method_generation_context.py b/src/som/compiler/bc/method_generation_context.py index 92b197b4..219e6aed 100644 --- a/src/som/compiler/bc/method_generation_context.py +++ b/src/som/compiler/bc/method_generation_context.py @@ -482,16 +482,18 @@ def _assemble_literal_return(self, return_candidate, push_candidate): ): return None + source_section = self.lexical_scope.arguments[0].source + if len(self._literals) == 1: - return LiteralReturn(self.signature, self._literals[0]) + return LiteralReturn(self.signature, self._literals[0], source_section) if self._bytecode[0] == Bytecodes.push_0: - return LiteralReturn(self.signature, int_0) + return LiteralReturn(self.signature, int_0, source_section) if self._bytecode[0] == Bytecodes.push_1: - return LiteralReturn(self.signature, int_1) + return LiteralReturn(self.signature, int_1, source_section) if self._bytecode[0] == Bytecodes.push_nil: from som.vm.globals import nilObject - return LiteralReturn(self.signature, nilObject) + return LiteralReturn(self.signature, nilObject, source_section) raise NotImplementedError( "Not sure what's going on. Perhaps some new bytecode or unexpected literal?" ) @@ -509,13 +511,13 @@ def _assemble_global_return(self, return_candidate, push_candidate): assert isinstance(global_name, Symbol) if global_name is sym_true: - return LiteralReturn(self.signature, trueObject) + return LiteralReturn(self.signature, trueObject, source_section) if global_name is sym_false: - return LiteralReturn(self.signature, falseObject) + return LiteralReturn(self.signature, falseObject, source_section) if global_name is sym_nil: from som.vm.globals import nilObject - return LiteralReturn(self.signature, nilObject) + return LiteralReturn(self.signature, nilObject, source_section) return GlobalRead( self.signature, diff --git a/src/som/interpreter/ast/nodes/literal_node.py b/src/som/interpreter/ast/nodes/literal_node.py index cbd4b2eb..dcff6cdf 100644 --- a/src/som/interpreter/ast/nodes/literal_node.py +++ b/src/som/interpreter/ast/nodes/literal_node.py @@ -14,4 +14,4 @@ def execute(self, _frame): def create_trivial_method(self, signature): from som.vmobjects.method_trivial import LiteralReturn - return LiteralReturn(signature, self._value) + return LiteralReturn(signature, self._value, self.source_section) diff --git a/src/som/vmobjects/method_trivial.py b/src/som/vmobjects/method_trivial.py index 0c74570d..594b282d 100644 --- a/src/som/vmobjects/method_trivial.py +++ b/src/som/vmobjects/method_trivial.py @@ -4,6 +4,7 @@ emit_push_global, emit_push_field_with_index, ) +from som.compiler.ast.variable import Argument from som.interp_type import is_ast_interpreter from som.interpreter.ast.frame import FRAME_AND_INNER_RCVR_IDX from som.interpreter.bc.frame import stack_pop_old_arguments_and_push_result @@ -40,11 +41,12 @@ def get_number_of_signature_arguments(self): class LiteralReturn(AbstractTrivialMethod): - _immutable_fields_ = ["_value"] + _immutable_fields_ = ["_value", "source_section"] - def __init__(self, signature, value): + def __init__(self, signature, value, source_section): AbstractTrivialMethod.__init__(self, signature, None) self._value = value + self.source_section = source_section def set_holder(self, value): self._holder = value @@ -80,6 +82,14 @@ def inline(self, _mgenc, merge_scope=True): # pylint: disable=unused-argument def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument emit_push_constant(mgenc, self._value) + def merge_scope_into(self, mgenc): + mgenc.inline_as_locals([Argument("$inlinedI", 1, self.source_section)]) + + def get_argument(self, idx, ctx_level): + if idx == 1 and ctx_level == 0: + return Argument("$inlinedI", idx, self.source_section) + raise Exception("This should not happen") + class GlobalRead(AbstractTrivialMethod): _immutable_fields_ = ["_assoc?", "_global_name", "_context_level", "universe"] From 0d0557c7e08cb1cef88e0e72f00b4214b119f9d0 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 9 Feb 2023 02:12:43 +0000 Subject: [PATCH 10/16] The literal to:do: as inlined in the AST interpreter needs to be able to handle double receivers Signed-off-by: Stefan Marr --- .../ast/nodes/specialized/literal_to_do.py | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/som/interpreter/ast/nodes/specialized/literal_to_do.py b/src/som/interpreter/ast/nodes/specialized/literal_to_do.py index 238d44ed..fba3ad9c 100644 --- a/src/som/interpreter/ast/nodes/specialized/literal_to_do.py +++ b/src/som/interpreter/ast/nodes/specialized/literal_to_do.py @@ -44,10 +44,13 @@ def __init__(self, from_expr, to_expr, do_expr, idx_var, source_section): def execute(self, frame): start = self._from_expr.execute(frame) - assert isinstance(start, Integer) - end = self._to_expr.execute(frame) + if isinstance(start, Double): + return self._execute_double(frame, start, end) + + assert isinstance(start, Integer) + if isinstance(end, Integer): end_int = end.get_embedded_integer() else: @@ -67,3 +70,24 @@ def execute(self, frame): i += 1 return start + + def _execute_double(self, frame, start, end): + if isinstance(end, Integer): + end_d = float(end.get_embedded_integer()) + else: + assert isinstance(end, Double) + end_d = end.get_embedded_double() + + if we_are_jitted(): + self._idx_write.write_value(frame, nilObject) + + i = start.get_embedded_double() + while i <= end_d: + driver.jit_merge_point(self=self) + self._idx_write.write_value(frame, Double(i)) + self._do_expr.execute(frame) + if we_are_jitted(): + self._idx_write.write_value(frame, nilObject) + i += 1.0 + + return start From 1c6feb8b72e14fb5c3a942b0a1c8bffae273e5ba Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 9 Feb 2023 09:39:56 +0000 Subject: [PATCH 11/16] Fix the scope merging for LiteralReturn in AST interpreter: TODO the other trivial methods needs similar fixes Signed-off-by: Stefan Marr --- src/som/vmobjects/method_trivial.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/som/vmobjects/method_trivial.py b/src/som/vmobjects/method_trivial.py index 594b282d..c3f70c2b 100644 --- a/src/som/vmobjects/method_trivial.py +++ b/src/som/vmobjects/method_trivial.py @@ -72,9 +72,11 @@ def invoke_n(self, stack, stack_ptr): if is_ast_interpreter(): - def inline(self, _mgenc, merge_scope=True): # pylint: disable=unused-argument + def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument from som.interpreter.ast.nodes.literal_node import LiteralNode + if merge_scope: + self.merge_scope_into(mgenc) return LiteralNode(self._value) else: From b35b16a73e35982ad17b1620de1d6d59b04d630b Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 9 Feb 2023 10:32:49 +0000 Subject: [PATCH 12/16] Support doubles in the inlined #to:do: in the BC interpreter Signed-off-by: Stefan Marr --- src/som/interpreter/bc/interpreter.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/som/interpreter/bc/interpreter.py b/src/som/interpreter/bc/interpreter.py index a506749f..1cf64167 100644 --- a/src/som/interpreter/bc/interpreter.py +++ b/src/som/interpreter/bc/interpreter.py @@ -16,7 +16,9 @@ from som.vm.globals import nilObject, trueObject, falseObject from som.vmobjects.array import Array from som.vmobjects.block_bc import BcBlock -from som.vmobjects.integer import int_0, int_1 +from som.vmobjects.integer import int_0, int_1, Integer +from som.vmobjects.double import Double + from rlib import jit from rlib.jit import promote, elidable_promote, we_are_jitted @@ -476,8 +478,6 @@ def interpret(method, frame, max_stack_size): elif bytecode == Bytecodes.inc: val = stack[stack_ptr] - from som.vmobjects.integer import Integer - from som.vmobjects.double import Double from som.vmobjects.biginteger import BigInteger if isinstance(val, Integer): @@ -492,8 +492,6 @@ def interpret(method, frame, max_stack_size): elif bytecode == Bytecodes.dec: val = stack[stack_ptr] - from som.vmobjects.integer import Integer - from som.vmobjects.double import Double from som.vmobjects.biginteger import BigInteger if isinstance(val, Integer): @@ -563,7 +561,8 @@ def interpret(method, frame, max_stack_size): elif bytecode == Bytecodes.jump_if_greater: top = stack[stack_ptr] top_2 = stack[stack_ptr - 1] - if top.get_embedded_integer() > top_2.get_embedded_integer(): + if ((isinstance(top, Integer) and isinstance(top_2, Integer) and top.get_embedded_integer() > top_2.get_embedded_integer()) + or (isinstance(top, Double) and isinstance(top_2, Double) and top.get_embedded_double() > top_2.get_embedded_double())): stack[stack_ptr] = None stack[stack_ptr - 1] = None stack_ptr -= 2 From 8607f0624ca7a70bd085dcfede0d43fa14a20b6c Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 9 Feb 2023 11:18:36 +0000 Subject: [PATCH 13/16] Missing source section [TODO] add test to find this Signed-off-by: Stefan Marr --- src/som/compiler/bc/method_generation_context.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/som/compiler/bc/method_generation_context.py b/src/som/compiler/bc/method_generation_context.py index 219e6aed..47f06741 100644 --- a/src/som/compiler/bc/method_generation_context.py +++ b/src/som/compiler/bc/method_generation_context.py @@ -510,6 +510,8 @@ def _assemble_global_return(self, return_candidate, push_candidate): global_name = self._literals[0] assert isinstance(global_name, Symbol) + source_section = self.lexical_scope.arguments[0].source + if global_name is sym_true: return LiteralReturn(self.signature, trueObject, source_section) if global_name is sym_false: From 4669a75ab0e24432ec9280868d3fb235d4acfbf6 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 9 Feb 2023 11:26:35 +0000 Subject: [PATCH 14/16] Fix formatting Signed-off-by: Stefan Marr --- src/som/interpreter/bc/interpreter.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/som/interpreter/bc/interpreter.py b/src/som/interpreter/bc/interpreter.py index 1cf64167..bc86b45f 100644 --- a/src/som/interpreter/bc/interpreter.py +++ b/src/som/interpreter/bc/interpreter.py @@ -561,8 +561,15 @@ def interpret(method, frame, max_stack_size): elif bytecode == Bytecodes.jump_if_greater: top = stack[stack_ptr] top_2 = stack[stack_ptr - 1] - if ((isinstance(top, Integer) and isinstance(top_2, Integer) and top.get_embedded_integer() > top_2.get_embedded_integer()) - or (isinstance(top, Double) and isinstance(top_2, Double) and top.get_embedded_double() > top_2.get_embedded_double())): + if ( + isinstance(top, Integer) + and isinstance(top_2, Integer) + and top.get_embedded_integer() > top_2.get_embedded_integer() + ) or ( + isinstance(top, Double) + and isinstance(top_2, Double) + and top.get_embedded_double() > top_2.get_embedded_double() + ): stack[stack_ptr] = None stack[stack_ptr - 1] = None stack_ptr -= 2 From 796667ed56c6c589a74731a6c4dd0ad78ff09cbb Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 9 Feb 2023 11:36:13 +0000 Subject: [PATCH 15/16] Needs separate JitDrivers for int and double Signed-off-by: Stefan Marr --- .../ast/nodes/specialized/literal_to_do.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/som/interpreter/ast/nodes/specialized/literal_to_do.py b/src/som/interpreter/ast/nodes/specialized/literal_to_do.py index fba3ad9c..d3e97f57 100644 --- a/src/som/interpreter/ast/nodes/specialized/literal_to_do.py +++ b/src/som/interpreter/ast/nodes/specialized/literal_to_do.py @@ -16,7 +16,14 @@ def get_printable_location(self): ) -driver = JitDriver( +driver_int = JitDriver( + greens=["self"], + reds="auto", + is_recursive=True, + get_printable_location=get_printable_location, +) + +driver_double = JitDriver( greens=["self"], reds="auto", is_recursive=True, @@ -62,7 +69,7 @@ def execute(self, frame): i = start.get_embedded_integer() while i <= end_int: - driver.jit_merge_point(self=self) + driver_int.jit_merge_point(self=self) self._idx_write.write_value(frame, Integer(i)) self._do_expr.execute(frame) if we_are_jitted(): @@ -83,7 +90,7 @@ def _execute_double(self, frame, start, end): i = start.get_embedded_double() while i <= end_d: - driver.jit_merge_point(self=self) + driver_double.jit_merge_point(self=self) self._idx_write.write_value(frame, Double(i)) self._do_expr.execute(frame) if we_are_jitted(): From ecdb4d8fadfc2374b22d711a670a150c8696e92c Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Thu, 9 Feb 2023 13:12:06 +0000 Subject: [PATCH 16/16] Added missing source section Signed-off-by: Stefan Marr --- src/som/compiler/ast/parser.py | 5 ++--- src/som/interpreter/ast/nodes/literal_node.py | 2 +- src/som/vmobjects/method_trivial.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/som/compiler/ast/parser.py b/src/som/compiler/ast/parser.py index 97e4c9ab..68cebeac 100644 --- a/src/som/compiler/ast/parser.py +++ b/src/som/compiler/ast/parser.py @@ -73,7 +73,7 @@ def _create_sequence_node(self, coordinate, expressions): if not expressions: from som.vm.globals import nilObject - return LiteralNode(nilObject) + return LiteralNode(nilObject, self._get_source_section(coordinate)) if len(expressions) == 1: return expressions[0] @@ -419,8 +419,7 @@ def _literal(self): coord = self._lexer.get_source_coordinate() val = self._get_object_for_current_literal() - lit = LiteralNode(val) - self._assign_source(lit, coord) + lit = LiteralNode(val, self._get_source_section(coord)) return lit def _get_object_for_current_literal(self): diff --git a/src/som/interpreter/ast/nodes/literal_node.py b/src/som/interpreter/ast/nodes/literal_node.py index dcff6cdf..74b9d8ed 100644 --- a/src/som/interpreter/ast/nodes/literal_node.py +++ b/src/som/interpreter/ast/nodes/literal_node.py @@ -4,7 +4,7 @@ class LiteralNode(ExpressionNode): _immutable_fields_ = ["_value"] - def __init__(self, value, source_section=None): + def __init__(self, value, source_section): ExpressionNode.__init__(self, source_section) self._value = value diff --git a/src/som/vmobjects/method_trivial.py b/src/som/vmobjects/method_trivial.py index c3f70c2b..50d9d0ec 100644 --- a/src/som/vmobjects/method_trivial.py +++ b/src/som/vmobjects/method_trivial.py @@ -77,7 +77,7 @@ def inline(self, mgenc, merge_scope=True): # pylint: disable=unused-argument if merge_scope: self.merge_scope_into(mgenc) - return LiteralNode(self._value) + return LiteralNode(self._value, self.source_section) else: