SPIRV-Tools/source/opt/ir_context.cpp
Greg Fischer 19dc86c48c
Handle NonSemantic.Shader Debug[No]Line (#4530)
Debug[No]Line are tracked and optimized using the same mechanism that tracks
and optimizes Op[No]Line.

Also:
    - Fix missing DebugScope at top of block.
    - Allow scalar replacement of access chain in DebugDeclare
2021-09-24 10:56:08 -04:00

1049 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/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) {
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 psuedo 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 & 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() == 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);
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;
}
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::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 (*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() == SpvOpName || inst->opcode() == SpvOpMemberName)) {
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 == 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,
SpvOpUndef,
SpvOpConstant,
SpvOpConstantTrue,
SpvOpConstantFalse,
SpvOpConstantComposite,
SpvOpConstantSampler,
SpvOpConstantNull,
SpvOpTypeVoid,
SpvOpTypeBool,
SpvOpTypeInt,
SpvOpTypeFloat,
SpvOpTypeVector,
SpvOpTypeMatrix,
SpvOpTypeImage,
SpvOpTypeSampler,
SpvOpTypeSampledImage,
SpvOpTypeAccelerationStructureNV,
SpvOpTypeAccelerationStructureKHR,
SpvOpTypeRayQueryKHR,
SpvOpTypeArray,
SpvOpTypeRuntimeArray,
SpvOpTypeStruct,
SpvOpTypeOpaque,
SpvOpTypePointer,
SpvOpTypeFunction,
SpvOpTypeEvent,
SpvOpTypeDeviceEvent,
SpvOpTypeReserveId,
SpvOpTypeQueue,
SpvOpTypePipe,
SpvOpTypeForwardPointer,
SpvOpVariable,
SpvOpImageTexelPointer,
SpvOpLoad,
SpvOpAccessChain,
SpvOpInBoundsAccessChain,
SpvOpArrayLength,
SpvOpVectorExtractDynamic,
SpvOpVectorInsertDynamic,
SpvOpVectorShuffle,
SpvOpCompositeConstruct,
SpvOpCompositeExtract,
SpvOpCompositeInsert,
SpvOpCopyObject,
SpvOpTranspose,
SpvOpSampledImage,
SpvOpImageSampleImplicitLod,
SpvOpImageSampleExplicitLod,
SpvOpImageSampleDrefImplicitLod,
SpvOpImageSampleDrefExplicitLod,
SpvOpImageSampleProjImplicitLod,
SpvOpImageSampleProjExplicitLod,
SpvOpImageSampleProjDrefImplicitLod,
SpvOpImageSampleProjDrefExplicitLod,
SpvOpImageFetch,
SpvOpImageGather,
SpvOpImageDrefGather,
SpvOpImageRead,
SpvOpImage,
SpvOpImageQueryFormat,
SpvOpImageQueryOrder,
SpvOpImageQuerySizeLod,
SpvOpImageQuerySize,
SpvOpImageQueryLevels,
SpvOpImageQuerySamples,
SpvOpConvertFToU,
SpvOpConvertFToS,
SpvOpConvertSToF,
SpvOpConvertUToF,
SpvOpUConvert,
SpvOpSConvert,
SpvOpFConvert,
SpvOpQuantizeToF16,
SpvOpBitcast,
SpvOpSNegate,
SpvOpFNegate,
SpvOpIAdd,
SpvOpFAdd,
SpvOpISub,
SpvOpFSub,
SpvOpIMul,
SpvOpFMul,
SpvOpUDiv,
SpvOpSDiv,
SpvOpFDiv,
SpvOpUMod,
SpvOpSRem,
SpvOpSMod,
SpvOpFRem,
SpvOpFMod,
SpvOpVectorTimesScalar,
SpvOpMatrixTimesScalar,
SpvOpVectorTimesMatrix,
SpvOpMatrixTimesVector,
SpvOpMatrixTimesMatrix,
SpvOpOuterProduct,
SpvOpDot,
SpvOpIAddCarry,
SpvOpISubBorrow,
SpvOpUMulExtended,
SpvOpSMulExtended,
SpvOpAny,
SpvOpAll,
SpvOpIsNan,
SpvOpIsInf,
SpvOpLogicalEqual,
SpvOpLogicalNotEqual,
SpvOpLogicalOr,
SpvOpLogicalAnd,
SpvOpLogicalNot,
SpvOpSelect,
SpvOpIEqual,
SpvOpINotEqual,
SpvOpUGreaterThan,
SpvOpSGreaterThan,
SpvOpUGreaterThanEqual,
SpvOpSGreaterThanEqual,
SpvOpULessThan,
SpvOpSLessThan,
SpvOpULessThanEqual,
SpvOpSLessThanEqual,
SpvOpFOrdEqual,
SpvOpFUnordEqual,
SpvOpFOrdNotEqual,
SpvOpFUnordNotEqual,
SpvOpFOrdLessThan,
SpvOpFUnordLessThan,
SpvOpFOrdGreaterThan,
SpvOpFUnordGreaterThan,
SpvOpFOrdLessThanEqual,
SpvOpFUnordLessThanEqual,
SpvOpFOrdGreaterThanEqual,
SpvOpFUnordGreaterThanEqual,
SpvOpShiftRightLogical,
SpvOpShiftRightArithmetic,
SpvOpShiftLeftLogical,
SpvOpBitwiseOr,
SpvOpBitwiseXor,
SpvOpBitwiseAnd,
SpvOpNot,
SpvOpBitFieldInsert,
SpvOpBitFieldSExtract,
SpvOpBitFieldUExtract,
SpvOpBitReverse,
SpvOpBitCount,
SpvOpPhi,
SpvOpImageSparseSampleImplicitLod,
SpvOpImageSparseSampleExplicitLod,
SpvOpImageSparseSampleDrefImplicitLod,
SpvOpImageSparseSampleDrefExplicitLod,
SpvOpImageSparseSampleProjImplicitLod,
SpvOpImageSparseSampleProjExplicitLod,
SpvOpImageSparseSampleProjDrefImplicitLod,
SpvOpImageSparseSampleProjDrefExplicitLod,
SpvOpImageSparseFetch,
SpvOpImageSparseGather,
SpvOpImageSparseDrefGather,
SpvOpImageSparseTexelsResident,
SpvOpImageSparseRead,
SpvOpSizeOf});
}
}
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")) {
combinator_ops_[extension->result_id()] = {GLSLstd450Round,
GLSLstd450RoundEven,
GLSLstd450Trunc,
GLSLstd450FAbs,
GLSLstd450SAbs,
GLSLstd450FSign,
GLSLstd450SSign,
GLSLstd450Floor,
GLSLstd450Ceil,
GLSLstd450Fract,
GLSLstd450Radians,
GLSLstd450Degrees,
GLSLstd450Sin,
GLSLstd450Cos,
GLSLstd450Tan,
GLSLstd450Asin,
GLSLstd450Acos,
GLSLstd450Atan,
GLSLstd450Sinh,
GLSLstd450Cosh,
GLSLstd450Tanh,
GLSLstd450Asinh,
GLSLstd450Acosh,
GLSLstd450Atanh,
GLSLstd450Atan2,
GLSLstd450Pow,
GLSLstd450Exp,
GLSLstd450Log,
GLSLstd450Exp2,
GLSLstd450Log2,
GLSLstd450Sqrt,
GLSLstd450InverseSqrt,
GLSLstd450Determinant,
GLSLstd450MatrixInverse,
GLSLstd450ModfStruct,
GLSLstd450FMin,
GLSLstd450UMin,
GLSLstd450SMin,
GLSLstd450FMax,
GLSLstd450UMax,
GLSLstd450SMax,
GLSLstd450FClamp,
GLSLstd450UClamp,
GLSLstd450SClamp,
GLSLstd450FMix,
GLSLstd450IMix,
GLSLstd450Step,
GLSLstd450SmoothStep,
GLSLstd450Fma,
GLSLstd450FrexpStruct,
GLSLstd450Ldexp,
GLSLstd450PackSnorm4x8,
GLSLstd450PackUnorm4x8,
GLSLstd450PackSnorm2x16,
GLSLstd450PackUnorm2x16,
GLSLstd450PackHalf2x16,
GLSLstd450PackDouble2x32,
GLSLstd450UnpackSnorm2x16,
GLSLstd450UnpackUnorm2x16,
GLSLstd450UnpackHalf2x16,
GLSLstd450UnpackSnorm4x8,
GLSLstd450UnpackUnorm4x8,
GLSLstd450UnpackDouble2x32,
GLSLstd450Length,
GLSLstd450Distance,
GLSLstd450Cross,
GLSLstd450Normalize,
GLSLstd450FaceForward,
GLSLstd450Reflect,
GLSLstd450Refract,
GLSLstd450FindILsb,
GLSLstd450FindSMsb,
GLSLstd450FindUMsb,
GLSLstd450InterpolateAtCentroid,
GLSLstd450InterpolateAtSample,
GLSLstd450InterpolateAtOffset,
GLSLstd450NMin,
GLSLstd450NMax,
GLSLstd450NClamp};
} else {
// Map the result id to the empty set.
combinator_ops_[extension->result_id()];
}
}
void IRContext::InitializeCombinators() {
get_feature_mgr()->GetCapabilities()->ForEach(
[this](SpvCapability cap) { AddCombinatorsForCapability(cap); });
for (auto& extension : module()->ext_inst_imports()) {
AddCombinatorsForExtension(&extension);
}
valid_analyses_ |= kAnalysisCombinators;
}
void IRContext::RemoveFromIdToName(const Instruction* inst) {
if (id_to_name_ &&
(inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) {
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 (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::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;
char* source = nullptr;
if (line_inst != nullptr) {
Instruction* file_name =
get_def_use_mgr()->GetDef(line_inst->GetSingleWordInOperand(0));
source = reinterpret_cast<char*>(&file_name->GetInOperand(0).words[0]);
// 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, {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);
}
} // namespace opt
} // namespace spvtools