mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-16 16:37:08 +00:00
tint/transform: Fix FoldTrivialSingleUseLet for abstracts
If the let initializer was an abstract numeric that was implicitly materialized to a concrete type, then we could inlining the initializer into the use without the implicit materialization cast. This could lead to validation errors, and subtly different results in the program. In this situation, add an explicit cast to keep the types the same before and after inlining. Fixed: tint:1664 Change-Id: Icca980cf8af74673906ad6c681a6b07d0c1932fd Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/101160 Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
committed by
Dawn LUCI CQ
parent
188be384f7
commit
8b30ca3efa
@@ -17,6 +17,7 @@
|
||||
#include "src/tint/program_builder.h"
|
||||
#include "src/tint/sem/block_statement.h"
|
||||
#include "src/tint/sem/function.h"
|
||||
#include "src/tint/sem/materialize.h"
|
||||
#include "src/tint/sem/statement.h"
|
||||
#include "src/tint/sem/variable.h"
|
||||
#include "src/tint/utils/scoped_assignment.h"
|
||||
@@ -26,6 +27,8 @@ TINT_INSTANTIATE_TYPEINFO(tint::transform::FoldTrivialSingleUseLets);
|
||||
namespace tint::transform {
|
||||
namespace {
|
||||
|
||||
/// @returns a @p stmt cast to a ast::VariableDeclStatement iff the statement is a let declaration,
|
||||
/// with an initializer that's an identifier or literal expression.
|
||||
const ast::VariableDeclStatement* AsTrivialLetDecl(const ast::Statement* stmt) {
|
||||
auto* var_decl = stmt->As<ast::VariableDeclStatement>();
|
||||
if (!var_decl) {
|
||||
@@ -50,10 +53,12 @@ FoldTrivialSingleUseLets::~FoldTrivialSingleUseLets() = default;
|
||||
|
||||
void FoldTrivialSingleUseLets::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
|
||||
for (auto* node : ctx.src->ASTNodes().Objects()) {
|
||||
// For each statement in each block of the entire program...
|
||||
if (auto* block = node->As<ast::BlockStatement>()) {
|
||||
auto& stmts = block->statements;
|
||||
for (size_t stmt_idx = 0; stmt_idx < stmts.Length(); stmt_idx++) {
|
||||
auto* stmt = stmts[stmt_idx];
|
||||
// Is the statement a trivial let declaration with a single user?
|
||||
if (auto* let_decl = AsTrivialLetDecl(stmt)) {
|
||||
auto* let = let_decl->variable;
|
||||
auto* sem_let = ctx.src->Sem().Get(let);
|
||||
@@ -65,11 +70,23 @@ void FoldTrivialSingleUseLets::Run(CloneContext& ctx, const DataMap&, DataMap&)
|
||||
auto* user = users[0];
|
||||
auto* user_stmt = user->Stmt()->Declaration();
|
||||
|
||||
// Scan forward to find the statement of use. If there's a statement between the
|
||||
// declaration and the use, then we cannot safely fold.
|
||||
for (size_t i = stmt_idx; i < stmts.Length(); i++) {
|
||||
if (user_stmt == stmts[i]) {
|
||||
auto* user_expr = user->Declaration();
|
||||
ctx.Remove(stmts, let_decl);
|
||||
ctx.Replace(user_expr, ctx.Clone(let->constructor));
|
||||
auto* initializer = ctx.Clone(let->constructor);
|
||||
if (auto* materialize =
|
||||
sem_let->Constructor()->As<sem::Materialize>()) {
|
||||
// The let initializer was an abstract numeric that was implicitly
|
||||
// materialized to a concrete type. As we're inlining the
|
||||
// initializer into the use, we need to make this cast explicit,
|
||||
// otherwise we'll have a different type for the substitution.
|
||||
auto* concrete_ty = CreateASTTypeFor(ctx, materialize->Type());
|
||||
initializer = ctx.dst->Construct(concrete_ty, initializer);
|
||||
}
|
||||
ctx.Replace(user_expr, initializer);
|
||||
}
|
||||
if (!AsTrivialLetDecl(stmts[i])) {
|
||||
// Stop if we hit a statement that isn't the single use of the
|
||||
|
||||
@@ -30,7 +30,26 @@ TEST_F(FoldTrivialSingleUseLetsTest, EmptyModule) {
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Single) {
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Single_Concrete) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
let x = 1i;
|
||||
_ = x;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
_ = 1i;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<FoldTrivialSingleUseLets>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Single_Abstract) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
let x = 1;
|
||||
@@ -40,7 +59,7 @@ fn f() {
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
_ = 1;
|
||||
_ = i32(1);
|
||||
}
|
||||
)";
|
||||
|
||||
@@ -49,7 +68,28 @@ fn f() {
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Multiple) {
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Multiple_Concrete) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
let x = 1u;
|
||||
let y = 2u;
|
||||
let z = 3u;
|
||||
_ = x + y + z;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
_ = ((1u + 2u) + 3u);
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<FoldTrivialSingleUseLets>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Multiple_Abstract) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
let x = 1;
|
||||
@@ -61,7 +101,7 @@ fn f() {
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
_ = ((1 + 2) + 3);
|
||||
_ = ((i32(1) + i32(2)) + i32(3));
|
||||
}
|
||||
)";
|
||||
|
||||
@@ -70,7 +110,28 @@ fn f() {
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Chained) {
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Chained_Concrete) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
let x = 1i;
|
||||
let y = x;
|
||||
let z = y;
|
||||
_ = z;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
_ = 1i;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<FoldTrivialSingleUseLets>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, Chained_Abstract) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
let x = 1;
|
||||
@@ -82,7 +143,7 @@ fn f() {
|
||||
|
||||
auto* expect = R"(
|
||||
fn f() {
|
||||
_ = 1;
|
||||
_ = i32(1);
|
||||
}
|
||||
)";
|
||||
|
||||
@@ -93,13 +154,13 @@ fn f() {
|
||||
|
||||
TEST_F(FoldTrivialSingleUseLetsTest, NoFold_NonTrivialLet) {
|
||||
auto* src = R"(
|
||||
fn function_with_posssible_side_effect() -> i32 {
|
||||
fn function_with_possible_side_effect() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn f() {
|
||||
let x = 1;
|
||||
let y = function_with_posssible_side_effect();
|
||||
let y = function_with_possible_side_effect();
|
||||
_ = (x + y);
|
||||
}
|
||||
)";
|
||||
@@ -115,11 +176,11 @@ TEST_F(FoldTrivialSingleUseLetsTest, NoFold_NonTrivialLet_OutOfOrder) {
|
||||
auto* src = R"(
|
||||
fn f() {
|
||||
let x = 1;
|
||||
let y = function_with_posssible_side_effect();
|
||||
let y = function_with_possible_side_effect();
|
||||
_ = (x + y);
|
||||
}
|
||||
|
||||
fn function_with_posssible_side_effect() -> i32 {
|
||||
fn function_with_possible_side_effect() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
)";
|
||||
|
||||
Reference in New Issue
Block a user