fix releasing in allocator

fell into hash new trap, which reuses the object you give it. not good for mutable objects like the array.
also previous logic was broken in terms of machine vs ssa names
This commit is contained in:
Torsten 2020-03-19 14:10:39 +02:00
parent 3f131a4018
commit f13e6dcf57
3 changed files with 42 additions and 11 deletions

View File

@ -13,7 +13,7 @@ module Risc
@compiler = compiler @compiler = compiler
@platform = platform @platform = platform
@used_regs = {} @used_regs = {}
@release_points = Hash.new( [] ) @release_points = Hash.new {|hash , key | hash[key] = [] }
@reg_names = (0 ... platform.num_registers).collect{|i| "r#{i}".to_sym } @reg_names = (0 ... platform.num_registers).collect{|i| "r#{i}".to_sym }
end end
attr_reader :used_regs , :compiler , :platform , :reg_names attr_reader :used_regs , :compiler , :platform , :reg_names
@ -28,11 +28,25 @@ module Risc
pointer = @compiler.risc_instructions pointer = @compiler.risc_instructions
while(pointer) while(pointer)
names = assign(pointer) names = assign(pointer)
names.each {|name| release_reg(name)} names.each {|name| release_after(pointer, name)}
pointer = pointer.next pointer = pointer.next
end end
end end
# if the instruction has a release point with the given name, release it
# Release points store the last use of a register (in ssa)
# This method is called after machine registers have been assigned,
# and give us the chance to reclaim any unsued machine regs
# (via the precalculated release_points)
def release_after(instruction , ssa_name)
release = @release_points[instruction]
return unless release
return unless release.include?(ssa_name)
#puts "ReleasePoint #{ssa_name} for #{instruction} #{release}"
pois = reverse_used(ssa_name)
release_reg( pois ) if pois
end
def assign(instruction) def assign(instruction)
names = instruction.register_names names = instruction.register_names
names.each do |for_name| names.each do |for_name|
@ -52,10 +66,11 @@ module Risc
def walk_and_mark(instruction) def walk_and_mark(instruction)
released = [] released = []
released = walk_and_mark(instruction.next) if instruction.next released = walk_and_mark(instruction.next) if instruction.next
#puts instruction.class.name #puts "Walking #{instruction}"
instruction.register_names.each do |name| instruction.register_names.each do |name|
next if released.include?(name) next if released.include?(name)
@release_points[instruction] << name @release_points[instruction] << name
#puts "ADDING #{name}"
released << name released << name
end end
released released
@ -65,21 +80,32 @@ module Risc
@used_regs.empty? @used_regs.empty?
end end
def use_reg(reg , for_name) # use the given reg (first) parameter and mark it as assigned to
# it's ssa form, the second parameter.
# forward check is trivial, and reverse_used provides reverse check
def use_reg(reg , ssa_name)
reg = reg.symbol if reg.is_a?(RegisterValue) reg = reg.symbol if reg.is_a?(RegisterValue)
raise "Stupid error #{reg}" unless reg.is_a?(Symbol) raise "Stupid error #{reg}" unless reg.is_a?(Symbol)
puts "Using #{reg} for #{for_name}" #puts "Using #{reg} for #{ssa_name}"
@used_regs[reg] = for_name @used_regs[reg] = ssa_name
end end
# if a register has been assigned to the given name, return that # Check whether a register has been assigned to the given ssa form given.
# Ie a reverse check on the used_regs hash
def reverse_used( ssa_name )
@used_regs.each {|reg,name| return reg if ssa_name == name }
return nil
end
# if a register has been assigned to the given ssa name, return that
# #
# otherwise find the first free register by going through the available names # otherwise find the first free register by going through the available names
# and checking if it is used # and checking if it is used
def get_reg(for_name) def get_reg(ssa_name)
@used_regs.each {|reg,name| return reg if for_name == name } name = reverse_used( ssa_name )
return name if name
@reg_names.each do |name| @reg_names.each do |name|
return use_reg(name , for_name) unless @used_regs.has_key?(name) return use_reg(name , ssa_name) unless @used_regs.has_key?(name)
end end
raise "No more registers #{self}" raise "No more registers #{self}"
end end
@ -89,6 +115,7 @@ module Risc
def release_reg( reg ) def release_reg( reg )
reg = reg.symbol if reg.is_a?(RegisterValue) reg = reg.symbol if reg.is_a?(RegisterValue)
raise "not symbol #{reg}:#{reg.class}" unless reg.is_a?(Symbol) raise "not symbol #{reg}:#{reg.class}" unless reg.is_a?(Symbol)
#puts "Releasing #{reg} "
@used_regs.delete(reg) @used_regs.delete(reg)
end end

View File

@ -37,6 +37,10 @@ module Risc
assert_equal Symbol, @allocator.use_reg(:r1, :some).class assert_equal Symbol, @allocator.use_reg(:r1, :some).class
assert @allocator.used_regs.include?(:r1) assert @allocator.used_regs.include?(:r1)
end end
def test_reverse_check
assert_equal Symbol, @allocator.use_reg(:r1, :some).class
assert_equal :r1 , @allocator.reverse_used(:some)
end
def test_add_fail def test_add_fail
assert_raises{ @allocator.use_reg(1)} assert_raises{ @allocator.use_reg(1)}
end end

View File

@ -13,7 +13,7 @@ module Risc
end end
def test_allocate_runs def test_allocate_runs
assert_nil @allocator.allocate_regs assert_nil @allocator.allocate_regs
assert_equal 10 , @allocator.used_regs.length assert_equal 0 , @allocator.used_regs.length
end end
def test_live_length def test_live_length
live = @allocator.walk_and_mark(@compiler.risc_instructions) live = @allocator.walk_and_mark(@compiler.risc_instructions)