[torque] Make 'test' and 'action' expression optional in for loop

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 <szuend@google.com>
Reviewed-by: Daniel Clifford <danno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54752}
This commit is contained in:
Simon Zünd 2018-07-27 12:32:14 +02:00 committed by Commit Bot
parent 8abada5b41
commit 72d5ad3e82
6 changed files with 145 additions and 25 deletions

View File

@ -477,7 +477,8 @@ struct GotoStatement : Statement {
struct ForLoopStatement : Statement {
DEFINE_AST_NODE_LEAF_BOILERPLATE(ForLoopStatement)
ForLoopStatement(SourcePosition pos, base::Optional<Statement*> declaration,
Expression* test, Expression* action, Statement* body)
base::Optional<Expression*> test,
base::Optional<Expression*> 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<VarDeclarationStatement*> var_declaration;
Expression* test;
Expression* action;
base::Optional<Expression*> test;
base::Optional<Expression*> action;
Statement* body;
};

View File

@ -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(),

View File

@ -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<Label*> 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);
}

View File

@ -550,8 +550,8 @@ base::Optional<ParseResult> MakeForOfLoopStatement(
base::Optional<ParseResult> MakeForLoopStatement(
ParseResultIterator* child_results) {
auto var_decl = child_results->NextAs<base::Optional<Statement*>>();
auto test = child_results->NextAs<Expression*>();
auto action = child_results->NextAs<Expression*>();
auto test = child_results->NextAs<base::Optional<Expression*>>();
auto action = child_results->NextAs<base::Optional<Expression*>>();
auto body = child_results->NextAs<Statement*>();
Statement* result = MakeNode<ForLoopStatement>(var_decl, test, action, body);
return ParseResult{result};
@ -1075,7 +1075,8 @@ struct TorqueGrammar : Grammar {
MakeForOfLoopStatement),
Rule({Token("for"), Token("("),
Optional<Statement*>(&varDeclarationWithInitialization), Token(";"),
expression, Token(";"), expression, Token(")"), &atomarStatement},
Optional<Expression*>(expression), Token(";"),
Optional<Expression*>(expression), Token(")"), &atomarStatement},
MakeForLoopStatement)};
// Result: base::Optional<Statement*>

View File

@ -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

View File

@ -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);
}
}