// Copyright (c) 2017 The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG 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 "pass.h" #include "iterator.h" namespace spvtools { namespace opt { namespace { const uint32_t kEntryPointFunctionIdInIdx = 1; const uint32_t kTypePointerTypeIdInIdx = 1; } // namespace Pass::Pass() : consumer_(nullptr), next_id_(0), context_(nullptr) {} void Pass::AddCalls(ir::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() == SpvOpFunctionCall) todo->push(ii->GetSingleWordInOperand(0)); } bool Pass::ProcessEntryPointCallTree(ProcessFunction& pfn, ir::Module* module) { // Map from function's result id to function std::unordered_map id2function; for (auto& fn : *module) id2function[fn.result_id()] = &fn; // 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, id2function, &roots); } bool Pass::ProcessReachableCallTree(ProcessFunction& pfn, ir::IRContext* irContext) { // Map from function's result id to function std::unordered_map id2function; for (auto& fn : *irContext->module()) id2function[fn.result_id()] = &fn; std::queue roots; // Add all entry points since they can be reached from outside the module. for (auto& e : irContext->module()->entry_points()) roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); // Add all exported functions since they can be reached from outside the // module. for (auto& a : irContext->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 (id2function.count(id) != 0) roots.push(id); } } } } return ProcessCallTreeFromRoots(pfn, id2function, &roots); } bool Pass::ProcessCallTreeFromRoots( ProcessFunction& pfn, const std::unordered_map& id2function, 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) { ir::Function* fn = id2function.at(fi); modified = pfn(fn) || modified; AddCalls(fn, roots); } } return modified; } Pass::Status Pass::Run(ir::IRContext* ctx) { Pass::Status status = Process(ctx); if (status == Status::SuccessWithChange) { ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses()); } assert(ctx->IsConsistent()); return status; } uint32_t Pass::GetPointeeTypeId(const ir::Instruction* ptrInst) const { const uint32_t ptrTypeId = ptrInst->type_id(); const ir::Instruction* ptrTypeInst = get_def_use_mgr()->GetDef(ptrTypeId); return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx); } } // namespace opt } // namespace spvtools