[ir] Make Value a pointer stored in the module.

This CL moves the Value class to pointers stored in the module.

Bug: tint:1718
Change-Id: I0441e898c011b34b0fe2f8ca716ea26c9c566bd7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112043
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index 0403807..2b5dea3 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -97,79 +97,79 @@
     return next_value_id++;
 }
 
-Instruction Builder::CreateInstruction(Instruction::Kind kind, Value lhs, Value rhs) {
-    return Instruction(kind, Value(AllocateValue()), lhs, rhs);
+Instruction Builder::CreateInstruction(Instruction::Kind kind, const Value* lhs, const Value* rhs) {
+    return Instruction(kind, MkValue(AllocateValue()), lhs, rhs);
 }
 
-Instruction Builder::And(Value lhs, Value rhs) {
+Instruction Builder::And(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kAnd, lhs, rhs);
 }
 
-Instruction Builder::Or(Value lhs, Value rhs) {
+Instruction Builder::Or(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kOr, lhs, rhs);
 }
 
-Instruction Builder::Xor(Value lhs, Value rhs) {
+Instruction Builder::Xor(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kXor, lhs, rhs);
 }
 
-Instruction Builder::LogicalAnd(Value lhs, Value rhs) {
+Instruction Builder::LogicalAnd(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kLogicalAnd, lhs, rhs);
 }
 
-Instruction Builder::LogicalOr(Value lhs, Value rhs) {
+Instruction Builder::LogicalOr(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kLogicalOr, lhs, rhs);
 }
 
-Instruction Builder::Equal(Value lhs, Value rhs) {
+Instruction Builder::Equal(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kEqual, lhs, rhs);
 }
 
-Instruction Builder::NotEqual(Value lhs, Value rhs) {
+Instruction Builder::NotEqual(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kNotEqual, lhs, rhs);
 }
 
-Instruction Builder::LessThan(Value lhs, Value rhs) {
+Instruction Builder::LessThan(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kLessThan, lhs, rhs);
 }
 
-Instruction Builder::GreaterThan(Value lhs, Value rhs) {
+Instruction Builder::GreaterThan(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kGreaterThan, lhs, rhs);
 }
 
-Instruction Builder::LessThanEqual(Value lhs, Value rhs) {
+Instruction Builder::LessThanEqual(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kLessThanEqual, lhs, rhs);
 }
 
-Instruction Builder::GreaterThanEqual(Value lhs, Value rhs) {
+Instruction Builder::GreaterThanEqual(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kGreaterThanEqual, lhs, rhs);
 }
 
-Instruction Builder::ShiftLeft(Value lhs, Value rhs) {
+Instruction Builder::ShiftLeft(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kShiftLeft, lhs, rhs);
 }
 
-Instruction Builder::ShiftRight(Value lhs, Value rhs) {
+Instruction Builder::ShiftRight(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kShiftRight, lhs, rhs);
 }
 
-Instruction Builder::Add(Value lhs, Value rhs) {
+Instruction Builder::Add(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kAdd, lhs, rhs);
 }
 
-Instruction Builder::Subtract(Value lhs, Value rhs) {
+Instruction Builder::Subtract(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kSubtract, lhs, rhs);
 }
 
-Instruction Builder::Multiply(Value lhs, Value rhs) {
+Instruction Builder::Multiply(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kMultiply, lhs, rhs);
 }
 
-Instruction Builder::Divide(Value lhs, Value rhs) {
+Instruction Builder::Divide(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kDivide, lhs, rhs);
 }
 
-Instruction Builder::Modulo(Value lhs, Value rhs) {
+Instruction Builder::Modulo(const Value* lhs, const Value* rhs) {
     return CreateInstruction(Instruction::Kind::kModulo, lhs, rhs);
 }
 
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index 6776d35..c380980 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -83,120 +83,128 @@
     /// @param to the node to branch too
     void Branch(Block* from, FlowNode* to);
 
+    /// Creates a new Value
+    /// @param val the value
+    /// @returns the new Value
+    template <typename T>
+    const Value* MkValue(T val) {
+        return ir.values.Create<Value>(val);
+    }
+
     /// Creates an op for `lhs kind rhs`
     /// @param kind the kind of operation
     /// @param lhs the left-hand-side of the operation
     /// @param rhs the right-hand-side of the operation
     /// @returns the operation
-    Instruction CreateInstruction(Instruction::Kind kind, Value lhs, Value rhs);
+    Instruction CreateInstruction(Instruction::Kind kind, const Value* lhs, const Value* rhs);
 
     /// Creates an And operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction And(Value lhs, Value rhs);
+    Instruction And(const Value* lhs, const Value* rhs);
 
     /// Creates an Or operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction Or(Value lhs, Value rhs);
+    Instruction Or(const Value* lhs, const Value* rhs);
 
     /// Creates an Xor operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction Xor(Value lhs, Value rhs);
+    Instruction Xor(const Value* lhs, const Value* rhs);
 
     /// Creates an LogicalAnd operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction LogicalAnd(Value lhs, Value rhs);
+    Instruction LogicalAnd(const Value* lhs, const Value* rhs);
 
     /// Creates an LogicalOr operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction LogicalOr(Value lhs, Value rhs);
+    Instruction LogicalOr(const Value* lhs, const Value* rhs);
 
     /// Creates an Equal operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction Equal(Value lhs, Value rhs);
+    Instruction Equal(const Value* lhs, const Value* rhs);
 
     /// Creates an NotEqual operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction NotEqual(Value lhs, Value rhs);
+    Instruction NotEqual(const Value* lhs, const Value* rhs);
 
     /// Creates an LessThan operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction LessThan(Value lhs, Value rhs);
+    Instruction LessThan(const Value* lhs, const Value* rhs);
 
     /// Creates an GreaterThan operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction GreaterThan(Value lhs, Value rhs);
+    Instruction GreaterThan(const Value* lhs, const Value* rhs);
 
     /// Creates an LessThanEqual operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction LessThanEqual(Value lhs, Value rhs);
+    Instruction LessThanEqual(const Value* lhs, const Value* rhs);
 
     /// Creates an GreaterThanEqual operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction GreaterThanEqual(Value lhs, Value rhs);
+    Instruction GreaterThanEqual(const Value* lhs, const Value* rhs);
 
     /// Creates an ShiftLeft operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction ShiftLeft(Value lhs, Value rhs);
+    Instruction ShiftLeft(const Value* lhs, const Value* rhs);
 
     /// Creates an ShiftRight operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction ShiftRight(Value lhs, Value rhs);
+    Instruction ShiftRight(const Value* lhs, const Value* rhs);
 
     /// Creates an Add operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction Add(Value lhs, Value rhs);
+    Instruction Add(const Value* lhs, const Value* rhs);
 
     /// Creates an Subtract operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction Subtract(Value lhs, Value rhs);
+    Instruction Subtract(const Value* lhs, const Value* rhs);
 
     /// Creates an Multiply operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction Multiply(Value lhs, Value rhs);
+    Instruction Multiply(const Value* lhs, const Value* rhs);
 
     /// Creates an Divide operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction Divide(Value lhs, Value rhs);
+    Instruction Divide(const Value* lhs, const Value* rhs);
 
     /// Creates an Modulo operation
     /// @param lhs the lhs of the add
     /// @param rhs the rhs of the add
     /// @returns the operation
-    Instruction Modulo(Value lhs, Value rhs);
+    Instruction Modulo(const Value* lhs, const Value* rhs);
 
     /// @returns a unique Value id
     Value::Id AllocateValue();
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index 3af54f9..36bcad8 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -516,7 +516,7 @@
     return true;
 }
 
-utils::Result<Value> BuilderImpl::EmitExpression(const ast::Expression* expr) {
+utils::Result<const Value*> BuilderImpl::EmitExpression(const ast::Expression* expr) {
     return tint::Switch(
         expr,
         // [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(a); },
@@ -551,7 +551,7 @@
         });
 }
 
-utils::Result<Value> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
+utils::Result<const Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
     auto lhs = EmitExpression(expr->lhs);
     if (!lhs) {
         return utils::Failure;
@@ -623,26 +623,29 @@
             return utils::Failure;
     }
 
-    auto result = instr.Result();
+    auto* result = instr.Result();
     current_flow_block->instructions.Push(instr);
-    return result;
+    return utils::Result<const Value*>(result);
 }
 
-utils::Result<Value> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
+utils::Result<const Value*> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
     return tint::Switch(  //
         lit,
-        [&](const ast::BoolLiteralExpression* l) { return utils::Result<Value>{Value(l->value)}; },
+        [&](const ast::BoolLiteralExpression* l) {
+            return utils::Result<const Value*>(builder.MkValue(l->value));
+        },
         [&](const ast::FloatLiteralExpression* l) {
             if (l->suffix == ast::FloatLiteralExpression::Suffix::kF) {
-                return utils::Result<Value>{Value(f32(static_cast<float>(l->value)))};
+                return utils::Result<const Value*>(
+                    builder.MkValue(f32(static_cast<float>(l->value))));
             }
-            return utils::Result<Value>{Value(f16(static_cast<float>(l->value)))};
+            return utils::Result<const Value*>(builder.MkValue(f16(static_cast<float>(l->value))));
         },
         [&](const ast::IntLiteralExpression* l) {
             if (l->suffix == ast::IntLiteralExpression::Suffix::kI) {
-                return utils::Result<Value>{Value(i32(l->value))};
+                return utils::Result<const Value*>(builder.MkValue(i32(l->value)));
             }
-            return utils::Result<Value>{Value(u32(l->value))};
+            return utils::Result<const Value*>(builder.MkValue(u32(l->value)));
         },
         [&](Default) {
             diagnostics_.add_warning(tint::diag::System::IR,
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index 60eda73..5090be7 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -140,7 +140,7 @@
     /// Emits an expression
     /// @param expr the expression to emit
     /// @returns true if successful, false otherwise
-    utils::Result<Value> EmitExpression(const ast::Expression* expr);
+    utils::Result<const Value*> EmitExpression(const ast::Expression* expr);
 
     /// Emits a variable
     /// @param var the variable to emit
@@ -150,12 +150,12 @@
     /// Emits a binary expression
     /// @param expr the binary expression
     /// @returns the value storing the result if successful, utils::Failure otherwise
-    utils::Result<Value> EmitBinary(const ast::BinaryExpression* expr);
+    utils::Result<const Value*> EmitBinary(const ast::BinaryExpression* expr);
 
     /// Emits a literal expression
     /// @param lit the literal to emit
     /// @returns true if successful, false otherwise
-    utils::Result<Value> EmitLiteral(const ast::LiteralExpression* lit);
+    utils::Result<const Value*> EmitLiteral(const ast::LiteralExpression* lit);
 
     /// Emits a type
     /// @param ty the type to emit
diff --git a/src/tint/ir/builder_impl_test.cc b/src/tint/ir/builder_impl_test.cc
index 6c8e496..edfe7be 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -101,9 +101,9 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 
     // Check condition
-    auto instr = flow->condition;
-    ASSERT_TRUE(instr.IsBool());
-    EXPECT_TRUE(instr.AsBool());
+    auto* instr = flow->condition;
+    ASSERT_TRUE(instr->IsBool());
+    EXPECT_TRUE(instr->AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
@@ -502,9 +502,9 @@
     EXPECT_EQ(loop_flow->merge_target->branch_target, nullptr);
 
     // Check condition
-    auto instr = if_flow->condition;
-    ASSERT_TRUE(instr.IsBool());
-    EXPECT_TRUE(instr.AsBool());
+    auto* instr = if_flow->condition;
+    ASSERT_TRUE(instr->IsBool());
+    EXPECT_TRUE(instr->AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
@@ -947,9 +947,9 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 
     // Check condition
-    auto instr = if_flow->condition;
-    ASSERT_TRUE(instr.IsBool());
-    EXPECT_FALSE(instr.AsBool());
+    auto* instr = if_flow->condition;
+    ASSERT_TRUE(instr->IsBool());
+    EXPECT_FALSE(instr->AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, While_Return) {
@@ -1071,9 +1071,9 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 
     // Check condition
-    auto instr = if_flow->condition;
-    ASSERT_TRUE(instr.IsBool());
-    EXPECT_FALSE(instr.AsBool());
+    auto* instr = if_flow->condition;
+    ASSERT_TRUE(instr->IsBool());
+    EXPECT_FALSE(instr->AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
@@ -1171,9 +1171,9 @@
     EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
 
     // Check condition
-    auto instr = flow->condition;
-    ASSERT_TRUE(instr.IsI32());
-    EXPECT_EQ(1_i, instr.AsI32());
+    auto* instr = flow->condition;
+    ASSERT_TRUE(instr->IsI32());
+    EXPECT_EQ(1_i, instr->AsI32());
 }
 
 TEST_F(IR_BuilderImplTest, Switch_OnlyDefault) {
@@ -1341,9 +1341,9 @@
     auto r = b.EmitLiteral(Expr(true));
     ASSERT_TRUE(r);
 
-    auto reg = r.Get();
-    EXPECT_TRUE(reg.IsBool());
-    EXPECT_TRUE(reg.AsBool());
+    auto* val = r.Get();
+    EXPECT_TRUE(val->IsBool());
+    EXPECT_TRUE(val->AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_False) {
@@ -1351,9 +1351,9 @@
     auto r = b.EmitLiteral(Expr(false));
     ASSERT_TRUE(r);
 
-    auto reg = r.Get();
-    EXPECT_TRUE(reg.IsBool());
-    EXPECT_FALSE(reg.AsBool());
+    auto val = r.Get();
+    EXPECT_TRUE(val->IsBool());
+    EXPECT_FALSE(val->AsBool());
 }
 
 TEST_F(IR_BuilderImplTest, EmitLiteral_F32) {
@@ -1361,9 +1361,9 @@
     auto r = b.EmitLiteral(Expr(1.2_f));
     ASSERT_TRUE(r);
 
-    auto reg = r.Get();
-    EXPECT_TRUE(reg.IsF32());
-    EXPECT_EQ(1.2_f, reg.AsF32());
+    auto val = r.Get();
+    EXPECT_TRUE(val->IsF32());
+    EXPECT_EQ(1.2_f, val->AsF32());
 }
 
 TEST_F(IR_BuilderImplTest, EmitLiteral_F16) {
@@ -1371,9 +1371,9 @@
     auto r = b.EmitLiteral(Expr(1.2_h));
     ASSERT_TRUE(r);
 
-    auto reg = r.Get();
-    EXPECT_TRUE(reg.IsF16());
-    EXPECT_EQ(1.2_h, reg.AsF16());
+    auto val = r.Get();
+    EXPECT_TRUE(val->IsF16());
+    EXPECT_EQ(1.2_h, val->AsF16());
 }
 
 TEST_F(IR_BuilderImplTest, EmitLiteral_I32) {
@@ -1381,9 +1381,9 @@
     auto r = b.EmitLiteral(Expr(-2_i));
     ASSERT_TRUE(r);
 
-    auto reg = r.Get();
-    EXPECT_TRUE(reg.IsI32());
-    EXPECT_EQ(-2_i, reg.AsI32());
+    auto val = r.Get();
+    EXPECT_TRUE(val->IsI32());
+    EXPECT_EQ(-2_i, val->AsI32());
 }
 
 TEST_F(IR_BuilderImplTest, EmitLiteral_U32) {
@@ -1391,9 +1391,9 @@
     auto r = b.EmitLiteral(Expr(2_u));
     ASSERT_TRUE(r);
 
-    auto reg = r.Get();
-    EXPECT_TRUE(reg.IsU32());
-    EXPECT_EQ(2_u, reg.AsU32());
+    auto val = r.Get();
+    EXPECT_TRUE(val->IsU32());
+    EXPECT_EQ(2_u, val->AsU32());
 }
 
 TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Add) {
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 4b2969d..905f311 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -45,7 +45,7 @@
     /// branches into it. (e.g. if both branches `return`)
     Block* merge_target = nullptr;
     /// Value holding the condition result
-    Value condition;
+    const Value* condition = nullptr;
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/instruction.cc b/src/tint/ir/instruction.cc
index 9460f39..a46d17c 100644
--- a/src/tint/ir/instruction.cc
+++ b/src/tint/ir/instruction.cc
@@ -18,7 +18,7 @@
 
 Instruction::Instruction() {}
 
-Instruction::Instruction(Kind kind, Value result, Value lhs, Value rhs)
+Instruction::Instruction(Kind kind, const Value* result, const Value* lhs, const Value* rhs)
     : kind_(kind), result_(result), args_({lhs, rhs}) {}
 
 Instruction::Instruction(const Instruction&) = default;
@@ -32,9 +32,9 @@
 Instruction& Instruction::operator=(Instruction&& instr) = default;
 
 std::ostream& operator<<(std::ostream& out, const Instruction& instr) {
-    out << instr.Result() << " = ";
+    out << *(instr.Result()) << " = ";
     if (instr.HasLHS()) {
-        out << instr.LHS();
+        out << *(instr.LHS());
     }
     out << " ";
 
@@ -96,7 +96,7 @@
     }
 
     if (instr.HasRHS()) {
-        out << " " << instr.RHS();
+        out << " " << *(instr.RHS());
     }
 
     return out;
diff --git a/src/tint/ir/instruction.h b/src/tint/ir/instruction.h
index b62300c..ea9dec2 100644
--- a/src/tint/ir/instruction.h
+++ b/src/tint/ir/instruction.h
@@ -58,7 +58,7 @@
     /// @param result the result value
     /// @param lhs the lhs of the instruction
     /// @param rhs the rhs of the instruction
-    Instruction(Kind kind, Value result, Value lhs, Value rhs);
+    Instruction(Kind kind, const Value* result, const Value* lhs, const Value* rhs);
     /// Copy constructor
     /// @param instr the instruction to copy from
     Instruction(const Instruction& instr);
@@ -81,12 +81,12 @@
     Kind GetKind() const { return kind_; }
 
     /// @returns the result value for the instruction
-    const Value& Result() const { return result_; }
+    const Value* Result() const { return result_; }
 
     /// @returns true if the instruction has a LHS
     bool HasLHS() const { return args_.Length() >= 1; }
     /// @returns the left-hand-side value for the instruction
-    const Value& LHS() const {
+    const Value* LHS() const {
         TINT_ASSERT(IR, HasLHS());
         return args_[0];
     }
@@ -94,7 +94,7 @@
     /// @returns true if the instruction has a RHS
     bool HasRHS() const { return args_.Length() >= 2; }
     /// @returns the right-hand-side value for the instruction
-    const Value& RHS() const {
+    const Value* RHS() const {
         TINT_ASSERT(IR, HasRHS());
         return args_[1];
     }
@@ -102,8 +102,8 @@
   private:
     Kind kind_;
 
-    Value result_;
-    utils::Vector<Value, 2> args_;
+    const Value* result_;
+    utils::Vector<const Value*, 2> args_;
 };
 
 std::ostream& operator<<(std::ostream& out, const Instruction&);
diff --git a/src/tint/ir/instruction_test.cc b/src/tint/ir/instruction_test.cc
index 8fdd541..d93b9c7 100644
--- a/src/tint/ir/instruction_test.cc
+++ b/src/tint/ir/instruction_test.cc
@@ -26,22 +26,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.And(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.And(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kAnd);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -52,22 +52,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.Or(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.Or(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kOr);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -78,22 +78,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.Xor(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.Xor(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kXor);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -104,22 +104,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.LogicalAnd(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.LogicalAnd(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kLogicalAnd);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -130,22 +130,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.LogicalOr(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.LogicalOr(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kLogicalOr);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -156,22 +156,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.Equal(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.Equal(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kEqual);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -182,22 +182,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.NotEqual(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.NotEqual(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kNotEqual);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -208,22 +208,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.LessThan(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.LessThan(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kLessThan);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -234,22 +234,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.GreaterThan(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.GreaterThan(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kGreaterThan);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -260,22 +260,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.LessThanEqual(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.LessThanEqual(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kLessThanEqual);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -286,22 +286,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.GreaterThanEqual(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.GreaterThanEqual(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kGreaterThanEqual);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -312,22 +312,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.ShiftLeft(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.ShiftLeft(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kShiftLeft);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -338,22 +338,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.ShiftRight(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.ShiftRight(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kShiftRight);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -364,22 +364,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.Add(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.Add(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kAdd);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -390,22 +390,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.Subtract(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.Subtract(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kSubtract);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -416,22 +416,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.Multiply(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.Multiply(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kMultiply);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -442,22 +442,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.Divide(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.Divide(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kDivide);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
@@ -468,22 +468,22 @@
     auto& b = CreateEmptyBuilder();
 
     b.builder.next_value_id = Value::Id(42);
-    auto instr = b.builder.Modulo(Value(i32(4)), Value(i32(2)));
+    auto instr = b.builder.Modulo(b.builder.MkValue(i32(4)), b.builder.MkValue(i32(2)));
 
     EXPECT_EQ(instr.GetKind(), Instruction::Kind::kModulo);
 
-    ASSERT_TRUE(instr.Result().IsTemp());
-    EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+    ASSERT_TRUE(instr.Result()->IsTemp());
+    EXPECT_EQ(Value::Id(42), instr.Result()->AsId());
 
     ASSERT_TRUE(instr.HasLHS());
-    auto& lhs = instr.LHS();
-    ASSERT_TRUE(lhs.IsI32());
-    EXPECT_EQ(i32(4), lhs.AsI32());
+    auto lhs = instr.LHS();
+    ASSERT_TRUE(lhs->IsI32());
+    EXPECT_EQ(i32(4), lhs->AsI32());
 
     ASSERT_TRUE(instr.HasRHS());
-    auto& rhs = instr.RHS();
-    ASSERT_TRUE(rhs.IsI32());
-    EXPECT_EQ(i32(2), rhs.AsI32());
+    auto rhs = instr.RHS();
+    ASSERT_TRUE(rhs->IsI32());
+    EXPECT_EQ(i32(2), rhs->AsI32());
 
     std::stringstream str;
     str << instr;
diff --git a/src/tint/ir/module.h b/src/tint/ir/module.h
index 4d38614..1e2f8b0 100644
--- a/src/tint/ir/module.h
+++ b/src/tint/ir/module.h
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "src/tint/ir/function.h"
+#include "src/tint/ir/value.h"
 #include "src/tint/utils/block_allocator.h"
 #include "src/tint/utils/result.h"
 #include "src/tint/utils/vector.h"
@@ -66,6 +67,8 @@
 
     /// The flow node allocator
     utils::BlockAllocator<FlowNode> flow_nodes;
+    /// The value allocator
+    utils::BlockAllocator<Value> values;
 
     /// List of functions in the program
     utils::Vector<Function*, 8> functions;
diff --git a/src/tint/ir/switch.h b/src/tint/ir/switch.h
index e526fe8..3ea9c3f 100644
--- a/src/tint/ir/switch.h
+++ b/src/tint/ir/switch.h
@@ -53,7 +53,7 @@
     utils::Vector<Case, 4> cases;
 
     /// Value holding the condition result
-    Value condition;
+    const Value* condition = nullptr;
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/value.cc b/src/tint/ir/value.cc
index e7a69e3..4177f63 100644
--- a/src/tint/ir/value.cc
+++ b/src/tint/ir/value.cc
@@ -16,8 +16,6 @@
 
 namespace tint::ir {
 
-Value::Value() : kind_(Kind::kUninitialized), data_(Id(0)) {}
-
 Value::Value(Id id) : kind_(Kind::kTemp), data_(id) {}
 
 Value::Value(f32 f) : kind_(Kind::kF32), data_(f) {}
@@ -60,9 +58,6 @@
         case Value::Kind::kBool:
             out << (r.AsBool() ? "true" : "false");
             break;
-        case Value::Kind::kUninitialized:
-            out << "unknown value";
-            break;
     }
     return out;
 }
diff --git a/src/tint/ir/value.h b/src/tint/ir/value.h
index 2a28ca6..bbe8974 100644
--- a/src/tint/ir/value.h
+++ b/src/tint/ir/value.h
@@ -32,8 +32,6 @@
 
     /// The type of the value
     enum class Kind {
-        /// A uninitialized value
-        kUninitialized,
         /// A temporary allocated value
         kTemp,
         /// A f32 value
@@ -49,10 +47,6 @@
     };
 
     /// Constructor
-    /// Creates a uninitialized value
-    Value();
-
-    /// Constructor
     /// @param id the id for the value
     explicit Value(Id id);
 
diff --git a/src/tint/ir/value_test.cc b/src/tint/ir/value_test.cc
index 8954560..4ffe66e 100644
--- a/src/tint/ir/value_test.cc
+++ b/src/tint/ir/value_test.cc
@@ -133,16 +133,5 @@
     EXPECT_TRUE(val.IsBool());
 }
 
-TEST_F(IR_ValueTest, uninitialized) {
-    Value val;
-
-    EXPECT_FALSE(val.IsF32());
-    EXPECT_FALSE(val.IsF16());
-    EXPECT_FALSE(val.IsI32());
-    EXPECT_FALSE(val.IsU32());
-    EXPECT_FALSE(val.IsTemp());
-    EXPECT_FALSE(val.IsBool());
-}
-
 }  // namespace
 }  // namespace tint::ir