[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);