Commit b57366f2 authored by Caitlin Potter's avatar Caitlin Potter Committed by Commit Bot

[async-await] allocate HeapObjects for Await all at once.

Allocates the Await success/failure closures, their context, and
the two required JSPromise objects all at once in a single call,
rather than performing multiple allocations throughout the function.

Saves about 2kb of snapshot space on an x64.release build.

Performance impact of this change has not been measured yet.

BUG=v8:4483
R=ishell@chromium.org, jgruber@chromium.org

Change-Id: I8d911cb91f5d0e00544ad3ba608aa170f6b2f704
Reviewed-on: https://chromium-review.googlesource.com/549999Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Cr-Commit-Position: refs/heads/master@{#46360}
parent 5f0d8288
......@@ -104,12 +104,9 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE));
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
NodeGenerator1 create_closure_context = [&](Node* native_context) -> Node* {
Node* const context =
CreatePromiseContext(native_context, AwaitContext::kLength);
ContextInitializer init_closure_context = [&](Node* context) {
StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
generator);
return context;
};
// TODO(jgruber): AsyncBuiltinsAssembler::Await currently does not reuse
......@@ -120,8 +117,8 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
// InternalPerformPromiseThen.
Node* const result = Await(
context, generator, awaited, outer_promise, create_closure_context,
Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
context, generator, awaited, outer_promise, AwaitContext::kLength,
init_closure_context, Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, is_predicted_as_caught);
Return(result);
......
......@@ -21,42 +21,116 @@ class ValueUnwrapContext {
Node* AsyncBuiltinsAssembler::Await(
Node* context, Node* generator, Node* value, Node* outer_promise,
const NodeGenerator1& create_closure_context, int on_resolve_context_index,
int on_reject_context_index, bool is_predicted_as_caught) {
// Let promiseCapability be ! NewPromiseCapability(%Promise%).
Node* const wrapped_value = AllocateAndInitJSPromise(context);
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
CallBuiltin(Builtins::kResolveNativePromise, context, wrapped_value, value);
int context_length, const ContextInitializer& init_closure_context,
int on_resolve_context_index, int on_reject_context_index,
bool is_predicted_as_caught) {
DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
Node* const native_context = LoadNativeContext(context);
Node* const closure_context = create_closure_context(native_context);
#ifdef DEBUG
{
Node* const map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
Node* const instance_size = LoadMapInstanceSize(map);
// Assert that the strict function map has an instance size is
// JSFunction::kSize
CSA_ASSERT(this, WordEqual(instance_size, IntPtrConstant(JSFunction::kSize /
kPointerSize)));
}
#endif
#ifdef DEBUG
{
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const map =
LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
Node* const instance_size = LoadMapInstanceSize(map);
// Assert that the JSPromise map has an instance size is
// JSPromise::kSize
CSA_ASSERT(this,
WordEqual(instance_size,
IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
kPointerSize)));
}
#endif
static const int kWrappedPromiseOffset = FixedArray::SizeFor(context_length);
static const int kThrowawayPromiseOffset =
kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kResolveClosureOffset =
kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kRejectClosureOffset =
kResolveClosureOffset + JSFunction::kSize;
static const int kTotalSize = kRejectClosureOffset + JSFunction::kSize;
Node* const base = AllocateInNewSpace(kTotalSize);
Node* const closure_context = base;
{
// Initialize closure context
InitializeFunctionContext(native_context, closure_context, context_length);
init_closure_context(closure_context);
}
// Let promiseCapability be ! NewPromiseCapability(%Promise%).
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const promise_map =
LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
{
// Initialize Promise
StoreMapNoWriteBarrier(wrapped_value, promise_map);
InitializeJSObjectFromMap(
wrapped_value, promise_map,
IntPtrConstant(JSPromise::kSizeWithEmbedderFields),
EmptyFixedArrayConstant(), EmptyFixedArrayConstant());
PromiseInit(wrapped_value);
}
Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
{
// Initialize throwawayPromise
StoreMapNoWriteBarrier(throwaway, promise_map);
InitializeJSObjectFromMap(
throwaway, promise_map,
IntPtrConstant(JSPromise::kSizeWithEmbedderFields),
EmptyFixedArrayConstant(), EmptyFixedArrayConstant());
PromiseInit(throwaway);
}
// Load and allocate on_resolve closure
Node* const on_resolve_shared_fun =
LoadContextElement(native_context, on_resolve_context_index);
CSA_SLOW_ASSERT(
this, HasInstanceType(on_resolve_shared_fun, SHARED_FUNCTION_INFO_TYPE));
Node* const on_resolve = AllocateFunctionWithMapAndContext(
map, on_resolve_shared_fun, closure_context);
// Load and allocate on_reject closure
Node* const on_reject_shared_fun =
LoadContextElement(native_context, on_reject_context_index);
CSA_SLOW_ASSERT(
this, HasInstanceType(on_reject_shared_fun, SHARED_FUNCTION_INFO_TYPE));
Node* const on_reject = AllocateFunctionWithMapAndContext(
map, on_reject_shared_fun, closure_context);
Node* const throwaway_promise =
AllocateAndInitJSPromise(context, wrapped_value);
Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
{
// Initialize resolve handler
InitializeNativeClosure(closure_context, native_context, on_resolve,
on_resolve_context_index);
}
Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
{
// Initialize reject handler
InitializeNativeClosure(closure_context, native_context, on_reject,
on_reject_context_index);
}
{
// Add PromiseHooks if needed
Label next(this);
GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &next);
CallRuntime(Runtime::kPromiseHookInit, context, wrapped_value,
outer_promise);
CallRuntime(Runtime::kPromiseHookInit, context, throwaway, wrapped_value);
Goto(&next);
BIND(&next);
}
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
CallBuiltin(Builtins::kResolveNativePromise, context, wrapped_value, value);
// The Promise will be thrown away and not handled, but it shouldn't trigger
// unhandled reject events as its work is done
PromiseSetHasHandler(throwaway_promise);
PromiseSetHasHandler(throwaway);
Label do_perform_promise_then(this);
GotoIfNot(IsDebugActive(), &do_perform_promise_then);
......@@ -82,18 +156,52 @@ Node* AsyncBuiltinsAssembler::Await(
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
CallRuntime(Runtime::kSetProperty, context, throwaway_promise, key,
outer_promise, SmiConstant(STRICT));
CallRuntime(Runtime::kSetProperty, context, throwaway, key, outer_promise,
SmiConstant(STRICT));
}
Goto(&do_perform_promise_then);
BIND(&do_perform_promise_then);
CallBuiltin(Builtins::kPerformNativePromiseThen, context, wrapped_value,
on_resolve, on_reject, throwaway_promise);
on_resolve, on_reject, throwaway);
return wrapped_value;
}
void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
Node* native_context,
Node* function,
int context_index) {
Node* const function_map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
StoreMapNoWriteBarrier(function, function_map);
StoreObjectFieldRoot(function, JSObject::kPropertiesOffset,
Heap::kEmptyFixedArrayRootIndex);
StoreObjectFieldRoot(function, JSObject::kElementsOffset,
Heap::kEmptyFixedArrayRootIndex);
StoreObjectFieldRoot(function, JSFunction::kFeedbackVectorOffset,
Heap::kUndefinedCellRootIndex);
StoreObjectFieldRoot(function, JSFunction::kPrototypeOrInitialMapOffset,
Heap::kTheHoleValueRootIndex);
Node* shared_info = LoadContextElement(native_context, context_index);
CSA_ASSERT(this, IsSharedFunctionInfo(shared_info));
StoreObjectFieldNoWriteBarrier(
function, JSFunction::kSharedFunctionInfoOffset, shared_info);
StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
Node* const code = BitcastTaggedToWord(
LoadObjectField(shared_info, SharedFunctionInfo::kCodeOffset));
Node* const code_entry =
IntPtrAdd(code, IntPtrConstant(Code::kHeaderSize - kHeapObjectTag));
StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeEntryOffset,
code_entry,
MachineType::PointerRepresentation());
StoreObjectFieldRoot(function, JSFunction::kNextFunctionLinkOffset,
Heap::kUndefinedValueRootIndex);
}
Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
Node* done) {
Node* const map = LoadContextElement(
......
......@@ -16,7 +16,7 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
: PromiseBuiltinsAssembler(state) {}
protected:
typedef std::function<Node*(Node*)> NodeGenerator1;
typedef std::function<void(Node*)> ContextInitializer;
// Perform steps to resume generator after `value` is resolved.
// `on_reject_context_index` is an index into the Native Context, which should
......@@ -24,7 +24,8 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
// value following the reject index should be a similar value for the resolve
// closure. Returns the Promise-wrapped `value`.
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
const NodeGenerator1& create_closure_context,
int context_length,
const ContextInitializer& init_closure_context,
int on_resolve_context_index, int on_reject_context_index,
bool is_predicted_as_caught);
......@@ -33,6 +34,8 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
Node* CreateUnwrapClosure(Node* const native_context, Node* const done);
private:
void InitializeNativeClosure(Node* context, Node* native_context,
Node* function, int context_index);
Node* AllocateAsyncIteratorValueUnwrapContext(Node* native_context,
Node* done);
};
......
......@@ -250,12 +250,9 @@ void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) {
Node* const request = LoadFirstAsyncGeneratorRequestFromQueue(generator);
CSA_ASSERT(this, WordNotEqual(request, UndefinedConstant()));
NodeGenerator1 closure_context = [&](Node* native_context) -> Node* {
Node* const context =
CreatePromiseContext(native_context, AwaitContext::kLength);
ContextInitializer init_closure_context = [&](Node* context) {
StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
generator);
return context;
};
Node* outer_promise =
......@@ -265,8 +262,8 @@ void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) {
const int reject_index = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN;
Node* promise =
Await(context, generator, value, outer_promise, closure_context,
resolve_index, reject_index, is_catchable);
Await(context, generator, value, outer_promise, AwaitContext::kLength,
init_closure_context, resolve_index, reject_index, is_catchable);
CSA_SLOW_ASSERT(this, IsGeneratorNotSuspendedForAwait(generator));
StoreObjectField(generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset,
......
......@@ -205,11 +205,10 @@ Node* PromiseBuiltinsAssembler::NewPromiseCapability(Node* context,
return var_result.value();
}
Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context,
void PromiseBuiltinsAssembler::InitializeFunctionContext(Node* native_context,
Node* context,
int slots) {
DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS);
Node* const context = AllocateInNewSpace(FixedArray::SizeFor(slots));
StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex);
StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset,
SmiConstant(slots));
......@@ -223,6 +222,14 @@ Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context,
TheHoleConstant());
StoreContextElementNoWriteBarrier(context, Context::NATIVE_CONTEXT_INDEX,
native_context);
}
Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context,
int slots) {
DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS);
Node* const context = AllocateInNewSpace(FixedArray::SizeFor(slots));
InitializeFunctionContext(native_context, context, slots);
return context;
}
......
......@@ -134,6 +134,7 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
void BranchIfFastPath(Node* native_context, Node* promise_fun, Node* promise,
Label* if_isunmodified, Label* if_ismodified);
void InitializeFunctionContext(Node* native_context, Node* context, int len);
Node* CreatePromiseContext(Node* native_context, int slots);
void PromiseFulfill(Node* context, Node* promise, Node* result,
v8::Promise::PromiseState status);
......
......@@ -56,7 +56,8 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(Tuple2Map, Tuple2Map) \
V(Tuple3Map, Tuple3Map) \
V(UndefinedValue, Undefined) \
V(WeakCellMap, WeakCellMap)
V(WeakCellMap, WeakCellMap) \
V(SharedFunctionInfoMap, SharedFunctionInfoMap)
// Provides JavaScript-specific "macro-assembler" functionality on top of the
// CodeAssembler. By factoring the JavaScript-isms out of the CodeAssembler,
......@@ -814,6 +815,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsPrivateSymbol(Node* object);
Node* IsPropertyCell(Node* object);
Node* IsSequentialStringInstanceType(Node* instance_type);
inline Node* IsSharedFunctionInfo(Node* object) {
return IsSharedFunctionInfoMap(LoadMap(object));
}
Node* IsShortExternalStringInstanceType(Node* instance_type);
Node* IsSpecialReceiverInstanceType(Node* instance_type);
Node* IsSpecialReceiverMap(Node* map);
......
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