removed the (too) fancy dsl. Also introduce register indirection

This commit is contained in:
Torsten Ruger
2014-06-07 17:59:44 +03:00
parent 6433a394e7
commit 36f237c633
26 changed files with 128 additions and 161 deletions

View File

@ -32,14 +32,8 @@ module Vm
attr_reader :name , :next , :codes , :function
def add_code(kode)
if kode.is_a? Hash
raise "Hack only for 1 element #{inspect} #{kode.inspect}" unless kode.length == 1
instruction , result = kode.first
instruction.assign result
kode = instruction
end
raise "alarm #{kode}" if kode.is_a? Word
raise "alarm #{kode}" unless kode.is_a? Code
raise "alarm #{kode.class} #{kode}" unless kode.is_a? Code
@insert_at.codes << kode
self
end
@ -70,27 +64,8 @@ module Vm
self
end
# to use the assignment syntax (see method_missing) the scope must be set, so variables can be resolved
# The scope you set should be a binding (literally, the kernel.binding)
# The function return the block, so it can be chained into an assignment
# Example (coding a function ) and having variable int defined
# b = function.body.scope(binding)
# b.int = 5 will create a mov instruction to set the register that int points to
def scope where
@scope = where
self
end
# sugar to create instructions easily. Actually just got double sweet with two versions:
# 1 for any method that ends in = we evaluate the method name in the current scope (see scope())
# for the result we call assign with the right value. The resulting instruction is added to
# the block.
# Thus we emulate assignment,
# Example: block b
# b.variable = value looks like what it does, but actually generates
# an instruction for the block (mov or add)
#
# 2- any other method will be passed on to the RegisterMachine and the result added to the block
# sugar to create instructions easily.
# any method will be passed on to the RegisterMachine and the result added to the block
# With this trick we can write what looks like assembler,
# Example b.instance_eval
# mov( r1 , r2 )
@ -98,16 +73,8 @@ module Vm
# end
# mov and add will be called on Machine and generate Inststuction that are then added
# to the block
# also symbols are supported and wrapped as register usages (for bare metal programming)
def method_missing(meth, *args, &block)
var = meth.to_s[0 ... -1]
if( args.length == 1) and ( meth.to_s[-1] == "=" )
if @scope.local_variable_defined? var.to_sym
l_val = @scope.local_variable_get var.to_sym
return add_code l_val.assign(args[0])
else
return super
end
end
add_code RegisterMachine.instance.send(meth , *args)
end

View File

@ -3,7 +3,7 @@ require_relative "meta_class"
module Vm
# class is mainly a list of functions with a name (for now)
# layout of object is seperated into Layout
class BootClass < Code
class BootClass < ObjectConstant
def initialize name , context , super_class = :Object
super()
@context = context

View File

@ -9,15 +9,23 @@ module Vm
@value = value
@args = args
@function = function
raise "oh #{name} " unless value
end
attr_reader :function , :args , :name , :value
def load_args into
if value.is_a?(IntegerConstant) or value.is_a?(ObjectConstant)
function.receiver.load into , value
else
raise "meta #{name} " if value.is_a? MetaClass
function.receiver.move( into, value ) if value.register_symbol != function.receiver.register_symbol
end
raise "function call '#{name}' has #{args.length} arguments, but function has #{function.args.length}" if args.length != function.args.length
args.each_with_index do |arg , index|
if arg.is_a?(IntegerConstant) or arg.is_a?(StringConstant)
function.args[index].load into , arg
else
function.args[index].move( into, arg ) if arg.register != function.args[index].register
function.args[index].move( into, arg ) if arg.register_symbol != function.args[index].register_symbol
end
end
end

View File

@ -43,5 +43,6 @@ module Vm
def assemble(io)
raise "Not implemented #{self.inspect}"
end
end
end

View File

@ -9,6 +9,10 @@ module Vm
end
# another abstract "marker" class (so we can check for it)
# derived classes are Boot/Meta Clas and StringConstant
class ObjectConstant < Constant
end
class IntegerConstant < Constant
def initialize int
@ -25,7 +29,7 @@ module Vm
# Currently string are stored "inline" , ie in the code segment.
# Mainly because that works an i aint no elf expert.
class StringConstant < Constant
class StringConstant < ObjectConstant
attr_reader :string
# currently aligned to 4 (ie padded with 0) and off course 0 at the end
def initialize str

View File

@ -27,16 +27,16 @@ module Vm
class Function < Code
TYPE_REG = 0
RECEIVER_REG = 1
RETURN_REG = 7
TYPE_REG = :r0
RECEIVER_REG = :r1
RETURN_REG = :r7
def initialize(name , receiver = Vm::Integer , args = [] , return_type = Vm::Integer)
super()
@name = name.to_sym
if receiver.is_a?(Value)
@receiver = receiver
raise "arg in non std register #{arg.inspect}" unless RECEIVER_REG == receiver.register
raise "arg in non std register #{arg.inspect}" unless RECEIVER_REG == receiver.register_symbol
else
@receiver = receiver.new(RECEIVER_REG)
end
@ -45,9 +45,9 @@ module Vm
args.each_with_index do |arg , i|
if arg.is_a?(Value)
@args[i] = arg
raise "arg in non std register #{arg.inspect}" unless (i+1+RECEIVER_REG) == arg.register
raise "arg in non std register #{arg.inspect}" unless RECEIVER_REG == arg.used_register.next_reg(i - 1)
else
@args[i] = arg.new(i+1+RECEIVER_REG)
@args[i] = arg.new(RegisterUse.new(RECEIVER_REG).next_reg(i + 1))
end
end
set_return return_type
@ -59,12 +59,12 @@ module Vm
@blocks = []
end
attr_reader :args , :entry , :exit , :body , :name , :return_type , :revceiver
attr_reader :args , :entry , :exit , :body , :name , :return_type , :receiver
def set_return type_or_value
@return_type = type_or_value || Vm::Integer
if @return_type.is_a?(Value)
raise "return in non std register #{@return_type.inspect}" unless RETURN_REG == @return_type.register
raise "return in non std register #{@return_type.inspect}" unless RETURN_REG == @return_type.register_symbol
else
@return_type = @return_type.new(RETURN_REG)
end
@ -76,7 +76,7 @@ module Vm
def new_local type = Vm::Integer
register = args.length + 1 + @locals.length # one for the receiver implicit arg
l = type.new(register + 1) # one for the type register 0, TODO add type as arg0 implicitly
puts "new local #{l.register}"
puts "new local #{l.register_symbol}"
# raise "Register overflow in function #{name}" if l.register > 6
@locals << l
l
@ -84,13 +84,13 @@ module Vm
#BUG - must save receiver
def save_locals context , into
save = args.collect{|a| a.register } + @locals.collect{|l| l.register}
save = args.collect{|a| a.register_symbol } + @locals.collect{|l| l.register_symbol}
into.push(save) unless save.empty?
end
def restore_locals context , into
#TODO assumes allocation in order, as the pop must be get regs in ascending order (also push)
restore = args.collect{|a| a.register } + @locals.collect{|l| l.register}
restore = args.collect{|a| a.register_symbol } + @locals.collect{|l| l.register_symbol}
into.pop(restore) unless restore.empty?
end

View File

@ -68,16 +68,6 @@ module Vm
@right = right
super(options)
end
# this is used to write code that looks like assignment
# So instructions can be created without the result (register) set, and this assigns where
# the reuslt after the fact, but usually in the same line
# Example (with block b, and variables int,a,b): b.int = a + b
# a + b actually creates an add instruction while the b.int= assigns the result to int
# b.add( int , a , b) is an alternative (assmbler style) way of writing the same.
def assign left
@result = left
self
end
end
class MathInstruction < Instruction
def initialize first , options = {}
@ -96,7 +86,7 @@ module Vm
def initialize to , from , options = {}
@to = to
@from = from
raise inspect unless from
raise "move must have from set #{inspect}" unless from
super(options)
end
end

View File

@ -8,7 +8,7 @@ module Vm
# PS: can't say i fancy the << self syntax and am considerernig adding a keyword for it, like meta
# In effect it is a very similar construct to def self.function(...)
# So one could write def meta.function(...) and thus define on the meta-class
class MetaClass < Code
class MetaClass < ObjectConstant
# no name, nor nothing. as this is just the object really
def initialize(object)

View File

@ -108,6 +108,7 @@ module Vm
options = {} if options == nil
options.merge defaults
options[:opcode] = inst
first = Vm::Integer.new(first) if first.is_a? Symbol
clazz.new(first , options)
end
end
@ -118,6 +119,8 @@ module Vm
create_method(inst) do |left ,right , options = nil|
options = {} if options == nil
options.merge defaults
left = Vm::Integer.new(left) if left.is_a? Symbol
right = Vm::Integer.new(right) if right.is_a? Symbol
options[:opcode] = inst
clazz.new(left , right ,options)
end
@ -130,6 +133,9 @@ module Vm
options = {} if options == nil
options.merge defaults
options[:opcode] = inst
result = Vm::Integer.new(result) if result.is_a? Symbol
left = Vm::Integer.new(left) if left.is_a? Symbol
right = Vm::Integer.new(right) if right.is_a? Symbol
clazz.new(result, left , right ,options)
end
end

View File

@ -53,9 +53,23 @@ module Vm
# but which register can be changed, and _all_ instructions sharing the RegisterUse then use that register
# In other words a simple level of indirection, or change from value to reference sematics.
class RegisterUse
attr_accessor :register
attr_accessor :symbol
def initialize r
@register = r
if( r.is_a? Fixnum)
r = "r#{r}".to_sym
end
raise "wrong type for register init #{r}" unless r.is_a? Symbol
raise "double r #{r}" if r == :rr1
@symbol = r
end
#helper methods to calculate with register symbols
def next_reg by = 1
int = @symbol[1,3].to_i
"r#{int + by}".to_sym
end
def next_reg_use by = 1
RegisterUse.new( next_reg(by) )
end
end
@ -66,23 +80,21 @@ module Vm
attr_accessor :used_register
def register
@used_register.register
def register_symbol
@used_register.symbol
end
def inspect
self.class.name + "(r#{used_register.register})"
"#{self.class.name} (#{register_symbol})"
end
def to_s
inspect
end
def initialize reg
if reg.is_a? Fixnum
@used_register = RegisterUse.new(reg)
else
if reg.is_a? RegisterUse
@used_register = reg
else
@used_register = RegisterUse.new(reg)
end
raise inspect if reg == nil
end
def length
4
@ -98,21 +110,6 @@ module Vm
class Integer < Word
# part of the dsl.
# Gets called with either fixnum/IntegerConstant or an Instruction (usually logic, iw add...)
# For instructions we flip, ie call the assign on the instruction
# but for constants we have to create instruction first (mov)
def assign other
other = Vm::IntegerConstant.new(other) if other.is_a? Fixnum
if other.is_a?(Vm::IntegerConstant) or other.is_a?(Vm::Integer)
class_for(MoveInstruction).new( self , other , :opcode => :mov)
elsif other.is_a?(Vm::StringConstant) # pc relative addressing
class_for(LogicInstruction).new(self , other , nil , opcode: :add)
else
other.assign(self)
end
end
def less_or_equal block , right
RegisterMachine.instance.integer_less_or_equal block , self , right
end
@ -155,7 +152,9 @@ module Vm
block.mov( self , right ) #move the value
elsif right.is_a? StringConstant
block.add( self , right , nil) #move the address, by "adding" to pc, ie pc relative
block.mov( Integer.new(register+1) , right.length ) #and the length HACK TODO
block.mov( Integer.new(self.used_register.next_reg) , right.length ) #and the length HACK TODO
elsif right.is_a?(BootClass) or right.is_a?(MetaClass)
block.add( self , right , nil) #move the address, by "adding" to pc, ie pc relative
else
raise "unknown #{right.inspect}"
end