[builtins] Port EnqueueMicrotask to CSA.
Previously the Promise builtins would always use a runtime function to schedule a new microtask, which is unnecessarily expensive. Since the runtime function only adds the microtask to a FixedArray (potentially growing that array) and increments the number of pending microtasks, it is fairly straight-forward to do this in CSA land instead. This change improves the Bluebird benchmarks by 2-4% on average. Bug: v8:7253 Change-Id: I77e96b9e5afbb4bdbe129b6bb289d9905ed581bf Reviewed-on: https://chromium-review.googlesource.com/851972 Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Cr-Commit-Position: refs/heads/master@{#50372}
This commit is contained in:
parent
204441b230
commit
2b4cc835f1
@ -616,12 +616,6 @@ class InternalBuiltinsAssembler : public CodeStubAssembler {
|
||||
explicit InternalBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
TNode<IntPtrT> GetPendingMicrotaskCount();
|
||||
void SetPendingMicrotaskCount(TNode<IntPtrT> count);
|
||||
|
||||
TNode<FixedArray> GetMicrotaskQueue();
|
||||
void SetMicrotaskQueue(TNode<FixedArray> queue);
|
||||
|
||||
TNode<Context> GetCurrentContext();
|
||||
void SetCurrentContext(TNode<Context> context);
|
||||
|
||||
@ -651,39 +645,6 @@ class InternalBuiltinsAssembler : public CodeStubAssembler {
|
||||
}
|
||||
};
|
||||
|
||||
TNode<IntPtrT> InternalBuiltinsAssembler::GetPendingMicrotaskCount() {
|
||||
auto ref = ExternalReference::pending_microtask_count_address(isolate());
|
||||
if (kIntSize == 8) {
|
||||
return TNode<IntPtrT>::UncheckedCast(
|
||||
Load(MachineType::Int64(), ExternalConstant(ref)));
|
||||
} else {
|
||||
Node* const value = Load(MachineType::Int32(), ExternalConstant(ref));
|
||||
return ChangeInt32ToIntPtr(value);
|
||||
}
|
||||
}
|
||||
|
||||
void InternalBuiltinsAssembler::SetPendingMicrotaskCount(TNode<IntPtrT> count) {
|
||||
auto ref = ExternalReference::pending_microtask_count_address(isolate());
|
||||
auto rep = kIntSize == 8 ? MachineRepresentation::kWord64
|
||||
: MachineRepresentation::kWord32;
|
||||
if (kIntSize == 4 && kPointerSize == 8) {
|
||||
Node* const truncated_count =
|
||||
TruncateInt64ToInt32(TNode<Int64T>::UncheckedCast(count));
|
||||
StoreNoWriteBarrier(rep, ExternalConstant(ref), truncated_count);
|
||||
} else {
|
||||
StoreNoWriteBarrier(rep, ExternalConstant(ref), count);
|
||||
}
|
||||
}
|
||||
|
||||
TNode<FixedArray> InternalBuiltinsAssembler::GetMicrotaskQueue() {
|
||||
return TNode<FixedArray>::UncheckedCast(
|
||||
LoadRoot(Heap::kMicrotaskQueueRootIndex));
|
||||
}
|
||||
|
||||
void InternalBuiltinsAssembler::SetMicrotaskQueue(TNode<FixedArray> queue) {
|
||||
StoreRoot(Heap::kMicrotaskQueueRootIndex, queue);
|
||||
}
|
||||
|
||||
TNode<Context> InternalBuiltinsAssembler::GetCurrentContext() {
|
||||
auto ref = ExternalReference(kContextAddress, isolate());
|
||||
return TNode<Context>::UncheckedCast(
|
||||
|
@ -547,8 +547,7 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(
|
||||
Node* info = AllocatePromiseReactionJobInfo(
|
||||
result, var_on_resolve.value(), deferred_promise, deferred_on_resolve,
|
||||
deferred_on_reject, context);
|
||||
// TODO(gsathya): Move this to TF
|
||||
CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info);
|
||||
EnqueueMicrotask(info);
|
||||
Goto(&out);
|
||||
|
||||
BIND(&reject);
|
||||
@ -567,8 +566,7 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(
|
||||
Node* info = AllocatePromiseReactionJobInfo(
|
||||
result, var_on_reject.value(), deferred_promise,
|
||||
deferred_on_resolve, deferred_on_reject, context);
|
||||
// TODO(gsathya): Move this to TF
|
||||
CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info);
|
||||
EnqueueMicrotask(info);
|
||||
Goto(&out);
|
||||
}
|
||||
}
|
||||
@ -776,8 +774,7 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
|
||||
// 12. Perform EnqueueJob("PromiseJobs",
|
||||
// PromiseResolveThenableJob, « promise, resolution, thenAction»).
|
||||
BIND(&enqueue);
|
||||
// TODO(gsathya): Move this to TF
|
||||
CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, info);
|
||||
EnqueueMicrotask(info);
|
||||
Goto(&out);
|
||||
}
|
||||
|
||||
@ -835,7 +832,7 @@ void PromiseBuiltinsAssembler::PromiseFulfill(
|
||||
result, tasks, deferred_promise, deferred_on_resolve, deferred_on_reject,
|
||||
context);
|
||||
|
||||
CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info);
|
||||
EnqueueMicrotask(info);
|
||||
Goto(&do_promisereset);
|
||||
|
||||
BIND(&do_promisereset);
|
||||
@ -988,6 +985,69 @@ void PromiseBuiltinsAssembler::PerformFulfillClosure(Node* context, Node* value,
|
||||
BIND(&out);
|
||||
}
|
||||
|
||||
void PromiseBuiltinsAssembler::EnqueueMicrotask(Node* microtask) {
|
||||
TNode<IntPtrT> num_tasks = GetPendingMicrotaskCount();
|
||||
TNode<IntPtrT> new_num_tasks = IntPtrAdd(num_tasks, IntPtrConstant(1));
|
||||
TNode<FixedArray> queue = GetMicrotaskQueue();
|
||||
TNode<IntPtrT> queue_length = LoadAndUntagFixedArrayBaseLength(queue);
|
||||
|
||||
Label if_append(this), if_grow(this), done(this);
|
||||
Branch(WordEqual(num_tasks, queue_length), &if_grow, &if_append);
|
||||
|
||||
BIND(&if_grow);
|
||||
{
|
||||
// Determine the new queue length and check if we need to allocate
|
||||
// in large object space (instead of just going to new space, where
|
||||
// we also know that we don't need any write barriers for setting
|
||||
// up the new queue object).
|
||||
Label if_newspace(this), if_lospace(this, Label::kDeferred);
|
||||
TNode<IntPtrT> new_queue_length =
|
||||
IntPtrMax(IntPtrConstant(8), IntPtrAdd(num_tasks, num_tasks));
|
||||
Branch(IntPtrLessThanOrEqual(new_queue_length,
|
||||
IntPtrConstant(FixedArray::kMaxRegularLength)),
|
||||
&if_newspace, &if_lospace);
|
||||
|
||||
BIND(&if_newspace);
|
||||
{
|
||||
// This is the likely case where the new queue fits into new space,
|
||||
// and thus we don't need any write barriers for initializing it.
|
||||
TNode<FixedArray> new_queue =
|
||||
CAST(AllocateFixedArray(PACKED_ELEMENTS, new_queue_length));
|
||||
CopyFixedArrayElements(PACKED_ELEMENTS, queue, new_queue, num_tasks,
|
||||
SKIP_WRITE_BARRIER);
|
||||
StoreFixedArrayElement(new_queue, num_tasks, microtask,
|
||||
SKIP_WRITE_BARRIER);
|
||||
FillFixedArrayWithValue(PACKED_ELEMENTS, new_queue, new_num_tasks,
|
||||
new_queue_length, Heap::kUndefinedValueRootIndex);
|
||||
SetMicrotaskQueue(new_queue);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_lospace);
|
||||
{
|
||||
// The fallback case where the new queue ends up in large object space.
|
||||
TNode<FixedArray> new_queue = CAST(AllocateFixedArray(
|
||||
PACKED_ELEMENTS, new_queue_length, INTPTR_PARAMETERS,
|
||||
AllocationFlag::kAllowLargeObjectAllocation));
|
||||
CopyFixedArrayElements(PACKED_ELEMENTS, queue, new_queue, num_tasks);
|
||||
StoreFixedArrayElement(new_queue, num_tasks, microtask);
|
||||
FillFixedArrayWithValue(PACKED_ELEMENTS, new_queue, new_num_tasks,
|
||||
new_queue_length, Heap::kUndefinedValueRootIndex);
|
||||
SetMicrotaskQueue(new_queue);
|
||||
Goto(&done);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&if_append);
|
||||
{
|
||||
StoreFixedArrayElement(queue, num_tasks, microtask);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
SetPendingMicrotaskCount(new_num_tasks);
|
||||
}
|
||||
|
||||
// ES#sec-promise-reject-functions
|
||||
// Promise Reject Functions
|
||||
TF_BUILTIN(PromiseRejectClosure, PromiseBuiltinsAssembler) {
|
||||
|
@ -177,6 +177,8 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
Node* PromiseStatus(Node* promise);
|
||||
void PerformFulfillClosure(Node* context, Node* value, bool should_resolve);
|
||||
|
||||
void EnqueueMicrotask(Node* microtask);
|
||||
|
||||
private:
|
||||
Node* IsPromiseStatus(Node* actual, v8::Promise::PromiseState expected);
|
||||
void PromiseSetStatus(Node* promise, v8::Promise::PromiseState status);
|
||||
|
@ -10522,6 +10522,39 @@ Node* CodeStubAssembler::AllocatePromiseReactionJobInfo(
|
||||
return result;
|
||||
}
|
||||
|
||||
TNode<IntPtrT> CodeStubAssembler::GetPendingMicrotaskCount() {
|
||||
auto ref = ExternalReference::pending_microtask_count_address(isolate());
|
||||
if (kIntSize == 8) {
|
||||
return TNode<IntPtrT>::UncheckedCast(
|
||||
Load(MachineType::Int64(), ExternalConstant(ref)));
|
||||
} else {
|
||||
Node* const value = Load(MachineType::Int32(), ExternalConstant(ref));
|
||||
return ChangeInt32ToIntPtr(value);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::SetPendingMicrotaskCount(TNode<IntPtrT> count) {
|
||||
auto ref = ExternalReference::pending_microtask_count_address(isolate());
|
||||
auto rep = kIntSize == 8 ? MachineRepresentation::kWord64
|
||||
: MachineRepresentation::kWord32;
|
||||
if (kIntSize == 4 && kPointerSize == 8) {
|
||||
Node* const truncated_count =
|
||||
TruncateInt64ToInt32(TNode<Int64T>::UncheckedCast(count));
|
||||
StoreNoWriteBarrier(rep, ExternalConstant(ref), truncated_count);
|
||||
} else {
|
||||
StoreNoWriteBarrier(rep, ExternalConstant(ref), count);
|
||||
}
|
||||
}
|
||||
|
||||
TNode<FixedArray> CodeStubAssembler::GetMicrotaskQueue() {
|
||||
return TNode<FixedArray>::UncheckedCast(
|
||||
LoadRoot(Heap::kMicrotaskQueueRootIndex));
|
||||
}
|
||||
|
||||
void CodeStubAssembler::SetMicrotaskQueue(TNode<FixedArray> queue) {
|
||||
StoreRoot(Heap::kMicrotaskQueueRootIndex, queue);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::MarkerIsFrameType(Node* marker_or_function,
|
||||
StackFrame::Type frame_type) {
|
||||
return WordEqual(marker_or_function,
|
||||
|
@ -1840,6 +1840,13 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
Node* deferred_on_resolve,
|
||||
Node* deferred_on_reject, Node* context);
|
||||
|
||||
// Microtask helpers
|
||||
TNode<IntPtrT> GetPendingMicrotaskCount();
|
||||
void SetPendingMicrotaskCount(TNode<IntPtrT> count);
|
||||
|
||||
TNode<FixedArray> GetMicrotaskQueue();
|
||||
void SetMicrotaskQueue(TNode<FixedArray> queue);
|
||||
|
||||
// Helpers for StackFrame markers.
|
||||
Node* MarkerIsFrameType(Node* marker_or_function,
|
||||
StackFrame::Type frame_type);
|
||||
|
@ -70,22 +70,6 @@ RUNTIME_FUNCTION(Runtime_PromiseRevokeReject) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_EnqueuePromiseReactionJob) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(PromiseReactionJobInfo, info, 0);
|
||||
isolate->EnqueueMicrotask(info);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_EnqueuePromiseResolveThenableJob) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(args.length(), 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(PromiseResolveThenableJobInfo, info, 0);
|
||||
isolate->EnqueueMicrotask(info);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_EnqueueMicrotask) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
|
@ -463,19 +463,17 @@ namespace internal {
|
||||
F(GreaterThanOrEqual, 2, 1) \
|
||||
F(InstanceOf, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_PROMISE(F) \
|
||||
F(EnqueueMicrotask, 1, 1) \
|
||||
F(EnqueuePromiseReactionJob, 1, 1) \
|
||||
F(EnqueuePromiseResolveThenableJob, 1, 1) \
|
||||
F(PromiseHookInit, 2, 1) \
|
||||
F(PromiseHookResolve, 1, 1) \
|
||||
F(PromiseHookBefore, 1, 1) \
|
||||
F(PromiseHookAfter, 1, 1) \
|
||||
F(PromiseMarkAsHandled, 1, 1) \
|
||||
F(PromiseRejectEventFromStack, 2, 1) \
|
||||
F(PromiseRevokeReject, 1, 1) \
|
||||
F(PromiseResult, 1, 1) \
|
||||
F(PromiseStatus, 1, 1) \
|
||||
#define FOR_EACH_INTRINSIC_PROMISE(F) \
|
||||
F(EnqueueMicrotask, 1, 1) \
|
||||
F(PromiseHookInit, 2, 1) \
|
||||
F(PromiseHookResolve, 1, 1) \
|
||||
F(PromiseHookBefore, 1, 1) \
|
||||
F(PromiseHookAfter, 1, 1) \
|
||||
F(PromiseMarkAsHandled, 1, 1) \
|
||||
F(PromiseRejectEventFromStack, 2, 1) \
|
||||
F(PromiseRevokeReject, 1, 1) \
|
||||
F(PromiseResult, 1, 1) \
|
||||
F(PromiseStatus, 1, 1) \
|
||||
F(ReportPromiseReject, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_PROXY(F) \
|
||||
|
Loading…
Reference in New Issue
Block a user