[tint][ir] Track owning block in BlockParam

This allows transforms to more easily determine suitable insertion
points, and enables the validator to ensure that block parameters are
only used in a single block.

Change-Id: Ia8ede6d86952bbc89be6fe6f09b6c8fb7ea377cf
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/185523
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/block_param.h b/src/tint/lang/core/ir/block_param.h
index 009e022..0b697a2 100644
--- a/src/tint/lang/core/ir/block_param.h
+++ b/src/tint/lang/core/ir/block_param.h
@@ -31,25 +31,42 @@
 #include "src/tint/lang/core/ir/value.h"
 #include "src/tint/utils/rtti/castable.h"
 
+// Forward declarations
+namespace tint::core::ir {
+class MultiInBlock;
+}  // namespace tint::core::ir
+
 namespace tint::core::ir {
 
-/// An instruction in the IR.
+/// A block parameter in the IR.
 class BlockParam : public Castable<BlockParam, Value> {
   public:
     /// Constructor
-    /// @param type the type of the var
+    /// @param type the type of the parameter
     explicit BlockParam(const core::type::Type* type);
     ~BlockParam() override;
 
-    /// @returns the type of the var
+    /// @returns the type of the parameter
     const core::type::Type* Type() const override { return type_; }
 
+    /// Sets the block that this parameter belongs to.
+    /// @param block the block
+    void SetBlock(MultiInBlock* block) { block_ = block; }
+
+    /// @returns the block that this parameter belongs to, or nullptr
+    MultiInBlock* Block() { return block_; }
+
+    /// @returns the block that this parameter belongs to, or nullptr
+    const MultiInBlock* Block() const { return block_; }
+
     /// @copydoc Instruction::Clone()
     BlockParam* Clone(CloneContext& ctx) override;
 
   private:
-    /// the result type of the instruction
+    /// the type of the parameter
     const core::type::Type* type_ = nullptr;
+    /// the block that the parameter belongs to
+    MultiInBlock* block_ = nullptr;
 };
 
 }  // namespace tint::core::ir
diff --git a/src/tint/lang/core/ir/multi_in_block.cc b/src/tint/lang/core/ir/multi_in_block.cc
index c57c622..411d06c 100644
--- a/src/tint/lang/core/ir/multi_in_block.cc
+++ b/src/tint/lang/core/ir/multi_in_block.cc
@@ -55,11 +55,25 @@
 }
 
 void MultiInBlock::SetParams(VectorRef<BlockParam*> params) {
+    for (auto* param : params_) {
+        param->SetBlock(nullptr);
+    }
     params_ = std::move(params);
+    TINT_ASSERT_OR_RETURN(!params_.Any(IsNull));
+    for (auto* param : params_) {
+        param->SetBlock(this);
+    }
 }
 
 void MultiInBlock::SetParams(std::initializer_list<BlockParam*> params) {
+    for (auto* param : params_) {
+        param->SetBlock(nullptr);
+    }
     params_ = std::move(params);
+    TINT_ASSERT_OR_RETURN(!params_.Any(IsNull));
+    for (auto* param : params_) {
+        param->SetBlock(this);
+    }
 }
 
 void MultiInBlock::AddInboundSiblingBranch(ir::Terminator* node) {
diff --git a/src/tint/lang/core/ir/multi_in_block_test.cc b/src/tint/lang/core/ir/multi_in_block_test.cc
index 790a90e..a377c44 100644
--- a/src/tint/lang/core/ir/multi_in_block_test.cc
+++ b/src/tint/lang/core/ir/multi_in_block_test.cc
@@ -54,7 +54,9 @@
     auto* blk = b.MultiInBlock();
     auto* add = b.Add(mod.Types().i32(), 1_i, 2_i);
     blk->Append(add);
-    blk->SetParams({b.BlockParam(mod.Types().i32()), b.BlockParam(mod.Types().f32())});
+    auto* param1 = b.BlockParam(mod.Types().i32());
+    auto* param2 = b.BlockParam(mod.Types().f32());
+    blk->SetParams({param1, param2});
     blk->SetParent(loop);
 
     auto* terminate = b.TerminateInvocation();
@@ -75,6 +77,12 @@
     EXPECT_NE(add, new_blk->Front());
     EXPECT_TRUE(new_blk->Front()->Is<Binary>());
     EXPECT_EQ(BinaryOp::kAdd, new_blk->Front()->As<Binary>()->Op());
+
+    // Check parameter ownership is correct.
+    EXPECT_EQ(param1->Block(), blk);
+    EXPECT_EQ(param2->Block(), blk);
+    EXPECT_EQ(new_blk->Params()[0]->Block(), new_blk);
+    EXPECT_EQ(new_blk->Params()[1]->Block(), new_blk);
 }
 
 TEST_F(IR_MultiInBlockTest, CloneEmpty) {
@@ -86,5 +94,23 @@
     EXPECT_EQ(0u, new_blk->Params().Length());
 }
 
+TEST_F(IR_MultiInBlockTest, Parameters) {
+    auto* blk = b.MultiInBlock();
+
+    auto* param1 = b.BlockParam("a", mod.Types().i32());
+    auto* param2 = b.BlockParam("b", mod.Types().f32());
+    auto* param3 = b.BlockParam("b", mod.Types().f32());
+
+    blk->SetParams({param1, param2});
+    EXPECT_EQ(param1->Block(), blk);
+    EXPECT_EQ(param2->Block(), blk);
+    EXPECT_EQ(param3->Block(), nullptr);
+
+    blk->SetParams({param1, param3});
+    EXPECT_EQ(param1->Block(), blk);
+    EXPECT_EQ(param2->Block(), nullptr);
+    EXPECT_EQ(param3->Block(), blk);
+}
+
 }  // namespace
 }  // namespace tint::core::ir