Skip to content
Browse files

added support for AOT compiled source code objects in #require

git-svn-id: http://svn.macosforge.org/repository/ruby/MacRuby/trunk@3125 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information...
1 parent 547292f commit b451db637041a5ef5fd52b9045e4debcc2184f07 Laurent Sansonetti committed
Showing with 131 additions and 21 deletions.
  1. +82 −19 bin/rubyc
  2. +6 −0 load.c
  3. +36 −0 vm.cpp
  4. +7 −2 vm.h
View
101 bin/rubyc
@@ -15,7 +15,6 @@ class Compiler
def initialize(argv)
@mode = :normal
@archs = []
- @frameworks = []
@internal = argv.delete('--internal')
# Parse arguments.
@@ -24,8 +23,8 @@ class Compiler
opts.on('-c', 'Compile and assemble, but do not link') { @dont_link = true }
opts.on('-o <file>', 'Place the output into <file>') { |output| @output = output }
#opts.on('--mode [MODE]', "Select compilation mode (normal or full)") { |mode| @mode = mode.intern }
- #opts.on('--framework <framework>', 'Link against <framework>') { |path| @frameworks << path }
- opts.on('--static', "Create a standalone static executable") { @static = true }
+ opts.on('--static', "Create a standalone static executable") { @static = true }
+ opts.on('--dylib', "Create a dynamic library") { @dylib = true }
opts.on('-C', 'Compile, assemble and link a loadable object file') { @bundle = true }
opts.on('-a', '--arch <ARCH>', 'Compile for specified CPU architecture') { |arch| @archs << arch }
opts.on('-v', '--version', 'Display the version') { puts RUBY_DESCRIPTION; exit 1 }
@@ -74,6 +73,7 @@ class Compiler
end
if @dont_link or @bundle
die "Cannot specify --static when not building an executable" if @static
+ die "Cannot specify -c or -C when building a dynamic library" if @dylib
die "Cannot specify -c and -C at the same time" if @bundle and @dont_link
if @files.size > 1 and @output
die "Cannot specify -o with -c or -C and multiple input files"
@@ -88,18 +88,26 @@ class Compiler
compile_object(file, @output)
end
else
+ die "Cannot specify --static and --dylib at the same time" if @dylib and @static
objs = @files.map do |file|
+ die "Given input file `#{file} must exist" unless File.exist?(file)
case File.extname(file)
when '.rb'
compile_object(file, nil)
when '.o'
- die "Given input file `#{file} must exist" unless File.exist?(file)
- file
+ [file, find_init_func(file)]
+ when '.dylib'
+ [file, nil]
else
- die "Given input file `#{file}' must be either a Ruby source file (.rb) or a Mach-O object file (.o)"
+ die "Given input file `#{file}' must be either a Ruby source file (.rb) or a Mach-O object file (.o) or dynamic library (.dylib)"
end
end
- compile_executable(objs, @output)
+ if @dylib
+ die "-o must be specified when building a dynamic library" unless @output
+ compile_dylib(objs, @output)
+ else
+ compile_executable(objs, @output)
+ end
end
end
@@ -149,6 +157,7 @@ class Compiler
output ||= File.join(File.dirname(file), base + '.rbo')
+ # Generate main file.
main_txt = <<EOS
extern "C" {
void *#{init_func}(void *, void *);
@@ -159,18 +168,43 @@ extern "C" {
}
EOS
+ # Build.
+ main = gen_tmpfile('main', 'c')
+ File.open(main, 'w') { |io| io.write(main_txt) }
+ linkf = @internal ? "-L. -lmacruby" : "-framework MacRuby"
+ execute("#{@gcxx} \"#{main}\" -dynamic -bundle -undefined suppress -flat_namespace #{arch_flags} #{linkf} \"#{obj}\" -o \"#{output}\"")
+ end
+
+ def compile_dylib(objs_data, output)
+ # Generate main file.
+ main_txt = <<EOS
+extern "C" {
+ void rb_vm_aot_feature_provide(const char *, void *);
+EOS
+ objs_data.each do |obj, init_func|
+ next if init_func == nil
+ main_txt << "void *#{init_func}(void *, void *);\n"
+ end
+ main_txt << <<EOS
+__attribute__((constructor)) static void __init__(void) {
+EOS
+ objs_data.each do |obj, init_func|
+ main_txt << "rb_vm_aot_feature_provide(\"#{feature_name(obj)}\", (void *)#{init_func});\n"
+ end
+ main_txt << "}}"
+
+ # Build.
main = gen_tmpfile('main', 'c')
File.open(main, 'w') { |io| io.write(main_txt) }
linkf = @internal ? "-L. -lmacruby" : "-framework MacRuby"
- archf = @archs.map { |x| "-arch #{x}" }.join(' ')
- execute("#{@gcxx} \"#{main}\" -dynamic -bundle -undefined suppress -flat_namespace #{archf} #{linkf} \"#{obj}\" -o \"#{output}\"")
+ objs = objs_data.map { |obj, f| "\"#{obj}\"" }.join(' ')
+ execute("#{@gcxx} \"#{main}\" -dynamiclib -dynamic -undefined suppress -flat_namespace #{arch_flags} #{linkf} #{objs} -o \"#{output}\"")
end
def compile_executable(objs_data, output)
output ||= 'a.out'
- objs = []
- init_funcs = []
- objs_data.each { |obj, init_func| objs << obj; init_funcs << init_func }
+ raise if objs_data.empty?
+ die "first object file must be a Ruby source file or object" if objs_data[0][1] == nil
# Generate main file.
main_txt = <<EOS
@@ -181,11 +215,15 @@ extern "C" {
void ruby_script(const char *);
void ruby_set_argv(int, char **);
void rb_vm_init_compiler(void);
+ void rb_vm_aot_feature_provide(const char *, void *);
void *rb_vm_top_self(void);
void rb_vm_print_current_exception(void);
void rb_exit(int);
EOS
- init_funcs.each { |x| main_txt << "void *#{x}(void *, void *);\n" }
+ objs_data.each do |obj, init_func|
+ next if init_func == nil
+ main_txt << "void *#{init_func}(void *, void *);\n"
+ end
main_txt << <<EOS
}
@@ -203,10 +241,14 @@ int main(int argc, char **argv)
rb_vm_init_compiler();
ruby_script(progname);
try {
- void *self = rb_vm_top_self();
EOS
- init_funcs.each { |x| main_txt << "#{x}(self, 0);\n" }
+ objs_data[1..-1].each do |obj, init_func|
+ next if init_func == nil
+ main_txt << "rb_vm_aot_feature_provide(\"#{feature_name(obj)}\", (void *)#{init_func});\n"
+ end
main_txt << <<EOS
+ void *self = rb_vm_top_self();
+ #{objs_data[0][1]}(self, 0);
}
catch (...) {
rb_vm_print_current_exception();
@@ -216,20 +258,22 @@ EOS
}
EOS
+ # Prepare objects.
+ objs = []
+ objs_data.each { |o, _| objs << o }
+
# Compile main file.
main = gen_tmpfile('main', 'mm')
File.open(main, 'w') { |io| io.write(main_txt) }
main_o = gen_tmpfile('main', 'o')
- archf = @archs.map { |x| "-arch #{x}" }.join(' ')
- execute("#{@gcxx} \"#{main}\" -c #{archf} -o \"#{main_o}\" -fobjc-gc")
+ execute("#{@gcxx} \"#{main}\" -c #{arch_flags} -o \"#{main_o}\" -fobjc-gc")
objs.unshift(main_o)
# Link all objects into executable.
linkf = @static ?
"-L#{RbConfig::CONFIG['libdir']} #{RbConfig::CONFIG['LIBRUBYARG_STATIC_REALLY']}" :
"-framework MacRuby -lobjc"
- line = "#{@gcxx} -o \"#{output}\" #{archf} #{linkf} "
- @frameworks.each { |f| line << "-framework #{f} " }
+ line = "#{@gcxx} -o \"#{output}\" #{arch_flags} #{linkf} "
objs.each { |o| line << " \"#{o}\"" }
execute(line)
end
@@ -266,6 +310,25 @@ EOS
end
end
+ def arch_flags
+ @archs.map { |x| "-arch #{x}" }.join(' ')
+ end
+
+ def find_init_func(obj)
+ output = `#{@nm} -j "#{obj}"`
+ output.scan(/^_MREP_.*$/).reject { |func|
+ # Ignore non-main functions.
+ func.include?('ruby_scope')
+ }.map { |func|
+ # Ignore the very first character (_).
+ func[1..-1]
+ }[0]
+ end
+
+ def feature_name(obj)
+ obj.sub(/#{File.extname(obj)}$/, '')
+ end
+
def gen_tmpfile(base, ext)
file = File.join(@tmpdir, "#{base}.#{ext}")
@tmpfiles << file
View
6 load.c
@@ -265,6 +265,12 @@ rb_require_safe(VALUE fname, int safe)
VALUE path;
int type = 0;
+ // Immediately, check out if we have an AOT feature for this.
+ if (rb_vm_aot_feature_load(RSTRING_PTR(fname))) {
+ rb_provide_feature(fname);
+ return Qtrue;
+ }
+
FilePathValue(fname);
if (search_required(fname, &path, &type)) {
if (path == 0) {
View
36 vm.cpp
@@ -4807,6 +4807,42 @@ rb_vm_unregister_current_alien_thread(void)
}
}
+// AOT features. These are registered at runtime once an AOT object file
+// is loaded, either directly from an executable's main() function or from
+// a gcc constructor (in case of a dylib).
+//
+// XXX this shared map is not part of RoxorCore because gcc constructors can
+// potentially be called *before* RoxorCore has been initialized. This is
+// definitely not thread-safe, but it shouldn't be a big deal at this point.
+static std::map<std::string, void *> aot_features;
+
+extern "C"
+bool
+rb_vm_aot_feature_load(const char *name)
+{
+ std::string key(name);
+ std::map<std::string, void *>::iterator iter = aot_features.find(name);
+ if (iter == aot_features.end()) {
+ return false;
+ }
+ void *init_func = iter->second;
+ ((void *(*)(void *, void *))init_func)((void *)rb_vm_top_self(), NULL);
+ aot_features.erase(iter);
+ return true;
+}
+
+extern "C"
+void
+rb_vm_aot_feature_provide(const char *name, void *init_func)
+{
+ std::string key(name);
+ std::map<std::string, void *>::iterator iter = aot_features.find(key);
+ if (iter != aot_features.end()) {
+ printf("WARNING: AOT feature '%s' already registered, new one will be ignored. This could happen if you link your executable against dylibs that contain the same Ruby file.\n", name);
+ }
+ aot_features[key] = init_func;
+}
+
extern "C"
void
Init_PostVM(void)
View
9 vm.h
@@ -413,6 +413,8 @@ Class rb_vm_get_current_class(void);
bool rb_vm_is_multithreaded(void);
void rb_vm_set_multithreaded(bool flag);
+bool rb_vm_aot_feature_load(const char *name);
+
static inline VALUE
rb_robject_allocate_instance(VALUE klass)
{
@@ -584,11 +586,13 @@ class RoxorCore {
// be empty when we exit and we need to call the remaining finalizers.
std::vector<rb_vm_finalizer_t *> finalizers;
+ // The global lock.
+ pthread_mutex_t gl;
+
// State.
bool running;
bool multithreaded;
bool abort_on_exception;
- pthread_mutex_t gl;
VALUE loaded_features;
VALUE load_path;
VALUE rand_seed;
@@ -803,7 +807,8 @@ class RoxorCore {
void invalidate_respond_to_cache(void) {
respond_to_cache.clear();
}
- bool respond_to(VALUE obj, VALUE klass, SEL sel, bool priv, bool check_override);
+ bool respond_to(VALUE obj, VALUE klass, SEL sel, bool priv,
+ bool check_override);
private:
bool register_bs_boxed(bs_element_type_t type, void *value);

0 comments on commit b451db6

Please sign in to comment.
Something went wrong with that request. Please try again.