Skip to content

Commit

Permalink
Merge pull request #1 from sandstorm-io/sandstorm
Browse files Browse the repository at this point in the history
deps: V8: backport 596d55a from upstream
  • Loading branch information
aboire authored Apr 16, 2018
2 parents 0b88256 + 0763074 commit abb7b47
Show file tree
Hide file tree
Showing 2 changed files with 295 additions and 20 deletions.
77 changes: 57 additions & 20 deletions deps/v8/src/deoptimizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,50 @@ void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
generator.Generate();
}

namespace {
class ActivationsFinder : public ThreadVisitor {
public:
explicit ActivationsFinder(std::set<Code*>* codes,
Code* topmost_optimized_code,
bool safe_to_deopt_topmost_optimized_code)
: codes_(codes) {
#ifdef DEBUG
topmost_ = topmost_optimized_code;
safe_to_deopt_ = safe_to_deopt_topmost_optimized_code;
#endif
}

// Find the frames with activations of codes marked for deoptimization, search
// for the trampoline to the deoptimizer call respective to each code, and use
// it to replace the current pc on the stack.
void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
if (it.frame()->type() == StackFrame::OPTIMIZED) {
Code* code = it.frame()->LookupCode();
if (code->kind() == Code::OPTIMIZED_FUNCTION &&
code->marked_for_deoptimization()) {
codes_->erase(code);
// Obtain the trampoline to the deoptimizer call.
SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc());
int trampoline_pc = safepoint.trampoline_pc();
DCHECK_IMPLIES(code == topmost_, safe_to_deopt_);
// Replace the current pc on the stack with the trampoline.
it.frame()->set_pc(code->instruction_start() + trampoline_pc);
}
}
}
}

private:
std::set<Code*>* codes_;

#ifdef DEBUG
Code* topmost_;
bool safe_to_deopt_;
#endif
};
} // namespace

void Deoptimizer::VisitAllOptimizedFunctionsForContext(
Context* context, OptimizedFunctionVisitor* visitor) {
DisallowHeapAllocation no_allocation;
Expand Down Expand Up @@ -264,9 +308,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
VisitAllOptimizedFunctionsForContext(context, &unlinker);

Isolate* isolate = context->GetHeap()->isolate();
#ifdef DEBUG
Code* topmost_optimized_code = NULL;
bool safe_to_deopt_topmost_optimized_code = false;
#ifdef DEBUG
// Make sure all activations of optimized code can deopt at their current PC.
// The topmost optimized code has special handling because it cannot be
// deoptimized due to weak object dependency.
Expand Down Expand Up @@ -304,6 +348,10 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
}
#endif

// We will use this set to mark those Code objects that are marked for
// deoptimization and have not been found in stack frames.
std::set<Code*> codes;

// Move marked code from the optimized code list to the deoptimized
// code list.
// Walk over all optimized code objects in this native context.
Expand Down Expand Up @@ -335,25 +383,14 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
element = next;
}

// Finds the with activations of codes marked for deoptimization, search for
// the trampoline to the deoptimizer call respective to each code, and use it
// to replace the current pc on the stack.
for (StackFrameIterator it(isolate, isolate->thread_local_top()); !it.done();
it.Advance()) {
if (it.frame()->type() == StackFrame::OPTIMIZED) {
Code* code = it.frame()->LookupCode();
if (code->kind() == Code::OPTIMIZED_FUNCTION &&
code->marked_for_deoptimization()) {
// Obtain the trampoline to the deoptimizer call.
SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc());
int trampoline_pc = safepoint.trampoline_pc();
DCHECK_IMPLIES(code == topmost_optimized_code,
safe_to_deopt_topmost_optimized_code);
// Replace the current pc on the stack with the trampoline.
it.frame()->set_pc(code->instruction_start() + trampoline_pc);
}
}
}
ActivationsFinder visitor(&codes, topmost_optimized_code,
safe_to_deopt_topmost_optimized_code);
// Iterate over the stack of this thread.
visitor.VisitThread(isolate, isolate->thread_local_top());
// In addition to iterate over the stack of this thread, we also
// need to consider all the other threads as they may also use
// the code currently beings deoptimized.
isolate->thread_manager()->IterateArchivedThreads(&visitor);
}


Expand Down
238 changes: 238 additions & 0 deletions deps/v8/test/cctest/test-lockers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,244 @@ using ::v8::Value;
using ::v8::V8;


namespace {

class DeoptimizeCodeThread : public v8::base::Thread {
public:
DeoptimizeCodeThread(v8::Isolate* isolate, v8::Local<v8::Context> context,
const char* trigger)
: Thread(Options("DeoptimizeCodeThread")),
isolate_(isolate),
context_(isolate, context),
source_(trigger) {}

void Run() {
v8::Locker locker(isolate_);
isolate_->Enter();
v8::HandleScope handle_scope(isolate_);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate_, context_);
v8::Context::Scope context_scope(context);
CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
// This code triggers deoptimization of some function that will be
// used in a different thread.
CompileRun(source_);
isolate_->Exit();
}

private:
v8::Isolate* isolate_;
Persistent<v8::Context> context_;
// The code that triggers the deoptimization.
const char* source_;
};

void UnlockForDeoptimization(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
// Gets the pointer to the thread that will trigger the deoptimization of the
// code.
DeoptimizeCodeThread* deoptimizer =
reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
{
// Exits and unlocks the isolate.
isolate->Exit();
v8::Unlocker unlocker(isolate);
// Starts the deoptimizing thread.
deoptimizer->Start();
// Waits for deoptimization to finish.
deoptimizer->Join();
}
// The deoptimizing thread has finished its work, and the isolate
// will now be used by the current thread.
isolate->Enter();
}

void UnlockForDeoptimizationIfReady(
const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
bool* ready_to_deoptimize = reinterpret_cast<bool*>(isolate->GetData(1));
if (*ready_to_deoptimize) {
// The test should enter here only once, so put the flag back to false.
*ready_to_deoptimize = false;
// Gets the pointer to the thread that will trigger the deoptimization of
// the code.
DeoptimizeCodeThread* deoptimizer =
reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
{
// Exits and unlocks the thread.
isolate->Exit();
v8::Unlocker unlocker(isolate);
// Starts the thread that deoptimizes the function.
deoptimizer->Start();
// Waits for the deoptimizing thread to finish.
deoptimizer->Join();
}
// The deoptimizing thread has finished its work, and the isolate
// will now be used by the current thread.
isolate->Enter();
}
}
} // namespace

TEST(LazyDeoptimizationMultithread) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
const char* trigger_deopt = "obj = { y: 0, x: 1 };";

// We use the isolate to pass arguments to the UnlockForDeoptimization
// function. Namely, we pass a pointer to the deoptimizing thread.
DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
isolate->SetData(0, &deoptimize_thread);
v8::Context::Scope context_scope(context);

// Create the function templace for C++ code that is invoked from
// JavaScript code.
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, UnlockForDeoptimization);
Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
CHECK(context->Global()
->Set(context, v8_str("unlock_for_deoptimization"), fun)
.FromJust());

// Optimizes a function f, which will be deoptimized in another
// thread.
CompileRun(
"var b = false; var obj = { x: 1 };"
"function f() { g(); return obj.x; }"
"function g() { if (b) { unlock_for_deoptimization(); } }"
"%NeverOptimizeFunction(g);"
"f(); f(); %OptimizeFunctionOnNextCall(f);"
"f();");

// Trigger the unlocking.
Local<Value> v = CompileRun("b = true; f();");

// Once the isolate has been unlocked, the thread will wait for the
// other thread to finish its task. Once this happens, this thread
// continues with its execution, that is, with the execution of the
// function g, which then returns to f. The function f should have
// also been deoptimized. If the replacement did not happen on this
// thread's stack, then the test will fail here.
CHECK(v->IsNumber());
CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
}
isolate->Dispose();
}

TEST(LazyDeoptimizationMultithreadWithNatives) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
const char* trigger_deopt = "%DeoptimizeFunction(f);";

// We use the isolate to pass arguments to the UnlockForDeoptimization
// function. Namely, we pass a pointer to the deoptimizing thread.
DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
isolate->SetData(0, &deoptimize_thread);
bool ready_to_deopt = false;
isolate->SetData(1, &ready_to_deopt);
v8::Context::Scope context_scope(context);

// Create the function templace for C++ code that is invoked from
// JavaScript code.
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
CHECK(context->Global()
->Set(context, v8_str("unlock_for_deoptimization"), fun)
.FromJust());

// Optimizes a function f, which will be deoptimized in another
// thread.
CompileRun(
"var obj = { x: 1 };"
"function f() { g(); return obj.x;}"
"function g() { "
" unlock_for_deoptimization(); }"
"%NeverOptimizeFunction(g);"
"f(); f(); %OptimizeFunctionOnNextCall(f);");

// Trigger the unlocking.
ready_to_deopt = true;
isolate->SetData(1, &ready_to_deopt);
Local<Value> v = CompileRun("f();");

// Once the isolate has been unlocked, the thread will wait for the
// other thread to finish its task. Once this happens, this thread
// continues with its execution, that is, with the execution of the
// function g, which then returns to f. The function f should have
// also been deoptimized. Otherwise, the test will fail here.
CHECK(v->IsNumber());
CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
}
isolate->Dispose();
}

TEST(EagerDeoptimizationMultithread) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
const char* trigger_deopt = "f({y: 0, x: 1});";

// We use the isolate to pass arguments to the UnlockForDeoptimization
// function. Namely, we pass a pointer to the deoptimizing thread.
DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
isolate->SetData(0, &deoptimize_thread);
bool ready_to_deopt = false;
isolate->SetData(1, &ready_to_deopt);
v8::Context::Scope context_scope(context);

// Create the function templace for C++ code that is invoked from
// JavaScript code.
Local<v8::FunctionTemplate> fun_templ =
v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
CHECK(context->Global()
->Set(context, v8_str("unlock_for_deoptimization"), fun)
.FromJust());

// Optimizes a function f, which will be deoptimized by another thread.
CompileRun(
"function f(obj) { unlock_for_deoptimization(); return obj.x; }"
"f({x: 1}); f({x: 1});"
"%OptimizeFunctionOnNextCall(f);"
"f({x: 1});");

// Trigger the unlocking.
ready_to_deopt = true;
isolate->SetData(1, &ready_to_deopt);
Local<Value> v = CompileRun("f({x: 1});");

// Once the isolate has been unlocked, the thread will wait for the
// other thread to finish its task. Once this happens, this thread
// continues with its execution, that is, with the execution of the
// function g, which then returns to f. The function f should have
// also been deoptimized. Otherwise, the test will fail here.
CHECK(v->IsNumber());
CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
}
isolate->Dispose();
}

// Migrating an isolate
class KangarooThread : public v8::base::Thread {
public:
Expand Down

0 comments on commit abb7b47

Please sign in to comment.