rubyx/lib/risc/standard_allocator.rb

97 lines
3.1 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( [] )
@reg_names = (0 ... platform.num_registers).collect{|i| "r#{i}".to_sym }
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_reg(name)}
pointer = pointer.next
end
end
def assign(instruction)
names = instruction.register_names
names.each do |for_name|
new_reg = get_reg(for_name)
# swap name out
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 instruction.class.name
instruction.register_names.each do |name|
next if released.include?(name)
@release_points[instruction] << name
released << name
end
released
end
def used_regs_empty?
@used_regs.empty?
end
def use_reg(reg , for_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
end
# if a register has been assigned to the given 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 }
@reg_names.each do |name|
return use_reg(name , for_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)
@used_regs.delete(reg)
end
end
end