[SPIR-V] Main program counter switch

This commit is contained in:
Triang3l 2020-10-18 15:36:02 +03:00
parent 1de144938c
commit 7846245b66
2 changed files with 114 additions and 22 deletions

View File

@ -29,7 +29,8 @@ void SpirvShaderTranslator::Reset() {
builder_.reset();
// main_switch_cases_.reset();
main_switch_op_.reset();
main_switch_next_pc_phi_operands_.clear();
}
void SpirvShaderTranslator::StartTranslation() {
@ -117,7 +118,6 @@ void SpirvShaderTranslator::StartTranslation() {
}
// Open the main loop.
spv::Block* main_loop_pre_header = builder_->getBuildPoint();
main_loop_header_ = &builder_->makeNewBlock();
spv::Block& main_loop_body = builder_->makeNewBlock();
@ -130,18 +130,25 @@ void SpirvShaderTranslator::StartTranslation() {
main_loop_merge_ = new spv::Block(builder_->getUniqueId(), *function_main_);
builder_->createBranch(main_loop_header_);
// If no jumps, don't create a switch, but still create a loop so exece can
// break.
bool has_main_switch = !label_addresses().empty();
// Main loop header - based on whether it's the first iteration (entered from
// the function or from the continuation), choose the program counter.
builder_->setBuildPoint(main_loop_header_);
id_vector_temp_.clear();
id_vector_temp_.reserve(4);
id_vector_temp_.push_back(const_int_0_);
id_vector_temp_.push_back(main_loop_pre_header->getId());
main_loop_pc_next_ = builder_->getUniqueId();
id_vector_temp_.push_back(main_loop_pc_next_);
id_vector_temp_.push_back(main_loop_continue_->getId());
spv::Id main_loop_pc_current =
builder_->createOp(spv::OpPhi, type_int_, id_vector_temp_);
spv::Id main_loop_pc_current = 0;
if (has_main_switch) {
id_vector_temp_.clear();
id_vector_temp_.reserve(4);
id_vector_temp_.push_back(const_int_0_);
id_vector_temp_.push_back(main_loop_pre_header->getId());
main_loop_pc_next_ = builder_->getUniqueId();
id_vector_temp_.push_back(main_loop_pc_next_);
id_vector_temp_.push_back(main_loop_continue_->getId());
main_loop_pc_current =
builder_->createOp(spv::OpPhi, type_int_, id_vector_temp_);
}
uint_vector_temp_.clear();
builder_->createLoopMerge(main_loop_merge_, main_loop_continue_,
spv::LoopControlDontUnrollMask, uint_vector_temp_);
@ -149,29 +156,86 @@ void SpirvShaderTranslator::StartTranslation() {
// Main loop body.
builder_->setBuildPoint(&main_loop_body);
// TODO(Triang3l): Create the switch, add the block for the case 0 and set the
// build point to it.
if (has_main_switch) {
// Create the program counter switch with cases for every label and for
// label 0.
main_switch_header_ = builder_->getBuildPoint();
main_switch_merge_ =
new spv::Block(builder_->getUniqueId(), *function_main_);
{
std::unique_ptr<spv::Instruction> main_switch_selection_merge_op =
std::make_unique<spv::Instruction>(spv::OpSelectionMerge);
main_switch_selection_merge_op->addIdOperand(main_switch_merge_->getId());
main_switch_selection_merge_op->addImmediateOperand(
spv::SelectionControlDontFlattenMask);
builder_->getBuildPoint()->addInstruction(
std::move(main_switch_selection_merge_op));
}
main_switch_op_ = std::make_unique<spv::Instruction>(spv::OpSwitch);
main_switch_op_->addIdOperand(main_loop_pc_current);
main_switch_op_->addIdOperand(main_switch_merge_->getId());
// The default case (the merge here) must have the header as a predecessor.
main_switch_merge_->addPredecessor(main_switch_header_);
// The instruction will be inserted later, when all cases are filled.
// Insert and enter case 0.
spv::Block* main_switch_case_0_block =
new spv::Block(builder_->getUniqueId(), *function_main_);
main_switch_op_->addImmediateOperand(0);
main_switch_op_->addIdOperand(main_switch_case_0_block->getId());
// Every switch case must have the OpSelectionMerge/OpSwitch block as a
// predecessor.
main_switch_case_0_block->addPredecessor(main_switch_header_);
function_main_->addBlock(main_switch_case_0_block);
builder_->setBuildPoint(main_switch_case_0_block);
}
}
std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
// Close the main loop.
// Break from the body after falling through the end or breaking.
builder_->createBranch(main_loop_merge_);
bool has_main_switch = !label_addresses().empty();
// After the final exec (if it happened to be not exece, which would already
// have a break branch), break from the switch if it exists, or from the
// loop it doesn't.
if (!builder_->getBuildPoint()->isTerminated()) {
builder_->createBranch(has_main_switch ? main_switch_merge_
: main_loop_merge_);
}
if (has_main_switch) {
// Insert the switch instruction with all cases added as operands.
builder_->setBuildPoint(main_switch_header_);
builder_->getBuildPoint()->addInstruction(std::move(main_switch_op_));
// Build the main switch merge, breaking out of the loop after falling
// through the end or breaking from exece (only continuing if a jump - from
// a guest loop or from jmp/call - was made).
function_main_->addBlock(main_switch_merge_);
builder_->setBuildPoint(main_switch_merge_);
builder_->createBranch(main_loop_merge_);
}
// Main loop continuation - choose the program counter based on the path
// taken (-1 if not from a jump as a safe fallback, which would result in not
// hitting any switch case and reaching the final break in the body).
function_main_->addBlock(main_loop_continue_);
builder_->setBuildPoint(main_loop_continue_);
{
if (has_main_switch) {
// If labels were added, but not jumps (for example, due to the call
// instruction not being implemented as of October 18, 2020), send an
// impossible program counter value (-1) to the OpPhi at the next iteration.
if (main_switch_next_pc_phi_operands_.empty()) {
main_switch_next_pc_phi_operands_.push_back(
builder_->makeIntConstant(-1));
}
std::unique_ptr<spv::Instruction> main_loop_pc_next_op =
std::make_unique<spv::Instruction>(main_loop_pc_next_, type_int_,
spv::OpCopyObject);
// TODO(Triang3l): Phi between the continues in the switch cases and the
// switch merge block.
main_loop_pc_next_op->addIdOperand(builder_->makeIntConstant(-1));
std::make_unique<spv::Instruction>(
main_loop_pc_next_, type_int_,
main_switch_next_pc_phi_operands_.size() >= 2 ? spv::OpPhi
: spv::OpCopyObject);
for (spv::Id operand : main_switch_next_pc_phi_operands_) {
main_loop_pc_next_op->addIdOperand(operand);
}
builder_->getBuildPoint()->addInstruction(std::move(main_loop_pc_next_op));
}
builder_->createBranch(main_loop_header_);
// Add the main loop merge block and go back to the function.
function_main_->addBlock(main_loop_merge_);
builder_->setBuildPoint(main_loop_merge_);
@ -214,6 +278,27 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
return module_bytes;
}
void SpirvShaderTranslator::ProcessLabel(uint32_t cf_index) {
if (cf_index == 0) {
// 0 already added in the beginning.
return;
}
spv::Function& function = builder_->getBuildPoint()->getParent();
// Create the next switch case and fallthrough to it.
spv::Block* new_case = new spv::Block(builder_->getUniqueId(), function);
main_switch_op_->addImmediateOperand(cf_index);
main_switch_op_->addIdOperand(new_case->getId());
// Every switch case must have the OpSelectionMerge/OpSwitch block as a
// predecessor.
new_case->addPredecessor(main_switch_header_);
// The previous block may have already been terminated if was exece.
if (!builder_->getBuildPoint()->isTerminated()) {
builder_->createBranch(new_case);
}
function.addBlock(new_case);
builder_->setBuildPoint(new_case);
}
void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
// Create the inputs.
if (IsSpirvTessEvalShader()) {

View File

@ -12,6 +12,7 @@
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
#include "third_party/glslang/SPIRV/SpvBuilder.h"
@ -32,6 +33,8 @@ class SpirvShaderTranslator : public ShaderTranslator {
std::vector<uint8_t> CompleteTranslation() override;
void ProcessLabel(uint32_t cf_index) override;
private:
// TODO(Triang3l): Depth-only pixel shader.
bool IsSpirvVertexOrTessEvalShader() const { return is_vertex_shader(); }
@ -104,6 +107,10 @@ class SpirvShaderTranslator : public ShaderTranslator {
spv::Block* main_loop_continue_;
spv::Block* main_loop_merge_;
spv::Id main_loop_pc_next_;
spv::Block* main_switch_header_;
std::unique_ptr<spv::Instruction> main_switch_op_;
spv::Block* main_switch_merge_;
std::vector<spv::Id> main_switch_next_pc_phi_operands_;
};
} // namespace gpu