module Virtual # Think flowcharts: blocks are the boxes. The smallest unit of linear code # Blocks must end in control instructions (jump/call/return). # And the only valid argument for a jump is a Block # Blocks form a graph, which is managed by the method class Block def initialize(name , method ) super() @method = method raise "Method is not Method, but #{method.class}" unless method == :__init__ or method.is_a?(Parfait::Method) @name = name.to_sym @branch = nil @codes = [] end attr_reader :name , :codes , :method , :position attr_accessor :branch def reachable ret = [] add_next ret add_branch ret ret end def add_code kode @codes << kode self end # replace a code with an array of new codes. This is what happens in passes all the time def replace code , new_codes index = @codes.index code raise "Code not found #{code} in #{self}" unless index @codes.delete_at(index) if( new_codes.is_a? Array) new_codes.reverse.each {|c| @codes.insert(index , c)} else @codes.insert(index , new_codes) end end # returns if this is a block that ends in a call (and thus needs local variable handling) def call_block? raise "called" 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 # 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 # Note: this will have to change for plocks and maybe anyway. def set_position at @position = at @codes.each do |code| begin code.set_position( at) rescue => e puts "BLOCK #{self.to_s[0..5000]}" raise e end raise code.inspect unless code.byte_length at += code.byte_length end end def byte_length @codes.inject(0){|count , instruction| count += instruction.byte_length } end private # helper for determining reachable blocks def add_next ret return if @next.nil? return if ret.include? @next ret << @next @next.reachable ret end # helper for determining reachable blocks def add_branch ret return if @branch.nil? return if ret.include? @branch ret << @branch @branch.reachable ret end end end