rename register to risc

seems to fit the layer much better as we really have a very reduced
instruction set
This commit is contained in:
Torsten Ruger
2017-01-19 09:02:29 +02:00
parent da5823a1a0
commit aa79e41d1c
127 changed files with 348 additions and 346 deletions

View File

@ -0,0 +1,18 @@
### Builtin module
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. (see register/boot.rb)
These functions return their code, ie a Parfait::TypedMethod with a MethodSource 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.
Slightly more here : http://ruby-x.org/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.

View File

@ -0,0 +1,27 @@
module Risc
module Builtin
module CompileHelper
def self_and_int_arg(compiler , source)
me = compiler.process( Vm::Tree::KnownName.new( :self) )
int_arg = load_int_arg_at(compiler , source , 1 )
return me , int_arg
end
def compiler_for( type , method_name , extra_args = {})
args = {:index => :Integer}.merge( extra_args )
Vm::MethodCompiler.create_method(type , method_name , args ).init_method
end
# Load the value
def load_int_arg_at(compiler, source , at)
int_arg = compiler.use_reg :Integer
compiler.add_slot_to_reg(source , :message , :arguments , int_arg )
compiler.add_slot_to_reg(source , int_arg , at + 1, int_arg ) #1 for type
return int_arg
end
end
end
end

View File

@ -0,0 +1,75 @@
#integer related kernel functions
module Risc
module Builtin
module Integer
module ClassMethods
include AST::Sexp
def mod4 context
compiler = Vm::MethodCompiler.create_method(:Integer,:mod4 ).init_method
return compiler.method
end
def putint context
compiler = Vm::MethodCompiler.create_method(:Integer,:putint ).init_method
return compiler.method
end
def div10 context
s = "div_10"
compiler = Vm::MethodCompiler.create_method(:Integer,:div10 ).init_method
me = compiler.process( Vm::Tree::KnownName.new( :self) )
tmp = compiler.process( Vm::Tree::KnownName.new( :self) )
q = compiler.process( Vm::Tree::KnownName.new( :self) )
const = compiler.process( Vm::Tree::IntegerExpression.new(1) )
# int tmp = self >> 1
compiler.add_code Risc.op( s , ">>" , tmp , const)
# int q = self >> 2
compiler.add_load_constant( s , 2 , const)
compiler.add_code Risc.op( s , ">>" , q , const)
# q = q + tmp
compiler.add_code Risc.op( s , "+" , q , tmp )
# tmp = q >> 4
compiler.add_load_constant( s , 4 , const)
compiler.add_transfer( s, q , tmp)
compiler.add_code Risc.op( s , ">>" , tmp , const)
# q = q + tmp
compiler.add_code Risc.op( s , "+" , q , tmp )
# tmp = q >> 8
compiler.add_load_constant( s , 8 , const)
compiler.add_transfer( s, q , tmp)
compiler.add_code Risc.op( s , ">>" , tmp , const)
# q = q + tmp
compiler.add_code Risc.op( s , "+" , q , tmp )
# tmp = q >> 16
compiler.add_load_constant( s , 16 , const)
compiler.add_transfer( s, q , tmp)
compiler.add_code Risc.op( s , ">>" , tmp , const)
# q = q + tmp
compiler.add_code Risc.op( s , "+" , q , tmp )
# q = q >> 3
compiler.add_load_constant( s , 3 , const)
compiler.add_code Risc.op( s , ">>" , q , const)
# tmp = q * 10
compiler.add_load_constant( s , 10 , const)
compiler.add_transfer( s, q , tmp)
compiler.add_code Risc.op( s , "*" , tmp , const)
# tmp = self - tmp
compiler.add_code Risc.op( s , "-" , me , tmp )
compiler.add_transfer( s , me , tmp)
# tmp = tmp + 6
compiler.add_load_constant( s , 6 , const)
compiler.add_code Risc.op( s , "+" , tmp , const )
# tmp = tmp >> 4
compiler.add_load_constant( s , 4 , const)
compiler.add_code Risc.op( s , ">>" , tmp , const )
# return q + tmp
compiler.add_code Risc.op( s , "+" , q , tmp )
compiler.add_reg_to_slot( s , q , :message , :return_value)
return compiler.method
end
end
extend ClassMethods
end
end
end

View File

@ -0,0 +1,69 @@
module Risc
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
compiler = Vm::MethodCompiler.create_method(:Kernel,:__init__ )
new_start = Risc.label("__init__ start" , "__init__" )
compiler.method.set_instructions( new_start)
compiler.set_current new_start
space = Parfait.object_space
space_reg = compiler.use_reg(:Space) #Set up the Space as self upon init
compiler.add_load_constant("__init__ load Space", space , space_reg)
message_ind = Risc.resolve_to_index( :space , :first_message )
compiler.add_slot_to_reg( "__init__ load 1st message" , space_reg , message_ind , :message)
compiler.add_reg_to_slot( "__init__ store Space in message", space_reg , :message , :receiver)
#fixme: should add arg type here, as done in call_site (which this sort of is)
exit_label = Risc.label("_exit_label for __init__" , "#{compiler.type.object_class.name}.#{compiler.method.name}" )
ret_tmp = compiler.use_reg(:Label)
compiler.add_load_constant("__init__ load return", exit_label , ret_tmp)
compiler.add_reg_to_slot("__init__ store return", ret_tmp , :message , :return_address)
compiler.add_code Risc.function_call( "__init__ issue call" , Parfait.object_space.get_main )
compiler.add_code exit_label
emit_syscall( compiler , :exit )
return compiler.method
end
def exit context
compiler = Vm::MethodCompiler.create_method(:Kernel,:exit ).init_method
emit_syscall( compiler , :exit )
return compiler.method
end
def emit_syscall compiler , name
save_message( compiler )
compiler.add_code Syscall.new("emit_syscall(#{name})", name )
restore_message(compiler)
return unless (@clazz and @method)
compiler.add_label( "#{@clazz.name}.#{@message.name}" , "return_syscall" )
end
# save the current message, as the syscall destroys all context
#
# This relies on linux to save and restore all registers
#
def save_message(compiler)
r8 = RiscValue.new( :r8 , :Message)
compiler.add_transfer("save_message", Risc.message_reg , r8 )
end
def restore_message(compiler)
r8 = RiscValue.new( :r8 , :Message)
return_tmp = Risc.tmp_reg :Integer
source = "_restore_message"
# get the sys return out of the way
compiler.add_transfer(source, Risc.message_reg , return_tmp )
# load the stored message into the base RiscMachine
compiler.add_transfer(source, r8 , Risc.message_reg )
# save the return value into the message
compiler.add_reg_to_slot( source , return_tmp , :message , :return_value )
end
end
extend ClassMethods
end
end
end

View File

@ -0,0 +1,40 @@
require_relative "compile_helper"
module Risc
module Builtin
class Object
module ClassMethods
include CompileHelper
# self[index] basically. Index is the first arg
# return is stored in return_value
# (this method returns a new method off course, like all builtin)
def get_internal_word context
compiler = compiler_for(:Object , :get_internal_word )
source = "get_internal_word"
me , index = self_and_int_arg(compiler,source)
# reduce me to me[index]
compiler.add_slot_to_reg( source , me , index , me)
# and put it back into the return value
compiler.add_reg_to_slot( source , me , :message , :return_value)
return compiler.method
end
# self[index] = val basically. Index is the first arg , value the second
# no return
def set_internal_word context
compiler = compiler_for(:Object , :set_internal_word , {:value => :Object} )
source = "set_internal_word"
me , index = self_and_int_arg(compiler,source)
value = load_int_arg_at(compiler,source , 2)
# do the set
compiler.add_reg_to_slot( source , value , me , index)
return compiler.method
end
end
extend ClassMethods
end
end
end

25
lib/risc/builtin/space.rb Normal file
View File

@ -0,0 +1,25 @@
require "ast/sexp"
module Risc
module Builtin
class Space
module ClassMethods
include AST::Sexp
# main entry point, ie __init__ calls this
# defined here as empty, to be redefined
def main context
compiler = Vm::MethodCompiler.create_method(:Space , :main ).init_method
return compiler.method
end
end
extend ClassMethods
end
end
end
require_relative "integer"
require_relative "object"
require_relative "kernel"
require_relative "word"

52
lib/risc/builtin/word.rb Normal file
View File

@ -0,0 +1,52 @@
require_relative "compile_helper"
module Risc
module Builtin
module Word
module ClassMethods
include CompileHelper
def putstring context
compiler = Vm::MethodCompiler.create_method(:Word , :putstring ).init_method
compiler.add_slot_to_reg( "putstring" , :message , :receiver , :new_message )
index = Parfait::Word.get_length_index
reg = RiscValue.new(:r2 , :Integer)
compiler.add_slot_to_reg( "putstring" , :new_message , index , reg )
Kernel.emit_syscall( compiler , :putstring )
compiler.method
end
# self[index] basically. Index is the first arg > 0
# return (and word sized int) is stored in return_value
def get_internal_byte context
compiler = compiler_for(:Word , :get_internal_byte)
source = "get_internal_byte"
me , index = self_and_int_arg(compiler,source)
# reduce me to me[index]
compiler.add_byte_to_reg( source , me , index , me)
# and put it back into the return value
compiler.add_reg_to_slot( source , me , :message , :return_value)
return compiler.method
end
# self[index] = val basically. Index is the first arg ( >0),
# value the second
# no return
def set_internal_byte context
compiler = compiler_for(:Word, :set_internal_byte , {:value => :Integer} )
args = compiler.method.arguments
len = args.instance_length
raise "Compiler arg number mismatch, method=#{args} " if len != 3
source = "set_internal_byte"
me , index = self_and_int_arg(compiler,source)
value = load_int_arg_at(compiler , source , 2 )
# do the set
compiler.add_reg_to_byte( source , value , me , index)
return compiler.method
end
end
extend ClassMethods
end
end
end