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.