Torsten
d2e7c647d0
unfortunately the reg instances are spread across instructions this causes problems when setting them
135 lines
4.8 KiB
Ruby
135 lines
4.8 KiB
Ruby
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 = {}
|
|
@release_points = Hash.new {|hash , key | hash[key] = [] }
|
|
@reg_names = platform.register_names
|
|
end
|
|
attr_reader :used_regs , :compiler , :platform , :reg_names
|
|
|
|
# main entry point and the whole reason for the class.
|
|
# Allocate regs by changing the names of compiler instructions to
|
|
# register names according to platform.
|
|
# Ie on arm register names are r0 .. . r15 , so it keeps a list of unused
|
|
# regs and frees regs according to live ranges
|
|
def allocate_regs
|
|
walk_and_mark(@compiler.risc_instructions)
|
|
pointer = @compiler.risc_instructions
|
|
while(pointer)
|
|
names = assign(pointer)
|
|
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)
|
|
#puts "release request for #{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
|
|
#puts "AT #{instruction}"
|
|
names.each do |ssa_name|
|
|
# BUG: clearly we do NOT do ssa, because some of the instances of
|
|
# RegisterValue are the same. This causes the symbols/names to
|
|
# have changed through the previous assign. Hence without next line check
|
|
# we assign risc regs to risc reg names. Easily avoided, but not clean
|
|
next if @reg_names.include?(ssa_name)
|
|
new_reg = get_reg(ssa_name)
|
|
instruction.set_registers( ssa_name , new_reg)
|
|
#puts "Assign #{new_reg} for #{ssa_name}"
|
|
end
|
|
names
|
|
end
|
|
|
|
# determines when registers can be freed
|
|
#
|
|
# this is done by walking the instructions backwards and saving the first
|
|
# occurence of a register name. (The last, as we walk backwards)
|
|
#
|
|
# First walk down, and on the way up mark register occurences, unless they
|
|
# have been marked already
|
|
def walk_and_mark(instruction)
|
|
released = []
|
|
released = walk_and_mark(instruction.next) if instruction.next
|
|
#puts "Walking #{instruction}"
|
|
instruction.register_names.each do |ssa_name|
|
|
next if released.include?(ssa_name)
|
|
@release_points[instruction] << ssa_name
|
|
#puts "ADDING #{ssa_name}"
|
|
released << ssa_name
|
|
end
|
|
released
|
|
end
|
|
|
|
# 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 #{ssa_name}"
|
|
@used_regs[reg] = ssa_name
|
|
reg
|
|
end
|
|
|
|
# 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(ssa_name)
|
|
name = reverse_used( ssa_name )
|
|
return name if name
|
|
get_next_free(ssa_name)
|
|
end
|
|
|
|
def get_next_free(ssa_name)
|
|
reg = platform.assign_reg?( ssa_name )
|
|
return use_reg(reg , ssa_name) if reg
|
|
@reg_names.each do |name|
|
|
return use_reg(name , ssa_name) unless @used_regs.has_key?(name)
|
|
end
|
|
raise "No more registers #{self}"
|
|
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)
|
|
#puts "Releasing #{reg} "
|
|
@used_regs.delete(reg)
|
|
end
|
|
|
|
end
|
|
end
|