SPIRV-Tools/source/opt/basic_block.cpp

287 lines
8.2 KiB
C++
Raw Normal View History

// 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 "source/opt/basic_block.h"
#include <ostream>
#include "source/opt/function.h"
#include "source/opt/ir_context.h"
#include "source/opt/module.h"
#include "source/opt/reflect.h"
#include "source/util/make_unique.h"
namespace spvtools {
namespace opt {
namespace {
constexpr uint32_t kLoopMergeContinueBlockIdInIdx = 1;
constexpr uint32_t kLoopMergeMergeBlockIdInIdx = 0;
constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
} // namespace
Adding an unique id to Instruction generated by IRContext Each instruction is given an unique id that can be used for ordering purposes. The ids are generated via the IRContext. Major changes: * Instructions now contain a uint32_t for unique id and a cached context pointer * Most constructors have been modified to take a context as input * unfortunately I cannot remove the default and copy constructors, but developers should avoid these * Added accessors to parents of basic block and function * Removed the copy constructors for BasicBlock and Function and replaced them with Clone functions * Reworked BuildModule to return an IRContext owning the built module * Since all instructions require a context, the context now becomes the basic unit for IR * Added a constructor to context to create an owned module internally * Replaced uses of Instruction's copy constructor with Clone whereever I found them * Reworked the linker functionality to perform clones into a different context instead of moves * Updated many tests to be consistent with the above changes * Still need to add new tests to cover added functionality * Added comparison operators to Instruction * Added an internal option to LinkerOptions to verify merged ids are unique * Added a test for the linker to verify merged ids are unique * Updated MergeReturnPass to supply a context * Updated DecorationManager to supply a context for cloned decorations * Reworked several portions of the def use tests in anticipation of next set of changes
2017-11-14 19:11:50 +00:00
BasicBlock* BasicBlock::Clone(IRContext* context) const {
BasicBlock* clone = new BasicBlock(
Linker code cleanups Turn `Linker::Link()` into free functions As very little information was kept in the Linker class, we can get rid of the whole class and have the `Link()` as free functions instead; the environment target as well as the consumer are passed along through an `spv_context` object. The resulting linked_binary is passed as a pointer rather than a reference to follow the Google C++ Style guidelines. Addresses remaining comments from https://github.com/KhronosGroup/SPIRV-Tools/pull/693 about the SPIR-V linker. Fix variable naming in the linker Some of the variables were using mixed case, which did not follow the Google C++ Style guidelines. Linker: Use EXPECT_EQ when possible and update some test * Replace occurrences of ASSERT_EQ by EXPECT_EQ when possible; * Reformulated some of the error messages; * Added the symbol name in the error message when there is a type or decoration mismatch between the imported and exported declarations. Opt: List all duplicates removed by RemoveDuplicatePass in the header Opt: Make the const version of GetLabelInst() return a pointer For consistency with the non-const version, as well as other similar functions. Opt: Rename function_end to EndInst() As pointed out by dneto0 the previous name was quite confusing and could be mistaken with a function returning an end iterator. Also change the return type of the const version to a pointer rather than a reference, for consistency. Opt: Add performance comment to RemoveDuplicateTypes and decorations This comment was requested during the review of https://github.com/KhronosGroup/SPIRV-Tools/pull/693. Opt: Add comments and fix variable naming in RemoveDuplicatePass * Add missing comments to private functions; * Rename variables that were using mixed case; * Add TODO for moving AreTypesEqual out. Linker: Remove commented out code and add TODOs Linker: Merged together strings that were too much splitted Implement a C++ RAII wrapper around spv_context
2018-01-03 00:54:55 +00:00
std::unique_ptr<Instruction>(GetLabelInst()->Clone(context)));
for (const auto& inst : insts_) {
Adding an unique id to Instruction generated by IRContext Each instruction is given an unique id that can be used for ordering purposes. The ids are generated via the IRContext. Major changes: * Instructions now contain a uint32_t for unique id and a cached context pointer * Most constructors have been modified to take a context as input * unfortunately I cannot remove the default and copy constructors, but developers should avoid these * Added accessors to parents of basic block and function * Removed the copy constructors for BasicBlock and Function and replaced them with Clone functions * Reworked BuildModule to return an IRContext owning the built module * Since all instructions require a context, the context now becomes the basic unit for IR * Added a constructor to context to create an owned module internally * Replaced uses of Instruction's copy constructor with Clone whereever I found them * Reworked the linker functionality to perform clones into a different context instead of moves * Updated many tests to be consistent with the above changes * Still need to add new tests to cover added functionality * Added comparison operators to Instruction * Added an internal option to LinkerOptions to verify merged ids are unique * Added a test for the linker to verify merged ids are unique * Updated MergeReturnPass to supply a context * Updated DecorationManager to supply a context for cloned decorations * Reworked several portions of the def use tests in anticipation of next set of changes
2017-11-14 19:11:50 +00:00
// Use the incoming context
clone->AddInstruction(std::unique_ptr<Instruction>(inst.Clone(context)));
}
if (context->AreAnalysesValid(
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
for (auto& inst : *clone) {
context->set_instr_block(&inst, clone);
}
}
Adding an unique id to Instruction generated by IRContext Each instruction is given an unique id that can be used for ordering purposes. The ids are generated via the IRContext. Major changes: * Instructions now contain a uint32_t for unique id and a cached context pointer * Most constructors have been modified to take a context as input * unfortunately I cannot remove the default and copy constructors, but developers should avoid these * Added accessors to parents of basic block and function * Removed the copy constructors for BasicBlock and Function and replaced them with Clone functions * Reworked BuildModule to return an IRContext owning the built module * Since all instructions require a context, the context now becomes the basic unit for IR * Added a constructor to context to create an owned module internally * Replaced uses of Instruction's copy constructor with Clone whereever I found them * Reworked the linker functionality to perform clones into a different context instead of moves * Updated many tests to be consistent with the above changes * Still need to add new tests to cover added functionality * Added comparison operators to Instruction * Added an internal option to LinkerOptions to verify merged ids are unique * Added a test for the linker to verify merged ids are unique * Updated MergeReturnPass to supply a context * Updated DecorationManager to supply a context for cloned decorations * Reworked several portions of the def use tests in anticipation of next set of changes
2017-11-14 19:11:50 +00:00
return clone;
}
const Instruction* BasicBlock::GetMergeInst() const {
const Instruction* result = nullptr;
// If it exists, the merge instruction immediately precedes the
// terminator.
auto iter = ctail();
if (iter != cbegin()) {
--iter;
const auto opcode = iter->opcode();
if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
result = &*iter;
}
}
return result;
}
Instruction* BasicBlock::GetMergeInst() {
Instruction* result = nullptr;
// If it exists, the merge instruction immediately precedes the
// terminator.
auto iter = tail();
if (iter != begin()) {
--iter;
const auto opcode = iter->opcode();
if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) {
result = &*iter;
}
}
return result;
}
const Instruction* BasicBlock::GetLoopMergeInst() const {
if (auto* merge = GetMergeInst()) {
if (merge->opcode() == spv::Op::OpLoopMerge) {
return merge;
}
}
return nullptr;
}
Instruction* BasicBlock::GetLoopMergeInst() {
if (auto* merge = GetMergeInst()) {
if (merge->opcode() == spv::Op::OpLoopMerge) {
return merge;
}
}
return nullptr;
}
void BasicBlock::KillAllInsts(bool killLabel) {
ForEachInst([killLabel](Instruction* ip) {
if (killLabel || ip->opcode() != spv::Op::OpLabel) {
ip->context()->KillInst(ip);
}
});
}
void BasicBlock::ForEachSuccessorLabel(
const std::function<void(const uint32_t)>& f) const {
WhileEachSuccessorLabel([f](const uint32_t l) {
f(l);
return true;
});
}
bool BasicBlock::WhileEachSuccessorLabel(
const std::function<bool(const uint32_t)>& f) const {
const auto br = &insts_.back();
switch (br->opcode()) {
case spv::Op::OpBranch:
return f(br->GetOperand(0).words[0]);
case spv::Op::OpBranchConditional:
case spv::Op::OpSwitch: {
bool is_first = true;
return br->WhileEachInId([&is_first, &f](const uint32_t* idp) {
if (!is_first) return f(*idp);
is_first = false;
return true;
});
}
default:
return true;
}
}
void BasicBlock::ForEachSuccessorLabel(
const std::function<void(uint32_t*)>& f) {
auto br = &insts_.back();
switch (br->opcode()) {
case spv::Op::OpBranch: {
uint32_t tmp_id = br->GetOperand(0).words[0];
f(&tmp_id);
if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id});
} break;
case spv::Op::OpBranchConditional:
case spv::Op::OpSwitch: {
bool is_first = true;
br->ForEachInId([&is_first, &f](uint32_t* idp) {
if (!is_first) f(idp);
is_first = false;
});
} break;
default:
break;
}
}
bool BasicBlock::IsSuccessor(const BasicBlock* block) const {
uint32_t succId = block->id();
bool isSuccessor = false;
ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) {
if (label == succId) isSuccessor = true;
});
return isSuccessor;
}
void BasicBlock::ForMergeAndContinueLabel(
const std::function<void(const uint32_t)>& f) {
auto ii = insts_.end();
--ii;
if (ii == insts_.begin()) return;
--ii;
if (ii->opcode() == spv::Op::OpSelectionMerge ||
ii->opcode() == spv::Op::OpLoopMerge) {
ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); });
}
}
uint32_t BasicBlock::MergeBlockIdIfAny() const {
auto merge_ii = cend();
--merge_ii;
uint32_t mbid = 0;
if (merge_ii != cbegin()) {
--merge_ii;
if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
} else if (merge_ii->opcode() == spv::Op::OpSelectionMerge) {
mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
}
}
return mbid;
}
uint32_t BasicBlock::MergeBlockId() const {
uint32_t mbid = MergeBlockIdIfAny();
assert(mbid && "Expected block to have a corresponding merge block");
return mbid;
}
uint32_t BasicBlock::ContinueBlockIdIfAny() const {
auto merge_ii = cend();
--merge_ii;
uint32_t cbid = 0;
if (merge_ii != cbegin()) {
--merge_ii;
if (merge_ii->opcode() == spv::Op::OpLoopMerge) {
cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
}
}
return cbid;
}
uint32_t BasicBlock::ContinueBlockId() const {
uint32_t cbid = ContinueBlockIdIfAny();
assert(cbid && "Expected block to have a corresponding continue target");
return cbid;
}
std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
str << block.PrettyPrint();
return str;
}
void BasicBlock::Dump() const {
std::cerr << "Basic block #" << id() << "\n" << *this << "\n ";
}
std::string BasicBlock::PrettyPrint(uint32_t options) const {
std::ostringstream str;
ForEachInst([&str, options](const Instruction* inst) {
str << inst->PrettyPrint(options);
if (!spvOpcodeIsBlockTerminator(inst->opcode())) {
str << std::endl;
}
});
return str.str();
}
BasicBlock* BasicBlock::SplitBasicBlock(IRContext* context, uint32_t label_id,
iterator iter) {
assert(!insts_.empty());
std::unique_ptr<BasicBlock> new_block_temp = MakeUnique<BasicBlock>(
MakeUnique<Instruction>(context, spv::Op::OpLabel, 0, label_id,
std::initializer_list<Operand>{}));
BasicBlock* new_block = new_block_temp.get();
function_->InsertBasicBlockAfter(std::move(new_block_temp), this);
new_block->insts_.Splice(new_block->end(), &insts_, iter, end());
assert(new_block->GetParent() == GetParent() &&
"The parent should already be set appropriately.");
context->AnalyzeDefUse(new_block->GetLabelInst());
// Update the phi nodes in the successor blocks to reference the new block id.
const_cast<const BasicBlock*>(new_block)->ForEachSuccessorLabel(
[new_block, this, context](const uint32_t label) {
BasicBlock* target_bb = context->get_instr_block(label);
target_bb->ForEachPhiInst(
[this, new_block, context](Instruction* phi_inst) {
bool changed = false;
for (uint32_t i = 1; i < phi_inst->NumInOperands(); i += 2) {
if (phi_inst->GetSingleWordInOperand(i) == this->id()) {
changed = true;
phi_inst->SetInOperand(i, {new_block->id()});
}
}
if (changed) {
context->UpdateDefUse(phi_inst);
}
});
});
if (context->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
context->set_instr_block(new_block->GetLabelInst(), new_block);
new_block->ForEachInst([new_block, context](Instruction* inst) {
context->set_instr_block(inst, new_block);
});
}
return new_block;
}
} // namespace opt
} // namespace spvtools