// 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 {
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
} // namespace
BasicBlock* BasicBlock::Clone(IRContext* context) const {
BasicBlock* clone = new BasicBlock(
std::unique_ptr<Instruction>(GetLabelInst()->Clone(context)));
for (const auto& inst : insts_) {
// 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);
}
}
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 == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
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 == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
result = &*iter;
}
}
return result;
}
const Instruction* BasicBlock::GetLoopMergeInst() const {
if (auto* merge = GetMergeInst()) {
if (merge->opcode() == SpvOpLoopMerge) {
return merge;
}
}
return nullptr;
}
Instruction* BasicBlock::GetLoopMergeInst() {
if (auto* merge = GetMergeInst()) {
if (merge->opcode() == SpvOpLoopMerge) {
return merge;
}
}
return nullptr;
}
void BasicBlock::KillAllInsts(bool killLabel) {
ForEachInst([killLabel](Instruction* ip) {
if (killLabel || ip->opcode() != SpvOpLabel) {
ip->context()->KillInst(ip);
}
});
}
void BasicBlock::ForEachSuccessorLabel(
const std::function<void(const uint32_t)>& f) const {
const auto br = &insts_.back();
switch (br->opcode()) {
case SpvOpBranch: {
f(br->GetOperand(0).words[0]);
} break;
case SpvOpBranchConditional:
case SpvOpSwitch: {
bool is_first = true;
br->ForEachInId([&is_first, &f](const uint32_t* idp) {
if (!is_first) f(*idp);
is_first = false;
});
} break;
default:
break;
}
}
void BasicBlock::ForEachSuccessorLabel(
const std::function<void(uint32_t*)>& f) {
auto br = &insts_.back();
switch (br->opcode()) {
case SpvOpBranch: {
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 SpvOpBranchConditional:
case SpvOpSwitch: {
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() == SpvOpSelectionMerge || ii->opcode() == SpvOpLoopMerge) {
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() == SpvOpLoopMerge) {
mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
} else if (merge_ii->opcode() == SpvOpSelectionMerge) {
mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
}
}
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() == SpvOpLoopMerge) {
cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
}
}
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 (!IsTerminatorInst(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, SpvOpLabel, 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());
new_block->SetParent(GetParent());
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