From 9867234c386249d3704571ef7ce8182828fd44d0 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Sun, 8 Apr 2018 18:51:20 +0300 Subject: [PATCH] move most code from method_compiler to builder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rather use builder in two ways, than sometimes compiler and sometimes builder Also makes it possible to reuse builtin code in mom’s to_risc, as both use builder. The builtin code by directly adding to compiler, the mom code not. --- lib/mom/instruction/dynamic_call.rb | 4 +- lib/risc/builder.rb | 105 +++++++++++++++++++++++----- lib/risc/method_compiler.rb | 82 ++-------------------- lib/risc/risc_value.rb | 8 +-- test/risc/test_builder.rb | 18 ++++- test/risc/test_risc_value.rb | 2 +- 6 files changed, 117 insertions(+), 102 deletions(-) diff --git a/lib/mom/instruction/dynamic_call.rb b/lib/mom/instruction/dynamic_call.rb index b27a0ce6..9280223b 100644 --- a/lib/mom/instruction/dynamic_call.rb +++ b/lib/mom/instruction/dynamic_call.rb @@ -2,12 +2,12 @@ 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 that we know this Method instance. + # a seperate instruction though, and here we assume that we know this Method instance. # # Both (to be called) Method instance and the type of receiver are stored as # variables here. The type is used to check before calling. # - # Setting up the method is not part of the instructions scope. That setup + # Setting up the method is not part of this instructions scope. That setup # includes the type check and any necccessay method resolution. # See vool send statement # diff --git a/lib/risc/builder.rb b/lib/risc/builder.rb index d7bb3947..a621fe99 100644 --- a/lib/risc/builder.rb +++ b/lib/risc/builder.rb @@ -1,13 +1,21 @@ module Risc + # A Builder is used to generate code, either by using it's api, or dsl + # + # The code that is generated can be added to the comiled method, ie to the compiler. + # This is used to generate the builtin methods. + # Or the code can be stored up and returned. This is used in Mom::to_risc methods class Builder attr_reader :built , :compiler # pass a compiler, to which instruction are added (usually) - # call build or build_and_return with a block - def initialize(compiler) + # second arg determines weather instructions are added (default true) + # call build with a block to build + def initialize(compiler, auto_add) @compiler = compiler + @auto_add = auto_add + @built = nil @names = {} end @@ -33,13 +41,13 @@ module Risc end def if_zero( label ) - add Risc::IsZero.new("jump if zero" , label) + add_code Risc::IsZero.new("jump if zero" , label) end def if_not_zero( label ) - add Risc::IsNotZero.new("jump if not zero" , label) + add_code Risc::IsNotZero.new("jump if not zero" , label) end def branch( label ) - add Risc::Branch.new("jump to" , label) + add_code Risc::Branch.new("jump to" , label) end # build code using dsl (see __init__ or MessageSetup for examples) @@ -52,29 +60,90 @@ module Risc # space << Parfait.object_space # load constant # message[:receiver] << space #make current message (r0) receiver the space # - # build result is available as built, but also gets added to compiler + # build result is available as built, but also gets added to compiler, if the + # builder is created with default args def build(&block) - risc = build_and_return(&block) - @compiler.add_code(risc) - risc - end - - # version of build that does not add to compiler, just returns the code - def build_and_return(&block) - @built = nil instance_eval(&block) - risc = @built - @built = nil - return risc + @built end - def add(ins) + # adding code to the builder either stores it in the built variable + # or adds it straight to the compiler. + # Depending on wether auto_add was given in construction. + def add_code(ins) + return @compiler.add_code(ins) if @auto_add if(@built) @built << ins else @built = ins end end + + # move a machine int from register "from" to a Parfait::Integer in register "to" + # have to grab an integer from space and stick it in the "to" register first. + def add_new_int( source , from, to ) + source += "add_new_int " + space = compiler.use_reg(:Space) + int = compiler.use_reg(:Integer) + space_i = Risc.resolve_to_index(:Space, :next_integer) + add_load_constant( source + "space" , Parfait.object_space , space ) + add_slot_to_reg( source + "next_i1" , space , space_i , to) + add_slot_to_reg( source + "next_i2" , to , Risc.resolve_to_index(:Integer, :next_integer) , int) + add_reg_to_slot( source + "store link" , int , space , space_i ) + add_reg_to_slot( source + "store value" , from , to , Parfait::Integer.integer_index) + end + + # load receiver and the first argument (int) + # return both registers + def self_and_int_arg( source ) + me = add_known( :receiver ) + int_arg = load_int_arg_at(source , 1 ) + return me , int_arg + end + + # Load the first argument, assumed to be integer + def load_int_arg_at( source , at) + int_arg = compiler.use_reg :Integer + add_slot_to_reg(source , :message , :arguments , int_arg ) + add_slot_to_reg(source , int_arg , at + 1, int_arg ) #1 for type + return int_arg + end + + # assumed Integer in given register is replaced by the fixnum that it is holding + def reduce_int( source , register ) + add_slot_to_reg( source + "int -> fix" , register , Parfait::Integer.integer_index , register) + end + + # for computationally building code (ie writing assembler) these short cuts + # help to instantiate risc instructions and add them immediately + [:label, :reg_to_slot , :slot_to_reg , :load_constant, :load_data, + :function_return , :function_call, :op , + :transfer , :reg_to_slot , :byte_to_reg , :reg_to_byte].each do |method| + define_method("add_#{method}".to_sym) do |*args| + add_code Risc.send( method , *args ) + end + end + + def add_known(name) + case name + when :receiver + ret = compiler.use_reg compiler.type + add_slot_to_reg(" load self" , :message , :receiver , ret ) + return ret + when :space + space = Parfait.object_space + reg = compiler.use_reg :Space , space + add_load_constant( "load space", space , reg ) + return reg + when :message + reg = compiler.use_reg :Message + add_transfer( "load message", Risc.message_reg , reg ) + return reg + else + raise "Unknow expression #{name}" + end + end + end # if a symbol is given, it may be the message or the new_message. diff --git a/lib/risc/method_compiler.rb b/lib/risc/method_compiler.rb index 576286c7..2d825cc4 100644 --- a/lib/risc/method_compiler.rb +++ b/lib/risc/method_compiler.rb @@ -41,31 +41,6 @@ module Risc self.new(method) end - def add_known(name) - case name - when :receiver - ret = use_reg @type - add_slot_to_reg(" load self" , :message , :receiver , ret ) - return ret - when :space - space = Parfait.object_space - reg = use_reg :Space , space - add_load_constant( "load space", space , reg ) - return reg - when :message - reg = use_reg :Message - add_transfer( "load message", Risc.message_reg , reg ) - return reg - else - raise "Unknow expression #{name}" - end - end - - # set the insertion point (where code is added with add_code) - def set_current c - @current = c - end - # convert the given mom instruction to_risc and then add it (see add_code) # continue down the instruction chain unti depleted # (adding moves the insertion point so the whole mom chain is added as a risc chain) @@ -80,6 +55,10 @@ module Risc end end + def add_constant(const) + Risc.machine.add_constant(const) + end + # add a risc instruction after the current (insertion point) # the added instruction will become the new insertion point def add_code( instruction ) @@ -91,16 +70,6 @@ module Risc self end - # for computationally building code (ie writing assembler) these short cuts - # help to instantiate risc instructions and add them immediately - [:label, :reg_to_slot , :slot_to_reg , :load_constant, :load_data, - :function_return , :function_call, :op , - :transfer , :reg_to_slot , :byte_to_reg , :reg_to_byte].each do |method| - define_method("add_#{method}".to_sym) do |*args| - add_code Risc.send( method , *args ) - end - end - # require a (temporary) register. code must give this back with release_reg def use_reg( type , value = nil ) raise "Not type #{type.inspect}" unless type.is_a?(Symbol) or type.is_a?(Parfait::Type) @@ -133,52 +102,15 @@ module Risc @regs.clear end - # move a machine int from register "from" to a Parfait::Integer in register "to" - # have to grab an integer from space and stick it in the "to" register first. - def add_new_int( source , from, to ) - source += "add_new_int " - space = use_reg(:Space) - int = use_reg(:Integer) - space_i = Risc.resolve_to_index(:Space, :next_integer) - add_load_constant( source + "space" , Parfait.object_space , space ) - add_slot_to_reg( source + "next_i1" , space , space_i , to) - add_slot_to_reg( source + "next_i2" , to , Risc.resolve_to_index(:Integer, :next_integer) , int) - add_reg_to_slot( source + "store link" , int , space , space_i ) - add_reg_to_slot( source + "store value" , from , to , Parfait::Integer.integer_index) - end - def add_constant(const) - Risc.machine.add_constant(const) - end - - # load receiver and the first argument (int) - # return both registers - def self_and_int_arg( source ) - me = add_known( :receiver ) - int_arg = load_int_arg_at(source , 1 ) - return me , int_arg - end - - # Load the first argument, assumed to be integer - def load_int_arg_at( source , at) - int_arg = use_reg :Integer - add_slot_to_reg(source , :message , :arguments , int_arg ) - add_slot_to_reg(source , int_arg , at + 1, int_arg ) #1 for type - return int_arg - end - - # assumed Integer in given register is replaced by the fixnum that it is holding - def reduce_int( source , register ) - add_slot_to_reg( source + "int -> fix" , register , Parfait::Integer.integer_index , register) - end - # Build with builder (see there), adding the created instructions def build(&block) builder.build(&block) end # return a new builder that uses this compiler - def builder - Builder.new(self) + # must specify weather to add code automatically to compiler + def builder( auto_add ) + Builder.new(self , auto_add) end end end diff --git a/lib/risc/risc_value.rb b/lib/risc/risc_value.rb index 8e9edd60..196aa0bc 100644 --- a/lib/risc/risc_value.rb +++ b/lib/risc/risc_value.rb @@ -73,17 +73,17 @@ module Risc else raise "not implemented" end - builder.add(ins) if builder + builder.add_code(ins) if builder return ins end def -( right ) raise "operators only on registers, not #{right.class}" unless right.is_a? RiscValue op = Risc.op("#{self.type} - #{right.type}", :- , self , right ) - builder.add(op) if builder + builder.add_code(op) if builder op end - + # just capture the values in an intermediary object (RValue) # The RValue then gets used in a RegToSlot ot SlotToReg, where # the values are unpacked to call Risc.reg_to_slot or Risc.slot_to_reg @@ -105,7 +105,7 @@ module Risc def <<( reg ) raise "not reg #{reg}" unless reg.is_a?(RiscValue) reg_to_slot = Risc.reg_to_slot("#{reg.type} -> #{register.type}[#{index}]" , reg , register, index) - builder.add(reg_to_slot) if builder + builder.add_code(reg_to_slot) if builder reg_to_slot end diff --git a/test/risc/test_builder.rb b/test/risc/test_builder.rb index f3e0248d..2bff05f3 100644 --- a/test/risc/test_builder.rb +++ b/test/risc/test_builder.rb @@ -1,13 +1,13 @@ require_relative "../helper" module Risc - class TestBuilderBoot < MiniTest::Test + class TestBuilderFalse < MiniTest::Test def setup Risc.machine.boot init = Parfait.object_space.get_init + @builder = Risc::MethodCompiler.new( init ).builder(false) @label = Risc::Label.new("source","name") - @builder = Risc::MethodCompiler.new( init ).builder end def test_has_build assert @builder.respond_to?(:build) @@ -97,4 +97,18 @@ module Risc assert_equal :Space , op.left.type end end + class TestBuilderTrue < MiniTest::Test + + def setup + Risc.machine.boot + @init = Parfait.object_space.get_init + @builder = Risc::MethodCompiler.new( @init ).builder(true) + end + def test_inserts_built + r1 = RiscValue.new(:r1 , :Space) + @builder.build{ space << r1 } + assert_equal Transfer , @init.risc_instructions.next.class , @init.risc_instructions.next + end + + end end diff --git a/test/risc/test_risc_value.rb b/test/risc/test_risc_value.rb index 3e64566a..cd32e9ca 100644 --- a/test/risc/test_risc_value.rb +++ b/test/risc/test_risc_value.rb @@ -2,7 +2,7 @@ require_relative "../helper" class FakeBuilder attr_reader :built - def add(ins) + def add_code(ins) @built = ins end end