void CompiledCode::Info::mark(Object* obj, memory::ObjectMark& mark) { auto_mark(obj, mark); mark_inliners(obj, mark); CompiledCode* code = as<CompiledCode>(obj); if(!code->machine_code()) return; MachineCode* mcode = code->machine_code(); mcode->set_mark(); for(int i = 0; i < MachineCode::cMaxSpecializations; i++) { // TODO: JIT } for(size_t i = 0; i < mcode->references_count(); i++) { if(size_t ip = mcode->references()[i]) { Object* ref = reinterpret_cast<Object*>(mcode->opcodes[ip]); if(Object* updated_ref = mark.call(ref)) { mcode->opcodes[ip] = reinterpret_cast<intptr_t>(updated_ref); mark.just_set(code, updated_ref); } } } }
void VM::update_profile(STATE) { timer::StopWatch<timer::nanoseconds> timer(metrics().machine.profile_ns); metrics().machine.profiles++; profile_sample_count_++; CompiledCode* code = state->vm()->call_frame()->compiled_code; code->machine_code()->sample_count++; Tuple* profile = profile_.get(); if(profile->nil_p()) { profile = Tuple::create(state, max_profile_entries_); profile_.set(profile); } ::qsort(reinterpret_cast<void*>(profile->field), profile->num_fields(), sizeof(intptr_t), profile_compare); for(native_int i = 0; i < profile->num_fields(); i++) { if(code == profile->at(i)) return; } CompiledCode* pcode = try_as<CompiledCode>(profile->at(0)); if(!pcode || (pcode && code->machine_code()->call_count > pcode->machine_code()->call_count)) { profile->put(state, 0, code); min_profile_call_count_ = code->machine_code()->call_count; } }
Tuple* CompiledCode::constant_caches(STATE) { CompiledCode* self = this; OnStack<1> os(state, self); if(self->machine_code() == NULL) { if(!self->internalize(state)) return force_as<Tuple>(Primitives::failure()); } MachineCode* mcode = self->machine_code(); return mcode->constant_caches(state); }
Object* CompiledCode::primitive_failed(STATE, Executable* exec, Module* mod, Arguments& args) { CompiledCode* code = as<CompiledCode>(exec); Class* cls = args.recv()->direct_class(state); uint64_t class_data = cls->data_raw(); MachineCode* v = code->machine_code(); executor target = v->unspecialized; for(int i = 0; i < MachineCode::cMaxSpecializations; i++) { uint64_t c_id = v->specializations[i].class_data.raw; executor x = v->specializations[i].execute; if(c_id == class_data && x != 0) { target = x; break; } } if(target) { return target(state, exec, mod, args); } else { return MachineCode::execute(state, exec, mod, args); } }
Object* CompiledCode::specialized_executor(STATE, CallFrame* call_frame, Executable* exec, Module* mod, Arguments& args) { CompiledCode* code = as<CompiledCode>(exec); Class* cls = args.recv()->class_object(state); int id = cls->class_id(); MachineCode* v = code->machine_code(); executor target = v->unspecialized; for(int i = 0; i < MachineCode::cMaxSpecializations; i++) { int c_id = v->specializations[i].class_id; executor x = v->specializations[i].execute; if(c_id == id && x != 0) { target = x; break; } } // This is a bug. We should not have this setup if there are no // specializations. FIX THIS BUG! if(!target) target = v->fallback; return target(state, call_frame, exec, mod, args); }
void Compiler::compile_method(BackgroundCompileRequest* req) { CompiledCode* code = req->method(); if(ctx_->llvm_state()->config().jit_inline_debug) { struct timeval tv; gettimeofday(&tv, NULL); ctx_->llvm_state()->log() << "JIT: compiling " << ctx_->llvm_state()->enclosure_name(code) << "#" << ctx_->llvm_state()->symbol_debug_str(code->name()) << " (" << tv.tv_sec << "." << tv.tv_usec << ")\n"; } JITMethodInfo info(ctx_, code, code->machine_code()); info.is_block = false; if(Class* cls = req->receiver_class()) { info.set_self_class(cls); } ctx_->set_root(&info); jit::MethodBuilder work(ctx_, info); work.setup(); compile_builder(info, work); ctx_->set_root(NULL); }
Object* CompiledCode::primitive_failed(STATE, CallFrame* call_frame, Executable* exec, Module* mod, Arguments& args) { CompiledCode* code = as<CompiledCode>(exec); Class* cls = args.recv()->lookup_begin(state); uint32_t id = cls->class_id(); MachineCode* v = code->machine_code(); executor target = v->unspecialized; for(int i = 0; i < MachineCode::cMaxSpecializations; i++) { uint32_t c_id = v->specializations[i].class_id; executor x = v->specializations[i].execute; if(c_id == id && x != 0) { target = x; break; } } if(target) { return target(state, call_frame, exec, mod, args); } else { return MachineCode::execute(state, call_frame, exec, mod, args); } }
r_mint Env::method_id(rmethod meth) { CompiledCode* code = i(meth); if(MachineCode* mcode = code->machine_code()) { return (mcode->method_id() << 1) | 1; } return 0; }
r_mint Env::method_id(rcompiled_code code) { CompiledCode* ccode = i(code); if(MachineCode* mcode = ccode->machine_code()) { return (mcode->method_id() << 1) | 1; } return 0; }
CompiledCode* CompiledCode::dup(STATE) { CompiledCode* code = state->memory()->new_object<CompiledCode>(state, G(compiled_code)); code->copy_object(state, this); code->set_executor(CompiledCode::default_executor); code->machine_code(NULL); return code; }
Object* CompiledCode::set_breakpoint(STATE, Fixnum* ip, Object* bp) { CompiledCode* self = this; OnStack<3> os(state, self, ip, bp); int i = ip->to_native(); if(self->machine_code() == NULL) { if(!self->internalize(state)) return Primitives::failure(); } if(!self->machine_code()->validate_ip(state, i)) return Primitives::failure(); if(self->breakpoints()->nil_p()) { self->breakpoints(state, LookupTable::create(state)); } self->breakpoints()->store(state, ip, bp); self->machine_code()->debugging = 1; self->machine_code()->run = MachineCode::debugger_interpreter; return ip; }
void CompiledCode::Info::show(STATE, Object* self, int level) { CompiledCode* code = as<CompiledCode>(self); class_header(state, self); indent_attribute(++level, "file"); code->file()->show(state, level); indent_attribute(level, "iseq"); code->iseq()->show(state, level); indent_attribute(level, "lines"); code->lines()->show_simple(state, level); indent_attribute(level, "literals"); code->literals()->show_simple(state, level); indent_attribute(level, "local_count"); code->local_count()->show(state, level); indent_attribute(level, "local_names"); code->local_names()->show_simple(state, level); indent_attribute(level, "name"); code->name()->show(state, level); indent_attribute(level, "required_args"); code->required_args()->show(state, level); indent_attribute(level, "scope"); code->scope()->show(state, level); indent_attribute(level, "splat"); code->splat()->show(state, level); indent_attribute(level, "stack_size"); code->stack_size()->show(state, level); indent_attribute(level, "total_args"); code->total_args()->show(state, level); indent_attribute(level, "internalized"); if(!code->machine_code_) { std::cout << "no\n"; } else { std::cout << "yes\n"; #ifdef ENABLE_LLVM MachineCode* v = code->machine_code(); for(int i = 0; i < MachineCode::cMaxSpecializations; i++) { if(!v->specializations[i].jit_data) continue; llvm::Function* func = v->specializations[i].jit_data->llvm_function(); llvm::outs() << "<LLVM>\n" << *func << "</LLVM>\n<MachineCode>\n"; LLVMState::show_machine_code( v->specializations[i].jit_data->native_func(), v->specializations[i].jit_data->native_size()); llvm::outs() << "</MachineCode>\n"; } #endif } close_body(level); }
CallFrame* LLVMState::find_candidate(STATE, CompiledCode* start, CallFrame* call_frame) { if(!config_.jit_inline_generic) { return call_frame; } int depth = config().jit_limit_search; if(!start) { throw CompileError("find_candidate: null start"); } if(!call_frame) { throw CompileError("find_candidate: null call frame"); } // if(!start) { // start = call_frame->compiled_code; // call_frame = call_frame->previous; // depth--; // } if(debug_search) { std::cout << "> call_count: " << call_frame->compiled_code->machine_code()->call_count << " size: " << call_frame->compiled_code->machine_code()->total << " sends: " << call_frame->compiled_code->machine_code()->call_site_count() << std::endl; call_frame->print_backtrace(state, 1); } if(start->machine_code()->total > (size_t)config_.jit_limit_inline_method) { if(debug_search) { std::cout << "JIT: STOP. reason: trigger method isn't small: " << start->machine_code()->total << " > " << config_.jit_limit_inline_method << std::endl; } return call_frame; } MachineCode* mcode = start->machine_code(); if(mcode->required_args != mcode->total_args) { if(debug_search) { std::cout << "JIT: STOP. reason: trigger method req_args != total_args" << std::endl; } return call_frame; } if(mcode->no_inline_p()) { if(debug_search) { std::cout << "JIT: STOP. reason: trigger method no_inline_p() = true" << std::endl; } return call_frame; } CallFrame* callee = call_frame; call_frame = call_frame->previous; if(!call_frame) return callee; // Now start looking at callers. while(depth-- > 0) { CompiledCode* cur = call_frame->compiled_code; if(!cur) { if(debug_search) { std::cout << "JIT: STOP. reason: synthetic CallFrame hit" << std::endl; } return callee; } MachineCode* mcode = cur->machine_code(); if(debug_search) { std::cout << "> call_count: " << mcode->call_count << " size: " << mcode->total << " sends: " << mcode->call_site_count() << std::endl; call_frame->print_backtrace(state, 1); } /* if(call_frame->block_p() || mcode->required_args != mcode->total_args // has a splat || mcode->call_count < 200 // not called much || mcode->jitted() // already jitted || mcode->parent() // is a block ) return callee; */ if(mcode->required_args != mcode->total_args) { if(debug_search) { std::cout << "JIT: STOP. reason: req_args != total_args" << std::endl; } return callee; } if(mcode->call_count < config_.jit_threshold_inline) { if(debug_search) { std::cout << "JIT: STOP. reason: call_count too small: " << mcode->call_count << " < " << config_.jit_threshold_inline << std::endl; } return callee; } if(mcode->jitted_p()) { if(debug_search) { std::cout << "JIT: STOP. reason: already jitted" << std::endl; } return callee; } if(mcode->no_inline_p()) { if(debug_search) { std::cout << "JIT: STOP. reason: no_inline_p() = true" << std::endl; } return callee; } if(call_frame->jitted_p() || call_frame->inline_method_p()) { return callee; } if(mcode->call_site_count() > eMaxInlineSendCount) { if(debug_search) { std::cout << "JIT: STOP. reason: high send count" << std::endl; } return call_frame; } // if(mcode->required_args != mcode->total_args // has a splat // || mcode->call_count < 200 // not called much // || mcode->jitted() // already jitted // || !mcode->no_inline_p() // method marked as not inlineable // ) return callee; CallFrame* prev = call_frame->previous; if(!prev) { if(debug_search) { std::cout << "JIT: STOP. reason: toplevel method" << std::endl; } return call_frame; } // if(cur->machine_code()->total > SMALL_METHOD_SIZE) { // if(debug_search) { // std::cout << "JIT: STOP. reason: big method: " // << cur->machine_code()->total << " > " // << SMALL_METHOD_SIZE // << "\n"; // } // return call_frame; // } // if(!next || cur->machine_code()->total > SMALL_METHOD_SIZE) return call_frame; callee = call_frame; call_frame = prev; } return callee; }
MachineCode* machine_code() { return method_->machine_code(); }
void Compiler::compile_method(BackgroundCompileRequest* req) { CompiledCode* code = req->method(); if(ctx_->llvm_state()->config().jit_inline_debug) { struct timeval tv; gettimeofday(&tv, NULL); ctx_->llvm_state()->log() << "JIT: compiling " << ctx_->llvm_state()->enclosure_name(code) << "#" << ctx_->llvm_state()->symbol_debug_str(code->name()) << " (" << tv.tv_sec << "." << tv.tv_usec << ")\n"; } #ifdef HAVE_DTRACE if(RUBINIUS_JIT_FUNCTION_BEGIN_ENABLED()) { RBX_DTRACE_CONST char* class_name = const_cast<RBX_DTRACE_CONST char*>( ctx_->llvm_state()->enclosure_name(code).c_str()); RBX_DTRACE_CONST char* method_name = const_cast<RBX_DTRACE_CONST char*>( ctx_->llvm_state()->symbol_debug_str(code->name()).c_str()); RBX_DTRACE_CONST char* file_name = const_cast<RBX_DTRACE_CONST char*>( ctx_->llvm_state()->symbol_debug_str(code->file()).c_str()); int line = code->start_line(); RUBINIUS_JIT_FUNCTION_BEGIN(class_name, method_name, file_name, line); } #endif JITMethodInfo info(ctx_, code, code->machine_code()); info.is_block = false; info.hits = req->hits(); if(Class* cls = req->receiver_class()) { info.set_self_class(cls); } ctx_->set_root(&info); jit::MethodBuilder work(ctx_, info); work.setup(); compile_builder(info, work); ctx_->set_root(NULL); #ifdef HAVE_DTRACE if(RUBINIUS_JIT_FUNCTION_END_ENABLED()) { RBX_DTRACE_CONST char* class_name = const_cast<RBX_DTRACE_CONST char*>( ctx_->llvm_state()->enclosure_name(code).c_str()); RBX_DTRACE_CONST char* method_name = const_cast<RBX_DTRACE_CONST char*>( ctx_->llvm_state()->symbol_debug_str(code->name()).c_str()); RBX_DTRACE_CONST char* file_name = const_cast<RBX_DTRACE_CONST char*>( ctx_->llvm_state()->symbol_debug_str(code->file()).c_str()); int line = code->start_line(); RUBINIUS_JIT_FUNCTION_END(class_name, method_name, file_name, line); } #endif }
Object* MachineCode::execute_specialized(STATE, CallFrame* previous, Executable* exec, Module* mod, Arguments& args) { CompiledCode* code = as<CompiledCode>(exec); MachineCode* mcode = code->machine_code(); StackVariables* scope = ALLOCA_STACKVARIABLES(mcode->number_of_locals); // Originally, I tried using msg.module directly, but what happens is if // super is used, that field is read. If you combine that with the method // being called recursively, msg.module can change, causing super() to // look in the wrong place. // // Thus, we have to cache the value in the StackVariables. scope->initialize(args.recv(), args.block(), mod, mcode->number_of_locals); InterpreterCallFrame* frame = ALLOCA_CALLFRAME(mcode->stack_size); // If argument handling fails.. if(ArgumentHandler::call(state, mcode, scope, args) == false) { Exception* exc = Exception::make_argument_error(state, mcode->total_args, args.total(), args.name()); exc->locations(state, Location::from_call_stack(state, previous)); state->raise_exception(exc); return NULL; } frame->prepare(mcode->stack_size); frame->previous = previous; frame->constant_scope_ = 0; frame->dispatch_data = 0; frame->compiled_code = code; frame->flags = 0; frame->optional_jit_data = 0; frame->top_scope_ = 0; frame->scope = scope; frame->arguments = &args; GCTokenImpl gct; #ifdef ENABLE_LLVM // A negative call_count means we've disabled usage based JIT // for this method. if(mcode->call_count >= 0) { if(mcode->call_count >= state->shared().config.jit_call_til_compile) { LLVMState* ls = LLVMState::get(state); OnStack<3> os(state, exec, mod, code); ls->compile_callframe(state, gct, code, frame); } else { mcode->call_count++; } } #endif OnStack<3> os(state, exec, mod, code); #ifdef RBX_PROFILER if(unlikely(state->vm()->tooling())) { // Check the stack and interrupts here rather than in the interpreter // loop itself. if(!state->check_interrupts(gct, frame, frame)) return NULL; state->checkpoint(gct, frame); tooling::MethodEntry method(state, exec, mod, args, code); RUBINIUS_METHOD_ENTRY_HOOK(state, mod, args.name(), previous); Object* result = (*mcode->run)(state, mcode, frame); RUBINIUS_METHOD_RETURN_HOOK(state, mod, args.name(), previous); return result; } else { if(!state->check_interrupts(gct, frame, frame)) return NULL; state->checkpoint(gct, frame); RUBINIUS_METHOD_ENTRY_HOOK(state, mod, args.name(), previous); Object* result = (*mcode->run)(state, mcode, frame); RUBINIUS_METHOD_RETURN_HOOK(state, mod, args.name(), previous); return result; } #else if(!state->check_interrupts(gct, frame, frame)) return NULL; state->checkpoint(gct, frame); RUBINIUS_METHOD_ENTRY_HOOK(state, mod, args.name(), previous); Object* result = (*mcode->run)(state, mcode, frame); RUBINIUS_METHOD_RETURN_HOOK(state, mod, args.name(), previous); return result; #endif }
void calculate_ip(void** pos) { ip_ = pos - compiled_code->machine_code()->addresses; }