Commit 849baecc authored by tzik's avatar tzik Committed by Commit Bot

Merge HandleScopeImplementer::microtask_context_ into entered_contexts_

This CL merges MicrotaskContext management into EnteredContext, so that
MicrotaskContext can nest.

Here is a brief explanation:
https://docs.google.com/document/d/1MY_xlsYS7E6_qbwwY66-FH3JkAYeTHBlF5qVBrBpWyY/edit

Benchmark result:
No significant performance difference is observed for this CL.
See "patched" row for the result of this CL. Maintaining |is_microtask_context_|
for the deprecated Isolate::GetEnteredContext() seems to cost 1~2% of the
score, but that will be resolved eventually.
https://github.com/v8/promise-performance-tests
https://docs.google.com/spreadsheets/d/1-SpO4nQNxcXQZAfHN5CmEyAyCBd33wZ_CdF4U78e44I/edit#gid=1701841321

Bug: v8:8124
Change-Id: Ic709bccba9c32d37578e15a7571014ce50129459
Reviewed-on: https://chromium-review.googlesource.com/c/1322290Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Taiju Tsuiki <tzik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58174}
parent 8d094249
......@@ -127,20 +127,20 @@ OPEN_HANDLE_LIST(MAKE_OPEN_HANDLE)
namespace internal {
Handle<Context> HandleScopeImplementer::MicrotaskContext() {
if (!microtask_context_.is_null()) {
return Handle<Context>(microtask_context_, isolate_);
Handle<Context> HandleScopeImplementer::LastEnteredContext() {
DCHECK_EQ(entered_contexts_.size(), is_microtask_context_.size());
for (size_t i = 0; i < entered_contexts_.size(); ++i) {
size_t j = entered_contexts_.size() - i - 1;
if (!is_microtask_context_.at(j)) {
return Handle<Context>(entered_contexts_.at(j), isolate_);
}
}
return Handle<Context>::null();
}
Handle<Context> HandleScopeImplementer::LastEnteredContext() {
if (entered_contexts_.empty()) return Handle<Context>::null();
return Handle<Context>(entered_contexts_.back(), isolate_);
return Handle<Context>::null();
}
Handle<Context> HandleScopeImplementer::LastEnteredOrMicrotaskContext() {
if (MicrotaskContextIsLastEnteredContext()) return MicrotaskContext();
if (entered_contexts_.empty()) return Handle<Context>::null();
return Handle<Context>(entered_contexts_.back(), isolate_);
}
......
......@@ -1155,7 +1155,7 @@ void Context::Enter() {
i::Isolate* isolate = env->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
i::HandleScopeImplementer* impl = isolate->handle_scope_implementer();
impl->EnterContext(env);
impl->EnterContext(*env);
impl->SaveContext(isolate->context());
isolate->set_context(*env);
}
......@@ -1165,8 +1165,7 @@ void Context::Exit() {
i::Isolate* isolate = env->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
i::HandleScopeImplementer* impl = isolate->handle_scope_implementer();
if (!Utils::ApiCheck(impl->LastEnteredContextWas(env),
"v8::Context::Exit()",
if (!Utils::ApiCheck(impl->LastEnteredContextWas(*env), "v8::Context::Exit()",
"Cannot exit non-entered context")) {
return;
}
......@@ -10561,6 +10560,11 @@ void EmbedderHeapTracer::GarbageCollectionForTesting(
namespace internal {
const size_t HandleScopeImplementer::kEnteredContextsOffset =
offsetof(HandleScopeImplementer, entered_contexts_);
const size_t HandleScopeImplementer::kIsMicrotaskContextOffset =
offsetof(HandleScopeImplementer, is_microtask_context_);
void HandleScopeImplementer::FreeThreadResources() {
Free();
}
......@@ -10634,10 +10638,6 @@ void HandleScopeImplementer::IterateThis(RootVisitor* v) {
v->VisitRootPointers(Root::kHandleScope, nullptr, start,
start + static_cast<int>(context_lists[i]->size()));
}
if (!microtask_context_.is_null()) {
v->VisitRootPointer(Root::kHandleScope, nullptr,
FullObjectSlot(&microtask_context_));
}
}
void HandleScopeImplementer::Iterate(RootVisitor* v) {
......
......@@ -331,14 +331,28 @@ class V8_EXPORT_PRIVATE DeferredHandles {
// data.
class HandleScopeImplementer {
public:
class EnteredContextRewindScope {
public:
explicit EnteredContextRewindScope(HandleScopeImplementer* hsi)
: hsi_(hsi), saved_entered_context_count_(hsi->EnteredContextCount()) {}
~EnteredContextRewindScope() {
DCHECK_LE(saved_entered_context_count_, hsi_->EnteredContextCount());
while (saved_entered_context_count_ < hsi_->EnteredContextCount())
hsi_->LeaveContext();
}
private:
HandleScopeImplementer* hsi_;
size_t saved_entered_context_count_;
};
explicit HandleScopeImplementer(Isolate* isolate)
: isolate_(isolate),
spare_(nullptr),
call_depth_(0),
microtasks_depth_(0),
microtasks_suppressions_(0),
entered_contexts_count_(0),
entered_context_count_during_microtasks_(0),
#ifdef DEBUG
debug_microtasks_depth_(0),
#endif
......@@ -393,23 +407,18 @@ class HandleScopeImplementer {
inline void set_microtasks_policy(v8::MicrotasksPolicy policy);
inline v8::MicrotasksPolicy microtasks_policy() const;
inline void EnterContext(Handle<Context> context);
inline void EnterContext(Context context);
inline void LeaveContext();
inline bool LastEnteredContextWas(Handle<Context> context);
inline bool LastEnteredContextWas(Context context);
inline size_t EnteredContextCount() const { return entered_contexts_.size(); }
inline void EnterMicrotaskContext(Context context);
// Returns the last entered context or an empty handle if no
// contexts have been entered.
inline Handle<Context> LastEnteredContext();
inline Handle<Context> LastEnteredOrMicrotaskContext();
inline void EnterMicrotaskContext(Handle<Context> context);
inline void LeaveMicrotaskContext();
inline Handle<Context> MicrotaskContext();
inline bool MicrotaskContextIsLastEnteredContext() const {
return !microtask_context_.is_null() &&
entered_context_count_during_microtasks_ == entered_contexts_.size();
}
inline void SaveContext(Context context);
inline Context RestoreContext();
inline bool HasSavedContexts();
......@@ -423,13 +432,15 @@ class HandleScopeImplementer {
spare_ = block;
}
static const size_t kEnteredContextsOffset;
static const size_t kIsMicrotaskContextOffset;
private:
void ResetAfterArchive() {
blocks_.detach();
entered_contexts_.detach();
is_microtask_context_.detach();
saved_contexts_.detach();
microtask_context_ = Context();
entered_context_count_during_microtasks_ = 0;
spare_ = nullptr;
last_handle_before_deferred_block_ = nullptr;
call_depth_ = 0;
......@@ -438,11 +449,12 @@ class HandleScopeImplementer {
void Free() {
DCHECK(blocks_.empty());
DCHECK(entered_contexts_.empty());
DCHECK(is_microtask_context_.empty());
DCHECK(saved_contexts_.empty());
DCHECK(microtask_context_.is_null());
blocks_.free();
entered_contexts_.free();
is_microtask_context_.free();
saved_contexts_.free();
if (spare_ != nullptr) {
DeleteArray(spare_);
......@@ -456,17 +468,21 @@ class HandleScopeImplementer {
Isolate* isolate_;
DetachableVector<Address*> blocks_;
// Used as a stack to keep track of entered contexts.
// If |i|th item of |entered_contexts_| is added by EnterMicrotaskContext,
// `is_microtask_context_[i]` is 1.
// TODO(tzik): Remove |is_microtask_context_| after the deprecated
// v8::Isolate::GetEnteredContext() is removed.
DetachableVector<Context> entered_contexts_;
DetachableVector<int8_t> is_microtask_context_;
// Used as a stack to keep track of saved contexts.
DetachableVector<Context> saved_contexts_;
Context microtask_context_;
Address* spare_;
int call_depth_;
int microtasks_depth_;
int microtasks_suppressions_;
size_t entered_contexts_count_;
size_t entered_context_count_during_microtasks_;
#ifdef DEBUG
int debug_microtasks_depth_;
#endif
......@@ -486,21 +502,6 @@ class HandleScopeImplementer {
DISALLOW_COPY_AND_ASSIGN(HandleScopeImplementer);
};
class HandleScopeImplementerOffsets {
public:
enum Offsets {
kMicrotaskContext = offsetof(HandleScopeImplementer, microtask_context_),
kEnteredContexts = offsetof(HandleScopeImplementer, entered_contexts_),
kEnteredContextsCount =
offsetof(HandleScopeImplementer, entered_contexts_count_),
kEnteredContextCountDuringMicrotasks = offsetof(
HandleScopeImplementer, entered_context_count_during_microtasks_)
};
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(HandleScopeImplementerOffsets);
};
const int kHandleBlockSize = v8::internal::KB - 2; // fit in one page
......@@ -529,30 +530,27 @@ bool HandleScopeImplementer::HasSavedContexts() {
return !saved_contexts_.empty();
}
void HandleScopeImplementer::EnterContext(Handle<Context> context) {
entered_contexts_.push_back(*context);
entered_contexts_count_ = entered_contexts_.size();
void HandleScopeImplementer::EnterContext(Context context) {
DCHECK_EQ(entered_contexts_.size(), is_microtask_context_.size());
entered_contexts_.push_back(context);
is_microtask_context_.push_back(0);
}
void HandleScopeImplementer::LeaveContext() {
DCHECK(!entered_contexts_.empty());
DCHECK_EQ(entered_contexts_.size(), is_microtask_context_.size());
entered_contexts_.pop_back();
entered_contexts_count_ = entered_contexts_.size();
}
bool HandleScopeImplementer::LastEnteredContextWas(Handle<Context> context) {
return !entered_contexts_.empty() && entered_contexts_.back() == *context;
is_microtask_context_.pop_back();
}
void HandleScopeImplementer::EnterMicrotaskContext(Handle<Context> context) {
DCHECK(microtask_context_.is_null());
microtask_context_ = *context;
entered_context_count_during_microtasks_ = entered_contexts_.size();
bool HandleScopeImplementer::LastEnteredContextWas(Context context) {
return !entered_contexts_.empty() && entered_contexts_.back() == context;
}
void HandleScopeImplementer::LeaveMicrotaskContext() {
microtask_context_ = Context();
entered_context_count_during_microtasks_ = 0;
void HandleScopeImplementer::EnterMicrotaskContext(Context context) {
DCHECK_EQ(entered_contexts_.size(), is_microtask_context_.size());
entered_contexts_.push_back(context);
is_microtask_context_.push_back(1);
}
// If there's a spare block, use it for growing the current scope.
......
......@@ -40,8 +40,9 @@ class MicrotaskQueueBuiltinsAssembler : public CodeStubAssembler {
TNode<Context> GetCurrentContext();
void SetCurrentContext(TNode<Context> context);
TNode<IntPtrT> GetEnteredContextCount();
void EnterMicrotaskContext(TNode<Context> native_context);
void LeaveMicrotaskContext();
void RewindEnteredContext(TNode<IntPtrT> saved_entered_context_count);
void RunPromiseHook(Runtime::FunctionId id, TNode<Context> context,
SloppyTNode<HeapObject> promise_or_capability);
......@@ -111,6 +112,7 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
CSA_ASSERT(this, TaggedIsNotSmi(microtask));
StoreRoot(RootIndex::kCurrentMicrotask, microtask);
TNode<IntPtrT> saved_entered_context_count = GetEnteredContextCount();
TNode<Map> microtask_map = LoadMap(microtask);
TNode<Int32T> microtask_type = LoadMapInstanceType(microtask_map);
......@@ -155,7 +157,7 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
microtask_context, callable, UndefinedConstant());
GotoIfException(result, &if_exception, &var_exception);
LeaveMicrotaskContext();
RewindEnteredContext(saved_entered_context_count);
SetCurrentContext(current_context);
Goto(&done);
}
......@@ -205,7 +207,7 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
CallBuiltin(Builtins::kPromiseResolveThenableJob, native_context,
promise_to_resolve, thenable, then);
GotoIfException(result, &if_exception, &var_exception);
LeaveMicrotaskContext();
RewindEnteredContext(saved_entered_context_count);
SetCurrentContext(current_context);
Goto(&done);
}
......@@ -240,7 +242,7 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
promise_or_capability);
LeaveMicrotaskContext();
RewindEnteredContext(saved_entered_context_count);
SetCurrentContext(current_context);
Goto(&done);
}
......@@ -275,7 +277,7 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
promise_or_capability);
LeaveMicrotaskContext();
RewindEnteredContext(saved_entered_context_count);
SetCurrentContext(current_context);
Goto(&done);
}
......@@ -295,7 +297,7 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
native_context, weak_factory);
GotoIfException(result, &if_exception, &var_exception);
LeaveMicrotaskContext();
RewindEnteredContext(saved_entered_context_count);
SetCurrentContext(current_context);
Goto(&done);
}
......@@ -308,7 +310,7 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
// Report unhandled exceptions from microtasks.
CallRuntime(Runtime::kReportMessage, current_context,
var_exception.value());
LeaveMicrotaskContext();
RewindEnteredContext(saved_entered_context_count);
SetCurrentContext(current_context);
Goto(&done);
}
......@@ -329,55 +331,110 @@ void MicrotaskQueueBuiltinsAssembler::SetCurrentContext(
context);
}
TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetEnteredContextCount() {
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
Node* hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
using ContextStack = DetachableVector<Context>;
TNode<IntPtrT> size_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kSizeOffset);
TNode<IntPtrT> size =
UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, size_offset));
return size;
}
void MicrotaskQueueBuiltinsAssembler::EnterMicrotaskContext(
TNode<Context> native_context) {
CSA_ASSERT(this, IsNativeContext(native_context));
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
Node* const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
StoreNoWriteBarrier(
MachineType::PointerRepresentation(), hsi,
IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
BitcastTaggedToWord(native_context));
Node* hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
using ContextStack = DetachableVector<Context>;
TNode<IntPtrT> capacity_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kCapacityOffset);
TNode<IntPtrT> size_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kSizeOffset);
TNode<IntPtrT> capacity =
UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, capacity_offset));
TNode<IntPtrT> size =
UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, size_offset));
Label if_append(this), if_grow(this, Label::kDeferred), done(this);
Branch(WordEqual(size, capacity), &if_grow, &if_append);
BIND(&if_append);
{
TNode<IntPtrT> data_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kDataOffset);
Node* data = Load(MachineType::Pointer(), hsi, data_offset);
StoreNoWriteBarrier(MachineType::Pointer().representation(), data,
TimesPointerSize(size),
BitcastTaggedToWord(native_context));
TNode<IntPtrT> new_size = IntPtrAdd(size, IntPtrConstant(1));
StoreNoWriteBarrier(MachineType::IntPtr().representation(), hsi,
size_offset, new_size);
using FlagStack = DetachableVector<int8_t>;
TNode<IntPtrT> flag_data_offset =
IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
FlagStack::kDataOffset);
Node* flag_data = Load(MachineType::Pointer(), hsi, flag_data_offset);
StoreNoWriteBarrier(MachineType::Int8().representation(), flag_data, size,
BoolConstant(true));
StoreNoWriteBarrier(
MachineType::IntPtr().representation(), hsi,
IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
FlagStack::kSizeOffset),
new_size);
// Load mirrored std::vector length from
// HandleScopeImplementer::entered_contexts_count_
auto type = kSizetSize == 8 ? MachineType::Uint64() : MachineType::Uint32();
Node* entered_contexts_length = Load(
type, hsi,
IntPtrConstant(HandleScopeImplementerOffsets::kEnteredContextsCount));
Goto(&done);
}
auto rep = kSizetSize == 8 ? MachineRepresentation::kWord64
: MachineRepresentation::kWord32;
BIND(&if_grow);
{
Node* function =
ExternalConstant(ExternalReference::call_enter_context_function());
CallCFunction2(MachineType::Int32(), MachineType::Pointer(),
MachineType::Pointer(), function, hsi,
BitcastTaggedToWord(native_context));
Goto(&done);
}
StoreNoWriteBarrier(
rep, hsi,
IntPtrConstant(
HandleScopeImplementerOffsets::kEnteredContextCountDuringMicrotasks),
entered_contexts_length);
BIND(&done);
}
void MicrotaskQueueBuiltinsAssembler::LeaveMicrotaskContext() {
void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext(
TNode<IntPtrT> saved_entered_context_count) {
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
Node* hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
using ContextStack = DetachableVector<Context>;
TNode<IntPtrT> size_offset =
IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
ContextStack::kSizeOffset);
Node* const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
#ifdef ENABLE_VERIFY_CSA
TNode<IntPtrT> size =
UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, size_offset));
CSA_ASSERT(this, IntPtrLessThan(IntPtrConstant(0), size));
CSA_ASSERT(this, IntPtrLessThanOrEqual(saved_entered_context_count, size));
#endif
StoreNoWriteBarrier(MachineType::IntPtr().representation(), hsi, size_offset,
saved_entered_context_count);
using FlagStack = DetachableVector<int8_t>;
StoreNoWriteBarrier(
MachineType::PointerRepresentation(), hsi,
IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
IntPtrConstant(0));
if (kSizetSize == 4) {
StoreNoWriteBarrier(
MachineRepresentation::kWord32, hsi,
IntPtrConstant(HandleScopeImplementerOffsets::
kEnteredContextCountDuringMicrotasks),
Int32Constant(0));
} else {
StoreNoWriteBarrier(
MachineRepresentation::kWord64, hsi,
IntPtrConstant(HandleScopeImplementerOffsets::
kEnteredContextCountDuringMicrotasks),
Int64Constant(0));
}
MachineType::IntPtr().representation(), hsi,
IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
FlagStack::kSizeOffset),
saved_entered_context_count);
}
void MicrotaskQueueBuiltinsAssembler::RunPromiseHook(
......@@ -417,7 +474,7 @@ TF_BUILTIN(EnqueueMicrotask, MicrotaskQueueBuiltinsAssembler) {
TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
Label if_grow(this);
Label if_grow(this, Label::kDeferred);
GotoIf(IntPtrEqual(size, capacity), &if_grow);
// |microtask_queue| has an unused slot to store |microtask|.
......
......@@ -957,6 +957,15 @@ static uint64_t atomic_pair_compare_exchange(intptr_t address,
FUNCTION_REFERENCE(atomic_pair_compare_exchange_function,
atomic_pair_compare_exchange)
static int EnterMicrotaskContextWrapper(HandleScopeImplementer* hsi,
Address raw_context) {
Context context = Context::cast(ObjectPtr(raw_context));
hsi->EnterMicrotaskContext(context);
return 0;
}
FUNCTION_REFERENCE(call_enter_context_function, EnterMicrotaskContextWrapper);
bool operator==(ExternalReference lhs, ExternalReference rhs) {
return lhs.address() == rhs.address();
}
......
......@@ -186,6 +186,7 @@ class StatsCounter;
V(wasm_memory_copy, "wasm::memory_copy") \
V(wasm_memory_fill, "wasm::memory_fill") \
V(call_enqueue_microtask_function, "MicrotaskQueue::CallEnqueueMicrotask") \
V(call_enter_context_function, "call_enter_context_function") \
V(atomic_pair_load_function, "atomic_pair_load_function") \
V(atomic_pair_store_function, "atomic_pair_store_function") \
V(atomic_pair_add_function, "atomic_pair_add_function") \
......
......@@ -4297,10 +4297,11 @@ void Isolate::RunMicrotasks() {
TRACE_EVENT0("v8.execute", "RunMicrotasks");
TRACE_EVENT_CALL_STATS_SCOPED(this, "v8", "V8.RunMicrotasks");
HandleScopeImplementer::EnteredContextRewindScope scope(
handle_scope_implementer());
// If execution is terminating, bail out, clean up, and propagate to
// TryCatch scope.
if (default_microtask_queue()->RunMicrotasks(this) < 0) {
handle_scope_implementer()->LeaveMicrotaskContext();
SetTerminationOnExternalTryCatch();
}
DCHECK_EQ(0, default_microtask_queue()->size());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment