From ceab460fcae4136404d3d85e4ac62620394a25a8 Mon Sep 17 00:00:00 2001 From: k0kubun Date: Fri, 27 Jul 2018 09:27:58 +0000 Subject: mjit.c: keep all .o files and lazily delete them on termination. This will be needed to create a large so file later. The large number of .o files will be probably compacted before the large so file is created. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64077 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- mjit.c | 39 +++++++++++++++++++++++++++++---------- test/ruby/test_jit.rb | 41 +++++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/mjit.c b/mjit.c index 709b5a30e6..8cba274899 100644 --- a/mjit.c +++ b/mjit.c @@ -162,6 +162,10 @@ struct rb_mjit_unit { /* Dlopen handle of the loaded object file. */ void *handle; const rb_iseq_t *iseq; +#ifndef _MSC_VER + /* This is lazily deleted so that it can be used again to create a large so file. */ + char *o_file; +#endif #ifdef _WIN32 /* DLL cannot be removed while loaded on Windows */ char *so_file; @@ -522,12 +526,24 @@ mjit_free_iseq(const rb_iseq_t *iseq) CRITICAL_SECTION_FINISH(4, "mjit_free_iseq"); } +/* Lazily delete .o and/or .so files. */ static void -clean_so_file(struct rb_mjit_unit *unit) +clean_object_files(struct rb_mjit_unit *unit) { +#ifndef _MSC_VER + if (unit->o_file) { + char *o_file = unit->o_file; + + unit->o_file = NULL; + remove_file(o_file); + free(o_file); + } +#endif + #ifdef _WIN32 - char *so_file = unit->so_file; - if (so_file) { + if (unit->so_file) { + char *so_file = unit->so_file; + unit->so_file = NULL; remove_file(so_file); free(so_file); @@ -538,8 +554,9 @@ clean_so_file(struct rb_mjit_unit *unit) /* This is called in the following situations: 1) On dequeue or `unload_units()`, associated ISeq is already GCed. 2) The unit is not called often and unloaded by `unload_units()`. + 3) Freeing lists on `mjit_finish()`. - `jit_func` state for 1 can be ignored because ISeq GC means it'll never be used. + `jit_func` value does not matter for 1 and 3 since the unit won't be used anymore. For the situation 2, this sets the ISeq's JIT state to NOT_COMPILED_JIT_ISEQ_FUNC to prevent the situation that the same methods are continously compiled. */ static void @@ -549,7 +566,7 @@ free_unit(struct rb_mjit_unit *unit) unit->iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; if (unit->handle) /* handle is NULL if it's in queue */ dlclose(unit->handle); - clean_so_file(unit); + clean_object_files(unit); xfree(unit); } @@ -1024,11 +1041,13 @@ convert_unit_to_func(struct rb_mjit_unit *unit) #ifdef _MSC_VER success = compile_c_to_so(c_file, so_file); #else - /* splitting .c -> .o and .o -> .so to cache .o files in the future */ - success = compile_c_to_o(c_file, o_file) && link_o_to_so(o_file, so_file); + /* splitting .c -> .o step and .o -> .so step, to cache .o files in the future */ + if (success = compile_c_to_o(c_file, o_file)) { + success = link_o_to_so(o_file, so_file); - if (!mjit_opts.save_temps) - remove_file(o_file); + if (!mjit_opts.save_temps) + unit->o_file = strdup(o_file); /* lazily delete on `clean_object_files()` */ + } #endif end_time = real_ms_time(); @@ -1042,7 +1061,7 @@ convert_unit_to_func(struct rb_mjit_unit *unit) func = load_func_from_so(so_file, funcname, unit); if (!mjit_opts.save_temps) { #ifdef _WIN32 - unit->so_file = strdup(so_file); + unit->so_file = strdup(so_file); /* lazily delete on `clean_object_files()` */ #else remove_file(so_file); #endif diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb index 92a7bd56d8..039a2ff35f 100644 --- a/test/ruby/test_jit.rb +++ b/test/ruby/test_jit.rb @@ -530,26 +530,31 @@ class TestJIT < Test::Unit::TestCase end def test_unload_units - # MIN_CACHE_SIZE is 10 - out, err = eval_with_jit("#{<<~"begin;"}\n#{<<~'end;'}", verbose: 1, min_calls: 1, max_cache: 10) - begin; - 10.times do |i| - eval(<<-EOS) - def mjit#{i} - print #{i} - end - mjit#{i} - EOS + Dir.mktmpdir("jit_test_clean_so_") do |dir| + # MIN_CACHE_SIZE is 10 + out, err = eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~'end;'}", verbose: 1, min_calls: 1, max_cache: 10) + begin; + 10.times do |i| + eval(<<-EOS) + def mjit#{i} + print #{i} + end + mjit#{i} + EOS + end + end; + assert_equal('0123456789', out) + errs = err.lines + assert_match(/\A#{JIT_SUCCESS_PREFIX}: block in
@-e:/, errs[0]) + 9.times do |i| + assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit#{i}@\(eval\):/, errs[i + 1]) end - end; - assert_equal('0123456789', out) - errs = err.lines - assert_match(/\A#{JIT_SUCCESS_PREFIX}: block in
@-e:/, errs[0]) - 9.times do |i| - assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit#{i}@\(eval\):/, errs[i + 1]) + assert_equal("Too many JIT code -- 1 units unloaded\n", errs[10]) + assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit9@\(eval\):/, errs[11]) + + # verify .o files are deleted on unload_units + assert_send([Dir, :empty?, dir]) end - assert_equal("Too many JIT code -- 1 units unloaded\n", errs[10]) - assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit9@\(eval\):/, errs[11]) end def test_local_stack_on_exception -- cgit v1.2.3