Import Tint changes from Dawn
Changes:
- 65edd3d23ee5a81b004bd6069fae69405bb4c14c Run SubstituteOverrides in fuzzers if needed. by dan sinclair <dsinclair@chromium.org>
- b7da8f612ec06f06563502d29560dc09b9f5b311 Fix out-of-bounds access in regex fuzzer by Alastair Donaldson <afdx@google.com>
- e14a27bc7f0492db0c75aa14c392004f2afb4bab tint: remove LoopToForLoop and FoldTrivialSingleUseLets t... by Antonio Maiorano <amaiorano@google.com>
GitOrigin-RevId: 65edd3d23ee5a81b004bd6069fae69405bb4c14c
Change-Id: I9db5a912ebdddfa1dbe607fd6d148785bf097976
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/102980
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/include/tint/tint.h b/include/tint/tint.h
index a6ab6ca..c7f8a8c 100644
--- a/include/tint/tint.h
+++ b/include/tint/tint.h
@@ -25,7 +25,6 @@
#include "src/tint/sem/type_manager.h"
#include "src/tint/transform/binding_remapper.h"
#include "src/tint/transform/first_index_offset.h"
-#include "src/tint/transform/fold_trivial_single_use_lets.h"
#include "src/tint/transform/manager.h"
#include "src/tint/transform/multiplanar_external_texture.h"
#include "src/tint/transform/renamer.h"
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 6f2d21d..7fcc8c1 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -498,14 +498,10 @@
"transform/expand_compound_assignment.h",
"transform/first_index_offset.cc",
"transform/first_index_offset.h",
- "transform/fold_trivial_single_use_lets.cc",
- "transform/fold_trivial_single_use_lets.h",
"transform/for_loop_to_loop.cc",
"transform/for_loop_to_loop.h",
"transform/localize_struct_array_assignment.cc",
"transform/localize_struct_array_assignment.h",
- "transform/loop_to_for_loop.cc",
- "transform/loop_to_for_loop.h",
"transform/manager.cc",
"transform/manager.h",
"transform/module_scope_var_to_entry_point_param.cc",
@@ -1202,10 +1198,8 @@
"transform/disable_uniformity_analysis_test.cc",
"transform/expand_compound_assignment_test.cc",
"transform/first_index_offset_test.cc",
- "transform/fold_trivial_single_use_lets_test.cc",
"transform/for_loop_to_loop_test.cc",
"transform/localize_struct_array_assignment_test.cc",
- "transform/loop_to_for_loop_test.cc",
"transform/module_scope_var_to_entry_point_param_test.cc",
"transform/multiplanar_external_texture_test.cc",
"transform/num_workgroups_from_uniform_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 545ff86..bcb4d4d 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -410,14 +410,10 @@
transform/expand_compound_assignment.h
transform/first_index_offset.cc
transform/first_index_offset.h
- transform/fold_trivial_single_use_lets.cc
- transform/fold_trivial_single_use_lets.h
transform/for_loop_to_loop.cc
transform/for_loop_to_loop.h
transform/localize_struct_array_assignment.cc
transform/localize_struct_array_assignment.h
- transform/loop_to_for_loop.cc
- transform/loop_to_for_loop.h
transform/manager.cc
transform/manager.h
transform/module_scope_var_to_entry_point_param.cc
@@ -1117,11 +1113,9 @@
transform/disable_uniformity_analysis_test.cc
transform/expand_compound_assignment_test.cc
transform/first_index_offset_test.cc
- transform/fold_trivial_single_use_lets_test.cc
transform/for_loop_to_loop_test.cc
transform/expand_compound_assignment.cc
transform/localize_struct_array_assignment_test.cc
- transform/loop_to_for_loop_test.cc
transform/module_scope_var_to_entry_point_param_test.cc
transform/multiplanar_external_texture_test.cc
transform/num_workgroups_from_uniform_test.cc
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index 6f12348..7927869 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -1025,11 +1025,6 @@
m.Add<tint::transform::FirstIndexOffset>();
return true;
}},
- {"fold_trivial_single_use_lets",
- [](tint::inspector::Inspector&, tint::transform::Manager& m, tint::transform::DataMap&) {
- m.Add<tint::transform::FoldTrivialSingleUseLets>();
- return true;
- }},
{"renamer",
[](tint::inspector::Inspector&, tint::transform::Manager& m, tint::transform::DataMap&) {
m.Add<tint::transform::Renamer>();
diff --git a/src/tint/fuzzers/tint_common_fuzzer.cc b/src/tint/fuzzers/tint_common_fuzzer.cc
index f547d55..9e12a12 100644
--- a/src/tint/fuzzers/tint_common_fuzzer.cc
+++ b/src/tint/fuzzers/tint_common_fuzzer.cc
@@ -197,8 +197,7 @@
RunInspector(&program);
diagnostics_ = program.Diagnostics();
- if (transform_manager_) {
- auto out = transform_manager_->Run(&program, *transform_inputs_);
+ auto validate_program = [&](auto& out) {
if (!out.program.IsValid()) {
// Transforms can produce error messages for bad input.
// Catch ICEs and errors from non transform systems.
@@ -214,6 +213,45 @@
program = std::move(out.program);
RunInspector(&program);
+
+ return 1;
+ };
+
+ if (transform_manager_) {
+ auto out = transform_manager_->Run(&program, *transform_inputs_);
+ if (!validate_program(out)) {
+ return 0;
+ }
+ }
+
+ {
+ // Run SubstituteOverride if required
+
+ transform::SubstituteOverride::Config cfg;
+ inspector::Inspector inspector(&program);
+ auto default_values = inspector.GetOverrideDefaultValues();
+ for (const auto& [override_id, scalar] : default_values) {
+ // If the override is not null, then it has a default value, we can just let it use the
+ // provided default instead of overriding.
+ if (!scalar.IsNull()) {
+ continue;
+ }
+
+ cfg.map.insert({override_id, 0.0});
+ }
+
+ if (!cfg.map.empty()) {
+ transform::DataMap override_data;
+ override_data.Add<transform::SubstituteOverride::Config>(cfg);
+
+ transform::Manager mgr;
+ mgr.append(std::make_unique<transform::SubstituteOverride>());
+
+ auto out = mgr.Run(&program, override_data);
+ if (!validate_program(out)) {
+ return 0;
+ }
+ }
}
switch (output_) {
diff --git a/src/tint/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc b/src/tint/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc
index fb1d508..b8e3288 100644
--- a/src/tint/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc
+++ b/src/tint/fuzzers/tint_regex_fuzzer/regex_fuzzer_tests.cc
@@ -545,6 +545,14 @@
}
}
+TEST(TestReplaceOperator, TestFindOperatorOccurrenceOnSmallStrings) {
+ RandomGenerator generator(0);
+ WgslMutatorTest mutator(generator);
+ ASSERT_FALSE(mutator.FindOperatorOccurrence("", 0).has_value());
+ ASSERT_FALSE(mutator.FindOperatorOccurrence(" ", 0).has_value());
+ ASSERT_FALSE(mutator.FindOperatorOccurrence(" ", 0).has_value());
+}
+
TEST(TestInsertBreakOrContinue, TestLoopPositions1) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
diff --git a/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc b/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
index 46db837..c5125a4 100644
--- a/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
+++ b/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
@@ -463,9 +463,9 @@
// case where search has reached the end of the code string.
char first_character = wgsl_code[current_index];
char second_character =
- current_index == wgsl_code.size() - 1 ? '\0' : wgsl_code[current_index + 1];
+ current_index + 1 == wgsl_code.size() ? '\0' : wgsl_code[current_index + 1];
char third_character =
- current_index >= wgsl_code.size() - 2 ? '\0' : wgsl_code[current_index + 2];
+ current_index + 2 >= wgsl_code.size() ? '\0' : wgsl_code[current_index + 2];
// This uses the extracted characters to match for the various WGSL operators.
switch (first_character) {
diff --git a/src/tint/transform/fold_trivial_single_use_lets.cc b/src/tint/transform/fold_trivial_single_use_lets.cc
deleted file mode 100644
index 4d6a2a5..0000000
--- a/src/tint/transform/fold_trivial_single_use_lets.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/tint/transform/fold_trivial_single_use_lets.h"
-
-#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"
-
-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) {
- return nullptr;
- }
- auto* let = var_decl->variable->As<ast::Let>();
- if (!let) {
- return nullptr;
- }
- auto* ctor = let->constructor;
- if (!IsAnyOf<ast::IdentifierExpression, ast::LiteralExpression>(ctor)) {
- return nullptr;
- }
- return var_decl;
-}
-
-} // namespace
-
-FoldTrivialSingleUseLets::FoldTrivialSingleUseLets() = default;
-
-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);
- auto& users = sem_let->Users();
- if (users.size() != 1) {
- continue; // Does not have a single user.
- }
-
- 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);
- 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
- // let, and isn't a let itself.
- break;
- }
- }
- }
- }
- }
- }
-
- ctx.Clone();
-}
-
-} // namespace tint::transform
diff --git a/src/tint/transform/fold_trivial_single_use_lets.h b/src/tint/transform/fold_trivial_single_use_lets.h
deleted file mode 100644
index 4036f02..0000000
--- a/src/tint/transform/fold_trivial_single_use_lets.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_TINT_TRANSFORM_FOLD_TRIVIAL_SINGLE_USE_LETS_H_
-#define SRC_TINT_TRANSFORM_FOLD_TRIVIAL_SINGLE_USE_LETS_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/transform/transform.h"
-
-namespace tint::transform {
-
-/// FoldTrivialSingleUseLets is an optimizer for folding away trivial `let`
-/// statements into their single place of use. This transform is intended to
-/// clean up the SSA `let`s produced by the SPIR-V reader.
-/// `let`s can only be folded if:
-/// * There is a single usage of the `let` value.
-/// * The `let` is constructed with a ScalarConstructorExpression, or with an
-/// IdentifierExpression.
-/// * There are only other foldable `let`s between the `let` declaration and its
-/// single usage.
-/// These rules prevent any hoisting of the let that may affect execution
-/// behaviour.
-class FoldTrivialSingleUseLets final : public Castable<FoldTrivialSingleUseLets, Transform> {
- public:
- /// Constructor
- FoldTrivialSingleUseLets();
-
- /// Destructor
- ~FoldTrivialSingleUseLets() override;
-
- protected:
- /// Runs the transform using the CloneContext built for transforming a
- /// program. Run() is responsible for calling Clone() on the CloneContext.
- /// @param ctx the CloneContext primed with the input program and
- /// ProgramBuilder
- /// @param inputs optional extra transform-specific input data
- /// @param outputs optional extra transform-specific output data
- void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
-};
-
-} // namespace tint::transform
-
-#endif // SRC_TINT_TRANSFORM_FOLD_TRIVIAL_SINGLE_USE_LETS_H_
diff --git a/src/tint/transform/fold_trivial_single_use_lets_test.cc b/src/tint/transform/fold_trivial_single_use_lets_test.cc
deleted file mode 100644
index 80e9841..0000000
--- a/src/tint/transform/fold_trivial_single_use_lets_test.cc
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/tint/transform/fold_trivial_single_use_lets.h"
-
-#include "src/tint/transform/test_helper.h"
-
-namespace tint::transform {
-namespace {
-
-using FoldTrivialSingleUseLetsTest = TransformTest;
-
-TEST_F(FoldTrivialSingleUseLetsTest, EmptyModule) {
- auto* src = "";
- auto* expect = "";
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-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;
- _ = x;
-}
-)";
-
- auto* expect = R"(
-fn f() {
- _ = i32(1);
-}
-)";
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-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;
- let y = 2;
- let z = 3;
- _ = x + y + z;
-}
-)";
-
- auto* expect = R"(
-fn f() {
- _ = ((i32(1) + i32(2)) + i32(3));
-}
-)";
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-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;
- let y = x;
- let z = y;
- _ = z;
-}
-)";
-
- auto* expect = R"(
-fn f() {
- _ = i32(1);
-}
-)";
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldTrivialSingleUseLetsTest, NoFold_NonTrivialLet) {
- auto* src = R"(
-fn function_with_possible_side_effect() -> i32 {
- return 1;
-}
-
-fn f() {
- let x = 1;
- let y = function_with_possible_side_effect();
- _ = (x + y);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldTrivialSingleUseLetsTest, NoFold_NonTrivialLet_OutOfOrder) {
- auto* src = R"(
-fn f() {
- let x = 1;
- let y = function_with_possible_side_effect();
- _ = (x + y);
-}
-
-fn function_with_possible_side_effect() -> i32 {
- return 1;
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldTrivialSingleUseLetsTest, NoFold_UseInSubBlock) {
- auto* src = R"(
-fn f() {
- let x = 1;
- {
- _ = x;
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldTrivialSingleUseLetsTest, NoFold_MultipleUses) {
- auto* src = R"(
-fn f() {
- let x = 1;
- _ = (x + x);
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldTrivialSingleUseLetsTest, NoFold_Shadowing) {
- auto* src = R"(
-fn f() {
- var y = 1;
- let x = y;
- {
- let y = false;
- _ = (x + x);
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<FoldTrivialSingleUseLets>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::transform
diff --git a/src/tint/transform/loop_to_for_loop.cc b/src/tint/transform/loop_to_for_loop.cc
deleted file mode 100644
index 00bf5ec..0000000
--- a/src/tint/transform/loop_to_for_loop.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/tint/transform/loop_to_for_loop.h"
-
-#include "src/tint/ast/break_statement.h"
-#include "src/tint/ast/for_loop_statement.h"
-#include "src/tint/program_builder.h"
-#include "src/tint/sem/block_statement.h"
-#include "src/tint/sem/function.h"
-#include "src/tint/sem/statement.h"
-#include "src/tint/sem/variable.h"
-#include "src/tint/utils/scoped_assignment.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::transform::LoopToForLoop);
-
-namespace tint::transform {
-namespace {
-
-bool IsBlockWithSingleBreak(const ast::BlockStatement* block) {
- if (block->statements.Length() != 1) {
- return false;
- }
- return block->statements[0]->Is<ast::BreakStatement>();
-}
-
-bool IsVarUsedByStmt(const sem::Info& sem, const ast::Variable* var, const ast::Statement* stmt) {
- auto* var_sem = sem.Get(var);
- for (auto* user : var_sem->Users()) {
- if (auto* s = user->Stmt()) {
- if (s->Declaration() == stmt) {
- return true;
- }
- }
- }
- return false;
-}
-
-} // namespace
-
-LoopToForLoop::LoopToForLoop() = default;
-
-LoopToForLoop::~LoopToForLoop() = default;
-
-bool LoopToForLoop::ShouldRun(const Program* program, const DataMap&) const {
- for (auto* node : program->ASTNodes().Objects()) {
- if (node->Is<ast::LoopStatement>()) {
- return true;
- }
- }
- return false;
-}
-
-void LoopToForLoop::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
- ctx.ReplaceAll([&](const ast::LoopStatement* loop) -> const ast::Statement* {
- // For loop condition is taken from the first statement in the loop.
- // This requires an if-statement with either:
- // * A true block with no else statements, and the true block contains a
- // single 'break' statement.
- // * An empty true block with a single, no-condition else statement
- // containing a single 'break' statement.
- // Examples:
- // loop { if (condition) { break; } ... }
- // loop { if (condition) {} else { break; } ... }
- auto& stmts = loop->body->statements;
- if (stmts.IsEmpty()) {
- return nullptr;
- }
- auto* if_stmt = stmts[0]->As<ast::IfStatement>();
- if (!if_stmt) {
- return nullptr;
- }
- auto* else_stmt = tint::As<ast::BlockStatement>(if_stmt->else_statement);
-
- bool negate_condition = false;
- if (IsBlockWithSingleBreak(if_stmt->body) && if_stmt->else_statement == nullptr) {
- negate_condition = true;
- } else if (if_stmt->body->Empty() && else_stmt && IsBlockWithSingleBreak(else_stmt)) {
- negate_condition = false;
- } else {
- return nullptr;
- }
-
- // The continuing block must be empty or contain a single, assignment or
- // function call statement.
- const ast::Statement* continuing = nullptr;
- if (auto* loop_cont = loop->continuing) {
- if (loop_cont->statements.Length() != 1) {
- return nullptr;
- }
-
- continuing = loop_cont->statements[0];
- if (!continuing->IsAnyOf<ast::AssignmentStatement, ast::CallStatement>()) {
- return nullptr;
- }
-
- // And the continuing statement must not use any of the variables declared
- // in the loop body.
- for (auto* stmt : loop->body->statements) {
- if (auto* var_decl = stmt->As<ast::VariableDeclStatement>()) {
- if (IsVarUsedByStmt(ctx.src->Sem(), var_decl->variable, continuing)) {
- return nullptr;
- }
- }
- }
-
- continuing = ctx.Clone(continuing);
- }
-
- auto* condition = ctx.Clone(if_stmt->condition);
- if (negate_condition) {
- condition = ctx.dst->create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, condition);
- }
-
- ast::Statement* initializer = nullptr;
-
- ctx.Remove(loop->body->statements, if_stmt);
- auto* body = ctx.Clone(loop->body);
- return ctx.dst->create<ast::ForLoopStatement>(initializer, condition, continuing, body);
- });
-
- ctx.Clone();
-}
-
-} // namespace tint::transform
diff --git a/src/tint/transform/loop_to_for_loop.h b/src/tint/transform/loop_to_for_loop.h
deleted file mode 100644
index 0e948c8..0000000
--- a/src/tint/transform/loop_to_for_loop.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_TINT_TRANSFORM_LOOP_TO_FOR_LOOP_H_
-#define SRC_TINT_TRANSFORM_LOOP_TO_FOR_LOOP_H_
-
-#include "src/tint/transform/transform.h"
-
-namespace tint::transform {
-
-/// LoopToForLoop is a Transform that attempts to convert WGSL `loop {}`
-/// statements into a for-loop statement.
-class LoopToForLoop final : public Castable<LoopToForLoop, Transform> {
- public:
- /// Constructor
- LoopToForLoop();
-
- /// Destructor
- ~LoopToForLoop() override;
-
- /// @param program the program to inspect
- /// @param data optional extra transform-specific input data
- /// @returns true if this transform should be run for the given program
- bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
- protected:
- /// Runs the transform using the CloneContext built for transforming a
- /// program. Run() is responsible for calling Clone() on the CloneContext.
- /// @param ctx the CloneContext primed with the input program and
- /// ProgramBuilder
- /// @param inputs optional extra transform-specific input data
- /// @param outputs optional extra transform-specific output data
- void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
-};
-
-} // namespace tint::transform
-
-#endif // SRC_TINT_TRANSFORM_LOOP_TO_FOR_LOOP_H_
diff --git a/src/tint/transform/loop_to_for_loop_test.cc b/src/tint/transform/loop_to_for_loop_test.cc
deleted file mode 100644
index 24e0e15..0000000
--- a/src/tint/transform/loop_to_for_loop_test.cc
+++ /dev/null
@@ -1,358 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/tint/transform/loop_to_for_loop.h"
-
-#include "src/tint/transform/test_helper.h"
-
-namespace tint::transform {
-namespace {
-
-using LoopToForLoopTest = TransformTest;
-
-TEST_F(LoopToForLoopTest, ShouldRunEmptyModule) {
- auto* src = R"()";
-
- EXPECT_FALSE(ShouldRun<LoopToForLoop>(src));
-}
-
-TEST_F(LoopToForLoopTest, ShouldRunHasForLoop) {
- auto* src = R"(
-fn f() {
- loop {
- break;
- }
-}
-)";
-
- EXPECT_TRUE(ShouldRun<LoopToForLoop>(src));
-}
-
-TEST_F(LoopToForLoopTest, EmptyModule) {
- auto* src = "";
- auto* expect = "";
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, IfBreak) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if (i > 15) {
- break;
- }
-
- _ = 123;
-
- continuing {
- i = i + 1;
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var i : i32;
- i = 0;
- for(; !((i > 15)); i = (i + 1)) {
- _ = 123;
- }
-}
-)";
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, IfElseBreak) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if (i < 15) {
- } else {
- break;
- }
-
- _ = 123;
-
- continuing {
- i = i + 1;
- }
- }
-}
-)";
-
- auto* expect = R"(
-fn f() {
- var i : i32;
- i = 0;
- for(; (i < 15); i = (i + 1)) {
- _ = 123;
- }
-}
-)";
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, Nested) {
- auto* src = R"(
-let N = 16u;
-
-fn f() {
- var i : u32 = 0u;
- loop {
- if (i >= N) {
- break;
- }
- {
- var j : u32 = 0u;
- loop {
- if (j >= N) {
- break;
- }
-
- _ = i;
- _ = j;
-
- continuing {
- j = (j + 1u);
- }
- }
- }
-
- continuing {
- i = (i + 1u);
- }
- }
-}
-)";
-
- auto* expect = R"(
-const N = 16u;
-
-fn f() {
- var i : u32 = 0u;
- for(; !((i >= N)); i = (i + 1u)) {
- {
- var j : u32 = 0u;
- for(; !((j >= N)); j = (j + 1u)) {
- _ = i;
- _ = j;
- }
- }
- }
-}
-)";
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, NoTransform_IfMultipleStmts) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if ((i < 15)) {
- _ = i;
- break;
- }
- _ = 123;
-
- continuing {
- i = (i + 1);
- }
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, NoTransform_IfElseMultipleStmts) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if ((i < 15)) {
- } else {
- _ = i;
- break;
- }
- _ = 123;
-
- continuing {
- i = (i + 1);
- }
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, NoTransform_ContinuingIsCompound) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if ((i < 15)) {
- break;
- }
- _ = 123;
-
- continuing {
- if (false) {
- }
- }
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, NoTransform_ContinuingMultipleStmts) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if ((i < 15)) {
- break;
- }
- _ = 123;
-
- continuing {
- i = (i + 1);
- _ = i;
- }
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, NoTransform_ContinuingUsesVarDeclInLoopBody) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if ((i < 15)) {
- break;
- }
- var j : i32;
-
- continuing {
- i = (i + j);
- }
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, NoTransform_IfBreakWithElse) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if ((i > 15)) {
- break;
- } else {
- }
- _ = 123;
-
- continuing {
- i = (i + 1);
- }
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LoopToForLoopTest, NoTransform_IfBreakWithElseIf) {
- auto* src = R"(
-fn f() {
- var i : i32;
- i = 0;
- loop {
- if ((i > 15)) {
- break;
- } else if (true) {
- }
- _ = 123;
-
- continuing {
- i = (i + 1);
- }
- }
-}
-)";
-
- auto* expect = src;
-
- auto got = Run<LoopToForLoop>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-} // namespace
-} // namespace tint::transform
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index d051981..e332495 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -55,8 +55,6 @@
#include "src/tint/transform/decompose_memory_access.h"
#include "src/tint/transform/disable_uniformity_analysis.h"
#include "src/tint/transform/expand_compound_assignment.h"
-#include "src/tint/transform/fold_trivial_single_use_lets.h"
-#include "src/tint/transform/loop_to_for_loop.h"
#include "src/tint/transform/manager.h"
#include "src/tint/transform/pad_structs.h"
#include "src/tint/transform/promote_initializers_to_let.h"
@@ -208,11 +206,6 @@
/* preserve_unicode */ false);
manager.Add<transform::Unshadow>();
- // Attempt to convert `loop`s into for-loops. This is to try and massage the
- // output into something that will not cause FXC to choke or misbehave.
- manager.Add<transform::FoldTrivialSingleUseLets>();
- manager.Add<transform::LoopToForLoop>();
-
if (!options.disable_workgroup_init) {
// ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
// ZeroInitWorkgroupMemory may inject new builtin parameters.
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 4678065..3186955 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -55,9 +55,7 @@
#include "src/tint/transform/decompose_memory_access.h"
#include "src/tint/transform/disable_uniformity_analysis.h"
#include "src/tint/transform/expand_compound_assignment.h"
-#include "src/tint/transform/fold_trivial_single_use_lets.h"
#include "src/tint/transform/localize_struct_array_assignment.h"
-#include "src/tint/transform/loop_to_for_loop.h"
#include "src/tint/transform/manager.h"
#include "src/tint/transform/num_workgroups_from_uniform.h"
#include "src/tint/transform/promote_initializers_to_let.h"
@@ -206,11 +204,6 @@
manager.Add<transform::SimplifyPointers>();
manager.Add<transform::LocalizeStructArrayAssignment>();
- // Attempt to convert `loop`s into for-loops. This is to try and massage the
- // output into something that will not cause FXC to choke or misbehave.
- manager.Add<transform::FoldTrivialSingleUseLets>();
- manager.Add<transform::LoopToForLoop>();
-
if (!options.disable_workgroup_init) {
// ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
// ZeroInitWorkgroupMemory may inject new builtin parameters.