new register allocation generates good looking push/pop
This commit is contained in:
parent
b66c4157d5
commit
d7a60f2803
@ -22,12 +22,12 @@ module Ast
|
||||
raise "No receiver error #{inspect}:#{value_receiver}:#{name}" if (value_receiver.nil?)
|
||||
call = Vm::CallSite.new( name , value_receiver , params , function)
|
||||
current_function = context.function
|
||||
current_function.save_locals(context , into) if current_function
|
||||
into.push([]) unless current_function.nil?
|
||||
call.load_args into
|
||||
call.do_call into
|
||||
after = into.new_block("#{into.name}_call#{@@counter+=1}")
|
||||
into.insert_at after
|
||||
current_function.restore_locals(context , after) if current_function
|
||||
after.pop([]) unless current_function.nil?
|
||||
puts "compile call #{function.return_type}"
|
||||
function.return_type
|
||||
end
|
||||
|
@ -11,7 +11,8 @@ module Ast
|
||||
puts "compiling if condition #{cond}"
|
||||
cond_val = cond.compile(context , into)
|
||||
into.b true_block , condition_code: cond_val.operator
|
||||
|
||||
into.branch = true_block
|
||||
|
||||
if_false.each do |part|
|
||||
puts "compiling in if false #{part}"
|
||||
last = part.compile(context , false_block )
|
||||
|
@ -7,6 +7,8 @@ module Ast
|
||||
puts "compiling while condition #{condition}"
|
||||
cond_val = condition.compile(context , while_block)
|
||||
while_block.b ret , condition_code: cond_val.not_operator
|
||||
while_block.branch = ret
|
||||
|
||||
last = nil
|
||||
body.each do |part|
|
||||
puts "compiling in while #{part}"
|
||||
|
@ -25,6 +25,7 @@ module Vm
|
||||
@function = function
|
||||
@name = name.to_sym
|
||||
@next = next_block
|
||||
@branch = nil
|
||||
@codes = []
|
||||
@insert_at = self
|
||||
# keeping track of register usage, left (assigns) or right (uses)
|
||||
@ -33,15 +34,27 @@ module Vm
|
||||
end
|
||||
|
||||
attr_reader :name , :next , :codes , :function , :assigns , :uses
|
||||
attr_accessor :branch
|
||||
|
||||
def reachable
|
||||
ret = []
|
||||
add_next ret
|
||||
add_branch ret
|
||||
ret
|
||||
end
|
||||
|
||||
def add_code(kode)
|
||||
raise "alarm #{kode}" if kode.is_a? Word
|
||||
raise "alarm #{kode.class} #{kode}" unless kode.is_a? Code
|
||||
@assigns += kode.assigns
|
||||
@uses += kode.uses
|
||||
@insert_at.codes << kode
|
||||
@insert_at.do_add kode
|
||||
self
|
||||
end
|
||||
def do_add kode
|
||||
kode.assigns.each { |a| (@assigns << a) unless @assigns.include?(a) }
|
||||
kode.uses.each { |use| (@uses << use) unless (@assigns.include?(use) or @uses.include?(use)) }
|
||||
#puts "IN ADD #{name}#{uses}"
|
||||
@codes << kode
|
||||
end
|
||||
alias :<< :add_code
|
||||
|
||||
# create a new linear block after this block. Linear means there is no brach needed from this one
|
||||
@ -85,6 +98,13 @@ module Vm
|
||||
add_code RegisterMachine.instance.send(meth , *args)
|
||||
end
|
||||
|
||||
# returns if this is a block that ends in a call (and thus needs local variable handling)
|
||||
def call_block?
|
||||
return false unless codes.last.is_a?(CallInstruction)
|
||||
return false unless codes.last.opcode == :call
|
||||
codes.dup.reverse.find{ |c| c.is_a? StackInstruction }
|
||||
end
|
||||
|
||||
# Code interface follows. Note position is inheitted as is from Code
|
||||
|
||||
# length of the block is the length of it's codes, plus any next block (ie no branch follower)
|
||||
@ -116,5 +136,19 @@ module Vm
|
||||
end
|
||||
@next.assemble(io) if @next
|
||||
end
|
||||
|
||||
private
|
||||
def add_next ret
|
||||
return if @next.nil?
|
||||
return if ret.include? @next
|
||||
ret << @next
|
||||
ret + @next.reachable
|
||||
end
|
||||
def add_branch ret
|
||||
return if @branch.nil?
|
||||
return if ret.include? @branch
|
||||
ret << @branch
|
||||
ret + @branch.reachable
|
||||
end
|
||||
end
|
||||
end
|
@ -56,7 +56,7 @@ module Vm
|
||||
@body = Block.new("body", self , @return)
|
||||
@entry = Core::Kernel::function_entry( Vm::Block.new("entry" , self , @body) ,name )
|
||||
@locals = []
|
||||
@blocks = []
|
||||
@linked = false # incase link is called twice, we only calculate locals once
|
||||
end
|
||||
|
||||
attr_reader :args , :entry , :exit , :body , :name , :return_type , :receiver
|
||||
@ -81,46 +81,53 @@ module Vm
|
||||
@locals << l
|
||||
l
|
||||
end
|
||||
#BUG - must save receiver
|
||||
|
||||
def save_locals context , into
|
||||
save = args.collect{|a| a.register_symbol } + @locals.collect{|l| l.register_symbol}
|
||||
into.push(save) unless save.empty?
|
||||
end
|
||||
|
||||
def restore_locals context , into
|
||||
#TODO assumes allocation in order, as the pop must be get regs in ascending order (also push)
|
||||
restore = args.collect{|a| a.register_symbol } + @locals.collect{|l| l.register_symbol}
|
||||
into.pop(restore) unless restore.empty?
|
||||
end
|
||||
|
||||
def new_block name
|
||||
block = Block.new(name , self)
|
||||
@blocks << block
|
||||
block
|
||||
# return a list of registers that are still in use after the given block
|
||||
# a call_site uses pushes and pops these to make them available for code after a call
|
||||
def locals_at l_block
|
||||
used =[]
|
||||
assigned = []
|
||||
l_block.reachable.each do |b|
|
||||
b.uses.each {|u|
|
||||
(used << u) unless assigned.include?(u)
|
||||
}
|
||||
assigned += b.assigns
|
||||
end
|
||||
used.uniq
|
||||
end
|
||||
|
||||
# return a list of the blocks that are addressable, ie entry and @blocks and all next
|
||||
def blocks
|
||||
ret = []
|
||||
(@blocks << @entry).each do |b|
|
||||
while b
|
||||
ret << b
|
||||
b = b.next
|
||||
end
|
||||
end
|
||||
b = @entry
|
||||
while b
|
||||
ret << b
|
||||
b = b.next
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
# following id the Code interface
|
||||
|
||||
# to link we link the entry and then any blocks. The entry links the straight line
|
||||
def link_at address , context
|
||||
super #just sets the position
|
||||
@entry.link_at address , context
|
||||
address += @entry.length
|
||||
@blocks.each do |block|
|
||||
block.link_at(pos , context)
|
||||
pos += block.length
|
||||
return if @linked
|
||||
@linked = true
|
||||
blocks.each do |b|
|
||||
if push = b.call_block?
|
||||
locals = locals_at b
|
||||
if(locals.empty?)
|
||||
puts "Empty #{b}"
|
||||
else
|
||||
puts "PUSH #{push}"
|
||||
push.set_registers(locals)
|
||||
pop = b.next.codes.first
|
||||
puts "POP #{pop}"
|
||||
pop.set_registers(locals)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -132,16 +139,12 @@ module Vm
|
||||
# length of a function is the entry block length (includes the straight line behind it)
|
||||
# plus any out of line blocks that have been added
|
||||
def length
|
||||
@blocks.inject(@entry.length) {| sum , item | sum + item.length}
|
||||
@entry.length
|
||||
end
|
||||
|
||||
# assembling assembles the entry (straight line/ no branch line) + any additional branches
|
||||
def assemble io
|
||||
@entry.assemble(io)
|
||||
@blocks.each do |block|
|
||||
block.assemble io
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -62,6 +62,10 @@ module Vm
|
||||
@first = first
|
||||
super(options)
|
||||
end
|
||||
# when calling we place a dummy push/pop in the stream and calculate later what registers actually need saving
|
||||
def set_registers regs
|
||||
@first = regs.collect{ |r| r.symbol }
|
||||
end
|
||||
def is_push?
|
||||
opcode == :push
|
||||
end
|
||||
|
@ -27,16 +27,16 @@ module Vm
|
||||
def get_function name
|
||||
name = name.to_sym
|
||||
f = @functions.detect{ |f| f.name == name }
|
||||
puts "no function for #{name} in Meta #{@me_self.inspect}" unless f
|
||||
puts "no function for :#{name} in Meta #{@me_self.inspect}" unless f
|
||||
f
|
||||
end
|
||||
# way of creating new functions that have not been parsed.
|
||||
def get_or_create_function name
|
||||
fun = get_function name
|
||||
unless fun or name == :Object
|
||||
supr = @context.object_space.get_or_create_class(@super_class)
|
||||
supr = @me_self.context.object_space.get_or_create_class(@super_class)
|
||||
fun = supr.get_function name
|
||||
puts "#{supr.functions.collect(&:name)} for #{name} GOT #{fun.class}" if name == :index_of
|
||||
puts "#{supr.functions.collect(&:name)} for #{name} GOT #{fun.class}"
|
||||
end
|
||||
fun
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user