diff --git a/lib/mom/README.md b/lib/mom/README.md index d04e9346..93998794 100644 --- a/lib/mom/README.md +++ b/lib/mom/README.md @@ -38,43 +38,21 @@ So to put a layer in the middle of those two, MOM will be: ### Linked list -But, see below, in two steps +But, very much like Risc, just higher level so it's easier to understand ### Use object memory object to object transfer -no registers +no registers (one could see the current message as the only register) ### Instruction based -So a machine rather than a language. No control structures, but compare and jump instructions. +So mom is a machine layer, rather than a language. +No control structures, but compare and jump instructions. No send or call, just objects and jump. Again in two steps, see below Machine capabilities (instructions) for basic operations. Use of macros for higher level. - -## Two step approach - -To make the transition even easier, it is done in two steps. - -## 1. Everything but control structures - -wSo we go from language to machine as the first step, in terms of memory instructions. -Memory gets moved around between the main machine objects (frames and messages). -But control structures stay "intact", so we stay at tree structure - -## 2. Flattening control structures - -By flattening control structures and introducing jumps instead, we go from tree to linked -list of instructions. - -After this, it is quite trivial to translate to risc, as it mostly expands instructions. - -## The future - -I hope that in the future this simple 2 stage pipeline will expand into more steps. -This is the ideal layer to do code analysis and meaningful optimisations, as one can still -understand what is going on in higher terms. diff --git a/lib/mom/instruction/instruction.rb b/lib/mom/instruction/instruction.rb index 6933dff2..56f3d23d 100644 --- a/lib/mom/instruction/instruction.rb +++ b/lib/mom/instruction/instruction.rb @@ -29,8 +29,6 @@ require_relative "truth_check" require_relative "not_same_check" require_relative "jump" require_relative "slot_load" -require_relative "slot_move" -require_relative "slot_constant" require_relative "return_sequence" require_relative "message_setup" require_relative "argument_transfer" diff --git a/lib/mom/instruction/slot_constant.rb b/lib/mom/instruction/slot_constant.rb deleted file mode 100644 index 715d48b6..00000000 --- a/lib/mom/instruction/slot_constant.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Mom - # A SlotConstant moves a constant into a known Slot. - # Eg when you write a = 5 , the 5 becomes a constant, and so the right side - # the a is an instance variable on the current frame, and the frame is an instance - # of the current message, so the effect is something like message.frame.a = 5 - # @left: See SlotLoad, an array of symbols - # @right: A Constant from parse, ie an instance of classes in basc_value, like TrueConstant - class SlotConstant < SlotLoad - - def initialize(left , right) - super - raise "right not constant, #{right}" unless right.is_a? Mom::Constant - end - - def to_risc(context) - reg = context.use_reg( @right.ct_type) - const = Risc.load_constant(self, @right , reg) - const.set_next Risc.reg_to_slot(self, reg , @left.known_object, @left.slots.first) - context.release_reg(reg) - return const - end - - end - -end diff --git a/lib/mom/instruction/slot_load.rb b/lib/mom/instruction/slot_load.rb index 2711d4c1..32dfb7e4 100644 --- a/lib/mom/instruction/slot_load.rb +++ b/lib/mom/instruction/slot_load.rb @@ -3,9 +3,10 @@ module Mom # SlotLoad is an abstract base class for moving data into a slot # A Slot is basically an instance variable, but it must be of known type # - # The value loaded can be a constant (SlotConstant) or come from another Slot (SlotMove) + # The value loaded (the right hand side) can be a constant (Mom::Constant) or come from + # another Slot (SlotDefinition) # - # The Slot is the left hand side, the right hand side being determined by the subclass. + # The Slot on the left hand side is always a SlotDefinition. # The only known object (*) for the left side is the current message, which is a bit like # the oo version of a PC (program Counter) # (* off course all class objects are global, and so they are allowed too) @@ -17,25 +18,46 @@ module Mom # From the outside a send is neccessary, both for get and set, (which goes through the method # resolution and guarantees the correct method for a type), in other words perfect data hiding. # - # @left: is an array of symbols, that specifies the first the object, and then the Slot. - # The first element is either a known type name (Capitalized symbol of the class name) , - # or the symbol :message - # And subsequent symbols must be instance variables on the previous type. - # Examples: [:message , :receiver] or [:Space : :next_message] + # @left: A SlotDefinition, or an array that can be passed to the constructor of the + # SlotDefinition (see there) # - # @right: depends on the derived Class + # @right: Either a SlotDefinition or a Constant # class SlotLoad < Instruction attr_reader :left , :right def initialize(left , right) left = SlotDefinition.new(left.shift , left) if left.is_a? Array + right = SlotDefinition.new(right.shift , right) if right.is_a? Array + raise "right not Mom, #{right.to_s}" unless right.is_a?( SlotDefinition )or right.is_a? Mom::Constant @left , @right = left , right - raise "left not SlotDefinition, #{left}" unless left.is_a? SlotDefinition end + + def to_risc_load(context) + reg = context.use_reg( @right.ct_type) + const = Risc.load_constant(self, @right , reg) + const.set_next Risc.reg_to_slot(self, reg , @left.known_object, @left.slots.first) + context.release_reg(reg) + return const + end + + def to_risc_move(context) + reg = context.use_reg(:int)#( @right.ct_type) + const = Risc.load_constant(self, @right , reg) +# const.set_next Risc.reg_to_slot(self, reg , @left.known_object, @left.slots.first) +# context.release_reg(reg) + return const + end + + end class SlotDefinition attr_reader :known_object , :slots + # is an array of symbols, that specifies the first the object, and then the Slot. + # The first element is either a known type name (Capitalized symbol of the class name) , + # or the symbol :message + # And subsequent symbols must be instance variables on the previous type. + # Examples: [:message , :receiver] or [:Space : :next_message] def initialize( object , slots) @known_object , @slots = object , slots slot = [slot] unless slot.is_a?(Array) diff --git a/lib/mom/instruction/slot_move.rb b/lib/mom/instruction/slot_move.rb deleted file mode 100644 index ac7fe588..00000000 --- a/lib/mom/instruction/slot_move.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Mom - - #SlotMove is a SlotLoad where the right side is a slot, just like the left. - class SlotMove < SlotLoad - - def initialize(left , right) - right = SlotDefinition.new(right.shift , right) if right.is_a? Array - raise "right not Mom, #{right.to_s}" unless right.is_a?( SlotDefinition ) - super(left , right) - end - - def to_risc(context) - reg = context.use_reg(:int)#( @right.ct_type) - const = Risc.load_constant(self, @right , reg) -# const.set_next Risc.reg_to_slot(self, reg , @left.known_object, @left.slots.first) -# context.release_reg(reg) - return const - end - - end -end diff --git a/lib/mom/mom.rb b/lib/mom/mom.rb index c6c7cdc0..429851a6 100644 --- a/lib/mom/mom.rb +++ b/lib/mom/mom.rb @@ -1,13 +1,13 @@ -# The *essential* step from vool to risc, is the one from a language to a machine. From statements -# that hang in the air, to an instruction set. +# The *essential* step from vool to risc, is the one from a language to a machine. +# From vools statements that hang in the air, to an instruction set. # -# ### Tree based: So almost 1-1 from vool +# ### List based: Bit like Risc, just no registers # # ### Use object memory : object to object transfer + no registers # # ### Instruction based # -# So a machine than language. No control structures, but compare and jump instructions. +# So a machine rather than a language. No control structures, but compare and jump instructions. # No send or call, just objects and jump. # Machine capabilities (instructions) for basic operations. Use of macros for higher level. @@ -15,4 +15,3 @@ module Mom end require_relative "instruction/instruction.rb" -require_relative "statement/statement.rb" diff --git a/lib/mom/statement/if_statement.rb b/lib/mom/statement/if_statement.rb deleted file mode 100644 index b0f037c3..00000000 --- a/lib/mom/statement/if_statement.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Mom - class IfStatement < Statement - attr_reader :condition , :if_true , :if_false - - attr_accessor :hoisted - - def initialize( cond , if_true , if_false = nil) - @condition = cond - @if_true = if_true - @if_false = if_false - raise if_true.class unless if_true.is_a? Statement - end - - def flatten(options = {}) - true_label = Label.new( "true_label_#{object_id}") - false_label = Label.new( "false_label_#{object_id}") - merge_label = Label.new( "merge_label_#{object_id}") - first = condition.flatten( true_label: true_label , false_label: false_label) - if hoisted - head = hoisted.flatten - head.append first - else - head = first - end - head.append true_label - head.append if_true.flatten( merge_label: merge_label) - if( if_false) - head.append false_label - head.append if_false.flatten( merge_label: merge_label) - end - head.append merge_label - head - end - end - -end diff --git a/lib/mom/statement/statement.rb b/lib/mom/statement/statement.rb deleted file mode 100644 index c5379986..00000000 --- a/lib/mom/statement/statement.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Mom - class Statement - include Common::List - # flattening will change the structure from a tree to a linked list (and use - # nekst to do so) - def flatten(options = {}) - raise "not implemented for #{self}" - end - end - -end - -require_relative "statements" -require_relative "if_statement" -require_relative "while_statement" diff --git a/lib/mom/statement/statements.rb b/lib/mom/statement/statements.rb deleted file mode 100644 index 6ec80161..00000000 --- a/lib/mom/statement/statements.rb +++ /dev/null @@ -1,20 +0,0 @@ -module Mom - class Statements < Statement - include Common::Statements - - def flatten( options = {} ) - flat = @statements.shift.flatten(options) - while( nekst = @statements.shift ) - flat.append nekst.flatten(options) - end - flat - end - - def initialize(arr) - super(arr) - arr.each {|s| - raise "Not a Statement #{s}" unless s.is_a?( Statement) or s.is_a?(Instruction) - } - end - end -end diff --git a/lib/mom/statement/while_statement.rb b/lib/mom/statement/while_statement.rb deleted file mode 100644 index 4c801cd9..00000000 --- a/lib/mom/statement/while_statement.rb +++ /dev/null @@ -1,25 +0,0 @@ - -module Mom - class WhileStatement < Statement - attr_reader :condition , :statements - - attr_accessor :hoisted - - def initialize( cond , statements) - @condition = cond - @statements = statements - end - - def flatten(options = {}) - merge_label = Label.new( "merge_label_#{object_id}") - cond_label = Label.new( "cond_label_#{object_id}") - @nekst = cond_label - @nekst.append(hoisted.flatten) if hoisted - @nekst.append condition.flatten( true_label: cond_label , false_label: merge_label) - @nekst.append merge_label - @nekst - end - - end - -end diff --git a/lib/risc/method_compiler.rb b/lib/risc/method_compiler.rb index d61c562c..a027b5d7 100644 --- a/lib/risc/method_compiler.rb +++ b/lib/risc/method_compiler.rb @@ -93,6 +93,8 @@ module Risc # continue down the instruction chain unti depleted # (adding moves the insertion point so the whole mom chain is added as a risc chain) def add_mom( instruction ) + raise "whats this a #{instruction}" unless instruction.is_a?(Mom::Instruction) + return while( instruction ) risc = instruction.to_risc( self ) add_code(risc) diff --git a/lib/vool/statement.rb b/lib/vool/statement.rb index caab098e..eb028afb 100644 --- a/lib/vool/statement.rb +++ b/lib/vool/statement.rb @@ -47,6 +47,15 @@ module Vool def normalize raise "should not be normalized #{self}" end + def to_mom(method) + raise "should not be momed #{self}" + end + + # for loading into a lot, return the "slot_definition" that can be passed to + # SlotLoad. + def slot_definition(method) + raise "not iplemented in #{self}" + end end diff --git a/lib/vool/statements/assign_statement.rb b/lib/vool/statements/assign_statement.rb index fd456eb1..eb1c7cf8 100644 --- a/lib/vool/statements/assign_statement.rb +++ b/lib/vool/statements/assign_statement.rb @@ -10,23 +10,32 @@ module Vool raise "not named left #{name.class}" unless @name.is_a?(Symbol) raise "unsupported right #{value}" unless @value.is_a?(Named) or @value.is_a?(SendStatement) or @value.is_a?(Constant) - self end - def collect(arr) - @value.collect(arr) - super + def chain_assign(assign , method) + return assign unless @value.is_a?(SendStatement) + first = @value.to_mom(method) + first.next = assign + return first + end + + def each(&block) + block.call(self) + @value.each(&block) end end class IvarAssignment < Assignment - # used to collect type information - def add_ivar( array ) - array << @name + + def normalize() + super() + return IvarAssignment.new(@name , @value) end def to_mom( method ) - @value.slot_class.new([:message , :receiver , @name] , @value.to_mom(method)) + to = Mom::SlotDefinition.new(:message ,[ :receiver , @name]) + from = @value.slot_definition(method) + return chain_assign( Mom::SlotLoad.new(to,from) , method) end end diff --git a/lib/vool/statements/basic_values.rb b/lib/vool/statements/basic_values.rb index d50ab179..df13dfd3 100644 --- a/lib/vool/statements/basic_values.rb +++ b/lib/vool/statements/basic_values.rb @@ -1,13 +1,6 @@ module Vool - class Statement - def slot_class - Mom::SlotMove - end - end class Constant < Expression - def slot_class - Mom::SlotConstant - end + end class IntegerConstant < Constant @@ -15,12 +8,15 @@ module Vool def initialize(value) @value = value end - def to_mom(method) + def slot_definition(method) return Mom::IntegerConstant.new(@value) end def ct_type Parfait.object_space.get_class_by_name(:Integer).instance_type end + #gobble it up + def each(&block) + end end class FloatConstant < Constant attr_reader :value @@ -63,7 +59,7 @@ module Vool def initialize(value) @value = value end - def to_mom(method) + def slot_definition(method) return Mom::StringConstant.new(@value) end def ct_type diff --git a/lib/vool/statements/class_statement.rb b/lib/vool/statements/class_statement.rb index 6aaa04f2..83210e6e 100644 --- a/lib/vool/statements/class_statement.rb +++ b/lib/vool/statements/class_statement.rb @@ -5,7 +5,6 @@ module Vool def initialize( name , supe , body) @name , @super_class_name , @body = name , supe , body - @body = ScopeStatement.new([]) unless body end def normalize diff --git a/lib/vool/statements/if_statement.rb b/lib/vool/statements/if_statement.rb index 5d9c2ea0..6c1f5322 100644 --- a/lib/vool/statements/if_statement.rb +++ b/lib/vool/statements/if_statement.rb @@ -30,6 +30,26 @@ module Vool check end + def flatten(options = {}) + true_label = Label.new( "true_label_#{object_id}") + false_label = Label.new( "false_label_#{object_id}") + merge_label = Label.new( "merge_label_#{object_id}") + first = condition.flatten( true_label: true_label , false_label: false_label) + if hoisted + head = hoisted.flatten + head.append first + else + head = first + end + head.append true_label + head.append if_true.flatten( merge_label: merge_label) + if( if_false) + head.append false_label + head.append if_false.flatten( merge_label: merge_label) + end + head.append merge_label + head + end def collect(arr) @if_true.collect(arr) diff --git a/lib/vool/statements/local_assignment.rb b/lib/vool/statements/local_assignment.rb index 07dc1234..6f3409f7 100644 --- a/lib/vool/statements/local_assignment.rb +++ b/lib/vool/statements/local_assignment.rb @@ -1,9 +1,10 @@ module Vool class LocalAssignment < Assignment - # used to collect frame information - def add_local( array ) - array << @name + + def normalize + super + return LocalAssignment.new(@name , @value) end def to_mom( method ) @@ -12,9 +13,9 @@ module Vool else type = :frame end - statements = @value.to_mom(method) - statements << @value.slot_class.new([:message , type , @name] , @value.slot_definition) - return statements + to = Mom::SlotDefinition.new(:message ,[ type , @name]) + from = @value.slot_definition(method) + return chain_assign( Mom::SlotLoad.new(to,from) , method) end end diff --git a/lib/vool/statements/method_statement.rb b/lib/vool/statements/method_statement.rb index 2dabf704..eeae8c7d 100644 --- a/lib/vool/statements/method_statement.rb +++ b/lib/vool/statements/method_statement.rb @@ -4,7 +4,7 @@ module Vool def initialize( name , args , body , clazz = nil) @name , @args , @body = name , args , body - @body = ScopeStatement.new([]) unless body + raise "no bod" unless body @clazz = clazz end @@ -34,7 +34,7 @@ module Vool @clazz.add_method( method ) typed_method = method.create_parfait_method(clazz.instance_type) compiler = Risc::MethodCompiler.new( typed_method ).init_method - head = @body.to_mom( method ).flatten + head = @body.to_mom( method ) compiler.add_mom(head) end diff --git a/lib/vool/statements/statements.rb b/lib/vool/statements/statements.rb index 8828b75f..add491a1 100644 --- a/lib/vool/statements/statements.rb +++ b/lib/vool/statements/statements.rb @@ -4,8 +4,12 @@ module Vool # create machine instructions def to_mom( method ) - all = @statements.collect { |statement| statement.to_mom( method ) } - Mom::Statements.new(all) + raise "Empty list ? #{statements.length}" unless @statements[0] + flat = @statements.shift.to_mom(method) + while( nekst = @statements.shift ) + flat.append nekst.to_mom(method) + end + flat end def create_objects diff --git a/lib/vool/statements/variables.rb b/lib/vool/statements/variables.rb index 39280d4d..149a423e 100644 --- a/lib/vool/statements/variables.rb +++ b/lib/vool/statements/variables.rb @@ -4,11 +4,13 @@ module Vool def initialize name @name = name end + def each(&block) + end end class LocalVariable < Expression include Named - def to_mom(method) + def slot_definition(method) if method.args_type.variable_index(@name) type = :arguments else @@ -20,7 +22,7 @@ module Vool class InstanceVariable < Expression include Named - def to_mom(method) + def slot_definition(method) Mom::SlotDefinition.new(:message , [ :receiver , @name] ) end # used to collect type information diff --git a/lib/vool/statements/while_statement.rb b/lib/vool/statements/while_statement.rb index 0914a286..b075ce1d 100644 --- a/lib/vool/statements/while_statement.rb +++ b/lib/vool/statements/while_statement.rb @@ -27,6 +27,16 @@ module Vool check end + def flatten(options = {}) + merge_label = Label.new( "merge_label_#{object_id}") + cond_label = Label.new( "cond_label_#{object_id}") + @nekst = cond_label + @nekst.append(hoisted.flatten) if hoisted + @nekst.append condition.flatten( true_label: cond_label , false_label: merge_label) + @nekst.append merge_label + @nekst + end + def simplify_condition return unless @condition.is_a?(ScopeStatement) @condition = @condition.first if @condition.single? diff --git a/test/support/compiling.rb b/test/support/compiling.rb index bff51501..4961f937 100644 --- a/test/support/compiling.rb +++ b/test/support/compiling.rb @@ -28,7 +28,7 @@ module MomCompile assert_equal Parfait::VoolMethod , @method.class res = lst.to_mom( nil ) #puts "#{res.class}" - res.first + res end def compile_first_method_flat(input) diff --git a/test/vool/to_mom/test_assign.rb b/test/vool/to_mom/test_assign.rb index d37ef6d0..42be2071 100644 --- a/test/vool/to_mom/test_assign.rb +++ b/test/vool/to_mom/test_assign.rb @@ -7,29 +7,28 @@ module Vool def setup Risc.machine.boot @stats = compile_first_method( "local = 5") - @first = @stats.first end def test_class_compiles - assert_equal Mom::SlotConstant , @first.class , @stats + assert_equal Mom::SlotLoad , @stats.class , @stats end def test_slot_is_set - assert @first.left + assert @stats.left end def test_slot_starts_at_message - assert_equal :message , @first.left.known_object + assert_equal :message , @stats.left.known_object end def test_slot_gets_self - assert_equal :frame , @first.left.slots[0] + assert_equal :frame , @stats.left.slots[0] end def test_slot_assigns_to_local - assert_equal :local , @first.left.slots[-1] + assert_equal :local , @stats.left.slots[-1] end def test_slot_assigns_something - assert @stats.first.right + assert @stats.right end def test_slot_assigns_int - assert_equal Mom::IntegerConstant , @first.right.class + assert_equal Mom::IntegerConstant , @stats.right.class end end @@ -41,7 +40,7 @@ module Vool @stats = compile_first_method( "local = @a") end def test_class_compiles - assert_equal Mom::SlotMove , @stats.first.class , @stats + assert_equal Mom::SlotLoad , @stats.class , @stats end end @@ -52,23 +51,22 @@ module Vool def setup Risc.machine.boot @stats = compile_first_method( "arg = 5") - @first = @stats.first end def test_class_compiles - assert_equal Mom::SlotConstant , @first.class , @stats + assert_equal Mom::SlotLoad , @stats.class , @stats end def test_slot_is_set - assert @first.left + assert @stats.left end def test_slot_starts_at_message - assert_equal :message , @first.left.known_object + assert_equal :message , @stats.left.known_object end def test_slot_gets_self - assert_equal :arguments , @first.left.slots[0] + assert_equal :arguments , @stats.left.slots[0] end def test_slot_assigns_to_local - assert_equal :arg , @first.left.slots[-1] + assert_equal :arg , @stats.left.slots[-1] end end @@ -79,11 +77,13 @@ module Vool end def test_assigns_const @stats = compile_first_method( "@a = 5") - assert_equal Mom::SlotConstant , @stats.first.class , @stats + assert_equal Mom::SlotLoad , @stats.class , @stats + assert_equal Mom::IntegerConstant , @stats.right.class , @stats end def test_assigns_move @stats = compile_first_method( "@a = arg") - assert_equal Mom::SlotMove , @stats.first.class , @stats + assert_equal Mom::SlotLoad , @stats.class , @stats + assert_equal Mom::SlotDefinition , @stats.right.class , @stats end end