[spirv-writer] Store SPIR-V instructions into blocks.

Currently the SPIR-V writer creates a list of instructions for a
function, those lists contain multiple SPIR-V blocks just appended
together. Instead, create a list of blocks, the blocks store
instructions. Then as blocks are emitted from the Tint IR they create
Blocks in the SPIR-V writer. This will allow us to refer to those SPIR-V
blocks later.

Bug: 409346479
Change-Id: Iaecef8f4293426af722ec45946aa1274a693864b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/235695
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/spirv/writer/common/function.cc b/src/tint/lang/spirv/writer/common/function.cc
index f1f2e4e..5ec0eb2 100644
--- a/src/tint/lang/spirv/writer/common/function.cc
+++ b/src/tint/lang/spirv/writer/common/function.cc
@@ -54,8 +54,10 @@
     for (const auto& var : vars_) {
         cb(var);
     }
-    for (const auto& inst : instructions_) {
-        cb(inst);
+    for (const auto& blk : blocks_) {
+        for (const auto& inst : blk) {
+            cb(inst);
+        }
     }
 
     cb(Instruction{spv::Op::OpFunctionEnd, {}});
diff --git a/src/tint/lang/spirv/writer/common/function.h b/src/tint/lang/spirv/writer/common/function.h
index 578fd05..dd1fa7a 100644
--- a/src/tint/lang/spirv/writer/common/function.h
+++ b/src/tint/lang/spirv/writer/common/function.h
@@ -29,6 +29,7 @@
 #define SRC_TINT_LANG_SPIRV_WRITER_COMMON_FUNCTION_H_
 
 #include <functional>
+#include <vector>
 
 #include "src/tint/lang/spirv/writer/common/instruction.h"
 
@@ -37,6 +38,8 @@
 /// A SPIR-V function
 class Function {
   public:
+    using Block = InstructionList;
+
     /// Constructor for testing purposes
     /// This creates a bad declaration, so won't generate correct SPIR-V
     Function();
@@ -72,10 +75,17 @@
     /// @param op the op to set
     /// @param operands the operands for the instruction
     void PushInst(spv::Op op, const OperandList& operands) {
-        instructions_.push_back(Instruction{op, operands});
+        blocks_[current_block_idx_].push_back(Instruction{op, operands});
     }
-    /// @returns the instruction list
-    const InstructionList& Instructions() const { return instructions_; }
+    /// Adds a new block to the block list
+    /// @returns the index of the new block
+    size_t AppendBlock() {
+        blocks_.push_back({});
+        return blocks_.size() - 1;
+    }
+    /// Sets the block to insert into
+    /// @param idx the index to set
+    void SetCurrentBlockIndex(size_t idx) { current_block_idx_ = idx; }
 
     /// Adds a variable to the variable list
     /// @param operands the operands for the variable
@@ -96,8 +106,10 @@
         for (const auto& var : vars_) {
             size += var.WordLength();
         }
-        for (const auto& inst : instructions_) {
-            size += inst.WordLength();
+        for (const auto& blk : blocks_) {
+            for (const auto& inst : blk) {
+                size += inst.WordLength();
+            }
         }
         return size;
     }
@@ -110,7 +122,8 @@
     Operand label_op_;
     InstructionList params_;
     InstructionList vars_;
-    InstructionList instructions_;
+    std::vector<Block> blocks_;
+    size_t current_block_idx_ = 0;
 };
 
 }  // namespace tint::spirv::writer
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index 1cc084a..7c323c8 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -731,7 +731,8 @@
         TINT_DEFER(current_function_ = Function());
 
         // Emit the body of the function.
-        EmitBlock(func->Block());
+        auto idx = current_function_.AppendBlock();
+        EmitBlockInternal(idx, func->Block());
 
         // Add the function to the module.
         module_.PushFunction(current_function_);
@@ -839,25 +840,34 @@
         }
     }
 
+    size_t NewBlock(uint32_t id) {
+        auto idx = current_function_.AppendBlock();
+        current_function_.SetCurrentBlockIndex(idx);
+        current_function_.PushInst(spv::Op::OpLabel, {id});
+        return idx;
+    }
+
     /// Emit a block, including the initial OpLabel, OpPhis and instructions.
     /// @param block the block to emit
     void EmitBlock(core::ir::Block* block) {
-        // Emit the label.
-        // Skip if this is the function's entry block, as it will be emitted by the function object.
-        if (!current_function_.Instructions().empty()) {
-            current_function_.PushInst(spv::Op::OpLabel, {Label(block)});
+        auto idx = NewBlock(Label(block));
+
+        if (!block->IsEmpty()) {
+            EmitBlockInternal(idx, block);
+            return;
         }
 
         // If there are no instructions in the block, it's a dead end, so we shouldn't be able to
         // get here to begin with.
-        if (block->IsEmpty()) {
-            if (!block->Parent()->Results().IsEmpty()) {
-                current_function_.PushInst(spv::Op::OpBranch, {GetMergeLabel(block->Parent())});
-            } else {
-                current_function_.PushInst(spv::Op::OpUnreachable, {});
-            }
-            return;
+        if (!block->Parent()->Results().IsEmpty()) {
+            current_function_.PushInst(spv::Op::OpBranch, {GetMergeLabel(block->Parent())});
+        } else {
+            current_function_.PushInst(spv::Op::OpUnreachable, {});
         }
+    }
+
+    void EmitBlockInternal(size_t idx, core::ir::Block* block) {
+        current_function_.SetCurrentBlockIndex(idx);
 
         if (auto* mib = block->As<core::ir::MultiInBlock>()) {
             // Emit all OpPhi nodes for incoming branches to block.
@@ -1031,7 +1041,7 @@
             EmitBlock(false_block);
         }
 
-        current_function_.PushInst(spv::Op::OpLabel, {merge_label});
+        NewBlock(merge_label);
 
         // Emit the OpPhis for the ExitIfs
         EmitExitPhis(i);
@@ -2260,14 +2270,14 @@
 
         // Emit the loop body header, which contains the OpLoopMerge and OpPhis.
         // This then unconditionally branches to body_label
-        current_function_.PushInst(spv::Op::OpLabel, {header_label});
+        [[maybe_unused]] auto header_idx = NewBlock(header_label);
         EmitIncomingPhis(loop->Body());
         current_function_.PushInst(spv::Op::OpLoopMerge, {merge_label, continuing_label,
                                                           U32Operand(SpvLoopControlMaskNone)});
         current_function_.PushInst(spv::Op::OpBranch, {body_label});
 
         // Emit the loop body
-        current_function_.PushInst(spv::Op::OpLabel, {body_label});
+        [[maybe_unused]] auto body_blk = NewBlock(body_label);
         EmitBlockInstructions(loop->Body());
 
         // Emit the loop continuing block.
@@ -2275,12 +2285,12 @@
             EmitBlock(loop->Continuing());
         } else {
             // We still need to emit a continuing block with a back-edge, even if it is unreachable.
-            current_function_.PushInst(spv::Op::OpLabel, {continuing_label});
+            [[maybe_unused]] auto continuing_blk = NewBlock(continuing_label);
             current_function_.PushInst(spv::Op::OpBranch, {header_label});
         }
 
         // Emit the loop merge block.
-        current_function_.PushInst(spv::Op::OpLabel, {merge_label});
+        [[maybe_unused]] auto merge_blk = NewBlock(merge_label);
 
         // Emit the OpPhis for the ExitLoops
         EmitExitPhis(loop);
@@ -2326,7 +2336,7 @@
         }
 
         // Emit the switch merge block.
-        current_function_.PushInst(spv::Op::OpLabel, {merge_label});
+        [[maybe_unused]] auto merge_blk = NewBlock(merge_label);
 
         // Emit the OpPhis for the ExitSwitches
         EmitExitPhis(swtch);