| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- // Copyright (c) 2022 The Khronos Group Inc.
- // Copyright (c) 2022 LunarG Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include "source/opt/eliminate_dead_output_stores_pass.h"
- #include "source/opt/instruction.h"
- #include "source/opt/ir_context.h"
- namespace spvtools {
- namespace opt {
- namespace {
- constexpr uint32_t kDecorationLocationInIdx = 2;
- constexpr uint32_t kOpDecorateMemberMemberInIdx = 1;
- constexpr uint32_t kOpDecorateBuiltInLiteralInIdx = 2;
- constexpr uint32_t kOpDecorateMemberBuiltInLiteralInIdx = 3;
- constexpr uint32_t kOpAccessChainIdx0InIdx = 1;
- constexpr uint32_t kOpConstantValueInIdx = 0;
- } // namespace
- Pass::Status EliminateDeadOutputStoresPass::Process() {
- // Current functionality assumes shader capability
- if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
- return Status::SuccessWithoutChange;
- Pass::Status status = DoDeadOutputStoreElimination();
- return status;
- }
- void EliminateDeadOutputStoresPass::InitializeElimination() {
- kill_list_.clear();
- }
- bool EliminateDeadOutputStoresPass::IsLiveBuiltin(uint32_t bi) {
- return live_builtins_->find(bi) != live_builtins_->end();
- }
- bool EliminateDeadOutputStoresPass::AnyLocsAreLive(uint32_t start,
- uint32_t count) {
- auto finish = start + count;
- for (uint32_t u = start; u < finish; ++u) {
- if (live_locs_->find(u) != live_locs_->end()) return true;
- }
- return false;
- }
- void EliminateDeadOutputStoresPass::KillAllStoresOfRef(Instruction* ref) {
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- if (ref->opcode() == spv::Op::OpStore) {
- kill_list_.push_back(ref);
- return;
- }
- assert((ref->opcode() == spv::Op::OpAccessChain ||
- ref->opcode() == spv::Op::OpInBoundsAccessChain) &&
- "unexpected use of output variable");
- def_use_mgr->ForEachUser(ref, [this](Instruction* user) {
- if (user->opcode() == spv::Op::OpStore) kill_list_.push_back(user);
- });
- }
- void EliminateDeadOutputStoresPass::KillAllDeadStoresOfLocRef(
- Instruction* ref, Instruction* var) {
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
- analysis::LivenessManager* live_mgr = context()->get_liveness_mgr();
- // Find variable location if present.
- uint32_t start_loc = 0;
- auto var_id = var->result_id();
- bool no_loc = deco_mgr->WhileEachDecoration(
- var_id, uint32_t(spv::Decoration::Location),
- [&start_loc](const Instruction& deco) {
- assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
- start_loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx);
- return false;
- });
- // Find patch decoration if present
- bool is_patch = !deco_mgr->WhileEachDecoration(
- var_id, uint32_t(spv::Decoration::Patch), [](const Instruction& deco) {
- if (deco.opcode() != spv::Op::OpDecorate)
- assert(false && "unexpected decoration");
- return false;
- });
- // Compute offset and final type of reference. If no location found
- // or any stored locations are live, return without removing stores.
- Instruction* ptr_type = get_def_use_mgr()->GetDef(var->type_id());
- assert(ptr_type && "unexpected var type");
- const uint32_t kPointerTypePointeeIdx = 1;
- uint32_t var_type_id =
- ptr_type->GetSingleWordInOperand(kPointerTypePointeeIdx);
- uint32_t ref_loc = start_loc;
- if (ref->opcode() == spv::Op::OpAccessChain ||
- ref->opcode() == spv::Op::OpInBoundsAccessChain) {
- var_type_id = live_mgr->AnalyzeAccessChainLoc(
- ref, var_type_id, &ref_loc, &no_loc, is_patch, /* input */ false);
- }
- const analysis::Type* curr_type = type_mgr->GetType(var_type_id);
- if (no_loc || AnyLocsAreLive(ref_loc, live_mgr->GetLocSize(curr_type)))
- return;
- // Kill all stores based on this reference
- KillAllStoresOfRef(ref);
- }
- void EliminateDeadOutputStoresPass::KillAllDeadStoresOfBuiltinRef(
- Instruction* ref, Instruction* var) {
- auto deco_mgr = context()->get_decoration_mgr();
- auto def_use_mgr = context()->get_def_use_mgr();
- auto type_mgr = context()->get_type_mgr();
- auto live_mgr = context()->get_liveness_mgr();
- // Search for builtin decoration of base variable
- uint32_t builtin = uint32_t(spv::BuiltIn::Max);
- auto var_id = var->result_id();
- (void)deco_mgr->WhileEachDecoration(
- var_id, uint32_t(spv::Decoration::BuiltIn),
- [&builtin](const Instruction& deco) {
- assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
- builtin = deco.GetSingleWordInOperand(kOpDecorateBuiltInLiteralInIdx);
- return false;
- });
- // If analyzed builtin and not live, kill stores.
- if (builtin != uint32_t(spv::BuiltIn::Max)) {
- if (live_mgr->IsAnalyzedBuiltin(builtin) && !IsLiveBuiltin(builtin))
- KillAllStoresOfRef(ref);
- return;
- }
- // Search for builtin decoration on indexed member
- auto ref_op = ref->opcode();
- if (ref_op != spv::Op::OpAccessChain &&
- ref_op != spv::Op::OpInBoundsAccessChain) {
- return;
- }
- uint32_t in_idx = kOpAccessChainIdx0InIdx;
- analysis::Type* var_type = type_mgr->GetType(var->type_id());
- analysis::Pointer* ptr_type = var_type->AsPointer();
- auto curr_type = ptr_type->pointee_type();
- auto arr_type = curr_type->AsArray();
- if (arr_type) {
- curr_type = arr_type->element_type();
- ++in_idx;
- }
- auto str_type = curr_type->AsStruct();
- auto str_type_id = type_mgr->GetId(str_type);
- auto member_idx_id = ref->GetSingleWordInOperand(in_idx);
- auto member_idx_inst = def_use_mgr->GetDef(member_idx_id);
- assert(member_idx_inst->opcode() == spv::Op::OpConstant &&
- "unexpected non-constant index");
- auto ac_idx = member_idx_inst->GetSingleWordInOperand(kOpConstantValueInIdx);
- (void)deco_mgr->WhileEachDecoration(
- str_type_id, uint32_t(spv::Decoration::BuiltIn),
- [ac_idx, &builtin](const Instruction& deco) {
- assert(deco.opcode() == spv::Op::OpMemberDecorate &&
- "unexpected decoration");
- auto deco_idx =
- deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx);
- if (deco_idx == ac_idx) {
- builtin =
- deco.GetSingleWordInOperand(kOpDecorateMemberBuiltInLiteralInIdx);
- return false;
- }
- return true;
- });
- assert(builtin != uint32_t(spv::BuiltIn::Max) && "builtin not found");
- // If analyzed builtin and not live, kill stores.
- if (live_mgr->IsAnalyzedBuiltin(builtin) && !IsLiveBuiltin(builtin))
- KillAllStoresOfRef(ref);
- }
- Pass::Status EliminateDeadOutputStoresPass::DoDeadOutputStoreElimination() {
- // Current implementation only supports vert, tesc, tese, geom shaders
- auto stage = context()->GetStage();
- if (stage != spv::ExecutionModel::Vertex &&
- stage != spv::ExecutionModel::TessellationControl &&
- stage != spv::ExecutionModel::TessellationEvaluation &&
- stage != spv::ExecutionModel::Geometry)
- return Status::Failure;
- InitializeElimination();
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
- // Process all output variables
- for (auto& var : context()->types_values()) {
- if (var.opcode() != spv::Op::OpVariable) {
- continue;
- }
- analysis::Type* var_type = type_mgr->GetType(var.type_id());
- analysis::Pointer* ptr_type = var_type->AsPointer();
- if (ptr_type->storage_class() != spv::StorageClass::Output) {
- continue;
- }
- // If builtin decoration on variable, process as builtin.
- auto var_id = var.result_id();
- bool is_builtin = false;
- if (deco_mgr->HasDecoration(var_id, uint32_t(spv::Decoration::BuiltIn))) {
- is_builtin = true;
- } else {
- // If interface block with builtin members, process as builtin.
- // Strip off outer array type if present.
- auto curr_type = ptr_type->pointee_type();
- auto arr_type = curr_type->AsArray();
- if (arr_type) curr_type = arr_type->element_type();
- auto str_type = curr_type->AsStruct();
- if (str_type) {
- auto str_type_id = type_mgr->GetId(str_type);
- if (deco_mgr->HasDecoration(str_type_id,
- uint32_t(spv::Decoration::BuiltIn)))
- is_builtin = true;
- }
- }
- // For each store or access chain using var, if dead builtin or all its
- // locations are dead, kill store or all access chain's stores
- def_use_mgr->ForEachUser(
- var_id, [this, &var, is_builtin](Instruction* user) {
- auto op = user->opcode();
- if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName ||
- op == spv::Op::OpDecorate || user->IsNonSemanticInstruction())
- return;
- if (is_builtin)
- KillAllDeadStoresOfBuiltinRef(user, &var);
- else
- KillAllDeadStoresOfLocRef(user, &var);
- });
- }
- for (auto& kinst : kill_list_) context()->KillInst(kinst);
- return kill_list_.empty() ? Status::SuccessWithoutChange
- : Status::SuccessWithChange;
- }
- } // namespace opt
- } // namespace spvtools
|