From 72d5ad3e825cba2c4be900d43eee99975a3df928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Z=C3=BCnd?= Date: Fri, 27 Jul 2018 12:32:14 +0200 Subject: [PATCH] [torque] Make 'test' and 'action' expression optional in for loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL changes the for-loop so all parts are optional, allowing loops like: for (;;) {} for (;; ++i) {} ... R=danno@chromium.org, tebbi@chromium.org Bug: v8:7793 Change-Id: I7bf9ef9e59d55eb9ae9f38904a1c1106ae50df5a Reviewed-on: https://chromium-review.googlesource.com/1152727 Commit-Queue: Simon Zünd Reviewed-by: Daniel Clifford Cr-Commit-Position: refs/heads/master@{#54752} --- src/torque/ast.h | 7 ++- src/torque/declaration-visitor.cc | 10 +++- src/torque/implementation-visitor.cc | 48 ++++++++++------ src/torque/torque-parser.cc | 7 ++- test/cctest/torque/test-torque.cc | 12 ++++ test/torque/test-torque.tq | 86 ++++++++++++++++++++++++++++ 6 files changed, 145 insertions(+), 25 deletions(-) diff --git a/src/torque/ast.h b/src/torque/ast.h index b54e3cf182..7c64cfd6c7 100644 --- a/src/torque/ast.h +++ b/src/torque/ast.h @@ -477,7 +477,8 @@ struct GotoStatement : Statement { struct ForLoopStatement : Statement { DEFINE_AST_NODE_LEAF_BOILERPLATE(ForLoopStatement) ForLoopStatement(SourcePosition pos, base::Optional declaration, - Expression* test, Expression* action, Statement* body) + base::Optional test, + base::Optional action, Statement* body) : Statement(kKind, pos), var_declaration(), test(std::move(test)), @@ -487,8 +488,8 @@ struct ForLoopStatement : Statement { var_declaration = VarDeclarationStatement::cast(*declaration); } base::Optional var_declaration; - Expression* test; - Expression* action; + base::Optional test; + base::Optional action; Statement* body; }; diff --git a/src/torque/declaration-visitor.cc b/src/torque/declaration-visitor.cc index b83dbb3bc3..999524a146 100644 --- a/src/torque/declaration-visitor.cc +++ b/src/torque/declaration-visitor.cc @@ -434,9 +434,15 @@ void DeclarationVisitor::Visit(ForLoopStatement* stmt) { Declarations::NodeScopeActivator scope(declarations(), stmt); if (stmt->var_declaration) Visit(*stmt->var_declaration); PushControlSplit(); - DeclareExpressionForBranch(stmt->test); + + // Same as DeclareExpressionForBranch, but without the extra scope. + // If no test expression is present we can not use it for the scope. + declarations()->DeclareLabel(kTrueLabelName); + declarations()->DeclareLabel(kFalseLabelName); + if (stmt->test) Visit(*stmt->test); + Visit(stmt->body); - Visit(stmt->action); + if (stmt->action) Visit(*stmt->action); auto changed_vars = PopControlSplit(); global_context_.AddControlSplitChangedVariables( stmt, declarations()->GetCurrentSpecializationTypeNamesVector(), diff --git a/src/torque/implementation-visitor.cc b/src/torque/implementation-visitor.cc index 5ac3bd5f6b..34b469cee9 100644 --- a/src/torque/implementation-visitor.cc +++ b/src/torque/implementation-visitor.cc @@ -993,33 +993,47 @@ const Type* ImplementationVisitor::Visit(ForLoopStatement* stmt) { if (stmt->var_declaration) Visit(*stmt->var_declaration); - Label* body_label = nullptr; - Label* exit_label = nullptr; - { - Declarations::NodeScopeActivator scope(declarations(), stmt->test); - body_label = declarations()->LookupLabel(kTrueLabelName); - GenerateLabelDefinition(body_label); - exit_label = declarations()->LookupLabel(kFalseLabelName); - GenerateLabelDefinition(exit_label); - } + Label* body_label = declarations()->LookupLabel(kTrueLabelName); + GenerateLabelDefinition(body_label); + Label* exit_label = declarations()->LookupLabel(kFalseLabelName); + GenerateLabelDefinition(exit_label); Label* header_label = declarations()->DeclarePrivateLabel("header"); GenerateLabelDefinition(header_label, stmt); GenerateLabelGoto(header_label); GenerateLabelBind(header_label); - Label* assignment_label = declarations()->DeclarePrivateLabel("assignment"); - GenerateLabelDefinition(assignment_label); + // The continue label is where "continue" statements jump to. If no action + // expression is provided, we jump directly to the header. + Label* continue_label = header_label; - BreakContinueActivator activator(global_context_, exit_label, - assignment_label); + // The action label is only needed when an action expression was provided. + Label* action_label = nullptr; + if (stmt->action) { + action_label = declarations()->DeclarePrivateLabel("action"); + GenerateLabelDefinition(action_label); + + // The action expression needs to be executed on a continue. + continue_label = action_label; + } + + BreakContinueActivator activator(global_context_, exit_label, continue_label); std::vector labels = {body_label, exit_label}; - if (GenerateExpressionBranch(stmt->test, labels, {stmt->body}, - assignment_label)) { + bool generate_action = true; + if (stmt->test) { + generate_action = GenerateExpressionBranch(*stmt->test, labels, + {stmt->body}, continue_label); + } else { + GenerateLabelGoto(body_label); + generate_action = + GenerateLabeledStatementBlocks({stmt->body}, labels, continue_label); + } + + if (generate_action && stmt->action) { ScopedIndent indent(this); - GenerateLabelBind(assignment_label); - Visit(stmt->action); + GenerateLabelBind(action_label); + Visit(*stmt->action); GenerateLabelGoto(header_label); } diff --git a/src/torque/torque-parser.cc b/src/torque/torque-parser.cc index e87817bcd6..f10eec6c71 100644 --- a/src/torque/torque-parser.cc +++ b/src/torque/torque-parser.cc @@ -550,8 +550,8 @@ base::Optional MakeForOfLoopStatement( base::Optional MakeForLoopStatement( ParseResultIterator* child_results) { auto var_decl = child_results->NextAs>(); - auto test = child_results->NextAs(); - auto action = child_results->NextAs(); + auto test = child_results->NextAs>(); + auto action = child_results->NextAs>(); auto body = child_results->NextAs(); Statement* result = MakeNode(var_decl, test, action, body); return ParseResult{result}; @@ -1075,7 +1075,8 @@ struct TorqueGrammar : Grammar { MakeForOfLoopStatement), Rule({Token("for"), Token("("), Optional(&varDeclarationWithInitialization), Token(";"), - expression, Token(";"), expression, Token(")"), &atomarStatement}, + Optional(expression), Token(";"), + Optional(expression), Token(")"), &atomarStatement}, MakeForLoopStatement)}; // Result: base::Optional diff --git a/test/cctest/torque/test-torque.cc b/test/cctest/torque/test-torque.cc index 032778069d..13d0c1e765 100644 --- a/test/cctest/torque/test-torque.cc +++ b/test/cctest/torque/test-torque.cc @@ -223,6 +223,18 @@ TEST(TestLocalConstBindings) { ft.Call(); } +TEST(TestForLoop) { + Isolate* isolate(CcTest::InitIsolateOnce()); + CodeAssemblerTester asm_tester(isolate, 0); + TestBuiltinsFromDSLAssembler m(asm_tester.state()); + { + m.TestForLoop(); + m.Return(m.UndefinedConstant()); + } + FunctionTester ft(asm_tester.GenerateCode(), 0); + ft.Call(); +} + } // namespace compiler } // namespace internal } // namespace v8 diff --git a/test/torque/test-torque.tq b/test/torque/test-torque.tq index 1ed8e986b6..47f2a54270 100644 --- a/test/torque/test-torque.tq +++ b/test/torque/test-torque.tq @@ -289,4 +289,90 @@ module test { macro TestStruct4(): TestStructC { return TestStructC{TestStruct2(), TestStruct2()}; } + + // This macro tests different versions of the for-loop where some parts + // are (not) present. + macro TestForLoop() { + let sum: Smi = 0; + for (let i: Smi = 0; i < 5; ++i) sum = sum + i; + check(sum == 10); + + sum = 0; + let j: Smi = 0; + for (; j < 5; ++j) sum = sum + j; + check(sum == 10); + + sum = 0; + j = 0; + for (; j < 5;) sum = sum + j++; + check(sum == 10); + + // Check that break works. No test expression. + sum = 0; + for (let i: Smi = 0;; ++i) { + if (i == 5) break; + sum = sum + i; + } + check(sum == 10); + + sum = 0; + j = 0; + for (;;) { + if (j == 5) break; + sum = sum + j; + j++; + } + check(sum == 10); + + // The following tests are the same as above, but use continue to skip + // index 3. + sum = 0; + for (let i: Smi = 0; i < 5; ++i) { + if (i == 3) continue; + sum = sum + i; + } + check(sum == 7); + + sum = 0; + j = 0; + for (; j < 5; ++j) { + if (j == 3) continue; + sum = sum + j; + } + check(sum == 7); + + sum = 0; + j = 0; + for (; j < 5;) { + if (j == 3) { + j++; + continue; + } + sum = sum + j; + j++; + } + check(sum == 7); + + sum = 0; + for (let i: Smi = 0;; ++i) { + if (i == 3) continue; + if (i == 5) break; + sum = sum + i; + } + check(sum == 7); + + sum = 0; + j = 0; + for (;;) { + if (j == 3) { + j++; + continue; + } + + if (j == 5) break; + sum = sum + j; + j++; + } + check(sum == 7); + } }