tint/transform/utils: Add HoistToDeclBefore::Replace()

Handles statement replacement of for-loop initializer and continuing
statements.

Change-Id: I83ddf6fbd9b19f5022f7b02d7aebcbd95ab4c1f8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/122302
Kokoro: Ben Clayton <bclayton@chromium.org>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
Ben Clayton 2023-03-02 17:37:53 +00:00 committed by Dawn LUCI CQ
parent c0c8abc569
commit 26157557e8
3 changed files with 296 additions and 0 deletions

View File

@ -99,6 +99,21 @@ struct HoistToDeclBefore::State {
return InsertBeforeImpl(before_stmt, std::move(builder));
}
/// @copydoc HoistToDeclBefore::Replace(const sem::Statement* what, const ast::Statement* with)
bool Replace(const sem::Statement* what, const ast::Statement* with) {
auto builder = [with] { return with; };
return Replace(what, std::move(builder));
}
/// @copydoc HoistToDeclBefore::Replace(const sem::Statement* what, const StmtBuilder& with)
bool Replace(const sem::Statement* what, const StmtBuilder& with) {
if (!InsertBeforeImpl(what, Decompose{})) {
return false;
}
ctx.Replace(what->Declaration(), with);
return true;
}
/// @copydoc HoistToDeclBefore::Prepare()
bool Prepare(const sem::ValueExpression* before_expr) {
return InsertBefore(before_expr->Stmt(), nullptr);
@ -413,6 +428,14 @@ bool HoistToDeclBefore::InsertBefore(const sem::Statement* before_stmt,
return state_->InsertBefore(before_stmt, builder);
}
bool HoistToDeclBefore::Replace(const sem::Statement* what, const ast::Statement* with) {
return state_->Replace(what, with);
}
bool HoistToDeclBefore::Replace(const sem::Statement* what, const StmtBuilder& with) {
return state_->Replace(what, with);
}
bool HoistToDeclBefore::Prepare(const sem::ValueExpression* before_expr) {
return state_->Prepare(before_expr);
}

View File

@ -76,6 +76,20 @@ class HoistToDeclBefore {
/// @return true on success
bool InsertBefore(const sem::Statement* before_stmt, const StmtBuilder& builder);
/// Replaces the statement @p what with the statement @p stmt, possibly converting 'for-loop's
/// to 'loop's if necessary.
/// @param what the statement to replace
/// @param with the replacement statement
/// @return true on success
bool Replace(const sem::Statement* what, const ast::Statement* with);
/// Replaces the statement @p what with the statement returned by @p stmt, possibly converting
/// 'for-loop's to 'loop's if necessary.
/// @param what the statement to replace
/// @param with the replacement statement builder
/// @return true on success
bool Replace(const sem::Statement* what, const StmtBuilder& with);
/// Use to signal that we plan on hoisting a decl before `before_expr`. This
/// will convert 'for-loop's to 'loop's and 'else-if's to 'else {if}'s if
/// needed.

View File

@ -877,5 +877,264 @@ fn f() {
EXPECT_EQ(expect, str(cloned));
}
TEST_F(HoistToDeclBeforeTest, Replace_Block) {
// fn foo() {
// }
// fn f() {
// var a = 1i;
// }
ProgramBuilder b;
b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{var});
Program original(std::move(b));
ProgramBuilder cloned_b;
CloneContext ctx(&cloned_b, &original);
HoistToDeclBefore hoistToDeclBefore(ctx);
auto* target_stmt = ctx.src->Sem().Get(var);
auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
hoistToDeclBefore.Replace(target_stmt, new_stmt);
ctx.Clone();
Program cloned(std::move(cloned_b));
auto* expect = R"(
fn foo() {
}
fn f() {
foo();
}
)";
EXPECT_EQ(expect, str(cloned));
}
TEST_F(HoistToDeclBeforeTest, Replace_Block_Function) {
// fn foo() {
// }
// fn f() {
// var a = 1i;
// }
ProgramBuilder b;
b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{var});
Program original(std::move(b));
ProgramBuilder cloned_b;
CloneContext ctx(&cloned_b, &original);
HoistToDeclBefore hoistToDeclBefore(ctx);
auto* target_stmt = ctx.src->Sem().Get(var);
hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
ctx.Clone();
Program cloned(std::move(cloned_b));
auto* expect = R"(
fn foo() {
}
fn f() {
foo();
}
)";
EXPECT_EQ(expect, str(cloned));
}
TEST_F(HoistToDeclBeforeTest, Replace_ForLoopInit) {
// fn foo() {
// }
// fn f() {
// for(var a = 1i; true;) {
// }
// }
ProgramBuilder b;
b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{s});
Program original(std::move(b));
ProgramBuilder cloned_b;
CloneContext ctx(&cloned_b, &original);
HoistToDeclBefore hoistToDeclBefore(ctx);
auto* target_stmt = ctx.src->Sem().Get(var);
auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
hoistToDeclBefore.Replace(target_stmt, new_stmt);
ctx.Clone();
Program cloned(std::move(cloned_b));
auto* expect = R"(
fn foo() {
}
fn f() {
{
foo();
loop {
if (!(true)) {
break;
}
{
}
}
}
}
)";
EXPECT_EQ(expect, str(cloned));
}
TEST_F(HoistToDeclBeforeTest, Replace_ForLoopInit_Function) {
// fn foo() {
// }
// fn f() {
// for(var a = 1i; true;) {
// }
// }
ProgramBuilder b;
b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{s});
Program original(std::move(b));
ProgramBuilder cloned_b;
CloneContext ctx(&cloned_b, &original);
HoistToDeclBefore hoistToDeclBefore(ctx);
auto* target_stmt = ctx.src->Sem().Get(var);
hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
ctx.Clone();
Program cloned(std::move(cloned_b));
auto* expect = R"(
fn foo() {
}
fn f() {
{
foo();
loop {
if (!(true)) {
break;
}
{
}
}
}
}
)";
EXPECT_EQ(expect, str(cloned));
}
TEST_F(HoistToDeclBeforeTest, Replace_ForLoopCont) {
// fn foo() {
// }
// fn f() {
// var a = 1i;
// for(; true; a+=1i) {
// }
// }
ProgramBuilder b;
b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
auto* cont = b.CompoundAssign("a", b.Expr(1_i), ast::BinaryOp::kAdd);
auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{var, s});
Program original(std::move(b));
ProgramBuilder cloned_b;
CloneContext ctx(&cloned_b, &original);
HoistToDeclBefore hoistToDeclBefore(ctx);
auto* target_stmt = ctx.src->Sem().Get(cont->As<ast::Statement>());
auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
hoistToDeclBefore.Replace(target_stmt, new_stmt);
ctx.Clone();
Program cloned(std::move(cloned_b));
auto* expect = R"(
fn foo() {
}
fn f() {
var a = 1i;
loop {
if (!(true)) {
break;
}
{
}
continuing {
foo();
}
}
}
)";
EXPECT_EQ(expect, str(cloned));
}
TEST_F(HoistToDeclBeforeTest, Replace_ForLoopCont_Function) {
// fn foo() {
// }
// fn f() {
// var a = 1i;
// for(; true; a+=1i) {
// }
// }
ProgramBuilder b;
b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
auto* cont = b.CompoundAssign("a", b.Expr(1_i), ast::BinaryOp::kAdd);
auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{var, s});
Program original(std::move(b));
ProgramBuilder cloned_b;
CloneContext ctx(&cloned_b, &original);
HoistToDeclBefore hoistToDeclBefore(ctx);
auto* target_stmt = ctx.src->Sem().Get(cont->As<ast::Statement>());
hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
ctx.Clone();
Program cloned(std::move(cloned_b));
auto* expect = R"(
fn foo() {
}
fn f() {
var a = 1i;
loop {
if (!(true)) {
break;
}
{
}
continuing {
foo();
}
}
}
)";
EXPECT_EQ(expect, str(cloned));
}
} // namespace
} // namespace tint::transform