splitting commpiler commands and adding preload option
This commit is contained in:
parent
12b29285d7
commit
8af17a69ea
39
lib/rubyx/compile.rb
Normal file
39
lib/rubyx/compile.rb
Normal file
@ -0,0 +1,39 @@
|
||||
class RubyXC < Thor
|
||||
desc "compile FILE" , "Compile given FILE to binary"
|
||||
long_desc <<-LONGDESC
|
||||
Compile the give file name to binary object file (see below.)
|
||||
|
||||
Output will be elf object file of the same name, with .o, in root directory.
|
||||
|
||||
Note: Because of Bug #13, you need to run "ld -N file.o" on the file, before
|
||||
executing it. This can be done on a mac by installing a cross linker
|
||||
(brew install arm-linux-gnueabihf-binutils), or on the target arm machine.
|
||||
LONGDESC
|
||||
|
||||
def compile(file)
|
||||
linker = do_compile(file)
|
||||
elf = { debug: options[:elf] || false }
|
||||
writer = Elf::ObjectWriter.new(linker , elf)
|
||||
|
||||
outfile = file.split("/").last.gsub(".rb" , ".o")
|
||||
writer.save outfile
|
||||
system "arm-linux-gnu-ld -N #{outfile}"
|
||||
File.delete outfile
|
||||
return outfile
|
||||
end
|
||||
|
||||
private
|
||||
# Open file, create compiler, compile and return linker
|
||||
def do_compile(file)
|
||||
begin
|
||||
ruby = File.read(file)
|
||||
rescue
|
||||
fail MalformattedArgumentError , "No such file #{file}"
|
||||
end
|
||||
puts "compiling #{file}"
|
||||
code = get_preload + ruby
|
||||
puts "Code= #{code}"
|
||||
RubyX::RubyXCompiler.new(extract_options).ruby_to_binary( code , :arm )
|
||||
end
|
||||
|
||||
end
|
27
lib/rubyx/execute.rb
Normal file
27
lib/rubyx/execute.rb
Normal file
@ -0,0 +1,27 @@
|
||||
class RubyXC < Thor
|
||||
|
||||
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)
|
||||
puts "Running #{outfile}\n\n"
|
||||
system "qemu-arm ./a.out ;echo $?"
|
||||
end
|
||||
end
|
37
lib/rubyx/interpret.rb
Normal file
37
lib/rubyx/interpret.rb
Normal file
@ -0,0 +1,37 @@
|
||||
class RubyXC < Thor
|
||||
|
||||
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
|
||||
end
|
@ -6,133 +6,23 @@ class RubyXC < Thor
|
||||
class_option :integers , type: :numeric
|
||||
class_option :mesages , type: :numeric
|
||||
class_option :elf , type: :boolean
|
||||
class_option :preload , type: :boolean
|
||||
|
||||
desc "stats FILE" , "Give object statistics on compiled FILE"
|
||||
long_desc <<-LONGDESC
|
||||
Compile the give file name and print statistics on the binary.
|
||||
A Binary file is in essence an ObjectSpace, ie a collection of objects.
|
||||
|
||||
This command tells you how many objects of each kind, the amount of bytes
|
||||
objects of that class take, and the total amount of bytes taken.
|
||||
|
||||
Together with various options this may be used to tune the executable, or just fyi.
|
||||
LONGDESC
|
||||
def stats(file)
|
||||
compile(file)
|
||||
by_class = Hash.new(0)
|
||||
Risc::Position.positions.each do |object , _ |
|
||||
by_class[object.class] += 1 if Risc::Position.is_object(object)
|
||||
end
|
||||
obj, total = 0 , 0
|
||||
by_class.each do |clazz , num|
|
||||
dis = (clazz == Symbol) ? 8 : clazz.memory_size
|
||||
puts clazz.name.split("::").last + " == #{num} / #{num*dis}"
|
||||
obj += num
|
||||
total += num * dis
|
||||
end
|
||||
puts "\nTotal Objects=#{obj} Words=#{total}"
|
||||
end
|
||||
|
||||
desc "compile FILE" , "Compile given FILE to binary"
|
||||
long_desc <<-LONGDESC
|
||||
Compile the give file name to binary object file (see below.)
|
||||
|
||||
Output will be elf object file of the same name, with .o, in root directory.
|
||||
|
||||
Note: Because of Bug #13, you need to run "ld -N file.o" on the file, before
|
||||
executing it. This can be done on a mac by installing a cross linker
|
||||
(brew install arm-linux-gnueabihf-binutils), or on the target arm machine.
|
||||
LONGDESC
|
||||
|
||||
def compile(file)
|
||||
linker = do_compile(file)
|
||||
elf = { debug: options[:elf] || false }
|
||||
writer = Elf::ObjectWriter.new(linker , elf)
|
||||
|
||||
outfile = file.split("/").last.gsub(".rb" , ".o")
|
||||
writer.save outfile
|
||||
system "arm-linux-gnu-ld -N #{outfile}"
|
||||
File.delete outfile
|
||||
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)
|
||||
puts "Running #{outfile}\n\n"
|
||||
system "qemu-arm ./a.out ;echo $?"
|
||||
end
|
||||
|
||||
#
|
||||
# Actual commands are required at the end, one per file, same name as command
|
||||
#
|
||||
private
|
||||
# Open file, create compiler, compile and return linker
|
||||
def do_compile(file)
|
||||
begin
|
||||
ruby = File.read(file)
|
||||
rescue
|
||||
fail MalformattedArgumentError , "No such file #{file}"
|
||||
end
|
||||
puts "compiling #{file}"
|
||||
::RubyX::RubyXCompiler.new(extract_options).ruby_to_binary( ruby , :arm )
|
||||
end
|
||||
|
||||
def extract_options
|
||||
opt = { Integer: options[:integers] || 1024 ,
|
||||
Message: options[:messages] || 1024}
|
||||
return {parfait: opt }
|
||||
end
|
||||
|
||||
def get_preload
|
||||
options[:preload] ? Vool::Builtin.builtin_code : ""
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "stats"
|
||||
require_relative "compile"
|
||||
require_relative "interpret"
|
||||
require_relative "execute"
|
||||
|
28
lib/rubyx/stats.rb
Normal file
28
lib/rubyx/stats.rb
Normal file
@ -0,0 +1,28 @@
|
||||
class RubyXC < Thor
|
||||
|
||||
desc "stats FILE" , "Give object statistics on compiled FILE"
|
||||
long_desc <<-LONGDESC
|
||||
Compile the give file name and print statistics on the binary.
|
||||
A Binary file is in essence an ObjectSpace, ie a collection of objects.
|
||||
|
||||
This command tells you how many objects of each kind, the amount of bytes
|
||||
objects of that class take, and the total amount of bytes taken.
|
||||
|
||||
Together with various options this may be used to tune the executable, or just fyi.
|
||||
LONGDESC
|
||||
def stats(file)
|
||||
compile(file)
|
||||
by_class = Hash.new(0)
|
||||
Risc::Position.positions.each do |object , _ |
|
||||
by_class[object.class] += 1 if Risc::Position.is_object(object)
|
||||
end
|
||||
obj, total = 0 , 0
|
||||
by_class.each do |clazz , num|
|
||||
dis = (clazz == Symbol) ? 8 : clazz.memory_size
|
||||
puts clazz.name.split("::").last + " == #{num} / #{num*dis}"
|
||||
obj += num
|
||||
total += num * dis
|
||||
end
|
||||
puts "\nTotal Objects=#{obj} Words=#{total}"
|
||||
end
|
||||
end
|
@ -1,13 +1,44 @@
|
||||
module Vool
|
||||
module Macro
|
||||
module Builtin
|
||||
def self.boot_methods(options)
|
||||
return if options[:boot_methods] == false
|
||||
load_builtin( :int_plus )
|
||||
load_builtin( "Integer.plus" )
|
||||
end
|
||||
|
||||
def self.load_builtin(name)
|
||||
fname = "./builtin/#{name}.rb"
|
||||
File.read File.expand_path(fname, File.dirname(__FILE__))
|
||||
def self.load_builtin(loads)
|
||||
clazz , meth = loads.split(".")
|
||||
"class #{clazz}; #{Vool::Builtin.builtin[loads]};end;"
|
||||
end
|
||||
|
||||
def self.builtin
|
||||
{
|
||||
"Object.get" => "def get_internal_word(at); X.get_internal_word;end",
|
||||
"Object.missing" => "def method_missing(at); X.method_missing;end",
|
||||
"Object.init" => "def __init__(at); X.init;end",
|
||||
"Object.exit" => "def exit; X.exit;end",
|
||||
"Integer.div4" => "def div4; X.div4;end",
|
||||
"Integer.div10" => "def div10; X.div10;end",
|
||||
"Integer.gt" => "def >; X.comparison(:>);end",
|
||||
"Integer.lt" => "def <; X.comparison(:<);end",
|
||||
"Integer.ge" => "def >=; X.comparison(:>=);end",
|
||||
"Integer.le" => "def <=; X.comparison(:<=);end",
|
||||
"Integer.plus" => "def +; X.int_operator(:+);end",
|
||||
"Integer.minus" => "def -; X.int_operator(:-);end",
|
||||
"Integer.mul" => "def *; X.int_operator(:*);end",
|
||||
"Integer.and" => "def &; X.int_operator(:&);end",
|
||||
"Integer.or" => "def |; X.int_operator(:|);end",
|
||||
"Integer.ls" => "def <<; X.int_operator(:<<);end",
|
||||
"Integer.rs" => "def >>; X.int_operator(:>>);end",
|
||||
"Word.put" => "def putstring(at); X.putstring;end",
|
||||
"Word.set" => "def set_internal_byte(at, val); X.set_internal_byte;end",
|
||||
"Word.get" => "def get_internal_byte(at); X.get_internal_byte;end",
|
||||
"Space.main" => "def main(args);return;end",
|
||||
}
|
||||
end
|
||||
def self.builtin_code
|
||||
keys = builtin.keys
|
||||
keys.pop
|
||||
keys.collect { |loads| load_builtin(loads)}.join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,36 +1,11 @@
|
||||
module Preloader
|
||||
def builtin
|
||||
{
|
||||
"Object.get" => "def get_internal_word(at); X.get_internal_word;end",
|
||||
"Object.missing" => "def method_missing(at); X.method_missing;end",
|
||||
"Object.init" => "def __init__(at); X.init;end",
|
||||
"Object.exit" => "def exit; X.exit;end",
|
||||
"Integer.div4" => "def div4; X.div4;end",
|
||||
"Integer.div10" => "def div10; X.div10;end",
|
||||
"Integer.gt" => "def >; X.comparison(:>);end",
|
||||
"Integer.lt" => "def <; X.comparison(:<);end",
|
||||
"Integer.ge" => "def >=; X.comparison(:>=);end",
|
||||
"Integer.le" => "def <=; X.comparison(:<=);end",
|
||||
"Integer.plus" => "def +; X.int_operator(:+);end",
|
||||
"Integer.minus" => "def -; X.int_operator(:-);end",
|
||||
"Integer.mul" => "def *; X.int_operator(:*);end",
|
||||
"Integer.and" => "def &; X.int_operator(:&);end",
|
||||
"Integer.or" => "def |; X.int_operator(:|);end",
|
||||
"Integer.ls" => "def <<; X.int_operator(:<<);end",
|
||||
"Integer.rs" => "def >>; X.int_operator(:>>);end",
|
||||
"Word.put" => "def putstring(at); X.putstring;end",
|
||||
"Word.set" => "def set_internal_byte(at, val); X.set_internal_byte;end",
|
||||
"Word.get" => "def get_internal_byte(at); X.get_internal_byte;end",
|
||||
"Space.main" => "def main(args);return;end",
|
||||
}
|
||||
end
|
||||
def get_preload(preload)
|
||||
return "" unless preload
|
||||
preload = builtin.keys.join(";") if(preload == "all" )
|
||||
preload = Vool::Builtin.builtin.keys.join(";") if(preload == "all" )
|
||||
preload.split(";").collect do |loads|
|
||||
raise "no preload #{loads}" unless builtin[loads]
|
||||
raise "no preload #{loads}" unless Vool::Builtin.builtin[loads]
|
||||
clazz , meth = loads.split(".")
|
||||
"class #{clazz}; #{builtin[loads]};end;"
|
||||
"class #{clazz}; #{Vool::Builtin.builtin[loads]};end;"
|
||||
end.join
|
||||
end
|
||||
def preload
|
||||
|
Loading…
Reference in New Issue
Block a user