Add while statement parsing.
This CL adds parsing for the WGSL `while` statement.
Bug: tint:1425
Change-Id: Ibce5e28568935ca4f51b5ac33e7a60af7a916b4a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/93540
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 82fb78e..03ec75c 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -2523,6 +2523,54 @@
return true;
}
+bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
+ TextBuffer cond_pre;
+ std::stringstream cond_buf;
+ {
+ auto* cond = stmt->condition;
+ TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
+ if (!EmitExpression(cond_buf, cond)) {
+ return false;
+ }
+ }
+
+ // If the whilehas a multi-statement conditional, then we cannot emit this
+ // as a regular while in GLSL. Instead we need to generate a `while(true)` loop.
+ bool emit_as_loop = cond_pre.lines.size() > 0;
+ if (emit_as_loop) {
+ line() << "while (true) {";
+ increment_indent();
+ TINT_DEFER({
+ decrement_indent();
+ line() << "}";
+ });
+
+ current_buffer_->Append(cond_pre);
+ line() << "if (!(" << cond_buf.str() << ")) { break; }";
+
+ if (!EmitStatements(stmt->body->statements)) {
+ return false;
+ }
+ } else {
+ // While can be generated.
+ {
+ auto out = line();
+ out << "while";
+ {
+ ScopedParen sp(out);
+ out << cond_buf.str();
+ }
+ out << " {";
+ }
+ if (!EmitStatementsWithIndent(stmt->body->statements)) {
+ return false;
+ }
+ line() << "}";
+ }
+
+ return true;
+}
+
bool GeneratorImpl::EmitMemberAccessor(std::ostream& out,
const ast::MemberAccessorExpression* expr) {
if (!EmitExpression(out, expr->structure)) {
@@ -2591,6 +2639,9 @@
if (auto* l = stmt->As<ast::ForLoopStatement>()) {
return EmitForLoop(l);
}
+ if (auto* l = stmt->As<ast::WhileStatement>()) {
+ return EmitWhile(l);
+ }
if (auto* r = stmt->As<ast::ReturnStatement>()) {
return EmitReturn(r);
}
diff --git a/src/tint/writer/glsl/generator_impl.h b/src/tint/writer/glsl/generator_impl.h
index 819c79b..ff1611c 100644
--- a/src/tint/writer/glsl/generator_impl.h
+++ b/src/tint/writer/glsl/generator_impl.h
@@ -357,6 +357,10 @@
/// @param stmt the statement to emit
/// @returns true if the statement was emitted
bool EmitForLoop(const ast::ForLoopStatement* stmt);
+ /// Handles a while statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted
+ bool EmitWhile(const ast::WhileStatement* stmt);
/// Handles generating an identifier expression
/// @param out the output of the expression stream
/// @param expr the identifier expression
diff --git a/src/tint/writer/glsl/generator_impl_loop_test.cc b/src/tint/writer/glsl/generator_impl_loop_test.cc
index 5187daf..5f4d955 100644
--- a/src/tint/writer/glsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/glsl/generator_impl_loop_test.cc
@@ -381,5 +381,52 @@
)");
}
+TEST_F(GlslGeneratorImplTest_Loop, Emit_While) {
+ // while(true) {
+ // return;
+ // }
+
+ auto* f = While(Expr(true), Block(Return()));
+ WrapInFunction(f);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+ EXPECT_EQ(gen.result(), R"( while(true) {
+ return;
+ }
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Loop, Emit_WhileWithMultiStmtCond) {
+ // while(true && false) {
+ // return;
+ // }
+
+ Func("a_statement", {}, ty.void_(), {});
+
+ auto* multi_stmt =
+ create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr(true), Expr(false));
+ auto* f = While(multi_stmt, Block(CallStmt(Call("a_statement"))));
+ WrapInFunction(f);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+ EXPECT_EQ(gen.result(), R"( while (true) {
+ bool tint_tmp = true;
+ if (tint_tmp) {
+ tint_tmp = false;
+ }
+ if (!((tint_tmp))) { break; }
+ a_statement();
+ }
+)");
+}
+
} // namespace
} // namespace tint::writer::glsl
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index a9e290c..39adcf8 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -3481,6 +3481,53 @@
return true;
}
+bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
+ TextBuffer cond_pre;
+ std::stringstream cond_buf;
+ {
+ auto* cond = stmt->condition;
+ TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
+ if (!EmitExpression(cond_buf, cond)) {
+ return false;
+ }
+ }
+
+ // If the while has a multi-statement conditional, then we cannot emit this
+ // as a regular while in HLSL. Instead we need to generate a `while(true)` loop.
+ bool emit_as_loop = cond_pre.lines.size() > 0;
+ if (emit_as_loop) {
+ line() << LoopAttribute() << "while (true) {";
+ increment_indent();
+ TINT_DEFER({
+ decrement_indent();
+ line() << "}";
+ });
+
+ current_buffer_->Append(cond_pre);
+ line() << "if (!(" << cond_buf.str() << ")) { break; }";
+ if (!EmitStatements(stmt->body->statements)) {
+ return false;
+ }
+ } else {
+ // While can be generated.
+ {
+ auto out = line();
+ out << LoopAttribute() << "while";
+ {
+ ScopedParen sp(out);
+ out << cond_buf.str();
+ }
+ out << " {";
+ }
+ if (!EmitStatementsWithIndent(stmt->body->statements)) {
+ return false;
+ }
+ line() << "}";
+ }
+
+ return true;
+}
+
bool GeneratorImpl::EmitMemberAccessor(std::ostream& out,
const ast::MemberAccessorExpression* expr) {
if (!EmitExpression(out, expr->structure)) {
@@ -3551,6 +3598,9 @@
[&](const ast::ForLoopStatement* l) { //
return EmitForLoop(l);
},
+ [&](const ast::WhileStatement* l) { //
+ return EmitWhile(l);
+ },
[&](const ast::ReturnStatement* r) { //
return EmitReturn(r);
},
diff --git a/src/tint/writer/hlsl/generator_impl.h b/src/tint/writer/hlsl/generator_impl.h
index af7e4c9..0205142 100644
--- a/src/tint/writer/hlsl/generator_impl.h
+++ b/src/tint/writer/hlsl/generator_impl.h
@@ -353,6 +353,10 @@
/// @param stmt the statement to emit
/// @returns true if the statement was emitted
bool EmitForLoop(const ast::ForLoopStatement* stmt);
+ /// Handles a while statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted
+ bool EmitWhile(const ast::WhileStatement* stmt);
/// Handles generating an identifier expression
/// @param out the output of the expression stream
/// @param expr the identifier expression
diff --git a/src/tint/writer/hlsl/generator_impl_loop_test.cc b/src/tint/writer/hlsl/generator_impl_loop_test.cc
index 0bf4090..38ecb9b 100644
--- a/src/tint/writer/hlsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_loop_test.cc
@@ -373,5 +373,50 @@
)");
}
+TEST_F(HlslGeneratorImplTest_Loop, Emit_While) {
+ // while(true) {
+ // return;
+ // }
+
+ auto* f = While(Expr(true), Block(Return()));
+ WrapInFunction(f);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+ EXPECT_EQ(gen.result(), R"( [loop] while(true) {
+ return;
+ }
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Loop, Emit_WhileWithMultiStmtCond) {
+ // while(true && false) {
+ // return;
+ // }
+
+ auto* multi_stmt =
+ create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr(true), Expr(false));
+ auto* f = While(multi_stmt, Block(Return()));
+ WrapInFunction(f);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+ EXPECT_EQ(gen.result(), R"( [loop] while (true) {
+ bool tint_tmp = true;
+ if (tint_tmp) {
+ tint_tmp = false;
+ }
+ if (!((tint_tmp))) { break; }
+ return;
+ }
+)");
+}
+
} // namespace
} // namespace tint::writer::hlsl
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index aca2227..4d4c78d 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -2124,6 +2124,53 @@
return true;
}
+bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
+ TextBuffer cond_pre;
+ std::stringstream cond_buf;
+
+ {
+ auto* cond = stmt->condition;
+ TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
+ if (!EmitExpression(cond_buf, cond)) {
+ return false;
+ }
+ }
+
+ // If the while has a multi-statement conditional, then we cannot emit this
+ // as a regular while in MSL. Instead we need to generate a `while(true)` loop.
+ bool emit_as_loop = cond_pre.lines.size() > 0;
+ if (emit_as_loop) {
+ line() << "while (true) {";
+ increment_indent();
+ TINT_DEFER({
+ decrement_indent();
+ line() << "}";
+ });
+
+ current_buffer_->Append(cond_pre);
+ line() << "if (!(" << cond_buf.str() << ")) { break; }";
+ if (!EmitStatements(stmt->body->statements)) {
+ return false;
+ }
+ } else {
+ // While can be generated.
+ {
+ auto out = line();
+ out << "while";
+ {
+ ScopedParen sp(out);
+ out << cond_buf.str();
+ }
+ out << " {";
+ }
+ if (!EmitStatementsWithIndent(stmt->body->statements)) {
+ return false;
+ }
+ line() << "}";
+ }
+ return true;
+}
+
bool GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) {
// TODO(dsinclair): Verify this is correct when the discard semantics are
// defined for WGSL (https://github.com/gpuweb/gpuweb/issues/361)
@@ -2280,6 +2327,9 @@
[&](const ast::ForLoopStatement* l) { //
return EmitForLoop(l);
},
+ [&](const ast::WhileStatement* l) { //
+ return EmitWhile(l);
+ },
[&](const ast::ReturnStatement* r) { //
return EmitReturn(r);
},
diff --git a/src/tint/writer/msl/generator_impl.h b/src/tint/writer/msl/generator_impl.h
index be98a86..a05f3b1 100644
--- a/src/tint/writer/msl/generator_impl.h
+++ b/src/tint/writer/msl/generator_impl.h
@@ -270,6 +270,10 @@
/// @param stmt the statement to emit
/// @returns true if the statement was emitted
bool EmitForLoop(const ast::ForLoopStatement* stmt);
+ /// Handles a while statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted
+ bool EmitWhile(const ast::WhileStatement* stmt);
/// Handles a member accessor expression
/// @param out the output of the expression stream
/// @param expr the member accessor expression
diff --git a/src/tint/writer/msl/generator_impl_loop_test.cc b/src/tint/writer/msl/generator_impl_loop_test.cc
index 248e711..73d88c0 100644
--- a/src/tint/writer/msl/generator_impl_loop_test.cc
+++ b/src/tint/writer/msl/generator_impl_loop_test.cc
@@ -344,5 +344,45 @@
)");
}
+TEST_F(MslGeneratorImplTest, Emit_While) {
+ // while(true) {
+ // return;
+ // }
+
+ auto* f = While(Expr(true), Block(Return()));
+ WrapInFunction(f);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+ EXPECT_EQ(gen.result(), R"( while(true) {
+ return;
+ }
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_WhileWithMultiCond) {
+ // while(true && false) {
+ // return;
+ // }
+
+ auto* multi_stmt =
+ create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr(true), Expr(false));
+ auto* f = While(multi_stmt, Block(Return()));
+ WrapInFunction(f);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+ EXPECT_EQ(gen.result(), R"( while((true && false)) {
+ return;
+ }
+)");
+}
+
} // namespace
} // namespace tint::writer::msl
diff --git a/src/tint/writer/spirv/generator_impl.cc b/src/tint/writer/spirv/generator_impl.cc
index b8ca89c..c5e9dad 100644
--- a/src/tint/writer/spirv/generator_impl.cc
+++ b/src/tint/writer/spirv/generator_impl.cc
@@ -32,6 +32,7 @@
#include "src/tint/transform/unwind_discard_functions.h"
#include "src/tint/transform/var_for_dynamic_index.h"
#include "src/tint/transform/vectorize_scalar_matrix_constructors.h"
+#include "src/tint/transform/while_to_loop.h"
#include "src/tint/transform/zero_init_workgroup_memory.h"
#include "src/tint/writer/generate_external_texture_bindings.h"
@@ -74,7 +75,7 @@
manager.Add<transform::SimplifyPointers>(); // Required for arrayLength()
manager.Add<transform::VectorizeScalarMatrixConstructors>();
manager.Add<transform::ForLoopToLoop>(); // Must come after
- // ZeroInitWorkgroupMemory
+ manager.Add<transform::WhileToLoop>(); // ZeroInitWorkgroupMemory
manager.Add<transform::CanonicalizeEntryPointIO>();
manager.Add<transform::AddEmptyEntryPoint>();
manager.Add<transform::AddSpirvBlockAttribute>();
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 5e0ce8c..35119ac 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -919,6 +919,7 @@
[&](const ast::IncrementDecrementStatement* l) { return EmitIncrementDecrement(l); },
[&](const ast::LoopStatement* l) { return EmitLoop(l); },
[&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
+ [&](const ast::WhileStatement* l) { return EmitWhile(l); },
[&](const ast::ReturnStatement* r) { return EmitReturn(r); },
[&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
[&](const ast::VariableDeclStatement* v) { return EmitVariable(line(), v->variable); },
@@ -1181,6 +1182,30 @@
return true;
}
+bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
+ {
+ auto out = line();
+ out << "while";
+ {
+ ScopedParen sp(out);
+
+ auto* cond = stmt->condition;
+ if (!EmitExpression(out, cond)) {
+ return false;
+ }
+ }
+ out << " {";
+ }
+
+ if (!EmitStatementsWithIndent(stmt->body->statements)) {
+ return false;
+ }
+
+ line() << "}";
+
+ return true;
+}
+
bool GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
auto out = line();
out << "return";
diff --git a/src/tint/writer/wgsl/generator_impl.h b/src/tint/writer/wgsl/generator_impl.h
index a17e2da..8ceeab2 100644
--- a/src/tint/writer/wgsl/generator_impl.h
+++ b/src/tint/writer/wgsl/generator_impl.h
@@ -152,6 +152,10 @@
/// @param stmt the statement to emit
/// @returns true if the statement was emtited
bool EmitForLoop(const ast::ForLoopStatement* stmt);
+ /// Handles a while statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emtited
+ bool EmitWhile(const ast::WhileStatement* stmt);
/// Handles a member accessor expression
/// @param out the output of the expression stream
/// @param expr the member accessor expression
diff --git a/src/tint/writer/wgsl/generator_impl_loop_test.cc b/src/tint/writer/wgsl/generator_impl_loop_test.cc
index 2d6a8f4..99fcdb0 100644
--- a/src/tint/writer/wgsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_loop_test.cc
@@ -198,5 +198,45 @@
)");
}
+TEST_F(WgslGeneratorImplTest, Emit_While) {
+ // while(true) {
+ // return;
+ // }
+
+ auto* f = While(Expr(true), Block(Return()));
+ WrapInFunction(f);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+ EXPECT_EQ(gen.result(), R"( while(true) {
+ return;
+ }
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_WhileMultiCond) {
+ // while(true && false) {
+ // return;
+ // }
+
+ auto* multi_stmt =
+ create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr(true), Expr(false));
+ auto* f = While(multi_stmt, Block(Return()));
+ WrapInFunction(f);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
+ EXPECT_EQ(gen.result(), R"( while((true && false)) {
+ return;
+ }
+)");
+}
+
} // namespace
} // namespace tint::writer::wgsl