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,67 @@
module Risc
# a branch must branch to a block.
class Branch < Instruction
def initialize source , to
super(source)
@label = to
end
attr_reader :label
def to_s
"#{self.class.name.split("::").last}: #{label ? label.name : ''}"
end
alias :inspect :to_s
def length labels = []
ret = super(labels)
ret += self.label.length(labels) if self.label
ret
end
def to_arr( labels = [] )
ret = super(labels)
ret += self.label.to_arr(labels) if self.label
ret
end
def total_byte_length labels = []
ret = super(labels)
ret += self.label.total_byte_length(labels) if self.label
#puts "#{self.class.name} return #{ret}"
ret
end
# labels have the same position as their next
def set_position position , labels = []
set_position self.label.set_position( position , labels ) if self.label
super(position,labels)
end
def assemble_all io , labels = []
self.assemble(io)
self.label.assemble_all(io,labels) if self.label
self.next.assemble_all(io, labels) if self.next
end
def each_label labels =[] , &block
super
self.label.each_label(labels , &block) if self.label
end
end
class IsZero < Branch
end
class IsNotzero < Branch
end
class IsMinus < Branch
end
class IsPlus < Branch
end
end

View File

@ -0,0 +1,20 @@
module Risc
# ByteToReg moves a single byte into a register from memory.
# indexes are 1 based (as for slots) , which means we sacrifice a byte of every word
# for our sanity
class ByteToReg < Getter
end
# Produce a ByteToReg instruction.
# from and to are translated (from symbol to register if neccessary)
# but index is left as is.
def self.byte_to_reg( source , array , index , to)
array = resolve_to_register( array)
to = resolve_to_register( to)
ByteToReg.new( source , array , index , to)
end
end

View File

@ -0,0 +1,33 @@
module Risc
# name says it all really
# only arg is the method object we want to call
# assembly takes care of the rest (ie getting the address)
class FunctionCall < Instruction
def initialize( source , method )
super(source)
@method = method
end
attr_reader :method
def to_s
"FunctionCall: #{method.name}"
end
end
def self.function_call( source , method )
Risc::FunctionCall.new( source , method )
end
def self.issue_call( compiler , callee )
return_label = Risc.label("_return_label #{callee.name}" , "#{compiler.type.object_class.name}.#{compiler.method.name}" )
ret_tmp = compiler.use_reg(:Label)
compiler.add_load_constant("#{callee.name} load ret", return_label , ret_tmp)
compiler.add_reg_to_slot("#{callee.name} store ret", ret_tmp , :new_message , :return_address)
compiler.add_transfer("#{callee.name} move new message", Risc.new_message_reg , Risc.message_reg )
compiler.add_code Risc.function_call( "#{callee.name} call" , callee )
compiler.add_code return_label
compiler.add_transfer("#{callee.name} remove new message", Risc.message_reg , Risc.new_message_reg )
compiler.add_slot_to_reg("#{callee.name} restore message" , :new_message , :caller , :message )
end
end

View File

@ -0,0 +1,22 @@
module Risc
# return from a function call
# register and index specify where the return address is stored
class FunctionReturn < Instruction
def initialize( source , register , index)
super(source)
@register = register
@index = index
end
attr_reader :register , :index
def to_s
"FunctionReturn: #{register} [#{index}]"
end
end
def self.function_return( source , register , index)
FunctionReturn.new( source , register , index)
end
end

View File

@ -0,0 +1,38 @@
module Risc
# Getter is a base class for get instructions (SlotToReg and ByteToReg , possibly more coming)
#
# The instruction that is modelled is loading data from an array into a register
#
# Getter has a
# - an array where the data comes from
# - and (array) index
# - Risc that the data is moved to
# Getter and Setter api follow the pattern from -> to
class Getter < Instruction
# If you had a c array and index offset
# the instruction would do register = array[index]
# The arguments are in the order that makes sense for the Instruction name
# So SlotToReg means the slot (array and index) moves to the register (last argument)
def initialize source , array , index , register
super(source)
@array = array
@index = index
@register = register
raise "index 0 " if index == 0
raise "Not integer or reg #{index}" unless index.is_a?(Numeric) or RiscValue.look_like_reg(index)
raise "Not register #{register}" unless RiscValue.look_like_reg(register)
raise "Not register #{array}" unless RiscValue.look_like_reg(array)
end
attr_accessor :array , :index , :register
def to_s
"#{self.class.name.split("::").last}: #{array}[#{index}] -> #{register}"
end
end
end

View File

@ -0,0 +1,85 @@
module Risc
# A label is a placeholder for it's next Instruction
# It's function is not to turn into code, but to be a valid brnch target
#
# So branches and Labels are pairs, fan out, fan in
#
#
class Label < Instruction
def initialize( source , name , nekst = nil)
super(source , nekst)
@name = name
end
attr_reader :name
def to_s
"Label: #{@name} (#{self.next.class.name.split("::").last})"
end
def sof_reference_name
@name
end
# a method start has a label of the form Class.method , test for that
def is_method
@name.split(".").length == 2
end
def to_arr labels = []
return [] if labels.include?(self)
labels << self
super
end
def length labels = []
return 0 if labels.include?(self)
labels << self
ret = 1
ret += self.next.length(labels) if self.next
ret
end
def assemble io
end
def assemble_all io , labels = []
return if labels.include?(self)
labels << self
self.next.assemble_all(io,labels)
end
def total_byte_length labels = []
return 0 if labels.include?(self)
labels << self
ret = self.next.total_byte_length(labels)
#puts "#{self.class.name} return #{ret}"
ret
end
# labels have the same position as their next
def set_position position , labels = []
return position if labels.include?(self)
labels << self
super(position , labels)
self.next.set_position(position,labels)
end
# shame we need this, just for logging
def byte_length
0
end
def each_label labels =[] , &block
return if labels.include?(self)
labels << self
block.yield(self)
super
end
end
def self.label( source , name , nekst = nil)
Label.new( source , name , nekst = nil)
end
end

View File

@ -0,0 +1,36 @@
module Risc
# load a constant into a register
#
# first is the actual constant, either immediate register or object reference (from the space)
# second argument is the register the constant is loaded into
class LoadConstant < Instruction
def initialize source , constant , register
super(source)
@register = register
@constant = constant
end
attr_accessor :register , :constant
def to_s
"LoadConstant: #{register} <- #{constant_str}"
end
private
def constant_str
case @constant
when String , Symbol , Fixnum , Integer
@constant.to_s
else
if( @constant.respond_to? :sof_reference_name )
constant.sof_reference_name
else
constant.class.name.to_s
end
end
end
end
def self.load_constant( source , constant , register )
LoadConstant.new source , constant , register
end
end

View File

@ -0,0 +1,21 @@
module Risc
class OperatorInstruction < Instruction
def initialize source , operator , left , right
super(source)
@operator = operator
@left = left
@right = right
end
attr_reader :operator, :left , :right
def to_s
"OperatorInstruction: #{left} #{operator} #{right}"
end
end
def self.op source , operator , left , right
OperatorInstruction.new source , operator , left , right
end
end

View File

@ -0,0 +1,21 @@
module Risc
# RegToByte moves a byte into memory from a register.
# indexes are 1 based !
class RegToByte < Setter
end
# Produce a RegToByte instruction.
# from and to are translated (from symbol to register if neccessary)
# but index is left as is.
def self.reg_to_byte( source , from , to , index)
from = resolve_to_register from
index = resolve_to_index( to , index)
to = resolve_to_register to
RegToByte.new( source, from , to , index)
end
end

View File

@ -0,0 +1,26 @@
module Risc
# RegToSlot moves data into memory from a register.
# SlotToReg moves data into a register from memory.
# Both use a base memory (a register)
# This is because that is what cpu's can do. In programming terms this would be accessing
# an element in an array, in the case of RegToSlot setting the register in the array.
# btw: to move data between registers, use RiscTransfer
class RegToSlot < Setter
end
# Produce a RegToSlot instruction.
# From and to are registers or symbols that can be transformed to a register by resolve_to_register
# index resolves with resolve_to_index.
def self.reg_to_slot source , from , to , index
from = resolve_to_register from
index = resolve_to_index( to , index)
to = resolve_to_register to
RegToSlot.new( source, from , to , index)
end
end

View File

@ -0,0 +1,35 @@
module Risc
# transfer the constents of one register to another.
# possibly called move in some cpus
# There are other instructions to move data from / to memory, namely SlotToReg and RegToSlot
# Get/Set Slot move data around in vm objects, but transfer moves the objects (in the machine)
#
# Also it is used for moving temorary data
#
class RiscTransfer < Instruction
# initialize with from and to registers.
# First argument from
# second argument to
#
# Note: this may be reversed from some assembler notations (also arm)
def initialize source , from , to
super(source)
@from = from
@to = to
raise "Fix me #{from}" unless from.is_a? RiscValue
raise "Fix me #{to}" unless to.is_a? RiscValue
end
attr_reader :from, :to
def to_s
"RiscTransfer: #{from} -> #{to}"
end
end
def self.transfer( source , from , to)
RiscTransfer.new( source , from , to)
end
end

View File

@ -0,0 +1,36 @@
module Risc
# Setter is a base class for set instructions (RegToSlot and RegToByte , possibly more coming)
#
# The instruction that is modelled is loading data from a register into an array
#
# Setter has a
# - Risc that the data is comes from
# - an array where the data goes
# - and (array) index
# Getter and Setter api follow the pattern from -> to
class Setter < Instruction
# If you had a c array and index offset
# the instruction would do array[index] = register
# So Setter means the register (first argument) moves to the slot (array and index)
def initialize source , register , array , index
super(source)
@register = register
@array = array
@index = index
raise "index 0 " if index == 0
raise "Not integer or reg #{index}" unless index.is_a?(Numeric) or RiscValue.look_like_reg(index)
raise "Not register #{register}" unless RiscValue.look_like_reg(register)
raise "Not register #{array}" unless RiscValue.look_like_reg(array)
end
attr_accessor :register , :array , :index
def to_s
"#{self.class.name.split("::").last}: #{register} -> #{array}[#{index}]"
end
end
end

View File

@ -0,0 +1,25 @@
module Risc
# SlotToReg moves data into a register from memory.
# RegToSlot moves data into memory from a register.
# Both use a base memory (a register)
# This is because that is what cpu's can do. In programming terms this would be accessing
# an element in an array, in the case of SlotToReg setting the value in the array.
# btw: to move data between registers, use RiscTransfer
class SlotToReg < Getter
end
# Produce a SlotToReg instruction.
# Array and to are registers or symbols that can be transformed to a register by resolve_to_register
# index resolves with resolve_to_index.
def self.slot_to_reg source , array , index , to
index = resolve_to_index( array , index)
array = resolve_to_register( array )
to = resolve_to_register( to )
SlotToReg.new( source , array , index , to)
end
end

View File

@ -0,0 +1,23 @@
module Risc
# name says it all really
# only arg is the method syscall name
# how the layer below executes these is really up to it
# Any function issuing a Syscall should also save the current message
# and restore it after the syscall, saving the return value in Return_index
class Syscall < Instruction
def initialize source ,name
super(source)
@name = name
end
attr_reader :name
def to_s
"Syscall: #{name}"
end
end
end