From 479e39a0585a7f67fe04fdea876d8a3a8c477ac5 Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Tue, 12 Mar 2013 18:03:18 +0000 Subject: [PATCH] Parallel recompilation: remove interrupt for code generation. R=jkummerow@chromium.org BUG= Review URL: https://chromiumcodereview.appspot.com/12488006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13917 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/builtins-arm.cc | 29 ++++++++++++ src/builtins.h | 3 ++ src/compiler.cc | 45 +++++++++++-------- src/deoptimizer.cc | 4 ++ src/execution.cc | 30 ------------- src/execution.h | 3 +- src/flag-definitions.h | 5 +-- src/full-codegen.cc | 1 + src/ia32/builtins-ia32.cc | 26 +++++++++++ src/mips/builtins-mips.cc | 29 ++++++++++++ src/objects-inl.h | 34 +++++++++++++- src/objects.cc | 47 +++++++++++++++---- src/objects.h | 14 ++++-- src/optimizing-compiler-thread.cc | 62 ++++++++++++-------------- src/optimizing-compiler-thread.h | 5 --- src/runtime-profiler.cc | 24 +++++++--- src/runtime.cc | 41 +++++++++-------- src/runtime.h | 2 +- src/x64/builtins-x64.cc | 27 +++++++++++ test/mjsunit/compiler/parallel-proto-change.js | 9 ++-- test/mjsunit/fuzz-natives-part1.js | 5 +-- test/mjsunit/fuzz-natives-part2.js | 5 +-- test/mjsunit/fuzz-natives-part3.js | 5 +-- test/mjsunit/fuzz-natives-part4.js | 5 +-- test/mjsunit/manual-parallel-recompile.js | 29 +++--------- test/mjsunit/parallel-optimize-disabled.js | 6 +-- 26 files changed, 315 insertions(+), 180 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index b2c2193..142787b 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -746,6 +746,35 @@ void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) { } +void Builtins::Generate_InstallRecompiledCode(MacroAssembler* masm) { + // Enter an internal frame. + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Preserve the function. + __ push(r1); + // Push call kind information. + __ push(r5); + + // Push the function on the stack as the argument to the runtime function. + __ push(r1); + __ CallRuntime(Runtime::kInstallRecompiledCode, 1); + // Calculate the entry point. + __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag)); + + // Restore call kind information. + __ pop(r5); + // Restore saved function. + __ pop(r1); + + // Tear down internal frame. + } + + // Do a tail-call of the compiled function. + __ Jump(r2); +} + + void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::INTERNAL); diff --git a/src/builtins.h b/src/builtins.h index 83b134c..12ed56a 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -87,6 +87,8 @@ enum BuiltinExtraArguments { Code::kNoExtraICState) \ V(InRecompileQueue, BUILTIN, UNINITIALIZED, \ Code::kNoExtraICState) \ + V(InstallRecompiledCode, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ V(JSConstructStubCountdown, BUILTIN, UNINITIALIZED, \ Code::kNoExtraICState) \ V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, \ @@ -366,6 +368,7 @@ class Builtins { CFunctionId id, BuiltinExtraArguments extra_args); static void Generate_InRecompileQueue(MacroAssembler* masm); + static void Generate_InstallRecompiledCode(MacroAssembler* masm); static void Generate_ParallelRecompile(MacroAssembler* masm); static void Generate_JSConstructStubCountdown(MacroAssembler* masm); static void Generate_JSConstructStubGeneric(MacroAssembler* masm); diff --git a/src/compiler.cc b/src/compiler.cc index c4ef8eb..8f4be0c 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -33,6 +33,7 @@ #include "codegen.h" #include "compilation-cache.h" #include "debug.h" +#include "deoptimizer.h" #include "full-codegen.h" #include "gdb-jit.h" #include "hydrogen.h" @@ -900,7 +901,6 @@ bool Compiler::CompileLazy(CompilationInfo* info) { void Compiler::RecompileParallel(Handle closure) { - if (closure->IsInRecompileQueue()) return; ASSERT(closure->IsMarkedForParallelRecompilation()); Isolate* isolate = closure->GetIsolate(); @@ -928,8 +928,7 @@ void Compiler::RecompileParallel(Handle closure) { { CompilationHandleScope handle_scope(*info); - if (!FLAG_manual_parallel_recompilation && - InstallCodeFromOptimizedCodeMap(*info)) { + if (InstallCodeFromOptimizedCodeMap(*info)) { return; } @@ -944,11 +943,10 @@ void Compiler::RecompileParallel(Handle closure) { new(info->zone()) OptimizingCompiler(*info); OptimizingCompiler::Status status = compiler->CreateGraph(); if (status == OptimizingCompiler::SUCCEEDED) { - isolate->optimizing_compiler_thread()->QueueForOptimization(compiler); + closure->MarkInRecompileQueue(); shared->code()->set_profiler_ticks(0); - closure->ReplaceCode(isolate->builtins()->builtin( - Builtins::kInRecompileQueue)); info.Detach(); + isolate->optimizing_compiler_thread()->QueueForOptimization(compiler); } else if (status == OptimizingCompiler::BAILED_OUT) { isolate->clear_pending_exception(); InstallFullCode(*info); @@ -957,24 +955,27 @@ void Compiler::RecompileParallel(Handle closure) { } } - if (isolate->has_pending_exception()) { - isolate->clear_pending_exception(); + if (shared->code()->stack_check_patched_for_osr()) { + // At this point we either put the function on recompilation queue or + // aborted optimization. In either case we want to continue executing + // the unoptimized code without running into OSR. If the unoptimized + // code has been patched for OSR, unpatch it. + InterruptStub interrupt_stub; + Handle check_code = interrupt_stub.GetCode(isolate); + Handle replacement_code = + isolate->builtins()->OnStackReplacement(); + Deoptimizer::RevertStackCheckCode(shared->code(), + *check_code, + *replacement_code); } + + if (isolate->has_pending_exception()) isolate->clear_pending_exception(); } void Compiler::InstallOptimizedCode(OptimizingCompiler* optimizing_compiler) { SmartPointer info(optimizing_compiler->info()); - // Function may have been optimized meanwhile by OSR. - if (FLAG_use_osr) { - // Function may have already been optimized meanwhile by OSR. - if (!info->code().is_null() && - info->code()->kind() == Code::OPTIMIZED_FUNCTION) { - return; - } - // OSR may also have caused optimization to be disabled. - if (info->shared_info()->optimization_disabled()) return; - } + ASSERT(info->closure()->IsMarkedForInstallingRecompiledCode()); Isolate* isolate = info->isolate(); VMState state(isolate, PARALLEL_COMPILER); @@ -1002,10 +1003,18 @@ void Compiler::InstallOptimizedCode(OptimizingCompiler* optimizing_compiler) { info->closure()->context()->native_context()) == -1) { InsertCodeIntoOptimizedCodeMap(*info); } + if (FLAG_trace_parallel_recompilation) { + PrintF(" ** Optimized code for "); + info->closure()->PrintName(); + PrintF(" installed.\n"); + } } else { info->SetCode(Handle(info->shared_info()->code())); InstallFullCode(*info); } + // Optimized code is finally replacing unoptimized code. Reset the latter's + // profiler ticks to prevent too soon re-opt after a deopt. + info->shared_info()->code()->set_profiler_ticks(0); } diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc index 3924636..d077fed 100644 --- a/src/deoptimizer.cc +++ b/src/deoptimizer.cc @@ -1993,6 +1993,7 @@ void Deoptimizer::PatchStackCheckCode(Code* unoptimized_code, // Iterate over the stack check table and patch every stack check // call to an unconditional call to the replacement code. ASSERT(unoptimized_code->kind() == Code::FUNCTION); + ASSERT(!unoptimized_code->stack_check_patched_for_osr()); Address stack_check_cursor = unoptimized_code->instruction_start() + unoptimized_code->stack_check_table_offset(); uint32_t table_length = Memory::uint32_at(stack_check_cursor); @@ -2006,6 +2007,7 @@ void Deoptimizer::PatchStackCheckCode(Code* unoptimized_code, replacement_code); stack_check_cursor += 2 * kIntSize; } + unoptimized_code->set_stack_check_patched_for_osr(true); } @@ -2015,6 +2017,7 @@ void Deoptimizer::RevertStackCheckCode(Code* unoptimized_code, // Iterate over the stack check table and revert the patched // stack check calls. ASSERT(unoptimized_code->kind() == Code::FUNCTION); + ASSERT(unoptimized_code->stack_check_patched_for_osr()); Address stack_check_cursor = unoptimized_code->instruction_start() + unoptimized_code->stack_check_table_offset(); uint32_t table_length = Memory::uint32_at(stack_check_cursor); @@ -2028,6 +2031,7 @@ void Deoptimizer::RevertStackCheckCode(Code* unoptimized_code, replacement_code); stack_check_cursor += 2 * kIntSize; } + unoptimized_code->set_stack_check_patched_for_osr(false); } diff --git a/src/execution.cc b/src/execution.cc index 1012911..dee3112 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -432,25 +432,6 @@ void StackGuard::TerminateExecution() { } -void StackGuard::RequestCodeReadyEvent() { - ASSERT(FLAG_parallel_recompilation); - if (ExecutionAccess::TryLock(isolate_)) { - thread_local_.interrupt_flags_ |= CODE_READY; - if (thread_local_.postpone_interrupts_nesting_ == 0) { - thread_local_.jslimit_ = thread_local_.climit_ = kInterruptLimit; - isolate_->heap()->SetStackLimits(); - } - ExecutionAccess::Unlock(isolate_); - } -} - - -bool StackGuard::IsCodeReadyEvent() { - ExecutionAccess access(isolate_); - return (thread_local_.interrupt_flags_ & CODE_READY) != 0; -} - - bool StackGuard::IsGCRequest() { ExecutionAccess access(isolate_); return (thread_local_.interrupt_flags_ & GC_REQUEST) != 0; @@ -899,17 +880,6 @@ MaybeObject* Execution::HandleStackGuardInterrupt(Isolate* isolate) { stack_guard->Continue(GC_REQUEST); } - if (stack_guard->IsCodeReadyEvent()) { - ASSERT(FLAG_parallel_recompilation); - if (FLAG_trace_parallel_recompilation) { - PrintF(" ** CODE_READY event received.\n"); - } - stack_guard->Continue(CODE_READY); - } - if (!stack_guard->IsTerminateExecution() && - !FLAG_manual_parallel_recompilation) { - isolate->optimizing_compiler_thread()->InstallOptimizedFunctions(); - } isolate->counters()->stack_interrupts()->Increment(); isolate->counters()->runtime_profiler_ticks()->Increment(); diff --git a/src/execution.h b/src/execution.h index 7ce5b8c..c350805 100644 --- a/src/execution.h +++ b/src/execution.h @@ -41,8 +41,7 @@ enum InterruptFlag { DEBUGCOMMAND = 1 << 2, PREEMPT = 1 << 3, TERMINATE = 1 << 4, - GC_REQUEST = 1 << 5, - CODE_READY = 1 << 6 + GC_REQUEST = 1 << 5 }; diff --git a/src/flag-definitions.h b/src/flag-definitions.h index a385671..3a2426b 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -246,13 +246,10 @@ DEFINE_bool(opt_safe_uint32_operations, true, DEFINE_bool(parallel_recompilation, false, "optimizing hot functions asynchronously on a separate thread") DEFINE_bool(trace_parallel_recompilation, false, "track parallel recompilation") -DEFINE_int(parallel_recompilation_queue_length, 2, +DEFINE_int(parallel_recompilation_queue_length, 3, "the length of the parallel compilation queue") DEFINE_int(parallel_recompilation_delay, 0, "artificial compilation delay in ms") -DEFINE_bool(manual_parallel_recompilation, false, - "disable automatic optimization") -DEFINE_implication(manual_parallel_recompilation, parallel_recompilation) DEFINE_bool(omit_prototype_checks_for_leaf_maps, true, "do not emit prototype checks if all prototypes have leaf maps, " "deoptimize the optimized code if the layout of the maps changes.") diff --git a/src/full-codegen.cc b/src/full-codegen.cc index a43f674..cb6f228 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -336,6 +336,7 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) { code->set_allow_osr_at_loop_nesting_level(0); code->set_profiler_ticks(0); code->set_stack_check_table_offset(table_offset); + code->set_stack_check_patched_for_osr(false); CodeGenerator::PrintCode(code, info); info->SetCode(code); // May be an empty handle. #ifdef ENABLE_GDB_JIT_INTERFACE diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 7ce211d..51e16fd 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -87,6 +87,32 @@ void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) { } +void Builtins::Generate_InstallRecompiledCode(MacroAssembler* masm) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Push a copy of the function. + __ push(edi); + // Push call kind information. + __ push(ecx); + + __ push(edi); // Function is also the parameter to the runtime call. + __ CallRuntime(Runtime::kInstallRecompiledCode, 1); + + // Restore call kind information. + __ pop(ecx); + // Restore receiver. + __ pop(edi); + + // Tear down internal frame. + } + + // Do a tail-call of the compiled function. + __ lea(eax, FieldOperand(eax, Code::kHeaderSize)); + __ jmp(eax); +} + + void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::INTERNAL); diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index c05fdc2..2facd13 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -758,6 +758,35 @@ void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) { } +void Builtins::Generate_InstallRecompiledCode(MacroAssembler* masm) { + // Enter an internal frame. + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Preserve the function. + __ push(a1); + // Push call kind information. + __ push(t1); + + // Push the function on the stack as the argument to the runtime function. + __ push(a1); + __ CallRuntime(Runtime::kInstallRecompiledCode, 1); + // Calculate the entry point. + __ Addu(t9, v0, Operand(Code::kHeaderSize - kHeapObjectTag)); + + // Restore call kind information. + __ pop(t1); + // Restore saved function. + __ pop(a1); + + // Tear down temporary frame. + } + + // Do a tail-call of the compiled function. + __ Jump(t9); +} + + void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::INTERNAL); diff --git a/src/objects-inl.h b/src/objects-inl.h index d636c8f..7ddfdfc 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -3768,6 +3768,22 @@ void Code::set_stack_check_table_offset(unsigned offset) { } +bool Code::stack_check_patched_for_osr() { + ASSERT_EQ(FUNCTION, kind()); + return StackCheckPatchedForOSRField::decode( + READ_UINT32_FIELD(this, kKindSpecificFlags2Offset)); +} + + +void Code::set_stack_check_patched_for_osr(bool value) { + ASSERT_EQ(FUNCTION, kind()); + int previous = READ_UINT32_FIELD(this, kKindSpecificFlags2Offset); + int updated = StackCheckPatchedForOSRField::update(previous, value); + WRITE_UINT32_FIELD(this, kKindSpecificFlags2Offset, updated); +} + + + CheckType Code::check_type() { ASSERT(is_call_stub() || is_keyed_call_stub()); byte type = READ_BYTE_FIELD(this, kCheckTypeOffset); @@ -4300,6 +4316,7 @@ BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_expression, kIsExpressionBit) BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_toplevel, kIsTopLevelBit) + BOOL_GETTER(SharedFunctionInfo, compiler_hints, has_only_simple_this_property_assignments, @@ -4706,9 +4723,15 @@ bool JSFunction::IsMarkedForLazyRecompilation() { } +bool JSFunction::IsMarkedForInstallingRecompiledCode() { + return code() == GetIsolate()->builtins()->builtin( + Builtins::kInstallRecompiledCode); +} + + bool JSFunction::IsMarkedForParallelRecompilation() { - return code() == - GetIsolate()->builtins()->builtin(Builtins::kParallelRecompile); + return code() == GetIsolate()->builtins()->builtin( + Builtins::kParallelRecompile); } @@ -4740,6 +4763,13 @@ void JSFunction::set_code(Code* value) { } +void JSFunction::set_code_no_write_barrier(Code* value) { + ASSERT(!HEAP->InNewSpace(value)); + Address entry = value->entry(); + WRITE_INTPTR_FIELD(this, kCodeEntryOffset, reinterpret_cast(entry)); +} + + void JSFunction::ReplaceCode(Code* code) { bool was_optimized = IsOptimized(); bool is_optimized = code->kind() == Code::OPTIMIZED_FUNCTION; diff --git a/src/objects.cc b/src/objects.cc index 96354c8..a57e264 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -7980,23 +7980,52 @@ void JSFunction::MarkForLazyRecompilation() { ASSERT(is_compiled() && !IsOptimized()); ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); - Builtins* builtins = GetIsolate()->builtins(); - ReplaceCode(builtins->builtin(Builtins::kLazyRecompile)); + set_code_no_write_barrier( + GetIsolate()->builtins()->builtin(Builtins::kLazyRecompile)); + // No write barrier required, since the builtin is part of the root set. } + void JSFunction::MarkForParallelRecompilation() { ASSERT(is_compiled() && !IsOptimized()); ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); - Builtins* builtins = GetIsolate()->builtins(); - ReplaceCode(builtins->builtin(Builtins::kParallelRecompile)); + ASSERT(FLAG_parallel_recompilation); + if (FLAG_trace_parallel_recompilation) { + PrintF(" ** Marking "); + PrintName(); + PrintF(" for parallel recompilation.\n"); + } + set_code_no_write_barrier( + GetIsolate()->builtins()->builtin(Builtins::kParallelRecompile)); + // No write barrier required, since the builtin is part of the root set. +} + - // Unlike MarkForLazyRecompilation, after queuing a function for - // recompilation on the compiler thread, we actually tail-call into - // the full code. We reset the profiler ticks here so that the - // function doesn't bother the runtime profiler too much. - shared()->code()->set_profiler_ticks(0); +void JSFunction::MarkForInstallingRecompiledCode() { + ASSERT(is_compiled() && !IsOptimized()); + ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); + ASSERT(FLAG_parallel_recompilation); + set_code_no_write_barrier( + GetIsolate()->builtins()->builtin(Builtins::kInstallRecompiledCode)); + // No write barrier required, since the builtin is part of the root set. } + +void JSFunction::MarkInRecompileQueue() { + ASSERT(is_compiled() && !IsOptimized()); + ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); + ASSERT(FLAG_parallel_recompilation); + if (FLAG_trace_parallel_recompilation) { + PrintF(" ** Queueing "); + PrintName(); + PrintF(" for parallel recompilation.\n"); + } + set_code_no_write_barrier( + GetIsolate()->builtins()->builtin(Builtins::kInRecompileQueue)); + // No write barrier required, since the builtin is part of the root set. +} + + static bool CompileLazyHelper(CompilationInfo* info, ClearExceptionFlag flag) { // Compile the source information to a code object. diff --git a/src/objects.h b/src/objects.h index 1b83c03..9d52554 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4499,6 +4499,9 @@ class Code: public HeapObject { inline unsigned stack_check_table_offset(); inline void set_stack_check_table_offset(unsigned offset); + inline bool stack_check_patched_for_osr(); + inline void set_stack_check_patched_for_osr(bool value); + // [check type]: For kind CALL_IC, tells how to check if the // receiver is valid for the given call. inline CheckType check_type(); @@ -4776,6 +4779,7 @@ class Code: public HeapObject { // KindSpecificFlags2 layout (FUNCTION) class StackCheckTableOffsetField: public BitField {}; + class StackCheckPatchedForOSRField: public BitField {}; // Signed field cannot be encoded using the BitField class. static const int kArgumentsCountShift = 17; @@ -6138,10 +6142,10 @@ class SharedFunctionInfo: public HeapObject { // Bit positions in start_position_and_type. // The source code start position is in the 30 most significant bits of // the start_position_and_type field. - static const int kIsExpressionBit = 0; - static const int kIsTopLevelBit = 1; + static const int kIsExpressionBit = 0; + static const int kIsTopLevelBit = 1; static const int kStartPositionShift = 2; - static const int kStartPositionMask = ~((1 << kStartPositionShift) - 1); + static const int kStartPositionMask = ~((1 << kStartPositionShift) - 1); // Bit positions in compiler_hints. static const int kCodeAgeSize = 3; @@ -6275,6 +6279,7 @@ class JSFunction: public JSObject { // 8.6.2, page 27. inline Code* code(); inline void set_code(Code* code); + inline void set_code_no_write_barrier(Code* code); inline void ReplaceCode(Code* code); inline Code* unchecked_code(); @@ -6295,6 +6300,8 @@ class JSFunction: public JSObject { // recompiled the next time it is executed. void MarkForLazyRecompilation(); void MarkForParallelRecompilation(); + void MarkForInstallingRecompiledCode(); + void MarkInRecompileQueue(); // Helpers to compile this function. Returns true on success, false on // failure (e.g., stack overflow during compilation). @@ -6310,6 +6317,7 @@ class JSFunction: public JSObject { // recompilation. inline bool IsMarkedForLazyRecompilation(); inline bool IsMarkedForParallelRecompilation(); + inline bool IsMarkedForInstallingRecompiledCode(); // Tells whether or not the function is on the parallel // recompilation queue. diff --git a/src/optimizing-compiler-thread.cc b/src/optimizing-compiler-thread.cc index bc26442..abcce7b 100644 --- a/src/optimizing-compiler-thread.cc +++ b/src/optimizing-compiler-thread.cc @@ -68,14 +68,6 @@ void OptimizingCompilerThread::Run() { CompileNext(); - if (!FLAG_manual_parallel_recompilation) { - isolate_->stack_guard()->RequestCodeReadyEvent(); - } else { - // In manual mode, do not trigger a code ready event. - // Instead, wait for the optimized functions to be installed manually. - output_queue_semaphore_->Signal(); - } - if (FLAG_trace_parallel_recompilation) { time_spent_compiling_ += OS::Ticks() - compiling_start; } @@ -89,11 +81,7 @@ void OptimizingCompilerThread::CompileNext() { input_queue_.Dequeue(&optimizing_compiler); Barrier_AtomicIncrement(&queue_length_, static_cast(-1)); - // Function may have been optimized meanwhile by OSR. - if (FLAG_use_osr && - optimizing_compiler->info()->closure()->IsOptimized()) { - return; - } + ASSERT(optimizing_compiler->info()->closure()->IsInRecompileQueue()); OptimizingCompiler::Status status = optimizing_compiler->OptimizeGraph(); ASSERT(status != OptimizingCompiler::FAILED); @@ -101,10 +89,21 @@ void OptimizingCompilerThread::CompileNext() { USE(status); output_queue_.Enqueue(optimizing_compiler); + + // The execution thread can call InstallOptimizedFunctions() at any time, + // including at this point, after queuing for install and before marking + // for install. To avoid race condition, functions that are queued but not + // yet marked for install are not processed by InstallOptimizedFunctions(). + + ASSERT(optimizing_compiler->info()->closure()->IsInRecompileQueue()); + // Mark function to generate and install optimized code. We assume this + // write to be atomic. + optimizing_compiler->info()->closure()->MarkForInstallingRecompiledCode(); } void OptimizingCompilerThread::Stop() { + ASSERT(!IsOptimizerThread()); Release_Store(&stop_thread_, static_cast(true)); input_queue_semaphore_->Signal(); stop_semaphore_->Wait(); @@ -133,45 +132,42 @@ void OptimizingCompilerThread::Stop() { void OptimizingCompilerThread::InstallOptimizedFunctions() { + ASSERT(!IsOptimizerThread()); HandleScope handle_scope(isolate_); int functions_installed = 0; while (!output_queue_.IsEmpty()) { - if (FLAG_manual_parallel_recompilation) { - output_queue_semaphore_->Wait(); + OptimizingCompiler* compiler = *output_queue_.Peek(); + + if (compiler->info()->closure()->IsInRecompileQueue()) { + // A function may be queued for install, but not marked as such yet. + // We continue with the output queue the next to avoid race condition. + break; } - OptimizingCompiler* compiler = NULL; output_queue_.Dequeue(&compiler); + +#ifdef DEBUG + // Create new closure handle since the deferred handle is about to die. + Handle closure(*compiler->info()->closure()); +#endif // DEBUG + Compiler::InstallOptimizedCode(compiler); + // Assert that the marker builtin has been replaced by actual code. + ASSERT(!closure->IsInRecompileQueue()); functions_installed++; } - if (FLAG_trace_parallel_recompilation && functions_installed != 0) { - PrintF(" ** Installed %d function(s).\n", functions_installed); - } -} - - -Handle - OptimizingCompilerThread::InstallNextOptimizedFunction() { - ASSERT(FLAG_manual_parallel_recompilation || - FLAG_parallel_recompilation_delay != 0); - output_queue_semaphore_->Wait(); - OptimizingCompiler* compiler = NULL; - output_queue_.Dequeue(&compiler); - // Copy a handle from deferred handle scope to the normal handle scope. - Handle shared(*compiler->info()->shared_info()); - Compiler::InstallOptimizedCode(compiler); - return shared; } void OptimizingCompilerThread::QueueForOptimization( OptimizingCompiler* optimizing_compiler) { ASSERT(IsQueueAvailable()); + ASSERT(!IsOptimizerThread()); Barrier_AtomicIncrement(&queue_length_, static_cast(1)); input_queue_.Enqueue(optimizing_compiler); input_queue_semaphore_->Signal(); } + #ifdef DEBUG bool OptimizingCompilerThread::IsOptimizerThread() { if (!FLAG_parallel_recompilation) return false; diff --git a/src/optimizing-compiler-thread.h b/src/optimizing-compiler-thread.h index 990005b..40599ff 100644 --- a/src/optimizing-compiler-thread.h +++ b/src/optimizing-compiler-thread.h @@ -50,7 +50,6 @@ class OptimizingCompilerThread : public Thread { isolate_(isolate), stop_semaphore_(OS::CreateSemaphore(0)), input_queue_semaphore_(OS::CreateSemaphore(0)), - output_queue_semaphore_(OS::CreateSemaphore(0)), time_spent_compiling_(0), time_spent_total_(0) { NoBarrier_Store(&stop_thread_, static_cast(false)); @@ -63,9 +62,6 @@ class OptimizingCompilerThread : public Thread { void QueueForOptimization(OptimizingCompiler* optimizing_compiler); void InstallOptimizedFunctions(); - // Wait for the next optimized function and install it. - Handle InstallNextOptimizedFunction(); - inline bool IsQueueAvailable() { // We don't need a barrier since we have a data dependency right // after. @@ -85,7 +81,6 @@ class OptimizingCompilerThread : public Thread { #endif ~OptimizingCompilerThread() { - delete output_queue_semaphore_; // Only used for manual mode. delete input_queue_semaphore_; delete stop_semaphore_; } diff --git a/src/runtime-profiler.cc b/src/runtime-profiler.cc index 94a5650..2606f8a 100644 --- a/src/runtime-profiler.cc +++ b/src/runtime-profiler.cc @@ -140,8 +140,6 @@ static void GetICCounts(JSFunction* function, void RuntimeProfiler::Optimize(JSFunction* function, const char* reason) { ASSERT(function->IsOptimizable()); - // If we are in manual mode, don't auto-optimize anything. - if (FLAG_manual_parallel_recompilation) return; if (FLAG_trace_opt) { PrintF("[marking "); @@ -157,6 +155,8 @@ void RuntimeProfiler::Optimize(JSFunction* function, const char* reason) { } if (FLAG_parallel_recompilation) { + ASSERT(!function->IsMarkedForInstallingRecompiledCode()); + ASSERT(!function->IsInRecompileQueue()); function->MarkForParallelRecompilation(); } else { // The next call to the function will trigger optimization. @@ -169,7 +169,8 @@ void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) { // See AlwaysFullCompiler (in compiler.cc) comment on why we need // Debug::has_break_points(). ASSERT(function->IsMarkedForLazyRecompilation() || - function->IsMarkedForParallelRecompilation()); + function->IsMarkedForParallelRecompilation() || + function->IsOptimized()); if (!FLAG_use_osr || isolate_->DebuggerHasBreakPoints() || function->IsBuiltin()) { @@ -245,6 +246,12 @@ void RuntimeProfiler::AddSample(JSFunction* function, int weight) { void RuntimeProfiler::OptimizeNow() { HandleScope scope(isolate_); + if (FLAG_parallel_recompilation) { + // Take this as opportunity to process the optimizing compiler thread's + // output queue so that it does not unnecessarily keep objects alive. + isolate_->optimizing_compiler_thread()->InstallOptimizedFunctions(); + } + // Run through the JavaScript frames and collect them. If we already // have a sample of the function, we mark it for optimizations // (eagerly or lazily). @@ -280,9 +287,14 @@ void RuntimeProfiler::OptimizeNow() { Code* shared_code = shared->code(); if (shared_code->kind() != Code::FUNCTION) continue; - - if (function->IsMarkedForLazyRecompilation() || - function->IsMarkedForParallelRecompilation()) { + if (function->IsInRecompileQueue()) continue; + + // Attempt OSR if we are still running unoptimized code even though the + // the function has long been marked or even already been optimized. + if (!frame->is_optimized() && + (function->IsMarkedForLazyRecompilation() || + function->IsMarkedForParallelRecompilation() || + function->IsOptimized())) { int nesting = shared_code->allow_osr_at_loop_nesting_level(); if (nesting == 0) AttemptOnStackReplacement(function); int new_nesting = Min(nesting + 1, Code::kMaxLoopNestingMarker); diff --git a/src/runtime.cc b/src/runtime.cc index e30b350..f68672d 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -7758,7 +7758,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) { RUNTIME_FUNCTION(MaybeObject*, Runtime_ParallelRecompile) { HandleScope handle_scope(isolate); - Handle function = args.at(0); + ASSERT(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); if (!AllowOptimization(isolate, function)) { function->ReplaceCode(function->shared()->code()); return function->code(); @@ -7770,30 +7771,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ParallelRecompile) { } -RUNTIME_FUNCTION(MaybeObject*, Runtime_ForceParallelRecompile) { - HandleScope handle_scope(isolate); - if (!V8::UseCrankshaft()) return isolate->heap()->undefined_value(); - ASSERT(FLAG_parallel_recompilation && FLAG_manual_parallel_recompilation); - if (!isolate->optimizing_compiler_thread()->IsQueueAvailable()) { - return isolate->Throw(*isolate->factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("Recompile queue is full."))); - } - CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0); - fun->ReplaceCode(isolate->builtins()->builtin(Builtins::kParallelRecompile)); - Compiler::RecompileParallel(fun); - return isolate->heap()->undefined_value(); -} - - RUNTIME_FUNCTION(MaybeObject*, Runtime_InstallRecompiledCode) { HandleScope handle_scope(isolate); + ASSERT(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); if (!V8::UseCrankshaft()) return isolate->heap()->undefined_value(); - ASSERT(FLAG_parallel_recompilation && FLAG_manual_parallel_recompilation); - CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0); + ASSERT(FLAG_parallel_recompilation); OptimizingCompilerThread* opt_thread = isolate->optimizing_compiler_thread(); - Handle shared(fun->shared()); - while (*opt_thread->InstallNextOptimizedFunction() != *shared) { } - return isolate->heap()->undefined_value(); + opt_thread->InstallOptimizedFunctions(); + return function->code(); } @@ -7964,6 +7950,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_WaitUntilOptimized) { + HandleScope scope(isolate); + ASSERT(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + if (FLAG_parallel_recompilation) { + if (V8::UseCrankshaft() && function->IsOptimizable()) { + while (!function->IsOptimized()) OS::Sleep(50); + } + } + return isolate->heap()->undefined_value(); +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationStatus) { HandleScope scope(isolate); ASSERT(args.length() == 1); diff --git a/src/runtime.h b/src/runtime.h index ac4a207..934de82 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -87,7 +87,6 @@ namespace internal { F(LazyCompile, 1, 1) \ F(LazyRecompile, 1, 1) \ F(ParallelRecompile, 1, 1) \ - F(ForceParallelRecompile, 1, 1) \ F(InstallRecompiledCode, 1, 1) \ F(NotifyDeoptimized, 1, 1) \ F(NotifyStubFailure, 0, 1) \ @@ -96,6 +95,7 @@ namespace internal { F(ClearFunctionTypeFeedback, 1, 1) \ F(RunningInSimulator, 0, 1) \ F(OptimizeFunctionOnNextCall, -1, 1) \ + F(WaitUntilOptimized, 1, 1) \ F(GetOptimizationStatus, 1, 1) \ F(GetOptimizationCount, 1, 1) \ F(CompileForOnStackReplacement, 1, 1) \ diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 144962b..7a1e88f 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -88,6 +88,33 @@ void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) { } +void Builtins::Generate_InstallRecompiledCode(MacroAssembler* masm) { + // Enter an internal frame. + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Push a copy of the function onto the stack. + __ push(rdi); + // Push call kind information. + __ push(rcx); + + __ push(rdi); // Function is also the parameter to the runtime call. + __ CallRuntime(Runtime::kInstallRecompiledCode, 1); + + // Restore call kind information. + __ pop(rcx); + // Restore function. + __ pop(rdi); + + // Tear down internal frame. + } + + // Do a tail-call of the compiled function. + __ lea(rax, FieldOperand(rax, Code::kHeaderSize)); + __ jmp(rax); +} + + void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::INTERNAL); diff --git a/test/mjsunit/compiler/parallel-proto-change.js b/test/mjsunit/compiler/parallel-proto-change.js index 1aa135a..74e6d86 100644 --- a/test/mjsunit/compiler/parallel-proto-change.js +++ b/test/mjsunit/compiler/parallel-proto-change.js @@ -25,8 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --allow-natives-syntax -// Flags: --parallel-recompilation --manual-parallel-recompilation +// Flags: --allow-natives-syntax --parallel-recompilation function f(foo) { return foo.bar(); } @@ -36,9 +35,11 @@ o.__proto__ = { __proto__: { bar: function() { return 1; } } }; assertEquals(1, f(o)); assertEquals(1, f(o)); -%ForceParallelRecompile(f); +%OptimizeFunctionOnNextCall(f, "parallel"); +assertEquals(1, f(o)); // Change the prototype chain during optimization. o.__proto__.__proto__ = { bar: function() { return 2; } }; -%InstallRecompiledCode(f); + +%WaitUntilOptimized(f); assertEquals(2, f(o)); diff --git a/test/mjsunit/fuzz-natives-part1.js b/test/mjsunit/fuzz-natives-part1.js index 596af97..8b290d5 100644 --- a/test/mjsunit/fuzz-natives-part1.js +++ b/test/mjsunit/fuzz-natives-part1.js @@ -151,6 +151,7 @@ var knownProblems = { "LazyCompile": true, "LazyRecompile": true, "ParallelRecompile": true, + "InstallRecompiledCode": true, "NotifyDeoptimized": true, "NotifyStubFailure": true, "NotifyOSR": true, @@ -200,10 +201,6 @@ var knownProblems = { "_GetCachedArrayIndex": true, "_OneByteSeqStringSetChar": true, "_TwoByteSeqStringSetChar": true, - - // Only for debugging parallel recompilation. - "InstallRecompiledCode": true, - "ForceParallelRecompile": true }; var currentlyUncallable = { diff --git a/test/mjsunit/fuzz-natives-part2.js b/test/mjsunit/fuzz-natives-part2.js index 2faad1d..50ca5c2 100644 --- a/test/mjsunit/fuzz-natives-part2.js +++ b/test/mjsunit/fuzz-natives-part2.js @@ -151,6 +151,7 @@ var knownProblems = { "LazyCompile": true, "LazyRecompile": true, "ParallelRecompile": true, + "InstallRecompiledCode": true, "NotifyDeoptimized": true, "NotifyOSR": true, "CreateObjectLiteralBoilerplate": true, @@ -199,10 +200,6 @@ var knownProblems = { "_GetCachedArrayIndex": true, "_OneByteSeqStringSetChar": true, "_TwoByteSeqStringSetChar": true, - - // Only for debugging parallel recompilation. - "InstallRecompiledCode": true, - "ForceParallelRecompile": true }; var currentlyUncallable = { diff --git a/test/mjsunit/fuzz-natives-part3.js b/test/mjsunit/fuzz-natives-part3.js index ed71d33..05d32e9 100644 --- a/test/mjsunit/fuzz-natives-part3.js +++ b/test/mjsunit/fuzz-natives-part3.js @@ -151,6 +151,7 @@ var knownProblems = { "LazyCompile": true, "LazyRecompile": true, "ParallelRecompile": true, + "InstallRecompiledCode": true, "NotifyDeoptimized": true, "NotifyOSR": true, "CreateObjectLiteralBoilerplate": true, @@ -199,10 +200,6 @@ var knownProblems = { "_GetCachedArrayIndex": true, "_OneByteSeqStringSetChar": true, "_TwoByteSeqStringSetChar": true, - - // Only for debugging parallel recompilation. - "InstallRecompiledCode": true, - "ForceParallelRecompile": true }; var currentlyUncallable = { diff --git a/test/mjsunit/fuzz-natives-part4.js b/test/mjsunit/fuzz-natives-part4.js index 1b128d5..e627065 100644 --- a/test/mjsunit/fuzz-natives-part4.js +++ b/test/mjsunit/fuzz-natives-part4.js @@ -151,6 +151,7 @@ var knownProblems = { "LazyCompile": true, "LazyRecompile": true, "ParallelRecompile": true, + "InstallRecompiledCode": true, "NotifyDeoptimized": true, "NotifyOSR": true, "CreateObjectLiteralBoilerplate": true, @@ -199,10 +200,6 @@ var knownProblems = { "_GetCachedArrayIndex": true, "_OneByteSeqStringSetChar": true, "_TwoByteSeqStringSetChar": true, - - // Only for debugging parallel recompilation. - "InstallRecompiledCode": true, - "ForceParallelRecompile": true }; var currentlyUncallable = { diff --git a/test/mjsunit/manual-parallel-recompile.js b/test/mjsunit/manual-parallel-recompile.js index 26b1605..8d660e0 100644 --- a/test/mjsunit/manual-parallel-recompile.js +++ b/test/mjsunit/manual-parallel-recompile.js @@ -25,13 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --allow-natives-syntax --expose-gc -// Flags: --parallel-recompilation --manual-parallel-recompilation - -function assertOptimized(fun) { - // This assertion takes --always-opt and --nocrankshaft flags into account. - assertTrue(%GetOptimizationStatus(fun) != 2); -} +// Flags: --allow-natives-syntax --expose-gc --parallel-recompilation function assertUnoptimized(fun) { assertTrue(%GetOptimizationStatus(fun) != 1); @@ -54,26 +48,15 @@ function k(x) { } f(g(1)); -f(g(2)); assertUnoptimized(f); assertUnoptimized(g); -%ForceParallelRecompile(f); -%ForceParallelRecompile(g); -assertUnoptimized(f); -assertUnoptimized(g); - -var sum = 0; -for (var i = 0; i < 10000; i++) sum += f(i) + g(i); -gc(); +%OptimizeFunctionOnNextCall(f, "parallel"); +%OptimizeFunctionOnNextCall(g, "parallel"); +f(g(2)); -assertEquals(95274, sum); assertUnoptimized(f); assertUnoptimized(g); -%InstallRecompiledCode(f); -assertOptimized(f); -assertUnoptimized(g); - -%InstallRecompiledCode(g); -assertOptimized(g); +%WaitUntilOptimized(f); +%WaitUntilOptimized(g); diff --git a/test/mjsunit/parallel-optimize-disabled.js b/test/mjsunit/parallel-optimize-disabled.js index 2487964..86b375c 100644 --- a/test/mjsunit/parallel-optimize-disabled.js +++ b/test/mjsunit/parallel-optimize-disabled.js @@ -39,8 +39,8 @@ function f(x) { f(); f(); -%OptimizeFunctionOnNextCall(g, "parallel"); %OptimizeFunctionOnNextCall(f); +%OptimizeFunctionOnNextCall(g, "parallel"); f(0); // g() is disabled for optimization on inlining attempt. -g(); // Attempt to optimize g() should not run into any assertion. - +// Attempt to optimize g() should not run into any assertion. +%WaitUntilOptimized(g); -- 2.7.4