diff --git a/lib/risc.rb b/lib/risc.rb index 334c2493..e9810ed8 100644 --- a/lib/risc.rb +++ b/lib/risc.rb @@ -29,7 +29,7 @@ require_relative "risc/linker" require_relative "risc/callable_compiler" require_relative "risc/method_compiler" require_relative "risc/block_compiler" -require_relative "risc/allocator" +require_relative "risc/standard_allocator" require_relative "risc/assembler" require_relative "risc/risc_collection" diff --git a/lib/risc/allocator.rb b/lib/risc/allocator.rb deleted file mode 100644 index 5a7e5302..00000000 --- a/lib/risc/allocator.rb +++ /dev/null @@ -1,64 +0,0 @@ -module Risc - # just moved compilers register related stuff here - # - # Allocator keeps a list of registers and passes them out - # upon request. they must be returned in order - class Allocator - - def initialize( compiler , platform) - @compiler = compiler - @platform = platform - @regs = [] - reset_regs - end - attr_reader :regs , :compiler , :platform - - def regs_empty? - @regs.empty? - end - def add_reg(reg) - raise "not reg #{reg.class}" unless reg.is_a?(RegisterValue) - @regs << reg - end - def pop - @regs.pop - end - def clear_regs - @regs.clear - end - - # require a (temporary) register. code must give this back with release_reg - # Second extra parameter may give extra info about the value, see RegisterValue - def use_reg( type , extra = {} ) - raise "Not type #{type.inspect}:#{type.class}" unless type.is_a?(Symbol) or type.is_a?(Parfait::Type) - if @regs.empty? - reg = Risc.tmp_reg(type , extra) - else - reg = @regs.last.next_reg_use(type , extra) - end - @regs << reg - return reg - end - - def copy( reg , source ) - copied = use_reg reg.type - add_code Register.transfer( source , reg , copied ) - copied - end - - # releasing a register (accuired by use_reg) makes it available for use again - # thus avoiding possibly using too many registers - def release_reg( reg ) - last = @regs.pop - raise "released register in wrong order, expect #{last} but was #{reg}" if reg != last - end - - # reset the registers to be used. Start at r4 for next usage. - # Every statement starts with this, meaning each statement may use all registers, but none - # get saved. Statements have affect on objects. - def reset_regs - @regs.clear - end - - end -end diff --git a/lib/risc/callable_compiler.rb b/lib/risc/callable_compiler.rb index f6a4423d..551172bc 100644 --- a/lib/risc/callable_compiler.rb +++ b/lib/risc/callable_compiler.rb @@ -155,4 +155,3 @@ module Risc end end end -require_relative "allocator" diff --git a/lib/risc/register_value.rb b/lib/risc/register_value.rb index 8b5800b5..e51a4813 100644 --- a/lib/risc/register_value.rb +++ b/lib/risc/register_value.rb @@ -130,14 +130,6 @@ module Risc symbol == other.symbol end - #helper method to calculate with register symbols - def next_reg_use( type , extra = {} ) - int = @symbol[1,3].to_i - raise "No more registers #{self}" if int > 11 - sym = "r#{int + 1}".to_sym - RegisterValue.new( sym , type, extra) - end - def rxf_reference_name @symbol end diff --git a/lib/risc/standard_allocator.rb b/lib/risc/standard_allocator.rb new file mode 100644 index 00000000..156508f2 --- /dev/null +++ b/lib/risc/standard_allocator.rb @@ -0,0 +1,68 @@ +module Risc + # + # StandardAllocator keeps a list of registers whole names it accuires from the + # Plaform. + # + # It allocates registers by going throught the instructions and dishing + # out sequential registers. + # Before doing that it determines live ranges, so registers can be returned + # when not used anymore, and thus reused. + class StandardAllocator + + def initialize( compiler , platform) + @compiler = compiler + @platform = platform + @used_regs = {} + reset_used_regs + end + attr_reader :used_regs , :compiler , :platform + + def used_regs_empty? + @used_regs.empty? + end + + def use_reg(reg) + raise "not reg #{reg.class}" unless reg.is_a?(RegisterValue) + @used_regs[reg.symbol] = reg + end + + # + def release_reg(reg) + @used_regs.pop + end + + def clear_used_regs + @used_regs.clear + end + + #helper method to calculate with register symbols + def next_reg_use( type , extra = {} ) + int = @symbol[1,3].to_i + raise "No more registers #{self}" if int > 11 + sym = "r#{int + 1}".to_sym + RegisterValue.new( sym , type, extra) + end + + def copy( reg , source ) + copied = use_reg reg.type + add_code Register.transfer( source , reg , copied ) + copied + end + + # releasing a register (accuired by use_reg) makes it available for use again + # thus avoiding possibly using too many registers + def release_reg( reg ) + reg = reg.symbol if reg.is_a?(RegisterValue) + raise "not symbol #{reg}:#{reg.class}" unless reg.is_a?(Symbol) + @used_regs.delete(reg) + end + + # reset the registers to be used. Start at r4 for next usage. + # Every statement starts with this, meaning each statement may use all registers, but none + # get saved. Statements have affect on objects. + def reset_used_regs + @used_regs.clear + end + + end +end diff --git a/test/risc/test_allocator.rb b/test/risc/test_allocator.rb deleted file mode 100644 index 10cbc912..00000000 --- a/test/risc/test_allocator.rb +++ /dev/null @@ -1,41 +0,0 @@ -require_relative "../helper" - -module Risc - class TestAllocator < MiniTest::Test - - def setup - Parfait.boot!(Parfait.default_test_options) - @allocator = Allocator.new(Risc.test_compiler , Platform.for(:arm)) - end - def tmp_reg - Risc.tmp_reg(:Type) - end - def test_regs - assert_equal Array , @allocator.regs.class - end - def test_empty - assert @allocator.regs_empty? - end - def test_compiler - assert_equal CallableCompiler , @allocator.compiler.class - assert_equal :fake_name , @allocator.compiler.callable.name - end - def test_platform - assert_equal Arm::ArmPlatform , @allocator.platform.class - end - def test_add_ok - assert_equal Array, @allocator.add_reg(tmp_reg).class - end - def test_add_fail - assert_raises{ @allocator.add_reg(1)} - end - def test_pop - @allocator.add_reg(tmp_reg) - assert_equal RegisterValue , @allocator.pop.class - end - def test_clear - @allocator.clear_regs - assert @allocator.regs_empty? - end - end -end diff --git a/test/risc/test_standard_allocator.rb b/test/risc/test_standard_allocator.rb new file mode 100644 index 00000000..abcf0861 --- /dev/null +++ b/test/risc/test_standard_allocator.rb @@ -0,0 +1,45 @@ +require_relative "../helper" + +module Risc + class TestStandardAllocator < MiniTest::Test + + def setup + Parfait.boot!(Parfait.default_test_options) + @allocator = StandardAllocator.new(Risc.test_compiler , Platform.for(:arm)) + end + def tmp_reg + Risc.tmp_reg(:Type) + end + def test_regs + assert_equal Hash , @allocator.used_regs.class + end + def test_empty + assert @allocator.used_regs_empty? + end + def test_compiler + assert_equal CallableCompiler , @allocator.compiler.class + assert_equal :fake_name , @allocator.compiler.callable.name + end + def test_platform + assert_equal Arm::ArmPlatform , @allocator.platform.class + end + def test_add_ok + assert_equal RegisterValue, @allocator.use_reg(tmp_reg).class + end + def test_add_fail + assert_raises{ @allocator.use_reg(1)} + end + def test_release_reg + @allocator.use_reg(tmp_reg) + assert_equal RegisterValue , @allocator.release_reg(tmp_reg).class + end + def test_remove_symbol + @allocator.use_reg(tmp_reg) + assert_equal RegisterValue , @allocator.release_reg(tmp_reg.symbol).class + end + def test_clear + @allocator.clear_used_regs + assert @allocator.used_regs_empty? + end + end +end