2014-08-13 10:59:51 +02:00
|
|
|
module Virtual
|
2015-05-13 11:22:51 +02:00
|
|
|
|
2014-08-13 10:59:51 +02:00
|
|
|
# Think flowcharts: blocks are the boxes. The smallest unit of linear code
|
2015-05-13 11:22:51 +02:00
|
|
|
|
|
|
|
# Blocks must end in control instructions (jump/call/return).
|
|
|
|
# And the only valid argument for a jump is a Block
|
|
|
|
|
2014-08-13 10:59:51 +02:00
|
|
|
# Blocks form a graph, which is managed by the method
|
2015-05-13 11:22:51 +02:00
|
|
|
|
2015-05-24 19:00:11 +02:00
|
|
|
class Block
|
2014-08-13 10:59:51 +02:00
|
|
|
|
|
|
|
def initialize(name , method )
|
|
|
|
super()
|
|
|
|
@method = method
|
|
|
|
@name = name.to_sym
|
|
|
|
@branch = nil
|
|
|
|
@codes = []
|
|
|
|
end
|
|
|
|
|
2014-09-16 16:16:56 +02:00
|
|
|
attr_reader :name , :codes , :method
|
2014-08-13 10:59:51 +02:00
|
|
|
attr_accessor :branch
|
2015-05-13 11:22:51 +02:00
|
|
|
|
2014-08-13 10:59:51 +02:00
|
|
|
def reachable ret = []
|
|
|
|
add_next ret
|
|
|
|
add_branch ret
|
|
|
|
ret
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_code kode
|
|
|
|
@codes << kode
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-08-21 16:46:12 +02:00
|
|
|
# replace a code with an array of new codes. This is what happens in passes all the time
|
2014-08-22 14:08:46 +02:00
|
|
|
def replace code , new_codes
|
2014-08-21 16:46:12 +02:00
|
|
|
index = @codes.index code
|
|
|
|
raise "Code not found #{code} in #{self}" unless index
|
|
|
|
@codes.delete_at(index)
|
2014-08-30 15:57:56 +02:00
|
|
|
if( new_codes.is_a? Array)
|
|
|
|
new_codes.reverse.each {|c| @codes.insert(index , c)}
|
|
|
|
else
|
|
|
|
@codes.insert(index , new_codes)
|
|
|
|
end
|
2014-08-21 16:46:12 +02:00
|
|
|
end
|
|
|
|
|
2014-08-13 10:59:51 +02:00
|
|
|
# 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
|
|
|
|
|
2014-08-30 12:47:51 +02:00
|
|
|
# position is what another block uses to jump to. this is determined by the assembler
|
|
|
|
# the assembler allso assembles and assumes a linear instruction sequence
|
2015-05-24 19:00:11 +02:00
|
|
|
# Note: this will have to change for plocks and maybe anyway.
|
2014-08-30 12:47:51 +02:00
|
|
|
def set_position at
|
|
|
|
@position = at
|
2014-08-30 15:57:56 +02:00
|
|
|
@codes.each do |code|
|
2015-05-24 19:00:11 +02:00
|
|
|
begin
|
|
|
|
code.set_position( at)
|
|
|
|
rescue => e
|
2015-05-25 17:48:35 +02:00
|
|
|
puts "BLOCK #{self.to_s[0..5000]}"
|
2015-05-24 19:00:11 +02:00
|
|
|
raise e
|
|
|
|
end
|
2015-06-05 08:20:43 +02:00
|
|
|
raise code.inspect unless code.byte_length
|
|
|
|
at += code.byte_length
|
2014-08-30 15:57:56 +02:00
|
|
|
end
|
2014-08-30 12:47:51 +02:00
|
|
|
end
|
|
|
|
|
2015-06-05 08:20:43 +02:00
|
|
|
def byte_length
|
|
|
|
@codes.inject(0){|count , instruction| count += instruction.byte_length }
|
2014-08-30 12:47:51 +02:00
|
|
|
end
|
|
|
|
|
2015-05-24 19:00:11 +02:00
|
|
|
def to_s
|
|
|
|
Sof::Writer.write(self)
|
|
|
|
end
|
|
|
|
|
2014-08-13 10:59:51 +02:00
|
|
|
private
|
2015-05-13 11:22:51 +02:00
|
|
|
# helper for determining reachable blocks
|
2014-08-13 10:59:51 +02:00
|
|
|
def add_next ret
|
|
|
|
return if @next.nil?
|
|
|
|
return if ret.include? @next
|
|
|
|
ret << @next
|
|
|
|
@next.reachable ret
|
|
|
|
end
|
2015-05-13 11:22:51 +02:00
|
|
|
# helper for determining reachable blocks
|
2014-08-13 10:59:51 +02:00
|
|
|
def add_branch ret
|
|
|
|
return if @branch.nil?
|
|
|
|
return if ret.include? @branch
|
|
|
|
ret << @branch
|
|
|
|
@branch.reachable ret
|
|
|
|
end
|
|
|
|
end
|
2015-05-13 11:22:51 +02:00
|
|
|
end
|