Commit b9df0003 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[generators] Improve yield* desugaring to save unnecessary try/catch and try/finally

Change-Id: Ia900c6c21d1ff330088a6566f8f6c7719c887ccf
Reviewed-on: https://chromium-review.googlesource.com/509256
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45466}
parent a5449b0f
...@@ -2518,15 +2518,18 @@ class RewritableExpression final : public Expression { ...@@ -2518,15 +2518,18 @@ class RewritableExpression final : public Expression {
// desired, must be done beforehand (see the parser). // desired, must be done beforehand (see the parser).
class Suspend final : public Expression { class Suspend final : public Expression {
public: public:
enum OnException { kOnExceptionThrow, kOnExceptionRethrow }; // With {kNoControl}, the {Suspend} behaves like yield, except that it never
// throws and never causes the current generator to return. This is used to
// desugar yield*.
enum OnAbruptResume { kOnExceptionThrow, kOnExceptionRethrow, kNoControl };
Expression* generator_object() const { return generator_object_; } Expression* generator_object() const { return generator_object_; }
Expression* expression() const { return expression_; } Expression* expression() const { return expression_; }
OnException on_exception() const { OnAbruptResume on_abrupt_resume() const {
return OnExceptionField::decode(bit_field_); return OnAbruptResumeField::decode(bit_field_);
} }
bool rethrow_on_exception() const { bool rethrow_on_exception() const {
return on_exception() == kOnExceptionRethrow; return on_abrupt_resume() == kOnExceptionRethrow;
} }
int suspend_id() const { return suspend_id_; } int suspend_id() const { return suspend_id_; }
...@@ -2563,23 +2566,23 @@ class Suspend final : public Expression { ...@@ -2563,23 +2566,23 @@ class Suspend final : public Expression {
friend class AstNodeFactory; friend class AstNodeFactory;
Suspend(Expression* generator_object, Expression* expression, int pos, Suspend(Expression* generator_object, Expression* expression, int pos,
OnException on_exception, SuspendFlags flags) OnAbruptResume on_abrupt_resume, SuspendFlags flags)
: Expression(pos, kSuspend), : Expression(pos, kSuspend),
suspend_id_(-1), suspend_id_(-1),
generator_object_(generator_object), generator_object_(generator_object),
expression_(expression) { expression_(expression) {
bit_field_ |= bit_field_ |= OnAbruptResumeField::encode(on_abrupt_resume) |
OnExceptionField::encode(on_exception) | FlagsField::encode(flags); FlagsField::encode(flags);
} }
int suspend_id_; int suspend_id_;
Expression* generator_object_; Expression* generator_object_;
Expression* expression_; Expression* expression_;
class OnExceptionField class OnAbruptResumeField
: public BitField<OnException, Expression::kNextBitFieldIndex, 1> {}; : public BitField<OnAbruptResume, Expression::kNextBitFieldIndex, 2> {};
class FlagsField class FlagsField
: public BitField<SuspendFlags, OnExceptionField::kNext, : public BitField<SuspendFlags, OnAbruptResumeField::kNext,
static_cast<int>(SuspendFlags::kBitWidth)> {}; static_cast<int>(SuspendFlags::kBitWidth)> {};
}; };
...@@ -3564,11 +3567,11 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3564,11 +3567,11 @@ class AstNodeFactory final BASE_EMBEDDED {
} }
Suspend* NewSuspend(Expression* generator_object, Expression* expression, Suspend* NewSuspend(Expression* generator_object, Expression* expression,
int pos, Suspend::OnException on_exception, int pos, Suspend::OnAbruptResume on_abrupt_resume,
SuspendFlags flags) { SuspendFlags flags) {
if (!expression) expression = NewUndefinedLiteral(pos); if (!expression) expression = NewUndefinedLiteral(pos);
return new (zone_) return new (zone_)
Suspend(generator_object, expression, pos, on_exception, flags); Suspend(generator_object, expression, pos, on_abrupt_resume, flags);
} }
Throw* NewThrow(Expression* exception, int pos) { Throw* NewThrow(Expression* exception, int pos) {
......
...@@ -2567,9 +2567,10 @@ void BytecodeGenerator::BuildGeneratorResume(Suspend* expr, ...@@ -2567,9 +2567,10 @@ void BytecodeGenerator::BuildGeneratorResume(Suspend* expr,
: Runtime::kInlineGeneratorGetInputOrDebugPos; : Runtime::kInlineGeneratorGetInputOrDebugPos;
DCHECK(generator.is_valid()); DCHECK(generator.is_valid());
builder() builder()->CallRuntime(get_generator_input, generator);
->CallRuntime(get_generator_input, generator)
.StoreAccumulatorInRegister(input); if (expr->on_abrupt_resume() != Suspend::kNoControl) {
builder()->StoreAccumulatorInRegister(input);
Register resume_mode = register_allocator()->NewRegister(); Register resume_mode = register_allocator()->NewRegister();
builder() builder()
...@@ -2577,7 +2578,6 @@ void BytecodeGenerator::BuildGeneratorResume(Suspend* expr, ...@@ -2577,7 +2578,6 @@ void BytecodeGenerator::BuildGeneratorResume(Suspend* expr,
.StoreAccumulatorInRegister(resume_mode); .StoreAccumulatorInRegister(resume_mode);
// Now dispatch on resume mode. // Now dispatch on resume mode.
BytecodeLabel resume_with_next; BytecodeLabel resume_with_next;
BytecodeLabel resume_with_throw; BytecodeLabel resume_with_throw;
...@@ -2615,6 +2615,7 @@ void BytecodeGenerator::BuildGeneratorResume(Suspend* expr, ...@@ -2615,6 +2615,7 @@ void BytecodeGenerator::BuildGeneratorResume(Suspend* expr,
builder()->Bind(&resume_with_next); builder()->Bind(&resume_with_next);
builder()->LoadAccumulatorWithRegister(input); builder()->LoadAccumulatorWithRegister(input);
}
} }
void BytecodeGenerator::VisitSuspend(Suspend* expr) { void BytecodeGenerator::VisitSuspend(Suspend* expr) {
......
...@@ -1397,17 +1397,16 @@ class ParserBase { ...@@ -1397,17 +1397,16 @@ class ParserBase {
return factory()->NewReturnStatement(expr, pos); return factory()->NewReturnStatement(expr, pos);
} }
inline SuspendExpressionT BuildSuspend(ExpressionT generator, inline SuspendExpressionT BuildSuspend(
ExpressionT expr, int pos, ExpressionT generator, ExpressionT expr, int pos,
Suspend::OnException on_exception, Suspend::OnAbruptResume on_abrupt_resume, SuspendFlags suspend_type) {
SuspendFlags suspend_type) {
DCHECK_EQ(0, DCHECK_EQ(0,
static_cast<int>(suspend_type & ~SuspendFlags::kSuspendTypeMask)); static_cast<int>(suspend_type & ~SuspendFlags::kSuspendTypeMask));
if (V8_UNLIKELY(is_async_generator())) { if (V8_UNLIKELY(is_async_generator())) {
suspend_type = static_cast<SuspendFlags>(suspend_type | suspend_type = static_cast<SuspendFlags>(suspend_type |
SuspendFlags::kAsyncGenerator); SuspendFlags::kAsyncGenerator);
} }
return factory()->NewSuspend(generator, expr, pos, on_exception, return factory()->NewSuspend(generator, expr, pos, on_abrupt_resume,
suspend_type); suspend_type);
} }
......
...@@ -4237,7 +4237,6 @@ void Parser::SetFunctionName(Expression* value, const AstRawString* name) { ...@@ -4237,7 +4237,6 @@ void Parser::SetFunctionName(Expression* value, const AstRawString* name) {
} }
} }
// Desugaring of yield* // Desugaring of yield*
// ==================== // ====================
// //
...@@ -4282,17 +4281,9 @@ void Parser::SetFunctionName(Expression* value, const AstRawString* name) { ...@@ -4282,17 +4281,9 @@ void Parser::SetFunctionName(Expression* value, const AstRawString* name) {
// //
// // From the generator to its user: // // From the generator to its user:
// // Forward output, receive new input, and determine resume mode. // // Forward output, receive new input, and determine resume mode.
// mode = kReturn;
// try {
// try {
// RawYield(output); // See explanation above. // RawYield(output); // See explanation above.
// mode = kNext; // mode = %GeneratorGetResumeMode();
// } catch (error) {
// mode = kThrow;
// }
// } finally {
// input = function.sent; // input = function.sent;
// continue;
// } // }
// } // }
// //
...@@ -4509,48 +4500,31 @@ Expression* Parser::RewriteYieldStar(Expression* generator, ...@@ -4509,48 +4500,31 @@ Expression* Parser::RewriteYieldStar(Expression* generator,
property, break_loop, factory()->NewEmptyStatement(nopos), nopos); property, break_loop, factory()->NewEmptyStatement(nopos), nopos);
} }
// mode = kReturn;
Statement* set_mode_return;
{
Expression* mode_proxy = factory()->NewVariableProxy(var_mode);
Expression* kreturn =
factory()->NewSmiLiteral(JSGeneratorObject::kReturn, nopos);
Expression* assignment =
factory()->NewAssignment(Token::ASSIGN, mode_proxy, kreturn, nopos);
set_mode_return = factory()->NewExpressionStatement(assignment, nopos);
}
// Yield(output); // Yield(output);
Statement* yield_output; Statement* yield_output;
{ {
Expression* output_proxy = factory()->NewVariableProxy(var_output); Expression* output_proxy = factory()->NewVariableProxy(var_output);
Suspend* yield = Suspend* yield =
BuildSuspend(generator, output_proxy, nopos, Suspend::kOnExceptionThrow, BuildSuspend(generator, output_proxy, nopos, Suspend::kNoControl,
SuspendFlags::kYieldStar); SuspendFlags::kYieldStar);
yield_output = factory()->NewExpressionStatement(yield, nopos); yield_output = factory()->NewExpressionStatement(yield, nopos);
} }
// mode = kNext; // mode = %GeneratorGetResumeMode();
Statement* set_mode_next; Statement* get_mode;
{ {
Expression* mode_proxy = factory()->NewVariableProxy(var_mode); Expression* mode_proxy = factory()->NewVariableProxy(var_mode);
Expression* knext =
factory()->NewSmiLiteral(JSGeneratorObject::kNext, nopos);
Expression* assignment =
factory()->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos);
set_mode_next = factory()->NewExpressionStatement(assignment, nopos);
}
// mode = kThrow; ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
Statement* set_mode_throw; VariableProxy* generator = factory()->NewVariableProxy(
{ function_state_->generator_object_variable());
Expression* mode_proxy = factory()->NewVariableProxy(var_mode); args->Add(generator, zone());
Expression* kthrow = Expression* mode = factory()->NewCallRuntime(
factory()->NewSmiLiteral(JSGeneratorObject::kThrow, nopos); Runtime::kInlineGeneratorGetResumeMode, args, pos);
Expression* assignment = Expression* assignment =
factory()->NewAssignment(Token::ASSIGN, mode_proxy, kthrow, nopos); factory()->NewAssignment(Token::ASSIGN, mode_proxy, mode, nopos);
set_mode_throw = factory()->NewExpressionStatement(assignment, nopos); get_mode = factory()->NewExpressionStatement(assignment, nopos);
} }
// input = function.sent; // input = function.sent;
...@@ -4596,36 +4570,6 @@ Expression* Parser::RewriteYieldStar(Expression* generator, ...@@ -4596,36 +4570,6 @@ Expression* Parser::RewriteYieldStar(Expression* generator,
// Now put things together. // Now put things together.
// try { ... } catch(e) { ... }
Statement* try_catch;
{
Block* try_block = factory()->NewBlock(nullptr, 2, false, nopos);
try_block->statements()->Add(yield_output, zone());
try_block->statements()->Add(set_mode_next, zone());
Block* catch_block = factory()->NewBlock(nullptr, 1, false, nopos);
catch_block->statements()->Add(set_mode_throw, zone());
Scope* catch_scope = NewHiddenCatchScopeWithParent(scope());
try_catch = factory()->NewTryCatchStatementForDesugaring(
try_block, catch_scope, catch_block, nopos);
}
// try { ... } finally { ... }
Statement* try_finally;
{
Block* try_block = factory()->NewBlock(nullptr, 1, false, nopos);
try_block->statements()->Add(try_catch, zone());
Block* finally = factory()->NewBlock(nullptr, 2, false, nopos);
finally->statements()->Add(get_input, zone());
finally->statements()->Add(factory()->NewContinueStatement(loop, nopos),
zone());
try_finally = factory()->NewTryFinallyStatement(try_block, finally, nopos);
}
// switch (mode) { ... } // switch (mode) { ... }
SwitchStatement* switch_mode = factory()->NewSwitchStatement(nullptr, nopos); SwitchStatement* switch_mode = factory()->NewSwitchStatement(nullptr, nopos);
{ {
...@@ -4665,7 +4609,6 @@ Expression* Parser::RewriteYieldStar(Expression* generator, ...@@ -4665,7 +4609,6 @@ Expression* Parser::RewriteYieldStar(Expression* generator,
Block* loop_body = factory()->NewBlock(nullptr, 5, false, nopos); Block* loop_body = factory()->NewBlock(nullptr, 5, false, nopos);
loop_body->statements()->Add(switch_mode, zone()); loop_body->statements()->Add(switch_mode, zone());
loop_body->statements()->Add(if_done, zone()); loop_body->statements()->Add(if_done, zone());
loop_body->statements()->Add(set_mode_return, zone());
if (is_async_generator()) { if (is_async_generator()) {
// AsyncGeneratorYield does not yield the original iterator result, // AsyncGeneratorYield does not yield the original iterator result,
...@@ -4682,7 +4625,9 @@ Expression* Parser::RewriteYieldStar(Expression* generator, ...@@ -4682,7 +4625,9 @@ Expression* Parser::RewriteYieldStar(Expression* generator,
factory()->NewExpressionStatement(assign, nopos), zone()); factory()->NewExpressionStatement(assign, nopos), zone());
} }
loop_body->statements()->Add(try_finally, zone()); loop_body->statements()->Add(yield_output, zone());
loop_body->statements()->Add(get_input, zone());
loop_body->statements()->Add(get_mode, zone());
loop->Initialize(factory()->NewBooleanLiteral(true, nopos), loop_body); loop->Initialize(factory()->NewBooleanLiteral(true, nopos), loop_body);
} }
......
...@@ -644,7 +644,7 @@ class PreParserFactory { ...@@ -644,7 +644,7 @@ class PreParserFactory {
} }
PreParserExpression NewSuspend(PreParserExpression generator_object, PreParserExpression NewSuspend(PreParserExpression generator_object,
PreParserExpression expression, int pos, PreParserExpression expression, int pos,
Suspend::OnException on_exception, Suspend::OnAbruptResume on_abrupt_resume,
SuspendFlags flags) { SuspendFlags flags) {
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
......
...@@ -2348,6 +2348,10 @@ TEST(Generators) { ...@@ -2348,6 +2348,10 @@ TEST(Generators) {
"function* f() { for (let x of [42]) yield x }\n" "function* f() { for (let x of [42]) yield x }\n"
"f();\n", "f();\n",
"function* g() { yield 42 }\n"
"function* f() { yield* g() }\n"
"f();\n",
}; };
CHECK(CompareTexts(BuildActual(printer, snippets), CHECK(CompareTexts(BuildActual(printer, snippets),
......
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