2016-07-27 21:37:04 +00:00
|
|
|
// Copyright (c) 2016 Google Inc.
|
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// 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
|
2016-07-27 21:37:04 +00:00
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2016-07-27 21:37:04 +00:00
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// 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.
|
2016-07-27 21:37:04 +00:00
|
|
|
|
2018-08-03 19:06:09 +00:00
|
|
|
#include "source/opt/def_use_manager.h"
|
2022-02-16 00:17:30 +00:00
|
|
|
#include "source/util/make_unique.h"
|
2016-07-27 21:37:04 +00:00
|
|
|
|
|
|
|
namespace spvtools {
|
|
|
|
namespace opt {
|
|
|
|
namespace analysis {
|
|
|
|
|
2022-02-16 00:17:30 +00:00
|
|
|
// Don't compact before we have a reasonable number of ids allocated (~32kb).
|
|
|
|
static const size_t kCompactThresholdMinTotalIds = (8 * 1024);
|
|
|
|
// Compact when fewer than this fraction of the storage is used (should be 2^n
|
|
|
|
// for performance).
|
|
|
|
static const size_t kCompactThresholdFractionFreeIds = 8;
|
|
|
|
|
|
|
|
DefUseManager::DefUseManager() {
|
|
|
|
use_pool_ = MakeUnique<UseListPool>();
|
|
|
|
used_id_pool_ = MakeUnique<UsedIdListPool>();
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void DefUseManager::AnalyzeInstDef(Instruction* inst) {
|
2016-08-12 14:05:58 +00:00
|
|
|
const uint32_t def_id = inst->result_id();
|
2016-08-16 17:06:03 +00:00
|
|
|
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;
|
2017-11-08 17:40:02 +00:00
|
|
|
} else {
|
2016-08-16 17:06:03 +00:00
|
|
|
ClearInst(inst);
|
|
|
|
}
|
2017-06-16 21:37:31 +00:00
|
|
|
}
|
2016-08-12 14:43:26 +00:00
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void DefUseManager::AnalyzeInstUse(Instruction* inst) {
|
2022-02-16 00:17:30 +00:00
|
|
|
// It might have existed before.
|
|
|
|
EraseUseRecordsOfOperandIds(inst);
|
|
|
|
|
2016-08-16 17:06:03 +00:00
|
|
|
// 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.
|
2022-02-16 00:17:30 +00:00
|
|
|
UsedIdList& used_ids =
|
|
|
|
inst_to_used_id_.insert({inst, UsedIdList(used_id_pool_.get())})
|
|
|
|
.first->second;
|
2016-08-12 14:05:58 +00:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < inst->NumOperands(); ++i) {
|
|
|
|
switch (inst->GetOperand(i).type) {
|
|
|
|
// For any id type but result id type
|
2017-11-08 17:40:02 +00:00
|
|
|
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);
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* def = GetDef(use_id);
|
2021-12-09 14:40:29 +00:00
|
|
|
assert(def && "Definition is not registered.");
|
2022-02-16 00:17:30 +00:00
|
|
|
|
|
|
|
// Add to inst's use records
|
|
|
|
used_ids.push_back(use_id);
|
|
|
|
|
|
|
|
// Add to the users, taking care to avoid adding duplicates. We know
|
|
|
|
// the duplicate for this instruction will always be at the tail.
|
|
|
|
UseList& list = inst_to_users_.insert({def, UseList(use_pool_.get())})
|
|
|
|
.first->second;
|
|
|
|
if (list.empty() || list.back() != inst) {
|
|
|
|
list.push_back(inst);
|
|
|
|
}
|
2017-11-08 17:40:02 +00:00
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
break;
|
2016-08-12 14:05:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void DefUseManager::AnalyzeInstDefUse(Instruction* inst) {
|
2017-06-16 21:37:31 +00:00
|
|
|
AnalyzeInstDef(inst);
|
|
|
|
AnalyzeInstUse(inst);
|
2021-09-24 14:56:08 +00:00
|
|
|
// Analyze lines last otherwise they will be cleared when inst is
|
|
|
|
// cleared by preceding two calls
|
|
|
|
for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst);
|
2017-06-16 21:37:31 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void DefUseManager::UpdateDefUse(Instruction* inst) {
|
2018-02-08 15:59:03 +00:00
|
|
|
const uint32_t def_id = inst->result_id();
|
|
|
|
if (def_id != 0) {
|
|
|
|
auto iter = id_to_def_.find(def_id);
|
2018-02-21 19:35:10 +00:00
|
|
|
if (iter == id_to_def_.end()) {
|
2018-02-08 15:59:03 +00:00
|
|
|
AnalyzeInstDef(inst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AnalyzeInstUse(inst);
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* DefUseManager::GetDef(uint32_t id) {
|
2016-08-16 17:06:03 +00:00
|
|
|
auto iter = id_to_def_.find(id);
|
|
|
|
if (iter == id_to_def_.end()) return nullptr;
|
|
|
|
return iter->second;
|
2016-07-27 21:37:04 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* DefUseManager::GetDef(uint32_t id) const {
|
2017-07-13 00:16:51 +00:00
|
|
|
const auto iter = id_to_def_.find(id);
|
|
|
|
if (iter == id_to_def_.end()) return nullptr;
|
|
|
|
return iter->second;
|
|
|
|
}
|
|
|
|
|
2018-01-12 20:05:53 +00:00
|
|
|
bool DefUseManager::WhileEachUser(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* def, const std::function<bool(Instruction*)>& f) const {
|
2017-11-14 19:11:50 +00:00
|
|
|
// Ensure that |def| has been registered.
|
2018-01-26 12:07:10 +00:00
|
|
|
assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) &&
|
2017-11-27 15:16:41 +00:00
|
|
|
"Definition is not registered.");
|
2018-01-26 12:07:10 +00:00
|
|
|
if (!def->HasResultId()) return true;
|
|
|
|
|
2022-02-16 00:17:30 +00:00
|
|
|
auto iter = inst_to_users_.find(def);
|
|
|
|
if (iter != inst_to_users_.end()) {
|
|
|
|
for (Instruction* user : iter->second) {
|
|
|
|
if (!f(user)) return false;
|
|
|
|
}
|
2017-11-14 19:11:50 +00:00
|
|
|
}
|
2018-01-12 20:05:53 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DefUseManager::WhileEachUser(
|
2018-07-12 19:14:43 +00:00
|
|
|
uint32_t id, const std::function<bool(Instruction*)>& f) const {
|
2018-01-12 20:05:53 +00:00
|
|
|
return WhileEachUser(GetDef(id), f);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefUseManager::ForEachUser(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* def, const std::function<void(Instruction*)>& f) const {
|
|
|
|
WhileEachUser(def, [&f](Instruction* user) {
|
2018-01-12 20:05:53 +00:00
|
|
|
f(user);
|
|
|
|
return true;
|
|
|
|
});
|
2017-11-14 19:11:50 +00:00
|
|
|
}
|
|
|
|
|
2017-11-27 15:16:41 +00:00
|
|
|
void DefUseManager::ForEachUser(
|
2018-07-12 19:14:43 +00:00
|
|
|
uint32_t id, const std::function<void(Instruction*)>& f) const {
|
2017-11-14 19:11:50 +00:00
|
|
|
ForEachUser(GetDef(id), f);
|
|
|
|
}
|
|
|
|
|
2018-01-12 20:05:53 +00:00
|
|
|
bool DefUseManager::WhileEachUse(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* def,
|
|
|
|
const std::function<bool(Instruction*, uint32_t)>& f) const {
|
2017-11-14 19:11:50 +00:00
|
|
|
// Ensure that |def| has been registered.
|
2018-01-26 12:07:10 +00:00
|
|
|
assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) &&
|
2017-11-27 15:16:41 +00:00
|
|
|
"Definition is not registered.");
|
2018-01-26 12:07:10 +00:00
|
|
|
if (!def->HasResultId()) return true;
|
|
|
|
|
2022-02-16 00:17:30 +00:00
|
|
|
auto iter = inst_to_users_.find(def);
|
|
|
|
if (iter != inst_to_users_.end()) {
|
|
|
|
for (Instruction* user : iter->second) {
|
|
|
|
for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) {
|
|
|
|
const Operand& op = user->GetOperand(idx);
|
|
|
|
if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) {
|
|
|
|
if (def->result_id() == op.words[0]) {
|
|
|
|
if (!f(user, idx)) return false;
|
|
|
|
}
|
2018-01-12 20:05:53 +00:00
|
|
|
}
|
2017-11-14 19:11:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-12 20:05:53 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DefUseManager::WhileEachUse(
|
2018-07-12 19:14:43 +00:00
|
|
|
uint32_t id, const std::function<bool(Instruction*, uint32_t)>& f) const {
|
2018-01-12 20:05:53 +00:00
|
|
|
return WhileEachUse(GetDef(id), f);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefUseManager::ForEachUse(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* def,
|
|
|
|
const std::function<void(Instruction*, uint32_t)>& f) const {
|
|
|
|
WhileEachUse(def, [&f](Instruction* user, uint32_t index) {
|
2018-01-12 20:05:53 +00:00
|
|
|
f(user, index);
|
|
|
|
return true;
|
|
|
|
});
|
2017-11-14 19:11:50 +00:00
|
|
|
}
|
|
|
|
|
2017-11-27 15:16:41 +00:00
|
|
|
void DefUseManager::ForEachUse(
|
2018-07-12 19:14:43 +00:00
|
|
|
uint32_t id, const std::function<void(Instruction*, uint32_t)>& f) const {
|
2017-11-14 19:11:50 +00:00
|
|
|
ForEachUse(GetDef(id), f);
|
2016-08-29 22:05:24 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
uint32_t DefUseManager::NumUsers(const Instruction* def) const {
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t count = 0;
|
2018-07-12 19:14:43 +00:00
|
|
|
ForEachUser(def, [&count](Instruction*) { ++count; });
|
2017-11-30 22:03:06 +00:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t DefUseManager::NumUsers(uint32_t id) const {
|
|
|
|
return NumUsers(GetDef(id));
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
uint32_t DefUseManager::NumUses(const Instruction* def) const {
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t count = 0;
|
2018-07-12 19:14:43 +00:00
|
|
|
ForEachUse(def, [&count](Instruction*, uint32_t) { ++count; });
|
2017-11-30 22:03:06 +00:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t DefUseManager::NumUses(uint32_t id) const {
|
|
|
|
return NumUses(GetDef(id));
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
std::vector<Instruction*> DefUseManager::GetAnnotations(uint32_t id) const {
|
|
|
|
std::vector<Instruction*> annos;
|
|
|
|
const Instruction* def = GetDef(id);
|
2017-11-14 19:11:50 +00:00
|
|
|
if (!def) return annos;
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
ForEachUser(def, [&annos](Instruction* user) {
|
|
|
|
if (IsAnnotationInst(user->opcode())) {
|
2017-11-14 19:11:50 +00:00
|
|
|
annos.push_back(user);
|
2016-08-29 22:05:24 +00:00
|
|
|
}
|
2017-11-14 19:11:50 +00:00
|
|
|
});
|
2016-08-29 22:05:24 +00:00
|
|
|
return annos;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void DefUseManager::AnalyzeDefUse(Module* module) {
|
2016-08-16 17:06:03 +00:00
|
|
|
if (!module) return;
|
2017-11-14 19:11:50 +00:00
|
|
|
// Analyze all the defs before any uses to catch forward references.
|
2017-11-27 15:16:41 +00:00
|
|
|
module->ForEachInst(
|
2021-09-24 14:56:08 +00:00
|
|
|
std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1),
|
|
|
|
true);
|
2017-11-27 15:16:41 +00:00
|
|
|
module->ForEachInst(
|
2021-09-24 14:56:08 +00:00
|
|
|
std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1),
|
|
|
|
true);
|
2016-08-15 17:41:47 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void DefUseManager::ClearInst(Instruction* inst) {
|
2022-02-16 00:17:30 +00:00
|
|
|
if (inst_to_used_id_.find(inst) != inst_to_used_id_.end()) {
|
2016-08-16 17:06:03 +00:00
|
|
|
EraseUseRecordsOfOperandIds(inst);
|
2022-02-16 00:17:30 +00:00
|
|
|
uint32_t const result_id = inst->result_id();
|
|
|
|
if (result_id != 0) {
|
|
|
|
// For each using instruction, remove result_id from their used ids.
|
|
|
|
auto iter = inst_to_users_.find(inst);
|
|
|
|
if (iter != inst_to_users_.end()) {
|
|
|
|
for (Instruction* use : iter->second) {
|
|
|
|
inst_to_used_id_.at(use).remove_first(result_id);
|
|
|
|
}
|
|
|
|
inst_to_users_.erase(iter);
|
2017-11-27 15:16:41 +00:00
|
|
|
}
|
2016-08-16 17:06:03 +00:00
|
|
|
id_to_def_.erase(inst->result_id());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-12 14:43:26 +00:00
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) {
|
2016-08-12 14:43:26 +00:00
|
|
|
// Go through all ids used by this instruction, remove this instruction's
|
|
|
|
// uses of them.
|
2022-02-16 00:17:30 +00:00
|
|
|
auto iter = inst_to_used_id_.find(inst);
|
|
|
|
if (iter != inst_to_used_id_.end()) {
|
|
|
|
const UsedIdList& used_ids = iter->second;
|
|
|
|
for (uint32_t def_id : used_ids) {
|
|
|
|
auto def_iter = inst_to_users_.find(GetDef(def_id));
|
|
|
|
if (def_iter != inst_to_users_.end()) {
|
|
|
|
def_iter->second.remove_first(const_cast<Instruction*>(inst));
|
|
|
|
}
|
2016-08-12 14:43:26 +00:00
|
|
|
}
|
2022-02-16 00:17:30 +00:00
|
|
|
inst_to_used_id_.erase(inst);
|
|
|
|
|
|
|
|
// If we're using only a fraction of the space in used_ids_, compact storage
|
|
|
|
// to prevent memory usage from being unbounded.
|
|
|
|
if (used_id_pool_->total_nodes() > kCompactThresholdMinTotalIds &&
|
|
|
|
used_id_pool_->used_nodes() <
|
|
|
|
used_id_pool_->total_nodes() / kCompactThresholdFractionFreeIds) {
|
|
|
|
CompactStorage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefUseManager::CompactStorage() {
|
|
|
|
CompactUseRecords();
|
|
|
|
CompactUsedIds();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefUseManager::CompactUseRecords() {
|
|
|
|
std::unique_ptr<UseListPool> new_pool = MakeUnique<UseListPool>();
|
|
|
|
for (auto& iter : inst_to_users_) {
|
|
|
|
iter.second.move_nodes(new_pool.get());
|
|
|
|
}
|
|
|
|
use_pool_ = std::move(new_pool);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefUseManager::CompactUsedIds() {
|
|
|
|
std::unique_ptr<UsedIdListPool> new_pool = MakeUnique<UsedIdListPool>();
|
|
|
|
for (auto& iter : inst_to_used_id_) {
|
|
|
|
iter.second.move_nodes(new_pool.get());
|
2016-08-12 14:43:26 +00:00
|
|
|
}
|
2022-02-16 00:17:30 +00:00
|
|
|
used_id_pool_ = std::move(new_pool);
|
2016-08-12 14:43:26 +00:00
|
|
|
}
|
|
|
|
|
2021-12-09 14:41:42 +00:00
|
|
|
bool CompareAndPrintDifferences(const DefUseManager& lhs,
|
|
|
|
const DefUseManager& rhs) {
|
|
|
|
bool same = true;
|
|
|
|
|
2017-11-09 16:24:41 +00:00
|
|
|
if (lhs.id_to_def_ != rhs.id_to_def_) {
|
2021-09-24 14:56:08 +00:00
|
|
|
for (auto p : lhs.id_to_def_) {
|
|
|
|
if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) {
|
2021-12-09 14:41:42 +00:00
|
|
|
printf("Diff in id_to_def: missing value in rhs\n");
|
2021-09-24 14:56:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto p : rhs.id_to_def_) {
|
|
|
|
if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) {
|
2021-12-09 14:41:42 +00:00
|
|
|
printf("Diff in id_to_def: missing value in lhs\n");
|
2021-09-24 14:56:08 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-09 14:41:42 +00:00
|
|
|
same = false;
|
2017-11-09 16:24:41 +00:00
|
|
|
}
|
|
|
|
|
2022-02-16 00:17:30 +00:00
|
|
|
for (const auto& l : lhs.inst_to_used_id_) {
|
|
|
|
std::set<uint32_t> ul, ur;
|
|
|
|
lhs.ForEachUse(l.first,
|
|
|
|
[&ul](Instruction*, uint32_t id) { ul.insert(id); });
|
|
|
|
rhs.ForEachUse(l.first,
|
|
|
|
[&ur](Instruction*, uint32_t id) { ur.insert(id); });
|
|
|
|
if (ul.size() != ur.size()) {
|
|
|
|
printf(
|
|
|
|
"Diff in inst_to_used_id_: different number of used ids (%zu != %zu)",
|
|
|
|
ul.size(), ur.size());
|
|
|
|
same = false;
|
|
|
|
} else if (ul != ur) {
|
|
|
|
printf("Diff in inst_to_used_id_: different used ids\n");
|
|
|
|
same = false;
|
2018-03-06 16:20:28 +00:00
|
|
|
}
|
2022-02-16 00:17:30 +00:00
|
|
|
}
|
|
|
|
for (const auto& r : rhs.inst_to_used_id_) {
|
|
|
|
auto iter_l = lhs.inst_to_used_id_.find(r.first);
|
|
|
|
if (r.second.empty() &&
|
|
|
|
!(iter_l == lhs.inst_to_used_id_.end() || iter_l->second.empty())) {
|
|
|
|
printf("Diff in inst_to_used_id_: unexpected instr in rhs\n");
|
|
|
|
same = false;
|
2018-03-06 16:20:28 +00:00
|
|
|
}
|
2017-11-09 16:24:41 +00:00
|
|
|
}
|
|
|
|
|
2022-02-16 00:17:30 +00:00
|
|
|
for (const auto& l : lhs.inst_to_users_) {
|
|
|
|
std::set<Instruction*> ul, ur;
|
|
|
|
lhs.ForEachUser(l.first, [&ul](Instruction* use) { ul.insert(use); });
|
|
|
|
rhs.ForEachUser(l.first, [&ur](Instruction* use) { ur.insert(use); });
|
|
|
|
if (ul.size() != ur.size()) {
|
|
|
|
printf("Diff in inst_to_users_: different number of users (%zu != %zu)",
|
|
|
|
ul.size(), ur.size());
|
|
|
|
same = false;
|
|
|
|
} else if (ul != ur) {
|
|
|
|
printf("Diff in inst_to_users_: different users\n");
|
|
|
|
same = false;
|
2018-11-08 18:54:54 +00:00
|
|
|
}
|
2022-02-16 00:17:30 +00:00
|
|
|
}
|
|
|
|
for (const auto& r : rhs.inst_to_users_) {
|
|
|
|
auto iter_l = lhs.inst_to_users_.find(r.first);
|
|
|
|
if (r.second.empty() &&
|
|
|
|
!(iter_l == lhs.inst_to_users_.end() || iter_l->second.empty())) {
|
|
|
|
printf("Diff in inst_to_users_: unexpected instr in rhs\n");
|
|
|
|
same = false;
|
2018-11-08 18:54:54 +00:00
|
|
|
}
|
2017-11-09 16:24:41 +00:00
|
|
|
}
|
2021-12-09 14:41:42 +00:00
|
|
|
return same;
|
2017-11-09 16:24:41 +00:00
|
|
|
}
|
|
|
|
|
2016-07-27 21:37:04 +00:00
|
|
|
} // namespace analysis
|
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|