GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — deps: V8: backport 596d55a from upstream · nodejs/node@d02b72e

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • deps/v8
    • src
      • deoptimizer.cc
    • test/cctest
      • test-lockers.cc
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎deps/v8/src/deoptimizer.cc‎

Copy file name to clipboardExpand all lines: deps/v8/src/deoptimizer.cc
+57-20Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,50 @@ void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
144144
generator.Generate();
145145
}
146146

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+
147191
void Deoptimizer::VisitAllOptimizedFunctionsForContext(
148192
Context* context, OptimizedFunctionVisitor* visitor) {
149193
DisallowHeapAllocation no_allocation;
@@ -264,9 +308,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
264308
VisitAllOptimizedFunctionsForContext(context, &unlinker);
265309

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

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+
307355
// Move marked code from the optimized code list to the deoptimized
308356
// code list.
309357
// Walk over all optimized code objects in this native context.
@@ -335,25 +383,14 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
335383
element = next;
336384
}
337385

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);
357394
}
358395

359396

Collapse file

‎deps/v8/test/cctest/test-lockers.cc‎

Copy file name to clipboardExpand all lines: deps/v8/test/cctest/test-lockers.cc
+238Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,244 @@ using ::v8::Value;
5555
using ::v8::V8;
5656

5757

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+
58296
// Migrating an isolate
59297
class KangarooThread : public v8::base::Thread {
60298
public:

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.