finally scoping builtin to register

had put this off because it breaks history
but now the references to register stuff which
builtin is off course full of, become much shorter
This commit is contained in:
Torsten Ruger 2015-06-29 21:03:58 +03:00
parent a03dcecbbd
commit 0f2c8e4201
7 changed files with 268 additions and 266 deletions

View File

@ -1,17 +1,18 @@
### Builtin module ### 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. 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 These functions return their code, ie a Virtual::CompiledMethod object, which can then be called by
as if it were a "normal" function. 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, A normal ruby function is one that is parsed and transformed to code. But not all functionality can
one of those chicken and egg things. C uses Assembler in this situation, we use Builtin functions. 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) 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. The Builtin module is scattered into several files, but that is just so the file doesn't get too long.

View File

@ -1,19 +1,21 @@
module Builtin module Register
class Array module Builtin
module ClassMethods class Array
def get context , index = Virtual::Integer module ClassMethods
get_function = Virtual::CompiledMethodInfo.create_method(:get , [ Virtual::Integer] , Virtual::Integer , Virtual::Integer ) def get context , index = Virtual::Integer
return get_function get_function = Virtual::CompiledMethodInfo.create_method(:get , [ Virtual::Integer] , Virtual::Integer , Virtual::Integer )
end return get_function
def set context , index = Virtual::Integer , object = Virtual::Reference end
set_function = Virtual::CompiledMethodInfo.create_method(:set , [Virtual::Integer, Virtual::Reference] ) def set context , index = Virtual::Integer , object = Virtual::Reference
return set_function set_function = Virtual::CompiledMethodInfo.create_method(:set , [Virtual::Integer, Virtual::Reference] )
end return set_function
def push context , object = Virtual::Reference end
push_function = Virtual::CompiledMethodInfo.create_method(:push , [Virtual::Reference] ) def push context , object = Virtual::Reference
return push_function push_function = Virtual::CompiledMethodInfo.create_method(:push , [Virtual::Reference] )
return push_function
end
end end
extend ClassMethods
end end
extend ClassMethods
end end
end end

View File

@ -1,98 +1,100 @@
#integer related kernel functions #integer related kernel functions
module Builtin module Register
module Integer module Builtin
module ClassMethods module Integer
# The conversion to base10 is quite a bit more complicated than i thought. module ClassMethods
# The bulk of it is in div10 # The conversion to base10 is quite a bit more complicated than i thought.
# We set up variables, do the devision and write the result to the string # The bulk of it is in div10
# then check if were done and recurse if neccessary # We set up variables, do the devision and write the result to the string
# As we write before we recurse (save a push) we write the number backwards # then check if were done and recurse if neccessary
# arguments: string address , integer # As we write before we recurse (save a push) we write the number backwards
def utoa context # arguments: string address , integer
utoa_function = Virtual::CompiledMethodInfo.create_method(:Integer ,:utoa , [ Virtual::Integer ] ) def utoa context
function.info.return_type = Virtual::Integer utoa_function = Virtual::CompiledMethodInfo.create_method(:Integer ,:utoa , [ Virtual::Integer ] )
function.info.receiver = Virtual::Integer function.info.return_type = Virtual::Integer
return utoa_function function.info.receiver = Virtual::Integer
str_addr = utoa_function.receiver return utoa_function
number = utoa_function.args.first str_addr = utoa_function.receiver
remainder = utoa_function.new_local number = utoa_function.args.first
Virtual::RegisterMachine.instance.div10( utoa_function , number , remainder ) remainder = utoa_function.new_local
# make char out of digit (by using ascii encoding) 48 == "0" Virtual::RegisterMachine.instance.div10( utoa_function , number , remainder )
utoa_function.instance_eval do # make char out of digit (by using ascii encoding) 48 == "0"
add( remainder , remainder , 48) utoa_function.instance_eval do
strb( remainder, str_addr ) add( remainder , remainder , 48)
sub( str_addr, str_addr , 1 ) strb( remainder, str_addr )
cmp( number , 0 ) sub( str_addr, str_addr , 1 )
callne( utoa_function ) cmp( number , 0 )
end callne( utoa_function )
return utoa_function end
end return utoa_function
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
end end
loop = fibo_function.new_block("loop") def putint context
fibo_function.insert_at loop putint_function = Virtual::CompiledMethodInfo.create_method(:Integer,:putint , [] )
putint_function.info.return_type = Virtual::Integer
fibo_function.instance_eval do #loop through putint_function.info.receiver = Virtual::Integer
add f1 , f1 , f2 # f1 = f1 + f2 return putint_function
sub f2 , f1 , f2 # f2 = f1 -f2 buffer = Parfait::Word.new(" ") # create a buffer
sub int , int , 1 # todo: set.. should do below cmp, but doesn't , set_update_status: 1 context.object_space.add_object buffer # and save it (function local variable: a no no)
cmp int , 1 int = putint_function.receiver
bne( loop ) moved_int = putint_function.new_local
mov( result , f1 ) 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 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
end
extend ClassMethods
end end
end end

View File

@ -1,81 +1,82 @@
module Builtin module Register
module Kernel module Builtin
module ClassMethods module Kernel
# this is the really really first place the machine starts (apart from the jump here) module ClassMethods
# it isn't really a function, ie it is jumped to (not called), exits and may not return # this is the really really first place the machine starts (apart from the jump here)
# so it is responsible for initial setup # it isn't really a function, ie it is jumped to (not called), exits and may not return
def __init__ context # so it is responsible for initial setup
function = Virtual::CompiledMethodInfo.create_method(:Kernel,:__init__ , []) def __init__ context
function.info.return_type = Virtual::Integer function = Virtual::CompiledMethodInfo.create_method(:Kernel,:__init__ , [])
# no method enter or return (automatically added), remove function.info.return_type = Virtual::Integer
function.info.blocks.first.codes.pop # no Method enter # no method enter or return (automatically added), remove
function.info.blocks.last.codes.pop # no Method return function.info.blocks.first.codes.pop # no Method enter
#Set up the Space as self upon init function.info.blocks.last.codes.pop # no Method return
space = Parfait::Space.object_space #Set up the Space as self upon init
function.info.add_code Register::LoadConstant.new( space , Register::RegisterReference.self_reg) space = Parfait::Space.object_space
message_ind = space.get_layout().index_of( :next_message ) function.info.add_code LoadConstant.new( space , Register.self_reg)
# Load the message to message register (0) message_ind = space.get_layout().index_of( :next_message )
function.info.add_code Register::GetSlot.new( Register::RegisterReference.self_reg , message_ind , Register::RegisterReference.new_message_reg) # Load the message to message register (0)
# And store the space as the new self (so the call can move it back as self) function.info.add_code Register.get_slot( :self , message_ind , :message)
function.info.add_code Register::SetSlot.new( Register::RegisterReference.self_reg , Register::RegisterReference.new_message_reg , Virtual::SELF_INDEX) # And store the space as the new self (so the call can move it back as self)
# now we are set up to issue a call to the main function.info.add_code Register.set_slot( :self , :message , :receiver)
function.info.add_code Virtual::MethodCall.new(Virtual.machine.space.get_main) # now we are set up to issue a call to the main
emit_syscall( function , :exit ) function.info.add_code Virtual::MethodCall.new(Virtual.machine.space.get_main)
return function emit_syscall( function , :exit )
end return function
def putstring context end
function = Virtual::CompiledMethodInfo.create_method(:Kernel , :putstring , [] ) def putstring context
emit_syscall( function , :putstring ) function = Virtual::CompiledMethodInfo.create_method(:Kernel , :putstring , [] )
function emit_syscall( function , :putstring )
end function
def exit context end
function = Virtual::CompiledMethodInfo.create_method(:Kernel,:exit , []) def exit context
function.info.return_type = Virtual::Integer function = Virtual::CompiledMethodInfo.create_method(:Kernel,:exit , [])
return function function.info.return_type = Virtual::Integer
ret = Virtual::RegisterMachine.instance.exit(function) return function
function.set_return ret ret = Virtual::RegisterMachine.instance.exit(function)
function function.set_return ret
end function
def __send context end
function = Virtual::CompiledMethodInfo.create_method(:Kernel ,:__send , [] ) def __send context
function.info.return_type = Virtual::Integer function = Virtual::CompiledMethodInfo.create_method(:Kernel ,:__send , [] )
return function function.info.return_type = Virtual::Integer
end return function
end
private private
def emit_syscall function , name def emit_syscall function , name
save_message( function ) save_message( function )
function.info.add_code Register::Syscall.new( name ) function.info.add_code Syscall.new( name )
restore_message(function) restore_message(function)
end end
# save the current message, as the syscall destroys all context # 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 # currently HACKED into the space as a temporary varaible. As the space is a globally
# unique object we can retrieve it from there # unique object we can retrieve it from there
# TODO : fix this to use global (later per thread) variable # TODO : fix this to use global (later per thread) variable
def save_message(function) def save_message(function)
space_tmp = Register::RegisterReference.tmp_reg space_tmp = Register.tmp_reg
ind = Parfait::Space.object_space.get_layout().index_of( :syscall_message ) ind = Parfait::Space.object_space.get_layout().index_of( :syscall_message )
raise "index not found for :syscall_message" unless ind 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 LoadConstant.new( Parfait::Space.object_space , space_tmp)
function.info.add_code Register::SetSlot.new( Register::RegisterReference.message_reg , space_tmp , ind) function.info.add_code SetSlot.new( Register.message_reg , space_tmp , ind)
end end
def restore_message(function) def restore_message(function)
# get the sys return out of the way # get the sys return out of the way
return_tmp = Register::RegisterReference.tmp_reg return_tmp = Register.tmp_reg
# load the space into the base register # load the space into the base register
function.info.add_code Register::RegisterTransfer.new( return_tmp , Register::RegisterReference.message_reg ) function.info.add_code RegisterTransfer.new( return_tmp , Register.message_reg )
slot = Virtual::Slot # find the stored message
# find the stored message ind = Parfait::Space.object_space.get_layout().index_of( :syscall_message )
ind = Parfait::Space.object_space.get_layout().index_of( :syscall_message ) raise "index not found for #{kind}.#{kind.class}" unless ind
raise "index not found for #{kind}.#{kind.class}" unless ind # and load it into the base RegisterMachine
# and load it into the base RegisterMachine function.info.add_code Register.get_slot :message , ind , :message
function.info.add_code Register::GetSlot.new( Register::RegisterReference.message_reg , ind , Register::RegisterReference.message_reg ) # and "unroll" self and frame
# and "unroll" self and frame function.info.add_code Register.get_slot(:message , :receiver, :self )
function.info.add_code Register::GetSlot.new( Register::RegisterReference.message_reg , Virtual::SELF_INDEX, Register::RegisterReference.self_reg ) function.info.add_code Register.get_slot(:message , :frame , :frame)
function.info.add_code Register::GetSlot.new( Register::RegisterReference.message_reg , Virtual::FRAME_INDEX, Register::RegisterReference.frame_reg ) end
end end
extend ClassMethods
end end
extend ClassMethods
end end
end end

View File

@ -1,75 +1,77 @@
module Builtin module Register
class Object module Builtin
module ClassMethods class Object
module ClassMethods
# main entry point, ie __init__ calls this # main entry point, ie __init__ calls this
# defined here as empty, to be redefined # defined here as empty, to be redefined
def main context def main context
function = Virtual::CompiledMethodInfo.create_method(:Object,:main , []) function = Virtual::CompiledMethodInfo.create_method(:Object,:main , [])
return function return function
end end
# in ruby, how this goes is # in ruby, how this goes is
# def _get_instance_variable var # def _get_instance_variable var
# i = self.index_of(var) # i = self.index_of(var)
# return at_index(i) # return at_index(i)
# end # end
# The at_index is just "below" the api, something we need but don't want to expose, # 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 # so we can't code the above in ruby
def _get_instance_variable context , name = Virtual::Integer def _get_instance_variable context , name = Virtual::Integer
get_function = Virtual::CompiledMethodInfo.create_method(:Object,:_get_instance_variable , [ Virtual::Reference ] ) get_function = Virtual::CompiledMethodInfo.create_method(:Object,:_get_instance_variable , [ Virtual::Reference ] )
return get_function return get_function
me = get_function.receiver me = get_function.receiver
var_name = get_function.args.first var_name = get_function.args.first
return_to = get_function.return_type return_to = get_function.return_type
index_function = ::Virtual.machine.space.get_class_by_name(:Object).resolve_method(:index_of) index_function = ::Virtual.machine.space.get_class_by_name(:Object).resolve_method(:index_of)
# get_function.push( [me] ) # get_function.push( [me] )
# index = get_function.call( index_function ) # index = get_function.call( index_function )
after_body = get_function.new_block("after_index") after_body = get_function.new_block("after_index")
get_function.current after_body get_function.current after_body
# get_function.pop([me]) # get_function.pop([me])
# return_to.at_index( get_function , me , return_to ) # return_to.at_index( get_function , me , return_to )
# get_function.set_return return_to # get_function.set_return return_to
return get_function return get_function
end end
def _set_instance_variable(context , name = Virtual::Integer , value = Virtual::Integer ) 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] ) set_function = Virtual::CompiledMethodInfo.create_method(:Object,:_set_instance_variable ,[Virtual::Reference ,Virtual::Reference] )
return set_function return set_function
receiver set_function receiver set_function
me = set_function.receiver me = set_function.receiver
var_name = set_function.args.first var_name = set_function.args.first
return_to = set_function.return_type return_to = set_function.return_type
index_function = context.object_space.get_class_by_name(:Object).resolve_method(:index_of) index_function = context.object_space.get_class_by_name(:Object).resolve_method(:index_of)
set_function.push( [me] ) set_function.push( [me] )
set_function.call( index_function ) set_function.call( index_function )
after_body = set_function.new_block("after_index") after_body = set_function.new_block("after_index")
set_function.current after_body set_function.current after_body
set_function.pop([me]) set_function.pop([me])
return_to.at_index( set_function , me , return_to ) return_to.at_index( set_function , me , return_to )
set_function.set_return return_to set_function.set_return return_to
return set_function return set_function
end end
def _get_singleton_method(context , name ) def _get_singleton_method(context , name )
raise name raise name
end end
def _add_singleton_method(context, method) def _add_singleton_method(context, method)
raise "4" raise "4"
end end
def initialize(context) def initialize(context)
raise "4" raise "4"
end
end end
extend ClassMethods
end end
extend ClassMethods
end end
end end
require_relative "integer" require_relative "integer"
require_relative "word"
require_relative "array" require_relative "array"
require_relative "kernel" require_relative "kernel"

View File

@ -1,7 +0,0 @@
module Builtin
class Word
module ClassMethods
end
extend ClassMethods
end
end

View File

@ -25,7 +25,8 @@ module Virtual
value_classes = values.collect { |cl| @space.create_class(cl,nil) } value_classes = values.collect { |cl| @space.create_class(cl,nil) }
layouts = { :Word => [] , layouts = { :Word => [] ,
:List => [] , :List => [] ,
:Message => [:next_message], # Assumtion is that name is the last of message
:Message => [:next_message , :receiver , :frame , :return_address , :caller , :name ],
:MetaClass => [], :MetaClass => [],
:BinaryCode => [], :BinaryCode => [],
:Space => [:classes ,:next_message ,:next_frame, :syscall_message], :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 # TODO go through the virtual parfait layer and adjust function names to what they really are
obj = @class_mappings[:Object ] obj = @class_mappings[:Object ]
[:main , :_get_instance_variable , :_set_instance_variable].each do |f| [: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 end
obj = @class_mappings[:Kernel ] obj = @class_mappings[:Kernel ]
# create dummy main first, __init__ calls it # create dummy main first, __init__ calls it
[:putstring,:exit,:__send , :__init__ ].each do |f| [: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 end
obj = @class_mappings[:Integer ] obj = @class_mappings[:Integer ]
[:putint,:fibo].each do |f| [: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
end end