SPIRV-Tools/source/opt/basic_block.cpp
Victor Lomuller 50e85c865c Add LoopUtils class to gather some loop transformation support.
This patch adds LoopUtils class to handle some loop related transformations. For now it has 2 transformations that simplifies other transformations such as loop unroll or unswitch:
 - Dedicate exit blocks: this ensure that all exit basic block
   (out-of-loop basic blocks that have a predecessor in the loop)
   have all their predecessors in the loop;
 - Loop Closed SSA (LCSSA): this ensure that all definitions in a loop are used inside the loop
   or in a phi instruction in an exit basic block.

It also adds the following capabilities:
 - Loop::IsLCSSA to test if the loop is in a LCSSA form
 - Loop::GetOrCreatePreHeaderBlock that can build a loop preheader if required;
 - New methods to allow on the fly updates of the loop descriptors.
 - New methods to allow on the fly updates of the CFG analysis.
 - Instruction::SetOperand to allow expression of the index relative to Instruction::NumOperands (to be compatible with the index returned by DefUseManager::ForEachUse)
2018-02-01 15:35:09 -05:00

195 lines
5.0 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 "basic_block.h"
#include "function.h"
#include "module.h"
#include "reflect.h"
#include "make_unique.h"
#include <ostream>
namespace spvtools {
namespace ir {
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)));
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::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 ir::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) {
block.ForEachInst([&str](const ir::Instruction* inst) {
str << *inst;
if (!IsTerminatorInst(inst->opcode())) {
str << std::endl;
}
});
return str;
}
} // namespace ir
} // namespace spvtools