| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a4c28d7 commit 36f664e
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -144,6 +144,50 @@ void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm, | |||
| 144 | 144 | generator.Generate(); | |
| 145 | 145 | } | |
| 146 | 146 | ||
| 147 | + namespace { | ||
| 148 | + class ActivationsFinder : public ThreadVisitor { | ||
| 149 | + public: | ||
| 150 | + explicit ActivationsFinder(std::set<Code*>* codes, | ||
| 151 | + Code* topmost_optimized_code, | ||
| 152 | + bool safe_to_deopt_topmost_optimized_code) | ||
| 153 | + : codes_(codes) { | ||
| 154 | + #ifdef DEBUG | ||
| 155 | + topmost_ = topmost_optimized_code; | ||
| 156 | + safe_to_deopt_ = safe_to_deopt_topmost_optimized_code; | ||
| 157 | + #endif | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + // Find the frames with activations of codes marked for deoptimization, search | ||
| 161 | + // for the trampoline to the deoptimizer call respective to each code, and use | ||
| 162 | + // it to replace the current pc on the stack. | ||
| 163 | + void VisitThread(Isolate* isolate, ThreadLocalTop* top) { | ||
| 164 | + for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) { | ||
| 165 | + if (it.frame()->type() == StackFrame::OPTIMIZED) { | ||
| 166 | + Code* code = it.frame()->LookupCode(); | ||
| 167 | + if (code->kind() == Code::OPTIMIZED_FUNCTION && | ||
| 168 | + code->marked_for_deoptimization()) { | ||
| 169 | + codes_->erase(code); | ||
| 170 | + // Obtain the trampoline to the deoptimizer call. | ||
| 171 | + SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc()); | ||
| 172 | + int trampoline_pc = safepoint.trampoline_pc(); | ||
| 173 | + DCHECK_IMPLIES(code == topmost_, safe_to_deopt_); | ||
| 174 | + // Replace the current pc on the stack with the trampoline. | ||
| 175 | + it.frame()->set_pc(code->instruction_start() + trampoline_pc); | ||
| 176 | + } | ||
| 177 | + } | ||
| 178 | + } | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + private: | ||
| 182 | + std::set<Code*>* codes_; | ||
| 183 | + | ||
| 184 | + #ifdef DEBUG | ||
| 185 | + Code* topmost_; | ||
| 186 | + bool safe_to_deopt_; | ||
| 187 | + #endif | ||
| 188 | + }; | ||
| 189 | + } // namespace | ||
| 190 | + | ||
| 147 | 191 | void Deoptimizer::VisitAllOptimizedFunctionsForContext( | |
| 148 | 192 | Context* context, OptimizedFunctionVisitor* visitor) { | |
| 149 | 193 | DisallowHeapAllocation no_allocation; | |
@@ -264,9 +308,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) { | |||
| 264 | 308 | VisitAllOptimizedFunctionsForContext(context, &unlinker); | |
| 265 | 309 | ||
| 266 | 310 | Isolate* isolate = context->GetHeap()->isolate(); | |
| 267 | - #ifdef DEBUG | ||
| 268 | 311 | Code* topmost_optimized_code = NULL; | |
| 269 | 312 | bool safe_to_deopt_topmost_optimized_code = false; | |
| 313 | + #ifdef DEBUG | ||
| 270 | 314 | // Make sure all activations of optimized code can deopt at their current PC. | |
| 271 | 315 | // The topmost optimized code has special handling because it cannot be | |
| 272 | 316 | // deoptimized due to weak object dependency. | |
@@ -304,6 +348,10 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) { | |||
| 304 | 348 | } | |
| 305 | 349 | #endif | |
| 306 | 350 | ||
| 351 | + // We will use this set to mark those Code objects that are marked for | ||
| 352 | + // deoptimization and have not been found in stack frames. | ||
| 353 | + std::set<Code*> codes; | ||
| 354 | + | ||
| 307 | 355 | // Move marked code from the optimized code list to the deoptimized | |
| 308 | 356 | // code list. | |
| 309 | 357 | // Walk over all optimized code objects in this native context. | |
@@ -335,25 +383,14 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) { | |||
| 335 | 383 | element = next; | |
| 336 | 384 | } | |
| 337 | 385 | ||
| 338 | - // Finds the with activations of codes marked for deoptimization, search for | ||
| 339 | - // the trampoline to the deoptimizer call respective to each code, and use it | ||
| 340 | - // to replace the current pc on the stack. | ||
| 341 | - for (StackFrameIterator it(isolate, isolate->thread_local_top()); !it.done(); | ||
| 342 | - it.Advance()) { | ||
| 343 | - if (it.frame()->type() == StackFrame::OPTIMIZED) { | ||
| 344 | - Code* code = it.frame()->LookupCode(); | ||
| 345 | - if (code->kind() == Code::OPTIMIZED_FUNCTION && | ||
| 346 | - code->marked_for_deoptimization()) { | ||
| 347 | - // Obtain the trampoline to the deoptimizer call. | ||
| 348 | - SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc()); | ||
| 349 | - int trampoline_pc = safepoint.trampoline_pc(); | ||
| 350 | - DCHECK_IMPLIES(code == topmost_optimized_code, | ||
| 351 | - safe_to_deopt_topmost_optimized_code); | ||
| 352 | - // Replace the current pc on the stack with the trampoline. | ||
| 353 | - it.frame()->set_pc(code->instruction_start() + trampoline_pc); | ||
| 354 | - } | ||
| 355 | - } | ||
| 356 | - } | ||
| 386 | + ActivationsFinder visitor(&codes, topmost_optimized_code, | ||
| 387 | + safe_to_deopt_topmost_optimized_code); | ||
| 388 | + // Iterate over the stack of this thread. | ||
| 389 | + visitor.VisitThread(isolate, isolate->thread_local_top()); | ||
| 390 | + // In addition to iterate over the stack of this thread, we also | ||
| 391 | + // need to consider all the other threads as they may also use | ||
| 392 | + // the code currently beings deoptimized. | ||
| 393 | + isolate->thread_manager()->IterateArchivedThreads(&visitor); | ||
| 357 | 394 | } | |
| 358 | 395 | ||
| 359 | 396 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -55,6 +55,244 @@ using ::v8::Value; | |||
| 55 | 55 | using ::v8::V8; | |
| 56 | 56 | ||
| 57 | 57 | ||
| 58 | + namespace { | ||
| 59 | + | ||
| 60 | + class DeoptimizeCodeThread : public v8::base::Thread { | ||
| 61 | + public: | ||
| 62 | + DeoptimizeCodeThread(v8::Isolate* isolate, v8::Local<v8::Context> context, | ||
| 63 | + const char* trigger) | ||
| 64 | + : Thread(Options("DeoptimizeCodeThread")), | ||
| 65 | + isolate_(isolate), | ||
| 66 | + context_(isolate, context), | ||
| 67 | + source_(trigger) {} | ||
| 68 | + | ||
| 69 | + void Run() { | ||
| 70 | + v8::Locker locker(isolate_); | ||
| 71 | + isolate_->Enter(); | ||
| 72 | + v8::HandleScope handle_scope(isolate_); | ||
| 73 | + v8::Local<v8::Context> context = | ||
| 74 | + v8::Local<v8::Context>::New(isolate_, context_); | ||
| 75 | + v8::Context::Scope context_scope(context); | ||
| 76 | + CHECK_EQ(isolate_, v8::Isolate::GetCurrent()); | ||
| 77 | + // This code triggers deoptimization of some function that will be | ||
| 78 | + // used in a different thread. | ||
| 79 | + CompileRun(source_); | ||
| 80 | + isolate_->Exit(); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + private: | ||
| 84 | + v8::Isolate* isolate_; | ||
| 85 | + Persistent<v8::Context> context_; | ||
| 86 | + // The code that triggers the deoptimization. | ||
| 87 | + const char* source_; | ||
| 88 | + }; | ||
| 89 | + | ||
| 90 | + void UnlockForDeoptimization(const v8::FunctionCallbackInfo<v8::Value>& args) { | ||
| 91 | + v8::Isolate* isolate = v8::Isolate::GetCurrent(); | ||
| 92 | + // Gets the pointer to the thread that will trigger the deoptimization of the | ||
| 93 | + // code. | ||
| 94 | + DeoptimizeCodeThread* deoptimizer = | ||
| 95 | + reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0)); | ||
| 96 | + { | ||
| 97 | + // Exits and unlocks the isolate. | ||
| 98 | + isolate->Exit(); | ||
| 99 | + v8::Unlocker unlocker(isolate); | ||
| 100 | + // Starts the deoptimizing thread. | ||
| 101 | + deoptimizer->Start(); | ||
| 102 | + // Waits for deoptimization to finish. | ||
| 103 | + deoptimizer->Join(); | ||
| 104 | + } | ||
| 105 | + // The deoptimizing thread has finished its work, and the isolate | ||
| 106 | + // will now be used by the current thread. | ||
| 107 | + isolate->Enter(); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + void UnlockForDeoptimizationIfReady( | ||
| 111 | + const v8::FunctionCallbackInfo<v8::Value>& args) { | ||
| 112 | + v8::Isolate* isolate = v8::Isolate::GetCurrent(); | ||
| 113 | + bool* ready_to_deoptimize = reinterpret_cast<bool*>(isolate->GetData(1)); | ||
| 114 | + if (*ready_to_deoptimize) { | ||
| 115 | + // The test should enter here only once, so put the flag back to false. | ||
| 116 | + *ready_to_deoptimize = false; | ||
| 117 | + // Gets the pointer to the thread that will trigger the deoptimization of | ||
| 118 | + // the code. | ||
| 119 | + DeoptimizeCodeThread* deoptimizer = | ||
| 120 | + reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0)); | ||
| 121 | + { | ||
| 122 | + // Exits and unlocks the thread. | ||
| 123 | + isolate->Exit(); | ||
| 124 | + v8::Unlocker unlocker(isolate); | ||
| 125 | + // Starts the thread that deoptimizes the function. | ||
| 126 | + deoptimizer->Start(); | ||
| 127 | + // Waits for the deoptimizing thread to finish. | ||
| 128 | + deoptimizer->Join(); | ||
| 129 | + } | ||
| 130 | + // The deoptimizing thread has finished its work, and the isolate | ||
| 131 | + // will now be used by the current thread. | ||
| 132 | + isolate->Enter(); | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + } // namespace | ||
| 136 | + | ||
| 137 | + TEST(LazyDeoptimizationMultithread) { | ||
| 138 | + i::FLAG_allow_natives_syntax = true; | ||
| 139 | + v8::Isolate::CreateParams create_params; | ||
| 140 | + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); | ||
| 141 | + v8::Isolate* isolate = v8::Isolate::New(create_params); | ||
| 142 | + { | ||
| 143 | + v8::Locker locker(isolate); | ||
| 144 | + v8::Isolate::Scope isolate_scope(isolate); | ||
| 145 | + v8::HandleScope scope(isolate); | ||
| 146 | + v8::Local<v8::Context> context = v8::Context::New(isolate); | ||
| 147 | + const char* trigger_deopt = "obj = { y: 0, x: 1 };"; | ||
| 148 | + | ||
| 149 | + // We use the isolate to pass arguments to the UnlockForDeoptimization | ||
| 150 | + // function. Namely, we pass a pointer to the deoptimizing thread. | ||
| 151 | + DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt); | ||
| 152 | + isolate->SetData(0, &deoptimize_thread); | ||
| 153 | + v8::Context::Scope context_scope(context); | ||
| 154 | + | ||
| 155 | + // Create the function templace for C++ code that is invoked from | ||
| 156 | + // JavaScript code. | ||
| 157 | + Local<v8::FunctionTemplate> fun_templ = | ||
| 158 | + v8::FunctionTemplate::New(isolate, UnlockForDeoptimization); | ||
| 159 | + Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked(); | ||
| 160 | + CHECK(context->Global() | ||
| 161 | + ->Set(context, v8_str("unlock_for_deoptimization"), fun) | ||
| 162 | + .FromJust()); | ||
| 163 | + | ||
| 164 | + // Optimizes a function f, which will be deoptimized in another | ||
| 165 | + // thread. | ||
| 166 | + CompileRun( | ||
| 167 | + "var b = false; var obj = { x: 1 };" | ||
| 168 | + "function f() { g(); return obj.x; }" | ||
| 169 | + "function g() { if (b) { unlock_for_deoptimization(); } }" | ||
| 170 | + "%NeverOptimizeFunction(g);" | ||
| 171 | + "f(); f(); %OptimizeFunctionOnNextCall(f);" | ||
| 172 | + "f();"); | ||
| 173 | + | ||
| 174 | + // Trigger the unlocking. | ||
| 175 | + Local<Value> v = CompileRun("b = true; f();"); | ||
| 176 | + | ||
| 177 | + // Once the isolate has been unlocked, the thread will wait for the | ||
| 178 | + // other thread to finish its task. Once this happens, this thread | ||
| 179 | + // continues with its execution, that is, with the execution of the | ||
| 180 | + // function g, which then returns to f. The function f should have | ||
| 181 | + // also been deoptimized. If the replacement did not happen on this | ||
| 182 | + // thread's stack, then the test will fail here. | ||
| 183 | + CHECK(v->IsNumber()); | ||
| 184 | + CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust())); | ||
| 185 | + } | ||
| 186 | + isolate->Dispose(); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + TEST(LazyDeoptimizationMultithreadWithNatives) { | ||
| 190 | + i::FLAG_allow_natives_syntax = true; | ||
| 191 | + v8::Isolate::CreateParams create_params; | ||
| 192 | + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); | ||
| 193 | + v8::Isolate* isolate = v8::Isolate::New(create_params); | ||
| 194 | + { | ||
| 195 | + v8::Locker locker(isolate); | ||
| 196 | + v8::Isolate::Scope isolate_scope(isolate); | ||
| 197 | + v8::HandleScope scope(isolate); | ||
| 198 | + v8::Local<v8::Context> context = v8::Context::New(isolate); | ||
| 199 | + const char* trigger_deopt = "%DeoptimizeFunction(f);"; | ||
| 200 | + | ||
| 201 | + // We use the isolate to pass arguments to the UnlockForDeoptimization | ||
| 202 | + // function. Namely, we pass a pointer to the deoptimizing thread. | ||
| 203 | + DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt); | ||
| 204 | + isolate->SetData(0, &deoptimize_thread); | ||
| 205 | + bool ready_to_deopt = false; | ||
| 206 | + isolate->SetData(1, &ready_to_deopt); | ||
| 207 | + v8::Context::Scope context_scope(context); | ||
| 208 | + | ||
| 209 | + // Create the function templace for C++ code that is invoked from | ||
| 210 | + // JavaScript code. | ||
| 211 | + Local<v8::FunctionTemplate> fun_templ = | ||
| 212 | + v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady); | ||
| 213 | + Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked(); | ||
| 214 | + CHECK(context->Global() | ||
| 215 | + ->Set(context, v8_str("unlock_for_deoptimization"), fun) | ||
| 216 | + .FromJust()); | ||
| 217 | + | ||
| 218 | + // Optimizes a function f, which will be deoptimized in another | ||
| 219 | + // thread. | ||
| 220 | + CompileRun( | ||
| 221 | + "var obj = { x: 1 };" | ||
| 222 | + "function f() { g(); return obj.x;}" | ||
| 223 | + "function g() { " | ||
| 224 | + " unlock_for_deoptimization(); }" | ||
| 225 | + "%NeverOptimizeFunction(g);" | ||
| 226 | + "f(); f(); %OptimizeFunctionOnNextCall(f);"); | ||
| 227 | + | ||
| 228 | + // Trigger the unlocking. | ||
| 229 | + ready_to_deopt = true; | ||
| 230 | + isolate->SetData(1, &ready_to_deopt); | ||
| 231 | + Local<Value> v = CompileRun("f();"); | ||
| 232 | + | ||
| 233 | + // Once the isolate has been unlocked, the thread will wait for the | ||
| 234 | + // other thread to finish its task. Once this happens, this thread | ||
| 235 | + // continues with its execution, that is, with the execution of the | ||
| 236 | + // function g, which then returns to f. The function f should have | ||
| 237 | + // also been deoptimized. Otherwise, the test will fail here. | ||
| 238 | + CHECK(v->IsNumber()); | ||
| 239 | + CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust())); | ||
| 240 | + } | ||
| 241 | + isolate->Dispose(); | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + TEST(EagerDeoptimizationMultithread) { | ||
| 245 | + i::FLAG_allow_natives_syntax = true; | ||
| 246 | + v8::Isolate::CreateParams create_params; | ||
| 247 | + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); | ||
| 248 | + v8::Isolate* isolate = v8::Isolate::New(create_params); | ||
| 249 | + { | ||
| 250 | + v8::Locker locker(isolate); | ||
| 251 | + v8::Isolate::Scope isolate_scope(isolate); | ||
| 252 | + v8::HandleScope scope(isolate); | ||
| 253 | + v8::Local<v8::Context> context = v8::Context::New(isolate); | ||
| 254 | + const char* trigger_deopt = "f({y: 0, x: 1});"; | ||
| 255 | + | ||
| 256 | + // We use the isolate to pass arguments to the UnlockForDeoptimization | ||
| 257 | + // function. Namely, we pass a pointer to the deoptimizing thread. | ||
| 258 | + DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt); | ||
| 259 | + isolate->SetData(0, &deoptimize_thread); | ||
| 260 | + bool ready_to_deopt = false; | ||
| 261 | + isolate->SetData(1, &ready_to_deopt); | ||
| 262 | + v8::Context::Scope context_scope(context); | ||
| 263 | + | ||
| 264 | + // Create the function templace for C++ code that is invoked from | ||
| 265 | + // JavaScript code. | ||
| 266 | + Local<v8::FunctionTemplate> fun_templ = | ||
| 267 | + v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady); | ||
| 268 | + Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked(); | ||
| 269 | + CHECK(context->Global() | ||
| 270 | + ->Set(context, v8_str("unlock_for_deoptimization"), fun) | ||
| 271 | + .FromJust()); | ||
| 272 | + | ||
| 273 | + // Optimizes a function f, which will be deoptimized by another thread. | ||
| 274 | + CompileRun( | ||
| 275 | + "function f(obj) { unlock_for_deoptimization(); return obj.x; }" | ||
| 276 | + "f({x: 1}); f({x: 1});" | ||
| 277 | + "%OptimizeFunctionOnNextCall(f);" | ||
| 278 | + "f({x: 1});"); | ||
| 279 | + | ||
| 280 | + // Trigger the unlocking. | ||
| 281 | + ready_to_deopt = true; | ||
| 282 | + isolate->SetData(1, &ready_to_deopt); | ||
| 283 | + Local<Value> v = CompileRun("f({x: 1});"); | ||
| 284 | + | ||
| 285 | + // Once the isolate has been unlocked, the thread will wait for the | ||
| 286 | + // other thread to finish its task. Once this happens, this thread | ||
| 287 | + // continues with its execution, that is, with the execution of the | ||
| 288 | + // function g, which then returns to f. The function f should have | ||
| 289 | + // also been deoptimized. Otherwise, the test will fail here. | ||
| 290 | + CHECK(v->IsNumber()); | ||
| 291 | + CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust())); | ||
| 292 | + } | ||
| 293 | + isolate->Dispose(); | ||
| 294 | + } | ||
| 295 | + | ||
| 58 | 296 | // Migrating an isolate | |
| 59 | 297 | class KangarooThread : public v8::base::Thread { | |
| 60 | 298 | public: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments