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:
18
lib/risc/builtin/README.md
Normal file
18
lib/risc/builtin/README.md
Normal 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.
|
27
lib/risc/builtin/compile_helper.rb
Normal file
27
lib/risc/builtin/compile_helper.rb
Normal 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
|
75
lib/risc/builtin/integer.rb
Normal file
75
lib/risc/builtin/integer.rb
Normal 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
|
69
lib/risc/builtin/kernel.rb
Normal file
69
lib/risc/builtin/kernel.rb
Normal 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
|
40
lib/risc/builtin/object.rb
Normal file
40
lib/risc/builtin/object.rb
Normal 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
25
lib/risc/builtin/space.rb
Normal 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
52
lib/risc/builtin/word.rb
Normal 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
|
Reference in New Issue
Block a user