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
@platform = platform
@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 }
end
attr_reader :used_regs , :compiler , :platform , :reg_names
@ -28,11 +28,25 @@ module Risc
pointer = @compiler.risc_instructions
while(pointer)
names = assign(pointer)
names.each {|name| release_reg(name)}
names.each {|name| release_after(pointer, name)}
pointer = pointer.next
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)
names = instruction.register_names
names.each do |for_name|
@ -52,10 +66,11 @@ module Risc
def walk_and_mark(instruction)
released = []
released = walk_and_mark(instruction.next) if instruction.next
#puts instruction.class.name
#puts "Walking #{instruction}"
instruction.register_names.each do |name|
next if released.include?(name)
@release_points[instruction] << name
#puts "ADDING #{name}"
released << name
end
released
@ -65,21 +80,32 @@ module Risc
@used_regs.empty?
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)
raise "Stupid error #{reg}" unless reg.is_a?(Symbol)
puts "Using #{reg} for #{for_name}"
@used_regs[reg] = for_name
#puts "Using #{reg} for #{ssa_name}"
@used_regs[reg] = ssa_name
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
# and checking if it is used
def get_reg(for_name)
@used_regs.each {|reg,name| return reg if for_name == name }
def get_reg(ssa_name)
name = reverse_used( ssa_name )
return name if 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
raise "No more registers #{self}"
end
@ -89,6 +115,7 @@ module Risc
def release_reg( reg )
reg = reg.symbol if reg.is_a?(RegisterValue)
raise "not symbol #{reg}:#{reg.class}" unless reg.is_a?(Symbol)
#puts "Releasing #{reg} "
@used_regs.delete(reg)
end

View File

@ -37,6 +37,10 @@ module Risc
assert_equal Symbol, @allocator.use_reg(:r1, :some).class
assert @allocator.used_regs.include?(:r1)
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
assert_raises{ @allocator.use_reg(1)}
end

View File

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