First part of int allocation

implemented allocate_int
instead of add_new_int
This commit is contained in:
Torsten Ruger 2018-11-21 11:12:39 +02:00
parent 5015a11108
commit bbb7dbef75
7 changed files with 98 additions and 12 deletions

View File

@ -71,6 +71,9 @@ module Mom
next_message_reg! << next_message[:next_message] next_message_reg! << next_message[:next_message]
factory[:next_object] << next_message_reg factory[:next_object] << next_message_reg
# FIXME: Also we relink used messages at the moment. This will have to stop
# when implementing continuations (or block passing/bindings)
# then we may run out and that means cheking and maybe getting more
message[:next_message] << next_message message[:next_message] << next_message
next_message[:caller] << message next_message[:caller] << message
next_message[:method] << callable next_message[:method] << callable

View File

@ -27,6 +27,8 @@ module Mom
caller_reg! << message[:caller] caller_reg! << message[:caller]
caller_reg[:return_value] << object caller_reg[:return_value] << object
factory? << Parfait.object_space.get_factory_for(:Message) factory? << Parfait.object_space.get_factory_for(:Message)
# here we return the current message to the list of messages
# which is only correct without blocks or real continuations
next_message! << factory[:next_object] next_message! << factory[:next_object]
message[:next_message] << next_message message[:next_message] << next_message
factory[:next_object] << message factory[:next_object] << message

View File

@ -21,7 +21,7 @@ module Risc
# make the magic: convert incoming names into registers that have the # make the magic: convert incoming names into registers that have the
# type set according to the name (using resolve_type) # type set according to the name (using resolve_type)
# anmes are stored, so subsequent calls use the same register # names are stored, so subsequent calls use the same register
def method_missing(name , *args) def method_missing(name , *args)
super if args.length != 0 super if args.length != 0
name = name.to_s name = name.to_s
@ -50,10 +50,10 @@ module Risc
reg reg
end end
# infer the type from a symbol. In the simplest case the sybbol is the class name # Infer the type from a symbol. In the simplest case the sybbol is the class name.
# But in building sometimes variations are needed, so next_message or caller work # But in building, sometimes variations are needed, so next_message or caller work
# too (and return Message) # too (and both return "Message")
# A general "_reg"/"_obj" or "_tmp" at the end of the name will be removed # A general "_reg"/"_obj"/"_const" or "_tmp" at the end of the name will be removed
# An error is raised if the symbol/object can not be inferred # An error is raised if the symbol/object can not be inferred
def infer_type( name ) def infer_type( name )
as_string = name.to_s as_string = name.to_s
@ -89,9 +89,9 @@ module Risc
add_code Risc::Branch.new(@source, label) add_code Risc::Branch.new(@source, label)
end end
# to avoid many an if, it can be candy to swap variable names. # To avoid many an if, it can be handy to swap variable names.
# but since the names in the builder are not variables, we need this method # But since the names in the builder are not variables, we need this method.
# as it says, swap the two names around. Names must exist # As it says, swap the two names around. Names must exist
def swap_names(left , right) def swap_names(left , right)
left , right = left.to_s , right.to_s left , right = left.to_s , right.to_s
l = @names[left] l = @names[left]
@ -102,7 +102,19 @@ module Risc
@names[right] = l @names[right] = l
end end
# build code using dsl (see __init__ or MessageSetup for examples) # Reset the names stored by the builder. The names are sort of variables names
# that can be used in the build block due to method_missing magic.
#
# But just as the compiler has reset_regs, the builder has this reset button, to
# start fresh. Quite crude for now, and only used in allocate_int
#
# Compiler regs are reset as well
def reset_names
@names = {}
compiler.reset_regs
end
# Build code using dsl (see __init__ or MessageSetup for examples)
# names (that ruby would resolve to a variable/method) are converted # names (that ruby would resolve to a variable/method) are converted
# to registers. << means assignment and [] is supported both on # to registers. << means assignment and [] is supported both on
# L and R values (but only one at a time). R values may also be constants. # L and R values (but only one at a time). R values may also be constants.
@ -125,8 +137,63 @@ module Risc
return ins return ins
end end
# move a machine int from register "from" to a Parfait::Integer in register "to" # allocate int fetches a new int, for sure. It is a builder method, rather than
# have to grab an integer from space and stick it in the "to" register first. # an inbuilt one, to avoid call overhead for 99.9%
# The factories allocate in 1k, so only when that runs out do we really need a call.
# Note:
# Unfortunately (or so me thinks), this creates code bloat, as the calling is
# included in 100%, but only needed in 0.1. Risc-levelBlocks or Macros may be needed.
# as the calling in (the same) 30-40 instructions for every basic int op.
#
# The method
# - grabs a Integer instance from the Integer factory
# - checks for nil and calls (get_more) for more if needed
# - returns the RiscValue (Regster) where the object is found
#
# The implicit condition is that the method is called at the entry of a method.
# It uses a fair few registers and resets all at the end. The returned object
# will always be in r1, because the method resets, and all others will be clobbered
def allocate_int
compiler.reset_regs
integer = self.integer!
build do
factory! << Parfait.object_space.get_factory_for(:Integer)
integer << factory[:next_object]
object! << Parfait.object_space.nil_object
object - integer
if_not_zero cont_label
integer_2! << factory[:reserve]
factory[:next_object] << integer_2
call_get_more
integer << factory[:next_object]
add_code cont_label
integer_2 << integer[:next_integer]
factory[:next_object] << integer_2
end
reset_names
integer_tmp!
end
# Call_get_more calls the method get_more on the factory (see there).
# From the callers perspective the method ensures there is a next_object.
#
# Calling is three step process
# - setting up the next message
# - moving receiver (factory) and arguments (none)
# - issuing the call
# These steps shadow the MomInstructions MessageSetup, ArgumentTransfer and SimpleCall
def call_get_more
factory = Parfait.object_space.get_factory_for( :Integer )
calling = factory.get_type.get_method( :get_more )
calling = Parfait.object_space.get_main #until we actually parse Factory
Mom::MessageSetup.new( calling ).build_with( self )
self.build do
factory_reg! << factory
message[:receiver] << factory_reg
end
Mom::SimpleCall.new(calling).to_risc(compiler)
end
def add_new_int( source , from, to ) def add_new_int( source , from, to )
to.set_builder( self ) # esecially div10 comes in without having used builder to.set_builder( self ) # esecially div10 comes in without having used builder
from.set_builder( self ) # not named regs, different regs ==> silent errors from.set_builder( self ) # not named regs, different regs ==> silent errors

View File

@ -55,7 +55,7 @@ module Risc
# add a risc instruction after the current (insertion point) # add a risc instruction after the current (insertion point)
# the added instruction will become the new insertion point # the added instruction will become the new insertion point
def add_code( instruction ) def add_code( instruction )
raise "Not an instruction:#{instruction.to_s}" unless instruction.is_a?(Risc::Instruction) raise "Not an instruction:#{instruction.to_s}:#{instruction.class.name}" unless instruction.is_a?(Risc::Instruction)
raise instruction.to_s if( instruction.class.name.split("::").first == "Arm") raise instruction.to_s if( instruction.class.name.split("::").first == "Arm")
new_current = instruction.last #after insertion this point is lost new_current = instruction.last #after insertion this point is lost
@current.insert(instruction) #insert after current @current.insert(instruction) #insert after current

View File

@ -6,6 +6,7 @@ module Risc
class Branch < Instruction class Branch < Instruction
def initialize( source , label ) def initialize( source , label )
super(source) super(source)
raise "not label #{label}:#{label.class}" unless label.is_a?(Label) or label.is_a?(Parfait::BinaryCode)
@label = label @label = label
end end
attr_reader :label attr_reader :label

View File

@ -28,6 +28,11 @@ module Risc
def test_not_alloc_space def test_not_alloc_space
assert_raises {@builder.space} assert_raises {@builder.space}
end end
def test_reset
assert_equal :r1 , @builder.integer!.symbol
@builder.reset_names
assert_equal :r1 , @builder.integer!.symbol # would raise if it existed
end
def test_next_message def test_next_message
reg = @builder.next_message! reg = @builder.next_message!
assert_equal :r1 , reg.symbol assert_equal :r1 , reg.symbol

View File

@ -32,5 +32,13 @@ module Risc
assert_equal :Integer , @builder.space.type.class_name assert_equal :Integer , @builder.space.type.class_name
assert_equal :Space , @builder.integer.type.class_name assert_equal :Space , @builder.integer.type.class_name
end end
def test_allocate_returns
int = @builder.allocate_int
assert_equal :r1 , int.symbol
end
def test_allocate_len
int = @builder.allocate_int
assert_equal 41 , @builder.compiler.risc_instructions.length
end
end end
end end