[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:
parent
8abada5b41
commit
72d5ad3e82
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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*>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user