1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "optimizing-compiler-thread.h" 29 30 #include "v8.h" 31 32 #include "hydrogen.h" 33 #include "isolate.h" 34 #include "v8threads.h" 35 36 namespace v8 { 37 namespace internal { 38 39 40 void OptimizingCompilerThread::Run() { 41 #ifdef DEBUG 42 { ScopedLock lock(thread_id_mutex_); 43 thread_id_ = ThreadId::Current().ToInteger(); 44 } 45 #endif 46 Isolate::SetIsolateThreadLocals(isolate_, NULL); 47 DisallowHeapAllocation no_allocation; 48 DisallowHandleAllocation no_handles; 49 DisallowHandleDereference no_deref; 50 51 int64_t epoch = 0; 52 if (FLAG_trace_parallel_recompilation) epoch = OS::Ticks(); 53 54 while (true) { 55 input_queue_semaphore_->Wait(); 56 Logger::TimerEventScope timer( 57 isolate_, Logger::TimerEventScope::v8_recompile_parallel); 58 59 if (FLAG_parallel_recompilation_delay != 0) { 60 OS::Sleep(FLAG_parallel_recompilation_delay); 61 } 62 63 switch (static_cast<StopFlag>(Acquire_Load(&stop_thread_))) { 64 case CONTINUE: 65 break; 66 case STOP: 67 if (FLAG_trace_parallel_recompilation) { 68 time_spent_total_ = OS::Ticks() - epoch; 69 } 70 stop_semaphore_->Signal(); 71 return; 72 case FLUSH: 73 // The main thread is blocked, waiting for the stop semaphore. 74 { AllowHandleDereference allow_handle_dereference; 75 FlushInputQueue(true); 76 } 77 Release_Store(&queue_length_, static_cast<AtomicWord>(0)); 78 Release_Store(&stop_thread_, static_cast<AtomicWord>(CONTINUE)); 79 stop_semaphore_->Signal(); 80 // Return to start of consumer loop. 81 continue; 82 } 83 84 int64_t compiling_start = 0; 85 if (FLAG_trace_parallel_recompilation) compiling_start = OS::Ticks(); 86 87 CompileNext(); 88 89 if (FLAG_trace_parallel_recompilation) { 90 time_spent_compiling_ += OS::Ticks() - compiling_start; 91 } 92 } 93 } 94 95 96 void OptimizingCompilerThread::CompileNext() { 97 OptimizingCompiler* optimizing_compiler = NULL; 98 bool result = input_queue_.Dequeue(&optimizing_compiler); 99 USE(result); 100 ASSERT(result); 101 Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(-1)); 102 103 // The function may have already been optimized by OSR. Simply continue. 104 OptimizingCompiler::Status status = optimizing_compiler->OptimizeGraph(); 105 USE(status); // Prevent an unused-variable error in release mode. 106 ASSERT(status != OptimizingCompiler::FAILED); 107 108 // The function may have already been optimized by OSR. Simply continue. 109 // Use a mutex to make sure that functions marked for install 110 // are always also queued. 111 ScopedLock mark_and_queue(install_mutex_); 112 { Heap::RelocationLock relocation_lock(isolate_->heap()); 113 AllowHandleDereference ahd; 114 optimizing_compiler->info()->closure()->MarkForInstallingRecompiledCode(); 115 } 116 output_queue_.Enqueue(optimizing_compiler); 117 } 118 119 120 void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) { 121 OptimizingCompiler* optimizing_compiler; 122 // The optimizing compiler is allocated in the CompilationInfo's zone. 123 while (input_queue_.Dequeue(&optimizing_compiler)) { 124 // This should not block, since we have one signal on the input queue 125 // semaphore corresponding to each element in the input queue. 126 input_queue_semaphore_->Wait(); 127 CompilationInfo* info = optimizing_compiler->info(); 128 if (restore_function_code) { 129 Handle<JSFunction> function = info->closure(); 130 function->ReplaceCode(function->shared()->code()); 131 } 132 delete info; 133 } 134 } 135 136 137 void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) { 138 OptimizingCompiler* optimizing_compiler; 139 // The optimizing compiler is allocated in the CompilationInfo's zone. 140 while (output_queue_.Dequeue(&optimizing_compiler)) { 141 CompilationInfo* info = optimizing_compiler->info(); 142 if (restore_function_code) { 143 Handle<JSFunction> function = info->closure(); 144 function->ReplaceCode(function->shared()->code()); 145 } 146 delete info; 147 } 148 } 149 150 151 void OptimizingCompilerThread::Flush() { 152 ASSERT(!IsOptimizerThread()); 153 Release_Store(&stop_thread_, static_cast<AtomicWord>(FLUSH)); 154 input_queue_semaphore_->Signal(); 155 stop_semaphore_->Wait(); 156 FlushOutputQueue(true); 157 } 158 159 160 void OptimizingCompilerThread::Stop() { 161 ASSERT(!IsOptimizerThread()); 162 Release_Store(&stop_thread_, static_cast<AtomicWord>(STOP)); 163 input_queue_semaphore_->Signal(); 164 stop_semaphore_->Wait(); 165 166 if (FLAG_parallel_recompilation_delay != 0) { 167 // Barrier when loading queue length is not necessary since the write 168 // happens in CompileNext on the same thread. 169 // This is used only for testing. 170 while (NoBarrier_Load(&queue_length_) > 0) CompileNext(); 171 InstallOptimizedFunctions(); 172 } else { 173 FlushInputQueue(false); 174 FlushOutputQueue(false); 175 } 176 177 if (FLAG_trace_parallel_recompilation) { 178 double compile_time = static_cast<double>(time_spent_compiling_); 179 double total_time = static_cast<double>(time_spent_total_); 180 double percentage = (compile_time * 100) / total_time; 181 PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage); 182 } 183 184 Join(); 185 } 186 187 188 void OptimizingCompilerThread::InstallOptimizedFunctions() { 189 ASSERT(!IsOptimizerThread()); 190 HandleScope handle_scope(isolate_); 191 OptimizingCompiler* compiler; 192 while (true) { 193 { // Memory barrier to ensure marked functions are queued. 194 ScopedLock marked_and_queued(install_mutex_); 195 if (!output_queue_.Dequeue(&compiler)) return; 196 } 197 Compiler::InstallOptimizedCode(compiler); 198 } 199 } 200 201 202 void OptimizingCompilerThread::QueueForOptimization( 203 OptimizingCompiler* optimizing_compiler) { 204 ASSERT(IsQueueAvailable()); 205 ASSERT(!IsOptimizerThread()); 206 Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(1)); 207 optimizing_compiler->info()->closure()->MarkInRecompileQueue(); 208 input_queue_.Enqueue(optimizing_compiler); 209 input_queue_semaphore_->Signal(); 210 } 211 212 213 #ifdef DEBUG 214 bool OptimizingCompilerThread::IsOptimizerThread() { 215 if (!FLAG_parallel_recompilation) return false; 216 ScopedLock lock(thread_id_mutex_); 217 return ThreadId::Current().ToInteger() == thread_id_; 218 } 219 #endif 220 221 222 } } // namespace v8::internal 223