diff --git a/lib/arm/arm_machine.rb b/lib/arm/arm_machine.rb index fc140bb4..ed849a62 100644 --- a/lib/arm/arm_machine.rb +++ b/lib/arm/arm_machine.rb @@ -36,23 +36,18 @@ module Arm end def function_call call raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall - e = Vm::Block.new("call_#{call.name}") - e.add_code( bl( :function => call.function )) + bl( :function => call.function ) end def main_entry - e = Vm::Block.new("main_entry") - e.add_code( mov( :left => :fp , :right => 0 )) + mov( :left => :fp , :right => 0 ) end def main_exit - e = Vm::Block.new("main_exit") - e.join( syscall(0) ) + syscall(0) end def syscall num - e = Vm::Block.new("syscall") - e.add_code( MoveInstruction.new( :left => 7 , :right => num ) ) - e.add_code( CallInstruction.new( :swi => 0 ) ) - e + mov( :left => 7 , :right => num ) + swi( {} ) end end end \ No newline at end of file diff --git a/lib/ast/basic_expressions.rb b/lib/ast/basic_expressions.rb index 357ca4fb..9e9c1db3 100644 --- a/lib/ast/basic_expressions.rb +++ b/lib/ast/basic_expressions.rb @@ -1,4 +1,5 @@ # collection of the simple ones, int and strings and such + module Ast class IntegerExpression < Expression @@ -29,7 +30,7 @@ module Ast def compile context # TODO check if needst to be added? - ObjectReference.new( StringValue.new(string) ) + Vm::ObjectReference.new( Vm::StringValue.new(string) ) end def == other compare other , [:string] diff --git a/lib/vm/block.rb b/lib/vm/block.rb index 2fcee873..c391ab58 100644 --- a/lib/vm/block.rb +++ b/lib/vm/block.rb @@ -16,8 +16,7 @@ module Vm # See Value description on how to create code/instructions - # Blocks have a list of expressions, that they compile into a list of codes - # Codes then get assembled into bytes (after positioning) + # Codes then get assembled into bytes (after linking) class Block < Code @@ -25,53 +24,35 @@ module Vm super() @name = name.to_sym @next = nil - @values = [] @codes = [] end - attr_reader :name , :next , :codes , :values + attr_reader :name , :next , :codes - def verify - end - - def add_value v - @values << v - end def length @codes.inject(0) {| sum , item | sum + item.length} end + def add_code(kode) - kode.at(@position) - length = kode.length - @position += length @codes << kode self end - def compile context - @values.each do |value| - value.compile(context) + def link_at pos , context + @position = pos + @codes.each do |code| + code.link_at(pos , context) + pos += code.length end + pos end + def assemble(io) @codes.each do |obj| obj.assemble io end end - # all machine methods produce blocks so it's a unified interface. But often they are just linear - # code after linear code, so then they can be joined. - # The other block is useless after, all instructions move here - def join other - raise "block is chained already, can't join #{inspect}" if @next - other.codes.each do |code| - add_code code - end - other.values.each do |value| - add_value value - end - self - end # set the next executed block after self. # why is this useful? if it's unconditional, why not merge them: # So the second block can be used as a jump target. You standard loop needs a block to setup diff --git a/lib/vm/code.rb b/lib/vm/code.rb index 967b0fc3..e0e6451f 100644 --- a/lib/vm/code.rb +++ b/lib/vm/code.rb @@ -1,9 +1,7 @@ -require_relative "values" - module Vm # Base class for anything that we can assemble - # Derived classes include instructions and data(strings) + # Derived classes include instructions and data(values) # The commonality abstracted here is the length and position # and the ability to assemble itself into the stream @@ -11,7 +9,7 @@ module Vm # All code is position independant once assembled. # But for jumps and calls two passes are neccessary. # The first setting the position, the second assembling - class Code < Value + class Code # set the position to zero, will have to reset later def initialize @@ -29,7 +27,7 @@ module Vm # The containing class (assembler/function) call this to tell the instruction/data where it is in the # stream. During assembly the position is then used to calculate pc relative addresses. - def at address + def link_at address , context @address = address end diff --git a/lib/vm/function.rb b/lib/vm/function.rb index 29abe97f..ae6d1599 100644 --- a/lib/vm/function.rb +++ b/lib/vm/function.rb @@ -28,18 +28,13 @@ module Vm @entry.length + @exit.length + super end - def compile context + def compiled context function = context.program.get_function(name) unless function function = Vm::Kernel.send(name) context.program.get_or_create_function( name , function , arity ) end end - - def verify - @entry.verify - @exit.verify - end private def add_arg value diff --git a/lib/vm/program.rb b/lib/vm/program.rb index 2ebb87fa..74614e8f 100644 --- a/lib/vm/program.rb +++ b/lib/vm/program.rb @@ -23,24 +23,25 @@ module Vm def initialize machine super("start") Machine.instance = eval("#{machine}::#{machine}Machine").new - @context = Context.new(self) + #global objects (data) @objects = [] + # global functions + @functions = [] @entry = Vm::Kernel::start + #main gets executed between entry and exit + @main = nil @exit = Vm::Kernel::exit end - attr_reader :context + attr_reader :context , :main , :functions - def functions - @values - end def add_object o return if @objects.include? o @objects << o # TODO check type , no basic values allowed (must be wrapped) end def get_function name - @values.detect{ |f| (f.name == name) && (f.class == Function) } + @functions.detect{ |f| (f.name == name) && (f.class == Function) } end # preferred way of creating new functions (also forward declarations, will flag unresolved later) @@ -48,57 +49,33 @@ module Vm fun = get_function name unless fun fun = Function.new(name) - @values << fun + @functions << fun end fun end - def compile(context) - @values.each do |function| - function.compile(context) - end - @entry.compile(context) - super(context) - @exit.compile(context) - #string_table - @objects.each do |o| - o.compile(context) - end - fix_positions - fix_positions # second time for forward references - end - - def fix_positions - @position = 0 - @entry.at( @position ) - @position += @entry.length - @values.each do |function| - function.at(@position) - @position += function.length + def link_at( start , context) + @position = start + @entry.link_at( start , context ) + start += @entry.length + @main.link_at( start , context ) + start += @main.length + @functions.each do |function| + function.link_at(start , context) + start += function.length end @objects.each do |o| - o.at(@position) - @position += o.length + o.link_at(start , context) + start += o.length end - @exit.at( @position ) - @position += @exit.length + @exit.link_at( start , context) + start += @exit.length end - def wrap_as_main block - # #blockify - unless block.is_a? Block - raise "No block, start coding torsten" - end - add_value block - @entry.next block - block.next @exit + def main= code + @main = code end - def verify - @values.each do |funct| - funct.verify - end - end - + private # the main function def create_main diff --git a/lib/vm/values.rb b/lib/vm/values.rb index 09bb1550..1cf76afd 100644 --- a/lib/vm/values.rb +++ b/lib/vm/values.rb @@ -1,3 +1,5 @@ +require_relative "code" + module Vm # Values represent the information as it is processed. Different subclasses for different types, @@ -16,29 +18,14 @@ module Vm # Word Values are what fits in a register. Derived classes # Float, Reference , Integer(s) must fit the same registers - class Value - def bit_size - 8 * byte_size - end - - def byte_size - raise "abstract method called #{self.inspect}" - end - - # since we convert ast to values in conversion, value itself is responsible for compiling itself - # Compile must return a value, usually used in the next level up - def compile(context) - raise "abstract method called #{self.inspect}" - end + # just a base class for data. not sure how this will be usefull (may just have read too much llvm) + class Value < Code end class Word < Value def load reg Machine.instance.word_load self , reg end - def compile context - #nothing to do here - end end class Unsigned < Word @@ -63,15 +50,9 @@ module Vm def initialize string @string = string end - def at pos - @pos = pos - end def length @string.length + 3 end - def compile context - #nothing to do here - end attr_reader :string end @@ -84,7 +65,7 @@ module Vm end attr_reader :object - def compile context + def compiled context if object.is_a? StringValue context.program.add_object object else diff --git a/test/test_runner.rb b/test/test_runner.rb index c419e12f..2c4b873d 100644 --- a/test/test_runner.rb +++ b/test/test_runner.rb @@ -19,13 +19,16 @@ class TestRunner < MiniTest::Test def execute file string = File.read(file) syntax = Parser::Composed.new.parse(string) - tree = Parser::Transform.new.apply(syntax) + main = Parser::Transform.new.apply(syntax) program = Vm::Program.new "Arm" - value = tree.to_value - program.wrap_as_main value - compiled = program.compile( program.context ) - program.verify + + program.main = main.compile( program.context ) + + program.link_at( 0 , program.context ) + + binary = program.assemble(StringIO.new ) + puts program.to_yaml end