diff options
Diffstat (limited to '3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.cpp')
-rw-r--r-- | 3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.cpp | 449 |
1 files changed, 428 insertions, 21 deletions
diff --git a/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.cpp b/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.cpp index 742ac1f6268..c9c3f1b5dc2 100644 --- a/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.cpp +++ b/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.cpp @@ -16,15 +16,33 @@ #include <cstring> +#include "OpenCLDebugInfo100.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/opt/log.h" #include "source/opt/mem_pass.h" #include "source/opt/reflect.h" +namespace { + +static const int kSpvDecorateTargetIdInIdx = 0; +static const int kSpvDecorateDecorationInIdx = 1; +static const int kSpvDecorateBuiltinInIdx = 2; +static const int kEntryPointInterfaceInIdx = 3; +static const int kEntryPointFunctionIdInIdx = 1; + +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 +// extension instructions. +static const uint32_t kDebugFunctionOperandFunctionIndex = 13; +static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; + +} // anonymous namespace + namespace spvtools { namespace opt { void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) { + set = Analysis(set & ~valid_analyses_); + if (set & kAnalysisDefUse) { BuildDefUseManager(); } @@ -43,6 +61,9 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) { if (set & kAnalysisLoopAnalysis) { ResetLoopAnalysis(); } + if (set & kAnalysisBuiltinVarId) { + ResetBuiltinAnalysis(); + } if (set & kAnalysisNameMap) { BuildIdToNameMap(); } @@ -55,6 +76,21 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) { if (set & kAnalysisValueNumberTable) { BuildValueNumberTable(); } + if (set & kAnalysisStructuredCFG) { + BuildStructuredCFGAnalysis(); + } + if (set & kAnalysisIdToFuncMapping) { + BuildIdToFuncMapping(); + } + if (set & kAnalysisConstants) { + BuildConstantManager(); + } + if (set & kAnalysisTypes) { + BuildTypeManager(); + } + if (set & kAnalysisDebugInfo) { + BuildDebugInfoManager(); + } } void IRContext::InvalidateAnalysesExceptFor( @@ -64,6 +100,21 @@ void IRContext::InvalidateAnalysesExceptFor( } void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { + // The ConstantManager and DebugInfoManager contain Type pointers. If the + // TypeManager goes away, the ConstantManager and DebugInfoManager have to + // go away. + if (analyses_to_invalidate & kAnalysisTypes) { + analyses_to_invalidate |= kAnalysisConstants; + analyses_to_invalidate |= kAnalysisDebugInfo; + } + + // The dominator analysis hold the pseudo entry and exit nodes from the CFG. + // Also if the CFG change the dominators many changed as well, so the + // dominator analysis should be invalidated as well. + if (analyses_to_invalidate & kAnalysisCFG) { + analyses_to_invalidate |= kAnalysisDominatorAnalysis; + } + if (analyses_to_invalidate & kAnalysisDefUse) { def_use_mgr_.reset(nullptr); } @@ -76,6 +127,9 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { if (analyses_to_invalidate & kAnalysisCombinators) { combinator_ops_.clear(); } + if (analyses_to_invalidate & kAnalysisBuiltinVarId) { + builtin_var_id_map_.clear(); + } if (analyses_to_invalidate & kAnalysisCFG) { cfg_.reset(nullptr); } @@ -89,6 +143,22 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { if (analyses_to_invalidate & kAnalysisValueNumberTable) { vn_table_.reset(nullptr); } + if (analyses_to_invalidate & kAnalysisStructuredCFG) { + struct_cfg_analysis_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisIdToFuncMapping) { + id_to_func_.clear(); + } + if (analyses_to_invalidate & kAnalysisConstants) { + constant_mgr_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisTypes) { + type_mgr_.reset(nullptr); + } + + if (analyses_to_invalidate & kAnalysisDebugInfo) { + debug_info_mgr_.reset(nullptr); + } valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate); } @@ -100,28 +170,39 @@ Instruction* IRContext::KillInst(Instruction* inst) { KillNamesAndDecorates(inst); + KillOperandFromDebugInstructions(inst); + if (AreAnalysesValid(kAnalysisDefUse)) { - get_def_use_mgr()->ClearInst(inst); + analysis::DefUseManager* def_use_mgr = get_def_use_mgr(); + def_use_mgr->ClearInst(inst); + for (auto& l_inst : inst->dbg_line_insts()) def_use_mgr->ClearInst(&l_inst); } if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { instr_to_block_.erase(inst); } if (AreAnalysesValid(kAnalysisDecorations)) { - if (inst->result_id() != 0) { - decoration_mgr_->RemoveDecorationsFrom(inst->result_id()); - } if (inst->IsDecoration()) { decoration_mgr_->RemoveDecoration(inst); } } - + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ClearDebugScopeAndInlinedAtUses(inst); + get_debug_info_mgr()->ClearDebugInfo(inst); + } if (type_mgr_ && IsTypeInst(inst->opcode())) { type_mgr_->RemoveId(inst->result_id()); } - if (constant_mgr_ && IsConstantInst(inst->opcode())) { constant_mgr_->RemoveId(inst->result_id()); } + if (inst->opcode() == SpvOpCapability || inst->opcode() == SpvOpExtension) { + // We reset the feature manager, instead of updating it, because it is just + // as much work. We would have to remove all capabilities implied by this + // capability that are not also implied by the remaining OpCapability + // instructions. We could update extensions, but we will see if it is + // needed. + ResetFeatureManager(); + } RemoveFromIdToName(inst); @@ -138,6 +219,28 @@ Instruction* IRContext::KillInst(Instruction* inst) { return next_instruction; } +void IRContext::CollectNonSemanticTree( + Instruction* inst, std::unordered_set<Instruction*>* to_kill) { + if (!inst->HasResultId()) return; + // Debug[No]Line result id is not used, so we are done + if (inst->IsDebugLineInst()) return; + std::vector<Instruction*> work_list; + std::unordered_set<Instruction*> seen; + work_list.push_back(inst); + + while (!work_list.empty()) { + auto* i = work_list.back(); + work_list.pop_back(); + get_def_use_mgr()->ForEachUser( + i, [&work_list, to_kill, &seen](Instruction* user) { + if (user->IsNonSemanticInstruction() && seen.insert(user).second) { + work_list.push_back(user); + to_kill->insert(user); + } + }); + } +} + bool IRContext::KillDef(uint32_t id) { Instruction* def = get_def_use_mgr()->GetDef(id); if (def != nullptr) { @@ -148,16 +251,30 @@ bool IRContext::KillDef(uint32_t id) { } bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) { + return ReplaceAllUsesWithPredicate(before, after, + [](Instruction*) { return true; }); +} + +bool IRContext::ReplaceAllUsesWithPredicate( + uint32_t before, uint32_t after, + const std::function<bool(Instruction*)>& predicate) { if (before == after) return false; + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ReplaceAllUsesInDebugScopeWithPredicate(before, after, + predicate); + } + // Ensure that |after| has been registered as def. assert(get_def_use_mgr()->GetDef(after) && "'after' is not a registered def."); std::vector<std::pair<Instruction*, uint32_t>> uses_to_update; get_def_use_mgr()->ForEachUse( - before, [&uses_to_update](Instruction* user, uint32_t index) { - uses_to_update.emplace_back(user, index); + before, [&predicate, &uses_to_update](Instruction* user, uint32_t index) { + if (predicate(user)) { + uses_to_update.emplace_back(user, index); + } }); Instruction* prev = nullptr; @@ -193,22 +310,28 @@ bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) { } AnalyzeUses(user); } - return true; } bool IRContext::IsConsistent() { #ifndef SPIRV_CHECK_CONTEXT return true; -#endif - +#else if (AreAnalysesValid(kAnalysisDefUse)) { analysis::DefUseManager new_def_use(module()); - if (*get_def_use_mgr() != new_def_use) { + if (!CompareAndPrintDifferences(*get_def_use_mgr(), new_def_use)) { return false; } } + if (AreAnalysesValid(kAnalysisIdToFuncMapping)) { + for (auto& fn : *module_) { + if (id_to_func_[fn.result_id()] != &fn) { + return false; + } + } + } + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { for (auto& func : *module()) { for (auto& block : func) { @@ -227,7 +350,25 @@ bool IRContext::IsConsistent() { return false; } + if (AreAnalysesValid(kAnalysisDecorations)) { + analysis::DecorationManager* dec_mgr = get_decoration_mgr(); + analysis::DecorationManager current(module()); + + if (*dec_mgr != current) { + return false; + } + } + + if (feature_mgr_ != nullptr) { + FeatureManager current(grammar_); + current.Analyze(module()); + + if (current != *feature_mgr_) { + return false; + } + } return true; +#endif } void IRContext::ForgetUses(Instruction* inst) { @@ -239,6 +380,9 @@ void IRContext::ForgetUses(Instruction* inst) { get_decoration_mgr()->RemoveDecoration(inst); } } + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ClearDebugInfo(inst); + } RemoveFromIdToName(inst); } @@ -251,6 +395,9 @@ void IRContext::AnalyzeUses(Instruction* inst) { get_decoration_mgr()->AddDecoration(inst); } } + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->AnalyzeDebugInst(inst); + } if (id_to_name_ && (inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) { id_to_name_->insert({inst->GetSingleWordInOperand(0), inst}); @@ -258,12 +405,8 @@ void IRContext::AnalyzeUses(Instruction* inst) { } void IRContext::KillNamesAndDecorates(uint32_t id) { - std::vector<Instruction*> decorations = - get_decoration_mgr()->GetDecorationsFor(id, true); - - for (Instruction* inst : decorations) { - KillInst(inst); - } + analysis::DecorationManager* dec_mgr = get_decoration_mgr(); + dec_mgr->RemoveDecorationsFrom(id); std::vector<Instruction*> name_to_kill; for (auto name : GetNames(id)) { @@ -280,6 +423,39 @@ void IRContext::KillNamesAndDecorates(Instruction* inst) { KillNamesAndDecorates(rId); } +void IRContext::KillOperandFromDebugInstructions(Instruction* inst) { + const auto opcode = inst->opcode(); + const uint32_t id = inst->result_id(); + // Kill id of OpFunction from DebugFunction. + if (opcode == SpvOpFunction) { + for (auto it = module()->ext_inst_debuginfo_begin(); + it != module()->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugFunction) + continue; + auto& operand = it->GetOperand(kDebugFunctionOperandFunctionIndex); + if (operand.words[0] == id) { + operand.words[0] = + get_debug_info_mgr()->GetDebugInfoNone()->result_id(); + get_def_use_mgr()->AnalyzeInstUse(&*it); + } + } + } + // Kill id of OpVariable for global variable from DebugGlobalVariable. + if (opcode == SpvOpVariable || IsConstantInst(opcode)) { + for (auto it = module()->ext_inst_debuginfo_begin(); + it != module()->ext_inst_debuginfo_end(); ++it) { + if (it->GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) + continue; + auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex); + if (operand.words[0] == id) { + operand.words[0] = + get_debug_info_mgr()->GetDebugInfoNone()->result_id(); + get_def_use_mgr()->AnalyzeInstUse(&*it); + } + } + } +} + void IRContext::AddCombinatorsForCapability(uint32_t capability) { if (capability == SpvCapabilityShader) { combinator_ops_[0].insert({SpvOpNop, @@ -299,6 +475,9 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) { SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage, + SpvOpTypeAccelerationStructureNV, + SpvOpTypeAccelerationStructureKHR, + SpvOpTypeRayQueryKHR, SpvOpTypeArray, SpvOpTypeRuntimeArray, SpvOpTypeStruct, @@ -446,9 +625,8 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) { void IRContext::AddCombinatorsForExtension(Instruction* extension) { assert(extension->opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast<const char*>(&extension->GetInOperand(0).words[0]); - if (!strcmp(extension_name, "GLSL.std.450")) { + const std::string extension_name = extension->GetInOperand(0).AsString(); + if (extension_name == "GLSL.std.450") { combinator_ops_[extension->result_id()] = {GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, @@ -574,6 +752,229 @@ LoopDescriptor* IRContext::GetLoopDescriptor(const Function* f) { return &it->second; } +uint32_t IRContext::FindBuiltinInputVar(uint32_t builtin) { + for (auto& a : module_->annotations()) { + if (a.opcode() != SpvOpDecorate) continue; + if (a.GetSingleWordInOperand(kSpvDecorateDecorationInIdx) != + SpvDecorationBuiltIn) + continue; + if (a.GetSingleWordInOperand(kSpvDecorateBuiltinInIdx) != builtin) continue; + uint32_t target_id = a.GetSingleWordInOperand(kSpvDecorateTargetIdInIdx); + Instruction* b_var = get_def_use_mgr()->GetDef(target_id); + if (b_var->opcode() != SpvOpVariable) continue; + if (b_var->GetSingleWordInOperand(0) != SpvStorageClassInput) continue; + return target_id; + } + return 0; +} + +void IRContext::AddVarToEntryPoints(uint32_t var_id) { + uint32_t ocnt = 0; + for (auto& e : module()->entry_points()) { + bool found = false; + e.ForEachInOperand([&ocnt, &found, &var_id](const uint32_t* idp) { + if (ocnt >= kEntryPointInterfaceInIdx) { + if (*idp == var_id) found = true; + } + ++ocnt; + }); + if (!found) { + e.AddOperand({SPV_OPERAND_TYPE_ID, {var_id}}); + get_def_use_mgr()->AnalyzeInstDefUse(&e); + } + } +} + +uint32_t IRContext::GetBuiltinInputVarId(uint32_t builtin) { + if (!AreAnalysesValid(kAnalysisBuiltinVarId)) ResetBuiltinAnalysis(); + // If cached, return it. + std::unordered_map<uint32_t, uint32_t>::iterator it = + builtin_var_id_map_.find(builtin); + if (it != builtin_var_id_map_.end()) return it->second; + // Look for one in shader + uint32_t var_id = FindBuiltinInputVar(builtin); + if (var_id == 0) { + // If not found, create it + // TODO(greg-lunarg): Add support for all builtins + analysis::TypeManager* type_mgr = get_type_mgr(); + analysis::Type* reg_type; + switch (builtin) { + case SpvBuiltInFragCoord: { + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v4float_ty(reg_float_ty, 4); + reg_type = type_mgr->GetRegisteredType(&v4float_ty); + break; + } + case SpvBuiltInVertexIndex: + case SpvBuiltInInstanceIndex: + case SpvBuiltInPrimitiveId: + case SpvBuiltInInvocationId: + case SpvBuiltInSubgroupLocalInvocationId: { + analysis::Integer uint_ty(32, false); + reg_type = type_mgr->GetRegisteredType(&uint_ty); + break; + } + case SpvBuiltInGlobalInvocationId: + case SpvBuiltInLaunchIdNV: { + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Vector v3uint_ty(reg_uint_ty, 3); + reg_type = type_mgr->GetRegisteredType(&v3uint_ty); + break; + } + case SpvBuiltInTessCoord: { + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v3float_ty(reg_float_ty, 3); + reg_type = type_mgr->GetRegisteredType(&v3float_ty); + break; + } + case SpvBuiltInSubgroupLtMask: { + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Vector v4uint_ty(reg_uint_ty, 4); + reg_type = type_mgr->GetRegisteredType(&v4uint_ty); + break; + } + default: { + assert(false && "unhandled builtin"); + return 0; + } + } + uint32_t type_id = type_mgr->GetTypeInstruction(reg_type); + uint32_t varTyPtrId = + type_mgr->FindPointerToType(type_id, SpvStorageClassInput); + // TODO(1841): Handle id overflow. + var_id = TakeNextId(); + std::unique_ptr<Instruction> newVarOp( + new Instruction(this, SpvOpVariable, varTyPtrId, var_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvStorageClassInput}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*newVarOp); + module()->AddGlobalValue(std::move(newVarOp)); + get_decoration_mgr()->AddDecorationVal(var_id, SpvDecorationBuiltIn, + builtin); + AddVarToEntryPoints(var_id); + } + builtin_var_id_map_[builtin] = var_id; + return var_id; +} + +void IRContext::AddCalls(const Function* func, std::queue<uint32_t>* todo) { + for (auto bi = func->begin(); bi != func->end(); ++bi) + for (auto ii = bi->begin(); ii != bi->end(); ++ii) + if (ii->opcode() == SpvOpFunctionCall) + todo->push(ii->GetSingleWordInOperand(0)); +} + +bool IRContext::ProcessEntryPointCallTree(ProcessFunction& pfn) { + // Collect all of the entry points as the roots. + std::queue<uint32_t> roots; + for (auto& e : module()->entry_points()) { + roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + } + return ProcessCallTreeFromRoots(pfn, &roots); +} + +bool IRContext::ProcessReachableCallTree(ProcessFunction& pfn) { + std::queue<uint32_t> roots; + + // Add all entry points since they can be reached from outside the module. + for (auto& e : module()->entry_points()) + roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + + // Add all exported functions since they can be reached from outside the + // module. + for (auto& a : annotations()) { + // TODO: Handle group decorations as well. Currently not generate by any + // front-end, but could be coming. + if (a.opcode() == SpvOp::SpvOpDecorate) { + if (a.GetSingleWordOperand(1) == + SpvDecoration::SpvDecorationLinkageAttributes) { + uint32_t lastOperand = a.NumOperands() - 1; + if (a.GetSingleWordOperand(lastOperand) == + SpvLinkageType::SpvLinkageTypeExport) { + uint32_t id = a.GetSingleWordOperand(0); + if (GetFunction(id)) { + roots.push(id); + } + } + } + } + } + + return ProcessCallTreeFromRoots(pfn, &roots); +} + +bool IRContext::ProcessCallTreeFromRoots(ProcessFunction& pfn, + std::queue<uint32_t>* roots) { + // Process call tree + bool modified = false; + std::unordered_set<uint32_t> done; + + while (!roots->empty()) { + const uint32_t fi = roots->front(); + roots->pop(); + if (done.insert(fi).second) { + Function* fn = GetFunction(fi); + assert(fn && "Trying to process a function that does not exist."); + modified = pfn(fn) || modified; + AddCalls(fn, roots); + } + } + return modified; +} + +void IRContext::CollectCallTreeFromRoots(unsigned entryId, + std::unordered_set<uint32_t>* funcs) { + std::queue<uint32_t> roots; + roots.push(entryId); + while (!roots.empty()) { + const uint32_t fi = roots.front(); + roots.pop(); + funcs->insert(fi); + Function* fn = GetFunction(fi); + AddCalls(fn, &roots); + } +} + +void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { + if (!consumer()) { + return; + } + + Instruction* line_inst = inst; + while (line_inst != nullptr) { // Stop at the beginning of the basic block. + if (!line_inst->dbg_line_insts().empty()) { + line_inst = &line_inst->dbg_line_insts().back(); + if (line_inst->IsNoLine()) { + line_inst = nullptr; + } + break; + } + line_inst = line_inst->PreviousNode(); + } + + uint32_t line_number = 0; + uint32_t col_number = 0; + std::string source; + if (line_inst != nullptr) { + Instruction* file_name = + get_def_use_mgr()->GetDef(line_inst->GetSingleWordInOperand(0)); + source = file_name->GetInOperand(0).AsString(); + + // Get the line number and column number. + line_number = line_inst->GetSingleWordInOperand(1); + col_number = line_inst->GetSingleWordInOperand(2); + } + + message += + "\n " + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + consumer()(SPV_MSG_ERROR, source.c_str(), {line_number, col_number, 0}, + message.c_str()); +} + // Gets the dominator analysis for function |f|. DominatorAnalysis* IRContext::GetDominatorAnalysis(const Function* f) { if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) { @@ -651,5 +1052,11 @@ bool IRContext::CheckCFG() { return true; } + +bool IRContext::IsReachable(const opt::BasicBlock& bb) { + auto enclosing_function = bb.GetParent(); + return GetDominatorAnalysis(enclosing_function) + ->Dominates(enclosing_function->entry().get(), &bb); +} } // namespace opt } // namespace spvtools |