summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.cpp
diff options
context:
space:
mode:
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.cpp449
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