[ir] Validate store instructions

Bug: tint:1952
Change-Id: I6ca60336c7b0f6cc0c134f5f0c742ac62d12b504
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/152461
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/lang/core/ir/disassembler.cc b/src/tint/lang/core/ir/disassembler.cc
index a7682b3..079fe43 100644
--- a/src/tint/lang/core/ir/disassembler.cc
+++ b/src/tint/lang/core/ir/disassembler.cc
@@ -449,9 +449,9 @@
         [&](Store* s) {
             EmitInstructionName(s);
             out_ << " ";
-            EmitValue(s->To());
+            EmitOperand(s, Store::kToOperandOffset);
             out_ << ", ";
-            EmitValue(s->From());
+            EmitOperand(s, Store::kFromOperandOffset);
         },
         [&](StoreVectorElement* s) {
             EmitInstructionName(s);
diff --git a/src/tint/lang/core/ir/transform/block_decorated_structs_test.cc b/src/tint/lang/core/ir/transform/block_decorated_structs_test.cc
index 4ef9c26..5526360 100644
--- a/src/tint/lang/core/ir/transform/block_decorated_structs_test.cc
+++ b/src/tint/lang/core/ir/transform/block_decorated_structs_test.cc
@@ -249,7 +249,8 @@
     b.RootBlock()->Append(private_var);
 
     auto* func = b.Function("foo", ty.void_());
-    func->Block()->Append(b.Store(buffer, private_var));
+    auto* load = func->Block()->Append(b.Load(private_var));
+    func->Block()->Append(b.Store(buffer, load));
     func->Block()->Append(b.Return(func));
 
     auto* expect = R"(
@@ -269,8 +270,9 @@
 
 %foo = func():void -> %b2 {
   %b2 = block {
-    %4:ptr<storage, MyStruct, read_write> = access %1, 0u
-    store %4, %2
+    %4:MyStruct = load %2
+    %5:ptr<storage, MyStruct, read_write> = access %1, 0u
+    store %5, %4
     ret
   }
 }
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index c2ffd81..b40e9bc 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -246,6 +246,10 @@
     /// @param l the exit loop to validate
     void CheckExitLoop(ExitLoop* l);
 
+    /// Validates the given store
+    /// @param s the store to validate
+    void CheckStore(Store* s);
+
     /// Validates the given load vector element
     /// @param l the load vector element to validate
     void CheckLoadVectorElement(LoadVectorElement* l);
@@ -483,7 +487,7 @@
         [&](Load*) {},                                               //
         [&](LoadVectorElement* l) { CheckLoadVectorElement(l); },    //
         [&](Loop* l) { CheckLoop(l); },                              //
-        [&](Store*) {},                                              //
+        [&](Store* s) { CheckStore(s); },                            //
         [&](StoreVectorElement* s) { CheckStoreVectorElement(s); },  //
         [&](Switch* s) { CheckSwitch(s); },                          //
         [&](Swizzle*) {},                                            //
@@ -794,6 +798,19 @@
     }
 }
 
+void Validator::CheckStore(Store* s) {
+    CheckOperandsNotNull(s, Store::kToOperandOffset, Store::kFromOperandOffset);
+
+    if (auto* from = s->From()) {
+        if (auto* to = s->To()) {
+            if (from->Type() != to->Type()->UnwrapPtr()) {
+                AddError(s, Store::kFromOperandOffset,
+                         "value type does not match pointer element type");
+            }
+        }
+    }
+}
+
 void Validator::CheckLoadVectorElement(LoadVectorElement* l) {
     CheckOperandsNotNull(l,  //
                          LoadVectorElement::kFromOperandOffset,
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 52aad79..777cd01 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -2735,6 +2735,94 @@
 )");
 }
 
+TEST_F(IR_ValidatorTest, Store_NullTo) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        b.Append(mod.instructions.Create<ir::Store>(nullptr, b.Constant(42_i)));
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_FALSE(res);
+    EXPECT_EQ(res.Failure().str(), R"(:3:11 error: store: operand is undefined
+    store undef, 42i
+          ^^^^^
+
+:2:3 note: In block
+  %b1 = block {
+  ^^^^^^^^^^^
+
+note: # Disassembly
+%my_func = func():void -> %b1 {
+  %b1 = block {
+    store undef, 42i
+    ret
+  }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Store_NullFrom) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        auto* var = b.Var(ty.ptr<function, i32>());
+        b.Append(mod.instructions.Create<ir::Store>(var->Result(), nullptr));
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_FALSE(res);
+    EXPECT_EQ(res.Failure().str(), R"(:4:15 error: store: operand is undefined
+    store %2, undef
+              ^^^^^
+
+:2:3 note: In block
+  %b1 = block {
+  ^^^^^^^^^^^
+
+note: # Disassembly
+%my_func = func():void -> %b1 {
+  %b1 = block {
+    %2:ptr<function, i32, read_write> = var
+    store %2, undef
+    ret
+  }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Store_TypeMismatch) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        auto* var = b.Var(ty.ptr<function, i32>());
+        b.Append(mod.instructions.Create<ir::Store>(var->Result(), b.Constant(42_u)));
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_FALSE(res);
+    EXPECT_EQ(res.Failure().str(), R"(:4:15 error: value type does not match pointer element type
+    store %2, 42u
+              ^^^
+
+:2:3 note: In block
+  %b1 = block {
+  ^^^^^^^^^^^
+
+note: # Disassembly
+%my_func = func():void -> %b1 {
+  %b1 = block {
+    %2:ptr<function, i32, read_write> = var
+    store %2, 42u
+    ret
+  }
+}
+)");
+}
+
 TEST_F(IR_ValidatorTest, LoadVectorElement_NullResult) {
     auto* f = b.Function("my_func", ty.void_());
 
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
index a479310..d4d758b 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
@@ -2386,7 +2386,7 @@
 
             b.Append(loop->Initializer(), [&] {
                 auto* load_i = b.Load(i);
-                auto* inc_i = b.Add(ty.i32(), load_i, 1_u);
+                auto* inc_i = b.Add(ty.u32(), load_i, 1_u);
                 b.Store(i, inc_i);
                 b.NextIteration(loop);
             });