mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 00:40:14 +00:00
Add a new class opt::CFG to represent the CFG for the module.
This class moves some of the CFG-related functionality into a new class opt::CFG. There is some other code related to the CFG in the inliner and in opt::LocalSingleStoreElimPass that should also be moved, but that require more changes than this pure restructuring. I will move those bits in a follow-up PR. Currently, the CFG is computed every time a pass is instantiated, but this should be later moved to the new IRContext class that @s-perron is working on. Other re-factoring: - Add BasicBlock::ContinueBlockIdIfAny. Re-factored out of MergeBlockIdIfAny - Rewrite IsLoopHeader in terms of GetLoopMergeInst. - Run clang-format on some files.
This commit is contained in:
parent
476cae6f7d
commit
fef669f30f
@ -51,6 +51,7 @@ SPVTOOLS_OPT_SRC_FILES := \
|
||||
source/opt/basic_block.cpp \
|
||||
source/opt/block_merge_pass.cpp \
|
||||
source/opt/build_module.cpp \
|
||||
source/opt/cfg.cpp \
|
||||
source/opt/cfg_cleanup_pass.cpp \
|
||||
source/opt/compact_ids_pass.cpp \
|
||||
source/opt/common_uniform_elim_pass.cpp \
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
@ -17,6 +17,7 @@ add_library(SPIRV-Tools-opt
|
||||
block_merge_pass.h
|
||||
build_module.h
|
||||
cfg_cleanup_pass.h
|
||||
cfg.h
|
||||
common_uniform_elim_pass.h
|
||||
compact_ids_pass.h
|
||||
constants.h
|
||||
@ -63,6 +64,7 @@ add_library(SPIRV-Tools-opt
|
||||
block_merge_pass.cpp
|
||||
build_module.cpp
|
||||
cfg_cleanup_pass.cpp
|
||||
cfg.cpp
|
||||
common_uniform_elim_pass.cpp
|
||||
compact_ids_pass.cpp
|
||||
decoration_manager.cpp
|
||||
|
@ -199,7 +199,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
ComputeInst2BlockMap(func);
|
||||
// Compute map from block to controlling conditional branch
|
||||
std::list<ir::BasicBlock*> structuredOrder;
|
||||
ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
|
||||
cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
|
||||
ComputeBlock2HeaderMaps(structuredOrder);
|
||||
bool modified = false;
|
||||
// Add instructions with external side effects to worklist. Also add branches
|
||||
|
@ -19,6 +19,14 @@
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
|
||||
const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
|
||||
const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
BasicBlock::BasicBlock(const BasicBlock& bb)
|
||||
: function_(nullptr),
|
||||
label_(MakeUnique<Instruction>(bb.GetLabelInst())),
|
||||
@ -35,7 +43,7 @@ const Instruction* BasicBlock::GetMergeInst() const {
|
||||
if (iter != cbegin()) {
|
||||
--iter;
|
||||
const auto opcode = iter->opcode();
|
||||
if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
|
||||
if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
|
||||
result = &*iter;
|
||||
}
|
||||
}
|
||||
@ -50,7 +58,7 @@ Instruction* BasicBlock::GetMergeInst() {
|
||||
if (iter != begin()) {
|
||||
--iter;
|
||||
const auto opcode = iter->opcode();
|
||||
if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
|
||||
if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
|
||||
result = &*iter;
|
||||
}
|
||||
}
|
||||
@ -101,13 +109,39 @@ void BasicBlock::ForMergeAndContinueLabel(
|
||||
--ii;
|
||||
if (ii == insts_.begin()) return;
|
||||
--ii;
|
||||
if (ii->opcode() == SpvOpSelectionMerge ||
|
||||
ii->opcode() == SpvOpLoopMerge)
|
||||
ii->ForEachInId([&f](const uint32_t* idp) {
|
||||
f(*idp);
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -64,6 +64,7 @@ class BasicBlock {
|
||||
// Otherwise return null. May be used whenever tail() can be used.
|
||||
const Instruction* GetMergeInst() const;
|
||||
Instruction* GetMergeInst();
|
||||
|
||||
// Returns the OpLoopMerge instruciton in this basic block, if it exists.
|
||||
// Otherwise return null. May be used whenever tail() can be used.
|
||||
const Instruction* GetLoopMergeInst() const;
|
||||
@ -84,6 +85,7 @@ class BasicBlock {
|
||||
assert(!insts_.empty());
|
||||
return --end();
|
||||
}
|
||||
|
||||
// Returns a const iterator, but othewrise similar to tail().
|
||||
const_iterator ctail() const {
|
||||
assert(!insts_.empty());
|
||||
@ -118,6 +120,18 @@ class BasicBlock {
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
// Return true if this block is a loop header block.
|
||||
bool IsLoopHeader() const { return GetLoopMergeInst() != nullptr; }
|
||||
|
||||
// Returns the ID of the merge block declared by a merge instruction in this
|
||||
// block, if any. If none, returns zero. If |cbid| is not nullptr, the ID of
|
||||
// the continue block in the merge instruction is set in |*cbid|.
|
||||
uint32_t MergeBlockIdIfAny() const;
|
||||
|
||||
// Returns the ID of the continue block declared by a merge instruction in
|
||||
// this block, if any. If none, returns zero.
|
||||
uint32_t ContinueBlockIdIfAny() const;
|
||||
|
||||
private:
|
||||
// The enclosing function.
|
||||
Function* function_;
|
||||
@ -136,7 +150,7 @@ inline void BasicBlock::AddInstruction(std::unique_ptr<Instruction> i) {
|
||||
|
||||
inline void BasicBlock::AddInstructions(BasicBlock* bp) {
|
||||
auto bEnd = end();
|
||||
(void) bEnd.MoveBefore(&bp->insts_);
|
||||
(void)bEnd.MoveBefore(&bp->insts_);
|
||||
}
|
||||
|
||||
inline void BasicBlock::ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
@ -152,8 +166,8 @@ inline void BasicBlock::ForEachInst(
|
||||
static_cast<const Instruction*>(label_.get())
|
||||
->ForEachInst(f, run_on_debug_line_insts);
|
||||
for (const auto& inst : insts_)
|
||||
static_cast<const Instruction*>(&inst)
|
||||
->ForEachInst(f, run_on_debug_line_insts);
|
||||
static_cast<const Instruction*>(&inst)->ForEachInst(
|
||||
f, run_on_debug_line_insts);
|
||||
}
|
||||
|
||||
inline void BasicBlock::ForEachPhiInst(
|
||||
|
@ -54,7 +54,7 @@ bool BlockMergePass::MergeBlocks(ir::Function* func) {
|
||||
bool modified = false;
|
||||
for (auto bi = func->begin(); bi != func->end(); ) {
|
||||
// Do not merge loop header blocks, at least for now.
|
||||
if (IsLoopHeader(&*bi)) {
|
||||
if (bi->IsLoopHeader()) {
|
||||
++bi;
|
||||
continue;
|
||||
}
|
||||
|
94
source/opt/cfg.cpp
Normal file
94
source/opt/cfg.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2017 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 "cfa.h"
|
||||
#include "cfg.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
namespace {
|
||||
|
||||
// Universal Limit of ResultID + 1
|
||||
const int kInvalidId = 0x400000;
|
||||
|
||||
} // namespace
|
||||
|
||||
CFG::CFG(ir::Module* module)
|
||||
: module_(module),
|
||||
pseudo_entry_block_(std::unique_ptr<ir::Instruction>(
|
||||
new ir::Instruction(SpvOpLabel, 0, 0, {}))),
|
||||
pseudo_exit_block_(std::unique_ptr<ir::Instruction>(
|
||||
new ir::Instruction(SpvOpLabel, 0, kInvalidId, {}))) {
|
||||
for (auto& fn : *module) {
|
||||
for (auto& blk : fn) {
|
||||
uint32_t blkId = blk.id();
|
||||
id2block_[blkId] = &blk;
|
||||
blk.ForEachSuccessorLabel([&blkId, this](uint32_t sbid) {
|
||||
label2preds_[sbid].push_back(blkId);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CFG::ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root,
|
||||
std::list<ir::BasicBlock*>* order) {
|
||||
assert(module_->HasCapability(SpvCapabilityShader) &&
|
||||
"This only works on structured control flow");
|
||||
|
||||
// Compute structured successors and do DFS.
|
||||
ComputeStructuredSuccessors(func);
|
||||
auto ignore_block = [](cbb_ptr) {};
|
||||
auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
|
||||
auto get_structured_successors = [this](const ir::BasicBlock* b) {
|
||||
return &(block2structured_succs_[b]);
|
||||
};
|
||||
|
||||
// TODO(greg-lunarg): Get rid of const_cast by making moving const
|
||||
// out of the cfa.h prototypes and into the invoking code.
|
||||
auto post_order = [&](cbb_ptr b) {
|
||||
order->push_front(const_cast<ir::BasicBlock*>(b));
|
||||
};
|
||||
spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal(
|
||||
root, get_structured_successors, ignore_block, post_order,
|
||||
ignore_edge);
|
||||
}
|
||||
|
||||
void CFG::ComputeStructuredSuccessors(ir::Function *func) {
|
||||
block2structured_succs_.clear();
|
||||
for (auto& blk : *func) {
|
||||
// If no predecessors in function, make successor to pseudo entry.
|
||||
if (label2preds_[blk.id()].size() == 0)
|
||||
block2structured_succs_[&pseudo_entry_block_].push_back(&blk);
|
||||
|
||||
// If header, make merge block first successor and continue block second
|
||||
// successor if there is one.
|
||||
uint32_t mbid = blk.MergeBlockIdIfAny();
|
||||
if (mbid != 0) {
|
||||
block2structured_succs_[&blk].push_back(id2block_[mbid]);
|
||||
uint32_t cbid = blk.ContinueBlockIdIfAny();
|
||||
if (cbid != 0)
|
||||
block2structured_succs_[&blk].push_back(id2block_[cbid]);
|
||||
}
|
||||
|
||||
// Add true successors.
|
||||
blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) {
|
||||
block2structured_succs_[&blk].push_back(id2block_[sbid]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
110
source/opt/cfg.h
Normal file
110
source/opt/cfg.h
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2017 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.
|
||||
|
||||
#ifndef LIBSPIRV_OPT_CFG_H_
|
||||
#define LIBSPIRV_OPT_CFG_H_
|
||||
|
||||
#include "basic_block.h"
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
class CFG {
|
||||
public:
|
||||
CFG(ir::Module *module);
|
||||
|
||||
// Return the module described by this CFG.
|
||||
ir::Module* get_module() const { return module_; }
|
||||
|
||||
// Return the list of predecesors for basic block with label |blkid|.
|
||||
// TODO(dnovillo): Move this to ir::BasicBlock.
|
||||
const std::vector<uint32_t>& preds(uint32_t blk_id) const {
|
||||
return label2preds_.at(blk_id);
|
||||
}
|
||||
|
||||
// Return a pointer to the basic block instance corresponding to the label
|
||||
// |blk_id|.
|
||||
ir::BasicBlock* block(uint32_t blk_id) const { return id2block_.at(blk_id); }
|
||||
|
||||
// Return the pseudo entry and exit blocks. TODO(dnovillo): Remove when
|
||||
// LocalSingleStoreElimPass::CalculateImmediateDominators() is moved into this
|
||||
// class.
|
||||
const ir::BasicBlock* pseudo_entry_block() const {
|
||||
return &pseudo_entry_block_;
|
||||
}
|
||||
ir::BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; }
|
||||
|
||||
const ir::BasicBlock* pseudo_exit_block() const {
|
||||
return &pseudo_exit_block_;
|
||||
}
|
||||
ir::BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; }
|
||||
|
||||
// Return true if |block_ptr| is the pseudo-entry block.
|
||||
bool IsPseudoEntryBlock(ir::BasicBlock* block_ptr) const {
|
||||
return block_ptr == &pseudo_entry_block_;
|
||||
}
|
||||
|
||||
// Return true if |block_ptr| is the pseudo-exit block.
|
||||
bool IsPseudoExitBlock(ir::BasicBlock* block_ptr) const {
|
||||
return block_ptr == &pseudo_exit_block_;
|
||||
}
|
||||
|
||||
// Compute structured block order into |structuredOrder| for |func| starting
|
||||
// at |root|. This order has the property that dominators come before all
|
||||
// blocks they dominate and merge blocks come after all blocks that are in
|
||||
// the control constructs of their header.
|
||||
void ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root,
|
||||
std::list<ir::BasicBlock*>* order);
|
||||
|
||||
private:
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
|
||||
// Compute structured successors for function |func|. A block's structured
|
||||
// successors are the blocks it branches to together with its declared merge
|
||||
// block and continue block if it has them. When order matters, the merge
|
||||
// block and continue block always appear first. This assures correct depth
|
||||
// first search in the presence of early returns and kills. If the successor
|
||||
// vector contain duplicates of the merge or continue blocks, they are safely
|
||||
// ignored by DFS.
|
||||
void ComputeStructuredSuccessors(ir::Function* func);
|
||||
|
||||
// Module for this CFG.
|
||||
ir::Module *module_;
|
||||
|
||||
// Map from block to its structured successor blocks. See
|
||||
// ComputeStructuredSuccessors() for definition.
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
|
||||
// Extra block whose successors are all blocks with no predecessors
|
||||
// in function. TODO(dnovillo): Needed?
|
||||
ir::BasicBlock pseudo_entry_block_;
|
||||
|
||||
// Augmented CFG Exit Block. TODO(dnovillo): Needed?
|
||||
ir::BasicBlock pseudo_exit_block_;
|
||||
|
||||
// Map from block's label id to its predecessor blocks ids
|
||||
std::unordered_map<uint32_t, std::vector<uint32_t>> label2preds_;
|
||||
|
||||
// Map from block's label id to block.
|
||||
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_CFG_H_
|
@ -34,14 +34,14 @@ const uint32_t kLoadPtrIdInIdx = 0;
|
||||
const uint32_t kCopyObjectOperandInIdx = 0;
|
||||
const uint32_t kTypeIntWidthInIdx = 0;
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
bool CommonUniformElimPass::IsNonPtrAccessChain(const SpvOp opcode) const {
|
||||
return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsSamplerOrImageType(
|
||||
const ir::Instruction* typeInst) const {
|
||||
const ir::Instruction* typeInst) const {
|
||||
switch (typeInst->opcode()) {
|
||||
case SpvOpTypeSampler:
|
||||
case SpvOpTypeImage:
|
||||
@ -50,8 +50,7 @@ bool CommonUniformElimPass::IsSamplerOrImageType(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (typeInst->opcode() != SpvOpTypeStruct)
|
||||
return false;
|
||||
if (typeInst->opcode() != SpvOpTypeStruct) return false;
|
||||
// Return true if any member is a sampler or image
|
||||
int samplerOrImageCnt = 0;
|
||||
typeInst->ForEachInId([&samplerOrImageCnt, this](const uint32_t* tid) {
|
||||
@ -61,24 +60,23 @@ bool CommonUniformElimPass::IsSamplerOrImageType(
|
||||
return samplerOrImageCnt > 0;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsSamplerOrImageVar(
|
||||
uint32_t varId) const {
|
||||
bool CommonUniformElimPass::IsSamplerOrImageVar(uint32_t varId) const {
|
||||
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
|
||||
assert(varInst->opcode() == SpvOpVariable);
|
||||
const uint32_t varTypeId = varInst->type_id();
|
||||
const ir::Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
|
||||
const uint32_t varPteTypeId =
|
||||
varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
|
||||
varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
|
||||
ir::Instruction* varPteTypeInst = get_def_use_mgr()->GetDef(varPteTypeId);
|
||||
return IsSamplerOrImageType(varPteTypeInst);
|
||||
}
|
||||
|
||||
ir::Instruction* CommonUniformElimPass::GetPtr(
|
||||
ir::Instruction* ip, uint32_t* objId) {
|
||||
ir::Instruction* CommonUniformElimPass::GetPtr(ir::Instruction* ip,
|
||||
uint32_t* objId) {
|
||||
const SpvOp op = ip->opcode();
|
||||
assert(op == SpvOpStore || op == SpvOpLoad);
|
||||
*objId = ip->GetSingleWordInOperand(
|
||||
op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx);
|
||||
*objId = ip->GetSingleWordInOperand(op == SpvOpStore ? kStorePtrIdInIdx
|
||||
: kLoadPtrIdInIdx);
|
||||
ir::Instruction* ptrInst = get_def_use_mgr()->GetDef(*objId);
|
||||
while (ptrInst->opcode() == SpvOpCopyObject) {
|
||||
*objId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
|
||||
@ -86,11 +84,10 @@ ir::Instruction* CommonUniformElimPass::GetPtr(
|
||||
}
|
||||
ir::Instruction* objInst = ptrInst;
|
||||
while (objInst->opcode() != SpvOpVariable &&
|
||||
objInst->opcode() != SpvOpFunctionParameter) {
|
||||
objInst->opcode() != SpvOpFunctionParameter) {
|
||||
if (IsNonPtrAccessChain(objInst->opcode())) {
|
||||
*objId = objInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
assert(objInst->opcode() == SpvOpCopyObject);
|
||||
*objId = objInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
|
||||
}
|
||||
@ -103,11 +100,14 @@ bool CommonUniformElimPass::IsVolatileStruct(uint32_t type_id) {
|
||||
assert(get_def_use_mgr()->GetDef(type_id)->opcode() == SpvOpTypeStruct);
|
||||
bool has_volatile_deco = false;
|
||||
dec_mgr_->ForEachDecoration(type_id, SpvDecorationVolatile,
|
||||
[&has_volatile_deco](const ir::Instruction&){ has_volatile_deco = true;});
|
||||
[&has_volatile_deco](const ir::Instruction&) {
|
||||
has_volatile_deco = true;
|
||||
});
|
||||
return has_volatile_deco;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsAccessChainToVolatileStructType(const ir::Instruction &AccessChainInst) {
|
||||
bool CommonUniformElimPass::IsAccessChainToVolatileStructType(
|
||||
const ir::Instruction& AccessChainInst) {
|
||||
assert(AccessChainInst.opcode() == SpvOpAccessChain);
|
||||
|
||||
uint32_t ptr_id = AccessChainInst.GetSingleWordInOperand(0);
|
||||
@ -132,8 +132,10 @@ bool CommonUniformElimPass::IsAccessChainToVolatileStructType(const ir::Instruct
|
||||
|
||||
if (idx < num_operands - 1) {
|
||||
const uint32_t index_id = AccessChainInst.GetSingleWordOperand(idx);
|
||||
const ir::Instruction* index_inst = get_def_use_mgr()->GetDef(index_id);
|
||||
uint32_t index_value = index_inst->GetSingleWordOperand(2); // TODO: replace with GetUintValueFromConstant()
|
||||
const ir::Instruction* index_inst =
|
||||
get_def_use_mgr()->GetDef(index_id);
|
||||
uint32_t index_value = index_inst->GetSingleWordOperand(
|
||||
2); // TODO: replace with GetUintValueFromConstant()
|
||||
pointee_type_id = pointee_type->GetSingleWordInOperand(index_value);
|
||||
}
|
||||
break;
|
||||
@ -149,8 +151,7 @@ bool CommonUniformElimPass::IsVolatileLoad(const ir::Instruction& loadInst) {
|
||||
// Check if this Load instruction has Volatile Memory Access flag
|
||||
if (loadInst.NumOperands() == 4) {
|
||||
uint32_t memory_access_mask = loadInst.GetSingleWordOperand(3);
|
||||
if (memory_access_mask & SpvMemoryAccessVolatileMask)
|
||||
return true;
|
||||
if (memory_access_mask & SpvMemoryAccessVolatileMask) return true;
|
||||
}
|
||||
// If we load a struct directly (result type is struct),
|
||||
// check if the struct is decorated volatile
|
||||
@ -163,65 +164,56 @@ bool CommonUniformElimPass::IsVolatileLoad(const ir::Instruction& loadInst) {
|
||||
|
||||
bool CommonUniformElimPass::IsUniformVar(uint32_t varId) {
|
||||
const ir::Instruction* varInst =
|
||||
get_def_use_mgr()->id_to_defs().find(varId)->second;
|
||||
if (varInst->opcode() != SpvOpVariable)
|
||||
return false;
|
||||
get_def_use_mgr()->id_to_defs().find(varId)->second;
|
||||
if (varInst->opcode() != SpvOpVariable) return false;
|
||||
const uint32_t varTypeId = varInst->type_id();
|
||||
const ir::Instruction* varTypeInst =
|
||||
get_def_use_mgr()->id_to_defs().find(varTypeId)->second;
|
||||
get_def_use_mgr()->id_to_defs().find(varTypeId)->second;
|
||||
return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
|
||||
SpvStorageClassUniform ||
|
||||
varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
|
||||
SpvStorageClassUniformConstant;
|
||||
SpvStorageClassUniform ||
|
||||
varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
|
||||
SpvStorageClassUniformConstant;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::HasUnsupportedDecorates(uint32_t id) const {
|
||||
analysis::UseList* uses = get_def_use_mgr()->GetUses(id);
|
||||
if (uses == nullptr)
|
||||
return false;
|
||||
if (uses == nullptr) return false;
|
||||
for (auto u : *uses) {
|
||||
const SpvOp op = u.inst->opcode();
|
||||
if (IsNonTypeDecorate(op))
|
||||
return true;
|
||||
if (IsNonTypeDecorate(op)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::HasOnlyNamesAndDecorates(uint32_t id) const {
|
||||
analysis::UseList* uses = get_def_use_mgr()->GetUses(id);
|
||||
if (uses == nullptr)
|
||||
return true;
|
||||
if (uses == nullptr) return true;
|
||||
for (auto u : *uses) {
|
||||
const SpvOp op = u.inst->opcode();
|
||||
if (op != SpvOpName && !IsNonTypeDecorate(op))
|
||||
return false;
|
||||
if (op != SpvOpName && !IsNonTypeDecorate(op)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::KillNamesAndDecorates(uint32_t id) {
|
||||
// TODO(greg-lunarg): Remove id from any OpGroupDecorate and
|
||||
// TODO(greg-lunarg): Remove id from any OpGroupDecorate and
|
||||
// kill if no other operands.
|
||||
analysis::UseList* uses = get_def_use_mgr()->GetUses(id);
|
||||
if (uses == nullptr)
|
||||
return;
|
||||
if (uses == nullptr) return;
|
||||
std::list<ir::Instruction*> killList;
|
||||
for (auto u : *uses) {
|
||||
const SpvOp op = u.inst->opcode();
|
||||
if (op != SpvOpName && !IsNonTypeDecorate(op))
|
||||
continue;
|
||||
if (op != SpvOpName && !IsNonTypeDecorate(op)) continue;
|
||||
killList.push_back(u.inst);
|
||||
}
|
||||
for (auto kip : killList)
|
||||
get_def_use_mgr()->KillInst(kip);
|
||||
for (auto kip : killList) get_def_use_mgr()->KillInst(kip);
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::KillNamesAndDecorates(ir::Instruction* inst) {
|
||||
// TODO(greg-lunarg): Remove inst from any OpGroupDecorate and
|
||||
// TODO(greg-lunarg): Remove inst from any OpGroupDecorate and
|
||||
// kill if not other operands.
|
||||
const uint32_t rId = inst->result_id();
|
||||
if (rId == 0)
|
||||
return;
|
||||
if (rId == 0) return;
|
||||
KillNamesAndDecorates(rId);
|
||||
}
|
||||
|
||||
@ -235,35 +227,34 @@ void CommonUniformElimPass::DeleteIfUseless(ir::Instruction* inst) {
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::ReplaceAndDeleteLoad(ir::Instruction* loadInst,
|
||||
uint32_t replId,
|
||||
ir::Instruction* ptrInst) {
|
||||
uint32_t replId,
|
||||
ir::Instruction* ptrInst) {
|
||||
const uint32_t loadId = loadInst->result_id();
|
||||
KillNamesAndDecorates(loadId);
|
||||
(void) get_def_use_mgr()->ReplaceAllUsesWith(loadId, replId);
|
||||
(void)get_def_use_mgr()->ReplaceAllUsesWith(loadId, replId);
|
||||
// remove load instruction
|
||||
get_def_use_mgr()->KillInst(loadInst);
|
||||
// if access chain, see if it can be removed as well
|
||||
if (IsNonPtrAccessChain(ptrInst->opcode()))
|
||||
DeleteIfUseless(ptrInst);
|
||||
if (IsNonPtrAccessChain(ptrInst->opcode())) DeleteIfUseless(ptrInst);
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::GenACLoadRepl(const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts,
|
||||
uint32_t* resultId) {
|
||||
|
||||
void CommonUniformElimPass::GenACLoadRepl(
|
||||
const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts,
|
||||
uint32_t* resultId) {
|
||||
// Build and append Load
|
||||
const uint32_t ldResultId = TakeNextId();
|
||||
const uint32_t varId =
|
||||
ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
|
||||
ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
|
||||
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
|
||||
assert(varInst->opcode() == SpvOpVariable);
|
||||
const uint32_t varPteTypeId = GetPointeeTypeId(varInst);
|
||||
std::vector<ir::Operand> load_in_operands;
|
||||
load_in_operands.push_back(
|
||||
ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
std::initializer_list<uint32_t>{varId}));
|
||||
std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(SpvOpLoad,
|
||||
varPteTypeId, ldResultId, load_in_operands));
|
||||
ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
std::initializer_list<uint32_t>{varId}));
|
||||
std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(
|
||||
SpvOpLoad, varPteTypeId, ldResultId, load_in_operands));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newLoad);
|
||||
newInsts->emplace_back(std::move(newLoad));
|
||||
|
||||
@ -272,21 +263,21 @@ void CommonUniformElimPass::GenACLoadRepl(const ir::Instruction* ptrInst,
|
||||
const uint32_t ptrPteTypeId = GetPointeeTypeId(ptrInst);
|
||||
std::vector<ir::Operand> ext_in_opnds;
|
||||
ext_in_opnds.push_back(
|
||||
ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
std::initializer_list<uint32_t>{ldResultId}));
|
||||
ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
std::initializer_list<uint32_t>{ldResultId}));
|
||||
uint32_t iidIdx = 0;
|
||||
ptrInst->ForEachInId([&iidIdx, &ext_in_opnds, this](const uint32_t *iid) {
|
||||
ptrInst->ForEachInId([&iidIdx, &ext_in_opnds, this](const uint32_t* iid) {
|
||||
if (iidIdx > 0) {
|
||||
const ir::Instruction* cInst = get_def_use_mgr()->GetDef(*iid);
|
||||
uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx);
|
||||
ext_in_opnds.push_back(
|
||||
ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
std::initializer_list<uint32_t>{val}));
|
||||
ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
std::initializer_list<uint32_t>{val}));
|
||||
}
|
||||
++iidIdx;
|
||||
});
|
||||
std::unique_ptr<ir::Instruction> newExt(new ir::Instruction(
|
||||
SpvOpCompositeExtract, ptrPteTypeId, extResultId, ext_in_opnds));
|
||||
SpvOpCompositeExtract, ptrPteTypeId, extResultId, ext_in_opnds));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newExt);
|
||||
newInsts->emplace_back(std::move(newExt));
|
||||
*resultId = extResultId;
|
||||
@ -309,27 +300,19 @@ bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) {
|
||||
bool modified = false;
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
if (ii->opcode() != SpvOpLoad)
|
||||
continue;
|
||||
if (ii->opcode() != SpvOpLoad) continue;
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode()))
|
||||
continue;
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode())) continue;
|
||||
// Do not convert nested access chains
|
||||
if (ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx) != varId)
|
||||
continue;
|
||||
if (!IsUniformVar(varId))
|
||||
continue;
|
||||
if (!IsConstantIndexAccessChain(ptrInst))
|
||||
continue;
|
||||
if (HasUnsupportedDecorates(ii->result_id()))
|
||||
continue;
|
||||
if (HasUnsupportedDecorates(ptrInst->result_id()))
|
||||
continue;
|
||||
if (IsVolatileLoad(*ii))
|
||||
continue;
|
||||
if (IsAccessChainToVolatileStructType(*ptrInst))
|
||||
continue;
|
||||
if (!IsUniformVar(varId)) continue;
|
||||
if (!IsConstantIndexAccessChain(ptrInst)) continue;
|
||||
if (HasUnsupportedDecorates(ii->result_id())) continue;
|
||||
if (HasUnsupportedDecorates(ptrInst->result_id())) continue;
|
||||
if (IsVolatileLoad(*ii)) continue;
|
||||
if (IsAccessChainToVolatileStructType(*ptrInst)) continue;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
|
||||
uint32_t replId;
|
||||
GenACLoadRepl(ptrInst, &newInsts, &replId);
|
||||
@ -344,18 +327,20 @@ bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) {
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::ComputeStructuredSuccessors(ir::Function* func) {
|
||||
block2structured_succs_.clear();
|
||||
for (auto& blk : *func) {
|
||||
// If header, make merge block first successor.
|
||||
uint32_t cbid;
|
||||
const uint32_t mbid = MergeBlockIdIfAny(blk, &cbid);
|
||||
uint32_t mbid = blk.MergeBlockIdIfAny();
|
||||
if (mbid != 0) {
|
||||
block2structured_succs_[&blk].push_back(id2block_[mbid]);
|
||||
if (cbid != 0)
|
||||
block2structured_succs_[&blk].push_back(id2block_[cbid]);
|
||||
block2structured_succs_[&blk].push_back(cfg()->block(mbid));
|
||||
uint32_t cbid = blk.ContinueBlockIdIfAny();
|
||||
if (cbid != 0) {
|
||||
block2structured_succs_[&blk].push_back(cfg()->block(mbid));
|
||||
}
|
||||
}
|
||||
// add true successors
|
||||
blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) {
|
||||
block2structured_succs_[&blk].push_back(id2block_[sbid]);
|
||||
block2structured_succs_[&blk].push_back(cfg()->block(sbid));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -367,16 +352,18 @@ void CommonUniformElimPass::ComputeStructuredOrder(
|
||||
auto ignore_block = [](cbb_ptr) {};
|
||||
auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
|
||||
auto get_structured_successors = [this](const ir::BasicBlock* block) {
|
||||
return &(block2structured_succs_[block]); };
|
||||
return &(block2structured_succs_[block]);
|
||||
};
|
||||
// TODO(greg-lunarg): Get rid of const_cast by making moving const
|
||||
// out of the cfa.h prototypes and into the invoking code.
|
||||
auto post_order = [&](cbb_ptr b) {
|
||||
order->push_front(const_cast<ir::BasicBlock*>(b)); };
|
||||
|
||||
order->push_front(const_cast<ir::BasicBlock*>(b));
|
||||
};
|
||||
|
||||
order->clear();
|
||||
spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal(
|
||||
&*func->begin(), get_structured_successors, ignore_block,
|
||||
post_order, ignore_edge);
|
||||
&*func->begin(), get_structured_successors, ignore_block, post_order,
|
||||
ignore_edge);
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
@ -391,7 +378,7 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
// Find insertion point in first block to copy non-dominating loads.
|
||||
auto insertItr = func->begin()->begin();
|
||||
while (insertItr->opcode() == SpvOpVariable ||
|
||||
insertItr->opcode() == SpvOpNop)
|
||||
insertItr->opcode() == SpvOpNop)
|
||||
++insertItr;
|
||||
uint32_t mergeBlockId = 0;
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
|
||||
@ -403,36 +390,29 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
insertItr = bp->begin();
|
||||
}
|
||||
for (auto ii = bp->begin(); ii != bp->end(); ++ii) {
|
||||
if (ii->opcode() != SpvOpLoad)
|
||||
continue;
|
||||
if (ii->opcode() != SpvOpLoad) continue;
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (ptrInst->opcode() != SpvOpVariable)
|
||||
continue;
|
||||
if (!IsUniformVar(varId))
|
||||
continue;
|
||||
if (IsSamplerOrImageVar(varId))
|
||||
continue;
|
||||
if (HasUnsupportedDecorates(ii->result_id()))
|
||||
continue;
|
||||
if (IsVolatileLoad(*ii))
|
||||
continue;
|
||||
if (ptrInst->opcode() != SpvOpVariable) continue;
|
||||
if (!IsUniformVar(varId)) continue;
|
||||
if (IsSamplerOrImageVar(varId)) continue;
|
||||
if (HasUnsupportedDecorates(ii->result_id())) continue;
|
||||
if (IsVolatileLoad(*ii)) continue;
|
||||
uint32_t replId;
|
||||
const auto uItr = uniform2load_id_.find(varId);
|
||||
if (uItr != uniform2load_id_.end()) {
|
||||
replId = uItr->second;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (mergeBlockId == 0) {
|
||||
// Load is in dominating block; just remember it
|
||||
uniform2load_id_[varId] = ii->result_id();
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Copy load into most recent dominating block and remember it
|
||||
replId = TakeNextId();
|
||||
std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(SpvOpLoad,
|
||||
ii->type_id(), replId, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}}));
|
||||
std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(
|
||||
SpvOpLoad, ii->type_id(), replId,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newLoad);
|
||||
insertItr = insertItr.InsertBefore(std::move(newLoad));
|
||||
++insertItr;
|
||||
@ -445,8 +425,7 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
// If we are outside of any control construct and entering one, remember
|
||||
// the id of the merge block
|
||||
if (mergeBlockId == 0) {
|
||||
uint32_t dummy;
|
||||
mergeBlockId = MergeBlockIdIfAny(*bp, &dummy);
|
||||
mergeBlockId = bp->MergeBlockIdIfAny();
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
@ -457,26 +436,19 @@ bool CommonUniformElimPass::CommonUniformLoadElimBlock(ir::Function* func) {
|
||||
for (auto& blk : *func) {
|
||||
uniform2load_id_.clear();
|
||||
for (auto ii = blk.begin(); ii != blk.end(); ++ii) {
|
||||
if (ii->opcode() != SpvOpLoad)
|
||||
continue;
|
||||
if (ii->opcode() != SpvOpLoad) continue;
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (ptrInst->opcode() != SpvOpVariable)
|
||||
continue;
|
||||
if (!IsUniformVar(varId))
|
||||
continue;
|
||||
if (!IsSamplerOrImageVar(varId))
|
||||
continue;
|
||||
if (HasUnsupportedDecorates(ii->result_id()))
|
||||
continue;
|
||||
if (IsVolatileLoad(*ii))
|
||||
continue;
|
||||
if (ptrInst->opcode() != SpvOpVariable) continue;
|
||||
if (!IsUniformVar(varId)) continue;
|
||||
if (!IsSamplerOrImageVar(varId)) continue;
|
||||
if (HasUnsupportedDecorates(ii->result_id())) continue;
|
||||
if (IsVolatileLoad(*ii)) continue;
|
||||
uint32_t replId;
|
||||
const auto uItr = uniform2load_id_.find(varId);
|
||||
if (uItr != uniform2load_id_.end()) {
|
||||
replId = uItr->second;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
uniform2load_id_[varId] = ii->result_id();
|
||||
continue;
|
||||
}
|
||||
@ -491,13 +463,10 @@ bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) {
|
||||
// Find all composite ids with duplicate extracts.
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
if (ii->opcode() != SpvOpCompositeExtract)
|
||||
continue;
|
||||
if (ii->opcode() != SpvOpCompositeExtract) continue;
|
||||
// TODO(greg-lunarg): Support multiple indices
|
||||
if (ii->NumInOperands() > 2)
|
||||
continue;
|
||||
if (HasUnsupportedDecorates(ii->result_id()))
|
||||
continue;
|
||||
if (ii->NumInOperands() > 2) continue;
|
||||
if (HasUnsupportedDecorates(ii->result_id())) continue;
|
||||
uint32_t compId = ii->GetSingleWordInOperand(kExtractCompositeIdInIdx);
|
||||
uint32_t idx = ii->GetSingleWordInOperand(kExtractIdx0InIdx);
|
||||
comp2idx2inst_[compId][idx].push_back(&*ii);
|
||||
@ -509,13 +478,12 @@ bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) {
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
const auto cItr = comp2idx2inst_.find(ii->result_id());
|
||||
if (cItr == comp2idx2inst_.end())
|
||||
continue;
|
||||
if (cItr == comp2idx2inst_.end()) continue;
|
||||
for (auto idxItr : cItr->second) {
|
||||
if (idxItr.second.size() < 2)
|
||||
continue;
|
||||
if (idxItr.second.size() < 2) continue;
|
||||
uint32_t replId = TakeNextId();
|
||||
std::unique_ptr<ir::Instruction> newExtract(new ir::Instruction(*idxItr.second.front()));
|
||||
std::unique_ptr<ir::Instruction> newExtract(
|
||||
new ir::Instruction(*idxItr.second.front()));
|
||||
newExtract->SetResultId(replId);
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newExtract);
|
||||
++ii;
|
||||
@ -534,13 +502,13 @@ bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) {
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::EliminateCommonUniform(ir::Function* func) {
|
||||
bool modified = false;
|
||||
modified |= UniformAccessChainConvert(func);
|
||||
modified |= CommonUniformLoadElimination(func);
|
||||
modified |= CommonExtractElimination(func);
|
||||
bool modified = false;
|
||||
modified |= UniformAccessChainConvert(func);
|
||||
modified |= CommonUniformLoadElimination(func);
|
||||
modified |= CommonExtractElimination(func);
|
||||
|
||||
modified |= CommonUniformLoadElimBlock(func);
|
||||
return modified;
|
||||
modified |= CommonUniformLoadElimBlock(func);
|
||||
return modified;
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::Initialize(ir::IRContext* c) {
|
||||
@ -557,8 +525,8 @@ void CommonUniformElimPass::Initialize(ir::IRContext* c) {
|
||||
bool CommonUniformElimPass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -575,14 +543,12 @@ Pass::Status CommonUniformElimPass::ProcessImpl() {
|
||||
if (get_module()->HasCapability(SpvCapabilityAddresses))
|
||||
return Status::SuccessWithoutChange;
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Do not process if module contains OpGroupDecorate. Additional
|
||||
// support required in KillNamesAndDecorates().
|
||||
// TODO(greg-lunarg): Add support for OpGroupDecorate
|
||||
for (auto& ai : get_module()->annotations())
|
||||
if (ai.opcode() == SpvOpGroupDecorate)
|
||||
return Status::SuccessWithoutChange;
|
||||
if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
|
||||
// If non-32-bit integer type in module, terminate processing
|
||||
// TODO(): Handle non-32-bit integer constants in access chains
|
||||
for (const ir::Instruction& inst : get_module()->types_values())
|
||||
@ -607,29 +573,29 @@ Pass::Status CommonUniformElimPass::Process(ir::IRContext* c) {
|
||||
void CommonUniformElimPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -190,6 +190,11 @@ class CommonUniformElimPass : public Pass {
|
||||
|
||||
// Extensions supported by this pass.
|
||||
std::unordered_set<std::string> extensions_whitelist_;
|
||||
|
||||
// Map from block to its structured successor blocks. See
|
||||
// ComputeStructuredSuccessors() for definition.
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -175,7 +175,7 @@ void DeadBranchElimPass::ComputeBackEdges(
|
||||
bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
// Traverse blocks in structured order
|
||||
std::list<ir::BasicBlock*> structuredOrder;
|
||||
ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
|
||||
cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
|
||||
ComputeBackEdges(structuredOrder);
|
||||
std::unordered_set<ir::BasicBlock*> elimBlocks;
|
||||
bool modified = false;
|
||||
@ -289,7 +289,7 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
pii->ForEachInId(
|
||||
[&deadPreds,&icnt,&lcnt,&lidx,this](uint32_t* idp) {
|
||||
if (icnt % 2 == 1) {
|
||||
if (deadPreds.find(id2block_[*idp]) == deadPreds.end()) {
|
||||
if (deadPreds.find(cfg()->block(*idp)) == deadPreds.end()) {
|
||||
++lcnt;
|
||||
lidx = icnt - 1;
|
||||
}
|
||||
@ -311,7 +311,7 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
pii->ForEachInId(
|
||||
[&deadPreds,&icnt,&phi_in_opnds,&lastId,this](uint32_t* idp) {
|
||||
if (icnt % 2 == 1) {
|
||||
if (deadPreds.find(id2block_[*idp]) == deadPreds.end()) {
|
||||
if (deadPreds.find(cfg()->block(*idp)) == deadPreds.end()) {
|
||||
phi_in_opnds.push_back(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {lastId}});
|
||||
phi_in_opnds.push_back(
|
||||
@ -348,15 +348,6 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
void DeadBranchElimPass::Initialize(ir::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
// Initialize function and block maps
|
||||
id2block_.clear();
|
||||
block2structured_succs_.clear();
|
||||
|
||||
// Initialize block map
|
||||
for (auto& fn : *get_module())
|
||||
for (auto& blk : fn)
|
||||
id2block_[blk.id()] = &blk;
|
||||
|
||||
// Initialize extension whitelist
|
||||
InitExtensions();
|
||||
};
|
||||
|
@ -563,15 +563,18 @@ bool InlinePass::HasMultipleReturns(ir::Function* func) {
|
||||
void InlinePass::ComputeStructuredSuccessors(ir::Function* func) {
|
||||
// If header, make merge block first successor.
|
||||
for (auto& blk : *func) {
|
||||
uint32_t mbid = MergeBlockIdIfAny(blk, nullptr);
|
||||
if (mbid != 0)
|
||||
uint32_t mbid = blk.MergeBlockIdIfAny();
|
||||
if (mbid != 0) {
|
||||
block2structured_succs_[&blk].push_back(id2block_[mbid]);
|
||||
// add true successors
|
||||
}
|
||||
|
||||
// Add true successors.
|
||||
blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) {
|
||||
block2structured_succs_[&blk].push_back(id2block_[sbid]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
InlinePass::GetBlocksFunction InlinePass::StructuredSuccessorsFunction() {
|
||||
return [this](const ir::BasicBlock* block) {
|
||||
return &(block2structured_succs_[block]);
|
||||
|
@ -166,7 +166,8 @@ class InlinePass : public Pass {
|
||||
// Map from function's result id to function.
|
||||
std::unordered_map<uint32_t, ir::Function*> id2function_;
|
||||
|
||||
// Map from block's label id to block.
|
||||
// Map from block's label id to block. TODO(dnovillo): This is superfluous wrt
|
||||
// opt::CFG. It has functionality not present in opt::CFG. Consolidate.
|
||||
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
|
||||
|
||||
// Set of ids of functions with multiple returns.
|
||||
@ -180,6 +181,13 @@ class InlinePass : public Pass {
|
||||
|
||||
// result id for OpConstantFalse
|
||||
uint32_t false_id_;
|
||||
|
||||
// Map from block to its structured successor blocks. See
|
||||
// ComputeStructuredSuccessors() for definition. TODO(dnovillo): This is
|
||||
// superfluous wrt opt::CFG, but it seems to be computed in a slightly
|
||||
// different way in the inliner. Can these be consolidated?
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -132,16 +132,16 @@ void LocalSingleStoreElimPass::CalculateImmediateDominators(
|
||||
// Compute Augmented CFG
|
||||
augmented_successors_map_.clear();
|
||||
augmented_predecessors_map_.clear();
|
||||
successors_map_[&pseudo_exit_block_] = {};
|
||||
predecessors_map_[&pseudo_entry_block_] = {};
|
||||
successors_map_[cfg()->pseudo_exit_block()] = {};
|
||||
predecessors_map_[cfg()->pseudo_entry_block()] = {};
|
||||
auto succ_func = [this](const ir::BasicBlock* b)
|
||||
{ return &successors_map_[b]; };
|
||||
auto pred_func = [this](const ir::BasicBlock* b)
|
||||
{ return &predecessors_map_[b]; };
|
||||
CFA<ir::BasicBlock>::ComputeAugmentedCFG(
|
||||
ordered_blocks,
|
||||
&pseudo_entry_block_,
|
||||
&pseudo_exit_block_,
|
||||
cfg()->pseudo_entry_block(),
|
||||
cfg()->pseudo_exit_block(),
|
||||
&augmented_successors_map_,
|
||||
&augmented_predecessors_map_,
|
||||
succ_func,
|
||||
|
@ -68,6 +68,7 @@ class LocalSingleStoreElimPass : public MemPass {
|
||||
|
||||
// Calculate immediate dominators for |func|'s CFG. Leaves result
|
||||
// in idom_. Entries for augmented CFG (pseudo blocks) are not created.
|
||||
// TODO(dnovillo): Move to new CFG class.
|
||||
void CalculateImmediateDominators(ir::Function* func);
|
||||
|
||||
// Return true if instruction in |blk0| at ordinal position |idx0|
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "mem_pass.h"
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "cfa.h"
|
||||
#include "iterator.h"
|
||||
|
||||
@ -272,19 +273,9 @@ void MemPass::InitSSARewrite(ir::Function* func) {
|
||||
visitedBlocks_.clear();
|
||||
type2undefs_.clear();
|
||||
supported_ref_vars_.clear();
|
||||
block2structured_succs_.clear();
|
||||
label2preds_.clear();
|
||||
label2ssa_map_.clear();
|
||||
phis_to_patch_.clear();
|
||||
|
||||
// Init predecessors
|
||||
label2preds_.clear();
|
||||
for (auto& blk : *func) {
|
||||
uint32_t blkId = blk.id();
|
||||
blk.ForEachSuccessorLabel(
|
||||
[&blkId, this](uint32_t sbid) { label2preds_[sbid].push_back(blkId); });
|
||||
}
|
||||
|
||||
// Collect target (and non-) variable sets. Remove variables with
|
||||
// non-load/store refs from target variable set
|
||||
for (auto& blk : *func) {
|
||||
@ -319,7 +310,7 @@ bool MemPass::IsLiveAfter(uint32_t var_id, uint32_t label) const {
|
||||
void MemPass::SSABlockInitSinglePred(ir::BasicBlock* block_ptr) {
|
||||
// Copy map entry from single predecessor
|
||||
const uint32_t label = block_ptr->id();
|
||||
const uint32_t predLabel = label2preds_[label].front();
|
||||
const uint32_t predLabel = cfg()->preds(label).front();
|
||||
assert(visitedBlocks_.find(predLabel) != visitedBlocks_.end());
|
||||
label2ssa_map_[label] = label2ssa_map_[predLabel];
|
||||
}
|
||||
@ -342,7 +333,7 @@ void MemPass::SSABlockInitLoopHeader(
|
||||
|
||||
// Determine the back-edge label.
|
||||
uint32_t backLabel = 0;
|
||||
for (uint32_t predLabel : label2preds_[label])
|
||||
for (uint32_t predLabel : cfg()->preds(label))
|
||||
if (visitedBlocks_.find(predLabel) == visitedBlocks_.end()) {
|
||||
assert(backLabel == 0);
|
||||
backLabel = predLabel;
|
||||
@ -362,7 +353,7 @@ void MemPass::SSABlockInitLoopHeader(
|
||||
// generated based on order and test results will otherwise vary across
|
||||
// platforms.
|
||||
std::map<uint32_t, uint32_t> liveVars;
|
||||
for (uint32_t predLabel : label2preds_[label]) {
|
||||
for (uint32_t predLabel : cfg()->preds(label)) {
|
||||
for (auto var_val : label2ssa_map_[predLabel]) {
|
||||
uint32_t varId = var_val.first;
|
||||
liveVars[varId] = var_val.second;
|
||||
@ -395,7 +386,7 @@ void MemPass::SSABlockInitLoopHeader(
|
||||
const uint32_t val0Id = var_val.second;
|
||||
bool needsPhi = false;
|
||||
if (val0Id != 0) {
|
||||
for (uint32_t predLabel : label2preds_[label]) {
|
||||
for (uint32_t predLabel : cfg()->preds(label)) {
|
||||
// Skip back edge predecessor.
|
||||
if (predLabel == backLabel) continue;
|
||||
const auto var_val_itr = label2ssa_map_[predLabel].find(varId);
|
||||
@ -426,7 +417,7 @@ void MemPass::SSABlockInitLoopHeader(
|
||||
// use undef.
|
||||
std::vector<ir::Operand> phi_in_operands;
|
||||
uint32_t typeId = GetPointeeTypeId(get_def_use_mgr()->GetDef(varId));
|
||||
for (uint32_t predLabel : label2preds_[label]) {
|
||||
for (uint32_t predLabel : cfg()->preds(label)) {
|
||||
uint32_t valId;
|
||||
if (predLabel == backLabel) {
|
||||
valId = varId;
|
||||
@ -462,7 +453,7 @@ void MemPass::SSABlockInitMultiPred(ir::BasicBlock* block_ptr) {
|
||||
// predecesors. Must be ordered map because phis are generated based on
|
||||
// order and test results will otherwise vary across platforms.
|
||||
std::map<uint32_t, uint32_t> liveVars;
|
||||
for (uint32_t predLabel : label2preds_[label]) {
|
||||
for (uint32_t predLabel : cfg()->preds(label)) {
|
||||
assert(visitedBlocks_.find(predLabel) != visitedBlocks_.end());
|
||||
for (auto var_val : label2ssa_map_[predLabel]) {
|
||||
const uint32_t varId = var_val.first;
|
||||
@ -477,7 +468,7 @@ void MemPass::SSABlockInitMultiPred(ir::BasicBlock* block_ptr) {
|
||||
if (!IsLiveAfter(varId, label)) continue;
|
||||
const uint32_t val0Id = var_val.second;
|
||||
bool differs = false;
|
||||
for (uint32_t predLabel : label2preds_[label]) {
|
||||
for (uint32_t predLabel : cfg()->preds(label)) {
|
||||
const auto var_val_itr = label2ssa_map_[predLabel].find(varId);
|
||||
// Missing values cause a difference because we'll need to create an
|
||||
// undef for that predecessor.
|
||||
@ -499,7 +490,7 @@ void MemPass::SSABlockInitMultiPred(ir::BasicBlock* block_ptr) {
|
||||
// id to the map.
|
||||
std::vector<ir::Operand> phi_in_operands;
|
||||
const uint32_t typeId = GetPointeeTypeId(get_def_use_mgr()->GetDef(varId));
|
||||
for (uint32_t predLabel : label2preds_[label]) {
|
||||
for (uint32_t predLabel : cfg()->preds(label)) {
|
||||
const auto var_val_itr = label2ssa_map_[predLabel].find(varId);
|
||||
// If variable not defined on this path, use undef
|
||||
const uint32_t valId = (var_val_itr != label2ssa_map_[predLabel].end())
|
||||
@ -521,11 +512,11 @@ void MemPass::SSABlockInitMultiPred(ir::BasicBlock* block_ptr) {
|
||||
}
|
||||
|
||||
void MemPass::SSABlockInit(std::list<ir::BasicBlock*>::iterator block_itr) {
|
||||
const size_t numPreds = label2preds_[(*block_itr)->id()].size();
|
||||
const size_t numPreds = cfg()->preds((*block_itr)->id()).size();
|
||||
if (numPreds == 0) return;
|
||||
if (numPreds == 1)
|
||||
SSABlockInitSinglePred(*block_itr);
|
||||
else if (IsLoopHeader(*block_itr))
|
||||
else if ((*block_itr)->IsLoopHeader())
|
||||
SSABlockInitLoopHeader(block_itr);
|
||||
else
|
||||
SSABlockInitMultiPred(*block_itr);
|
||||
@ -557,7 +548,7 @@ bool MemPass::IsTargetVar(uint32_t varId) {
|
||||
}
|
||||
|
||||
void MemPass::PatchPhis(uint32_t header_id, uint32_t back_id) {
|
||||
ir::BasicBlock* header = id2block_[header_id];
|
||||
ir::BasicBlock* header = cfg()->block(header_id);
|
||||
auto phiItr = header->begin();
|
||||
for (; phiItr->opcode() == SpvOpPhi; ++phiItr) {
|
||||
// Only patch phis that we created in a loop header.
|
||||
@ -590,8 +581,8 @@ Pass::Status MemPass::InsertPhiInstructions(ir::Function* func) {
|
||||
// TODO(dnovillo) the current Phi placement mechanism assumes structured
|
||||
// control-flow. This should be generalized
|
||||
// (https://github.com/KhronosGroup/SPIRV-Tools/issues/893).
|
||||
if (!get_module()->HasCapability(SpvCapabilityShader))
|
||||
return Status::SuccessWithoutChange;
|
||||
assert(get_module()->HasCapability(SpvCapabilityShader) &&
|
||||
"This only works on structured control flow");
|
||||
|
||||
// Initialize the data structures used to insert Phi instructions.
|
||||
InitSSARewrite(func);
|
||||
@ -600,10 +591,14 @@ Pass::Status MemPass::InsertPhiInstructions(ir::Function* func) {
|
||||
// simplest?) to make sure all predecessors blocks are processed before
|
||||
// a block itself.
|
||||
std::list<ir::BasicBlock*> structuredOrder;
|
||||
ComputeStructuredOrder(func, &pseudo_entry_block_, &structuredOrder);
|
||||
cfg()->ComputeStructuredOrder(func, cfg()->pseudo_entry_block(),
|
||||
&structuredOrder);
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
|
||||
// Skip pseudo entry block
|
||||
if (*bi == &pseudo_entry_block_) continue;
|
||||
if (cfg()->IsPseudoEntryBlock(*bi)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialize this block's label2ssa_map_ entry using predecessor maps.
|
||||
// Then process all stores and loads of targeted variables.
|
||||
SSABlockInit(bi);
|
||||
@ -725,7 +720,7 @@ void MemPass::RemovePhiOperands(
|
||||
assert(i % 2 == 0 && i < phi->NumOperands() - 1 &&
|
||||
"malformed Phi arguments");
|
||||
|
||||
ir::BasicBlock *in_block = label2block_[phi->GetSingleWordOperand(i + 1)];
|
||||
ir::BasicBlock *in_block = cfg()->block(phi->GetSingleWordOperand(i + 1));
|
||||
if (reachable_blocks.find(in_block) == reachable_blocks.end()) {
|
||||
// If the incoming block is unreachable, remove both operands as this
|
||||
// means that the |phi| has lost an incoming edge.
|
||||
@ -798,7 +793,7 @@ bool MemPass::RemoveUnreachableBlocks(ir::Function* func) {
|
||||
|
||||
auto mark_reachable = [&reachable_blocks, &visited_blocks, &worklist,
|
||||
this](uint32_t label_id) {
|
||||
auto successor = label2block_[label_id];
|
||||
auto successor = cfg()->block(label_id);
|
||||
if (visited_blocks.count(successor) == 0) {
|
||||
reachable_blocks.insert(successor);
|
||||
worklist.push(successor);
|
||||
@ -855,16 +850,13 @@ bool MemPass::CFGCleanup(ir::Function* func) {
|
||||
}
|
||||
|
||||
void MemPass::InitializeCFGCleanup(ir::IRContext* c) {
|
||||
// Initialize block lookup map.
|
||||
label2block_.clear();
|
||||
// Build a map between SSA names to the block they are defined in.
|
||||
//
|
||||
// TODO(dnovillo): This is expensive and unnecessary if ir::Instruction
|
||||
// instances could figure out what basic block they belong to. Remove this
|
||||
// once this is possible.
|
||||
for (auto& fn : *c->module()) {
|
||||
for (auto& block : fn) {
|
||||
label2block_[block.id()] = █
|
||||
|
||||
// Build a map between SSA names to the block they are defined in.
|
||||
// TODO(dnovillo): This is expensive and unnecessary if ir::Instruction
|
||||
// instances could figure out what basic block they belong to. Remove this
|
||||
// once this is possible.
|
||||
block.ForEachInst([this, &block](ir::Instruction* inst) {
|
||||
uint32_t result_id = inst->result_id();
|
||||
if (result_id > 0) {
|
||||
@ -875,7 +867,6 @@ void MemPass::InitializeCFGCleanup(ir::IRContext* c) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -132,10 +132,6 @@ class MemPass : public Pass {
|
||||
// non-target variables.
|
||||
bool IsTargetVar(uint32_t varId);
|
||||
|
||||
// Initialize data structures used by EliminateLocalMultiStore for
|
||||
// function |func|, specifically block predecessors and target variables.
|
||||
void InitSSARewrite(ir::Function* func);
|
||||
|
||||
// Return undef in function for type. Create and insert an undef after the
|
||||
// first non-variable in the function if it doesn't already exist. Add
|
||||
// undef to function undef map.
|
||||
@ -168,6 +164,10 @@ class MemPass : public Pass {
|
||||
// predecessor map.
|
||||
void PatchPhis(uint32_t header_id, uint32_t back_id);
|
||||
|
||||
// Initialize data structures used by EliminateLocalMultiStore for
|
||||
// function |func|, specifically block predecessors and target variables.
|
||||
void InitSSARewrite(ir::Function* func);
|
||||
|
||||
// Initialize label2ssa_map_ entry for block |block_ptr| with single
|
||||
// predecessor.
|
||||
void SSABlockInitSinglePred(ir::BasicBlock* block_ptr);
|
||||
@ -232,10 +232,6 @@ class MemPass : public Pass {
|
||||
// patching of the value for the loop back-edge.
|
||||
std::unordered_set<uint32_t> phis_to_patch_;
|
||||
|
||||
// Map from block's label id to block. TODO(dnovillo): Basic blocks ought to
|
||||
// have basic blocks in their pred/succ list.
|
||||
std::unordered_map<uint32_t, ir::BasicBlock*> label2block_;
|
||||
|
||||
// Map from an instruction result ID to the block that holds it.
|
||||
// TODO(dnovillo): This would be unnecessary if ir::Instruction instances
|
||||
// knew what basic block they belong to.
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
#include "pass.h"
|
||||
|
||||
#include "cfa.h"
|
||||
#include "iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -25,22 +24,12 @@ namespace opt {
|
||||
namespace {
|
||||
|
||||
const uint32_t kEntryPointFunctionIdInIdx = 1;
|
||||
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
|
||||
const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
|
||||
const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
|
||||
const uint32_t kTypePointerTypeIdInIdx = 1;
|
||||
|
||||
// Universal Limit of ResultID + 1
|
||||
const int kInvalidId = 0x400000;
|
||||
|
||||
} // namespace
|
||||
|
||||
Pass::Pass()
|
||||
: pseudo_entry_block_(std::unique_ptr<ir::Instruction>(
|
||||
new ir::Instruction(SpvOpLabel, 0, 0, {}))),
|
||||
pseudo_exit_block_(std::unique_ptr<ir::Instruction>(
|
||||
new ir::Instruction(SpvOpLabel, 0, kInvalidId, {}))),
|
||||
consumer_(nullptr),
|
||||
: consumer_(nullptr),
|
||||
def_use_mgr_(nullptr),
|
||||
next_id_(0),
|
||||
context_(nullptr) {}
|
||||
@ -117,85 +106,12 @@ bool Pass::ProcessCallTreeFromRoots(
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool Pass::IsLoopHeader(ir::BasicBlock* block_ptr) const {
|
||||
auto iItr = block_ptr->end();
|
||||
--iItr;
|
||||
if (iItr == block_ptr->begin())
|
||||
return false;
|
||||
--iItr;
|
||||
return iItr->opcode() == SpvOpLoopMerge;
|
||||
}
|
||||
|
||||
uint32_t Pass::GetPointeeTypeId(const ir::Instruction* ptrInst) const {
|
||||
const uint32_t ptrTypeId = ptrInst->type_id();
|
||||
const ir::Instruction* ptrTypeInst = get_def_use_mgr()->GetDef(ptrTypeId);
|
||||
return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
|
||||
}
|
||||
|
||||
void Pass::ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root,
|
||||
std::list<ir::BasicBlock*>* order) {
|
||||
// Compute structured successors and do DFS
|
||||
ComputeStructuredSuccessors(func);
|
||||
auto ignore_block = [](cbb_ptr) {};
|
||||
auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
|
||||
auto get_structured_successors = [this](const ir::BasicBlock* block) {
|
||||
return &(block2structured_succs_[block]);
|
||||
};
|
||||
|
||||
// TODO(greg-lunarg): Get rid of const_cast by making moving const
|
||||
// out of the cfa.h prototypes and into the invoking code.
|
||||
auto post_order = [&](cbb_ptr b) {
|
||||
order->push_front(const_cast<ir::BasicBlock*>(b));
|
||||
};
|
||||
spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal(
|
||||
root, get_structured_successors, ignore_block, post_order,
|
||||
ignore_edge);
|
||||
}
|
||||
|
||||
void Pass::ComputeStructuredSuccessors(ir::Function *func) {
|
||||
block2structured_succs_.clear();
|
||||
for (auto& blk : *func) {
|
||||
// If no predecessors in function, make successor to pseudo entry
|
||||
if (label2preds_[blk.id()].size() == 0)
|
||||
block2structured_succs_[&pseudo_entry_block_].push_back(&blk);
|
||||
// If header, make merge block first successor and continue block second
|
||||
// successor if there is one.
|
||||
uint32_t cbid;
|
||||
const uint32_t mbid = MergeBlockIdIfAny(blk, &cbid);
|
||||
if (mbid != 0) {
|
||||
block2structured_succs_[&blk].push_back(id2block_[mbid]);
|
||||
if (cbid != 0)
|
||||
block2structured_succs_[&blk].push_back(id2block_[cbid]);
|
||||
}
|
||||
// add true successors
|
||||
blk.ForEachSuccessorLabel([&blk, this](uint32_t sbid) {
|
||||
block2structured_succs_[&blk].push_back(id2block_[sbid]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t Pass::MergeBlockIdIfAny(const ir::BasicBlock& blk, uint32_t* cbid) {
|
||||
auto merge_ii = blk.cend();
|
||||
--merge_ii;
|
||||
if (cbid != nullptr) {
|
||||
*cbid = 0;
|
||||
}
|
||||
uint32_t mbid = 0;
|
||||
if (merge_ii != blk.cbegin()) {
|
||||
--merge_ii;
|
||||
if (merge_ii->opcode() == SpvOpLoopMerge) {
|
||||
mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
|
||||
if (cbid != nullptr) {
|
||||
*cbid =
|
||||
merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
|
||||
}
|
||||
} else if (merge_ii->opcode() == SpvOpSelectionMerge) {
|
||||
mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
|
||||
}
|
||||
}
|
||||
return mbid;
|
||||
}
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -16,18 +16,18 @@
|
||||
#define LIBSPIRV_OPT_PASS_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "cfg.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "basic_block.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -85,6 +85,10 @@ class Pass {
|
||||
// Returns a pointer to the current context for this pass.
|
||||
ir::IRContext* context() const { return context_; }
|
||||
|
||||
// Returns a pointer to the CFG for current module. TODO(dnovillo): This
|
||||
// should belong in IRContext.
|
||||
CFG *cfg() const { return cfg_.get(); }
|
||||
|
||||
// Add to |todo| all ids of functions called in |func|.
|
||||
void AddCalls(ir::Function* func, std::queue<uint32_t>* todo);
|
||||
|
||||
@ -121,37 +125,9 @@ class Pass {
|
||||
context_ = c;
|
||||
next_id_ = context_->IdBound();
|
||||
def_use_mgr_.reset(new analysis::DefUseManager(consumer(), get_module()));
|
||||
block2structured_succs_.clear();
|
||||
label2preds_.clear();
|
||||
id2block_.clear();
|
||||
for (auto& fn : *context_->module()) {
|
||||
for (auto& blk : fn) {
|
||||
id2block_[blk.id()] = &blk;
|
||||
}
|
||||
}
|
||||
cfg_.reset(new CFG(get_module()));
|
||||
}
|
||||
|
||||
// Return true if |block_ptr| points to a loop header block. TODO(dnovillo)
|
||||
// This belongs in a CFG class.
|
||||
bool IsLoopHeader(ir::BasicBlock* block_ptr) const;
|
||||
|
||||
// Compute structured successors for function |func|. A block's structured
|
||||
// successors are the blocks it branches to together with its declared merge
|
||||
// block and continue block if it has them. When order matters, the merge
|
||||
// block and continue block always appear first. This assures correct depth
|
||||
// first search in the presence of early returns and kills. If the successor
|
||||
// vector contain duplicates of the merge or continue blocks, they are safely
|
||||
// ignored by DFS. TODO(dnovillo): This belongs in a CFG class.
|
||||
void ComputeStructuredSuccessors(ir::Function* func);
|
||||
|
||||
// Compute structured block order into |structuredOrder| for |func| starting
|
||||
// at |root|. This order has the property that dominators come before all
|
||||
// blocks they dominate and merge blocks come after all blocks that are in
|
||||
// the control constructs of their header. TODO(dnovillo): This belongs in
|
||||
// a CFG class.
|
||||
void ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root,
|
||||
std::list<ir::BasicBlock*>* order);
|
||||
|
||||
// Return type id for |ptrInst|'s pointee
|
||||
uint32_t GetPointeeTypeId(const ir::Instruction* ptrInst) const;
|
||||
|
||||
@ -163,31 +139,7 @@ class Pass {
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Returns the id of the merge block declared by a merge instruction in this
|
||||
// block, if any. If none, returns zero.
|
||||
uint32_t MergeBlockIdIfAny(const ir::BasicBlock& blk, uint32_t* cbid);
|
||||
|
||||
// Map from block to its structured successor blocks. See
|
||||
// ComputeStructuredSuccessors() for definition.
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
|
||||
// Extra block whose successors are all blocks with no predecessors
|
||||
// in function.
|
||||
ir::BasicBlock pseudo_entry_block_;
|
||||
|
||||
// Augmented CFG Exit Block.
|
||||
ir::BasicBlock pseudo_exit_block_;
|
||||
|
||||
// Map from block's label id to its predecessor blocks ids
|
||||
std::unordered_map<uint32_t, std::vector<uint32_t>> label2preds_;
|
||||
|
||||
// Map from block's label id to block.
|
||||
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
|
||||
|
||||
private:
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
|
||||
MessageConsumer consumer_; // Message consumer.
|
||||
|
||||
// Def-Uses for the module we are processing
|
||||
@ -196,8 +148,11 @@ class Pass {
|
||||
// Next unused ID
|
||||
uint32_t next_id_;
|
||||
|
||||
// The module that the pass is being applied to.
|
||||
// The context that this pass belongs to.
|
||||
ir::IRContext* context_;
|
||||
|
||||
// The CFG for all the functions in this module.
|
||||
std::unique_ptr<CFG> cfg_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
Loading…
Reference in New Issue
Block a user