[tint][ir] Refactor Instruction result methods

Remove HasResults() and HasMultiResults(). These are virtual functions,
and are usually followed up with another virtual call to Result() or
Results(). This pattern is expensive and so should be discouraged.

Add an index to Operand() and Result() to keep these accessors
symmetrical.

Replace the Args() virtual methods with a virtual ArgsOperandOffset().
When we split methods into const and non-const flavors, this reduces the
number of duplicated methods as we can implement a const and non-const
Args() in the base class.

Change-Id: I7d0ecdb58746dcba90fc4631fead235474ccf1d3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/161005
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/lang/core/ir/access_test.cc b/src/tint/lang/core/ir/access_test.cc
index be295e8..afc2c0b 100644
--- a/src/tint/lang/core/ir/access_test.cc
+++ b/src/tint/lang/core/ir/access_test.cc
@@ -45,7 +45,7 @@
     auto* idx = b.Constant(u32(1));
     auto* a = b.Access(ty.i32(), var, idx);
 
-    EXPECT_THAT(var->Result()->Usages(), testing::UnorderedElementsAre(Usage{a, 0u}));
+    EXPECT_THAT(var->Result(0)->Usages(), testing::UnorderedElementsAre(Usage{a, 0u}));
     EXPECT_THAT(idx->Usages(), testing::UnorderedElementsAre(Usage{a, 1u}));
 }
 
@@ -55,11 +55,10 @@
     auto* idx = b.Constant(u32(1));
     auto* a = b.Access(ty.i32(), var, idx);
 
-    EXPECT_TRUE(a->HasResults());
-    EXPECT_FALSE(a->HasMultiResults());
+    EXPECT_EQ(a->Results().Length(), 1u);
 
-    EXPECT_TRUE(a->Result()->Is<InstructionResult>());
-    EXPECT_EQ(a, a->Result()->Source());
+    EXPECT_TRUE(a->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(a, a->Result(0)->Source());
 }
 
 TEST_F(IR_AccessTest, Fail_NullType) {
@@ -85,8 +84,8 @@
 
     EXPECT_NE(a, new_a);
 
-    EXPECT_NE(a->Result(), new_a->Result());
-    EXPECT_EQ(type, new_a->Result()->Type());
+    EXPECT_NE(a->Result(0), new_a->Result(0));
+    EXPECT_EQ(type, new_a->Result(0)->Type());
 
     EXPECT_NE(nullptr, new_a->Object());
     EXPECT_EQ(a->Object(), new_a->Object());
diff --git a/src/tint/lang/core/ir/binary_test.cc b/src/tint/lang/core/ir/binary_test.cc
index 0eae85d..65387ee 100644
--- a/src/tint/lang/core/ir/binary_test.cc
+++ b/src/tint/lang/core/ir/binary_test.cc
@@ -53,10 +53,9 @@
 TEST_F(IR_BinaryTest, Result) {
     auto* a = b.Add(mod.Types().i32(), 4_i, 2_i);
 
-    EXPECT_TRUE(a->HasResults());
-    EXPECT_FALSE(a->HasMultiResults());
-    EXPECT_TRUE(a->Result()->Is<InstructionResult>());
-    EXPECT_EQ(a, a->Result()->Source());
+    EXPECT_EQ(a->Results().Length(), 1u);
+    EXPECT_TRUE(a->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(a, a->Result(0)->Source());
 }
 
 TEST_F(IR_BinaryTest, CreateAnd) {
@@ -396,7 +395,7 @@
 
     EXPECT_NE(inst, c);
 
-    EXPECT_EQ(mod.Types().i32(), c->Result()->Type());
+    EXPECT_EQ(mod.Types().i32(), c->Result(0)->Type());
     EXPECT_EQ(BinaryOp::kAnd, c->Op());
 
     auto new_lhs = c->LHS()->As<Constant>()->Value();
diff --git a/src/tint/lang/core/ir/bitcast_test.cc b/src/tint/lang/core/ir/bitcast_test.cc
index 3075df1..229b7b7 100644
--- a/src/tint/lang/core/ir/bitcast_test.cc
+++ b/src/tint/lang/core/ir/bitcast_test.cc
@@ -45,7 +45,7 @@
     auto* inst = b.Bitcast(mod.Types().i32(), 4_i);
 
     ASSERT_TRUE(inst->Is<ir::Bitcast>());
-    ASSERT_NE(inst->Result()->Type(), nullptr);
+    ASSERT_NE(inst->Result(0)->Type(), nullptr);
 
     auto args = inst->Args();
     ASSERT_EQ(args.Length(), 1u);
@@ -58,10 +58,9 @@
 TEST_F(IR_BitcastTest, Result) {
     auto* a = b.Bitcast(mod.Types().i32(), 4_i);
 
-    EXPECT_TRUE(a->HasResults());
-    EXPECT_FALSE(a->HasMultiResults());
-    EXPECT_TRUE(a->Result()->Is<InstructionResult>());
-    EXPECT_EQ(a, a->Result()->Source());
+    EXPECT_EQ(a->Results().Length(), 1u);
+    EXPECT_TRUE(a->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(a, a->Result(0)->Source());
 }
 
 TEST_F(IR_BitcastTest, Bitcast_Usage) {
@@ -90,7 +89,7 @@
 
     EXPECT_NE(inst, n);
 
-    EXPECT_EQ(mod.Types().i32(), n->Result()->Type());
+    EXPECT_EQ(mod.Types().i32(), n->Result(0)->Type());
 
     auto new_val = n->Val()->As<Constant>()->Value();
     ASSERT_TRUE(new_val->Is<core::constant::Scalar<i32>>());
diff --git a/src/tint/lang/core/ir/break_if.h b/src/tint/lang/core/ir/break_if.h
index b2b1b5e..c5e269e 100644
--- a/src/tint/lang/core/ir/break_if.h
+++ b/src/tint/lang/core/ir/break_if.h
@@ -60,10 +60,8 @@
     /// @copydoc Instruction::Clone()
     BreakIf* Clone(CloneContext& ctx) override;
 
-    /// @returns the MultiInBlock arguments
-    tint::Slice<Value* const> Args() override {
-        return operands_.Slice().Offset(kArgsOperandOffset);
-    }
+    /// @returns the offset of the arguments in Operands()
+    size_t ArgsOperandOffset() const override { return kArgsOperandOffset; }
 
     /// @returns the break condition
     Value* Condition() { return operands_[kConditionOperandOffset]; }
diff --git a/src/tint/lang/core/ir/break_if_test.cc b/src/tint/lang/core/ir/break_if_test.cc
index df13922..9b36ad1 100644
--- a/src/tint/lang/core/ir/break_if_test.cc
+++ b/src/tint/lang/core/ir/break_if_test.cc
@@ -57,8 +57,7 @@
     auto* arg2 = b.Constant(2_u);
 
     auto* brk = b.BreakIf(loop, cond, arg1, arg2);
-    EXPECT_FALSE(brk->HasResults());
-    EXPECT_FALSE(brk->HasMultiResults());
+    EXPECT_TRUE(brk->Results().IsEmpty());
 }
 
 TEST_F(IR_BreakIfTest, Fail_NullLoop) {
diff --git a/src/tint/lang/core/ir/builder.h b/src/tint/lang/core/ir/builder.h
index 6020902..e1bdb13 100644
--- a/src/tint/lang/core/ir/builder.h
+++ b/src/tint/lang/core/ir/builder.h
@@ -396,8 +396,9 @@
                 return in;  /// Pass-through
             } else if constexpr (is_instruction) {
                 /// Extract the first result from the instruction
-                TINT_ASSERT(in->HasResults() && !in->HasMultiResults());
-                return in->Result();
+                auto results = in->Results();
+                TINT_ASSERT(results.Length() == 1);
+                return results[0];
             }
         } else if constexpr (is_numeric) {
             /// Creates a value from the given number
@@ -820,7 +821,7 @@
         }
         auto* var = Var(name, ir.Types().ptr(SPACE, val->Type(), ACCESS));
         var->SetInitializer(val);
-        ir.SetName(var->Result(), name);
+        ir.SetName(var->Result(0), name);
         return var;
     }
 
@@ -857,7 +858,7 @@
             return nullptr;
         }
         auto* let = Append(ir.instructions.Create<ir::Let>(InstructionResult(val->Type()), val));
-        ir.SetName(let->Result(), name);
+        ir.SetName(let->Result(0), name);
         return let;
     }
 
diff --git a/src/tint/lang/core/ir/call.h b/src/tint/lang/core/ir/call.h
index 6df2c85..78ef290 100644
--- a/src/tint/lang/core/ir/call.h
+++ b/src/tint/lang/core/ir/call.h
@@ -38,8 +38,11 @@
   public:
     ~Call() override;
 
+    /// @returns the offset of the arguments in Operands()
+    virtual size_t ArgsOperandOffset() const { return 0; }
+
     /// @returns the call arguments
-    virtual tint::Slice<Value*> Args() { return operands_.Slice(); }
+    tint::Slice<Value* const> Args() { return operands_.Slice().Offset(ArgsOperandOffset()); }
 
     /// Append a new argument to the argument list for this call instruction.
     /// @param arg the argument value to append
diff --git a/src/tint/lang/core/ir/construct_test.cc b/src/tint/lang/core/ir/construct_test.cc
index 51cf309..847616e 100644
--- a/src/tint/lang/core/ir/construct_test.cc
+++ b/src/tint/lang/core/ir/construct_test.cc
@@ -51,10 +51,9 @@
     auto* arg2 = b.Constant(false);
     auto* c = b.Construct(mod.Types().f32(), arg1, arg2);
 
-    EXPECT_TRUE(c->HasResults());
-    EXPECT_FALSE(c->HasMultiResults());
-    EXPECT_TRUE(c->Result()->Is<InstructionResult>());
-    EXPECT_EQ(c, c->Result()->Source());
+    EXPECT_EQ(c->Results().Length(), 1u);
+    EXPECT_TRUE(c->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(c, c->Result(0)->Source());
 }
 
 TEST_F(IR_ConstructTest, Fail_NullType) {
@@ -75,8 +74,8 @@
     auto* new_c = clone_ctx.Clone(c);
 
     EXPECT_NE(c, new_c);
-    EXPECT_NE(c->Result(), new_c->Result());
-    EXPECT_EQ(mod.Types().f32(), new_c->Result()->Type());
+    EXPECT_NE(c->Result(0), new_c->Result(0));
+    EXPECT_EQ(mod.Types().f32(), new_c->Result(0)->Type());
 
     auto args = new_c->Args();
     EXPECT_EQ(2u, args.Length());
@@ -92,8 +91,8 @@
     auto* c = b.Construct(mod.Types().f32());
 
     auto* new_c = clone_ctx.Clone(c);
-    EXPECT_NE(c->Result(), new_c->Result());
-    EXPECT_EQ(mod.Types().f32(), new_c->Result()->Type());
+    EXPECT_NE(c->Result(0), new_c->Result(0));
+    EXPECT_EQ(mod.Types().f32(), new_c->Result(0)->Type());
     EXPECT_TRUE(new_c->Args().IsEmpty());
 }
 
diff --git a/src/tint/lang/core/ir/continue_test.cc b/src/tint/lang/core/ir/continue_test.cc
index a25d232..5346bde 100644
--- a/src/tint/lang/core/ir/continue_test.cc
+++ b/src/tint/lang/core/ir/continue_test.cc
@@ -55,8 +55,7 @@
 
     auto* brk = b.Continue(loop, arg1, arg2);
 
-    EXPECT_FALSE(brk->HasResults());
-    EXPECT_FALSE(brk->HasMultiResults());
+    EXPECT_TRUE(brk->Results().IsEmpty());
 }
 
 TEST_F(IR_ContinueTest, Fail_NullLoop) {
diff --git a/src/tint/lang/core/ir/convert_test.cc b/src/tint/lang/core/ir/convert_test.cc
index fad55b5..edd1509 100644
--- a/src/tint/lang/core/ir/convert_test.cc
+++ b/src/tint/lang/core/ir/convert_test.cc
@@ -48,10 +48,9 @@
 TEST_F(IR_ConvertTest, Results) {
     auto* c = b.Convert(mod.Types().i32(), 1_u);
 
-    EXPECT_TRUE(c->HasResults());
-    EXPECT_FALSE(c->HasMultiResults());
-    EXPECT_TRUE(c->Result()->Is<InstructionResult>());
-    EXPECT_EQ(c->Result()->Source(), c);
+    EXPECT_EQ(c->Results().Length(), 1u);
+    EXPECT_TRUE(c->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(c->Result(0)->Source(), c);
 }
 
 TEST_F(IR_ConvertTest, Clone) {
@@ -60,8 +59,8 @@
     auto* new_c = clone_ctx.Clone(c);
 
     EXPECT_NE(c, new_c);
-    EXPECT_NE(c->Result(), new_c->Result());
-    EXPECT_EQ(mod.Types().f32(), new_c->Result()->Type());
+    EXPECT_NE(c->Result(0), new_c->Result(0));
+    EXPECT_EQ(mod.Types().f32(), new_c->Result(0)->Type());
 
     auto args = new_c->Args();
     EXPECT_EQ(1u, args.Length());
diff --git a/src/tint/lang/core/ir/core_builtin_call_test.cc b/src/tint/lang/core/ir/core_builtin_call_test.cc
index 447788a..e5b500f 100644
--- a/src/tint/lang/core/ir/core_builtin_call_test.cc
+++ b/src/tint/lang/core/ir/core_builtin_call_test.cc
@@ -50,10 +50,9 @@
     auto* arg2 = b.Constant(2_u);
     auto* builtin = b.Call(mod.Types().f32(), core::BuiltinFn::kAbs, arg1, arg2);
 
-    EXPECT_TRUE(builtin->HasResults());
-    EXPECT_FALSE(builtin->HasMultiResults());
-    EXPECT_TRUE(builtin->Result()->Is<InstructionResult>());
-    EXPECT_EQ(builtin->Result()->Source(), builtin);
+    EXPECT_EQ(builtin->Results().Length(), 1u);
+    EXPECT_TRUE(builtin->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(builtin->Result(0)->Source(), builtin);
 }
 
 TEST_F(IR_CoreBuiltinCallTest, Fail_NullType) {
@@ -92,8 +91,8 @@
     auto* new_b = clone_ctx.Clone(builtin);
 
     EXPECT_NE(builtin, new_b);
-    EXPECT_NE(builtin->Result(), new_b->Result());
-    EXPECT_EQ(mod.Types().f32(), new_b->Result()->Type());
+    EXPECT_NE(builtin->Result(0), new_b->Result(0));
+    EXPECT_EQ(mod.Types().f32(), new_b->Result(0)->Type());
 
     EXPECT_EQ(core::BuiltinFn::kAbs, new_b->Func());
 
@@ -111,8 +110,8 @@
     auto* builtin = b.Call(mod.Types().f32(), core::BuiltinFn::kAbs);
 
     auto* new_b = clone_ctx.Clone(builtin);
-    EXPECT_NE(builtin->Result(), new_b->Result());
-    EXPECT_EQ(mod.Types().f32(), new_b->Result()->Type());
+    EXPECT_NE(builtin->Result(0), new_b->Result(0));
+    EXPECT_EQ(mod.Types().f32(), new_b->Result(0)->Type());
 
     EXPECT_EQ(core::BuiltinFn::kAbs, new_b->Func());
 
diff --git a/src/tint/lang/core/ir/disassembler.cc b/src/tint/lang/core/ir/disassembler.cc
index 09d5c75..f266200 100644
--- a/src/tint/lang/core/ir/disassembler.cc
+++ b/src/tint/lang/core/ir/disassembler.cc
@@ -360,8 +360,8 @@
 
 void Disassembler::EmitValueWithType(Instruction* val) {
     SourceMarker sm(this);
-    if (val->Result()) {
-        EmitValueWithType(val->Result());
+    if (val->Result(0)) {
+        EmitValueWithType(val->Result(0));
     } else {
         out_ << "undef";
     }
@@ -573,9 +573,9 @@
 }
 
 void Disassembler::EmitOperand(Instruction* inst, size_t index) {
-    SourceMarker condMarker(this);
+    SourceMarker marker(this);
     EmitValue(inst->Operands()[index]);
-    condMarker.Store(Usage{inst, static_cast<uint32_t>(index)});
+    marker.Store(Usage{inst, static_cast<uint32_t>(index)});
 }
 
 void Disassembler::EmitOperandList(Instruction* inst, size_t start_index /* = 0 */) {
@@ -589,14 +589,13 @@
 
 void Disassembler::EmitIf(If* if_) {
     SourceMarker sm(this);
-    if (if_->HasResults()) {
-        auto res = if_->Results();
-        for (size_t i = 0; i < res.Length(); ++i) {
+    if (auto results = if_->Results(); !results.IsEmpty()) {
+        for (size_t i = 0; i < results.Length(); ++i) {
             if (i > 0) {
                 out_ << ", ";
             }
             SourceMarker rs(this);
-            EmitValueWithType(res[i]);
+            EmitValueWithType(results[i]);
             rs.StoreResult(Usage{if_, i});
         }
         out_ << " = ";
@@ -625,7 +624,7 @@
     if (has_false) {
         ScopedIndent si(indent_size_);
         EmitBlock(if_->False(), "false");
-    } else if (if_->HasResults()) {
+    } else if (auto results = if_->Results(); !results.IsEmpty()) {
         ScopedIndent si(indent_size_);
         Indent();
         out_ << "# implicit false block: exit_if undef";
@@ -650,14 +649,13 @@
         parts.Push("c: %b" + std::to_string(IdOf(l->Continuing())));
     }
     SourceMarker sm(this);
-    if (l->HasResults()) {
-        auto res = l->Results();
-        for (size_t i = 0; i < res.Length(); ++i) {
+    if (auto results = l->Results(); !results.IsEmpty()) {
+        for (size_t i = 0; i < results.Length(); ++i) {
             if (i > 0) {
                 out_ << ", ";
             }
             SourceMarker rs(this);
-            EmitValueWithType(res[i]);
+            EmitValueWithType(results[i]);
             rs.StoreResult(Usage{l, i});
         }
         out_ << " = ";
@@ -690,14 +688,13 @@
 
 void Disassembler::EmitSwitch(Switch* s) {
     SourceMarker sm(this);
-    if (s->HasResults()) {
-        auto res = s->Results();
-        for (size_t i = 0; i < res.Length(); ++i) {
+    if (auto results = s->Results(); !results.IsEmpty()) {
+        for (size_t i = 0; i < results.Length(); ++i) {
             if (i > 0) {
                 out_ << ", ";
             }
             SourceMarker rs(this);
-            EmitValueWithType(res[i]);
+            EmitValueWithType(results[i]);
             rs.StoreResult(Usage{s, i});
         }
         out_ << " = ";
@@ -745,7 +742,7 @@
         b,
         [&](ir::Return*) {
             out_ << "ret";
-            args_offset = ir::Return::kArgOperandOffset;
+            args_offset = ir::Return::kArgsOperandOffset;
         },
         [&](ir::Continue* cont) {
             out_ << "continue %b" << IdOf(cont->Loop()->Continuing());
diff --git a/src/tint/lang/core/ir/discard_test.cc b/src/tint/lang/core/ir/discard_test.cc
index bc9bc09..098b99f 100644
--- a/src/tint/lang/core/ir/discard_test.cc
+++ b/src/tint/lang/core/ir/discard_test.cc
@@ -43,8 +43,7 @@
 TEST_F(IR_DiscardTest, Result) {
     auto* inst = b.Discard();
 
-    EXPECT_FALSE(inst->HasResults());
-    EXPECT_FALSE(inst->HasMultiResults());
+    EXPECT_TRUE(inst->Results().IsEmpty());
 }
 
 TEST_F(IR_DiscardTest, Clone) {
diff --git a/src/tint/lang/core/ir/exit_if_test.cc b/src/tint/lang/core/ir/exit_if_test.cc
index ad838f9..7eb07ce 100644
--- a/src/tint/lang/core/ir/exit_if_test.cc
+++ b/src/tint/lang/core/ir/exit_if_test.cc
@@ -44,7 +44,7 @@
 
     EXPECT_THAT(arg1->Usages(), testing::UnorderedElementsAre(Usage{e, 0u}));
     EXPECT_THAT(arg2->Usages(), testing::UnorderedElementsAre(Usage{e, 1u}));
-    EXPECT_EQ(if_->Result(), nullptr);
+    EXPECT_EQ(if_->Result(0), nullptr);
 }
 
 TEST_F(IR_ExitIfTest, Result) {
@@ -53,8 +53,7 @@
     auto* if_ = b.If(true);
     auto* e = b.ExitIf(if_, arg1, arg2);
 
-    EXPECT_FALSE(e->HasResults());
-    EXPECT_FALSE(e->HasMultiResults());
+    EXPECT_TRUE(e->Results().IsEmpty());
 }
 
 TEST_F(IR_ExitIfTest, Destroy) {
diff --git a/src/tint/lang/core/ir/exit_loop_test.cc b/src/tint/lang/core/ir/exit_loop_test.cc
index 2f25903..18c59a3 100644
--- a/src/tint/lang/core/ir/exit_loop_test.cc
+++ b/src/tint/lang/core/ir/exit_loop_test.cc
@@ -44,7 +44,7 @@
 
     EXPECT_THAT(arg1->Usages(), testing::UnorderedElementsAre(Usage{e, 0u}));
     EXPECT_THAT(arg2->Usages(), testing::UnorderedElementsAre(Usage{e, 1u}));
-    EXPECT_EQ(loop->Result(), nullptr);
+    EXPECT_EQ(loop->Result(0), nullptr);
 }
 
 TEST_F(IR_ExitLoopTest, Destroy) {
diff --git a/src/tint/lang/core/ir/exit_switch_test.cc b/src/tint/lang/core/ir/exit_switch_test.cc
index 222cc3c..2fc1f77 100644
--- a/src/tint/lang/core/ir/exit_switch_test.cc
+++ b/src/tint/lang/core/ir/exit_switch_test.cc
@@ -44,7 +44,7 @@
 
     EXPECT_THAT(arg1->Usages(), testing::UnorderedElementsAre(Usage{e, 0u}));
     EXPECT_THAT(arg2->Usages(), testing::UnorderedElementsAre(Usage{e, 1u}));
-    EXPECT_EQ(switch_->Result(), nullptr);
+    EXPECT_EQ(switch_->Result(0), nullptr);
 }
 
 TEST_F(IR_ExitSwitchTest, Result) {
@@ -53,8 +53,7 @@
     auto* switch_ = b.Switch(true);
     auto* e = b.ExitSwitch(switch_, arg1, arg2);
 
-    EXPECT_FALSE(e->HasResults());
-    EXPECT_FALSE(e->HasMultiResults());
+    EXPECT_TRUE(e->Results().IsEmpty());
 }
 
 TEST_F(IR_ExitSwitchTest, Destroy) {
diff --git a/src/tint/lang/core/ir/if_test.cc b/src/tint/lang/core/ir/if_test.cc
index 3d3ea8b..6d609fd 100644
--- a/src/tint/lang/core/ir/if_test.cc
+++ b/src/tint/lang/core/ir/if_test.cc
@@ -45,8 +45,7 @@
 TEST_F(IR_IfTest, Result) {
     auto* if_ = b.If(b.Constant(true));
 
-    EXPECT_FALSE(if_->HasResults());
-    EXPECT_FALSE(if_->HasMultiResults());
+    EXPECT_TRUE(if_->Results().IsEmpty());
 }
 
 TEST_F(IR_IfTest, Parent) {
diff --git a/src/tint/lang/core/ir/instruction.h b/src/tint/lang/core/ir/instruction.h
index c15e6c6..c392fff 100644
--- a/src/tint/lang/core/ir/instruction.h
+++ b/src/tint/lang/core/ir/instruction.h
@@ -57,17 +57,8 @@
     /// @returns the operands of the instruction
     virtual VectorRef<ir::Value*> Operands() = 0;
 
-    /// @returns true if the instruction has result values
-    virtual bool HasResults() { return false; }
-    /// @returns true if the instruction has multiple values
-    virtual bool HasMultiResults() { return false; }
-
-    /// @returns the first result. Returns `nullptr` if there are no results, or if ther are
-    /// multi-results
-    virtual InstructionResult* Result() { return nullptr; }
-
     /// @returns the result values for this instruction
-    virtual VectorRef<InstructionResult*> Results() { return tint::Empty; }
+    virtual VectorRef<InstructionResult*> Results() = 0;
 
     /// Removes the instruction from the block, and destroys all the result values.
     /// The result values must not be in use.
@@ -106,6 +97,14 @@
     /// Removes this instruction from the owning block
     void Remove();
 
+    /// @param idx the index of the operand
+    /// @returns the operand with index @p idx, or `nullptr` if there are no operands or the index
+    /// is out of bounds.
+    Value* Operand(size_t idx) {
+        auto res = Operands();
+        return idx < res.Length() ? res[idx] : nullptr;
+    }
+
     /// @param idx the index of the result
     /// @returns the result with index @p idx, or `nullptr` if there are no results or the index is
     /// out of bounds.
diff --git a/src/tint/lang/core/ir/instruction_result_test.cc b/src/tint/lang/core/ir/instruction_result_test.cc
index 71212db..b5265c8 100644
--- a/src/tint/lang/core/ir/instruction_result_test.cc
+++ b/src/tint/lang/core/ir/instruction_result_test.cc
@@ -42,14 +42,14 @@
         {
             Module mod;
             Builder b{mod};
-            auto* val = b.Add(mod.Types().i32(), 1_i, 2_i)->Result();
+            auto* val = b.Add(mod.Types().i32(), 1_i, 2_i)->Result(0);
             val->Destroy();
         },
         "");
 }
 
 TEST_F(IR_InstructionResultTest, Clone) {
-    auto* val = b.Add(mod.Types().i32(), 1_i, 2_i)->Result();
+    auto* val = b.Add(mod.Types().i32(), 1_i, 2_i)->Result(0);
     auto* new_res = clone_ctx.Clone(val);
 
     EXPECT_NE(val, new_res);
diff --git a/src/tint/lang/core/ir/let_test.cc b/src/tint/lang/core/ir/let_test.cc
index 59c5947..3aba44f 100644
--- a/src/tint/lang/core/ir/let_test.cc
+++ b/src/tint/lang/core/ir/let_test.cc
@@ -55,11 +55,10 @@
 TEST_F(IR_LetTest, Results) {
     auto* value = b.Constant(1_f);
     auto* let = b.Let("l", value);
-    EXPECT_TRUE(let->HasResults());
-    EXPECT_FALSE(let->HasMultiResults());
-    EXPECT_TRUE(let->Result()->Is<InstructionResult>());
-    EXPECT_EQ(let->Result()->Source(), let);
-    EXPECT_EQ(let->Result()->Type(), value->Type());
+    EXPECT_EQ(let->Results().Length(), 1u);
+    EXPECT_TRUE(let->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(let->Result(0)->Source(), let);
+    EXPECT_EQ(let->Result(0)->Type(), value->Type());
 }
 
 TEST_F(IR_LetTest, Clone) {
@@ -69,15 +68,15 @@
     auto* new_let = clone_ctx.Clone(let);
 
     EXPECT_NE(let, new_let);
-    EXPECT_NE(nullptr, new_let->Result());
-    EXPECT_NE(let->Result(), new_let->Result());
+    EXPECT_NE(nullptr, new_let->Result(0));
+    EXPECT_NE(let->Result(0), new_let->Result(0));
 
     auto new_val = new_let->Value()->As<Constant>()->Value();
     ASSERT_TRUE(new_val->Is<core::constant::Scalar<f32>>());
     EXPECT_FLOAT_EQ(4_f, new_val->As<core::constant::Scalar<f32>>()->ValueAs<f32>());
 
     EXPECT_EQ(std::string("l"), mod.NameOf(new_let).Name());
-    EXPECT_EQ(std::string("l"), mod.NameOf(new_let->Result()).Name());
+    EXPECT_EQ(std::string("l"), mod.NameOf(new_let->Result(0)).Name());
 }
 
 }  // namespace
diff --git a/src/tint/lang/core/ir/load_test.cc b/src/tint/lang/core/ir/load_test.cc
index ccb77dc..adea675 100644
--- a/src/tint/lang/core/ir/load_test.cc
+++ b/src/tint/lang/core/ir/load_test.cc
@@ -45,8 +45,8 @@
     auto* inst = b.Load(var);
 
     ASSERT_TRUE(inst->Is<Load>());
-    ASSERT_EQ(inst->From(), var->Result());
-    EXPECT_EQ(inst->Result()->Type(), store_type);
+    ASSERT_EQ(inst->From(), var->Result(0));
+    EXPECT_EQ(inst->Result(0)->Type(), store_type);
 
     auto* result = inst->From()->As<InstructionResult>();
     ASSERT_NE(result, nullptr);
@@ -66,10 +66,9 @@
     auto* var = b.Var(ty.ptr<function, i32>());
     auto* inst = b.Load(var);
 
-    EXPECT_TRUE(inst->HasResults());
-    EXPECT_FALSE(inst->HasMultiResults());
-    EXPECT_TRUE(inst->Result()->Is<InstructionResult>());
-    EXPECT_EQ(inst->Result()->Source(), inst);
+    EXPECT_EQ(inst->Results().Length(), 1u);
+    EXPECT_TRUE(inst->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(inst->Result(0)->Source(), inst);
 }
 
 TEST_F(IR_LoadTest, Fail_NonPtr_Builder) {
@@ -90,10 +89,10 @@
     auto* new_inst = clone_ctx.Clone(inst);
 
     EXPECT_NE(inst, new_inst);
-    EXPECT_NE(nullptr, new_inst->Result());
-    EXPECT_NE(inst->Result(), new_inst->Result());
+    EXPECT_NE(nullptr, new_inst->Result(0));
+    EXPECT_NE(inst->Result(0), new_inst->Result(0));
 
-    EXPECT_EQ(new_var->Result(), new_inst->From());
+    EXPECT_EQ(new_var->Result(0), new_inst->From());
 }
 
 }  // namespace
diff --git a/src/tint/lang/core/ir/load_vector_element_test.cc b/src/tint/lang/core/ir/load_vector_element_test.cc
index 78bcd44..0f69ec1 100644
--- a/src/tint/lang/core/ir/load_vector_element_test.cc
+++ b/src/tint/lang/core/ir/load_vector_element_test.cc
@@ -44,7 +44,7 @@
     auto* inst = b.LoadVectorElement(from, 2_i);
 
     ASSERT_TRUE(inst->Is<LoadVectorElement>());
-    ASSERT_EQ(inst->From(), from->Result());
+    ASSERT_EQ(inst->From(), from->Result(0));
 
     ASSERT_TRUE(inst->Index()->Is<Constant>());
     auto index = inst->Index()->As<Constant>()->Value();
@@ -67,8 +67,7 @@
     auto* from = b.Var(ty.ptr<private_, vec3<i32>>());
     auto* inst = b.LoadVectorElement(from, 2_i);
 
-    EXPECT_TRUE(inst->HasResults());
-    EXPECT_FALSE(inst->HasMultiResults());
+    EXPECT_EQ(inst->Results().Length(), 1u);
 }
 
 TEST_F(IR_LoadVectorElementTest, Clone) {
@@ -79,10 +78,10 @@
     auto* new_inst = clone_ctx.Clone(inst);
 
     EXPECT_NE(inst, new_inst);
-    EXPECT_NE(nullptr, new_inst->Result());
-    EXPECT_NE(inst->Result(), new_inst->Result());
+    EXPECT_NE(nullptr, new_inst->Result(0));
+    EXPECT_NE(inst->Result(0), new_inst->Result(0));
 
-    EXPECT_EQ(new_from->Result(), new_inst->From());
+    EXPECT_EQ(new_from->Result(0), new_inst->From());
 
     auto new_idx = new_inst->Index()->As<Constant>()->Value();
     ASSERT_TRUE(new_idx->Is<core::constant::Scalar<i32>>());
diff --git a/src/tint/lang/core/ir/loop_test.cc b/src/tint/lang/core/ir/loop_test.cc
index df6abcc..b8721e2 100644
--- a/src/tint/lang/core/ir/loop_test.cc
+++ b/src/tint/lang/core/ir/loop_test.cc
@@ -44,8 +44,7 @@
 
 TEST_F(IR_LoopTest, Result) {
     auto* loop = b.Loop();
-    EXPECT_FALSE(loop->HasResults());
-    EXPECT_FALSE(loop->HasMultiResults());
+    EXPECT_TRUE(loop->Results().IsEmpty());
 }
 
 TEST_F(IR_LoopTest, Fail_NullInitializerBlock) {
@@ -83,7 +82,7 @@
     auto* new_loop = clone_ctx.Clone(loop);
 
     EXPECT_NE(loop, new_loop);
-    EXPECT_FALSE(new_loop->HasResults());
+    EXPECT_TRUE(new_loop->Results().IsEmpty());
     EXPECT_EQ(0u, new_loop->Exits().Count());
     EXPECT_NE(nullptr, new_loop->Initializer());
     EXPECT_NE(loop->Initializer(), new_loop->Initializer());
diff --git a/src/tint/lang/core/ir/module.cc b/src/tint/lang/core/ir/module.cc
index a6f2c5b..d8f5448 100644
--- a/src/tint/lang/core/ir/module.cc
+++ b/src/tint/lang/core/ir/module.cc
@@ -42,8 +42,10 @@
 Module& Module::operator=(Module&&) = default;
 
 Symbol Module::NameOf(Instruction* inst) {
-    TINT_ASSERT(inst->HasResults() && !inst->HasMultiResults());
-    return NameOf(inst->Result());
+    if (inst->Results().Length() != 1) {
+        return Symbol{};
+    }
+    return NameOf(inst->Result(0));
 }
 
 Symbol Module::NameOf(Value* value) {
@@ -51,8 +53,8 @@
 }
 
 void Module::SetName(Instruction* inst, std::string_view name) {
-    TINT_ASSERT(inst->HasResults() && !inst->HasMultiResults());
-    return SetName(inst->Result(), name);
+    TINT_ASSERT(inst->Results().Length() == 1);
+    return SetName(inst->Result(0), name);
 }
 
 void Module::SetName(Value* value, std::string_view name) {
diff --git a/src/tint/lang/core/ir/module.h b/src/tint/lang/core/ir/module.h
index 4f37391..51148dd 100644
--- a/src/tint/lang/core/ir/module.h
+++ b/src/tint/lang/core/ir/module.h
@@ -71,7 +71,7 @@
 
     /// @param inst the instruction
     /// @return the name of the given instruction, or an invalid symbol if the instruction is not
-    /// named. Requires that the instruction only has a single return value.
+    /// named or does not have a single return value.
     Symbol NameOf(Instruction* inst);
 
     /// @param value the value
diff --git a/src/tint/lang/core/ir/next_iteration_test.cc b/src/tint/lang/core/ir/next_iteration_test.cc
index 75cdca1..249bd63 100644
--- a/src/tint/lang/core/ir/next_iteration_test.cc
+++ b/src/tint/lang/core/ir/next_iteration_test.cc
@@ -48,8 +48,7 @@
 TEST_F(IR_NextIterationTest, Result) {
     auto* inst = b.NextIteration(b.Loop());
 
-    EXPECT_FALSE(inst->HasResults());
-    EXPECT_FALSE(inst->HasMultiResults());
+    EXPECT_TRUE(inst->Results().IsEmpty());
 }
 
 TEST_F(IR_NextIterationTest, Clone) {
diff --git a/src/tint/lang/core/ir/operand_instruction.h b/src/tint/lang/core/ir/operand_instruction.h
index 624eeb8..24c6e97 100644
--- a/src/tint/lang/core/ir/operand_instruction.h
+++ b/src/tint/lang/core/ir/operand_instruction.h
@@ -94,25 +94,16 @@
     /// @returns the operands of the instruction
     VectorRef<ir::Value*> Operands() override { return operands_; }
 
-    /// @returns true if the instruction has result values
-    bool HasResults() override { return !results_.IsEmpty(); }
-    /// @returns true if the instruction has multiple values
-    bool HasMultiResults() override { return results_.Length() > 1; }
-
-    /// @returns the first result. Returns `nullptr` if there are no results, or if ther are
-    /// multi-results
-    InstructionResult* Result() override {
-        if (!HasResults() || HasMultiResults()) {
-            return nullptr;
-        }
-        return results_[0];
-    }
-
-    using Instruction::Result;
-
     /// @returns the result values for this instruction
     VectorRef<InstructionResult*> Results() override { return results_; }
 
+    /// @param idx the index of the result
+    /// @returns the result with index @p idx, or `nullptr` if there are no results or the index is
+    /// out of bounds.
+    InstructionResult* Result(size_t idx = 0) {
+        return idx < results_.Length() ? results_[idx] : nullptr;
+    }
+
   protected:
     /// Append a new operand to the operand list for this instruction.
     /// @param idx the index the operand should be at
diff --git a/src/tint/lang/core/ir/operand_instruction_test.cc b/src/tint/lang/core/ir/operand_instruction_test.cc
index b66a600..b82c9d5 100644
--- a/src/tint/lang/core/ir/operand_instruction_test.cc
+++ b/src/tint/lang/core/ir/operand_instruction_test.cc
@@ -45,14 +45,14 @@
     EXPECT_EQ(inst->Block(), block);
     EXPECT_THAT(lhs->Usages(), testing::ElementsAre(Usage{inst, 0u}));
     EXPECT_THAT(rhs->Usages(), testing::ElementsAre(Usage{inst, 1u}));
-    EXPECT_TRUE(inst->Result()->Alive());
+    EXPECT_TRUE(inst->Result(0)->Alive());
 
     inst->Destroy();
 
     EXPECT_EQ(inst->Block(), nullptr);
     EXPECT_TRUE(lhs->Usages().IsEmpty());
     EXPECT_TRUE(rhs->Usages().IsEmpty());
-    EXPECT_FALSE(inst->Result()->Alive());
+    EXPECT_FALSE(inst->Result(0)->Alive());
 }
 
 TEST_F(IR_OperandInstructionTest, ClearOperands_WithNullOperand) {
@@ -63,7 +63,7 @@
 
     inst->Destroy();
     EXPECT_EQ(inst->Block(), nullptr);
-    EXPECT_FALSE(inst->Result()->Alive());
+    EXPECT_FALSE(inst->Result(0)->Alive());
 }
 
 TEST_F(IR_OperandInstructionTest, SetOperands_WithNullOperand) {
diff --git a/src/tint/lang/core/ir/return.cc b/src/tint/lang/core/ir/return.cc
index a854d62..131766d 100644
--- a/src/tint/lang/core/ir/return.cc
+++ b/src/tint/lang/core/ir/return.cc
@@ -43,7 +43,7 @@
 
 Return::Return(Function* func, ir::Value* arg) {
     AddOperand(Return::kFunctionOperandOffset, func);
-    AddOperand(Return::kArgOperandOffset, arg);
+    AddOperand(Return::kArgsOperandOffset, arg);
 }
 
 Return::~Return() = default;
diff --git a/src/tint/lang/core/ir/return.h b/src/tint/lang/core/ir/return.h
index 412369f..afacf4a 100644
--- a/src/tint/lang/core/ir/return.h
+++ b/src/tint/lang/core/ir/return.h
@@ -47,7 +47,7 @@
     static constexpr size_t kFunctionOperandOffset = 0;
 
     /// The offset in Operands() for the return argument
-    static constexpr size_t kArgOperandOffset = 1;
+    static constexpr size_t kArgsOperandOffset = 1;
 
     /// Constructor (no return value)
     /// @param func the function being returned
@@ -68,17 +68,15 @@
 
     /// @returns the return value, or nullptr
     ir::Value* Value() const {
-        return operands_.Length() > kArgOperandOffset ? operands_[kArgOperandOffset] : nullptr;
+        return operands_.Length() > kArgsOperandOffset ? operands_[kArgsOperandOffset] : nullptr;
     }
 
     /// Sets the return value
     /// @param val the new return value
-    void SetValue(ir::Value* val) { SetOperand(kArgOperandOffset, val); }
+    void SetValue(ir::Value* val) { SetOperand(kArgsOperandOffset, val); }
 
-    /// @returns the return arguments
-    tint::Slice<ir::Value* const> Args() override {
-        return operands_.Slice().Offset(kArgOperandOffset);
-    }
+    /// @returns the offset of the arguments in Operands()
+    size_t ArgsOperandOffset() const override { return kArgsOperandOffset; }
 
     /// @returns the friendly name for the instruction
     std::string FriendlyName() override { return "return"; }
diff --git a/src/tint/lang/core/ir/return_test.cc b/src/tint/lang/core/ir/return_test.cc
index f019437..dfd088b 100644
--- a/src/tint/lang/core/ir/return_test.cc
+++ b/src/tint/lang/core/ir/return_test.cc
@@ -64,14 +64,12 @@
 
     {
         auto* ret1 = b.Return(vfunc);
-        EXPECT_FALSE(ret1->HasResults());
-        EXPECT_FALSE(ret1->HasMultiResults());
+        EXPECT_TRUE(ret1->Results().IsEmpty());
     }
 
     {
         auto* ret2 = b.Return(ifunc, b.Constant(42_i));
-        EXPECT_FALSE(ret2->HasResults());
-        EXPECT_FALSE(ret2->HasMultiResults());
+        EXPECT_TRUE(ret2->Results().IsEmpty());
     }
 }
 
diff --git a/src/tint/lang/core/ir/store_test.cc b/src/tint/lang/core/ir/store_test.cc
index aff0b12..28e4d85 100644
--- a/src/tint/lang/core/ir/store_test.cc
+++ b/src/tint/lang/core/ir/store_test.cc
@@ -44,7 +44,7 @@
     auto* inst = b.Store(to, 4_i);
 
     ASSERT_TRUE(inst->Is<Store>());
-    ASSERT_EQ(inst->To(), to->Result());
+    ASSERT_EQ(inst->To(), to->Result(0));
 
     ASSERT_TRUE(inst->From()->Is<Constant>());
     auto lhs = inst->From()->As<Constant>()->Value();
@@ -67,8 +67,7 @@
     auto* to = b.Var(ty.ptr<private_, i32>());
     auto* inst = b.Store(to, 4_i);
 
-    EXPECT_FALSE(inst->HasResults());
-    EXPECT_FALSE(inst->HasMultiResults());
+    EXPECT_TRUE(inst->Results().IsEmpty());
 }
 
 TEST_F(IR_StoreTest, Clone) {
@@ -79,7 +78,7 @@
     auto* new_s = clone_ctx.Clone(s);
 
     EXPECT_NE(s, new_s);
-    EXPECT_EQ(new_v->Result(), new_s->To());
+    EXPECT_EQ(new_v->Result(0), new_s->To());
 
     auto new_from = new_s->From()->As<Constant>()->Value();
     ASSERT_TRUE(new_from->Is<core::constant::Scalar<i32>>());
diff --git a/src/tint/lang/core/ir/store_vector_element_test.cc b/src/tint/lang/core/ir/store_vector_element_test.cc
index 74664ba..971b12b 100644
--- a/src/tint/lang/core/ir/store_vector_element_test.cc
+++ b/src/tint/lang/core/ir/store_vector_element_test.cc
@@ -44,7 +44,7 @@
     auto* inst = b.StoreVectorElement(to, 2_i, 4_i);
 
     ASSERT_TRUE(inst->Is<StoreVectorElement>());
-    ASSERT_EQ(inst->To(), to->Result());
+    ASSERT_EQ(inst->To(), to->Result(0));
 
     ASSERT_TRUE(inst->Index()->Is<Constant>());
     auto index = inst->Index()->As<Constant>()->Value();
@@ -75,8 +75,7 @@
     auto* to = b.Var(ty.ptr<private_, vec3<i32>>());
     auto* inst = b.StoreVectorElement(to, 2_i, 4_i);
 
-    EXPECT_FALSE(inst->HasResults());
-    EXPECT_FALSE(inst->HasMultiResults());
+    EXPECT_TRUE(inst->Results().IsEmpty());
 }
 
 TEST_F(IR_StoreVectorElementTest, Clone) {
@@ -87,7 +86,7 @@
     auto* new_inst = clone_ctx.Clone(inst);
 
     EXPECT_NE(inst, new_inst);
-    EXPECT_EQ(new_to->Result(), new_inst->To());
+    EXPECT_EQ(new_to->Result(0), new_inst->To());
 
     auto new_idx = new_inst->Index()->As<Constant>()->Value();
     ASSERT_TRUE(new_idx->Is<core::constant::Scalar<i32>>());
diff --git a/src/tint/lang/core/ir/switch_test.cc b/src/tint/lang/core/ir/switch_test.cc
index f71b256..8e6f7c8 100644
--- a/src/tint/lang/core/ir/switch_test.cc
+++ b/src/tint/lang/core/ir/switch_test.cc
@@ -47,8 +47,7 @@
 TEST_F(IR_SwitchTest, Results) {
     auto* cond = b.Constant(true);
     auto* switch_ = b.Switch(cond);
-    EXPECT_FALSE(switch_->HasResults());
-    EXPECT_FALSE(switch_->HasMultiResults());
+    EXPECT_TRUE(switch_->Results().IsEmpty());
 }
 
 TEST_F(IR_SwitchTest, Parent) {
diff --git a/src/tint/lang/core/ir/swizzle_test.cc b/src/tint/lang/core/ir/swizzle_test.cc
index 153db7b..088318d 100644
--- a/src/tint/lang/core/ir/swizzle_test.cc
+++ b/src/tint/lang/core/ir/swizzle_test.cc
@@ -42,17 +42,16 @@
     auto* var = b.Var(ty.ptr<function, i32>());
     auto* a = b.Swizzle(mod.Types().i32(), var, {1u});
 
-    EXPECT_THAT(var->Result()->Usages(), testing::UnorderedElementsAre(Usage{a, 0u}));
+    EXPECT_THAT(var->Result(0)->Usages(), testing::UnorderedElementsAre(Usage{a, 0u}));
 }
 
 TEST_F(IR_SwizzleTest, Results) {
     auto* var = b.Var(ty.ptr<function, i32>());
     auto* a = b.Swizzle(mod.Types().i32(), var, {1u});
 
-    EXPECT_TRUE(a->HasResults());
-    EXPECT_FALSE(a->HasMultiResults());
-    EXPECT_TRUE(a->Result()->Is<InstructionResult>());
-    EXPECT_EQ(a->Result()->Source(), a);
+    EXPECT_EQ(a->Results().Length(), 1u);
+    EXPECT_TRUE(a->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(a->Result(0)->Source(), a);
 }
 
 TEST_F(IR_SwizzleTest, Fail_NullType) {
@@ -107,10 +106,10 @@
     auto* new_s = clone_ctx.Clone(s);
 
     EXPECT_NE(s, new_s);
-    EXPECT_NE(nullptr, new_s->Result());
-    EXPECT_NE(s->Result(), new_s->Result());
+    EXPECT_NE(nullptr, new_s->Result(0));
+    EXPECT_NE(s->Result(0), new_s->Result(0));
 
-    EXPECT_EQ(new_var->Result(), new_s->Object());
+    EXPECT_EQ(new_var->Result(0), new_s->Object());
 
     EXPECT_EQ(1u, new_s->Indices().Length());
     EXPECT_EQ(2u, new_s->Indices().Front());
diff --git a/src/tint/lang/core/ir/terminator.h b/src/tint/lang/core/ir/terminator.h
index 1f12541..e9df9d1 100644
--- a/src/tint/lang/core/ir/terminator.h
+++ b/src/tint/lang/core/ir/terminator.h
@@ -44,8 +44,11 @@
   public:
     ~Terminator() override;
 
-    /// @returns the terminator arguments
-    virtual tint::Slice<Value* const> Args() { return operands_.Slice(); }
+    /// @returns the offset of the arguments in Operands()
+    virtual size_t ArgsOperandOffset() const { return 0; }
+
+    /// @returns the call arguments
+    tint::Slice<Value* const> Args() { return operands_.Slice().Offset(ArgsOperandOffset()); }
 };
 
 }  // namespace tint::core::ir
diff --git a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.cc b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.cc
index 5d81e6b..d3416eb 100644
--- a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.cc
@@ -63,7 +63,7 @@
                 if (!var) {
                     continue;
                 }
-                auto* ptr = var->Result()->Type()->As<core::type::Pointer>();
+                auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
                 if (!ptr) {
                     continue;
                 }
@@ -108,7 +108,7 @@
         }
 
         // Replace all uses of the old variable with the new one.
-        ReplaceUses(old_var->Result(), new_var->Result());
+        ReplaceUses(old_var->Result(0), new_var->Result(0));
     }
 
     /// Replace a function parameter with one that uses rgba8unorm instead of bgra8unorm.
@@ -147,7 +147,7 @@
                     // Replace load instructions with new ones that have the updated type.
                     auto* new_load = b.Load(new_value);
                     new_load->InsertBefore(load);
-                    ReplaceUses(load->Result(), new_load->Result());
+                    ReplaceUses(load->Result(0), new_load->Result(0));
                     load->Destroy();
                 },
                 [&](CoreBuiltinCall* call) {
@@ -160,14 +160,14 @@
                         auto* value = call->Args()[index];
                         auto* swizzle = b.Swizzle(value->Type(), value, Vector{2u, 1u, 0u, 3u});
                         swizzle->InsertBefore(call);
-                        call->SetOperand(index, swizzle->Result());
+                        call->SetOperand(index, swizzle->Result(0));
                     } else if (call->Func() == core::BuiltinFn::kTextureLoad) {
                         // Swizzle the result of a `textureLoad()` builtin.
                         auto* swizzle =
-                            b.Swizzle(call->Result()->Type(), nullptr, Vector{2u, 1u, 0u, 3u});
-                        call->Result()->ReplaceAllUsesWith(swizzle->Result());
+                            b.Swizzle(call->Result(0)->Type(), nullptr, Vector{2u, 1u, 0u, 3u});
+                        call->Result(0)->ReplaceAllUsesWith(swizzle->Result(0));
                         swizzle->InsertAfter(call);
-                        swizzle->SetOperand(Swizzle::kObjectOperandOffset, call->Result());
+                        swizzle->SetOperand(Swizzle::kObjectOperandOffset, call->Result(0));
                     }
                 },
                 [&](UserCall* call) {
diff --git a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill_test.cc b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill_test.cc
index a51835b..6357040 100644
--- a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill_test.cc
+++ b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill_test.cc
@@ -72,7 +72,7 @@
     auto* value = b.FunctionParam("value", ty.vec4<f32>());
     func->SetParams({value, coords});
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         b.Call(ty.void_(), core::BuiltinFn::kTextureStore, load, coords, value);
         b.Return(func);
     });
@@ -145,7 +145,7 @@
     auto* value = b.FunctionParam("value", ty.vec4<f32>());
     func->SetParams({value, coords});
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         b.Call(ty.void_(), core::BuiltinFn::kTextureStore, load, coords, value);
         b.Return(func);
     });
@@ -252,7 +252,7 @@
         auto* value = b.FunctionParam("value", ty.vec4<f32>());
         foo->SetParams({coords, value});
         b.Append(foo->Block(), [&] {
-            auto* load = b.Load(var->Result());
+            auto* load = b.Load(var->Result(0));
             b.Call(ty.void_(), bar, load, coords, value);
             b.Return(foo);
         });
@@ -342,9 +342,9 @@
         auto* value = b.FunctionParam("value", ty.vec4<f32>());
         foo->SetParams({coords, value});
         b.Append(foo->Block(), [&] {
-            auto* load_a = b.Load(var_a->Result());
-            auto* load_b = b.Load(var_b->Result());
-            auto* load_c = b.Load(var_c->Result());
+            auto* load_a = b.Load(var_a->Result(0));
+            auto* load_b = b.Load(var_b->Result(0));
+            auto* load_c = b.Load(var_c->Result(0));
             b.Call(ty.void_(), bar, load_a, load_b, load_c, coords, value);
             b.Return(foo);
         });
@@ -440,11 +440,11 @@
         auto* value = b.FunctionParam("value", ty.vec4<f32>());
         foo->SetParams({coords, value});
         b.Append(foo->Block(), [&] {
-            auto* load_a = b.Load(var_a->Result());
+            auto* load_a = b.Load(var_a->Result(0));
             b.Call(ty.void_(), core::BuiltinFn::kTextureStore, load_a, coords, value);
-            auto* load_b = b.Load(var_a->Result());
+            auto* load_b = b.Load(var_a->Result(0));
             b.Call(ty.void_(), bar, load_b, coords, value);
-            auto* load_c = b.Load(var_a->Result());
+            auto* load_c = b.Load(var_a->Result(0));
             b.Call(ty.void_(), bar, load_c, coords, value);
             b.Return(foo);
         });
@@ -527,7 +527,7 @@
     auto* value = b.FunctionParam("value", ty.vec4<f32>());
     func->SetParams({value, coords});
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         b.Call(ty.void_(), core::BuiltinFn::kTextureStore, load, coords, index, value);
         b.Return(func);
     });
@@ -578,7 +578,7 @@
 
     auto* func = b.Function("foo", ty.vec2<u32>());
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         auto* dims = b.Call(ty.vec2<u32>(), core::BuiltinFn::kTextureDimensions, load);
         b.Return(func, dims);
         mod.SetName(dims, "dims");
@@ -631,7 +631,7 @@
     auto* coords = b.FunctionParam("coords", ty.vec2<u32>());
     func->SetParams({coords});
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, load, coords);
         b.Return(func, result);
         mod.SetName(result, "result");
@@ -685,7 +685,7 @@
     auto* coords = b.FunctionParam("coords", ty.vec2<u32>());
     func->SetParams({coords});
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, load, coords);
         b.Call(ty.void_(), core::BuiltinFn::kTextureStore, load, coords, result);
         b.Return(func);
diff --git a/src/tint/lang/core/ir/transform/binary_polyfill.cc b/src/tint/lang/core/ir/transform/binary_polyfill.cc
index 276bec4..ba175c6 100644
--- a/src/tint/lang/core/ir/transform/binary_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/binary_polyfill.cc
@@ -76,7 +76,7 @@
                     case BinaryOp::kDivide:
                     case BinaryOp::kModulo:
                         if (config.int_div_mod &&
-                            binary->Result()->Type()->is_integer_scalar_or_vector()) {
+                            binary->Result(0)->Type()->is_integer_scalar_or_vector()) {
                             worklist.Push(binary);
                         }
                         break;
@@ -109,12 +109,12 @@
             }
             TINT_ASSERT_OR_RETURN(replacement);
 
-            if (replacement != binary->Result()) {
+            if (replacement != binary->Result(0)) {
                 // Replace the old binary instruction result with the new value.
-                if (auto name = ir.NameOf(binary->Result())) {
+                if (auto name = ir.NameOf(binary->Result(0))) {
                     ir.SetName(replacement, name);
                 }
-                binary->Result()->ReplaceAllUsesWith(replacement);
+                binary->Result(0)->ReplaceAllUsesWith(replacement);
                 binary->Destroy();
             }
         }
@@ -150,7 +150,7 @@
     /// @param binary the binary instruction
     /// @returns the replacement value
     ir::Value* IntDivMod(ir::Binary* binary) {
-        auto* result_ty = binary->Result()->Type();
+        auto* result_ty = binary->Result(0)->Type();
         bool is_div = binary->Op() == BinaryOp::kDivide;
         bool is_signed = result_ty->is_signed_integer_scalar_or_vector();
 
@@ -197,7 +197,7 @@
 
                 if (binary->Op() == BinaryOp::kDivide) {
                     // Perform the divide with the modified RHS.
-                    b.Return(func, b.Divide(result_ty, lhs, rhs_or_one)->Result());
+                    b.Return(func, b.Divide(result_ty, lhs, rhs_or_one)->Result(0));
                 } else if (binary->Op() == BinaryOp::kModulo) {
                     // Calculate the modulo manually, as modulo with negative operands is undefined
                     // behavior for many backends:
@@ -205,7 +205,7 @@
                     auto* whole = b.Divide(result_ty, lhs, rhs_or_one);
                     auto* remainder =
                         b.Subtract(result_ty, lhs, b.Multiply(result_ty, whole, rhs_or_one));
-                    b.Return(func, remainder->Result());
+                    b.Return(func, remainder->Result(0));
                 }
             });
             return func;
@@ -214,7 +214,7 @@
         /// Helper to splat a value to match the vector width of the result type if necessary.
         auto maybe_splat = [&](ir::Value* value) -> ir::Value* {
             if (value->Type()->Is<type::Scalar>() && result_ty->Is<core::type::Vector>()) {
-                return b.Construct(result_ty, value)->Result();
+                return b.Construct(result_ty, value)->Result(0);
             }
             return value;
         };
@@ -224,7 +224,7 @@
         b.InsertBefore(binary, [&] {
             auto* lhs = maybe_splat(binary->LHS());
             auto* rhs = maybe_splat(binary->RHS());
-            result = b.Call(result_ty, helper, lhs, rhs)->Result();
+            result = b.Call(result_ty, helper, lhs, rhs)->Result(0);
         });
         return result;
     }
@@ -238,8 +238,8 @@
         auto* mask = b.Constant(u32(lhs->Type()->DeepestElement()->Size() * 8 - 1));
         auto* masked = b.And(rhs->Type(), rhs, MatchWidth(mask, rhs->Type()));
         masked->InsertBefore(binary);
-        binary->SetOperand(ir::Binary::kRhsOperandOffset, masked->Result());
-        return binary->Result();
+        binary->SetOperand(ir::Binary::kRhsOperandOffset, masked->Result(0));
+        return binary->Result(0);
     }
 };
 
diff --git a/src/tint/lang/core/ir/transform/block_decorated_structs.cc b/src/tint/lang/core/ir/transform/block_decorated_structs.cc
index 32cbbd5..91d4b3d 100644
--- a/src/tint/lang/core/ir/transform/block_decorated_structs.cc
+++ b/src/tint/lang/core/ir/transform/block_decorated_structs.cc
@@ -56,7 +56,7 @@
         if (!var) {
             continue;
         }
-        auto* ptr = var->Result()->Type()->As<core::type::Pointer>();
+        auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
         if (!ptr || !core::IsHostShareable(ptr->AddressSpace())) {
             continue;
         }
@@ -65,7 +65,7 @@
 
     // Now process the buffer variables.
     for (auto* var : buffer_variables) {
-        auto* ptr = var->Result()->Type()->As<core::type::Pointer>();
+        auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
         auto* store_ty = ptr->StoreType();
 
         if (auto* str = store_ty->As<core::type::Struct>(); str && !str->HasFixedFootprint()) {
@@ -93,10 +93,10 @@
         // Replace uses of the old variable.
         // The structure has been wrapped, so replace all uses of the old variable with a member
         // accessor on the new variable.
-        var->Result()->ReplaceAllUsesWith([&](Usage use) -> Value* {
-            auto* access = builder.Access(var->Result()->Type(), new_var, 0_u);
+        var->Result(0)->ReplaceAllUsesWith([&](Usage use) -> Value* {
+            auto* access = builder.Access(var->Result(0)->Type(), new_var, 0_u);
             access->InsertBefore(use.instruction);
-            return access->Result();
+            return access->Result(0);
         });
 
         var->Destroy();
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 dfe8d6a..1b287e6 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
@@ -256,7 +256,7 @@
 
     auto* func = b.Function("foo", ty.u32());
     b.Append(func->Block(), [&] {
-        auto* let_root = b.Let("root", buffer->Result());
+        auto* let_root = b.Let("root", buffer->Result(0));
         auto* let_arr = b.Let("arr", b.Access(ty.ptr(storage, ty.array<i32>()), let_root, 1_u));
         auto* length = b.Call(ty.u32(), core::BuiltinFn::kArrayLength, let_arr);
         b.Return(func, length);
diff --git a/src/tint/lang/core/ir/transform/builtin_polyfill.cc b/src/tint/lang/core/ir/transform/builtin_polyfill.cc
index b59f6e4..07f299c 100644
--- a/src/tint/lang/core/ir/transform/builtin_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/builtin_polyfill.cc
@@ -70,7 +70,7 @@
                 switch (builtin->Func()) {
                     case core::BuiltinFn::kClamp:
                         if (config.clamp_int &&
-                            builtin->Result()->Type()->is_integer_scalar_or_vector()) {
+                            builtin->Result(0)->Type()->is_integer_scalar_or_vector()) {
                             worklist.Push(builtin);
                         }
                         break;
@@ -161,12 +161,12 @@
             }
             TINT_ASSERT_OR_RETURN(replacement);
 
-            if (replacement != builtin->Result()) {
+            if (replacement != builtin->Result(0)) {
                 // Replace the old builtin call result with the new value.
-                if (auto name = ir.NameOf(builtin->Result())) {
+                if (auto name = ir.NameOf(builtin->Result(0))) {
                     ir.SetName(replacement, name);
                 }
-                builtin->Result()->ReplaceAllUsesWith(replacement);
+                builtin->Result(0)->ReplaceAllUsesWith(replacement);
                 builtin->Destroy();
             }
         }
@@ -201,7 +201,7 @@
     /// @param call the builtin call instruction
     /// @returns the replacement value
     ir::Value* ClampInt(ir::CoreBuiltinCall* call) {
-        auto* type = call->Result()->Type();
+        auto* type = call->Result(0)->Type();
         auto* e = call->Args()[0];
         auto* low = call->Args()[1];
         auto* high = call->Args()[2];
@@ -210,7 +210,7 @@
         b.InsertBefore(call, [&] {
             auto* max = b.Call(type, core::BuiltinFn::kMax, e, low);
             auto* min = b.Call(type, core::BuiltinFn::kMin, max, high);
-            result = min->Result();
+            result = min->Result(0);
         });
         return result;
     }
@@ -247,20 +247,20 @@
 
             auto* x = input;
             if (result_ty->is_signed_integer_scalar_or_vector()) {
-                x = b.Bitcast(uint_ty, x)->Result();
+                x = b.Bitcast(uint_ty, x)->Result(0);
             }
             auto* b16 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(16),
                                b.LessThanEqual(bool_ty, x, V(0x0000ffff)));
-            x = b.ShiftLeft(uint_ty, x, b16)->Result();
+            x = b.ShiftLeft(uint_ty, x, b16)->Result(0);
             auto* b8 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(8),
                               b.LessThanEqual(bool_ty, x, V(0x00ffffff)));
-            x = b.ShiftLeft(uint_ty, x, b8)->Result();
+            x = b.ShiftLeft(uint_ty, x, b8)->Result(0);
             auto* b4 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(4),
                               b.LessThanEqual(bool_ty, x, V(0x0fffffff)));
-            x = b.ShiftLeft(uint_ty, x, b4)->Result();
+            x = b.ShiftLeft(uint_ty, x, b4)->Result(0);
             auto* b2 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(2),
                               b.LessThanEqual(bool_ty, x, V(0x3fffffff)));
-            x = b.ShiftLeft(uint_ty, x, b2)->Result();
+            x = b.ShiftLeft(uint_ty, x, b2)->Result(0);
             auto* b1 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(1),
                               b.LessThanEqual(bool_ty, x, V(0x7fffffff)));
             auto* b0 =
@@ -270,9 +270,9 @@
                                 b.Or(uint_ty, b8,
                                      b.Or(uint_ty, b4, b.Or(uint_ty, b2, b.Or(uint_ty, b1, b0))))),
                            b0)
-                         ->Result();
+                         ->Result(0);
             if (result_ty->is_signed_integer_scalar_or_vector()) {
-                result = b.Bitcast(result_ty, result)->Result();
+                result = b.Bitcast(result_ty, result)->Result(0);
             }
         });
         return result;
@@ -310,20 +310,20 @@
 
             auto* x = input;
             if (result_ty->is_signed_integer_scalar_or_vector()) {
-                x = b.Bitcast(uint_ty, x)->Result();
+                x = b.Bitcast(uint_ty, x)->Result(0);
             }
             auto* b16 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(16),
                                b.Equal(bool_ty, b.And(uint_ty, x, V(0x0000ffff)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b16)->Result();
+            x = b.ShiftRight(uint_ty, x, b16)->Result(0);
             auto* b8 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(8),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x000000ff)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b8)->Result();
+            x = b.ShiftRight(uint_ty, x, b8)->Result(0);
             auto* b4 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(4),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x0000000f)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b4)->Result();
+            x = b.ShiftRight(uint_ty, x, b4)->Result(0);
             auto* b2 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(2),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x00000003)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b2)->Result();
+            x = b.ShiftRight(uint_ty, x, b2)->Result(0);
             auto* b1 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(1),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x00000001)), V(0)));
             auto* b0 =
@@ -332,9 +332,9 @@
                            b.Or(uint_ty, b16,
                                 b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1)))),
                            b0)
-                         ->Result();
+                         ->Result(0);
             if (result_ty->is_signed_integer_scalar_or_vector()) {
-                result = b.Bitcast(result_ty, result)->Result();
+                result = b.Bitcast(result_ty, result)->Result(0);
             }
         });
         return result;
@@ -359,10 +359,10 @@
                     auto* o = b.Call(ty.u32(), core::BuiltinFn::kMin, offset, 32_u);
                     auto* c = b.Call(ty.u32(), core::BuiltinFn::kMin, count,
                                      b.Subtract(ty.u32(), 32_u, o));
-                    call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 1, o->Result());
-                    call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 2, c->Result());
+                    call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 1, o->Result(0));
+                    call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 2, c->Result(0));
                 });
-                return call->Result();
+                return call->Result(0);
             }
             default:
                 TINT_UNIMPLEMENTED() << "extractBits polyfill level";
@@ -402,33 +402,33 @@
 
             auto* x = input;
             if (result_ty->is_signed_integer_scalar_or_vector()) {
-                x = b.Bitcast(uint_ty, x)->Result();
+                x = b.Bitcast(uint_ty, x)->Result(0);
                 auto* inverted = b.Complement(uint_ty, x);
                 x = b.Call(uint_ty, core::BuiltinFn::kSelect, inverted, x,
                            b.LessThan(bool_ty, x, V(0x80000000)))
-                        ->Result();
+                        ->Result(0);
             }
             auto* b16 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(16), V(0),
                                b.Equal(bool_ty, b.And(uint_ty, x, V(0xffff0000)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b16)->Result();
+            x = b.ShiftRight(uint_ty, x, b16)->Result(0);
             auto* b8 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(8), V(0),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x0000ff00)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b8)->Result();
+            x = b.ShiftRight(uint_ty, x, b8)->Result(0);
             auto* b4 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(4), V(0),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x000000f0)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b4)->Result();
+            x = b.ShiftRight(uint_ty, x, b4)->Result(0);
             auto* b2 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(2), V(0),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x0000000c)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b2)->Result();
+            x = b.ShiftRight(uint_ty, x, b2)->Result(0);
             auto* b1 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(1), V(0),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x00000002)), V(0)));
             result = b.Or(uint_ty, b16, b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1))))
-                         ->Result();
+                         ->Result(0);
             result = b.Call(uint_ty, core::BuiltinFn::kSelect, result, V(0xffffffff),
                             b.Equal(bool_ty, x, V(0)))
-                         ->Result();
+                         ->Result(0);
             if (result_ty->is_signed_integer_scalar_or_vector()) {
-                result = b.Bitcast(result_ty, result)->Result();
+                result = b.Bitcast(result_ty, result)->Result(0);
             }
         });
         return result;
@@ -466,29 +466,29 @@
 
             auto* x = input;
             if (result_ty->is_signed_integer_scalar_or_vector()) {
-                x = b.Bitcast(uint_ty, x)->Result();
+                x = b.Bitcast(uint_ty, x)->Result(0);
             }
             auto* b16 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(16),
                                b.Equal(bool_ty, b.And(uint_ty, x, V(0x0000ffff)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b16)->Result();
+            x = b.ShiftRight(uint_ty, x, b16)->Result(0);
             auto* b8 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(8),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x000000ff)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b8)->Result();
+            x = b.ShiftRight(uint_ty, x, b8)->Result(0);
             auto* b4 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(4),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x0000000f)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b4)->Result();
+            x = b.ShiftRight(uint_ty, x, b4)->Result(0);
             auto* b2 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(2),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x00000003)), V(0)));
-            x = b.ShiftRight(uint_ty, x, b2)->Result();
+            x = b.ShiftRight(uint_ty, x, b2)->Result(0);
             auto* b1 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(1),
                               b.Equal(bool_ty, b.And(uint_ty, x, V(0x00000001)), V(0)));
             result = b.Or(uint_ty, b16, b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1))))
-                         ->Result();
+                         ->Result(0);
             result = b.Call(uint_ty, core::BuiltinFn::kSelect, result, V(0xffffffff),
                             b.Equal(bool_ty, x, V(0)))
-                         ->Result();
+                         ->Result(0);
             if (result_ty->is_signed_integer_scalar_or_vector()) {
-                result = b.Bitcast(result_ty, result)->Result();
+                result = b.Bitcast(result_ty, result)->Result(0);
             }
         });
         return result;
@@ -513,10 +513,10 @@
                     auto* o = b.Call(ty.u32(), core::BuiltinFn::kMin, offset, 32_u);
                     auto* c = b.Call(ty.u32(), core::BuiltinFn::kMin, count,
                                      b.Subtract(ty.u32(), 32_u, o));
-                    call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 2, o->Result());
-                    call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 3, c->Result());
+                    call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 2, o->Result(0));
+                    call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 3, c->Result(0));
                 });
-                return call->Result();
+                return call->Result(0);
             }
             default:
                 TINT_UNIMPLEMENTED() << "insertBits polyfill level";
@@ -529,7 +529,7 @@
     /// @returns the replacement value
     ir::Value* Saturate(ir::CoreBuiltinCall* call) {
         // Replace `saturate(x)` with `clamp(x, 0., 1.)`.
-        auto* type = call->Result()->Type();
+        auto* type = call->Result(0)->Type();
         ir::Constant* zero = nullptr;
         ir::Constant* one = nullptr;
         if (type->DeepestElement()->Is<core::type::F32>()) {
@@ -541,7 +541,7 @@
         }
         auto* clamp = b.Call(type, core::BuiltinFn::kClamp, Vector{call->Args()[0], zero, one});
         clamp->InsertBefore(call);
-        return clamp->Result();
+        return clamp->Result(0);
     }
 
     /// Polyfill a `textureSampleBaseClampToEdge()` builtin call for 2D F32 textures.
@@ -567,7 +567,7 @@
                 b.Call(vec2f, core::BuiltinFn::kClamp, coords, half_texel, one_minus_half_texel);
             result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleLevel, texture, sampler,
                             clamped, 0_f)
-                         ->Result();
+                         ->Result(0);
         });
         return result;
     }
diff --git a/src/tint/lang/core/ir/transform/combine_access_instructions.cc b/src/tint/lang/core/ir/transform/combine_access_instructions.cc
index 856d4cb..da304ea 100644
--- a/src/tint/lang/core/ir/transform/combine_access_instructions.cc
+++ b/src/tint/lang/core/ir/transform/combine_access_instructions.cc
@@ -52,7 +52,7 @@
             if (auto* access = inst->As<ir::Access>(); access && access->Alive()) {
                 // Look for places where the result of this access instruction is used as a base
                 // pointer for another access instruction.
-                access->Result()->ForEachUse([&](Usage use) {
+                access->Result(0)->ForEachUse([&](Usage use) {
                     auto* child = use.instruction->As<ir::Access>();
                     if (child && use.operand_index == ir::Access::kObjectOperandOffset) {
                         // Push the indices of the parent access instruction into the child.
@@ -69,7 +69,7 @@
                 });
 
                 // If there are no other uses of the access instruction, remove it.
-                if (access->Result()->Usages().IsEmpty()) {
+                if (access->Result(0)->Usages().IsEmpty()) {
                     access->Destroy();
                 }
             }
diff --git a/src/tint/lang/core/ir/transform/conversion_polyfill.cc b/src/tint/lang/core/ir/transform/conversion_polyfill.cc
index 62af849..f99ea69 100644
--- a/src/tint/lang/core/ir/transform/conversion_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/conversion_polyfill.cc
@@ -74,7 +74,7 @@
             }
             if (auto* convert = inst->As<ir::Convert>()) {
                 auto* src_ty = convert->Args()[0]->Type();
-                auto* res_ty = convert->Result()->Type();
+                auto* res_ty = convert->Result(0)->Type();
                 if (config.ftoi &&                          //
                     src_ty->is_float_scalar_or_vector() &&  //
                     res_ty->is_integer_scalar_or_vector()) {
@@ -88,10 +88,10 @@
             auto* replacement = ftoi(convert);
 
             // Replace the old conversion instruction result with the new value.
-            if (auto name = ir.NameOf(convert->Result())) {
+            if (auto name = ir.NameOf(convert->Result(0))) {
                 ir.SetName(replacement, name);
             }
-            convert->Result()->ReplaceAllUsesWith(replacement);
+            convert->Result(0)->ReplaceAllUsesWith(replacement);
             convert->Destroy();
         }
     }
@@ -101,7 +101,7 @@
     /// @param convert the conversion instruction
     /// @returns the replacement value
     ir::Value* ftoi(ir::Convert* convert) {
-        auto* res_ty = convert->Result()->Type();
+        auto* res_ty = convert->Result(0)->Type();
         auto* src_ty = convert->Args()[0]->Type();
         auto* src_el_ty = src_ty->DeepestElement();
 
@@ -187,7 +187,7 @@
                 auto* select_high = b.Call(res_ty, core::BuiltinFn::kSelect, limits.high_limit_i,
                                            select_low, high_cond);
 
-                b.Return(func, select_high->Result());
+                b.Return(func, select_high->Result(0));
             });
             return func;
         });
@@ -195,7 +195,7 @@
         // Call the helper function, splatting the arguments to match the target vector width.
         auto* call = b.Call(res_ty, helper, convert->Args()[0]);
         call->InsertBefore(convert);
-        return call->Result();
+        return call->Result(0);
     }
 
     /// Return a type with element type @p type that has the same number of vector components as
diff --git a/src/tint/lang/core/ir/transform/demote_to_helper.cc b/src/tint/lang/core/ir/transform/demote_to_helper.cc
index 6e9953c..e8aed9b 100644
--- a/src/tint/lang/core/ir/transform/demote_to_helper.cc
+++ b/src/tint/lang/core/ir/transform/demote_to_helper.cc
@@ -145,11 +145,12 @@
             // Move the original instruction into the if-true block.
             auto* result = ifelse->True()->Append(inst);
 
-            TINT_ASSERT(!inst->HasMultiResults());
-            if (inst->HasResults() && !inst->Result()->Type()->Is<core::type::Void>()) {
+            auto results = inst->Results();
+            TINT_ASSERT(results.Length() < 2);
+            if (!results.IsEmpty() && !results[0]->Type()->Is<core::type::Void>()) {
                 // The original instruction had a result, so return it from the if instruction.
-                ifelse->SetResults(Vector{b.InstructionResult(inst->Result()->Type())});
-                inst->Result()->ReplaceAllUsesWith(ifelse->Result());
+                ifelse->SetResults(Vector{b.InstructionResult(results[0]->Type())});
+                results[0]->ReplaceAllUsesWith(ifelse->Result(0));
                 ifelse->True()->Append(b.ExitIf(ifelse, result));
             } else {
                 ifelse->True()->Append(b.ExitIf(ifelse));
diff --git a/src/tint/lang/core/ir/transform/direct_variable_access.cc b/src/tint/lang/core/ir/transform/direct_variable_access.cc
index 6a264b9..5824630 100644
--- a/src/tint/lang/core/ir/transform/direct_variable_access.cc
+++ b/src/tint/lang/core/ir/transform/direct_variable_access.cc
@@ -330,7 +330,7 @@
                         if (size_t array_len = chain.indices.Length(); array_len > 0) {
                             auto* array = ty.array(ty.u32(), static_cast<uint32_t>(array_len));
                             auto* indices = b.Construct(array, std::move(chain.indices));
-                            new_args.Push(indices->Result());
+                            new_args.Push(indices->Result(0));
                         }
                         // Record the parameter shape for the variant's signature.
                         signature.Add(i, chain.shape);
@@ -436,7 +436,7 @@
                                 // Array or matrix access.
                                 // Convert index to u32 if it isn't already.
                                 if (!idx->Type()->Is<type::U32>()) {
-                                    idx = b.Convert(ty.u32(), idx)->Result();
+                                    idx = b.Convert(ty.u32(), idx)->Result(0);
                                 }
 
                                 ops.Push(IndexAccess{});
@@ -455,7 +455,7 @@
                                 chain.indices.Push(idx);
                             }
 
-                            TINT_ASSERT(obj_ty == access->Result()->Type()->UnwrapPtr());
+                            TINT_ASSERT(obj_ty == access->Result(0)->Type()->UnwrapPtr());
                             return access->Object();
                         },
                         [&](Var* var) {
@@ -466,9 +466,9 @@
                             } else {
                                 // Root pointer is a function-scope 'var'
                                 chain.shape.root =
-                                    RootPtrParameter{var->Result()->Type()->As<type::Pointer>()};
+                                    RootPtrParameter{var->Result(0)->Type()->As<type::Pointer>()};
                             }
-                            chain.root_ptr = var->Result();
+                            chain.root_ptr = var->Result(0);
                             return nullptr;
                         },
                         [&](Let* let) { return let->Value(); },  //
@@ -524,7 +524,7 @@
                     root_ptr = root_ptr_param;
                 } else if (auto* global = std::get_if<RootModuleScopeVar>(&shape->root)) {
                     // Root pointer is a module-scope var
-                    root_ptr = global->var->Result();
+                    root_ptr = global->var->Result(0);
                 } else {
                     TINT_ICE() << "unhandled AccessShape root variant";
                 }
@@ -555,12 +555,12 @@
                         return b.Constant(u32(m->member->Index()));
                     }
                     auto* access = b.Access(ty.u32(), indices_param, u32(index_index++));
-                    return access->Result();
+                    return access->Result(0);
                 });
                 auto* access = b.Access(old_param->Type(), root_ptr, std::move(chain));
 
                 // Replace the now removed parameter value with the access instruction
-                old_param->ReplaceAllUsesWith(access->Result());
+                old_param->ReplaceAllUsesWith(access->Result(0));
                 old_param->Destroy();
             }
 
diff --git a/src/tint/lang/core/ir/transform/multiplanar_external_texture.cc b/src/tint/lang/core/ir/transform/multiplanar_external_texture.cc
index 7766c59..60e2b42 100644
--- a/src/tint/lang/core/ir/transform/multiplanar_external_texture.cc
+++ b/src/tint/lang/core/ir/transform/multiplanar_external_texture.cc
@@ -84,7 +84,7 @@
                 if (!var) {
                     continue;
                 }
-                auto* ptr = var->Result()->Type()->As<core::type::Pointer>();
+                auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
                 if (ptr->StoreType()->Is<core::type::ExternalTexture>()) {
                     ReplaceVar(var);
                     to_remove.Push(var);
@@ -148,8 +148,8 @@
         }
 
         // Replace all uses of the old variable with the new ones.
-        ReplaceUses(old_var->Result(), plane_0->Result(), plane_1->Result(),
-                    external_texture_params->Result());
+        ReplaceUses(old_var->Result(0), plane_0->Result(0), plane_1->Result(0),
+                    external_texture_params->Result(0));
     }
 
     /// Replace an external texture function parameter.
@@ -208,11 +208,11 @@
                     Value* plane_1_load = nullptr;
                     Value* params_load = nullptr;
                     b.InsertBefore(load, [&] {
-                        plane_0_load = b.Load(plane_0)->Result();
-                        plane_1_load = b.Load(plane_1)->Result();
-                        params_load = b.Load(params)->Result();
+                        plane_0_load = b.Load(plane_0)->Result(0);
+                        plane_1_load = b.Load(plane_1)->Result(0);
+                        params_load = b.Load(params)->Result(0);
                     });
-                    ReplaceUses(load->Result(), plane_0_load, plane_1_load, params_load);
+                    ReplaceUses(load->Result(0), plane_0_load, plane_1_load, params_load);
                     load->Destroy();
                 },
                 [&](CoreBuiltinCall* call) {
@@ -225,14 +225,14 @@
                         if (coords->Type()->is_signed_integer_vector()) {
                             auto* convert = b.Convert(ty.vec2<u32>(), coords);
                             convert->InsertBefore(call);
-                            coords = convert->Result();
+                            coords = convert->Result(0);
                         }
 
                         // Call the `TextureLoadExternal()` helper function.
                         auto* helper = b.Call(ty.vec4<f32>(), TextureLoadExternal(), plane_0,
                                               plane_1, params, coords);
                         helper->InsertBefore(call);
-                        call->Result()->ReplaceAllUsesWith(helper->Result());
+                        call->Result(0)->ReplaceAllUsesWith(helper->Result(0));
                         call->Destroy();
                     } else if (call->Func() == core::BuiltinFn::kTextureSampleBaseClampToEdge) {
                         // Call the `TextureSampleExternal()` helper function.
@@ -241,7 +241,7 @@
                         auto* helper = b.Call(ty.vec4<f32>(), TextureSampleExternal(), plane_0,
                                               plane_1, params, sampler, coords);
                         helper->InsertBefore(call);
-                        call->Result()->ReplaceAllUsesWith(helper->Result());
+                        call->Result(0)->ReplaceAllUsesWith(helper->Result(0));
                         call->Destroy();
                     } else {
                         TINT_ICE() << "unhandled texture_external builtin call: " << call->Func();
diff --git a/src/tint/lang/core/ir/transform/multiplanar_external_texture_test.cc b/src/tint/lang/core/ir/transform/multiplanar_external_texture_test.cc
index 7ae0828..5e2336a 100644
--- a/src/tint/lang/core/ir/transform/multiplanar_external_texture_test.cc
+++ b/src/tint/lang/core/ir/transform/multiplanar_external_texture_test.cc
@@ -197,7 +197,7 @@
 
     auto* func = b.Function("foo", ty.vec2<u32>());
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         auto* result = b.Call(ty.vec2<u32>(), core::BuiltinFn::kTextureDimensions, load);
         b.Return(func, result);
         mod.SetName(result, "result");
@@ -272,7 +272,7 @@
     auto* coords = b.FunctionParam("coords", ty.vec2<u32>());
     func->SetParams({coords});
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, load, coords);
         b.Return(func, result);
         mod.SetName(result, "result");
@@ -416,7 +416,7 @@
     auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
     func->SetParams({coords});
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, load, coords);
         b.Return(func, result);
         mod.SetName(result, "result");
@@ -562,7 +562,7 @@
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
     func->SetParams({sampler, coords});
     b.Append(func->Block(), [&] {
-        auto* load = b.Load(var->Result());
+        auto* load = b.Load(var->Result(0));
         auto* result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleBaseClampToEdge, load,
                               sampler, coords);
         b.Return(func, result);
@@ -735,7 +735,7 @@
         auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
         bar->SetParams({sampler, coords});
         b.Append(bar->Block(), [&] {
-            auto* load = b.Load(var->Result());
+            auto* load = b.Load(var->Result(0));
             auto* result = b.Call(ty.vec4<f32>(), foo, load, sampler, coords);
             b.Return(bar, result);
             mod.SetName(result, "result");
@@ -920,15 +920,15 @@
         auto* coords_f = b.FunctionParam("coords", ty.vec2<f32>());
         bar->SetParams({sampler, coords_f});
         b.Append(bar->Block(), [&] {
-            auto* load_a = b.Load(var->Result());
+            auto* load_a = b.Load(var->Result(0));
             b.Call(ty.vec2<u32>(), core::BuiltinFn::kTextureDimensions, load_a);
-            auto* load_b = b.Load(var->Result());
+            auto* load_b = b.Load(var->Result(0));
             b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleBaseClampToEdge, load_b, sampler,
                    coords_f);
-            auto* load_c = b.Load(var->Result());
+            auto* load_c = b.Load(var->Result(0));
             b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleBaseClampToEdge, load_c, sampler,
                    coords_f);
-            auto* load_d = b.Load(var->Result());
+            auto* load_d = b.Load(var->Result(0));
             auto* result_a = b.Call(ty.vec4<f32>(), foo, load_d, sampler, coords_f);
             auto* result_b = b.Call(ty.vec4<f32>(), foo, load_d, sampler, coords_f);
             b.Return(bar, b.Add(ty.vec4<f32>(), result_a, result_b));
@@ -1129,11 +1129,11 @@
     auto* coords = b.FunctionParam("coords", ty.vec2<u32>());
     foo->SetParams({coords});
     b.Append(foo->Block(), [&] {
-        auto* load_a = b.Load(var_a->Result());
+        auto* load_a = b.Load(var_a->Result(0));
         b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, load_a, coords);
-        auto* load_b = b.Load(var_b->Result());
+        auto* load_b = b.Load(var_b->Result(0));
         b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, load_b, coords);
-        auto* load_c = b.Load(var_c->Result());
+        auto* load_c = b.Load(var_c->Result(0));
         b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, load_c, coords);
         b.Return(foo);
     });
diff --git a/src/tint/lang/core/ir/transform/preserve_padding.cc b/src/tint/lang/core/ir/transform/preserve_padding.cc
index 0ac5f44..c9a87e8 100644
--- a/src/tint/lang/core/ir/transform/preserve_padding.cc
+++ b/src/tint/lang/core/ir/transform/preserve_padding.cc
@@ -139,7 +139,7 @@
                                 auto* el_ptr =
                                     b.Access(ty.ptr(storage, arr->ElemType()), target, idx);
                                 auto* el_value = b.Access(arr->ElemType(), value_param, idx);
-                                MakeStore(el_ptr->Result(), el_value->Result());
+                                MakeStore(el_ptr->Result(0), el_value->Result(0));
                             });
                     },
                     [&](const type::Matrix* mat) {
@@ -147,7 +147,7 @@
                             auto* col_ptr =
                                 b.Access(ty.ptr(storage, mat->ColumnType()), target, u32(i));
                             auto* col_value = b.Access(mat->ColumnType(), value_param, u32(i));
-                            MakeStore(col_ptr->Result(), col_value->Result());
+                            MakeStore(col_ptr->Result(0), col_value->Result(0));
                         }
                     },
                     [&](const type::Struct* str) {
@@ -156,7 +156,7 @@
                                                      u32(member->Index()));
                             auto* sub_value =
                                 b.Access(member->Type(), value_param, u32(member->Index()));
-                            MakeStore(sub_ptr->Result(), sub_value->Result());
+                            MakeStore(sub_ptr->Result(0), sub_value->Result(0));
                         }
                     });
 
diff --git a/src/tint/lang/core/ir/transform/robustness.cc b/src/tint/lang/core/ir/transform/robustness.cc
index e0e4839..25d9d1e 100644
--- a/src/tint/lang/core/ir/transform/robustness.cc
+++ b/src/tint/lang/core/ir/transform/robustness.cc
@@ -186,7 +186,7 @@
         if (auto* vec = value->Type()->As<type::Vector>()) {
             type = ty.vec(type, vec->Width());
         }
-        return b.Convert(type, value)->Result();
+        return b.Convert(type, value)->Result(0);
     }
 
     /// Clamp operand @p op_idx of @p inst to ensure it is within @p limit.
@@ -205,7 +205,7 @@
                                                   const_limit->Value()->ValueAs<uint32_t>())));
         } else {
             // Clamp it to the dynamic limit.
-            clamped_idx = b.Call(ty.u32(), core::BuiltinFn::kMin, CastToU32(idx), limit)->Result();
+            clamped_idx = b.Call(ty.u32(), core::BuiltinFn::kMin, CastToU32(idx), limit)->Result(0);
         }
 
         // Replace the index operand with the clamped version.
@@ -251,12 +251,12 @@
                         TINT_ASSERT_OR_RETURN_VALUE(base_ptr != nullptr, nullptr);
                         TINT_ASSERT_OR_RETURN_VALUE(i == 1, nullptr);
                         auto* arr_ptr = ty.ptr(base_ptr->AddressSpace(), arr, base_ptr->Access());
-                        object = b.Access(arr_ptr, object, indices[0])->Result();
+                        object = b.Access(arr_ptr, object, indices[0])->Result(0);
                     }
 
                     // Use the `arrayLength` builtin to get the limit of a runtime-sized array.
                     auto* length = b.Call(ty.u32(), core::BuiltinFn::kArrayLength, object);
-                    return b.Subtract(ty.u32(), length, b.Constant(1_u))->Result();
+                    return b.Subtract(ty.u32(), length, b.Constant(1_u))->Result(0);
                 });
 
             // If there's a dynamic limit that needs enforced, clamp the index operand.
@@ -284,7 +284,7 @@
             auto* num_levels = b.Call(ty.u32(), core::BuiltinFn::kTextureNumLevels, args[0]);
             auto* limit = b.Subtract(ty.u32(), num_levels, 1_u);
             clamped_level =
-                b.Call(ty.u32(), core::BuiltinFn::kMin, CastToU32(args[idx]), limit)->Result();
+                b.Call(ty.u32(), core::BuiltinFn::kMin, CastToU32(args[idx]), limit)->Result(0);
             call->SetOperand(CoreBuiltinCall::kArgsOperandOffset + idx, clamped_level);
         };
 
@@ -302,7 +302,7 @@
             auto* limit = b.Subtract(type, dims, one);
             call->SetOperand(
                 CoreBuiltinCall::kArgsOperandOffset + idx,
-                b.Call(type, core::BuiltinFn::kMin, CastToU32(args[idx]), limit)->Result());
+                b.Call(type, core::BuiltinFn::kMin, CastToU32(args[idx]), limit)->Result(0));
         };
 
         // Helper for clamping the array index.
@@ -311,7 +311,7 @@
             auto* limit = b.Subtract(ty.u32(), num_layers, 1_u);
             call->SetOperand(
                 CoreBuiltinCall::kArgsOperandOffset + idx,
-                b.Call(ty.u32(), core::BuiltinFn::kMin, CastToU32(args[idx]), limit)->Result());
+                b.Call(ty.u32(), core::BuiltinFn::kMin, CastToU32(args[idx]), limit)->Result(0));
         };
 
         // Select which arguments to clamp based on the function overload.
diff --git a/src/tint/lang/core/ir/transform/shader_io.cc b/src/tint/lang/core/ir/transform/shader_io.cc
index 6e5f8a1..c2f40ac 100644
--- a/src/tint/lang/core/ir/transform/shader_io.cc
+++ b/src/tint/lang/core/ir/transform/shader_io.cc
@@ -152,7 +152,7 @@
         // Call the original function, passing it the inputs and capturing its return value.
         auto inner_call_args = BuildInnerCallArgs(wrapper);
         auto* inner_result = wrapper.Call(func->ReturnType(), func, std::move(inner_call_args));
-        SetOutputs(wrapper, inner_result->Result());
+        SetOutputs(wrapper, inner_result->Result(0));
         if (vertex_point_size_index) {
             backend->SetOutput(wrapper, vertex_point_size_index.value(), b.Constant(1_f));
         }
@@ -245,7 +245,7 @@
                 for (uint32_t i = 0; i < str->Members().Length(); i++) {
                     construct_args.Push(backend->GetInput(builder, input_idx++));
                 }
-                args.Push(builder.Construct(param->Type(), construct_args)->Result());
+                args.Push(builder.Construct(param->Type(), construct_args)->Result(0));
             } else {
                 args.Push(backend->GetInput(builder, input_idx++));
             }
@@ -261,7 +261,7 @@
         if (auto* str = inner_result->Type()->As<core::type::Struct>()) {
             for (auto* member : str->Members()) {
                 Value* from =
-                    builder.Access(member->Type(), inner_result, u32(member->Index()))->Result();
+                    builder.Access(member->Type(), inner_result, u32(member->Index()))->Result(0);
                 backend->SetOutput(builder, member->Index(), from);
             }
         } else if (!inner_result->Type()->Is<core::type::Void>()) {
diff --git a/src/tint/lang/core/ir/transform/std140.cc b/src/tint/lang/core/ir/transform/std140.cc
index 56897fd..85a2036 100644
--- a/src/tint/lang/core/ir/transform/std140.cc
+++ b/src/tint/lang/core/ir/transform/std140.cc
@@ -79,7 +79,7 @@
             if (!var || !var->Alive()) {
                 continue;
             }
-            auto* ptr = var->Result()->Type()->As<core::type::Pointer>();
+            auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
             if (!ptr || ptr->AddressSpace() != core::AddressSpace::kUniform) {
                 continue;
             }
@@ -93,16 +93,16 @@
         for (auto* var : buffer_variables) {
             // Create a new variable with the modified store type.
             const auto& bp = var->BindingPoint();
-            auto* store_type = var->Result()->Type()->As<core::type::Pointer>()->StoreType();
+            auto* store_type = var->Result(0)->Type()->As<core::type::Pointer>()->StoreType();
             auto* new_var = b.Var(ty.ptr(uniform, RewriteType(store_type)));
             new_var->SetBindingPoint(bp->group, bp->binding);
             if (auto name = ir.NameOf(var)) {
-                ir.SetName(new_var->Result(), name);
+                ir.SetName(new_var->Result(0), name);
             }
 
             // Replace every instruction that uses the original variable.
-            var->Result()->ForEachUse(
-                [&](Usage use) { Replace(use.instruction, new_var->Result()); });
+            var->Result(0)->ForEachUse(
+                [&](Usage use) { Replace(use.instruction, new_var->Result(0)); });
 
             // Replace the original variable with the new variable.
             var->ReplaceWith(new_var);
@@ -201,9 +201,9 @@
         for (uint32_t i = 0; i < mat->columns(); i++) {
             indices.Back() = b.Constant(u32(first_column + i));
             auto* access = b.Access(ty.ptr(uniform, mat->ColumnType()), root, indices);
-            args.Push(b.Load(access->Result())->Result());
+            args.Push(b.Load(access->Result(0))->Result(0));
         }
-        return b.Construct(mat, std::move(args))->Result();
+        return b.Construct(mat, std::move(args))->Result(0);
     }
 
     /// Convert a value that may contain decomposed matrices to a value with the original type.
@@ -234,15 +234,15 @@
                                 Vector<Value*, 4> columns;
                                 for (uint32_t i = 0; i < mat->columns(); i++) {
                                     auto* extract = b.Access(mat->ColumnType(), input, u32(index));
-                                    columns.Push(extract->Result());
+                                    columns.Push(extract->Result(0));
                                     index++;
                                 }
-                                args.Push(b.Construct(mat, std::move(columns))->Result());
+                                args.Push(b.Construct(mat, std::move(columns))->Result(0));
                             } else {
                                 // Extract and convert the member.
                                 auto* type = input_str->Element(index);
                                 auto* extract = b.Access(type, input, u32(index));
-                                args.Push(Convert(extract->Result(), member->Type()));
+                                args.Push(Convert(extract->Result(0), member->Type()));
                                 index++;
                             }
                         }
@@ -254,7 +254,7 @@
                 });
 
                 // Call the helper function to convert the struct.
-                return b.Call(str, helper, source)->Result();
+                return b.Call(str, helper, source)->Result(0);
             },
             [&](const core::type::Array* arr) -> Value* {
                 // Create a loop that copies and converts each element of the array.
@@ -263,10 +263,10 @@
                 b.LoopRange(ty, 0_u, u32(arr->ConstantCount().value()), 1_u, [&](Value* idx) {
                     // Convert arr[idx] and store to new_arr[idx];
                     auto* to = b.Access(ty.ptr(function, arr->ElemType()), new_arr, idx);
-                    auto* from = b.Access(el_ty, source, idx)->Result();
+                    auto* from = b.Access(el_ty, source, idx)->Result(0);
                     b.Store(to, Convert(from, arr->ElemType()));
                 });
-                return b.Load(new_arr)->Result();
+                return b.Load(new_arr)->Result(0);
             },
             [&](Default) { return source; });
     }
@@ -309,23 +309,23 @@
                             current_type = ty.ptr(uniform, RewriteType(current_type));
                         }
                         auto* new_access = b.Access(current_type, replacement, std::move(indices));
-                        replacement = new_access->Result();
+                        replacement = new_access->Result(0);
                     }
 
                     // Replace every instruction that uses the original access instruction.
-                    access->Result()->ForEachUse(
+                    access->Result(0)->ForEachUse(
                         [&](Usage use) { Replace(use.instruction, replacement); });
                     access->Destroy();
                 },
                 [&](Load* load) {
                     if (!replacement->Type()->Is<core::type::Pointer>()) {
                         // We have already loaded to a value type, so this load just folds away.
-                        load->Result()->ReplaceAllUsesWith(replacement);
+                        load->Result(0)->ReplaceAllUsesWith(replacement);
                     } else {
                         // Load the decomposed value and then convert it to the original type.
                         auto* decomposed = b.Load(replacement);
-                        auto* converted = Convert(decomposed->Result(), load->Result()->Type());
-                        load->Result()->ReplaceAllUsesWith(converted);
+                        auto* converted = Convert(decomposed->Result(0), load->Result(0)->Type());
+                        load->Result(0)->ReplaceAllUsesWith(converted);
                     }
                     load->Destroy();
                 },
@@ -333,8 +333,9 @@
                     if (!replacement->Type()->Is<core::type::Pointer>()) {
                         // We have loaded a decomposed matrix and reconstructed it, so this is now
                         // extracting from a value type.
-                        auto* access = b.Access(load->Result()->Type(), replacement, load->Index());
-                        load->Result()->ReplaceAllUsesWith(access->Result());
+                        auto* access =
+                            b.Access(load->Result(0)->Type(), replacement, load->Index());
+                        load->Result(0)->ReplaceAllUsesWith(access->Result(0));
                         load->Destroy();
                     } else {
                         // There was no decomposed matrix on the path to this instruction so just
@@ -344,7 +345,7 @@
                 },
                 [&](Let* let) {
                     // Let instructions just fold away.
-                    let->Result()->ForEachUse(
+                    let->Result(0)->ForEachUse(
                         [&](Usage use) { Replace(use.instruction, replacement); });
                     let->Destroy();
                 });
diff --git a/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc b/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc
index 68e365a..134844c 100644
--- a/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc
+++ b/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc
@@ -57,7 +57,7 @@
         Vector<Construct*, 8> worklist;
         for (auto inst : ir.instructions.Objects()) {
             if (auto* construct = inst->As<Construct>(); construct && construct->Alive()) {
-                if (construct->Result()->Type()->As<type::Matrix>()) {
+                if (construct->Result(0)->Type()->As<type::Matrix>()) {
                     if (construct->Operands().Length() > 0 &&
                         construct->Operands()[0]->Type()->Is<type::Scalar>()) {
                         b.InsertBefore(construct, [&] {  //
@@ -72,7 +72,7 @@
     /// Replace a matrix construct instruction.
     /// @param construct the instruction to replace
     void ReplaceConstructor(Construct* construct) {
-        auto* mat = construct->Result()->Type()->As<type::Matrix>();
+        auto* mat = construct->Result(0)->Type()->As<type::Matrix>();
         auto* col = mat->ColumnType();
         const auto& scalars = construct->Operands();
 
@@ -83,12 +83,12 @@
             for (uint32_t r = 0; r < col->Width(); r++) {
                 values.Push(scalars[c * col->Width() + r]);
             }
-            columns.Push(b.Construct(col, std::move(values))->Result());
+            columns.Push(b.Construct(col, std::move(values))->Result(0));
         }
 
         // Construct the matrix from the column vectors and replace the original instruction.
-        auto* replacement = b.Construct(mat, std::move(columns))->Result();
-        construct->Result()->ReplaceAllUsesWith(replacement);
+        auto* replacement = b.Construct(mat, std::move(columns))->Result(0);
+        construct->Result(0)->ReplaceAllUsesWith(replacement);
         construct->Destroy();
     }
 };
diff --git a/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors_test.cc b/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors_test.cc
index 4f42888..826ba54 100644
--- a/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors_test.cc
+++ b/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors_test.cc
@@ -45,7 +45,7 @@
     auto* func = b.Function("foo", mat);
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -72,7 +72,7 @@
     func->SetParams({value});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, value);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -101,7 +101,7 @@
     func->SetParams({v1, v2, v3});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -131,7 +131,7 @@
     func->SetParams({v1, v2, v3, v4});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -172,7 +172,7 @@
     func->SetParams({v1, v2, v3, v4, v5, v6});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4, v5, v6);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -215,7 +215,7 @@
     func->SetParams({v1, v2, v3, v4, v5, v6, v7, v8});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4, v5, v6, v7, v8);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -256,7 +256,7 @@
     func->SetParams({v1, v2, v3, v4, v5, v6});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4, v5, v6);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -301,7 +301,7 @@
     func->SetParams({v1, v2, v3, v4, v5, v6, v7, v8, v9});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4, v5, v6, v7, v8, v9);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -349,7 +349,7 @@
     func->SetParams({v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -393,7 +393,7 @@
     func->SetParams({v1, v2, v3, v4, v5, v6, v7, v8});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4, v5, v6, v7, v8);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -442,7 +442,7 @@
     func->SetParams({v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -496,7 +496,7 @@
     b.Append(func->Block(), [&] {
         auto* construct =
             b.Construct(mat, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
@@ -542,7 +542,7 @@
     func->SetParams({v1, v2, v3, v4, v5, v6, v7, v8, v9});
     b.Append(func->Block(), [&] {
         auto* construct = b.Construct(mat, v1, v2, v3, v4, v5, v6, v7, v8, v9);
-        b.Return(func, construct->Result());
+        b.Return(func, construct->Result(0));
     });
 
     auto* src = R"(
diff --git a/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.cc b/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.cc
index 927844b..58928cb 100644
--- a/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.cc
@@ -101,10 +101,10 @@
         uint32_t next_id = 0;
         for (auto inst : *ir.root_block) {
             if (auto* var = inst->As<Var>()) {
-                auto* ptr = var->Result()->Type()->As<core::type::Pointer>();
+                auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
                 if (ptr && ptr->AddressSpace() == core::AddressSpace::kWorkgroup) {
                     // Record the usage of the variable for each block that references it.
-                    var->Result()->ForEachUse([&](const Usage& use) {
+                    var->Result(0)->ForEachUse([&](const Usage& use) {
                         block_to_direct_vars.GetOrZero(use.instruction->Block())->Add(var);
                     });
                     var_to_id.Add(var, next_id++);
@@ -138,7 +138,7 @@
         // Build list of store descriptors for all workgroup variables.
         StoreMap stores;
         for (auto* var : sorted_vars) {
-            PrepareStores(var, var->Result()->Type()->UnwrapPtr(), 1, {}, stores);
+            PrepareStores(var, var->Result(0)->Type()->UnwrapPtr(), 1, {}, stores);
         }
 
         // Sort the iteration counts to get deterministic output in tests.
@@ -279,7 +279,7 @@
                                                             BuiltinValue::kLocalInvocationIndex) {
                         auto* access = b.Access(ty.u32(), param, u32(member->Index()));
                         access->InsertBefore(func->Block()->Front());
-                        return access->Result();
+                        return access->Result(0);
                     }
                 }
             } else {
@@ -305,7 +305,7 @@
     /// @param total_count the total number of elements that will be zeroed
     /// @param linear_index the linear index of the single element that will be zeroed
     void GenerateStore(const Store& store, uint32_t total_count, Value* linear_index) {
-        auto* to = store.var->Result();
+        auto* to = store.var->Result(0);
         if (!store.indices.IsEmpty()) {
             // Build the access indices to get to the target element.
             // We walk backwards along the index list so that adjacent invocation store to
@@ -319,10 +319,10 @@
                     auto array_index = std::get<ArrayIndex>(idx);
                     Value* index = linear_index;
                     if (count > 1) {
-                        index = b.Divide(ty.u32(), index, u32(count))->Result();
+                        index = b.Divide(ty.u32(), index, u32(count))->Result(0);
                     }
                     if (total_count > count * array_index.count) {
-                        index = b.Modulo(ty.u32(), index, u32(array_index.count))->Result();
+                        index = b.Modulo(ty.u32(), index, u32(array_index.count))->Result(0);
                     }
                     indices.Push(index);
                     count *= array_index.count;
@@ -332,7 +332,7 @@
                 }
             }
             indices.Reverse();
-            to = b.Access(ty.ptr(workgroup, store.store_type), to, indices)->Result();
+            to = b.Access(ty.ptr(workgroup, store.store_type), to, indices)->Result(0);
         }
 
         // Generate the store instruction.
diff --git a/src/tint/lang/core/ir/unary_test.cc b/src/tint/lang/core/ir/unary_test.cc
index bbdccdd..96aa138 100644
--- a/src/tint/lang/core/ir/unary_test.cc
+++ b/src/tint/lang/core/ir/unary_test.cc
@@ -75,10 +75,9 @@
 
 TEST_F(IR_UnaryTest, Result) {
     auto* inst = b.Negation(mod.Types().i32(), 4_i);
-    EXPECT_TRUE(inst->HasResults());
-    EXPECT_FALSE(inst->HasMultiResults());
-    EXPECT_TRUE(inst->Result()->Is<InstructionResult>());
-    EXPECT_EQ(inst->Result()->Source(), inst);
+    EXPECT_EQ(inst->Results().Length(), 1u);
+    EXPECT_TRUE(inst->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(inst->Result(0)->Source(), inst);
 }
 
 TEST_F(IR_UnaryTest, Fail_NullType) {
@@ -96,8 +95,8 @@
     auto* new_inst = clone_ctx.Clone(inst);
 
     EXPECT_NE(inst, new_inst);
-    EXPECT_NE(nullptr, new_inst->Result());
-    EXPECT_NE(inst->Result(), new_inst->Result());
+    EXPECT_NE(nullptr, new_inst->Result(0));
+    EXPECT_NE(inst->Result(0), new_inst->Result(0));
 
     EXPECT_EQ(UnaryOp::kComplement, new_inst->Op());
 
diff --git a/src/tint/lang/core/ir/unreachable_test.cc b/src/tint/lang/core/ir/unreachable_test.cc
index 23359f3..97a56dd 100644
--- a/src/tint/lang/core/ir/unreachable_test.cc
+++ b/src/tint/lang/core/ir/unreachable_test.cc
@@ -43,8 +43,7 @@
 TEST_F(IR_UnreachableTest, Result) {
     auto* inst = b.Unreachable();
 
-    EXPECT_FALSE(inst->HasResults());
-    EXPECT_FALSE(inst->HasMultiResults());
+    EXPECT_TRUE(inst->Results().IsEmpty());
 }
 
 TEST_F(IR_UnreachableTest, Clone) {
diff --git a/src/tint/lang/core/ir/user_call.h b/src/tint/lang/core/ir/user_call.h
index 4f74824..775d7a5 100644
--- a/src/tint/lang/core/ir/user_call.h
+++ b/src/tint/lang/core/ir/user_call.h
@@ -55,8 +55,8 @@
     /// @copydoc Instruction::Clone()
     UserCall* Clone(CloneContext& ctx) override;
 
-    /// @returns the call arguments
-    tint::Slice<Value*> Args() override { return operands_.Slice().Offset(kArgsOperandOffset); }
+    /// @returns the offset of the arguments in Operands()
+    size_t ArgsOperandOffset() const override { return kArgsOperandOffset; }
 
     /// Replaces the call arguments to @p arguments
     /// @param arguments the new call arguments
diff --git a/src/tint/lang/core/ir/user_call_test.cc b/src/tint/lang/core/ir/user_call_test.cc
index 1758bae..567e1f6 100644
--- a/src/tint/lang/core/ir/user_call_test.cc
+++ b/src/tint/lang/core/ir/user_call_test.cc
@@ -53,10 +53,9 @@
     auto* arg2 = b.Constant(2_u);
     auto* e = b.Call(mod.Types().void_(), func, Vector{arg1, arg2});
 
-    EXPECT_TRUE(e->HasResults());
-    EXPECT_FALSE(e->HasMultiResults());
-    EXPECT_TRUE(e->Result()->Is<InstructionResult>());
-    EXPECT_EQ(e->Result()->Source(), e);
+    EXPECT_EQ(e->Results().Length(), 1u);
+    EXPECT_TRUE(e->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(e->Result(0)->Source(), e);
 }
 
 TEST_F(IR_UserCallTest, Fail_NullType) {
@@ -77,8 +76,8 @@
     auto* new_e = clone_ctx.Clone(e);
 
     EXPECT_NE(e, new_e);
-    EXPECT_NE(nullptr, new_e->Result());
-    EXPECT_NE(e->Result(), new_e->Result());
+    EXPECT_NE(nullptr, new_e->Result(0));
+    EXPECT_NE(e->Result(0), new_e->Result(0));
 
     EXPECT_EQ(new_func, new_e->Target());
 
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 8550d2f..02f6d98 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -476,21 +476,19 @@
         AddError(inst, InstError(inst, "destroyed instruction found in instruction list"));
         return;
     }
-    if (inst->HasResults()) {
-        auto results = inst->Results();
-        for (size_t i = 0; i < results.Length(); ++i) {
-            auto* res = results[i];
-            if (!res) {
-                AddResultError(inst, i, InstError(inst, "instruction result is undefined"));
-                continue;
-            }
+    auto results = inst->Results();
+    for (size_t i = 0; i < results.Length(); ++i) {
+        auto* res = results[i];
+        if (!res) {
+            AddResultError(inst, i, InstError(inst, "instruction result is undefined"));
+            continue;
+        }
 
-            if (res->Source() == nullptr) {
-                AddResultError(inst, i, InstError(inst, "instruction result source is undefined"));
-            } else if (res->Source() != inst) {
-                AddResultError(inst, i,
-                               InstError(inst, "instruction result source has wrong instruction"));
-            }
+        if (res->Source() == nullptr) {
+            AddResultError(inst, i, InstError(inst, "instruction result source is undefined"));
+        } else if (res->Source() != inst) {
+            AddResultError(inst, i,
+                           InstError(inst, "instruction result source has wrong instruction"));
         }
     }
 
@@ -536,8 +534,8 @@
 }
 
 void Validator::CheckVar(Var* var) {
-    if (var->Result() && var->Initializer()) {
-        if (var->Initializer()->Type() != var->Result()->Type()->UnwrapPtr()) {
+    if (var->Result(0) && var->Initializer()) {
+        if (var->Initializer()->Type() != var->Result(0)->Type()->UnwrapPtr()) {
             AddError(var, InstError(var, "initializer has incorrect type"));
         }
     }
@@ -546,8 +544,8 @@
 void Validator::CheckLet(Let* let) {
     CheckOperandNotNull(let, let->Value(), Let::kValueOperandOffset);
 
-    if (let->Result() && let->Value()) {
-        if (let->Result()->Type() != let->Value()->Type()) {
+    if (let->Result(0) && let->Value()) {
+        if (let->Result(0)->Type() != let->Value()->Type()) {
             AddError(let, InstError(let, "result type does not match value type"));
         }
     }
@@ -574,7 +572,7 @@
     auto result = core::intrinsic::LookupFn(context, call->FriendlyName().c_str(), call->FuncId(),
                                             args, core::EvaluationStage::kRuntime, Source{});
     if (result) {
-        if (result->return_type != call->Result()->Type()) {
+        if (result->return_type != call->Result(0)->Type()) {
             AddError(call, InstError(call, "call result type does not match builtin return type"));
         }
     }
@@ -645,8 +643,8 @@
         }
     }
 
-    auto* want_ty = a->Result()->Type()->UnwrapPtr();
-    bool want_ptr = a->Result()->Type()->Is<core::type::Pointer>();
+    auto* want_ty = a->Result(0)->Type()->UnwrapPtr();
+    bool want_ptr = a->Result(0)->Type()->Is<core::type::Pointer>();
     if (TINT_UNLIKELY(ty != want_ty || is_ptr != want_ptr)) {
         std::string want =
             want_ptr ? "ptr<" + want_ty->FriendlyName() + ">" : want_ty->FriendlyName();
@@ -663,8 +661,8 @@
 void Validator::CheckUnary(Unary* u) {
     CheckOperandNotNull(u, u->Val(), Unary::kValueOperandOffset);
 
-    if (u->Result() && u->Val()) {
-        if (u->Result()->Type() != u->Val()->Type()) {
+    if (u->Result(0) && u->Val()) {
+        if (u->Result(0)->Type() != u->Val()->Type()) {
             AddError(u, InstError(u, "result type must match value type"));
         }
     }
@@ -860,7 +858,7 @@
                          LoadVectorElement::kFromOperandOffset,
                          LoadVectorElement::kIndexOperandOffset);
 
-    if (auto* res = l->Result()) {
+    if (auto* res = l->Result(0)) {
         if (auto* el_ty = GetVectorPtrElementType(l, LoadVectorElement::kFromOperandOffset)) {
             if (res->Type() != el_ty) {
                 AddResultError(l, 0, "result type does not match vector pointer element type");
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index d9f6abf..728d65d 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -1112,7 +1112,7 @@
     auto* v = sb.Var(ty.ptr<function, f32>());
     sb.Return(f);
 
-    v->Result()->SetSource(nullptr);
+    v->Result(0)->SetSource(nullptr);
 
     auto res = ir::Validate(mod);
     ASSERT_FALSE(res);
@@ -2916,7 +2916,7 @@
 
     b.Append(f->Block(), [&] {
         auto* var = b.Var(ty.ptr<function, i32>());
-        b.Append(mod.instructions.Create<ir::Store>(var->Result(), nullptr));
+        b.Append(mod.instructions.Create<ir::Store>(var->Result(0), nullptr));
         b.Return(f);
     });
 
@@ -2946,7 +2946,7 @@
 
     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.Append(mod.instructions.Create<ir::Store>(var->Result(0), b.Constant(42_u)));
         b.Return(f);
     });
 
@@ -2977,7 +2977,7 @@
 
     b.Append(f->Block(), [&] {
         auto* var = b.Var(ty.ptr<function, vec3<f32>>());
-        b.Append(mod.instructions.Create<ir::LoadVectorElement>(nullptr, var->Result(),
+        b.Append(mod.instructions.Create<ir::LoadVectorElement>(nullptr, var->Result(0),
                                                                 b.Constant(1_i)));
         b.Return(f);
     });
@@ -3039,7 +3039,7 @@
     b.Append(f->Block(), [&] {
         auto* var = b.Var(ty.ptr<function, vec3<f32>>());
         b.Append(mod.instructions.Create<ir::LoadVectorElement>(b.InstructionResult(ty.f32()),
-                                                                var->Result(), nullptr));
+                                                                var->Result(0), nullptr));
         b.Return(f);
     });
 
@@ -3098,7 +3098,7 @@
 
     b.Append(f->Block(), [&] {
         auto* var = b.Var(ty.ptr<function, vec3<f32>>());
-        b.Append(mod.instructions.Create<ir::StoreVectorElement>(var->Result(), nullptr,
+        b.Append(mod.instructions.Create<ir::StoreVectorElement>(var->Result(0), nullptr,
                                                                  b.Constant(2_i)));
         b.Return(f);
     });
@@ -3137,7 +3137,7 @@
 
     b.Append(f->Block(), [&] {
         auto* var = b.Var(ty.ptr<function, vec3<f32>>());
-        b.Append(mod.instructions.Create<ir::StoreVectorElement>(var->Result(), b.Constant(1_i),
+        b.Append(mod.instructions.Create<ir::StoreVectorElement>(var->Result(0), b.Constant(1_i),
                                                                  nullptr));
         b.Return(f);
     });
diff --git a/src/tint/lang/core/ir/value_test.cc b/src/tint/lang/core/ir/value_test.cc
index 6ea04e7..a1adee2 100644
--- a/src/tint/lang/core/ir/value_test.cc
+++ b/src/tint/lang/core/ir/value_test.cc
@@ -71,7 +71,7 @@
         {
             Module mod;
             Builder b{mod};
-            auto* val = b.Add(mod.Types().i32(), 1_i, 2_i)->Result();
+            auto* val = b.Add(mod.Types().i32(), 1_i, 2_i)->Result(0);
             val->Destroy();
         },
         "");
diff --git a/src/tint/lang/core/ir/var_test.cc b/src/tint/lang/core/ir/var_test.cc
index bd3d59a..9b2cc7c 100644
--- a/src/tint/lang/core/ir/var_test.cc
+++ b/src/tint/lang/core/ir/var_test.cc
@@ -53,10 +53,9 @@
 
 TEST_F(IR_VarTest, Results) {
     auto* var = b.Var(ty.ptr<function, f32>());
-    EXPECT_TRUE(var->HasResults());
-    EXPECT_FALSE(var->HasMultiResults());
-    EXPECT_TRUE(var->Result()->Is<InstructionResult>());
-    EXPECT_EQ(var->Result()->Source(), var);
+    EXPECT_EQ(var->Results().Length(), 1u);
+    EXPECT_TRUE(var->Result(0)->Is<InstructionResult>());
+    EXPECT_EQ(var->Result(0)->Source(), var);
 }
 
 TEST_F(IR_VarTest, Initializer_Usage) {
@@ -83,9 +82,9 @@
     auto* new_v = clone_ctx.Clone(v);
 
     EXPECT_NE(v, new_v);
-    ASSERT_NE(nullptr, new_v->Result());
-    EXPECT_NE(v->Result(), new_v->Result());
-    EXPECT_EQ(new_v->Result()->Type(),
+    ASSERT_NE(nullptr, new_v->Result(0));
+    EXPECT_NE(v->Result(0), new_v->Result(0));
+    EXPECT_EQ(new_v->Result(0)->Type(),
               mod.Types().ptr(core::AddressSpace::kFunction, mod.Types().f32()));
 
     auto new_val = v->Initializer()->As<Constant>()->Value();
diff --git a/src/tint/lang/msl/writer/printer/printer.cc b/src/tint/lang/msl/writer/printer/printer.cc
index 02fb9c5..be96561 100644
--- a/src/tint/lang/msl/writer/printer/printer.cc
+++ b/src/tint/lang/msl/writer/printer/printer.cc
@@ -1028,7 +1028,7 @@
 
             // Instruction has a single result value.
             // Check to see if the result of this instruction is a candidate for inlining.
-            auto* result = inst->Result();
+            auto* result = inst->Result(0);
             // Only values with a single usage can be inlined.
             // Named values are not inlined, as we want to emit the name for a let.
             if (result->Usages().Count() == 1 && !ir_.NameOf(result).IsValid()) {
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index 03456a9..cb2f29e 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -538,7 +538,7 @@
     /// Get the result ID of the instruction result `value`, emitting its instruction if necessary.
     /// @param inst the instruction to get the ID for
     /// @returns the result ID of the instruction
-    uint32_t Value(core::ir::Instruction* inst) { return Value(inst->Result()); }
+    uint32_t Value(core::ir::Instruction* inst) { return Value(inst->Result(0)); }
 
     /// Get the result ID of the value `value`, emitting its instruction if necessary.
     /// @param value the value to get the ID for
@@ -897,7 +897,7 @@
                 TINT_ICE_ON_NO_MATCH);
 
             // Set the name for the SPIR-V result ID if provided in the module.
-            if (inst->Result() && !inst->Is<core::ir::Var>()) {
+            if (inst->Result(0) && !inst->Is<core::ir::Var>()) {
                 if (auto name = ir_.NameOf(inst)) {
                     module_.PushDebug(spv::Op::OpName, {Value(inst), Operand(name.Name())});
                 }
@@ -974,11 +974,11 @@
 
         uint32_t true_label = merge_label;
         uint32_t false_label = merge_label;
-        if (true_block->Length() > 1 || i->HasResults() ||
+        if (true_block->Length() > 1 || !i->Results().IsEmpty() ||
             (true_block->Terminator() && !true_block->Terminator()->Is<core::ir::ExitIf>())) {
             true_label = Label(true_block);
         }
-        if (false_block->Length() > 1 || i->HasResults() ||
+        if (false_block->Length() > 1 || !i->Results().IsEmpty() ||
             (false_block->Terminator() && !false_block->Terminator()->Is<core::ir::ExitIf>())) {
             false_label = Label(false_block);
         }
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
index ddce778..a668bbd 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
@@ -349,7 +349,7 @@
                     }
                 });
             }
-            return sum->Result();
+            return sum->Result(0);
         }
 
         // Replace the builtin call with a call to the spirv.dot intrinsic.
diff --git a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
index 7df2b91..033cb2a 100644
--- a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
+++ b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
@@ -55,7 +55,7 @@
         if (auto* construct = inst->As<core::ir::Construct>()) {
             // A vector constructor with a single scalar argument needs to be modified to replicate
             // the argument N times.
-            auto* vec = construct->Result()->Type()->As<core::type::Vector>();
+            auto* vec = construct->Result(0)->Type()->As<core::type::Vector>();
             if (vec &&  //
                 construct->Args().Length() == 1 &&
                 construct->Args()[0]->Type()->Is<core::type::Scalar>()) {
@@ -66,7 +66,7 @@
         } else if (auto* binary = inst->As<core::ir::Binary>()) {
             // A binary instruction that mixes vector and scalar operands needs to have the scalar
             // operand replaced with an explicit vector constructor.
-            if (binary->Result()->Type()->Is<core::type::Vector>()) {
+            if (binary->Result(0)->Type()->Is<core::type::Vector>()) {
                 if (binary->LHS()->Type()->Is<core::type::Scalar>() ||
                     binary->RHS()->Type()->Is<core::type::Scalar>()) {
                     binary_worklist.Push(binary);
@@ -76,7 +76,7 @@
             // A mix builtin call that mixes vector and scalar operands needs to have the scalar
             // operand replaced with an explicit vector constructor.
             if (builtin->Func() == core::BuiltinFn::kMix) {
-                if (builtin->Result()->Type()->Is<core::type::Vector>()) {
+                if (builtin->Result(0)->Type()->Is<core::type::Vector>()) {
                     if (builtin->Args()[2]->Type()->Is<core::type::Scalar>()) {
                         builtin_worklist.Push(builtin);
                     }
@@ -88,19 +88,19 @@
     // Helper to expand a scalar operand of an instruction by replacing it with an explicitly
     // constructed vector that matches the result type.
     auto expand_operand = [&](core::ir::Instruction* inst, size_t operand_idx) {
-        auto* vec = inst->Result()->Type()->As<core::type::Vector>();
+        auto* vec = inst->Result(0)->Type()->As<core::type::Vector>();
 
         Vector<core::ir::Value*, 4> args;
         args.Resize(vec->Width(), inst->Operands()[operand_idx]);
 
         auto* construct = b.Construct(vec, std::move(args));
         construct->InsertBefore(inst);
-        inst->SetOperand(operand_idx, construct->Result());
+        inst->SetOperand(operand_idx, construct->Result(0));
     };
 
     // Replace scalar operands to binary instructions that produce vectors.
     for (auto* binary : binary_worklist) {
-        auto* result_ty = binary->Result()->Type();
+        auto* result_ty = binary->Result(0)->Type();
         if (result_ty->is_float_vector() && binary->Op() == core::ir::BinaryOp::kMultiply) {
             // Use OpVectorTimesScalar for floating point multiply.
             auto* vts =
@@ -113,9 +113,9 @@
                 vts->AppendArg(binary->RHS());
             }
             if (auto name = ir.NameOf(binary)) {
-                ir.SetName(vts->Result(), name);
+                ir.SetName(vts->Result(0), name);
             }
-            binary->Result()->ReplaceAllUsesWith(vts->Result());
+            binary->Result(0)->ReplaceAllUsesWith(vts->Result(0));
             binary->ReplaceWith(vts);
             binary->Destroy();
         } else {
diff --git a/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.cc b/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.cc
index bfd212f..43ede7c 100644
--- a/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.cc
+++ b/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.cc
@@ -61,7 +61,7 @@
                 binary_worklist.Push(binary);
             }
         } else if (auto* convert = inst->As<core::ir::Convert>()) {
-            if (convert->Result()->Type()->Is<core::type::Matrix>()) {
+            if (convert->Result(0)->Type()->Is<core::type::Matrix>()) {
                 convert_worklist.Push(convert);
             }
         }
@@ -73,14 +73,14 @@
         auto* rhs = binary->RHS();
         auto* lhs_ty = lhs->Type();
         auto* rhs_ty = rhs->Type();
-        auto* ty = binary->Result()->Type();
+        auto* ty = binary->Result(0)->Type();
 
         // Helper to replace the instruction with a new one.
         auto replace = [&](core::ir::Instruction* inst) {
             if (auto name = ir.NameOf(binary)) {
-                ir.SetName(inst->Result(), name);
+                ir.SetName(inst->Result(0), name);
             }
-            binary->Result()->ReplaceAllUsesWith(inst->Result());
+            binary->Result(0)->ReplaceAllUsesWith(inst->Result(0));
             binary->ReplaceWith(inst);
             binary->Destroy();
         };
@@ -94,7 +94,7 @@
                     auto* lhs_col = b.Access(mat->ColumnType(), lhs, u32(col));
                     auto* rhs_col = b.Access(mat->ColumnType(), rhs, u32(col));
                     auto* add = b.Binary(op, mat->ColumnType(), lhs_col, rhs_col);
-                    args.Push(add->Result());
+                    args.Push(add->Result(0));
                 });
             }
             replace(b.Construct(ty, std::move(args)));
@@ -141,7 +141,7 @@
     for (auto* convert : convert_worklist) {
         auto* arg = convert->Args()[core::ir::Convert::kValueOperandOffset];
         auto* in_mat = arg->Type()->As<core::type::Matrix>();
-        auto* out_mat = convert->Result()->Type()->As<core::type::Matrix>();
+        auto* out_mat = convert->Result(0)->Type()->As<core::type::Matrix>();
 
         // Extract and convert each column separately.
         Vector<core::ir::Value*, 4> args;
@@ -149,16 +149,16 @@
             b.InsertBefore(convert, [&] {
                 auto* col = b.Access(in_mat->ColumnType(), arg, u32(c));
                 auto* new_col = b.Convert(out_mat->ColumnType(), col);
-                args.Push(new_col->Result());
+                args.Push(new_col->Result(0));
             });
         }
 
         // Reconstruct the result matrix from the converted columns.
         auto* construct = b.Construct(out_mat, std::move(args));
         if (auto name = ir.NameOf(convert)) {
-            ir.SetName(construct->Result(), name);
+            ir.SetName(construct->Result(0), name);
         }
-        convert->Result()->ReplaceAllUsesWith(construct->Result());
+        convert->Result(0)->ReplaceAllUsesWith(construct->Result(0));
         convert->ReplaceWith(construct);
         convert->Destroy();
     }
diff --git a/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc b/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
index e58d05d..2597941 100644
--- a/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
+++ b/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
@@ -189,13 +189,13 @@
 
         core::ir::Instruction* load = nullptr;
         if (to_replace.vector_access_type) {
-            load = builder.LoadVectorElement(new_access->Result(), vector_index);
+            load = builder.LoadVectorElement(new_access->Result(0), vector_index);
         } else {
             load = builder.Load(new_access);
         }
 
         // Replace all uses of the old access instruction with the loaded result.
-        access->Result()->ReplaceAllUsesWith(load->Result());
+        access->Result()->ReplaceAllUsesWith(load->Result(0));
         access->ReplaceWith(load);
         access->Destroy();
     }
diff --git a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
index e472629..8190dd0 100644
--- a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
+++ b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
@@ -574,13 +574,13 @@
         auto b = builder_.Append(current_block_);
         if (auto* v = std::get_if<core::ir::Value*>(&lhs)) {
             auto* load = b.Load(*v);
-            auto* ty = load->Result()->Type();
-            auto* inst = current_block_->Append(BinaryOp(ty, load->Result(), rhs, op));
+            auto* ty = load->Result(0)->Type();
+            auto* inst = current_block_->Append(BinaryOp(ty, load->Result(0), rhs, op));
             b.Store(*v, inst);
         } else if (auto ref = std::get_if<VectorRefElementAccess>(&lhs)) {
             auto* load = b.LoadVectorElement(ref->vector, ref->index);
-            auto* ty = load->Result()->Type();
-            auto* inst = b.Append(BinaryOp(ty, load->Result(), rhs, op));
+            auto* ty = load->Result(0)->Type();
+            auto* inst = b.Append(BinaryOp(ty, load->Result(0), rhs, op));
             b.StoreVectorElement(ref->vector, ref->index, inst);
         }
     }
@@ -878,7 +878,7 @@
                 if (impl.program_.Sem().Get<sem::Load>(expr)) {
                     auto* load = impl.builder_.Load(value);
                     impl.current_block_->Append(load);
-                    value = load->Result();
+                    value = load->Result(0);
                 }
                 bindings_.Add(expr, value);
             }
@@ -888,7 +888,7 @@
                 if (impl.program_.Sem().Get<sem::Load>(expr)) {
                     auto* load = impl.builder_.LoadVectorElement(access.vector, access.index);
                     impl.current_block_->Append(load);
-                    bindings_.Add(expr, load->Result());
+                    bindings_.Add(expr, load->Result(0));
                 } else {
                     bindings_.Add(expr, access);
                 }
@@ -978,7 +978,7 @@
                         }
                         auto* val = impl.builder_.Swizzle(ty, obj, std::move(indices));
                         impl.current_block_->Append(val);
-                        Bind(expr, val->Result());
+                        Bind(expr, val->Result(0));
                         return nullptr;
                     },  //
                     TINT_ICE_ON_NO_MATCH);
@@ -993,14 +993,14 @@
                     if (auto* inst_res = obj->As<core::ir::InstructionResult>()) {
                         if (auto* access = inst_res->Source()->As<core::ir::Access>()) {
                             access->AddIndex(index);
-                            access->Result()->SetType(ty);
+                            access->Result(0)->SetType(ty);
                             bindings_.Remove(expr->object);
                             // Move the access after the index expression.
                             if (impl.current_block_->Back() != access) {
                                 impl.current_block_->Remove(access);
                                 impl.current_block_->Append(access);
                             }
-                            Bind(expr, access->Result());
+                            Bind(expr, access->Result(0));
                             return;
                         }
                     }
@@ -1009,7 +1009,7 @@
                 // Create a new access
                 auto* access = impl.builder_.Access(ty, obj, index);
                 impl.current_block_->Append(access);
-                Bind(expr, access->Result());
+                Bind(expr, access->Result(0));
             }
 
             void EmitBinary(const ast::BinaryExpression* b) {
@@ -1028,7 +1028,7 @@
                     return;
                 }
                 impl.current_block_->Append(inst);
-                Bind(b, inst->Result());
+                Bind(b, inst->Result(0));
             }
 
             void EmitUnary(const ast::UnaryOpExpression* expr) {
@@ -1057,7 +1057,7 @@
                         break;
                 }
                 impl.current_block_->Append(inst);
-                Bind(expr, inst->Result());
+                Bind(expr, inst->Result(0));
             }
 
             void EmitBitcast(const ast::BitcastExpression* b) {
@@ -1069,7 +1069,7 @@
                 auto* ty = sem->Type()->Clone(impl.clone_ctx_.type_ctx);
                 auto* inst = impl.builder_.Bitcast(ty, val);
                 impl.current_block_->Append(inst);
-                Bind(b, inst->Result());
+                Bind(b, inst->Result(0));
             }
 
             void EmitCall(const ast::CallExpression* expr) {
@@ -1128,7 +1128,7 @@
                     return;
                 }
                 impl.current_block_->Append(inst);
-                Bind(expr, inst->Result());
+                Bind(expr, inst->Result(0));
             }
 
             void EmitIdentifier(const ast::IdentifierExpression* i) {
@@ -1328,7 +1328,7 @@
                 }
 
                 // Store the declaration so we can get the instruction to store too
-                scopes_.Set(v->name->symbol, val->Result());
+                scopes_.Set(v->name->symbol, val->Result(0));
 
                 // Record the original name of the var
                 builder_.ir.SetName(val, v->name->symbol.Name());
@@ -1348,7 +1348,7 @@
                     // let, and can be used by consumers of the IR to produce a variable or
                     // debug info.
                     auto* let = current_block_->Append(builder_.Let(l->name->symbol.Name(), value));
-                    value = let->Result();
+                    value = let->Result(0);
                 } else {
                     // Record the original name of the let
                     builder_.ir.SetName(value, l->name->symbol.Name());
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
index e3650e6..3a6ca12 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
@@ -283,7 +283,7 @@
             if (inst->Results().Length() == 1) {
                 // Instruction has a single result value.
                 // Check to see if the result of this instruction is a candidate for inlining.
-                auto* result = inst->Result();
+                auto* result = inst->Result(0);
                 // Only values with a single usage can be inlined.
                 // Named values are not inlined, as we want to emit the name for a let.
                 if (result->Usages().Count() == 1 && !mod.NameOf(result).IsValid()) {
@@ -398,7 +398,7 @@
             for (auto* inst : *l->Body()) {
                 if (body_stmts.IsEmpty()) {
                     if (auto* if_ = inst->As<core::ir::If>()) {
-                        if (!if_->HasResults() &&                                //
+                        if (if_->Results().IsEmpty() &&                          //
                             if_->True()->Length() == 1 &&                        //
                             if_->False()->Length() == 1 &&                       //
                             tint::Is<core::ir::ExitIf>(if_->True()->Front()) &&  //
@@ -590,7 +590,7 @@
                     }
                 }
                 auto* expr = b.Call(NameFor(c->Target()), std::move(args));
-                if (!call->HasResults() || call->Result()->Usages().IsEmpty()) {
+                if (call->Results().IsEmpty() || call->Result()->Usages().IsEmpty()) {
                     Append(b.CallStmt(expr));
                     return;
                 }
@@ -614,7 +614,7 @@
                 }
 
                 auto* expr = b.Call(c->Func(), std::move(args));
-                if (!call->HasResults() || call->Result()->Type()->Is<core::type::Void>()) {
+                if (call->Results().IsEmpty() || call->Result()->Type()->Is<core::type::Void>()) {
                     Append(b.CallStmt(expr));
                     return;
                 }
@@ -1082,7 +1082,7 @@
     bool AsShortCircuit(core::ir::If* i,
                         const StatementList& true_stmts,
                         const StatementList& false_stmts) {
-        if (!i->HasResults()) {
+        if (i->Results().IsEmpty()) {
             return false;
         }
         auto* result = i->Result();
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/rename_conflicts.cc b/src/tint/lang/wgsl/writer/ir_to_program/rename_conflicts.cc
index a7f210f..df51eda 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/rename_conflicts.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/rename_conflicts.cc
@@ -181,11 +181,11 @@
             },
             [&](core::ir::Var*) {
                 // Ensure the var's type is resolvable
-                EnsureResolvable(inst->Result()->Type());
+                EnsureResolvable(inst->Result(0)->Type());
             },
             [&](core::ir::Construct*) {
                 // Ensure the type of a type constructor is resolvable
-                EnsureResolvable(inst->Result()->Type());
+                EnsureResolvable(inst->Result(0)->Type());
             },
             [&](core::ir::CoreBuiltinCall* call) {
                 // Ensure builtin of a builtin call is resolvable