diff --git a/lib/register/builtin/README.md b/lib/register/builtin/README.md index 1cb4e6f4..34f1a72a 100644 --- a/lib/register/builtin/README.md +++ b/lib/register/builtin/README.md @@ -1,17 +1,18 @@ ### Builtin module -The Builtin module contains functions that can not be coded in ruby. +The Builtin module contains functions that can not be coded in ruby. It is the other side of the parfait coin, part of the runtime. -The functions are organized by their respective class and get loaded in boot_classes! , right at the start. +The functions are organized by their respective class and get loaded in boot_classes! , +right at the start. (see virtual/boot.rb) -These functions return their code, ie a Virtual::CompiledMethod object, which can then be called by ruby code -as if it were a "normal" function. +These functions return their code, ie a Virtual::CompiledMethod object, which can then be called by +ruby code as if it were a "normal" function. -A normal ruby function is one that is parsed and transformed to code. But not all functionality can be written in ruby, -one of those chicken and egg things. C uses Assembler in this situation, we use Builtin functions. +A normal ruby function is one that is parsed and transformed to code. But not all functionality can +be written in ruby, one of those chicken and egg things. +C uses Assembler in this situation, we use Builtin functions. Slightly more here : http://salama.github.io/2014/06/10/more-clarity.html (then still called Kernel) The Builtin module is scattered into several files, but that is just so the file doesn't get too long. - \ No newline at end of file diff --git a/lib/register/builtin/array.rb b/lib/register/builtin/array.rb index a289dda9..9a0c61ea 100644 --- a/lib/register/builtin/array.rb +++ b/lib/register/builtin/array.rb @@ -1,19 +1,21 @@ -module Builtin - class Array - module ClassMethods - def get context , index = Virtual::Integer - get_function = Virtual::CompiledMethodInfo.create_method(:get , [ Virtual::Integer] , Virtual::Integer , Virtual::Integer ) - return get_function - end - def set context , index = Virtual::Integer , object = Virtual::Reference - set_function = Virtual::CompiledMethodInfo.create_method(:set , [Virtual::Integer, Virtual::Reference] ) - return set_function - end - def push context , object = Virtual::Reference - push_function = Virtual::CompiledMethodInfo.create_method(:push , [Virtual::Reference] ) - return push_function +module Register + module Builtin + class Array + module ClassMethods + def get context , index = Virtual::Integer + get_function = Virtual::CompiledMethodInfo.create_method(:get , [ Virtual::Integer] , Virtual::Integer , Virtual::Integer ) + return get_function + end + def set context , index = Virtual::Integer , object = Virtual::Reference + set_function = Virtual::CompiledMethodInfo.create_method(:set , [Virtual::Integer, Virtual::Reference] ) + return set_function + end + def push context , object = Virtual::Reference + push_function = Virtual::CompiledMethodInfo.create_method(:push , [Virtual::Reference] ) + return push_function + end end + extend ClassMethods end - extend ClassMethods end end diff --git a/lib/register/builtin/integer.rb b/lib/register/builtin/integer.rb index 17a4a380..b2951e0d 100644 --- a/lib/register/builtin/integer.rb +++ b/lib/register/builtin/integer.rb @@ -1,98 +1,100 @@ #integer related kernel functions -module Builtin - module Integer - module ClassMethods - # The conversion to base10 is quite a bit more complicated than i thought. - # The bulk of it is in div10 - # We set up variables, do the devision and write the result to the string - # then check if were done and recurse if neccessary - # As we write before we recurse (save a push) we write the number backwards - # arguments: string address , integer - def utoa context - utoa_function = Virtual::CompiledMethodInfo.create_method(:Integer ,:utoa , [ Virtual::Integer ] ) - function.info.return_type = Virtual::Integer - function.info.receiver = Virtual::Integer - return utoa_function - str_addr = utoa_function.receiver - number = utoa_function.args.first - remainder = utoa_function.new_local - Virtual::RegisterMachine.instance.div10( utoa_function , number , remainder ) - # make char out of digit (by using ascii encoding) 48 == "0" - utoa_function.instance_eval do - add( remainder , remainder , 48) - strb( remainder, str_addr ) - sub( str_addr, str_addr , 1 ) - cmp( number , 0 ) - callne( utoa_function ) - end - return utoa_function - end - - def putint context - putint_function = Virtual::CompiledMethodInfo.create_method(:Integer,:putint , [] ) - putint_function.info.return_type = Virtual::Integer - putint_function.info.receiver = Virtual::Integer - return putint_function - buffer = Parfait::Word.new(" ") # create a buffer - context.object_space.add_object buffer # and save it (function local variable: a no no) - int = putint_function.receiver - moved_int = putint_function.new_local - utoa = context.object_space.get_class_by_name(:Object).resolve_method(:utoa) - putint_function.instance_eval do - mov( moved_int , int ) # move arg up - add( int , buffer ,nil ) # string to write to (add string address to pc) - add( int , int , buffer.length - 3) # 3 for good measure , ahem. - call( utoa ) - after = new_block("after_call") - insert_at after - # And now we "just" have to print it, using the write_stdout - add( int , buffer , nil ) # string to write to - mov( moved_int , buffer.length ) - end - Virtual::RegisterMachine.instance.write_stdout(putint_function) - putint_function - end - - # testing method, hand coded fibo, expects arg in receiver_register - # result comes in return_register - # a hand coded version of the fibonachi numbers - # not my hand off course, found in the net http://www.peter-cockerell.net/aalp/html/ch-5.html - def fibo context - fibo_function = Virtual::CompiledMethodInfo.create_method(:Integer,:fibo , [] ) - fibo_function.info.return_type = Virtual::Integer - fibo_function.info.receiver = Virtual::Integer - return fibo_function - result = fibo_function.return_type - int = fibo_function.receiver - - last = fibo_function.new_block("return") - - f1 = fibo_function.new_local - f2 = fibo_function.new_local - - fibo_function.instance_eval do - cmp int , 1 - mov( result, int , condition_code: :le) - ble( last ) #branch to return, rather than return (as the original) - mov f1 , 1 #set up initial values - mov f2 , 0 +module Register + module Builtin + module Integer + module ClassMethods + # The conversion to base10 is quite a bit more complicated than i thought. + # The bulk of it is in div10 + # We set up variables, do the devision and write the result to the string + # then check if were done and recurse if neccessary + # As we write before we recurse (save a push) we write the number backwards + # arguments: string address , integer + def utoa context + utoa_function = Virtual::CompiledMethodInfo.create_method(:Integer ,:utoa , [ Virtual::Integer ] ) + function.info.return_type = Virtual::Integer + function.info.receiver = Virtual::Integer + return utoa_function + str_addr = utoa_function.receiver + number = utoa_function.args.first + remainder = utoa_function.new_local + Virtual::RegisterMachine.instance.div10( utoa_function , number , remainder ) + # make char out of digit (by using ascii encoding) 48 == "0" + utoa_function.instance_eval do + add( remainder , remainder , 48) + strb( remainder, str_addr ) + sub( str_addr, str_addr , 1 ) + cmp( number , 0 ) + callne( utoa_function ) + end + return utoa_function end - loop = fibo_function.new_block("loop") - fibo_function.insert_at loop - - fibo_function.instance_eval do #loop through - add f1 , f1 , f2 # f1 = f1 + f2 - sub f2 , f1 , f2 # f2 = f1 -f2 - sub int , int , 1 # todo: set.. should do below cmp, but doesn't , set_update_status: 1 - cmp int , 1 - bne( loop ) - mov( result , f1 ) + def putint context + putint_function = Virtual::CompiledMethodInfo.create_method(:Integer,:putint , [] ) + putint_function.info.return_type = Virtual::Integer + putint_function.info.receiver = Virtual::Integer + return putint_function + buffer = Parfait::Word.new(" ") # create a buffer + context.object_space.add_object buffer # and save it (function local variable: a no no) + int = putint_function.receiver + moved_int = putint_function.new_local + utoa = context.object_space.get_class_by_name(:Object).resolve_method(:utoa) + putint_function.instance_eval do + mov( moved_int , int ) # move arg up + add( int , buffer ,nil ) # string to write to (add string address to pc) + add( int , int , buffer.length - 3) # 3 for good measure , ahem. + call( utoa ) + after = new_block("after_call") + insert_at after + # And now we "just" have to print it, using the write_stdout + add( int , buffer , nil ) # string to write to + mov( moved_int , buffer.length ) + end + Virtual::RegisterMachine.instance.write_stdout(putint_function) + putint_function end - fibo_function + # testing method, hand coded fibo, expects arg in receiver_register + # result comes in return_register + # a hand coded version of the fibonachi numbers + # not my hand off course, found in the net http://www.peter-cockerell.net/aalp/html/ch-5.html + def fibo context + fibo_function = Virtual::CompiledMethodInfo.create_method(:Integer,:fibo , [] ) + fibo_function.info.return_type = Virtual::Integer + fibo_function.info.receiver = Virtual::Integer + return fibo_function + result = fibo_function.return_type + int = fibo_function.receiver + + last = fibo_function.new_block("return") + + f1 = fibo_function.new_local + f2 = fibo_function.new_local + + fibo_function.instance_eval do + cmp int , 1 + mov( result, int , condition_code: :le) + ble( last ) #branch to return, rather than return (as the original) + mov f1 , 1 #set up initial values + mov f2 , 0 + end + + loop = fibo_function.new_block("loop") + fibo_function.insert_at loop + + fibo_function.instance_eval do #loop through + add f1 , f1 , f2 # f1 = f1 + f2 + sub f2 , f1 , f2 # f2 = f1 -f2 + sub int , int , 1 # todo: set.. should do below cmp, but doesn't , set_update_status: 1 + cmp int , 1 + bne( loop ) + mov( result , f1 ) + end + + fibo_function + end + end + extend ClassMethods end - end - extend ClassMethods end end diff --git a/lib/register/builtin/kernel.rb b/lib/register/builtin/kernel.rb index 6dddceca..a8ba199c 100644 --- a/lib/register/builtin/kernel.rb +++ b/lib/register/builtin/kernel.rb @@ -1,81 +1,82 @@ -module Builtin - module Kernel - module ClassMethods - # this is the really really first place the machine starts (apart from the jump here) - # it isn't really a function, ie it is jumped to (not called), exits and may not return - # so it is responsible for initial setup - def __init__ context - function = Virtual::CompiledMethodInfo.create_method(:Kernel,:__init__ , []) - function.info.return_type = Virtual::Integer - # no method enter or return (automatically added), remove - function.info.blocks.first.codes.pop # no Method enter - function.info.blocks.last.codes.pop # no Method return - #Set up the Space as self upon init - space = Parfait::Space.object_space - function.info.add_code Register::LoadConstant.new( space , Register::RegisterReference.self_reg) - message_ind = space.get_layout().index_of( :next_message ) - # Load the message to message register (0) - function.info.add_code Register::GetSlot.new( Register::RegisterReference.self_reg , message_ind , Register::RegisterReference.new_message_reg) - # And store the space as the new self (so the call can move it back as self) - function.info.add_code Register::SetSlot.new( Register::RegisterReference.self_reg , Register::RegisterReference.new_message_reg , Virtual::SELF_INDEX) - # now we are set up to issue a call to the main - function.info.add_code Virtual::MethodCall.new(Virtual.machine.space.get_main) - emit_syscall( function , :exit ) - return function - end - def putstring context - function = Virtual::CompiledMethodInfo.create_method(:Kernel , :putstring , [] ) - emit_syscall( function , :putstring ) - function - end - def exit context - function = Virtual::CompiledMethodInfo.create_method(:Kernel,:exit , []) - function.info.return_type = Virtual::Integer - return function - ret = Virtual::RegisterMachine.instance.exit(function) - function.set_return ret - function - end - def __send context - function = Virtual::CompiledMethodInfo.create_method(:Kernel ,:__send , [] ) - function.info.return_type = Virtual::Integer - return function - end +module Register + module Builtin + module Kernel + module ClassMethods + # this is the really really first place the machine starts (apart from the jump here) + # it isn't really a function, ie it is jumped to (not called), exits and may not return + # so it is responsible for initial setup + def __init__ context + function = Virtual::CompiledMethodInfo.create_method(:Kernel,:__init__ , []) + function.info.return_type = Virtual::Integer + # no method enter or return (automatically added), remove + function.info.blocks.first.codes.pop # no Method enter + function.info.blocks.last.codes.pop # no Method return + #Set up the Space as self upon init + space = Parfait::Space.object_space + function.info.add_code LoadConstant.new( space , Register.self_reg) + message_ind = space.get_layout().index_of( :next_message ) + # Load the message to message register (0) + function.info.add_code Register.get_slot( :self , message_ind , :message) + # And store the space as the new self (so the call can move it back as self) + function.info.add_code Register.set_slot( :self , :message , :receiver) + # now we are set up to issue a call to the main + function.info.add_code Virtual::MethodCall.new(Virtual.machine.space.get_main) + emit_syscall( function , :exit ) + return function + end + def putstring context + function = Virtual::CompiledMethodInfo.create_method(:Kernel , :putstring , [] ) + emit_syscall( function , :putstring ) + function + end + def exit context + function = Virtual::CompiledMethodInfo.create_method(:Kernel,:exit , []) + function.info.return_type = Virtual::Integer + return function + ret = Virtual::RegisterMachine.instance.exit(function) + function.set_return ret + function + end + def __send context + function = Virtual::CompiledMethodInfo.create_method(:Kernel ,:__send , [] ) + function.info.return_type = Virtual::Integer + return function + end - private - def emit_syscall function , name - save_message( function ) - function.info.add_code Register::Syscall.new( name ) - restore_message(function) - end - # save the current message, as the syscall destroys all context - # - # currently HACKED into the space as a temporary varaible. As the space is a globally - # unique object we can retrieve it from there - # TODO : fix this to use global (later per thread) variable - def save_message(function) - space_tmp = Register::RegisterReference.tmp_reg - ind = Parfait::Space.object_space.get_layout().index_of( :syscall_message ) - raise "index not found for :syscall_message" unless ind - function.info.add_code Register::LoadConstant.new( Parfait::Space.object_space , space_tmp) - function.info.add_code Register::SetSlot.new( Register::RegisterReference.message_reg , space_tmp , ind) - end - def restore_message(function) - # get the sys return out of the way - return_tmp = Register::RegisterReference.tmp_reg - # load the space into the base register - function.info.add_code Register::RegisterTransfer.new( return_tmp , Register::RegisterReference.message_reg ) - slot = Virtual::Slot - # find the stored message - ind = Parfait::Space.object_space.get_layout().index_of( :syscall_message ) - raise "index not found for #{kind}.#{kind.class}" unless ind - # and load it into the base RegisterMachine - function.info.add_code Register::GetSlot.new( Register::RegisterReference.message_reg , ind , Register::RegisterReference.message_reg ) - # and "unroll" self and frame - function.info.add_code Register::GetSlot.new( Register::RegisterReference.message_reg , Virtual::SELF_INDEX, Register::RegisterReference.self_reg ) - function.info.add_code Register::GetSlot.new( Register::RegisterReference.message_reg , Virtual::FRAME_INDEX, Register::RegisterReference.frame_reg ) + private + def emit_syscall function , name + save_message( function ) + function.info.add_code Syscall.new( name ) + restore_message(function) + end + # save the current message, as the syscall destroys all context + # + # currently HACKED into the space as a temporary varaible. As the space is a globally + # unique object we can retrieve it from there + # TODO : fix this to use global (later per thread) variable + def save_message(function) + space_tmp = Register.tmp_reg + ind = Parfait::Space.object_space.get_layout().index_of( :syscall_message ) + raise "index not found for :syscall_message" unless ind + function.info.add_code LoadConstant.new( Parfait::Space.object_space , space_tmp) + function.info.add_code SetSlot.new( Register.message_reg , space_tmp , ind) + end + def restore_message(function) + # get the sys return out of the way + return_tmp = Register.tmp_reg + # load the space into the base register + function.info.add_code RegisterTransfer.new( return_tmp , Register.message_reg ) + # find the stored message + ind = Parfait::Space.object_space.get_layout().index_of( :syscall_message ) + raise "index not found for #{kind}.#{kind.class}" unless ind + # and load it into the base RegisterMachine + function.info.add_code Register.get_slot :message , ind , :message + # and "unroll" self and frame + function.info.add_code Register.get_slot(:message , :receiver, :self ) + function.info.add_code Register.get_slot(:message , :frame , :frame) + end end + extend ClassMethods end - extend ClassMethods end end diff --git a/lib/register/builtin/object.rb b/lib/register/builtin/object.rb index 2ba621a5..79b910fc 100644 --- a/lib/register/builtin/object.rb +++ b/lib/register/builtin/object.rb @@ -1,75 +1,77 @@ -module Builtin - class Object - module ClassMethods +module Register + module Builtin + class Object + module ClassMethods - # main entry point, ie __init__ calls this - # defined here as empty, to be redefined - def main context - function = Virtual::CompiledMethodInfo.create_method(:Object,:main , []) - return function - end - - # in ruby, how this goes is - # def _get_instance_variable var - # i = self.index_of(var) - # return at_index(i) - # end - # The at_index is just "below" the api, something we need but don't want to expose, - # so we can't code the above in ruby - def _get_instance_variable context , name = Virtual::Integer - get_function = Virtual::CompiledMethodInfo.create_method(:Object,:_get_instance_variable , [ Virtual::Reference ] ) - return get_function - me = get_function.receiver - var_name = get_function.args.first - return_to = get_function.return_type - - index_function = ::Virtual.machine.space.get_class_by_name(:Object).resolve_method(:index_of) -# get_function.push( [me] ) -# index = get_function.call( index_function ) - - after_body = get_function.new_block("after_index") - get_function.current after_body - -# get_function.pop([me]) -# return_to.at_index( get_function , me , return_to ) - -# get_function.set_return return_to - return get_function - end - - def _set_instance_variable(context , name = Virtual::Integer , value = Virtual::Integer ) - set_function = Virtual::CompiledMethodInfo.create_method(:Object,:_set_instance_variable ,[Virtual::Reference ,Virtual::Reference] ) - return set_function - receiver set_function - me = set_function.receiver - var_name = set_function.args.first - return_to = set_function.return_type - index_function = context.object_space.get_class_by_name(:Object).resolve_method(:index_of) - set_function.push( [me] ) - set_function.call( index_function ) - after_body = set_function.new_block("after_index") - - set_function.current after_body - set_function.pop([me]) - return_to.at_index( set_function , me , return_to ) - set_function.set_return return_to - return set_function - end - - def _get_singleton_method(context , name ) - raise name - end - def _add_singleton_method(context, method) - raise "4" - end - def initialize(context) - raise "4" + # main entry point, ie __init__ calls this + # defined here as empty, to be redefined + def main context + function = Virtual::CompiledMethodInfo.create_method(:Object,:main , []) + return function + end + + # in ruby, how this goes is + # def _get_instance_variable var + # i = self.index_of(var) + # return at_index(i) + # end + # The at_index is just "below" the api, something we need but don't want to expose, + # so we can't code the above in ruby + def _get_instance_variable context , name = Virtual::Integer + get_function = Virtual::CompiledMethodInfo.create_method(:Object,:_get_instance_variable , [ Virtual::Reference ] ) + return get_function + me = get_function.receiver + var_name = get_function.args.first + return_to = get_function.return_type + + index_function = ::Virtual.machine.space.get_class_by_name(:Object).resolve_method(:index_of) + # get_function.push( [me] ) + # index = get_function.call( index_function ) + + after_body = get_function.new_block("after_index") + get_function.current after_body + + # get_function.pop([me]) + # return_to.at_index( get_function , me , return_to ) + + # get_function.set_return return_to + return get_function + end + + def _set_instance_variable(context , name = Virtual::Integer , value = Virtual::Integer ) + set_function = Virtual::CompiledMethodInfo.create_method(:Object,:_set_instance_variable ,[Virtual::Reference ,Virtual::Reference] ) + return set_function + receiver set_function + me = set_function.receiver + var_name = set_function.args.first + return_to = set_function.return_type + index_function = context.object_space.get_class_by_name(:Object).resolve_method(:index_of) + set_function.push( [me] ) + set_function.call( index_function ) + after_body = set_function.new_block("after_index") + + set_function.current after_body + set_function.pop([me]) + return_to.at_index( set_function , me , return_to ) + set_function.set_return return_to + return set_function + end + + def _get_singleton_method(context , name ) + raise name + end + def _add_singleton_method(context, method) + raise "4" + end + def initialize(context) + raise "4" + end end + extend ClassMethods end - extend ClassMethods end end + require_relative "integer" -require_relative "word" require_relative "array" require_relative "kernel" diff --git a/lib/register/builtin/word.rb b/lib/register/builtin/word.rb deleted file mode 100644 index ed634799..00000000 --- a/lib/register/builtin/word.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Builtin - class Word - module ClassMethods - end - extend ClassMethods - end -end diff --git a/lib/virtual/boot.rb b/lib/virtual/boot.rb index 07316257..0c1ca6ef 100644 --- a/lib/virtual/boot.rb +++ b/lib/virtual/boot.rb @@ -25,7 +25,8 @@ module Virtual value_classes = values.collect { |cl| @space.create_class(cl,nil) } layouts = { :Word => [] , :List => [] , - :Message => [:next_message], + # Assumtion is that name is the last of message + :Message => [:next_message , :receiver , :frame , :return_address , :caller , :name ], :MetaClass => [], :BinaryCode => [], :Space => [:classes ,:next_message ,:next_frame, :syscall_message], @@ -99,17 +100,17 @@ module Virtual # TODO go through the virtual parfait layer and adjust function names to what they really are obj = @class_mappings[:Object ] [:main , :_get_instance_variable , :_set_instance_variable].each do |f| - obj.add_instance_method Builtin::Object.send(f , nil) + obj.add_instance_method Register::Builtin::Object.send(f , nil) end obj = @class_mappings[:Kernel ] # create dummy main first, __init__ calls it [:putstring,:exit,:__send , :__init__ ].each do |f| - obj.add_instance_method Builtin::Kernel.send(f , nil) + obj.add_instance_method Register::Builtin::Kernel.send(f , nil) end obj = @class_mappings[:Integer ] [:putint,:fibo].each do |f| - obj.add_instance_method Builtin::Integer.send(f , nil) + obj.add_instance_method Register::Builtin::Integer.send(f , nil) end end