mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 13:20:05 +00:00
bf03d40922
GetCapabilities returned a const*, and GetExtensions did not exist. This commit adds GetExtensions, and changes the return value to be a const&. This commit also removes the overload to GetCapabilities which returns a mutable set, as it is unused. Signed-off-by: Nathan Gauër <brioche@google.com>
1144 lines
38 KiB
C++
1144 lines
38 KiB
C++
// Copyright (c) 2017 Google 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/ir_context.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include "OpenCLDebugInfo100.h"
|
|
#include "source/latest_version_glsl_std_450_header.h"
|
|
#include "source/opt/log.h"
|
|
#include "source/opt/reflect.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
namespace {
|
|
constexpr int kSpvDecorateTargetIdInIdx = 0;
|
|
constexpr int kSpvDecorateDecorationInIdx = 1;
|
|
constexpr int kSpvDecorateBuiltinInIdx = 2;
|
|
constexpr int kEntryPointInterfaceInIdx = 3;
|
|
constexpr int kEntryPointFunctionIdInIdx = 1;
|
|
constexpr int kEntryPointExecutionModelInIdx = 0;
|
|
|
|
// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
|
|
// extension instructions.
|
|
constexpr uint32_t kDebugFunctionOperandFunctionIndex = 13;
|
|
constexpr uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
|
|
} // namespace
|
|
|
|
void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
|
|
set = Analysis(set & ~valid_analyses_);
|
|
|
|
if (set & kAnalysisDefUse) {
|
|
BuildDefUseManager();
|
|
}
|
|
if (set & kAnalysisInstrToBlockMapping) {
|
|
BuildInstrToBlockMapping();
|
|
}
|
|
if (set & kAnalysisDecorations) {
|
|
BuildDecorationManager();
|
|
}
|
|
if (set & kAnalysisCFG) {
|
|
BuildCFG();
|
|
}
|
|
if (set & kAnalysisDominatorAnalysis) {
|
|
ResetDominatorAnalysis();
|
|
}
|
|
if (set & kAnalysisLoopAnalysis) {
|
|
ResetLoopAnalysis();
|
|
}
|
|
if (set & kAnalysisBuiltinVarId) {
|
|
ResetBuiltinAnalysis();
|
|
}
|
|
if (set & kAnalysisNameMap) {
|
|
BuildIdToNameMap();
|
|
}
|
|
if (set & kAnalysisScalarEvolution) {
|
|
BuildScalarEvolutionAnalysis();
|
|
}
|
|
if (set & kAnalysisRegisterPressure) {
|
|
BuildRegPressureAnalysis();
|
|
}
|
|
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(
|
|
IRContext::Analysis preserved_analyses) {
|
|
uint32_t analyses_to_invalidate = valid_analyses_ & (~preserved_analyses);
|
|
InvalidateAnalyses(static_cast<IRContext::Analysis>(analyses_to_invalidate));
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (analyses_to_invalidate & kAnalysisInstrToBlockMapping) {
|
|
instr_to_block_.clear();
|
|
}
|
|
if (analyses_to_invalidate & kAnalysisDecorations) {
|
|
decoration_mgr_.reset(nullptr);
|
|
}
|
|
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);
|
|
}
|
|
if (analyses_to_invalidate & kAnalysisDominatorAnalysis) {
|
|
dominator_trees_.clear();
|
|
post_dominator_trees_.clear();
|
|
}
|
|
if (analyses_to_invalidate & kAnalysisNameMap) {
|
|
id_to_name_.reset(nullptr);
|
|
}
|
|
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 & kAnalysisLiveness) {
|
|
liveness_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);
|
|
}
|
|
|
|
Instruction* IRContext::KillInst(Instruction* inst) {
|
|
if (!inst) {
|
|
return nullptr;
|
|
}
|
|
|
|
KillNamesAndDecorates(inst);
|
|
|
|
KillOperandFromDebugInstructions(inst);
|
|
|
|
if (AreAnalysesValid(kAnalysisDefUse)) {
|
|
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->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() == spv::Op::OpCapability ||
|
|
inst->opcode() == spv::Op::OpExtension) {
|
|
// 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);
|
|
|
|
Instruction* next_instruction = nullptr;
|
|
if (inst->IsInAList()) {
|
|
next_instruction = inst->NextNode();
|
|
inst->RemoveFromList();
|
|
delete inst;
|
|
} else {
|
|
// Needed for instructions that are not part of a list like OpLabels,
|
|
// OpFunction, OpFunctionEnd, etc..
|
|
inst->ToNop();
|
|
}
|
|
return next_instruction;
|
|
}
|
|
|
|
bool IRContext::KillInstructionIf(Module::inst_iterator begin,
|
|
Module::inst_iterator end,
|
|
std::function<bool(Instruction*)> condition) {
|
|
bool removed = false;
|
|
for (auto it = begin; it != end;) {
|
|
if (!condition(&*it)) {
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
removed = true;
|
|
// `it` is an iterator on an intrusive list. Next is invalidated on the
|
|
// current node when an instruction is killed. The iterator must be moved
|
|
// forward before deleting the node.
|
|
auto instruction = &*it;
|
|
++it;
|
|
KillInst(instruction);
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
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) {
|
|
KillInst(def);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IRContext::RemoveCapability(spv::Capability capability) {
|
|
const bool removed = KillInstructionIf(
|
|
module()->capability_begin(), module()->capability_end(),
|
|
[capability](Instruction* inst) {
|
|
return static_cast<spv::Capability>(inst->GetSingleWordOperand(0)) ==
|
|
capability;
|
|
});
|
|
|
|
if (removed && feature_mgr_ != nullptr) {
|
|
feature_mgr_->RemoveCapability(capability);
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
bool IRContext::RemoveExtension(Extension extension) {
|
|
const std::string_view extensionName = ExtensionToString(extension);
|
|
const bool removed = KillInstructionIf(
|
|
module()->extension_begin(), module()->extension_end(),
|
|
[&extensionName](Instruction* inst) {
|
|
return inst->GetOperand(0).AsString() == extensionName;
|
|
});
|
|
|
|
if (removed && feature_mgr_ != nullptr) {
|
|
feature_mgr_->RemoveExtension(extension);
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
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, [&predicate, &uses_to_update](Instruction* user, uint32_t index) {
|
|
if (predicate(user)) {
|
|
uses_to_update.emplace_back(user, index);
|
|
}
|
|
});
|
|
|
|
Instruction* prev = nullptr;
|
|
for (auto p : uses_to_update) {
|
|
Instruction* user = p.first;
|
|
uint32_t index = p.second;
|
|
if (prev == nullptr || prev != user) {
|
|
ForgetUses(user);
|
|
prev = user;
|
|
}
|
|
const uint32_t type_result_id_count =
|
|
(user->result_id() != 0) + (user->type_id() != 0);
|
|
|
|
if (index < type_result_id_count) {
|
|
// Update the type_id. Note that result id is immutable so it should
|
|
// never be updated.
|
|
if (user->type_id() != 0 && index == 0) {
|
|
user->SetResultType(after);
|
|
} else if (user->type_id() == 0) {
|
|
SPIRV_ASSERT(consumer_, false,
|
|
"Result type id considered as use while the instruction "
|
|
"doesn't have a result type id.");
|
|
(void)consumer_; // Makes the compiler happy for release build.
|
|
} else {
|
|
SPIRV_ASSERT(consumer_, false,
|
|
"Trying setting the immutable result id.");
|
|
}
|
|
} else {
|
|
// Update an in-operand.
|
|
uint32_t in_operand_pos = index - type_result_id_count;
|
|
// Make the modification in the instruction.
|
|
user->SetInOperand(in_operand_pos, {after});
|
|
}
|
|
AnalyzeUses(user);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool IRContext::IsConsistent() {
|
|
#ifndef SPIRV_CHECK_CONTEXT
|
|
return true;
|
|
#else
|
|
if (AreAnalysesValid(kAnalysisDefUse)) {
|
|
analysis::DefUseManager new_def_use(module());
|
|
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) {
|
|
if (!block.WhileEachInst([this, &block](Instruction* inst) {
|
|
if (get_instr_block(inst) != &block) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!CheckCFG()) {
|
|
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) {
|
|
if (AreAnalysesValid(kAnalysisDefUse)) {
|
|
get_def_use_mgr()->EraseUseRecordsOfOperandIds(inst);
|
|
}
|
|
if (AreAnalysesValid(kAnalysisDecorations)) {
|
|
if (inst->IsDecoration()) {
|
|
get_decoration_mgr()->RemoveDecoration(inst);
|
|
}
|
|
}
|
|
if (AreAnalysesValid(kAnalysisDebugInfo)) {
|
|
get_debug_info_mgr()->ClearDebugInfo(inst);
|
|
}
|
|
RemoveFromIdToName(inst);
|
|
}
|
|
|
|
void IRContext::AnalyzeUses(Instruction* inst) {
|
|
if (AreAnalysesValid(kAnalysisDefUse)) {
|
|
get_def_use_mgr()->AnalyzeInstUse(inst);
|
|
}
|
|
if (AreAnalysesValid(kAnalysisDecorations)) {
|
|
if (inst->IsDecoration()) {
|
|
get_decoration_mgr()->AddDecoration(inst);
|
|
}
|
|
}
|
|
if (AreAnalysesValid(kAnalysisDebugInfo)) {
|
|
get_debug_info_mgr()->AnalyzeDebugInst(inst);
|
|
}
|
|
if (id_to_name_ && (inst->opcode() == spv::Op::OpName ||
|
|
inst->opcode() == spv::Op::OpMemberName)) {
|
|
id_to_name_->insert({inst->GetSingleWordInOperand(0), inst});
|
|
}
|
|
}
|
|
|
|
void IRContext::KillNamesAndDecorates(uint32_t id) {
|
|
analysis::DecorationManager* dec_mgr = get_decoration_mgr();
|
|
dec_mgr->RemoveDecorationsFrom(id);
|
|
|
|
std::vector<Instruction*> name_to_kill;
|
|
for (auto name : GetNames(id)) {
|
|
name_to_kill.push_back(name.second);
|
|
}
|
|
for (Instruction* name_inst : name_to_kill) {
|
|
KillInst(name_inst);
|
|
}
|
|
}
|
|
|
|
void IRContext::KillNamesAndDecorates(Instruction* inst) {
|
|
const uint32_t rId = inst->result_id();
|
|
if (rId == 0) return;
|
|
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 == spv::Op::OpFunction) {
|
|
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 == spv::Op::OpVariable || 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) {
|
|
spv::Capability cap = spv::Capability(capability);
|
|
if (cap == spv::Capability::Shader) {
|
|
combinator_ops_[0].insert(
|
|
{(uint32_t)spv::Op::OpNop,
|
|
(uint32_t)spv::Op::OpUndef,
|
|
(uint32_t)spv::Op::OpConstant,
|
|
(uint32_t)spv::Op::OpConstantTrue,
|
|
(uint32_t)spv::Op::OpConstantFalse,
|
|
(uint32_t)spv::Op::OpConstantComposite,
|
|
(uint32_t)spv::Op::OpConstantSampler,
|
|
(uint32_t)spv::Op::OpConstantNull,
|
|
(uint32_t)spv::Op::OpTypeVoid,
|
|
(uint32_t)spv::Op::OpTypeBool,
|
|
(uint32_t)spv::Op::OpTypeInt,
|
|
(uint32_t)spv::Op::OpTypeFloat,
|
|
(uint32_t)spv::Op::OpTypeVector,
|
|
(uint32_t)spv::Op::OpTypeMatrix,
|
|
(uint32_t)spv::Op::OpTypeImage,
|
|
(uint32_t)spv::Op::OpTypeSampler,
|
|
(uint32_t)spv::Op::OpTypeSampledImage,
|
|
(uint32_t)spv::Op::OpTypeAccelerationStructureNV,
|
|
(uint32_t)spv::Op::OpTypeAccelerationStructureKHR,
|
|
(uint32_t)spv::Op::OpTypeRayQueryKHR,
|
|
(uint32_t)spv::Op::OpTypeHitObjectNV,
|
|
(uint32_t)spv::Op::OpTypeArray,
|
|
(uint32_t)spv::Op::OpTypeRuntimeArray,
|
|
(uint32_t)spv::Op::OpTypeStruct,
|
|
(uint32_t)spv::Op::OpTypeOpaque,
|
|
(uint32_t)spv::Op::OpTypePointer,
|
|
(uint32_t)spv::Op::OpTypeFunction,
|
|
(uint32_t)spv::Op::OpTypeEvent,
|
|
(uint32_t)spv::Op::OpTypeDeviceEvent,
|
|
(uint32_t)spv::Op::OpTypeReserveId,
|
|
(uint32_t)spv::Op::OpTypeQueue,
|
|
(uint32_t)spv::Op::OpTypePipe,
|
|
(uint32_t)spv::Op::OpTypeForwardPointer,
|
|
(uint32_t)spv::Op::OpVariable,
|
|
(uint32_t)spv::Op::OpImageTexelPointer,
|
|
(uint32_t)spv::Op::OpLoad,
|
|
(uint32_t)spv::Op::OpAccessChain,
|
|
(uint32_t)spv::Op::OpInBoundsAccessChain,
|
|
(uint32_t)spv::Op::OpArrayLength,
|
|
(uint32_t)spv::Op::OpVectorExtractDynamic,
|
|
(uint32_t)spv::Op::OpVectorInsertDynamic,
|
|
(uint32_t)spv::Op::OpVectorShuffle,
|
|
(uint32_t)spv::Op::OpCompositeConstruct,
|
|
(uint32_t)spv::Op::OpCompositeExtract,
|
|
(uint32_t)spv::Op::OpCompositeInsert,
|
|
(uint32_t)spv::Op::OpCopyObject,
|
|
(uint32_t)spv::Op::OpTranspose,
|
|
(uint32_t)spv::Op::OpSampledImage,
|
|
(uint32_t)spv::Op::OpImageSampleImplicitLod,
|
|
(uint32_t)spv::Op::OpImageSampleExplicitLod,
|
|
(uint32_t)spv::Op::OpImageSampleDrefImplicitLod,
|
|
(uint32_t)spv::Op::OpImageSampleDrefExplicitLod,
|
|
(uint32_t)spv::Op::OpImageSampleProjImplicitLod,
|
|
(uint32_t)spv::Op::OpImageSampleProjExplicitLod,
|
|
(uint32_t)spv::Op::OpImageSampleProjDrefImplicitLod,
|
|
(uint32_t)spv::Op::OpImageSampleProjDrefExplicitLod,
|
|
(uint32_t)spv::Op::OpImageFetch,
|
|
(uint32_t)spv::Op::OpImageGather,
|
|
(uint32_t)spv::Op::OpImageDrefGather,
|
|
(uint32_t)spv::Op::OpImageRead,
|
|
(uint32_t)spv::Op::OpImage,
|
|
(uint32_t)spv::Op::OpImageQueryFormat,
|
|
(uint32_t)spv::Op::OpImageQueryOrder,
|
|
(uint32_t)spv::Op::OpImageQuerySizeLod,
|
|
(uint32_t)spv::Op::OpImageQuerySize,
|
|
(uint32_t)spv::Op::OpImageQueryLevels,
|
|
(uint32_t)spv::Op::OpImageQuerySamples,
|
|
(uint32_t)spv::Op::OpConvertFToU,
|
|
(uint32_t)spv::Op::OpConvertFToS,
|
|
(uint32_t)spv::Op::OpConvertSToF,
|
|
(uint32_t)spv::Op::OpConvertUToF,
|
|
(uint32_t)spv::Op::OpUConvert,
|
|
(uint32_t)spv::Op::OpSConvert,
|
|
(uint32_t)spv::Op::OpFConvert,
|
|
(uint32_t)spv::Op::OpQuantizeToF16,
|
|
(uint32_t)spv::Op::OpBitcast,
|
|
(uint32_t)spv::Op::OpSNegate,
|
|
(uint32_t)spv::Op::OpFNegate,
|
|
(uint32_t)spv::Op::OpIAdd,
|
|
(uint32_t)spv::Op::OpFAdd,
|
|
(uint32_t)spv::Op::OpISub,
|
|
(uint32_t)spv::Op::OpFSub,
|
|
(uint32_t)spv::Op::OpIMul,
|
|
(uint32_t)spv::Op::OpFMul,
|
|
(uint32_t)spv::Op::OpUDiv,
|
|
(uint32_t)spv::Op::OpSDiv,
|
|
(uint32_t)spv::Op::OpFDiv,
|
|
(uint32_t)spv::Op::OpUMod,
|
|
(uint32_t)spv::Op::OpSRem,
|
|
(uint32_t)spv::Op::OpSMod,
|
|
(uint32_t)spv::Op::OpFRem,
|
|
(uint32_t)spv::Op::OpFMod,
|
|
(uint32_t)spv::Op::OpVectorTimesScalar,
|
|
(uint32_t)spv::Op::OpMatrixTimesScalar,
|
|
(uint32_t)spv::Op::OpVectorTimesMatrix,
|
|
(uint32_t)spv::Op::OpMatrixTimesVector,
|
|
(uint32_t)spv::Op::OpMatrixTimesMatrix,
|
|
(uint32_t)spv::Op::OpOuterProduct,
|
|
(uint32_t)spv::Op::OpDot,
|
|
(uint32_t)spv::Op::OpIAddCarry,
|
|
(uint32_t)spv::Op::OpISubBorrow,
|
|
(uint32_t)spv::Op::OpUMulExtended,
|
|
(uint32_t)spv::Op::OpSMulExtended,
|
|
(uint32_t)spv::Op::OpAny,
|
|
(uint32_t)spv::Op::OpAll,
|
|
(uint32_t)spv::Op::OpIsNan,
|
|
(uint32_t)spv::Op::OpIsInf,
|
|
(uint32_t)spv::Op::OpLogicalEqual,
|
|
(uint32_t)spv::Op::OpLogicalNotEqual,
|
|
(uint32_t)spv::Op::OpLogicalOr,
|
|
(uint32_t)spv::Op::OpLogicalAnd,
|
|
(uint32_t)spv::Op::OpLogicalNot,
|
|
(uint32_t)spv::Op::OpSelect,
|
|
(uint32_t)spv::Op::OpIEqual,
|
|
(uint32_t)spv::Op::OpINotEqual,
|
|
(uint32_t)spv::Op::OpUGreaterThan,
|
|
(uint32_t)spv::Op::OpSGreaterThan,
|
|
(uint32_t)spv::Op::OpUGreaterThanEqual,
|
|
(uint32_t)spv::Op::OpSGreaterThanEqual,
|
|
(uint32_t)spv::Op::OpULessThan,
|
|
(uint32_t)spv::Op::OpSLessThan,
|
|
(uint32_t)spv::Op::OpULessThanEqual,
|
|
(uint32_t)spv::Op::OpSLessThanEqual,
|
|
(uint32_t)spv::Op::OpFOrdEqual,
|
|
(uint32_t)spv::Op::OpFUnordEqual,
|
|
(uint32_t)spv::Op::OpFOrdNotEqual,
|
|
(uint32_t)spv::Op::OpFUnordNotEqual,
|
|
(uint32_t)spv::Op::OpFOrdLessThan,
|
|
(uint32_t)spv::Op::OpFUnordLessThan,
|
|
(uint32_t)spv::Op::OpFOrdGreaterThan,
|
|
(uint32_t)spv::Op::OpFUnordGreaterThan,
|
|
(uint32_t)spv::Op::OpFOrdLessThanEqual,
|
|
(uint32_t)spv::Op::OpFUnordLessThanEqual,
|
|
(uint32_t)spv::Op::OpFOrdGreaterThanEqual,
|
|
(uint32_t)spv::Op::OpFUnordGreaterThanEqual,
|
|
(uint32_t)spv::Op::OpShiftRightLogical,
|
|
(uint32_t)spv::Op::OpShiftRightArithmetic,
|
|
(uint32_t)spv::Op::OpShiftLeftLogical,
|
|
(uint32_t)spv::Op::OpBitwiseOr,
|
|
(uint32_t)spv::Op::OpBitwiseXor,
|
|
(uint32_t)spv::Op::OpBitwiseAnd,
|
|
(uint32_t)spv::Op::OpNot,
|
|
(uint32_t)spv::Op::OpBitFieldInsert,
|
|
(uint32_t)spv::Op::OpBitFieldSExtract,
|
|
(uint32_t)spv::Op::OpBitFieldUExtract,
|
|
(uint32_t)spv::Op::OpBitReverse,
|
|
(uint32_t)spv::Op::OpBitCount,
|
|
(uint32_t)spv::Op::OpPhi,
|
|
(uint32_t)spv::Op::OpImageSparseSampleImplicitLod,
|
|
(uint32_t)spv::Op::OpImageSparseSampleExplicitLod,
|
|
(uint32_t)spv::Op::OpImageSparseSampleDrefImplicitLod,
|
|
(uint32_t)spv::Op::OpImageSparseSampleDrefExplicitLod,
|
|
(uint32_t)spv::Op::OpImageSparseSampleProjImplicitLod,
|
|
(uint32_t)spv::Op::OpImageSparseSampleProjExplicitLod,
|
|
(uint32_t)spv::Op::OpImageSparseSampleProjDrefImplicitLod,
|
|
(uint32_t)spv::Op::OpImageSparseSampleProjDrefExplicitLod,
|
|
(uint32_t)spv::Op::OpImageSparseFetch,
|
|
(uint32_t)spv::Op::OpImageSparseGather,
|
|
(uint32_t)spv::Op::OpImageSparseDrefGather,
|
|
(uint32_t)spv::Op::OpImageSparseTexelsResident,
|
|
(uint32_t)spv::Op::OpImageSparseRead,
|
|
(uint32_t)spv::Op::OpSizeOf});
|
|
}
|
|
}
|
|
|
|
void IRContext::AddCombinatorsForExtension(Instruction* extension) {
|
|
assert(extension->opcode() == spv::Op::OpExtInstImport &&
|
|
"Expecting an import of an extension's instruction set.");
|
|
const std::string extension_name = extension->GetInOperand(0).AsString();
|
|
if (extension_name == "GLSL.std.450") {
|
|
combinator_ops_[extension->result_id()] = {
|
|
(uint32_t)GLSLstd450Round,
|
|
(uint32_t)GLSLstd450RoundEven,
|
|
(uint32_t)GLSLstd450Trunc,
|
|
(uint32_t)GLSLstd450FAbs,
|
|
(uint32_t)GLSLstd450SAbs,
|
|
(uint32_t)GLSLstd450FSign,
|
|
(uint32_t)GLSLstd450SSign,
|
|
(uint32_t)GLSLstd450Floor,
|
|
(uint32_t)GLSLstd450Ceil,
|
|
(uint32_t)GLSLstd450Fract,
|
|
(uint32_t)GLSLstd450Radians,
|
|
(uint32_t)GLSLstd450Degrees,
|
|
(uint32_t)GLSLstd450Sin,
|
|
(uint32_t)GLSLstd450Cos,
|
|
(uint32_t)GLSLstd450Tan,
|
|
(uint32_t)GLSLstd450Asin,
|
|
(uint32_t)GLSLstd450Acos,
|
|
(uint32_t)GLSLstd450Atan,
|
|
(uint32_t)GLSLstd450Sinh,
|
|
(uint32_t)GLSLstd450Cosh,
|
|
(uint32_t)GLSLstd450Tanh,
|
|
(uint32_t)GLSLstd450Asinh,
|
|
(uint32_t)GLSLstd450Acosh,
|
|
(uint32_t)GLSLstd450Atanh,
|
|
(uint32_t)GLSLstd450Atan2,
|
|
(uint32_t)GLSLstd450Pow,
|
|
(uint32_t)GLSLstd450Exp,
|
|
(uint32_t)GLSLstd450Log,
|
|
(uint32_t)GLSLstd450Exp2,
|
|
(uint32_t)GLSLstd450Log2,
|
|
(uint32_t)GLSLstd450Sqrt,
|
|
(uint32_t)GLSLstd450InverseSqrt,
|
|
(uint32_t)GLSLstd450Determinant,
|
|
(uint32_t)GLSLstd450MatrixInverse,
|
|
(uint32_t)GLSLstd450ModfStruct,
|
|
(uint32_t)GLSLstd450FMin,
|
|
(uint32_t)GLSLstd450UMin,
|
|
(uint32_t)GLSLstd450SMin,
|
|
(uint32_t)GLSLstd450FMax,
|
|
(uint32_t)GLSLstd450UMax,
|
|
(uint32_t)GLSLstd450SMax,
|
|
(uint32_t)GLSLstd450FClamp,
|
|
(uint32_t)GLSLstd450UClamp,
|
|
(uint32_t)GLSLstd450SClamp,
|
|
(uint32_t)GLSLstd450FMix,
|
|
(uint32_t)GLSLstd450IMix,
|
|
(uint32_t)GLSLstd450Step,
|
|
(uint32_t)GLSLstd450SmoothStep,
|
|
(uint32_t)GLSLstd450Fma,
|
|
(uint32_t)GLSLstd450FrexpStruct,
|
|
(uint32_t)GLSLstd450Ldexp,
|
|
(uint32_t)GLSLstd450PackSnorm4x8,
|
|
(uint32_t)GLSLstd450PackUnorm4x8,
|
|
(uint32_t)GLSLstd450PackSnorm2x16,
|
|
(uint32_t)GLSLstd450PackUnorm2x16,
|
|
(uint32_t)GLSLstd450PackHalf2x16,
|
|
(uint32_t)GLSLstd450PackDouble2x32,
|
|
(uint32_t)GLSLstd450UnpackSnorm2x16,
|
|
(uint32_t)GLSLstd450UnpackUnorm2x16,
|
|
(uint32_t)GLSLstd450UnpackHalf2x16,
|
|
(uint32_t)GLSLstd450UnpackSnorm4x8,
|
|
(uint32_t)GLSLstd450UnpackUnorm4x8,
|
|
(uint32_t)GLSLstd450UnpackDouble2x32,
|
|
(uint32_t)GLSLstd450Length,
|
|
(uint32_t)GLSLstd450Distance,
|
|
(uint32_t)GLSLstd450Cross,
|
|
(uint32_t)GLSLstd450Normalize,
|
|
(uint32_t)GLSLstd450FaceForward,
|
|
(uint32_t)GLSLstd450Reflect,
|
|
(uint32_t)GLSLstd450Refract,
|
|
(uint32_t)GLSLstd450FindILsb,
|
|
(uint32_t)GLSLstd450FindSMsb,
|
|
(uint32_t)GLSLstd450FindUMsb,
|
|
(uint32_t)GLSLstd450InterpolateAtCentroid,
|
|
(uint32_t)GLSLstd450InterpolateAtSample,
|
|
(uint32_t)GLSLstd450InterpolateAtOffset,
|
|
(uint32_t)GLSLstd450NMin,
|
|
(uint32_t)GLSLstd450NMax,
|
|
(uint32_t)GLSLstd450NClamp};
|
|
} else {
|
|
// Map the result id to the empty set.
|
|
combinator_ops_[extension->result_id()];
|
|
}
|
|
}
|
|
|
|
void IRContext::InitializeCombinators() {
|
|
for (auto capability : get_feature_mgr()->GetCapabilities()) {
|
|
AddCombinatorsForCapability(uint32_t(capability));
|
|
}
|
|
|
|
for (auto& extension : module()->ext_inst_imports()) {
|
|
AddCombinatorsForExtension(&extension);
|
|
}
|
|
|
|
valid_analyses_ |= kAnalysisCombinators;
|
|
}
|
|
|
|
void IRContext::RemoveFromIdToName(const Instruction* inst) {
|
|
if (id_to_name_ && (inst->opcode() == spv::Op::OpName ||
|
|
inst->opcode() == spv::Op::OpMemberName)) {
|
|
auto range = id_to_name_->equal_range(inst->GetSingleWordInOperand(0));
|
|
for (auto it = range.first; it != range.second; ++it) {
|
|
if (it->second == inst) {
|
|
id_to_name_->erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LoopDescriptor* IRContext::GetLoopDescriptor(const Function* f) {
|
|
if (!AreAnalysesValid(kAnalysisLoopAnalysis)) {
|
|
ResetLoopAnalysis();
|
|
}
|
|
|
|
std::unordered_map<const Function*, LoopDescriptor>::iterator it =
|
|
loop_descriptors_.find(f);
|
|
if (it == loop_descriptors_.end()) {
|
|
return &loop_descriptors_
|
|
.emplace(std::make_pair(f, LoopDescriptor(this, f)))
|
|
.first->second;
|
|
}
|
|
|
|
return &it->second;
|
|
}
|
|
|
|
uint32_t IRContext::FindBuiltinInputVar(uint32_t builtin) {
|
|
for (auto& a : module_->annotations()) {
|
|
if (spv::Op(a.opcode()) != spv::Op::OpDecorate) continue;
|
|
if (spv::Decoration(a.GetSingleWordInOperand(
|
|
kSpvDecorateDecorationInIdx)) != spv::Decoration::BuiltIn)
|
|
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() != spv::Op::OpVariable) continue;
|
|
if (spv::StorageClass(b_var->GetSingleWordInOperand(0)) !=
|
|
spv::StorageClass::Input)
|
|
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 (spv::BuiltIn(builtin)) {
|
|
case spv::BuiltIn::FragCoord: {
|
|
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 spv::BuiltIn::VertexIndex:
|
|
case spv::BuiltIn::InstanceIndex:
|
|
case spv::BuiltIn::PrimitiveId:
|
|
case spv::BuiltIn::InvocationId:
|
|
case spv::BuiltIn::SubgroupLocalInvocationId: {
|
|
analysis::Integer uint_ty(32, false);
|
|
reg_type = type_mgr->GetRegisteredType(&uint_ty);
|
|
break;
|
|
}
|
|
case spv::BuiltIn::GlobalInvocationId:
|
|
case spv::BuiltIn::LaunchIdNV: {
|
|
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 spv::BuiltIn::TessCoord: {
|
|
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 spv::BuiltIn::SubgroupLtMask: {
|
|
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, spv::StorageClass::Input);
|
|
// TODO(1841): Handle id overflow.
|
|
var_id = TakeNextId();
|
|
std::unique_ptr<Instruction> newVarOp(
|
|
new Instruction(this, spv::Op::OpVariable, varTyPtrId, var_id,
|
|
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
|
{uint32_t(spv::StorageClass::Input)}}}));
|
|
get_def_use_mgr()->AnalyzeInstDefUse(&*newVarOp);
|
|
module()->AddGlobalValue(std::move(newVarOp));
|
|
get_decoration_mgr()->AddDecorationVal(
|
|
var_id, uint32_t(spv::Decoration::BuiltIn), 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() == spv::Op::OpFunctionCall)
|
|
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() == spv::Op::OpDecorate) {
|
|
if (spv::Decoration(a.GetSingleWordOperand(1)) ==
|
|
spv::Decoration::LinkageAttributes) {
|
|
uint32_t lastOperand = a.NumOperands() - 1;
|
|
if (spv::LinkageType(a.GetSingleWordOperand(lastOperand)) ==
|
|
spv::LinkageType::Export) {
|
|
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)) {
|
|
ResetDominatorAnalysis();
|
|
}
|
|
|
|
if (dominator_trees_.find(f) == dominator_trees_.end()) {
|
|
dominator_trees_[f].InitializeTree(*cfg(), f);
|
|
}
|
|
|
|
return &dominator_trees_[f];
|
|
}
|
|
|
|
// Gets the postdominator analysis for function |f|.
|
|
PostDominatorAnalysis* IRContext::GetPostDominatorAnalysis(const Function* f) {
|
|
if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) {
|
|
ResetDominatorAnalysis();
|
|
}
|
|
|
|
if (post_dominator_trees_.find(f) == post_dominator_trees_.end()) {
|
|
post_dominator_trees_[f].InitializeTree(*cfg(), f);
|
|
}
|
|
|
|
return &post_dominator_trees_[f];
|
|
}
|
|
|
|
bool IRContext::CheckCFG() {
|
|
std::unordered_map<uint32_t, std::vector<uint32_t>> real_preds;
|
|
if (!AreAnalysesValid(kAnalysisCFG)) {
|
|
return true;
|
|
}
|
|
|
|
for (Function& function : *module()) {
|
|
for (const auto& bb : function) {
|
|
bb.ForEachSuccessorLabel([&bb, &real_preds](const uint32_t lab_id) {
|
|
real_preds[lab_id].push_back(bb.id());
|
|
});
|
|
}
|
|
|
|
for (auto& bb : function) {
|
|
std::vector<uint32_t> preds = cfg()->preds(bb.id());
|
|
std::vector<uint32_t> real = real_preds[bb.id()];
|
|
std::sort(preds.begin(), preds.end());
|
|
std::sort(real.begin(), real.end());
|
|
|
|
bool same = true;
|
|
if (preds.size() != real.size()) {
|
|
same = false;
|
|
}
|
|
|
|
for (size_t i = 0; i < real.size() && same; i++) {
|
|
if (preds[i] != real[i]) {
|
|
same = false;
|
|
}
|
|
}
|
|
|
|
if (!same) {
|
|
std::cerr << "Predecessors for " << bb.id() << " are different:\n";
|
|
|
|
std::cerr << "Real:";
|
|
for (uint32_t i : real) {
|
|
std::cerr << ' ' << i;
|
|
}
|
|
std::cerr << std::endl;
|
|
|
|
std::cerr << "Recorded:";
|
|
for (uint32_t i : preds) {
|
|
std::cerr << ' ' << i;
|
|
}
|
|
std::cerr << std::endl;
|
|
}
|
|
if (!same) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IRContext::IsReachable(const opt::BasicBlock& bb) {
|
|
auto enclosing_function = bb.GetParent();
|
|
return GetDominatorAnalysis(enclosing_function)
|
|
->Dominates(enclosing_function->entry().get(), &bb);
|
|
}
|
|
|
|
spv::ExecutionModel IRContext::GetStage() {
|
|
const auto& entry_points = module()->entry_points();
|
|
if (entry_points.empty()) {
|
|
return spv::ExecutionModel::Max;
|
|
}
|
|
|
|
uint32_t stage = entry_points.begin()->GetSingleWordInOperand(
|
|
kEntryPointExecutionModelInIdx);
|
|
auto it = std::find_if(
|
|
entry_points.begin(), entry_points.end(), [stage](const Instruction& x) {
|
|
return x.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) !=
|
|
stage;
|
|
});
|
|
if (it != entry_points.end()) {
|
|
EmitErrorMessage("Mixed stage shader module not supported", &(*it));
|
|
}
|
|
|
|
return static_cast<spv::ExecutionModel>(stage);
|
|
}
|
|
|
|
} // namespace opt
|
|
} // namespace spvtools
|