[ir] Fix ValueToLet for store-before-load

Function calls that may store were sometimes being inlined over load
instructions. Track the fact that there are accesses pending
resolution that contain stores to fix this.

Change-Id: Idba741e83f0502a897e71ee8966e01452ca5d3f0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/191660
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/transform/value_to_let.cc b/src/tint/lang/core/ir/transform/value_to_let.cc
index ac51672..ec25a39 100644
--- a/src/tint/lang/core/ir/transform/value_to_let.cc
+++ b/src/tint/lang/core/ir/transform/value_to_let.cc
@@ -141,6 +141,7 @@
 
             if (accesses.Contains(Access::kStore)) {  // Note: Also handles load + store
                 put_pending_in_lets();
+                pending_access = Access::kStore;
                 maybe_put_in_let(inst);
             } else if (accesses.Contains(Access::kLoad)) {
                 if (pending_access != Access::kLoad) {
diff --git a/src/tint/lang/core/ir/transform/value_to_let_test.cc b/src/tint/lang/core/ir/transform/value_to_let_test.cc
index 46e1c12..d88039e 100644
--- a/src/tint/lang/core/ir/transform/value_to_let_test.cc
+++ b/src/tint/lang/core/ir/transform/value_to_let_test.cc
@@ -29,6 +29,7 @@
 
 #include <utility>
 
+#include "gtest/gtest.h"
 #include "src/tint/lang/core/ir/transform/helper_test.h"
 
 namespace tint::core::ir::transform {
@@ -45,7 +46,7 @@
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, NoModify_Blah) {
@@ -59,13 +60,13 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = src;
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, NoModify_Unsequenced) {
@@ -88,13 +89,13 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = src;
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 TEST_F(IR_ValueToLetTest, NoModify_SequencedValueUsedWithNonSequenced) {
     auto* i = b.Var<private_, i32>("i");
@@ -139,13 +140,13 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = src;
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, NoModify_Inlinable_NestedCalls) {
@@ -192,13 +193,13 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = src;
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, NoModify_LetUsedTwice) {
@@ -245,13 +246,13 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = src;
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, NoModify_VarUsedTwice) {
@@ -287,13 +288,13 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = src;
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, VarLoadUsedTwice) {
@@ -314,7 +315,7 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = R"(
 %F = func():i32 {
@@ -330,7 +331,7 @@
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, VarLoad_ThenStore_ThenUse) {
@@ -352,7 +353,7 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = R"(
 %F = func():i32 {
@@ -368,7 +369,141 @@
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
+}
+
+TEST_F(IR_ValueToLetTest, Call_ThenLoad_ThenUseCallBeforeLoad) {
+    auto* v = b.Var<private_, i32>("v");
+    mod.root_block->Append(v);
+
+    auto* foo = b.Function("foo", ty.i32());
+    b.Append(foo->Block(), [&] {
+        b.Store(v, 42_i);
+        b.Return(foo, 1_i);
+    });
+
+    auto* fn = b.Function("F", ty.i32());
+    b.Append(fn->Block(), [&] {
+        auto* c = b.Call<i32>(foo);
+        auto* l = b.Name("l", b.Load(v));
+        auto* add = b.Name("add", b.Add<i32>(c, l));
+        b.Return(fn, add);
+    });
+
+    auto* src = R"(
+$B1: {  # root
+  %v:ptr<private, i32, read_write> = var
+}
+
+%foo = func():i32 {
+  $B2: {
+    store %v, 42i
+    ret 1i
+  }
+}
+%F = func():i32 {
+  $B3: {
+    %4:i32 = call %foo
+    %l:i32 = load %v
+    %add:i32 = add %4, %l
+    ret %add
+  }
+}
+)";
+    EXPECT_EQ(str(), src);
+
+    auto* expect = R"(
+$B1: {  # root
+  %v:ptr<private, i32, read_write> = var
+}
+
+%foo = func():i32 {
+  $B2: {
+    store %v, 42i
+    ret 1i
+  }
+}
+%F = func():i32 {
+  $B3: {
+    %4:i32 = call %foo
+    %5:i32 = let %4
+    %l:i32 = load %v
+    %add:i32 = add %5, %l
+    ret %add
+  }
+}
+)";
+
+    Run(ValueToLet);
+
+    EXPECT_EQ(str(), expect);
+}
+
+TEST_F(IR_ValueToLetTest, Call_ThenLoad_ThenUseLoadBeforeCall) {
+    auto* v = b.Var<private_, i32>("v");
+    mod.root_block->Append(v);
+
+    auto* foo = b.Function("foo", ty.i32());
+    b.Append(foo->Block(), [&] {
+        b.Store(v, 42_i);
+        b.Return(foo, 1_i);
+    });
+
+    auto* fn = b.Function("F", ty.i32());
+    b.Append(fn->Block(), [&] {
+        auto* c = b.Call<i32>(foo);
+        auto* l = b.Name("l", b.Load(v));
+        auto* add = b.Name("add", b.Add<i32>(l, c));
+        b.Return(fn, add);
+    });
+
+    auto* src = R"(
+$B1: {  # root
+  %v:ptr<private, i32, read_write> = var
+}
+
+%foo = func():i32 {
+  $B2: {
+    store %v, 42i
+    ret 1i
+  }
+}
+%F = func():i32 {
+  $B3: {
+    %4:i32 = call %foo
+    %l:i32 = load %v
+    %add:i32 = add %l, %4
+    ret %add
+  }
+}
+)";
+    EXPECT_EQ(str(), src);
+
+    auto* expect = R"(
+$B1: {  # root
+  %v:ptr<private, i32, read_write> = var
+}
+
+%foo = func():i32 {
+  $B2: {
+    store %v, 42i
+    ret 1i
+  }
+}
+%F = func():i32 {
+  $B3: {
+    %4:i32 = call %foo
+    %5:i32 = let %4
+    %l:i32 = load %v
+    %add:i32 = add %l, %5
+    ret %add
+  }
+}
+)";
+
+    Run(ValueToLet);
+
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, TwoCalls_ThenUseReturnValues) {
@@ -415,7 +550,7 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = R"(
 $B1: {  # root
@@ -444,11 +579,11 @@
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 
     Run(ValueToLet);  // running a second time should be no-op
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, SequencedUsedInDifferentBlock) {
@@ -500,7 +635,7 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = R"(
 $B1: {  # root
@@ -532,11 +667,11 @@
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 
     Run(ValueToLet);  // running a second time should be no-op
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, NameMe1) {
@@ -560,7 +695,7 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = R"(
 %F = func():i32 {
@@ -577,11 +712,11 @@
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 
     Run(ValueToLet);  // running a second time should be no-op
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 
 TEST_F(IR_ValueToLetTest, NameMe2) {
@@ -609,14 +744,15 @@
   }
 }
 )";
-    EXPECT_EQ(src, str());
+    EXPECT_EQ(str(), src);
 
     auto* expect = R"(
 %F = func():void {
   $B1: {
     %i:i32 = max 1i, 2i
     %v:ptr<function, i32, read_write> = var, %i
-    %x:i32 = max 3i, 4i
+    %4:i32 = max 3i, 4i
+    %x:i32 = let %4
     %y:i32 = load %v
     %z:i32 = add %y, %x
     store %v, %z
@@ -627,11 +763,11 @@
 
     Run(ValueToLet);
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 
     Run(ValueToLet);  // running a second time should be no-op
 
-    EXPECT_EQ(expect, str());
+    EXPECT_EQ(str(), expect);
 }
 }  // namespace
 }  // namespace tint::core::ir::transform
diff --git a/test/tint/bug/tint/1118.wgsl.expected.ir.msl b/test/tint/bug/tint/1118.wgsl.expected.ir.msl
index 82729f0a..984cfe6 100644
--- a/test/tint/bug/tint/1118.wgsl.expected.ir.msl
+++ b/test/tint/bug/tint/1118.wgsl.expected.ir.msl
@@ -114,10 +114,11 @@
   (*tint_module_vars.fClipDistance3) = fClipDistance3_param;
   (*tint_module_vars.fClipDistance4) = fClipDistance4_param;
   main_1(tint_module_vars);
+  main_out const v_3 = main_out{.glFragColor_1=(*tint_module_vars.glFragColor)};
   if (!((*tint_module_vars.continue_execution))) {
     discard_fragment();
   }
-  return main_out{.glFragColor_1=(*tint_module_vars.glFragColor)};
+  return v_3;
 }
 fragment tint_symbol_outputs tint_symbol(tint_symbol_inputs inputs [[stage_in]], const constant Scene* x_29 [[buffer(0)]], const constant Material* x_49 [[buffer(1)]], const constant Mesh* x_137 [[buffer(2)]]) {
   thread float fClipDistance3 = 0.0f;
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.ir.msl
index d5a8840..bf9fdcb 100644
--- a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.ir.msl
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.ir.msl
@@ -8,5 +8,6 @@
 }
 void b() {
   int b_1 = c(2, 3, 4);
-  b_1 = (b_1 + c(3, 4, 5));
+  int const v = c(3, 4, 5);
+  b_1 = (b_1 + v);
 }
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/no_params_return.wgsl.expected.ir.msl
index bb96547..32aa59d 100644
--- a/test/tint/expressions/user_call/no_params_return.wgsl.expected.ir.msl
+++ b/test/tint/expressions/user_call/no_params_return.wgsl.expected.ir.msl
@@ -8,5 +8,6 @@
 }
 void b() {
   int b_1 = c();
-  b_1 = (b_1 + c());
+  int const v = c();
+  b_1 = (b_1 + v);
 }
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/one_param_return.wgsl.expected.ir.msl
index b98d67f..407a01b 100644
--- a/test/tint/expressions/user_call/one_param_return.wgsl.expected.ir.msl
+++ b/test/tint/expressions/user_call/one_param_return.wgsl.expected.ir.msl
@@ -8,5 +8,6 @@
 }
 void b() {
   int b_1 = c(2);
-  b_1 = (b_1 + c(3));
+  int const v = c(3);
+  b_1 = (b_1 + v);
 }