From 2739747453220bb30d94e9d681bca0e783014275 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Thu, 14 Sep 2017 16:07:02 +0300 Subject: [PATCH] a start on dynamic dispatch (wip) --- lib/mom/dynamic_call.rb | 25 ++++++++++ lib/mom/instruction.rb | 2 + lib/mom/not_same_check.rb | 16 ++++++ lib/vool/statements/send_statement.rb | 50 +++++++++++++++++-- .../to_mom/send/test_send_cached_simple.rb | 6 +-- 5 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 lib/mom/dynamic_call.rb create mode 100644 lib/mom/not_same_check.rb diff --git a/lib/mom/dynamic_call.rb b/lib/mom/dynamic_call.rb new file mode 100644 index 00000000..196ca7a5 --- /dev/null +++ b/lib/mom/dynamic_call.rb @@ -0,0 +1,25 @@ +module Mom + + # A dynamic call calls a method at runtime. This off course implies that we don't know the + # method at compile time and so must "find" it. Resolving, or finding the method, is a + # a seperate step though, and here we assume there is a Method instance in some variable + # + # The only argument given is the variable's name. + # The instruction thus load the variable, finds the jump address from it and jumps there + # (ie calls). Calls are after all just jumps with the intent to return. Return addresses + # are setup in the preamble. + # + # As a side note: All argument setup/handling is outside the scope of this Instruction + # and assumed to be done beforehand. + # Also, in an ideal world we would check that the variable actually holds a Method + # but at the momeent we just assume it. + # + class DynamicCall < Instruction + attr_reader :method_var_name + + def initialize(method_var_name) + @method_var_name = method_var_name + end + end + +end diff --git a/lib/mom/instruction.rb b/lib/mom/instruction.rb index fe995d23..c4b6ac3f 100644 --- a/lib/mom/instruction.rb +++ b/lib/mom/instruction.rb @@ -20,7 +20,9 @@ module Mom end require_relative "simple_call" +require_relative "dynamic_call" require_relative "truth_check" +require_relative "not_same_check" require_relative "jump" require_relative "slot_load" require_relative "return_sequence" diff --git a/lib/mom/not_same_check.rb b/lib/mom/not_same_check.rb new file mode 100644 index 00000000..43efb855 --- /dev/null +++ b/lib/mom/not_same_check.rb @@ -0,0 +1,16 @@ +module Mom + + # Mom internal check, as the name says to see if two values are not the same + # In other words, we this checks identity, bit-values, pointers + # + # The values that are compared are defined as SlotDefinitions, ie can be anything + # available to the machine through frame message or self + # + class NotSameCheck < Check + attr_reader :left , :right + + def initialize(left, right) + @left , @right = left , right + end + end +end diff --git a/lib/vool/statements/send_statement.rb b/lib/vool/statements/send_statement.rb index 7af19a23..7b91a626 100644 --- a/lib/vool/statements/send_statement.rb +++ b/lib/vool/statements/send_statement.rb @@ -28,7 +28,7 @@ module Vool # # A Send breaks down to 2 steps: # - Setting up the next message, with receiver, arguments, and (importantly) return address - # - a CachedCall , or a SimpleCall, depending on weather the receiver type can be determined + # - a CachedCall , or a SimpleCall, depending on wether the receiver type can be determined # # FIXME: we now presume direct (assignable) values for the arguments and receiver. # in a not so distant future, temporary variables will have to be created @@ -59,11 +59,51 @@ module Vool Mom::Statements.new( message_setup(in_method) << Mom::SimpleCall.new( called_method) ) end - def cached_call(method) - raise "Not implemented #{method}" - Mom::Statements.new( message_setup(method) + call_instruction ) - [@receiver.slot_class.new([:message , :next_message , :receiver] , @receiver) ] + # this breaks cleanly into two parts: + # - check the cached type and if neccessary update + # - call the cached method + def cached_call(in_method) + create_tmps(in_method) + Mom::Statements.new( cache_check(in_method) + call_cached_method(in_method) ) end + def flatten + raise "flat" + end + # check that current type is the cached type + # if not, change and find method for the type (simple_call to resolve_method) + # conceptually easy in ruby, but we have to compile that "easy" ruby + def cache_check(in_method) + # return if cached_type == current_type + # cached_type = current_type + # cached_method = current_type.resolve_method(method.name) + check = [] + cached_type = Mom::SlotDefinition.new(:message , [:frame , type_var_name]) + current_type = Mom::SlotDefinition.new(:message , [:self , :type]) + cond = Mom::NotSameCheck.new(cached_type , current_type) + if_true = nil #@if_true.to_mom( in_method ) #find and assign + check << Mom::IfStatement.new( cond , if_true ) + check + end + + # this may look like a simple_call, but the difference is that we don't know + # the method until run-time. Alas the setup is the same + def call_cached_method(in_method) + message_setup(in_method) << Mom::DynamicCall.new( method_var_name) + end + private + # cached type and method are stored in the frame as local variables. + # this creates the varables in the frame. Names are method_var_name and type_var_name + def create_tmps(in_method) + in_method.create_tmp + end + # we store the (one!) cached mathod in the frame, under the name that this + # method returns + def method_var_name + "cached_method_#{object_id}" + end + def type_var_name + "cached_type_#{object_id}" + end end end diff --git a/test/vool/to_mom/send/test_send_cached_simple.rb b/test/vool/to_mom/send/test_send_cached_simple.rb index 0c00f16c..e8360c80 100644 --- a/test/vool/to_mom/send/test_send_cached_simple.rb +++ b/test/vool/to_mom/send/test_send_cached_simple.rb @@ -10,11 +10,11 @@ module Vool @first = @stats.first end - def est_compiles_not_array + def test_compiles_not_array assert Array != @stats.class , @stats end - def est_class_compiles - assert_equal Mom::SlotConstant , @first.class , @stats + def test_class_compiles + assert_equal Mom::IfStatement , @first.class , @stats end def est_slot_is_set assert @stats.first.left