[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(); builder_.reset();
// main_switch_cases_.reset(); main_switch_op_.reset();
main_switch_next_pc_phi_operands_.clear();
} }
void SpirvShaderTranslator::StartTranslation() { void SpirvShaderTranslator::StartTranslation() {
@ -117,7 +118,6 @@ void SpirvShaderTranslator::StartTranslation() {
} }
// Open the main loop. // Open the main loop.
spv::Block* main_loop_pre_header = builder_->getBuildPoint(); spv::Block* main_loop_pre_header = builder_->getBuildPoint();
main_loop_header_ = &builder_->makeNewBlock(); main_loop_header_ = &builder_->makeNewBlock();
spv::Block& main_loop_body = 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_); main_loop_merge_ = new spv::Block(builder_->getUniqueId(), *function_main_);
builder_->createBranch(main_loop_header_); 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 // Main loop header - based on whether it's the first iteration (entered from
// the function or from the continuation), choose the program counter. // the function or from the continuation), choose the program counter.
builder_->setBuildPoint(main_loop_header_); builder_->setBuildPoint(main_loop_header_);
id_vector_temp_.clear(); spv::Id main_loop_pc_current = 0;
id_vector_temp_.reserve(4); if (has_main_switch) {
id_vector_temp_.push_back(const_int_0_); id_vector_temp_.clear();
id_vector_temp_.push_back(main_loop_pre_header->getId()); id_vector_temp_.reserve(4);
main_loop_pc_next_ = builder_->getUniqueId(); id_vector_temp_.push_back(const_int_0_);
id_vector_temp_.push_back(main_loop_pc_next_); id_vector_temp_.push_back(main_loop_pre_header->getId());
id_vector_temp_.push_back(main_loop_continue_->getId()); main_loop_pc_next_ = builder_->getUniqueId();
spv::Id main_loop_pc_current = id_vector_temp_.push_back(main_loop_pc_next_);
builder_->createOp(spv::OpPhi, type_int_, id_vector_temp_); 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(); uint_vector_temp_.clear();
builder_->createLoopMerge(main_loop_merge_, main_loop_continue_, builder_->createLoopMerge(main_loop_merge_, main_loop_continue_,
spv::LoopControlDontUnrollMask, uint_vector_temp_); spv::LoopControlDontUnrollMask, uint_vector_temp_);
@ -149,29 +156,86 @@ void SpirvShaderTranslator::StartTranslation() {
// Main loop body. // Main loop body.
builder_->setBuildPoint(&main_loop_body); builder_->setBuildPoint(&main_loop_body);
// TODO(Triang3l): Create the switch, add the block for the case 0 and set the if (has_main_switch) {
// build point to it. // 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() { std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
// Close the main loop. bool has_main_switch = !label_addresses().empty();
// Break from the body after falling through the end or breaking. // After the final exec (if it happened to be not exece, which would already
builder_->createBranch(main_loop_merge_); // 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 // 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 // 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). // hitting any switch case and reaching the final break in the body).
function_main_->addBlock(main_loop_continue_); function_main_->addBlock(main_loop_continue_);
builder_->setBuildPoint(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::unique_ptr<spv::Instruction> main_loop_pc_next_op =
std::make_unique<spv::Instruction>(main_loop_pc_next_, type_int_, std::make_unique<spv::Instruction>(
spv::OpCopyObject); main_loop_pc_next_, type_int_,
// TODO(Triang3l): Phi between the continues in the switch cases and the main_switch_next_pc_phi_operands_.size() >= 2 ? spv::OpPhi
// switch merge block. : spv::OpCopyObject);
main_loop_pc_next_op->addIdOperand(builder_->makeIntConstant(-1)); 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_->getBuildPoint()->addInstruction(std::move(main_loop_pc_next_op));
} }
builder_->createBranch(main_loop_header_); builder_->createBranch(main_loop_header_);
// Add the main loop merge block and go back to the function. // Add the main loop merge block and go back to the function.
function_main_->addBlock(main_loop_merge_); function_main_->addBlock(main_loop_merge_);
builder_->setBuildPoint(main_loop_merge_); builder_->setBuildPoint(main_loop_merge_);
@ -214,6 +278,27 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
return module_bytes; 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() { void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
// Create the inputs. // Create the inputs.
if (IsSpirvTessEvalShader()) { if (IsSpirvTessEvalShader()) {

View File

@ -12,6 +12,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <utility>
#include <vector> #include <vector>
#include "third_party/glslang/SPIRV/SpvBuilder.h" #include "third_party/glslang/SPIRV/SpvBuilder.h"
@ -32,6 +33,8 @@ class SpirvShaderTranslator : public ShaderTranslator {
std::vector<uint8_t> CompleteTranslation() override; std::vector<uint8_t> CompleteTranslation() override;
void ProcessLabel(uint32_t cf_index) override;
private: private:
// TODO(Triang3l): Depth-only pixel shader. // TODO(Triang3l): Depth-only pixel shader.
bool IsSpirvVertexOrTessEvalShader() const { return is_vertex_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_continue_;
spv::Block* main_loop_merge_; spv::Block* main_loop_merge_;
spv::Id main_loop_pc_next_; 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 } // namespace gpu