mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-30 15:00:06 +00:00
114af21994
Also convert some uses of assert() in optimization code to use SPIRV_ASSERT(), SPIRV_UNIMPLEMENTED(), or SPIRV_UNREACHABLE() accordingly.
187 lines
5.8 KiB
C++
187 lines
5.8 KiB
C++
// 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 <functional>
|
|
|
|
#include "instruction.h"
|
|
#include "log.h"
|
|
#include "module.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<ir::Instruction*> DefUseManager::GetAnnotations(uint32_t id) const {
|
|
std::vector<ir::Instruction*> 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
|