Merge branch 'master' into new_mom
This commit is contained in:
@ -50,16 +50,15 @@
|
||||
# and rebuilt the reserve (get_next already instantiates the reserve)
|
||||
#
|
||||
def get_more
|
||||
first_object = get_chain
|
||||
link = first_object
|
||||
self.reserve = get_chain
|
||||
last_link = self.reserve
|
||||
count = Factory.reserve_size
|
||||
while(count > 0)
|
||||
link = get_next_for(link)
|
||||
last_link = get_next_for(last_link)
|
||||
count -= 1
|
||||
end
|
||||
self.next_object = get_next_for(link)
|
||||
set_next_for( link , nil )
|
||||
self.reserve = first_object
|
||||
self.next_object = get_next_for(last_link)
|
||||
set_next_for( last_link , nil )
|
||||
self
|
||||
end
|
||||
|
||||
|
@ -1,43 +1,76 @@
|
||||
module Risc
|
||||
|
||||
# collect anything that is in the space but and reachable from init
|
||||
# collect anything that is in the space and reachable (linker constants)
|
||||
#
|
||||
# The place we collect in is the position map in Position class
|
||||
module Collector
|
||||
# Collect all object that need to be added to the binary
|
||||
# This means the object_space and aby constants the linker has
|
||||
# we call keep on each object, see there for details
|
||||
# return all positions
|
||||
def self.collect_space(linker)
|
||||
keep Parfait.object_space , 0
|
||||
keep Parfait.object_space
|
||||
linker.constants.each do |obj|
|
||||
keep(obj,0)
|
||||
keep(obj)
|
||||
end
|
||||
Position.positions
|
||||
end
|
||||
|
||||
def self.keep( object , depth )
|
||||
# keep "collects" the object for "keeping". Such objects get written to binary
|
||||
# keeping used to be done by adding to a hash, but now the object is
|
||||
# given a position, and the Position class has a hash of all positions
|
||||
# (the same hash has all objects, off course)
|
||||
def self.keep( object)
|
||||
collection = []
|
||||
mark_1k( object , 0 , collection)
|
||||
collection.each do |obj|
|
||||
#puts "obj #{obj.object_id}"
|
||||
keep(obj)
|
||||
end
|
||||
end
|
||||
|
||||
# marking object that make up the binary.
|
||||
# "Only" up to 1k stack depth, collect object that make up the "border"
|
||||
#
|
||||
# Collection is an empty arry that is passed on. Objects below 1k get added
|
||||
# So basically it "should" be a return, but then we would keep creating and adding
|
||||
# arrays, most of which would be empty
|
||||
def self.mark_1k(object , depth , collection)
|
||||
return if object.nil?
|
||||
return unless add_object( object , depth )
|
||||
if depth > 1000
|
||||
collection << object
|
||||
return
|
||||
end
|
||||
return unless position!( object )
|
||||
return unless object.respond_to? :has_type?
|
||||
type = object.get_type
|
||||
keep(type , depth + 1)
|
||||
mark_1k(type , depth + 1 , collection)
|
||||
return if object.is_a? Symbol
|
||||
type.names.each do |name|
|
||||
keep(name , depth + 1)
|
||||
mark_1k(name , depth + 1, collection)
|
||||
inst = object.get_instance_variable name
|
||||
keep(inst , depth + 1)
|
||||
#puts "getting name #{name}, val=#{inst} #{inst.object_id}"
|
||||
mark_1k(inst , depth + 1, collection)
|
||||
end
|
||||
if object.is_a? Parfait::List
|
||||
object.each do |item|
|
||||
keep(item , depth + 1)
|
||||
mark_1k(item , depth + 1, collection)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Objects are data and get assembled after functions
|
||||
def self.add_object( objekt , depth)
|
||||
# Give the object a position. Position class keeps a list of all positions
|
||||
# and associated objects. The actual position is determined later, here a
|
||||
# Position object is assigned.
|
||||
#
|
||||
# All Objects that end up in the binary must have a Position.
|
||||
#
|
||||
# return if the position was assigned (true) or had been assigned already (false)
|
||||
def self.position!( objekt )
|
||||
return false if Position.set?(objekt)
|
||||
return true if objekt.is_a? ::Integer
|
||||
return true if objekt.is_a?( Risc::Label)
|
||||
#puts message(objekt , depth)
|
||||
#puts "ADD #{objekt.inspect}, #{objekt.name}" if objekt.is_a? Parfait::CallableMethod
|
||||
#puts "ADD #{objekt.class.name}"
|
||||
unless objekt.is_a?( Parfait::Object) or objekt.is_a?( Symbol)
|
||||
raise "adding non parfait #{objekt.class}:#{objekt}"
|
||||
end
|
||||
@ -46,13 +79,5 @@ module Risc
|
||||
true
|
||||
end
|
||||
|
||||
def self.message(object , depth)
|
||||
msg = "adding #{depth}#{' ' * depth}:"
|
||||
if( object.respond_to?(:rxf_reference_name))
|
||||
msg + object.rxf_reference_name.to_s
|
||||
else
|
||||
msg + object.class.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -8,7 +8,8 @@ module Risc
|
||||
# will be executed by method execute_SlotToReg
|
||||
#
|
||||
# The Interpreter (a bit like a cpu) has a state flag, a current instruction and registers
|
||||
# We collect the stdout (as a hack not to interpret the OS)
|
||||
# We collect the stdout (as a hack not to interpret the OS) in a string. It can also be passed
|
||||
# in to the init, as an IO
|
||||
#
|
||||
class Interpreter
|
||||
# fire events for changed pc and register contents
|
||||
@ -18,11 +19,13 @@ module Risc
|
||||
|
||||
attr_reader :instruction , :clock , :pc # current instruction and pc
|
||||
attr_reader :registers # the registers, 16 (a hash, sym -> contents)
|
||||
attr_reader :stdout, :state , :flags # somewhat like the lags on a cpu, hash sym => bool (zero .. . )
|
||||
attr_reader :stdout, :state , :flags # somewhat like the flags on a cpu, hash sym => bool (zero .. . )
|
||||
|
||||
#start in state :stopped and set registers to unknown
|
||||
def initialize( linker )
|
||||
@stdout , @clock , @pc , @state = "", 0 , 0 , :stopped
|
||||
# start in state :stopped and set registers to unknown
|
||||
# Linker gives the state of the program
|
||||
# Passing a stdout in (an IO, only << called) can be used to get output immediately.
|
||||
def initialize( linker , stdout = "")
|
||||
@stdout , @clock , @pc , @state = stdout, 0 , 0 , :stopped
|
||||
@registers = {}
|
||||
@flags = { :zero => false , :plus => false ,
|
||||
:minus => false , :overflow => false }
|
||||
@ -32,8 +35,7 @@ module Risc
|
||||
@linker = linker
|
||||
end
|
||||
|
||||
def start_program(linker = nil)
|
||||
initialize(linker || @linker)
|
||||
def start_program()
|
||||
init = @linker.cpu_init
|
||||
set_state(:running)
|
||||
set_pc( Position.get(init).at )
|
||||
@ -249,10 +251,12 @@ module Risc
|
||||
str = get_register( :r1 ) # should test length, ie r2
|
||||
case str
|
||||
when Symbol
|
||||
@stdout += str.to_s
|
||||
@stdout << str.to_s
|
||||
@stdout.flush if @stdout.respond_to?(:flush)
|
||||
return str.to_s.length
|
||||
when Parfait::Word
|
||||
@stdout += str.to_string
|
||||
@stdout << str.to_string
|
||||
@stdout.flush if @stdout.respond_to?(:flush)
|
||||
return str.char_length
|
||||
else
|
||||
raise "NO string for putstring #{str.class}:#{str.object_id}" unless str.is_a?(Symbol)
|
||||
|
@ -152,8 +152,10 @@ module Parfait
|
||||
Data8: {},
|
||||
Data16: {},
|
||||
Dictionary: {i_keys: :List , i_values: :List } ,
|
||||
Integer: {next_integer: :Integer},
|
||||
FalseClass: {},
|
||||
Factory: { for_type: :Type , next_object: :Object ,
|
||||
reserve: :Object , attribute_name: :Word },
|
||||
Integer: {next_integer: :Integer},
|
||||
List: {indexed_length: :Integer , next_list: :List} ,
|
||||
Message: { next_message: :Message, receiver: :Object, frame: :NamedList ,
|
||||
return_address: :Integer, return_value: :Object,
|
||||
@ -162,8 +164,6 @@ module Parfait
|
||||
NamedList: {},
|
||||
NilClass: {},
|
||||
Object: {},
|
||||
Factory: { for_type: :Type , next_object: :Object ,
|
||||
reserve: :Object , attribute_name: :Word },
|
||||
ReturnAddress: {next_integer: :ReturnAddress},
|
||||
Space: {classes: :Dictionary , types: :Dictionary , factories: :Dictionary,
|
||||
true_object: :TrueClass, false_object: :FalseClass , nil_object: :NilClass},
|
||||
|
@ -1,11 +1,14 @@
|
||||
require "thor"
|
||||
require "rubyx"
|
||||
require "risc/interpreter"
|
||||
|
||||
class RubyXC < Thor
|
||||
class_option :parfait , type: :numeric
|
||||
|
||||
|
||||
desc "compile FILE" , "Compile given FILE to binary"
|
||||
long_desc <<-LONGDESC
|
||||
Very basic cli to compile ruby programs.
|
||||
Currently only compile command supported without option.
|
||||
Compile the give file name to binary object file (see long descr.)
|
||||
|
||||
Output will be elf object file of the same name, with .o, in root directory.
|
||||
|
||||
@ -22,7 +25,7 @@ class RubyXC < Thor
|
||||
end
|
||||
puts "compiling #{file}"
|
||||
|
||||
linker = ::RubyX::RubyXCompiler.new({}).ruby_to_binary( ruby , :arm )
|
||||
linker = ::RubyX::RubyXCompiler.new(extract_options).ruby_to_binary( ruby , :arm )
|
||||
writer = Elf::ObjectWriter.new(linker)
|
||||
|
||||
outfile = file.split("/").last.gsub(".rb" , ".o")
|
||||
@ -30,4 +33,74 @@ class RubyXC < Thor
|
||||
|
||||
return outfile
|
||||
end
|
||||
|
||||
|
||||
desc "interpret FILE" , "Interpret given FILE "
|
||||
long_desc <<-LONGDESC
|
||||
Compiles the given file to an intermediate RISC format, and runs the
|
||||
Interpreter.
|
||||
|
||||
RISC is the last abstract layer inside the compiler. It is in nature
|
||||
very close to arm (without quirks and much smaller).
|
||||
|
||||
An interpreter was originally developed for the RISC layer for debugging purposes.
|
||||
Running the interpreter is about 50k slower than binary, but it can be used
|
||||
to veryfy simple programs.
|
||||
|
||||
No output file will be generated, the only output is generated by the
|
||||
given program.
|
||||
|
||||
The program must define a main method on the Space class, which will be invoked.
|
||||
LONGDESC
|
||||
|
||||
def interpret(file)
|
||||
begin
|
||||
ruby = File.read(file)
|
||||
rescue
|
||||
fail MalformattedArgumentError , "No such file #{file}"
|
||||
end
|
||||
compiler = RubyX::RubyXCompiler.new(extract_options)
|
||||
linker = compiler.ruby_to_binary(ruby, :interpreter)
|
||||
|
||||
puts "interpreting #{file}"
|
||||
|
||||
interpreter = Risc::Interpreter.new(linker , STDOUT )
|
||||
interpreter.start_program
|
||||
interpreter.tick while(interpreter.instruction)
|
||||
|
||||
end
|
||||
|
||||
desc "execute FILE" , "Compile given FILE and execute resulting binary"
|
||||
long_desc <<-LONGDESC
|
||||
Just like the compile task, this compiles the file to an object/binary file.
|
||||
|
||||
Then rubyxc will link and run the resulting object file. For this to work,
|
||||
qemu needs to be set up correctly on the system. Specifically, because of
|
||||
bug #13, arm-linux-gnueabihf-ld needs to exist (it's part of the cross compiled
|
||||
arm binutils).
|
||||
|
||||
The resulting a.out will be run via qemu-arm. This is part of the qemu "linux" package
|
||||
and interprets the arm binary on the host, assuming a linux os.
|
||||
|
||||
This whole approach should only be used for preliminary checking that no core-dumps
|
||||
are generated by the program, or when no benchmarking (as the times will be whatever).
|
||||
|
||||
For simple functional test though, it is a much much quicker way to run the binary
|
||||
than transferring it to another machine. The a.out is left in place to be run again.
|
||||
LONGDESC
|
||||
|
||||
def execute(file)
|
||||
outfile = compile(file)
|
||||
system "arm-linux-gnueabihf-ld -N #{outfile}"
|
||||
puts "Linked ok, now running #{file}"
|
||||
system "qemu-arm ./a.out"
|
||||
end
|
||||
|
||||
private
|
||||
def extract_options
|
||||
opt = { factory: options[:parfait] || 1024 }
|
||||
puts opt
|
||||
return {parfait: opt}
|
||||
end
|
||||
|
||||
end
|
||||
|
Reference in New Issue
Block a user