mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-30 23:10:05 +00:00
efaae24d00
For many spirv-opt passes such as simplify-instructions pass, we have to correctly clear the OpenCL.DebugInfo.100 debug information for KillInst() and ReplaceAllUses(). If we keep some debug information that disappeared because of KillInst() and ReplaceAllUses(), adding new DebugValue instructions based on the existing DebugDeclare information will generate incorrect information. This CL update DebugInfoManager and IRContext to correctly clear debug information.
1021 lines
37 KiB
C++
1021 lines
37 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 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)) {
|
|
get_def_use_mgr()->ClearInst(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()->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;
|
|
}
|
|
|
|
bool IRContext::KillDef(uint32_t id) {
|
|
Instruction* def = get_def_use_mgr()->GetDef(id);
|
|
if (def != nullptr) {
|
|
KillInst(def);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void IRContext::KillDebugDeclareInsts(Function* fn) {
|
|
fn->ForEachInst([this](Instruction* inst) {
|
|
if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
|
|
KillInst(inst);
|
|
});
|
|
}
|
|
|
|
bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
|
|
return ReplaceAllUsesWithPredicate(
|
|
before, after, [](Instruction*, uint32_t) { return true; });
|
|
}
|
|
|
|
bool IRContext::ReplaceAllUsesWithPredicate(
|
|
uint32_t before, uint32_t after,
|
|
const std::function<bool(Instruction*, uint32_t)>& predicate) {
|
|
if (before == after) return false;
|
|
|
|
// 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, index)) {
|
|
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->GetOpenCL100DebugOpcode() !=
|
|
OpenCLDebugInfo100DebugGlobalVariable)
|
|
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,
|
|
SpvOpTypeRayQueryProvisionalKHR,
|
|
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->opcode() == SpvOpNoLine) {
|
|
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;
|
|
}
|
|
} // namespace opt
|
|
} // namespace spvtools
|