// 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 #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; static const int kEntryPointExecutionModelInIdx = 0; // Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 // extension instructions. static const uint32_t kDebugFunctionOperandFunctionIndex = 13; static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; } // anonymous namespace namespace spvtools { namespace opt { void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) { set = Analysis(set & ~valid_analyses_); if (set & kAnalysisDefUse) { BuildDefUseManager(); } 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(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; } void IRContext::CollectNonSemanticTree( Instruction* inst, std::unordered_set* to_kill) { if (!inst->HasResultId()) return; // Debug[No]Line result id is not used, so we are done if (inst->IsDebugLineInst()) return; std::vector work_list; std::unordered_set 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& 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> 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 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::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() { get_feature_mgr()->GetCapabilities()->ForEach([this](spv::Capability cap) { AddCombinatorsForCapability(uint32_t(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() == 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::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::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 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* 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 roots; for (auto& e : module()->entry_points()) { roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); } return ProcessCallTreeFromRoots(pfn, &roots); } bool IRContext::ProcessReachableCallTree(ProcessFunction& pfn) { std::queue 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* roots) { // Process call tree bool modified = false; std::unordered_set 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* funcs) { std::queue 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> 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 preds = cfg()->preds(bb.id()); std::vector 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(stage); } } // namespace opt } // namespace spvtools