splitting commpiler commands and adding preload option

This commit is contained in:
Torsten Rüger 2019-09-13 19:07:51 +03:00
parent 12b29285d7
commit 8af17a69ea
7 changed files with 182 additions and 155 deletions

39
lib/rubyx/compile.rb Normal file
View 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
View 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
View 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

View File

@ -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
View 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

View File

@ -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

View File

@ -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