Revision: 3125 http://trac.macosforge.org/projects/ruby/changeset/3125 Author: lsansonetti@apple.com Date: 2009-12-16 14:40:49 -0800 (Wed, 16 Dec 2009) Log Message: ----------- added support for AOT compiled source code objects in #require Modified Paths: -------------- MacRuby/trunk/bin/rubyc MacRuby/trunk/load.c MacRuby/trunk/vm.cpp MacRuby/trunk/vm.h Modified: MacRuby/trunk/bin/rubyc =================================================================== --- MacRuby/trunk/bin/rubyc 2009-12-15 06:27:31 UTC (rev 3124) +++ MacRuby/trunk/bin/rubyc 2009-12-16 22:40:49 UTC (rev 3125) @@ -15,7 +15,6 @@ def initialize(argv) @mode = :normal @archs = [] - @frameworks = [] @internal = argv.delete('--internal') # Parse arguments. @@ -24,8 +23,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ } EOS + # 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}\"") + 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" + 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 @@ 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 @@ 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 + # 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 @@ 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 Modified: MacRuby/trunk/load.c =================================================================== --- MacRuby/trunk/load.c 2009-12-15 06:27:31 UTC (rev 3124) +++ MacRuby/trunk/load.c 2009-12-16 22:40:49 UTC (rev 3125) @@ -265,6 +265,12 @@ 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) { Modified: MacRuby/trunk/vm.cpp =================================================================== --- MacRuby/trunk/vm.cpp 2009-12-15 06:27:31 UTC (rev 3124) +++ MacRuby/trunk/vm.cpp 2009-12-16 22:40:49 UTC (rev 3125) @@ -4807,8 +4807,44 @@ } } +// 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) { // Create and register the main thread. Modified: MacRuby/trunk/vm.h =================================================================== --- MacRuby/trunk/vm.h 2009-12-15 06:27:31 UTC (rev 3124) +++ MacRuby/trunk/vm.h 2009-12-16 22:40:49 UTC (rev 3125) @@ -413,6 +413,8 @@ 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 @@ // 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 @@ 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);
participants (1)
-
source_changes@macosforge.org