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

View File

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

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
# 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)

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

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
# (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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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