collapsed slot classes into one

different slot operation have different right sides
mom assignment tests work again
157 others don’t
This commit is contained in:
Torsten Ruger 2018-03-15 20:33:38 +05:30
parent 3247c2036c
commit 79bf416e58
23 changed files with 140 additions and 233 deletions

View File

@ -38,43 +38,21 @@ So to put a layer in the middle of those two, MOM will be:
### Linked list ### 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 ### Use object memory
object to object transfer object to object transfer
no registers no registers (one could see the current message as the only register)
### Instruction based ### 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. No send or call, just objects and jump.
Again in two steps, see below Again in two steps, see below
Machine capabilities (instructions) for basic operations. Use of macros for higher level. 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.

View File

@ -29,8 +29,6 @@ require_relative "truth_check"
require_relative "not_same_check" require_relative "not_same_check"
require_relative "jump" require_relative "jump"
require_relative "slot_load" require_relative "slot_load"
require_relative "slot_move"
require_relative "slot_constant"
require_relative "return_sequence" require_relative "return_sequence"
require_relative "message_setup" require_relative "message_setup"
require_relative "argument_transfer" require_relative "argument_transfer"

View File

@ -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

View File

@ -3,9 +3,10 @@ module Mom
# SlotLoad is an abstract base class for moving data into a slot # 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 # 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 only known object (*) for the left side is the current message, which is a bit like
# the oo version of a PC (program Counter) # the oo version of a PC (program Counter)
# (* off course all class objects are global, and so they are allowed too) # (* 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 # 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. # 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. # @left: A SlotDefinition, or an array that can be passed to the constructor of the
# The first element is either a known type name (Capitalized symbol of the class name) , # SlotDefinition (see there)
# or the symbol :message
# And subsequent symbols must be instance variables on the previous type.
# Examples: [:message , :receiver] or [:Space : :next_message]
# #
# @right: depends on the derived Class # @right: Either a SlotDefinition or a Constant
# #
class SlotLoad < Instruction class SlotLoad < Instruction
attr_reader :left , :right attr_reader :left , :right
def initialize(left , right) def initialize(left , right)
left = SlotDefinition.new(left.shift , left) if left.is_a? Array 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 @left , @right = left , right
raise "left not SlotDefinition, #{left}" unless left.is_a? SlotDefinition
end 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 end
class SlotDefinition class SlotDefinition
attr_reader :known_object , :slots 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) def initialize( object , slots)
@known_object , @slots = object , slots @known_object , @slots = object , slots
slot = [slot] unless slot.is_a?(Array) slot = [slot] unless slot.is_a?(Array)

View File

@ -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

View File

@ -1,13 +1,13 @@
# The *essential* step from vool to risc, is the one from a language to a machine. From statements # The *essential* step from vool to risc, is the one from a language to a machine.
# that hang in the air, to an instruction set. # 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 # ### Use object memory : object to object transfer + no registers
# #
# ### Instruction based # ### 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. # No send or call, just objects and jump.
# Machine capabilities (instructions) for basic operations. Use of macros for higher level. # Machine capabilities (instructions) for basic operations. Use of macros for higher level.
@ -15,4 +15,3 @@ module Mom
end end
require_relative "instruction/instruction.rb" require_relative "instruction/instruction.rb"
require_relative "statement/statement.rb"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -93,6 +93,8 @@ module Risc
# continue down the instruction chain unti depleted # continue down the instruction chain unti depleted
# (adding moves the insertion point so the whole mom chain is added as a risc chain) # (adding moves the insertion point so the whole mom chain is added as a risc chain)
def add_mom( instruction ) def add_mom( instruction )
raise "whats this a #{instruction}" unless instruction.is_a?(Mom::Instruction)
return
while( instruction ) while( instruction )
risc = instruction.to_risc( self ) risc = instruction.to_risc( self )
add_code(risc) add_code(risc)

View File

@ -47,6 +47,15 @@ module Vool
def normalize def normalize
raise "should not be normalized #{self}" raise "should not be normalized #{self}"
end 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 end

View File

@ -10,23 +10,32 @@ module Vool
raise "not named left #{name.class}" unless @name.is_a?(Symbol) raise "not named left #{name.class}" unless @name.is_a?(Symbol)
raise "unsupported right #{value}" unless @value.is_a?(Named) or raise "unsupported right #{value}" unless @value.is_a?(Named) or
@value.is_a?(SendStatement) or @value.is_a?(Constant) @value.is_a?(SendStatement) or @value.is_a?(Constant)
self
end end
def collect(arr) def chain_assign(assign , method)
@value.collect(arr) return assign unless @value.is_a?(SendStatement)
super first = @value.to_mom(method)
first.next = assign
return first
end
def each(&block)
block.call(self)
@value.each(&block)
end end
end end
class IvarAssignment < Assignment class IvarAssignment < Assignment
# used to collect type information
def add_ivar( array ) def normalize()
array << @name super()
return IvarAssignment.new(@name , @value)
end end
def to_mom( method ) 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
end end

View File

@ -1,13 +1,6 @@
module Vool module Vool
class Statement
def slot_class
Mom::SlotMove
end
end
class Constant < Expression class Constant < Expression
def slot_class
Mom::SlotConstant
end
end end
class IntegerConstant < Constant class IntegerConstant < Constant
@ -15,12 +8,15 @@ module Vool
def initialize(value) def initialize(value)
@value = value @value = value
end end
def to_mom(method) def slot_definition(method)
return Mom::IntegerConstant.new(@value) return Mom::IntegerConstant.new(@value)
end end
def ct_type def ct_type
Parfait.object_space.get_class_by_name(:Integer).instance_type Parfait.object_space.get_class_by_name(:Integer).instance_type
end end
#gobble it up
def each(&block)
end
end end
class FloatConstant < Constant class FloatConstant < Constant
attr_reader :value attr_reader :value
@ -63,7 +59,7 @@ module Vool
def initialize(value) def initialize(value)
@value = value @value = value
end end
def to_mom(method) def slot_definition(method)
return Mom::StringConstant.new(@value) return Mom::StringConstant.new(@value)
end end
def ct_type def ct_type

View File

@ -5,7 +5,6 @@ module Vool
def initialize( name , supe , body) def initialize( name , supe , body)
@name , @super_class_name , @body = name , supe , body @name , @super_class_name , @body = name , supe , body
@body = ScopeStatement.new([]) unless body
end end
def normalize def normalize

View File

@ -30,6 +30,26 @@ module Vool
check check
end 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) def collect(arr)
@if_true.collect(arr) @if_true.collect(arr)

View File

@ -1,9 +1,10 @@
module Vool module Vool
class LocalAssignment < Assignment class LocalAssignment < Assignment
# used to collect frame information
def add_local( array ) def normalize
array << @name super
return LocalAssignment.new(@name , @value)
end end
def to_mom( method ) def to_mom( method )
@ -12,9 +13,9 @@ module Vool
else else
type = :frame type = :frame
end end
statements = @value.to_mom(method) to = Mom::SlotDefinition.new(:message ,[ type , @name])
statements << @value.slot_class.new([:message , type , @name] , @value.slot_definition) from = @value.slot_definition(method)
return statements return chain_assign( Mom::SlotLoad.new(to,from) , method)
end end
end end

View File

@ -4,7 +4,7 @@ module Vool
def initialize( name , args , body , clazz = nil) def initialize( name , args , body , clazz = nil)
@name , @args , @body = name , args , body @name , @args , @body = name , args , body
@body = ScopeStatement.new([]) unless body raise "no bod" unless body
@clazz = clazz @clazz = clazz
end end
@ -34,7 +34,7 @@ module Vool
@clazz.add_method( method ) @clazz.add_method( method )
typed_method = method.create_parfait_method(clazz.instance_type) typed_method = method.create_parfait_method(clazz.instance_type)
compiler = Risc::MethodCompiler.new( typed_method ).init_method compiler = Risc::MethodCompiler.new( typed_method ).init_method
head = @body.to_mom( method ).flatten head = @body.to_mom( method )
compiler.add_mom(head) compiler.add_mom(head)
end end

View File

@ -4,8 +4,12 @@ module Vool
# create machine instructions # create machine instructions
def to_mom( method ) def to_mom( method )
all = @statements.collect { |statement| statement.to_mom( method ) } raise "Empty list ? #{statements.length}" unless @statements[0]
Mom::Statements.new(all) flat = @statements.shift.to_mom(method)
while( nekst = @statements.shift )
flat.append nekst.to_mom(method)
end
flat
end end
def create_objects def create_objects

View File

@ -4,11 +4,13 @@ module Vool
def initialize name def initialize name
@name = name @name = name
end end
def each(&block)
end
end end
class LocalVariable < Expression class LocalVariable < Expression
include Named include Named
def to_mom(method) def slot_definition(method)
if method.args_type.variable_index(@name) if method.args_type.variable_index(@name)
type = :arguments type = :arguments
else else
@ -20,7 +22,7 @@ module Vool
class InstanceVariable < Expression class InstanceVariable < Expression
include Named include Named
def to_mom(method) def slot_definition(method)
Mom::SlotDefinition.new(:message , [ :receiver , @name] ) Mom::SlotDefinition.new(:message , [ :receiver , @name] )
end end
# used to collect type information # used to collect type information

View File

@ -27,6 +27,16 @@ module Vool
check check
end 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 def simplify_condition
return unless @condition.is_a?(ScopeStatement) return unless @condition.is_a?(ScopeStatement)
@condition = @condition.first if @condition.single? @condition = @condition.first if @condition.single?

View File

@ -28,7 +28,7 @@ module MomCompile
assert_equal Parfait::VoolMethod , @method.class assert_equal Parfait::VoolMethod , @method.class
res = lst.to_mom( nil ) res = lst.to_mom( nil )
#puts "#{res.class}" #puts "#{res.class}"
res.first res
end end
def compile_first_method_flat(input) def compile_first_method_flat(input)

View File

@ -7,29 +7,28 @@ module Vool
def setup def setup
Risc.machine.boot Risc.machine.boot
@stats = compile_first_method( "local = 5") @stats = compile_first_method( "local = 5")
@first = @stats.first
end end
def test_class_compiles def test_class_compiles
assert_equal Mom::SlotConstant , @first.class , @stats assert_equal Mom::SlotLoad , @stats.class , @stats
end end
def test_slot_is_set def test_slot_is_set
assert @first.left assert @stats.left
end end
def test_slot_starts_at_message def test_slot_starts_at_message
assert_equal :message , @first.left.known_object assert_equal :message , @stats.left.known_object
end end
def test_slot_gets_self def test_slot_gets_self
assert_equal :frame , @first.left.slots[0] assert_equal :frame , @stats.left.slots[0]
end end
def test_slot_assigns_to_local def test_slot_assigns_to_local
assert_equal :local , @first.left.slots[-1] assert_equal :local , @stats.left.slots[-1]
end end
def test_slot_assigns_something def test_slot_assigns_something
assert @stats.first.right assert @stats.right
end end
def test_slot_assigns_int def test_slot_assigns_int
assert_equal Mom::IntegerConstant , @first.right.class assert_equal Mom::IntegerConstant , @stats.right.class
end end
end end
@ -41,7 +40,7 @@ module Vool
@stats = compile_first_method( "local = @a") @stats = compile_first_method( "local = @a")
end end
def test_class_compiles def test_class_compiles
assert_equal Mom::SlotMove , @stats.first.class , @stats assert_equal Mom::SlotLoad , @stats.class , @stats
end end
end end
@ -52,23 +51,22 @@ module Vool
def setup def setup
Risc.machine.boot Risc.machine.boot
@stats = compile_first_method( "arg = 5") @stats = compile_first_method( "arg = 5")
@first = @stats.first
end end
def test_class_compiles def test_class_compiles
assert_equal Mom::SlotConstant , @first.class , @stats assert_equal Mom::SlotLoad , @stats.class , @stats
end end
def test_slot_is_set def test_slot_is_set
assert @first.left assert @stats.left
end end
def test_slot_starts_at_message def test_slot_starts_at_message
assert_equal :message , @first.left.known_object assert_equal :message , @stats.left.known_object
end end
def test_slot_gets_self def test_slot_gets_self
assert_equal :arguments , @first.left.slots[0] assert_equal :arguments , @stats.left.slots[0]
end end
def test_slot_assigns_to_local def test_slot_assigns_to_local
assert_equal :arg , @first.left.slots[-1] assert_equal :arg , @stats.left.slots[-1]
end end
end end
@ -79,11 +77,13 @@ module Vool
end end
def test_assigns_const def test_assigns_const
@stats = compile_first_method( "@a = 5") @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 end
def test_assigns_move def test_assigns_move
@stats = compile_first_method( "@a = arg") @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
end end