void LLVMState::compile_callframe(STATE, GCToken gct, CompiledCode* start, CallFrame* call_frame, int primitive) { // TODO: Fix compile policy checks if(!start->keywords()->nil_p()) { metrics().m.jit_metrics.methods_failed++; return; } if(debug_search) { std::cout << std::endl << "JIT: triggered: " << enclosure_name(start) << "#" << symbol_debug_str(start->name()) << std::endl; } CallFrame* candidate = find_candidate(state, start, call_frame); if(!candidate || candidate->jitted_p() || candidate->inline_method_p()) { if(config().jit_show_compiling) { llvm::outs() << "[[[ JIT error finding call frame " << enclosure_name(start) << "#" << symbol_debug_str(start->name()); if(!candidate) { llvm::outs() << " no candidate"; } else { if(candidate->jitted_p()) { llvm::outs() << " candidate is jitted"; } if(candidate->inline_method_p()) { llvm::outs() << " candidate is inlined"; } } llvm::outs() << " ]]]\n"; } return; } if(debug_search) { std::cout << "! "; candidate->print_backtrace(state, 1); } if(candidate->compiled_code->machine_code()->call_count <= 1) { if(!start || start->machine_code()->jitted_p()) return; // Ignore it. compile this one. candidate = call_frame; } if(candidate->block_p()) { compile_soon(state, gct, candidate->compiled_code, call_frame, candidate->self()->direct_class(state), candidate->block_env(), true); } else { if(candidate->compiled_code->can_specialize_p()) { compile_soon(state, gct, candidate->compiled_code, call_frame, candidate->self()->direct_class(state)); } else { compile_soon(state, gct, candidate->compiled_code, call_frame, NULL); } } }
void LLVMState::compile_soon(STATE, GCToken gct, CompiledCode* code, CallFrame* call_frame, Class* receiver_class, BlockEnvironment* block_env, bool is_block) { bool wait = config().jit_sync; if(!enabled_) return; // TODO: Fix compile policy checks if(!code->keywords()->nil_p()) { metrics().m.jit_metrics.methods_failed++; return; } if(code->machine_code()->call_count <= 1) { return; } if(code->machine_code()->compiling_p()) { return; } int hits = code->machine_code()->method_call_count(); code->machine_code()->set_compiling(); JITCompileRequest* req = JITCompileRequest::create(state, code, receiver_class, hits, block_env, is_block); if(wait) { wait_mutex.lock(); req->set_waiter(&wait_cond); add(state, req); bool req_block = req->is_block(); state->set_call_frame(call_frame); wait_cond.wait(wait_mutex); wait_mutex.unlock(); state->set_call_frame(0); if(state->shared().config.jit_show_compiling) { llvm::outs() << "[[[ JIT compiled " << enclosure_name(code) << "#" << symbol_debug_str(code->name()) << (req_block ? " (block) " : " (method) ") << " (" << hits << ") " << " ]]]\n"; } } else { add(state, req); if(state->shared().config.jit_show_compiling) { llvm::outs() << "[[[ JIT queued " << enclosure_name(code) << "#" << symbol_debug_str(code->name()) << (req->is_block() ? " (block) " : " (method) ") << " (" << hits << ") " << " ]]]\n"; } } }
void LLVMState::compile_soon(STATE, GCToken gct, CompiledCode* code, CallFrame* call_frame, Object* placement, bool is_block) { bool wait = config().jit_sync; if(code->machine_code()->call_count <= 1) { return; } if(code->machine_code()->compiling_p()) { return; } int hits = code->machine_code()->call_count; code->machine_code()->set_compiling(); BackgroundCompileRequest* req = new BackgroundCompileRequest(state, code, placement, hits, is_block); queued_methods_++; if(wait) { wait_mutex.lock(); req->set_waiter(&wait_cond); background_thread_->add(req); state->set_call_frame(call_frame); gc_independent(); wait_cond.wait(wait_mutex); wait_mutex.unlock(); gc_dependent(); state->set_call_frame(0); if(state->shared().config.jit_show_compiling) { llvm::outs() << "[[[ JIT compiled " << enclosure_name(code) << "#" << symbol_debug_str(code->name()) << (req->is_block() ? " (block) " : " (method) ") << queued_methods() << "/" << jitted_methods() << " ]]]\n"; } } else { background_thread_->add(req); if(state->shared().config.jit_show_compiling) { llvm::outs() << "[[[ JIT queued " << enclosure_name(code) << "#" << symbol_debug_str(code->name()) << (req->is_block() ? " (block) " : " (method) ") << queued_methods() << "/" << jitted_methods() << " ]]]\n"; } } }
std::string LLVMState::enclosure_name(CompiledCode* code) { ConstantScope* cs = code->scope(); if(!kind_of<ConstantScope>(cs) || !kind_of<Module>(cs->module())) { return "ANONYMOUS"; } return symbol_debug_str(cs->module()->module_name()); }
void LLVMState::compile(STATE, GCToken gct, CompiledCode* code, CallFrame* call_frame, Class* receiver_class, BlockEnvironment* block_env, bool is_block) { if(!enabled_) return; // TODO: Fix compile policy checks if(!code->keywords()->nil_p()) { metrics().m.jit_metrics.methods_failed++; return; } // In case the method hasn't been internalized yet if(!code->machine_code()) { code->internalize(state, gct, call_frame); } JITCompileRequest* req = JITCompileRequest::create(state, code, receiver_class, 0, block_env, is_block); wait_mutex.lock(); req->set_waiter(&wait_cond); add(state, req); state->set_call_frame(call_frame); { GCIndependent guard(state, 0); wait_cond.wait(wait_mutex); } wait_mutex.unlock(); state->set_call_frame(0); if(state->shared().config.jit_show_compiling) { llvm::outs() << "[[[ JIT compiled " << enclosure_name(code) << "#" << symbol_debug_str(code->name()) << (req->is_block() ? " (block) " : " (method) ") << " ]]]\n"; } }
void LLVMState::run(STATE) { GCTokenImpl gct; JITCompileRequest* compile_request = nil<JITCompileRequest>(); OnStack<1> os(state, compile_request); metrics().init(metrics::eJITMetrics); state->gc_dependent(gct, 0); bool show_machine_code_ = jit_dump_code() & cMachineCode; while(!thread_exit_) { current_compiler_ = 0; { GCIndependent guard(state, 0); { utilities::thread::Mutex::LockGuard lg(compile_lock_); while(compile_list_.get()->empty_p()) { compile_cond_.wait(compile_lock_); if(thread_exit_) break; } } } if(thread_exit_) break; { utilities::thread::Mutex::LockGuard guard(request_lock_); compile_request = try_as<JITCompileRequest>(compile_list_.get()->shift(state)); if(!compile_request || compile_request->nil_p()) continue; } utilities::thread::Condition* cond = compile_request->waiter(); // Don't proceed until requester has reached the wait_cond if(cond) wait_mutex.lock(); Context ctx(this); jit::Compiler jit(&ctx); current_compiler_ = &jit; uint32_t class_id = 0; uint32_t serial_id = 0; void* func = 0; try { if(compile_request->receiver_class() && !compile_request->receiver_class()->nil_p()) { // Apparently already compiled, probably some race if(compile_request->method()->find_specialized( compile_request->receiver_class())) { if(config().jit_show_compiling) { CompiledCode* code = compile_request->method(); llvm::outs() << "[[[ JIT already compiled " << enclosure_name(code) << "#" << symbol_debug_str(code->name()) << (compile_request->is_block() ? " (block)" : " (method)") << " ]]]\n"; } // If someone was waiting on this, wake them up. if(cond) { wait_mutex.unlock(); cond->signal(); } current_compiler_ = 0; continue; } class_id = compile_request->receiver_class()->class_id(); serial_id = compile_request->receiver_class()->serial_id(); } { timer::StopWatch<timer::microseconds> timer( metrics().m.jit_metrics.time_last_us, metrics().m.jit_metrics.time_total_us); jit.compile(compile_request); bool indy = !config().jit_sync; func = jit.generate_function(indy); } // We were unable to compile this function, likely // because it's got something we don't support. if(!func) { if(config().jit_show_compiling) { CompiledCode* code = compile_request->method(); llvm::outs() << "[[[ JIT error background compiling " << enclosure_name(code) << "#" << symbol_debug_str(code->name()) << (compile_request->is_block() ? " (block)" : " (method)") << " ]]]\n"; } // If someone was waiting on this, wake them up. if(cond) { wait_mutex.unlock(); cond->signal(); } current_compiler_ = 0; continue; } } catch(LLVMState::CompileError& e) { utilities::logger::warn("JIT: compile error: %s", e.error()); metrics().m.jit_metrics.methods_failed++; // If someone was waiting on this, wake them up. if(cond) { wait_mutex.unlock(); cond->signal(); } current_compiler_ = 0; continue; } if(show_machine_code_) { jit.show_machine_code(); } // If the method has had jit'ing request disabled since we started // JIT'ing it, discard our work. if(!compile_request->machine_code()->jit_disabled()) { jit::RuntimeDataHolder* rd = ctx.runtime_data_holder(); atomic::memory_barrier(); start_method_update(); if(!compile_request->is_block()) { if(class_id) { compile_request->method()->add_specialized(state, class_id, serial_id, reinterpret_cast<executor>(func), rd); } else { compile_request->method()->set_unspecialized(reinterpret_cast<executor>(func), rd); } } else { compile_request->method()->set_unspecialized(reinterpret_cast<executor>(func), rd); } compile_request->machine_code()->clear_compiling(); end_method_update(); rd->run_write_barrier(shared().om, compile_request->method()); if(config().jit_show_compiling) { CompiledCode* code = compile_request->method(); llvm::outs() << "[[[ JIT finished background compiling " << enclosure_name(code) << "#" << symbol_debug_str(code->name()) << (compile_request->is_block() ? " (block)" : " (method)") << " ]]]\n"; } } // If someone was waiting on this, wake them up. if(cond) { wait_mutex.unlock(); cond->signal(); } current_compiler_ = 0; metrics().m.jit_metrics.methods_compiled++; } }