diff --git a/lib/neumann/call_site.rb b/lib/neumann/call_site.rb deleted file mode 100644 index 272b44ac..00000000 --- a/lib/neumann/call_site.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Vm - - # name and args , return - - class CallSite < Value - - def initialize(name , value , args , function ) - @name = name - @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? Boot::MetaClass - function.receiver.move( into, value ) if value.register_symbol != function.receiver.register_symbol - end - raise "function call '#{args.inspect}' 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_symbol != function.args[index].register_symbol - end - end - end - - def do_call into - RegisterMachine.instance.function_call into , self - end - end -end diff --git a/lib/neumann/constants.rb b/lib/neumann/constants.rb deleted file mode 100644 index ee52c711..00000000 --- a/lib/neumann/constants.rb +++ /dev/null @@ -1,62 +0,0 @@ -module Vm - - # constants are the stuff that you embedd in the program as numbers or strings. - # Another way to think about them is as Operands, they have no seperate "identity" - # and usually end up embedded in the instructions. ie your basic foo + 4 will encode - # the 4 in the instruction opcode. The 4 is not accessible anywhere else. - # When it should be usable in other forms, the constant must become a Value first - class Constant < Code - - 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 - @integer = int - end - attr_reader :integer - def value - @integer - end - def to_asm - @integer.to_s - end - end - - # The name really says it all. - # The only interesting thing is storage. - # Currently string are stored "inline" , ie in the code segment. - # Mainly because that works an i aint no elf expert. - - class StringConstant < ObjectConstant - attr_reader :string - # currently aligned to 4 (ie padded with 0) and off course 0 at the end - def initialize str - str = str.to_s if str.is_a? Symbol - length = str.length - # rounding up to the next 4 (always adding one for zero pad) - pad = ((length / 4 ) + 1 ) * 4 - length - raise "#{pad} #{self}" unless pad >= 1 - @string = str + " " * pad - end - - def result= value - class_for(MoveInstruction).new(value , self , :opcode => :mov) - end - - # the strings length plus padding - def length - string.length - end - - # just writing the string - def assemble(io) - io << string - end - end - -end \ No newline at end of file diff --git a/lib/neumann/context.rb b/lib/neumann/context.rb deleted file mode 100644 index bbfea8a9..00000000 --- a/lib/neumann/context.rb +++ /dev/null @@ -1,16 +0,0 @@ - -module Vm - - #currently just holding the object_space in here so we can have global access - class Context - - def initialize object_space - @object_space = object_space - @locals = {} - end - attr_reader :attributes ,:object_space - - attr_accessor :current_class , :locals , :function - - end -end diff --git a/lib/neumann/function.rb b/lib/neumann/function.rb deleted file mode 100644 index cfa375ea..00000000 --- a/lib/neumann/function.rb +++ /dev/null @@ -1,185 +0,0 @@ -require_relative "block" -require_relative "passes" - -module Vm - - # Functions are similar to Blocks. Where Blocks can be jumped to, Functions can be called. - - # Functions also have arguments and a return. These are Value subclass instances, ie specify - # type (by class type) and register by instance - - # They also have local variables. Args take up the first n regs, then locals the rest. No - # direct manipulating of registers (ie specifying the number) should be done. - - # Code-wise Functions are made up from a list of Blocks, in a similar way blocks are made up of codes - # Four of the block have a special role: - # - entry/exit: are usually system specific - # - body: the logical start of the function - # - return: the logical end, where ALL blocks must end - - # Blocks can be linked in two ways: - # -linear: flow continues from one to the next as they are sequential both logically and "physically" - # use the block set_next for this. - # This "the straight line", there must be a continuous sequence from body to return - # Linear blocks may be created from an existing block with new_block - # - branched: You create new blocks using function.new_block which gets added "after" return - # These (eg if/while) blocks may themselves have linear blocks ,but the last of these - # MUST have an uncoditional branch. And remember, all roads lead to return. - - class Function < Code - - def initialize(name , receiver = Vm::Reference , args = [] , return_type = Vm::Reference) - super() - @name = name.to_sym - if receiver.is_a?(Value) - @receiver = receiver - raise "arg in non std register #{receiver.inspect}" unless RegisterMachine.instance.receiver_register == receiver.register_symbol - else - puts receiver.inspect - @receiver = receiver.new(RegisterMachine.instance.receiver_register) - end - - @args = Array.new(args.length) - args.each_with_index do |arg , i| - shouldda = RegisterReference.new(RegisterMachine.instance.receiver_register).next_reg_use(i + 1) - if arg.is_a?(Value) - @args[i] = arg - raise "arg #{i} in non std register #{arg.register}, expecting #{shouldda}" unless shouldda == arg.register - else - @args[i] = arg.new(shouldda) - end - end - set_return return_type - @exit = RegisterMachine.instance.function_exit( Vm::Block.new("exit" , self , nil) , name ) - @return = Block.new("return", self , @exit) - @body = Block.new("body", self , @return) - @insert_at = @body - @entry = RegisterMachine.instance.function_entry( Vm::Block.new("entry" , self , @body) ,name ) - @locals = [] - end - - attr_reader :args , :entry , :exit , :body , :name , :return_type , :receiver - - def insertion_point - @insert_at - end - def set_return type_or_value - @return_type = type_or_value || Vm::Reference - if @return_type.is_a?(Value) - raise "return in non std register #{@return_type.inspect}" unless RegisterMachine.instance.return_register == @return_type.register_symbol - else - @return_type = @return_type.new(RegisterMachine.instance.return_register) - end - end - def arity - @args.length - end - - def new_local type = Vm::Integer - register = args.length + 3 + @locals.length # three for the receiver, return and type regs - l = type.new(register) #so start at r3 - #puts "new local #{l.register_symbol}" - raise "Register overflow in function #{name}" if register >= 13 # yep, 13 is bad luck - @locals << l - l - end - - # return a list of registers that are still in use after the given block - # a call_site uses pushes and pops these to make them available for code after a call - def locals_at l_block - used =[] - # call assigns the return register, but as it is in l_block, it is not asked. - assigned = [ RegisterReference.new(Vm::RegisterMachine.instance.return_register) ] - l_block.reachable.each do |b| - b.uses.each {|u| - (used << u) unless assigned.include?(u) - } - assigned += b.assigns - end - used.uniq - end - - # return a list of the blocks that are addressable, ie entry and @blocks and all next - def blocks - ret = [] - b = @entry - while b - ret << b - b = b.next - end - ret - end - - # when control structures create new blocks (with new_block) control continues at some new block the - # the control structure creates. - # Example: while, needs 2 extra blocks - # 1 condition code, must be its own blockas we jump back to it - # - the body, can actually be after the condition as we don't need to jump there - # 2 after while block. Condition jumps here - # After block 2, the function is linear again and the calling code does not need to know what happened - - # But subsequent statements are still using the original block (self) to add code to - # So the while expression creates the extra blocks, adds them and the code and then "moves" the insertion point along - def insert_at block - @insert_at = block - self - end - - # create a new linear block after the current insertion block. - # Linear means there is no brach needed from that one to the new one. - # Usually the new one just serves as jump address for a control statement - # In code generation (assembly) , new new_block is written after this one, ie zero runtime cost - # This does _not_ change the insertion point, that has do be done with insert_at(block) - def new_block new_name - block_name = "#{@insert_at.name}_#{new_name}" - new_b = Block.new( block_name , self , @insert_at.next ) - @insert_at.set_next new_b - return new_b - end - - def add_code(kode) - raise "alarm #{kode}" if kode.is_a? Word - raise "alarm #{kode.class} #{kode}" unless kode.is_a? Code - @insert_at.add_code kode - self - end - - # sugar to create instructions easily. - # any method will be passed on to the RegisterMachine and the result added to the insertion block - # With this trick we can write what looks like assembler, - # Example func.instance_eval - # mov( r1 , r2 ) - # add( r1 , r2 , 4) - # end - # mov and add will be called on Machine and generate Inststuction that are then added - # to the current block - # also symbols are supported and wrapped as register usages (for bare metal programming) - def method_missing(meth, *args, &block) - add_code RegisterMachine.instance.send(meth , *args) - end - - # following id the Code interface - - # to link we link the entry and then any blocks. The entry links the straight line - def link_at address , context - super #just sets the position - @entry.link_at address , context - end - - # position of the function is the position of the entry block - def position - @entry.position - end - - # length of a function is the entry block length (includes the straight line behind it) - # plus any out of line blocks that have been added - def length - @entry.length - end - - # assembling assembles the entry (straight line/ no branch line) + any additional branches - def assemble io - @entry.assemble(io) - end - end -end \ No newline at end of file diff --git a/lib/neumann/values.rb b/lib/neumann/values.rb deleted file mode 100644 index 1c5f89cc..00000000 --- a/lib/neumann/values.rb +++ /dev/null @@ -1,52 +0,0 @@ -require_relative "code" -require_relative "register_reference" - -module Vm - - # Values represent the information as it is processed. Different subclasses for different types, - # each type with different operations. - # The oprerations on values is what makes a machine do things. Operations are captured as - # subclasses of Instruction and saved to Blocks - - # Values are a way to reason about (create/validate) instructions. - - # Word Values are what fits in a register. Derived classes - # Float, Reference , Integer(s) must fit the same registers - - # just a base class for data. not sure how this will be usefull (may just have read too much llvm) - class Value - def class_for clazz - RegisterMachine.instance.class_for(clazz) - end - end - - # Just a nice way to write branches - # Comparisons produce them, and branches take them as argument. - class BranchCondition < Value - - def initialize operator - @operator = operator - end - attr_accessor :operator - #needed to check the opposite, ie not true - def not_operator - case @operator - when :le - :gt - when :gt - :le - when :lt - :ge - when :eq - :ne - else - raise "no implemented #{@operator}" - end - end - end -end -require_relative "values/constants" -require_relative "values/word" -require_relative "values/integer" -require_relative "values/reference" -require_relative "values/mystery" diff --git a/lib/neumann/word.rb b/lib/neumann/word.rb deleted file mode 100644 index 18069bf4..00000000 --- a/lib/neumann/word.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Vm - # Word is an abstract base class for the obvious values, ie those that fit into a register - # Marked as abstract by private constructor - # - # Integer and (Object) References are the main derived classes, but float will come and ... - # The Mystery Value has unknown type and has only casting methods. So it must be cast to be useful. - # Types are stored at runtime when needed in TYPE_REGISTER (r1 on arm), which is mostly before calls, - # so that the called function can do casts / branching correctly - class Word < Value - attr_accessor :register - def register_symbol - @register.symbol - end - def inspect - "#{self.class.name} (#{register_symbol})" - end - def to_s - inspect - end - def length - 4 - end - # aka to string - def to_asm - "#{register_symbol}" - end - private - def initialize reg - if reg.is_a? RegisterReference - @register = reg - else - @register = RegisterReference.new(reg) - end - end - end -end \ No newline at end of file diff --git a/lib/virtual/method_definition.rb b/lib/virtual/method_definition.rb index 9c67110d..3043f576 100644 --- a/lib/virtual/method_definition.rb +++ b/lib/virtual/method_definition.rb @@ -150,5 +150,44 @@ module Virtual @tmps << name Ast::NameExpression.new(name) end + + # sugar to create instructions easily. + # any method will be passed on to the RegisterMachine and the result added to the insertion block + # With this trick we can write what looks like assembler, + # Example func.instance_eval + # mov( r1 , r2 ) + # add( r1 , r2 , 4) + # end + # mov and add will be called on Machine and generate Inststuction that are then added + # to the current block + # also symbols are supported and wrapped as register usages (for bare metal programming) + def method_missing(meth, *args, &block) + add_code RegisterMachine.instance.send(meth , *args) + end + + # following id the Code interface + + # to link we link the entry and then any blocks. The entry links the straight line + def link_at address , context + super #just sets the position + @entry.link_at address , context + end + + # position of the function is the position of the entry block + def position + @entry.position + end + + # length of a function is the entry block length (includes the straight line behind it) + # plus any out of line blocks that have been added + def length + @entry.length + end + + # assembling assembles the entry (straight line/ no branch line) + any additional branches + def assemble io + @entry.assemble(io) + end end + end