mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-30 15:00:06 +00:00
860b2ee5fc
The combinator initialization was only looking at the capabilities in the shader and not the inferred capabilities. Geometry and tessellation shaders were not setting the Shader capability which is inferred. So the combinator set was not initialized correctly causing problems for ADCE.
532 lines
18 KiB
C++
532 lines
18 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 "ir_context.h"
|
|
#include "latest_version_glsl_std_450_header.h"
|
|
#include "log.h"
|
|
#include "mem_pass.h"
|
|
#include "reflect.h"
|
|
|
|
#include <cstring>
|
|
|
|
namespace spvtools {
|
|
namespace ir {
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
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) {
|
|
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 & kAnalysisCFG) {
|
|
cfg_.reset(nullptr);
|
|
}
|
|
if (analyses_to_invalidate & kAnalysisDominatorAnalysis) {
|
|
dominator_trees_.clear();
|
|
post_dominator_trees_.clear();
|
|
}
|
|
|
|
valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
|
|
}
|
|
|
|
Instruction* IRContext::KillInst(ir::Instruction* inst) {
|
|
if (!inst) {
|
|
return nullptr;
|
|
}
|
|
|
|
KillNamesAndDecorates(inst);
|
|
|
|
if (AreAnalysesValid(kAnalysisDefUse)) {
|
|
get_def_use_mgr()->ClearInst(inst);
|
|
}
|
|
if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
|
|
instr_to_block_.erase(inst);
|
|
}
|
|
if (AreAnalysesValid(kAnalysisDecorations)) {
|
|
if (inst->result_id() != 0) {
|
|
decoration_mgr_->RemoveDecorationsFrom(inst->result_id());
|
|
}
|
|
if (inst->IsDecoration()) {
|
|
decoration_mgr_->RemoveDecoration(inst);
|
|
}
|
|
}
|
|
|
|
if (type_mgr_ && ir::IsTypeInst(inst->opcode())) {
|
|
type_mgr_->RemoveId(inst->result_id());
|
|
}
|
|
|
|
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) {
|
|
ir::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) {
|
|
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<ir::Instruction*, uint32_t>> uses_to_update;
|
|
get_def_use_mgr()->ForEachUse(
|
|
before, [&uses_to_update](ir::Instruction* user, uint32_t index) {
|
|
uses_to_update.emplace_back(user, index);
|
|
});
|
|
|
|
ir::Instruction* prev = nullptr;
|
|
for (auto p : uses_to_update) {
|
|
ir::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;
|
|
#endif
|
|
|
|
if (AreAnalysesValid(kAnalysisDefUse)) {
|
|
opt::analysis::DefUseManager new_def_use(module());
|
|
if (*get_def_use_mgr() != new_def_use) {
|
|
return false;
|
|
}
|
|
}
|
|
if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
|
|
for (auto& func : *module()) {
|
|
for (auto& block : func) {
|
|
if (!block.WhileEachInst([this, &block](ir::Instruction* inst) {
|
|
if (get_instr_block(inst) != &block) return false;
|
|
return true;
|
|
}))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void spvtools::ir::IRContext::ForgetUses(Instruction* inst) {
|
|
if (AreAnalysesValid(kAnalysisDefUse)) {
|
|
get_def_use_mgr()->EraseUseRecordsOfOperandIds(inst);
|
|
}
|
|
if (AreAnalysesValid(kAnalysisDecorations)) {
|
|
if (inst->IsDecoration()) {
|
|
get_decoration_mgr()->RemoveDecoration(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void IRContext::KillNamesAndDecorates(uint32_t id) {
|
|
std::vector<ir::Instruction*> decorations =
|
|
get_decoration_mgr()->GetDecorationsFor(id, true);
|
|
|
|
for (Instruction* inst : decorations) {
|
|
KillInst(inst);
|
|
}
|
|
|
|
Instruction* debug_inst = &*debug2_begin();
|
|
while (debug_inst) {
|
|
bool killed_inst = false;
|
|
if (debug_inst->opcode() == SpvOpMemberName ||
|
|
debug_inst->opcode() == SpvOpName) {
|
|
if (debug_inst->GetSingleWordInOperand(0) == id) {
|
|
debug_inst = KillInst(debug_inst);
|
|
killed_inst = true;
|
|
}
|
|
}
|
|
if (!killed_inst) {
|
|
debug_inst = debug_inst->NextNode();
|
|
}
|
|
}
|
|
}
|
|
|
|
void IRContext::KillNamesAndDecorates(Instruction* inst) {
|
|
const uint32_t rId = inst->result_id();
|
|
if (rId == 0) return;
|
|
KillNamesAndDecorates(rId);
|
|
}
|
|
|
|
void IRContext::AddCombinatorsForCapability(uint32_t capability) {
|
|
if (capability == SpvCapabilityShader) {
|
|
combinator_ops_[0].insert({
|
|
SpvOpNop,
|
|
SpvOpUndef,
|
|
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,
|
|
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
|
|
// TODO(dneto): Add instructions enabled by ImageQuery
|
|
});
|
|
}
|
|
}
|
|
|
|
void IRContext::AddCombinatorsForExtension(ir::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;
|
|
}
|
|
|
|
ir::LoopDescriptor* IRContext::GetLoopDescriptor(const ir::Function* f) {
|
|
if (!AreAnalysesValid(kAnalysisLoopAnalysis)) {
|
|
ResetLoopAnalysis();
|
|
}
|
|
|
|
std::unordered_map<const ir::Function*, ir::LoopDescriptor>::iterator it =
|
|
loop_descriptors_.find(f);
|
|
if (it == loop_descriptors_.end()) {
|
|
return &loop_descriptors_.emplace(std::make_pair(f, ir::LoopDescriptor(f)))
|
|
.first->second;
|
|
}
|
|
|
|
return &it->second;
|
|
}
|
|
|
|
// Gets the dominator analysis for function |f|.
|
|
opt::DominatorAnalysis* IRContext::GetDominatorAnalysis(const ir::Function* f,
|
|
const ir::CFG& in_cfg) {
|
|
if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) {
|
|
ResetDominatorAnalysis();
|
|
}
|
|
|
|
if (dominator_trees_.find(f) == dominator_trees_.end()) {
|
|
dominator_trees_[f].InitializeTree(f, in_cfg);
|
|
}
|
|
|
|
return &dominator_trees_[f];
|
|
}
|
|
|
|
// Gets the postdominator analysis for function |f|.
|
|
opt::PostDominatorAnalysis* IRContext::GetPostDominatorAnalysis(
|
|
const ir::Function* f, const ir::CFG& in_cfg) {
|
|
if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) {
|
|
ResetDominatorAnalysis();
|
|
}
|
|
|
|
if (post_dominator_trees_.find(f) == post_dominator_trees_.end()) {
|
|
post_dominator_trees_[f].InitializeTree(f, in_cfg);
|
|
}
|
|
|
|
return &post_dominator_trees_[f];
|
|
}
|
|
|
|
} // namespace ir
|
|
} // namespace spvtools
|