mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-28 06:01:04 +00:00
7075c49923
The current implementation of merge return can create bad, but correct, code. When it is not in a loop construct, it will insert a lot of extra branch around code. The potentially large number of branches are bad. At the same time, it can separate code store to variables from its uses hiding the fact that the store dominates the load. This hurts the later analysis because the compiler thinks that multiple values can reach a load, when there is really only 1. This poorer analysis leads to missed optimizations. The solution is to create a dummy loop around the entire body of the function, then we can break from that loop with a single branch. Also only new merge nodes would be those at the end of loops meaning that most analysies will not be hurt. Remove dead code for cases that are no longer possible. It seems like some drivers expect there the be an OpSelectionMerge before conditional branches, even if they are not strictly needed. So we add them.
258 lines
7.2 KiB
C++
258 lines
7.2 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 "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)));
|
|
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
|