// Copyright (c) 2016 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 "def_use_manager.h" #include "log.h" #include "reflect.h" namespace spvtools { namespace opt { namespace analysis { void DefUseManager::AnalyzeInstDefUse(ir::Instruction* inst) { const uint32_t def_id = inst->result_id(); if (def_id != 0) { auto iter = id_to_def_.find(def_id); if (iter != id_to_def_.end()) { // Clear the original instruction that defining the same result id of the // new instruction. ClearInst(iter->second); } id_to_def_[def_id] = inst; } else { ClearInst(inst); } // Create entry for the given instruction. Note that the instruction may // not have any in-operands. In such cases, we still need a entry for those // instructions so this manager knows it has seen the instruction later. inst_to_used_ids_[inst] = {}; for (uint32_t i = 0; i < inst->NumOperands(); ++i) { switch (inst->GetOperand(i).type) { // For any id type but result id type case SPV_OPERAND_TYPE_ID: case SPV_OPERAND_TYPE_TYPE_ID: case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: case SPV_OPERAND_TYPE_SCOPE_ID: { uint32_t use_id = inst->GetSingleWordOperand(i); // use_id is used by the instruction generating def_id. id_to_uses_[use_id].push_back({inst, i}); inst_to_used_ids_[inst].push_back(use_id); } break; default: break; } } } ir::Instruction* DefUseManager::GetDef(uint32_t id) { auto iter = id_to_def_.find(id); if (iter == id_to_def_.end()) return nullptr; return iter->second; } UseList* DefUseManager::GetUses(uint32_t id) { auto iter = id_to_uses_.find(id); if (iter == id_to_uses_.end()) return nullptr; return &iter->second; } const UseList* DefUseManager::GetUses(uint32_t id) const { const auto iter = id_to_uses_.find(id); if (iter == id_to_uses_.end()) return nullptr; return &iter->second; } std::vector DefUseManager::GetAnnotations(uint32_t id) const { std::vector annos; const auto* uses = GetUses(id); if (!uses) return annos; for (const auto& c : *uses) { if (ir::IsAnnotationInst(c.inst->opcode())) { annos.push_back(c.inst); } } return annos; } bool DefUseManager::KillDef(uint32_t id) { auto iter = id_to_def_.find(id); if (iter == id_to_def_.end()) return false; KillInst(iter->second); return true; } void DefUseManager::KillInst(ir::Instruction* inst) { if (!inst) return; ClearInst(inst); inst->ToNop(); } bool DefUseManager::ReplaceAllUsesWith(uint32_t before, uint32_t after) { if (before == after) return false; if (id_to_uses_.count(before) == 0) return false; for (auto it = id_to_uses_[before].cbegin(); it != id_to_uses_[before].cend(); ++it) { const uint32_t type_result_id_count = (it->inst->result_id() != 0) + (it->inst->type_id() != 0); if (it->operand_index < type_result_id_count) { // Update the type_id. Note that result id is immutable so it should // never be updated. if (it->inst->type_id() != 0 && it->operand_index == 0) { it->inst->SetResultType(after); } else if (it->inst->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 = it->operand_index - type_result_id_count; // Make the modification in the instruction. it->inst->SetInOperand(in_operand_pos, {after}); } // Register the use of |after| id into id_to_uses_. // TODO(antiagainst): de-duplication. id_to_uses_[after].push_back({it->inst, it->operand_index}); } id_to_uses_.erase(before); return true; } void DefUseManager::AnalyzeDefUse(ir::Module* module) { if (!module) return; module->ForEachInst(std::bind(&DefUseManager::AnalyzeInstDefUse, this, std::placeholders::_1)); } void DefUseManager::ClearInst(ir::Instruction* inst) { auto iter = inst_to_used_ids_.find(inst); if (iter != inst_to_used_ids_.end()) { EraseUseRecordsOfOperandIds(inst); if (inst->result_id() != 0) { id_to_uses_.erase(inst->result_id()); // Remove all uses of this id. id_to_def_.erase(inst->result_id()); } } } void DefUseManager::EraseUseRecordsOfOperandIds(const ir::Instruction* inst) { // Go through all ids used by this instruction, remove this instruction's // uses of them. auto iter = inst_to_used_ids_.find(inst); if (iter != inst_to_used_ids_.end()) { for (const auto use_id : iter->second) { auto uses_iter = id_to_uses_.find(use_id); if (uses_iter == id_to_uses_.end()) continue; auto& uses = uses_iter->second; for (auto it = uses.begin(); it != uses.end();) { if (it->inst == inst) { it = uses.erase(it); } else { ++it; } } if (uses.empty()) id_to_uses_.erase(use_id); } inst_to_used_ids_.erase(inst); } } } // namespace analysis } // namespace opt } // namespace spvtools