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

[async-iteration] eliminate implicit Await when resuming with .return()

AsyncGenerators, when resumed with a "return" completion, Await the sent
value to provide consistency with syntactic return statements. This
moves the await to during AsyncGeneratorResumeNext, shrinking the number
of bytecodes.

There's a minor change to BytecodeGenerator which removes a
%_GeneratorClose() call, since it's inserted implicitly by the parser.

BUG=v8:5855
TBR=neis@chromium.org

Change-Id: I2965c610e5985ac24c713b481e62f6b97f96a3d8
Reviewed-on: https://chromium-review.googlesource.com/582218
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47253}
parent 06f5f846
......@@ -219,13 +219,7 @@ void AstNumberingVisitor::VisitSuspend(Suspend* node) {
Visit(node->expression());
}
void AstNumberingVisitor::VisitYield(Yield* node) {
node->set_suspend_id(suspend_count_++);
if (IsAsyncGeneratorFunction(function_kind_)) {
node->set_await_return_value_suspend_id(suspend_count_++);
}
Visit(node->expression());
}
void AstNumberingVisitor::VisitYield(Yield* node) { VisitSuspend(node); }
void AstNumberingVisitor::VisitYieldStar(YieldStar* node) {
VisitSuspend(node);
......
......@@ -2275,24 +2275,10 @@ class Suspend : public Expression {
};
class Yield final : public Suspend {
public:
inline int await_return_value_suspend_id() const {
DCHECK_NE(await_return_value_suspend_id_, -1);
return await_return_value_suspend_id_;
}
void set_await_return_value_suspend_id(int id) {
await_return_value_suspend_id_ = id;
}
private:
friend class AstNodeFactory;
Yield(Expression* expression, int pos, OnAbruptResume on_abrupt_resume)
: Suspend(kYield, expression, pos, on_abrupt_resume),
await_return_value_suspend_id_(-1) {}
// TODO(caitp): remove from class once `await` handled by AsyncGeneratorReturn
// stub.
int await_return_value_suspend_id_;
: Suspend(kYield, expression, pos, on_abrupt_resume) {}
};
class YieldStar final : public Suspend {
......
......@@ -1500,11 +1500,19 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
info->set_length(1);
native_context()->set_async_generator_return_resolve_shared_fun(*info);
code = BUILTIN_CODE(isolate, AsyncGeneratorReturnRejectClosure);
code = BUILTIN_CODE(isolate, AsyncGeneratorReturnClosedResolveClosure);
info = factory->NewSharedFunctionInfo(factory->empty_string(), code, false);
info->set_internal_formal_parameter_count(1);
info->set_length(1);
native_context()->set_async_generator_return_reject_shared_fun(*info);
native_context()->set_async_generator_return_closed_resolve_shared_fun(
*info);
code = BUILTIN_CODE(isolate, AsyncGeneratorReturnClosedRejectClosure);
info = factory->NewSharedFunctionInfo(factory->empty_string(), code, false);
info->set_internal_formal_parameter_count(1);
info->set_length(1);
native_context()->set_async_generator_return_closed_reject_shared_fun(
*info);
}
{ // --- A r r a y ---
......
......@@ -22,7 +22,7 @@ class ValueUnwrapContext {
Node* AsyncBuiltinsAssembler::Await(
Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length, const ContextInitializer& init_closure_context,
int on_resolve_context_index, int on_reject_context_index,
Node* on_resolve_context_index, Node* on_reject_context_index,
Node* is_predicted_as_caught) {
DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
......@@ -173,7 +173,7 @@ Node* AsyncBuiltinsAssembler::Await(
void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
Node* native_context,
Node* function,
int context_index) {
Node* context_index) {
Node* const function_map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
StoreMapNoWriteBarrier(function, function_map);
......
......@@ -26,8 +26,18 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length,
const ContextInitializer& init_closure_context,
int on_resolve_context_index, int on_reject_context_index,
Node* on_resolve_context_index, Node* on_reject_context_index,
Node* is_predicted_as_caught);
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length,
const ContextInitializer& init_closure_context,
int on_resolve_context_index, int on_reject_context_index,
Node* is_predicted_as_caught) {
return Await(context, generator, value, outer_promise, context_length,
init_closure_context, IntPtrConstant(on_resolve_context_index),
IntPtrConstant(on_reject_context_index),
is_predicted_as_caught);
}
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length,
const ContextInitializer& init_closure_context,
......@@ -45,7 +55,7 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
private:
void InitializeNativeClosure(Node* context, Node* native_context,
Node* function, int context_index);
Node* function, Node* context_index);
Node* AllocateAsyncIteratorValueUnwrapContext(Node* native_context,
Node* done);
};
......
......@@ -1054,7 +1054,7 @@ namespace internal {
TFS(AsyncGeneratorResolve, kGenerator, kValue, kDone) \
TFS(AsyncGeneratorReject, kGenerator, kValue) \
TFS(AsyncGeneratorYield, kGenerator, kValue, kIsCaught) \
TFS(AsyncGeneratorReturnProcessor, kGenerator) \
TFS(AsyncGeneratorReturn, kGenerator, kValue, kIsCaught) \
TFS(AsyncGeneratorResumeNext, kGenerator) \
\
/* AsyncGeneratorFunction( p1, p2, ... pn, body ) */ \
......@@ -1080,8 +1080,9 @@ namespace internal {
TFJ(AsyncGeneratorAwaitResolveClosure, 1, kValue) \
TFJ(AsyncGeneratorAwaitRejectClosure, 1, kValue) \
TFJ(AsyncGeneratorYieldResolveClosure, 1, kValue) \
TFJ(AsyncGeneratorReturnClosedResolveClosure, 1, kValue) \
TFJ(AsyncGeneratorReturnClosedRejectClosure, 1, kValue) \
TFJ(AsyncGeneratorReturnResolveClosure, 1, kValue) \
TFJ(AsyncGeneratorReturnRejectClosure, 1, kValue) \
\
/* Async-from-Sync Iterator */ \
\
......
......@@ -221,8 +221,10 @@ enum ContextLookupFlags {
async_generator_yield_resolve_shared_fun) \
V(ASYNC_GENERATOR_RETURN_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
async_generator_return_resolve_shared_fun) \
V(ASYNC_GENERATOR_RETURN_REJECT_SHARED_FUN, SharedFunctionInfo, \
async_generator_return_reject_shared_fun) \
V(ASYNC_GENERATOR_RETURN_CLOSED_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
async_generator_return_closed_resolve_shared_fun) \
V(ASYNC_GENERATOR_RETURN_CLOSED_REJECT_SHARED_FUN, SharedFunctionInfo, \
async_generator_return_closed_reject_shared_fun) \
V(ATOMICS_OBJECT, JSObject, atomics_object) \
V(BOOLEAN_FUNCTION_INDEX, JSFunction, boolean_function) \
V(BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX, Map, \
......
......@@ -2228,20 +2228,6 @@ void BytecodeGenerator::BuildReturn(int source_position) {
if (info()->literal()->feedback_vector_spec()->HasTypeProfileSlot()) {
builder()->CollectTypeProfile(info()->literal()->return_position());
}
if (IsAsyncGeneratorFunction(info()->literal()->kind())) {
// Mark the generator as closed if returning from an async generator
// function. Note that non-async generators are closed by the
// generator-resume builtin.
// TODO(jarin,caitp) Move the async generator closing to the resume
// builtin.
RegisterAllocationScope register_scope(this);
Register result = register_allocator()->NewRegister();
builder()
->StoreAccumulatorInRegister(result)
.CallRuntime(Runtime::kInlineGeneratorClose, generator_object_)
.LoadAccumulatorWithRegister(result);
}
builder()->SetReturnPosition(source_position, info()->literal());
builder()->Return();
}
......@@ -2251,19 +2237,11 @@ void BytecodeGenerator::BuildAsyncReturn(int source_position) {
if (IsAsyncGeneratorFunction(info()->literal()->kind())) {
RegisterList args = register_allocator()->NewRegisterList(3);
Register generator = args[0];
Register result = args[1];
Register done = args[2];
builder()->StoreAccumulatorInRegister(result);
Variable* var_generator_object = closure_scope()->generator_object_var();
DCHECK_NOT_NULL(var_generator_object);
BuildVariableLoad(var_generator_object, FeedbackSlot::Invalid(),
HoleCheckMode::kElided);
builder()
->StoreAccumulatorInRegister(generator)
->MoveRegister(generator_object_, args[0]) // generator
.StoreAccumulatorInRegister(args[1]) // value
.LoadTrue()
.StoreAccumulatorInRegister(done)
.StoreAccumulatorInRegister(args[2]) // done
.CallRuntime(Runtime::kInlineAsyncGeneratorResolve, args);
} else {
DCHECK(IsAsyncFunction(info()->literal()->kind()));
......@@ -2661,8 +2639,6 @@ void BytecodeGenerator::VisitYield(Yield* expr) {
builder()->Bind(jump_table, JSGeneratorObject::kReturn);
builder()->LoadAccumulatorWithRegister(input);
if (IsAsyncGeneratorFunction(function_kind())) {
// Async generator methods will produce the iter result object.
BuildAwait(expr->await_return_value_suspend_id());
execution_control()->AsyncReturnAccumulator();
} else {
execution_control()->ReturnAccumulator();
......
......@@ -109,5 +109,32 @@ RUNTIME_FUNCTION(Runtime_GeneratorGetSourcePosition) {
return Smi::FromInt(generator->source_position());
}
// Return true if {generator}'s PC has a catch handler. This allows
// catch prediction to happen from the AsyncGeneratorResumeNext stub.
RUNTIME_FUNCTION(Runtime_AsyncGeneratorHasCatchHandlerForPC) {
DisallowHeapAllocation no_allocation_scope;
DCHECK_EQ(1, args.length());
DCHECK(args[0]->IsJSAsyncGeneratorObject());
JSAsyncGeneratorObject* generator = JSAsyncGeneratorObject::cast(args[0]);
int state = generator->continuation();
DCHECK_NE(state, JSAsyncGeneratorObject::kGeneratorExecuting);
// If state is 0 ("suspendedStart"), there is guaranteed to be no catch
// handler. Otherwise, if state is below 0, the generator is closed and will
// not reach a catch handler.
if (state < 1) return isolate->heap()->false_value();
SharedFunctionInfo* shared = generator->function()->shared();
DCHECK(shared->HasBytecodeArray());
HandlerTable* handler_table =
HandlerTable::cast(shared->bytecode_array()->handler_table());
int pc = Smi::cast(generator->input_or_debug_pos())->value();
HandlerTable::CatchPrediction catch_prediction = HandlerTable::ASYNC_AWAIT;
handler_table->LookupRange(pc, nullptr, &catch_prediction);
return isolate->heap()->ToBoolean(catch_prediction == HandlerTable::CAUGHT);
}
} // namespace internal
} // namespace v8
......@@ -242,7 +242,8 @@ namespace internal {
F(AsyncGeneratorYield, 3, 1) \
F(GeneratorGetContinuation, 1, 1) \
F(GeneratorGetSourcePosition, 1, 1) \
F(GeneratorGetResumeMode, 1, 1)
F(GeneratorGetResumeMode, 1, 1) \
F(AsyncGeneratorHasCatchHandlerForPC, 1, 1)
#ifdef V8_INTL_SUPPORT
#define FOR_EACH_INTRINSIC_INTL(F) \
......
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