removed the (too) fancy dsl. Also introduce register indirection
This commit is contained in:
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -43,5 +43,6 @@ module Vm
|
||||
def assemble(io)
|
||||
raise "Not implemented #{self.inspect}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user