mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 00:40:14 +00:00
Move the ir namespace to opt. (#1680)
This CL moves the files in opt/ to consistenly be under the opt:: namespace. This frees up the ir:: namespace so it can be used to make a shared ir represenation.
This commit is contained in:
parent
50312ca146
commit
e6b953361d
@ -37,10 +37,10 @@
|
||||
|
||||
namespace spvtools {
|
||||
|
||||
using ir::Instruction;
|
||||
using ir::IRContext;
|
||||
using ir::Module;
|
||||
using ir::Operand;
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
using opt::Module;
|
||||
using opt::Operand;
|
||||
using opt::PassManager;
|
||||
using opt::RemoveDuplicatesPass;
|
||||
using opt::analysis::DecorationManager;
|
||||
@ -73,7 +73,7 @@ using LinkageTable = std::vector<LinkageEntry>;
|
||||
// not be empty either. Furthermore |modules| should not contain any null
|
||||
// pointers.
|
||||
static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
|
||||
std::vector<ir::Module*>* modules,
|
||||
std::vector<opt::Module*>* modules,
|
||||
uint32_t* max_id_bound);
|
||||
|
||||
// Generates the header for the linked module and returns it in |header|.
|
||||
@ -85,9 +85,9 @@ static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
|
||||
// SPIR-V? For now, use the max of all versions found in
|
||||
// the input modules.
|
||||
static spv_result_t GenerateHeader(const MessageConsumer& consumer,
|
||||
const std::vector<ir::Module*>& modules,
|
||||
const std::vector<opt::Module*>& modules,
|
||||
uint32_t max_id_bound,
|
||||
ir::ModuleHeader* header);
|
||||
opt::ModuleHeader* header);
|
||||
|
||||
// Merge all the modules from |in_modules| into a single module owned by
|
||||
// |linked_context|.
|
||||
@ -108,7 +108,7 @@ static spv_result_t MergeModules(const MessageConsumer& consumer,
|
||||
// TODO(pierremoreau): What should be the proper behaviour with built-in
|
||||
// symbols?
|
||||
static spv_result_t GetImportExportPairs(
|
||||
const MessageConsumer& consumer, const ir::IRContext& linked_context,
|
||||
const MessageConsumer& consumer, const opt::IRContext& linked_context,
|
||||
const DefUseManager& def_use_manager,
|
||||
const DecorationManager& decoration_manager, bool allow_partial_linkage,
|
||||
LinkageTable* linkings_to_do);
|
||||
@ -120,7 +120,7 @@ static spv_result_t GetImportExportPairs(
|
||||
// checked.
|
||||
static spv_result_t CheckImportExportCompatibility(
|
||||
const MessageConsumer& consumer, const LinkageTable& linkings_to_do,
|
||||
ir::IRContext* context);
|
||||
opt::IRContext* context);
|
||||
|
||||
// Remove linkage specific instructions, such as prototypes of imported
|
||||
// functions, declarations of imported variables, import (and export if
|
||||
@ -137,12 +137,12 @@ static spv_result_t CheckImportExportCompatibility(
|
||||
static spv_result_t RemoveLinkageSpecificInstructions(
|
||||
const MessageConsumer& consumer, const LinkerOptions& options,
|
||||
const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
|
||||
ir::IRContext* linked_context);
|
||||
opt::IRContext* linked_context);
|
||||
|
||||
// Verify that the unique ids of each instruction in |linked_context| (i.e. the
|
||||
// merged module) are truly unique. Does not check the validity of other ids
|
||||
static spv_result_t VerifyIds(const MessageConsumer& consumer,
|
||||
ir::IRContext* linked_context);
|
||||
opt::IRContext* linked_context);
|
||||
|
||||
spv_result_t Link(const Context& context,
|
||||
const std::vector<std::vector<uint32_t>>& binaries,
|
||||
@ -202,7 +202,7 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries,
|
||||
if (res != SPV_SUCCESS) return res;
|
||||
|
||||
// Phase 2: Generate the header
|
||||
ir::ModuleHeader header;
|
||||
opt::ModuleHeader header;
|
||||
res = GenerateHeader(consumer, modules, max_id_bound, &header);
|
||||
if (res != SPV_SUCCESS) return res;
|
||||
IRContext linked_context(c_context->target_env, consumer);
|
||||
@ -262,7 +262,7 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries,
|
||||
}
|
||||
|
||||
static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
|
||||
std::vector<ir::Module*>* modules,
|
||||
std::vector<opt::Module*>* modules,
|
||||
uint32_t* max_id_bound) {
|
||||
spv_position_t position = {};
|
||||
|
||||
@ -290,7 +290,7 @@ static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
|
||||
<< " " << id_bound << " is the current ID bound.";
|
||||
|
||||
// Invalidate the DefUseManager
|
||||
module->context()->InvalidateAnalyses(ir::IRContext::kAnalysisDefUse);
|
||||
module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse);
|
||||
}
|
||||
++id_bound;
|
||||
if (id_bound > 0x3FFFFF)
|
||||
@ -304,9 +304,9 @@ static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
|
||||
}
|
||||
|
||||
static spv_result_t GenerateHeader(const MessageConsumer& consumer,
|
||||
const std::vector<ir::Module*>& modules,
|
||||
const std::vector<opt::Module*>& modules,
|
||||
uint32_t max_id_bound,
|
||||
ir::ModuleHeader* header) {
|
||||
opt::ModuleHeader* header) {
|
||||
spv_position_t position = {};
|
||||
|
||||
if (modules.empty())
|
||||
@ -477,7 +477,7 @@ static spv_result_t MergeModules(const MessageConsumer& consumer,
|
||||
// Process functions and their basic blocks
|
||||
for (const auto& module : input_modules) {
|
||||
for (const auto& func : *module) {
|
||||
std::unique_ptr<ir::Function> cloned_func(func.Clone(linked_context));
|
||||
std::unique_ptr<opt::Function> cloned_func(func.Clone(linked_context));
|
||||
cloned_func->SetParent(linked_module);
|
||||
linked_module->AddFunction(std::move(cloned_func));
|
||||
}
|
||||
@ -487,7 +487,7 @@ static spv_result_t MergeModules(const MessageConsumer& consumer,
|
||||
}
|
||||
|
||||
static spv_result_t GetImportExportPairs(
|
||||
const MessageConsumer& consumer, const ir::IRContext& linked_context,
|
||||
const MessageConsumer& consumer, const opt::IRContext& linked_context,
|
||||
const DefUseManager& def_use_manager,
|
||||
const DecorationManager& decoration_manager, bool allow_partial_linkage,
|
||||
LinkageTable* linkings_to_do) {
|
||||
@ -584,7 +584,7 @@ static spv_result_t GetImportExportPairs(
|
||||
|
||||
static spv_result_t CheckImportExportCompatibility(
|
||||
const MessageConsumer& consumer, const LinkageTable& linkings_to_do,
|
||||
ir::IRContext* context) {
|
||||
opt::IRContext* context) {
|
||||
spv_position_t position = {};
|
||||
|
||||
// Ensure th import and export types are the same.
|
||||
@ -628,7 +628,7 @@ static spv_result_t CheckImportExportCompatibility(
|
||||
static spv_result_t RemoveLinkageSpecificInstructions(
|
||||
const MessageConsumer& consumer, const LinkerOptions& options,
|
||||
const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
|
||||
ir::IRContext* linked_context) {
|
||||
opt::IRContext* linked_context) {
|
||||
spv_position_t position = {};
|
||||
|
||||
if (decoration_manager == nullptr)
|
||||
@ -746,11 +746,11 @@ static spv_result_t RemoveLinkageSpecificInstructions(
|
||||
}
|
||||
|
||||
spv_result_t VerifyIds(const MessageConsumer& consumer,
|
||||
ir::IRContext* linked_context) {
|
||||
opt::IRContext* linked_context) {
|
||||
std::unordered_set<uint32_t> ids;
|
||||
bool ok = true;
|
||||
linked_context->module()->ForEachInst(
|
||||
[&ids, &ok](const ir::Instruction* inst) {
|
||||
[&ids, &ok](const opt::Instruction* inst) {
|
||||
ok &= ids.insert(inst->unique_id()).second;
|
||||
});
|
||||
|
||||
|
@ -50,8 +50,8 @@ const uint32_t kCopyMemorySourceAddrInIdx = 1;
|
||||
// SpvOpDecorateStringGOOGLE
|
||||
// SpvOpDecorationGroup
|
||||
struct DecorationLess {
|
||||
bool operator()(const ir::Instruction* lhs,
|
||||
const ir::Instruction* rhs) const {
|
||||
bool operator()(const opt::Instruction* lhs,
|
||||
const opt::Instruction* rhs) const {
|
||||
assert(lhs && rhs);
|
||||
SpvOp lhsOp = lhs->opcode();
|
||||
SpvOp rhsOp = rhs->opcode();
|
||||
@ -82,11 +82,11 @@ struct DecorationLess {
|
||||
|
||||
bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
|
||||
if (varId == 0) return false;
|
||||
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
|
||||
const opt::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
|
||||
const SpvOp op = varInst->opcode();
|
||||
if (op != SpvOpVariable) return false;
|
||||
const uint32_t varTypeId = varInst->type_id();
|
||||
const ir::Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
|
||||
const opt::Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
|
||||
if (varTypeInst->opcode() != SpvOpTypePointer) return false;
|
||||
return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
|
||||
storageClass;
|
||||
@ -105,7 +105,7 @@ bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddStores(uint32_t ptrId) {
|
||||
get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId](ir::Instruction* user) {
|
||||
get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId](opt::Instruction* user) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
@ -140,7 +140,7 @@ bool AggressiveDCEPass::AllExtensionsSupported() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsDead(ir::Instruction* inst) {
|
||||
bool AggressiveDCEPass::IsDead(opt::Instruction* inst) {
|
||||
if (IsLive(inst)) return false;
|
||||
if (inst->IsBranch() && !IsStructuredHeader(context()->get_instr_block(inst),
|
||||
nullptr, nullptr, nullptr))
|
||||
@ -148,16 +148,16 @@ bool AggressiveDCEPass::IsDead(ir::Instruction* inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsTargetDead(ir::Instruction* inst) {
|
||||
bool AggressiveDCEPass::IsTargetDead(opt::Instruction* inst) {
|
||||
const uint32_t tId = inst->GetSingleWordInOperand(0);
|
||||
ir::Instruction* tInst = get_def_use_mgr()->GetDef(tId);
|
||||
if (ir::IsAnnotationInst(tInst->opcode())) {
|
||||
opt::Instruction* tInst = get_def_use_mgr()->GetDef(tId);
|
||||
if (opt::IsAnnotationInst(tInst->opcode())) {
|
||||
// This must be a decoration group. We go through annotations in a specific
|
||||
// order. So if this is not used by any group or group member decorates, it
|
||||
// is dead.
|
||||
assert(tInst->opcode() == SpvOpDecorationGroup);
|
||||
bool dead = true;
|
||||
get_def_use_mgr()->ForEachUser(tInst, [&dead](ir::Instruction* user) {
|
||||
get_def_use_mgr()->ForEachUser(tInst, [&dead](opt::Instruction* user) {
|
||||
if (user->opcode() == SpvOpGroupDecorate ||
|
||||
user->opcode() == SpvOpGroupMemberDecorate)
|
||||
dead = false;
|
||||
@ -178,14 +178,14 @@ void AggressiveDCEPass::ProcessLoad(uint32_t varId) {
|
||||
live_local_vars_.insert(varId);
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsStructuredHeader(ir::BasicBlock* bp,
|
||||
ir::Instruction** mergeInst,
|
||||
ir::Instruction** branchInst,
|
||||
bool AggressiveDCEPass::IsStructuredHeader(opt::BasicBlock* bp,
|
||||
opt::Instruction** mergeInst,
|
||||
opt::Instruction** branchInst,
|
||||
uint32_t* mergeBlockId) {
|
||||
if (!bp) return false;
|
||||
ir::Instruction* mi = bp->GetMergeInst();
|
||||
opt::Instruction* mi = bp->GetMergeInst();
|
||||
if (mi == nullptr) return false;
|
||||
ir::Instruction* bri = &*bp->tail();
|
||||
opt::Instruction* bri = &*bp->tail();
|
||||
if (branchInst != nullptr) *branchInst = bri;
|
||||
if (mergeInst != nullptr) *mergeInst = mi;
|
||||
if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0);
|
||||
@ -193,11 +193,11 @@ bool AggressiveDCEPass::IsStructuredHeader(ir::BasicBlock* bp,
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::ComputeBlock2HeaderMaps(
|
||||
std::list<ir::BasicBlock*>& structuredOrder) {
|
||||
std::list<opt::BasicBlock*>& structuredOrder) {
|
||||
block2headerBranch_.clear();
|
||||
branch2merge_.clear();
|
||||
structured_order_index_.clear();
|
||||
std::stack<ir::Instruction*> currentHeaderBranch;
|
||||
std::stack<opt::Instruction*> currentHeaderBranch;
|
||||
currentHeaderBranch.push(nullptr);
|
||||
uint32_t currentMergeBlockId = 0;
|
||||
uint32_t index = 0;
|
||||
@ -208,12 +208,12 @@ void AggressiveDCEPass::ComputeBlock2HeaderMaps(
|
||||
// we are leaving the current construct so we must update state
|
||||
if ((*bi)->id() == currentMergeBlockId) {
|
||||
currentHeaderBranch.pop();
|
||||
ir::Instruction* chb = currentHeaderBranch.top();
|
||||
opt::Instruction* chb = currentHeaderBranch.top();
|
||||
if (chb != nullptr)
|
||||
currentMergeBlockId = branch2merge_[chb]->GetSingleWordInOperand(0);
|
||||
}
|
||||
ir::Instruction* mergeInst;
|
||||
ir::Instruction* branchInst;
|
||||
opt::Instruction* mergeInst;
|
||||
opt::Instruction* branchInst;
|
||||
uint32_t mergeBlockId;
|
||||
bool is_header =
|
||||
IsStructuredHeader(*bi, &mergeInst, &branchInst, &mergeBlockId);
|
||||
@ -235,8 +235,8 @@ void AggressiveDCEPass::ComputeBlock2HeaderMaps(
|
||||
}
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddBranch(uint32_t labelId, ir::BasicBlock* bp) {
|
||||
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
|
||||
void AggressiveDCEPass::AddBranch(uint32_t labelId, opt::BasicBlock* bp) {
|
||||
std::unique_ptr<opt::Instruction> newBranch(new opt::Instruction(
|
||||
context(), SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
|
||||
context()->AnalyzeDefUse(&*newBranch);
|
||||
@ -245,35 +245,35 @@ void AggressiveDCEPass::AddBranch(uint32_t labelId, ir::BasicBlock* bp) {
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
|
||||
ir::Instruction* loopMerge) {
|
||||
ir::BasicBlock* header = context()->get_instr_block(loopMerge);
|
||||
opt::Instruction* loopMerge) {
|
||||
opt::BasicBlock* header = context()->get_instr_block(loopMerge);
|
||||
uint32_t headerIndex = structured_order_index_[header];
|
||||
const uint32_t mergeId =
|
||||
loopMerge->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx);
|
||||
ir::BasicBlock* merge = context()->get_instr_block(mergeId);
|
||||
opt::BasicBlock* merge = context()->get_instr_block(mergeId);
|
||||
uint32_t mergeIndex = structured_order_index_[merge];
|
||||
get_def_use_mgr()->ForEachUser(
|
||||
mergeId, [headerIndex, mergeIndex, this](ir::Instruction* user) {
|
||||
mergeId, [headerIndex, mergeIndex, this](opt::Instruction* user) {
|
||||
if (!user->IsBranch()) return;
|
||||
ir::BasicBlock* block = context()->get_instr_block(user);
|
||||
opt::BasicBlock* block = context()->get_instr_block(user);
|
||||
uint32_t index = structured_order_index_[block];
|
||||
if (headerIndex < index && index < mergeIndex) {
|
||||
// This is a break from the loop.
|
||||
AddToWorklist(user);
|
||||
// Add branch's merge if there is one.
|
||||
ir::Instruction* userMerge = branch2merge_[user];
|
||||
opt::Instruction* userMerge = branch2merge_[user];
|
||||
if (userMerge != nullptr) AddToWorklist(userMerge);
|
||||
}
|
||||
});
|
||||
const uint32_t contId =
|
||||
loopMerge->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
|
||||
get_def_use_mgr()->ForEachUser(contId, [&contId,
|
||||
this](ir::Instruction* user) {
|
||||
this](opt::Instruction* user) {
|
||||
SpvOp op = user->opcode();
|
||||
if (op == SpvOpBranchConditional || op == SpvOpSwitch) {
|
||||
// A conditional branch or switch can only be a continue if it does not
|
||||
// have a merge instruction or its merge block is not the continue block.
|
||||
ir::Instruction* hdrMerge = branch2merge_[user];
|
||||
opt::Instruction* hdrMerge = branch2merge_[user];
|
||||
if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) {
|
||||
uint32_t hdrMergeId =
|
||||
hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
|
||||
@ -284,10 +284,10 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
|
||||
} else if (op == SpvOpBranch) {
|
||||
// An unconditional branch can only be a continue if it is not
|
||||
// branching to its own merge block.
|
||||
ir::BasicBlock* blk = context()->get_instr_block(user);
|
||||
ir::Instruction* hdrBranch = block2headerBranch_[blk];
|
||||
opt::BasicBlock* blk = context()->get_instr_block(user);
|
||||
opt::Instruction* hdrBranch = block2headerBranch_[blk];
|
||||
if (hdrBranch == nullptr) return;
|
||||
ir::Instruction* hdrMerge = branch2merge_[hdrBranch];
|
||||
opt::Instruction* hdrMerge = branch2merge_[hdrBranch];
|
||||
if (hdrMerge->opcode() == SpvOpLoopMerge) return;
|
||||
uint32_t hdrMergeId =
|
||||
hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
|
||||
@ -299,17 +299,17 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
|
||||
});
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
bool AggressiveDCEPass::AggressiveDCE(opt::Function* func) {
|
||||
// Mark function parameters as live.
|
||||
AddToWorklist(&func->DefInst());
|
||||
func->ForEachParam(
|
||||
[this](const ir::Instruction* param) {
|
||||
AddToWorklist(const_cast<ir::Instruction*>(param));
|
||||
[this](const opt::Instruction* param) {
|
||||
AddToWorklist(const_cast<opt::Instruction*>(param));
|
||||
},
|
||||
false);
|
||||
|
||||
// Compute map from block to controlling conditional branch
|
||||
std::list<ir::BasicBlock*> structuredOrder;
|
||||
std::list<opt::BasicBlock*> structuredOrder;
|
||||
cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
|
||||
ComputeBlock2HeaderMaps(structuredOrder);
|
||||
bool modified = false;
|
||||
@ -404,10 +404,10 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
for (auto& ps : private_stores_) AddToWorklist(ps);
|
||||
// Perform closure on live instruction set.
|
||||
while (!worklist_.empty()) {
|
||||
ir::Instruction* liveInst = worklist_.front();
|
||||
opt::Instruction* liveInst = worklist_.front();
|
||||
// Add all operand instructions if not already live
|
||||
liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) {
|
||||
ir::Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
|
||||
opt::Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
|
||||
// Do not add label if an operand of a branch. This is not needed
|
||||
// as part of live code discovery and can create false live code,
|
||||
// for example, the branch to a header of a loop.
|
||||
@ -421,11 +421,11 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
// conditional branch and its merge. Any containing control construct
|
||||
// is marked live when the merge and branch are processed out of the
|
||||
// worklist.
|
||||
ir::BasicBlock* blk = context()->get_instr_block(liveInst);
|
||||
ir::Instruction* branchInst = block2headerBranch_[blk];
|
||||
opt::BasicBlock* blk = context()->get_instr_block(liveInst);
|
||||
opt::Instruction* branchInst = block2headerBranch_[blk];
|
||||
if (branchInst != nullptr) {
|
||||
AddToWorklist(branchInst);
|
||||
ir::Instruction* mergeInst = branch2merge_[branchInst];
|
||||
opt::Instruction* mergeInst = branch2merge_[branchInst];
|
||||
AddToWorklist(mergeInst);
|
||||
// If in a loop, mark all its break and continue instructions live
|
||||
if (mergeInst->opcode() == SpvOpLoopMerge)
|
||||
@ -476,7 +476,8 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
// Kill dead instructions and remember dead blocks
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) {
|
||||
uint32_t mergeBlockId = 0;
|
||||
(*bi)->ForEachInst([this, &modified, &mergeBlockId](ir::Instruction* inst) {
|
||||
(*bi)->ForEachInst([this, &modified,
|
||||
&mergeBlockId](opt::Instruction* inst) {
|
||||
if (!IsDead(inst)) return;
|
||||
if (inst->opcode() == SpvOpLabel) return;
|
||||
// If dead instruction is selection merge, remember merge block
|
||||
@ -502,7 +503,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::Initialize(ir::IRContext* c) {
|
||||
void AggressiveDCEPass::Initialize(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
// Initialize extensions whitelist
|
||||
@ -549,7 +550,7 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
InitializeModuleScopeLiveInstructions();
|
||||
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) { return AggressiveDCE(fp); };
|
||||
ProcessFunction pfn = [this](opt::Function* fp) { return AggressiveDCE(fp); };
|
||||
modified |= ProcessEntryPointCallTree(pfn, get_module());
|
||||
|
||||
// Process module-level instructions. Now that all live instructions have
|
||||
@ -562,7 +563,7 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
}
|
||||
|
||||
// Cleanup all CFG including all unreachable blocks.
|
||||
ProcessFunction cleanup = [this](ir::Function* f) { return CFGCleanup(f); };
|
||||
ProcessFunction cleanup = [this](opt::Function* f) { return CFGCleanup(f); };
|
||||
modified |= ProcessEntryPointCallTree(cleanup, get_module());
|
||||
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
@ -572,8 +573,8 @@ bool AggressiveDCEPass::EliminateDeadFunctions() {
|
||||
// Identify live functions first. Those that are not live
|
||||
// are dead. ADCE is disabled for non-shaders so we do not check for exported
|
||||
// functions here.
|
||||
std::unordered_set<const ir::Function*> live_function_set;
|
||||
ProcessFunction mark_live = [&live_function_set](ir::Function* fp) {
|
||||
std::unordered_set<const opt::Function*> live_function_set;
|
||||
ProcessFunction mark_live = [&live_function_set](opt::Function* fp) {
|
||||
live_function_set.insert(fp);
|
||||
return false;
|
||||
};
|
||||
@ -594,10 +595,10 @@ bool AggressiveDCEPass::EliminateDeadFunctions() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::EliminateFunction(ir::Function* func) {
|
||||
void AggressiveDCEPass::EliminateFunction(opt::Function* func) {
|
||||
// Remove all of the instruction in the function body
|
||||
func->ForEachInst(
|
||||
[this](ir::Instruction* inst) { context()->KillInst(inst); }, true);
|
||||
[this](opt::Instruction* inst) { context()->KillInst(inst); }, true);
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
@ -605,7 +606,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
// This must be done before killing the instructions, otherwise there are
|
||||
// dead objects in the def/use database.
|
||||
bool modified = false;
|
||||
ir::Instruction* instruction = &*get_module()->debug2_begin();
|
||||
opt::Instruction* instruction = &*get_module()->debug2_begin();
|
||||
while (instruction) {
|
||||
if (instruction->opcode() != SpvOpName) {
|
||||
instruction = instruction->NextNode();
|
||||
@ -623,7 +624,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
// This code removes all unnecessary decorations safely (see #1174). It also
|
||||
// does so in a more efficient manner than deleting them only as the targets
|
||||
// are deleted.
|
||||
std::vector<ir::Instruction*> annotations;
|
||||
std::vector<opt::Instruction*> annotations;
|
||||
for (auto& inst : get_module()->annotations()) annotations.push_back(&inst);
|
||||
std::sort(annotations.begin(), annotations.end(), DecorationLess());
|
||||
for (auto annotation : annotations) {
|
||||
@ -642,7 +643,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
// target. If all targets are dead, remove this decoration.
|
||||
bool dead = true;
|
||||
for (uint32_t i = 1; i < annotation->NumOperands();) {
|
||||
ir::Instruction* opInst =
|
||||
opt::Instruction* opInst =
|
||||
get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
|
||||
if (IsDead(opInst)) {
|
||||
// Don't increment |i|.
|
||||
@ -665,7 +666,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
// decoration.
|
||||
bool dead = true;
|
||||
for (uint32_t i = 1; i < annotation->NumOperands();) {
|
||||
ir::Instruction* opInst =
|
||||
opt::Instruction* opInst =
|
||||
get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
|
||||
if (IsDead(opInst)) {
|
||||
// Don't increment |i|.
|
||||
@ -710,7 +711,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
|
||||
AggressiveDCEPass::AggressiveDCEPass() {}
|
||||
|
||||
Pass::Status AggressiveDCEPass::Process(ir::IRContext* c) {
|
||||
Pass::Status AggressiveDCEPass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -35,19 +35,19 @@ namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class AggressiveDCEPass : public MemPass {
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
using cbb_ptr = const opt::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
std::function<std::vector<opt::BasicBlock*>*(const opt::BasicBlock*)>;
|
||||
|
||||
AggressiveDCEPass();
|
||||
const char* name() const override { return "eliminate-dead-code-aggressive"; }
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -61,19 +61,19 @@ class AggressiveDCEPass : public MemPass {
|
||||
bool IsLocalVar(uint32_t varId);
|
||||
|
||||
// Return true if |inst| is marked live.
|
||||
bool IsLive(const ir::Instruction* inst) const {
|
||||
bool IsLive(const opt::Instruction* inst) const {
|
||||
return live_insts_.Get(inst->unique_id());
|
||||
}
|
||||
|
||||
// Returns true if |inst| is dead.
|
||||
bool IsDead(ir::Instruction* inst);
|
||||
bool IsDead(opt::Instruction* inst);
|
||||
|
||||
// Adds entry points, execution modes and workgroup size decorations to the
|
||||
// worklist for processing with the first function.
|
||||
void InitializeModuleScopeLiveInstructions();
|
||||
|
||||
// Add |inst| to worklist_ and live_insts_.
|
||||
void AddToWorklist(ir::Instruction* inst) {
|
||||
void AddToWorklist(opt::Instruction* inst) {
|
||||
if (!live_insts_.Set(inst->unique_id())) {
|
||||
worklist_.push(inst);
|
||||
}
|
||||
@ -92,7 +92,7 @@ class AggressiveDCEPass : public MemPass {
|
||||
// Returns true if the target of |inst| is dead. An instruction is dead if
|
||||
// its result id is used in decoration or debug instructions only. |inst| is
|
||||
// assumed to be OpName, OpMemberName or an annotation instruction.
|
||||
bool IsTargetDead(ir::Instruction* inst);
|
||||
bool IsTargetDead(opt::Instruction* inst);
|
||||
|
||||
// If |varId| is local, mark all stores of varId as live.
|
||||
void ProcessLoad(uint32_t varId);
|
||||
@ -102,19 +102,20 @@ class AggressiveDCEPass : public MemPass {
|
||||
// merge block if they are not nullptr. Any of |mergeInst|, |branchInst| or
|
||||
// |mergeBlockId| may be a null pointer. Returns false if |bp| is a null
|
||||
// pointer.
|
||||
bool IsStructuredHeader(ir::BasicBlock* bp, ir::Instruction** mergeInst,
|
||||
ir::Instruction** branchInst, uint32_t* mergeBlockId);
|
||||
bool IsStructuredHeader(opt::BasicBlock* bp, opt::Instruction** mergeInst,
|
||||
opt::Instruction** branchInst,
|
||||
uint32_t* mergeBlockId);
|
||||
|
||||
// Initialize block2headerBranch_ and branch2merge_ using |structuredOrder|
|
||||
// to order blocks.
|
||||
void ComputeBlock2HeaderMaps(std::list<ir::BasicBlock*>& structuredOrder);
|
||||
void ComputeBlock2HeaderMaps(std::list<opt::BasicBlock*>& structuredOrder);
|
||||
|
||||
// Add branch to |labelId| to end of block |bp|.
|
||||
void AddBranch(uint32_t labelId, ir::BasicBlock* bp);
|
||||
void AddBranch(uint32_t labelId, opt::BasicBlock* bp);
|
||||
|
||||
// Add all break and continue branches in the loop associated with
|
||||
// |mergeInst| to worklist if not already live
|
||||
void AddBreaksAndContinuesToWorklist(ir::Instruction* mergeInst);
|
||||
void AddBreaksAndContinuesToWorklist(opt::Instruction* mergeInst);
|
||||
|
||||
// Eliminates dead debug2 and annotation instructions. Marks dead globals for
|
||||
// removal (e.g. types, constants and variables).
|
||||
@ -124,7 +125,7 @@ class AggressiveDCEPass : public MemPass {
|
||||
bool EliminateDeadFunctions();
|
||||
|
||||
// Removes |func| from the module and deletes all its instructions.
|
||||
void EliminateFunction(ir::Function* func);
|
||||
void EliminateFunction(opt::Function* func);
|
||||
|
||||
// For function |func|, mark all Stores to non-function-scope variables
|
||||
// and block terminating instructions as live. Recursively mark the values
|
||||
@ -135,9 +136,9 @@ class AggressiveDCEPass : public MemPass {
|
||||
// existing control structures will remain. This can leave not-insignificant
|
||||
// sequences of ultimately useless code.
|
||||
// TODO(): Remove useless control constructs.
|
||||
bool AggressiveDCE(ir::Function* func);
|
||||
bool AggressiveDCE(opt::Function* func);
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
// True if current function has a call instruction contained in it
|
||||
@ -154,22 +155,22 @@ class AggressiveDCEPass : public MemPass {
|
||||
// If we don't know, then add it to this list. Instructions are
|
||||
// removed from this list as the algorithm traces side effects,
|
||||
// building up the live instructions set |live_insts_|.
|
||||
std::queue<ir::Instruction*> worklist_;
|
||||
std::queue<opt::Instruction*> worklist_;
|
||||
|
||||
// Map from block to the branch instruction in the header of the most
|
||||
// immediate controlling structured if or loop. A loop header block points
|
||||
// to its own branch instruction. An if-selection block points to the branch
|
||||
// of an enclosing construct's header, if one exists.
|
||||
std::unordered_map<ir::BasicBlock*, ir::Instruction*> block2headerBranch_;
|
||||
std::unordered_map<opt::BasicBlock*, opt::Instruction*> block2headerBranch_;
|
||||
|
||||
// Maps basic block to their index in the structured order traversal.
|
||||
std::unordered_map<ir::BasicBlock*, uint32_t> structured_order_index_;
|
||||
std::unordered_map<opt::BasicBlock*, uint32_t> structured_order_index_;
|
||||
|
||||
// Map from branch to its associated merge instruction, if any
|
||||
std::unordered_map<ir::Instruction*, ir::Instruction*> branch2merge_;
|
||||
std::unordered_map<opt::Instruction*, opt::Instruction*> branch2merge_;
|
||||
|
||||
// Store instructions to variables of private storage
|
||||
std::vector<ir::Instruction*> private_stores_;
|
||||
std::vector<opt::Instruction*> private_stores_;
|
||||
|
||||
// Live Instructions
|
||||
utils::BitVector live_insts_;
|
||||
@ -179,7 +180,7 @@ class AggressiveDCEPass : public MemPass {
|
||||
|
||||
// List of instructions to delete. Deletion is delayed until debug and
|
||||
// annotation instructions are processed.
|
||||
std::vector<ir::Instruction*> to_kill_;
|
||||
std::vector<opt::Instruction*> to_kill_;
|
||||
|
||||
// Extensions supported by this pass.
|
||||
std::unordered_set<std::string> extensions_whitelist_;
|
||||
|
@ -23,8 +23,7 @@
|
||||
#include <ostream>
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
|
||||
@ -91,7 +90,7 @@ Instruction* BasicBlock::GetLoopMergeInst() {
|
||||
}
|
||||
|
||||
void BasicBlock::KillAllInsts(bool killLabel) {
|
||||
ForEachInst([killLabel](ir::Instruction* ip) {
|
||||
ForEachInst([killLabel](opt::Instruction* ip) {
|
||||
if (killLabel || ip->opcode() != SpvOpLabel) {
|
||||
ip->context()->KillInst(ip);
|
||||
}
|
||||
@ -140,7 +139,7 @@ void BasicBlock::ForEachSuccessorLabel(
|
||||
}
|
||||
}
|
||||
|
||||
bool BasicBlock::IsSuccessor(const ir::BasicBlock* block) const {
|
||||
bool BasicBlock::IsSuccessor(const opt::BasicBlock* block) const {
|
||||
uint32_t succId = block->id();
|
||||
bool isSuccessor = false;
|
||||
ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) {
|
||||
@ -196,7 +195,7 @@ std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
|
||||
|
||||
std::string BasicBlock::PrettyPrint(uint32_t options) const {
|
||||
std::ostringstream str;
|
||||
ForEachInst([&str, options](const ir::Instruction* inst) {
|
||||
ForEachInst([&str, options](const opt::Instruction* inst) {
|
||||
str << inst->PrettyPrint(options);
|
||||
if (!IsTerminatorInst(inst->opcode())) {
|
||||
str << std::endl;
|
||||
@ -210,13 +209,13 @@ BasicBlock* BasicBlock::SplitBasicBlock(IRContext* context, uint32_t label_id,
|
||||
assert(!insts_.empty());
|
||||
|
||||
BasicBlock* new_block = new BasicBlock(MakeUnique<Instruction>(
|
||||
context, SpvOpLabel, 0, label_id, std::initializer_list<ir::Operand>{}));
|
||||
context, SpvOpLabel, 0, label_id, std::initializer_list<opt::Operand>{}));
|
||||
|
||||
new_block->insts_.Splice(new_block->end(), &insts_, iter, end());
|
||||
new_block->SetParent(GetParent());
|
||||
|
||||
if (context->AreAnalysesValid(ir::IRContext::kAnalysisInstrToBlockMapping)) {
|
||||
new_block->ForEachInst([new_block, context](ir::Instruction* inst) {
|
||||
if (context->AreAnalysesValid(opt::IRContext::kAnalysisInstrToBlockMapping)) {
|
||||
new_block->ForEachInst([new_block, context](opt::Instruction* inst) {
|
||||
context->set_instr_block(inst, new_block);
|
||||
});
|
||||
}
|
||||
@ -224,5 +223,5 @@ BasicBlock* BasicBlock::SplitBasicBlock(IRContext* context, uint32_t label_id,
|
||||
return new_block;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include "iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
class Function;
|
||||
class IRContext;
|
||||
@ -159,14 +159,14 @@ class BasicBlock {
|
||||
void ForEachSuccessorLabel(const std::function<void(uint32_t*)>& f);
|
||||
|
||||
// Returns true if |block| is a direct successor of |this|.
|
||||
bool IsSuccessor(const ir::BasicBlock* block) const;
|
||||
bool IsSuccessor(const opt::BasicBlock* block) const;
|
||||
|
||||
// Runs the given function |f| on the merge and continue label, if any
|
||||
void ForMergeAndContinueLabel(const std::function<void(const uint32_t)>& f);
|
||||
|
||||
// Returns true if this basic block has any Phi instructions.
|
||||
bool HasPhiInstructions() {
|
||||
return !WhileEachPhiInst([](ir::Instruction*) { return false; });
|
||||
return !WhileEachPhiInst([](opt::Instruction*) { return false; });
|
||||
}
|
||||
|
||||
// Return true if this block is a loop header block.
|
||||
@ -313,7 +313,7 @@ inline void BasicBlock::ForEachPhiInst(
|
||||
run_on_debug_line_insts);
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_BASIC_BLOCK_H_
|
||||
|
@ -22,9 +22,9 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
void BlockMergePass::KillInstAndName(ir::Instruction* inst) {
|
||||
std::vector<ir::Instruction*> to_kill;
|
||||
get_def_use_mgr()->ForEachUser(inst, [&to_kill](ir::Instruction* user) {
|
||||
void BlockMergePass::KillInstAndName(opt::Instruction* inst) {
|
||||
std::vector<opt::Instruction*> to_kill;
|
||||
get_def_use_mgr()->ForEachUser(inst, [&to_kill](opt::Instruction* user) {
|
||||
if (user->opcode() == SpvOpName) {
|
||||
to_kill.push_back(user);
|
||||
}
|
||||
@ -35,13 +35,13 @@ void BlockMergePass::KillInstAndName(ir::Instruction* inst) {
|
||||
context()->KillInst(inst);
|
||||
}
|
||||
|
||||
bool BlockMergePass::MergeBlocks(ir::Function* func) {
|
||||
bool BlockMergePass::MergeBlocks(opt::Function* func) {
|
||||
bool modified = false;
|
||||
for (auto bi = func->begin(); bi != func->end();) {
|
||||
// Find block with single successor which has no other predecessors.
|
||||
auto ii = bi->end();
|
||||
--ii;
|
||||
ir::Instruction* br = &*ii;
|
||||
opt::Instruction* br = &*ii;
|
||||
if (br->opcode() != SpvOpBranch) {
|
||||
++bi;
|
||||
continue;
|
||||
@ -69,14 +69,14 @@ bool BlockMergePass::MergeBlocks(ir::Function* func) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ir::Instruction* merge_inst = bi->GetMergeInst();
|
||||
opt::Instruction* merge_inst = bi->GetMergeInst();
|
||||
if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) {
|
||||
// If this is a header block and the successor is not its merge, we must
|
||||
// be careful about which blocks we are willing to merge together.
|
||||
// OpLoopMerge must be followed by a conditional or unconditional branch.
|
||||
// The merge must be a loop merge because a selection merge cannot be
|
||||
// followed by an unconditional branch.
|
||||
ir::BasicBlock* succ_block = context()->get_instr_block(lab_id);
|
||||
opt::BasicBlock* succ_block = context()->get_instr_block(lab_id);
|
||||
SpvOp succ_term_op = succ_block->terminator()->opcode();
|
||||
assert(merge_inst->opcode() == SpvOpLoopMerge);
|
||||
if (succ_term_op != SpvOpBranch &&
|
||||
@ -122,7 +122,7 @@ bool BlockMergePass::MergeBlocks(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool BlockMergePass::IsHeader(ir::BasicBlock* block) {
|
||||
bool BlockMergePass::IsHeader(opt::BasicBlock* block) {
|
||||
return block->GetMergeInst() != nullptr;
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ bool BlockMergePass::IsHeader(uint32_t id) {
|
||||
}
|
||||
|
||||
bool BlockMergePass::IsMerge(uint32_t id) {
|
||||
return !get_def_use_mgr()->WhileEachUse(id, [](ir::Instruction* user,
|
||||
return !get_def_use_mgr()->WhileEachUse(id, [](opt::Instruction* user,
|
||||
uint32_t index) {
|
||||
SpvOp op = user->opcode();
|
||||
if ((op == SpvOpLoopMerge || op == SpvOpSelectionMerge) && index == 0u) {
|
||||
@ -141,22 +141,22 @@ bool BlockMergePass::IsMerge(uint32_t id) {
|
||||
});
|
||||
}
|
||||
|
||||
bool BlockMergePass::IsMerge(ir::BasicBlock* block) {
|
||||
bool BlockMergePass::IsMerge(opt::BasicBlock* block) {
|
||||
return IsMerge(block->id());
|
||||
}
|
||||
|
||||
void BlockMergePass::Initialize(ir::IRContext* c) { InitializeProcessing(c); }
|
||||
void BlockMergePass::Initialize(opt::IRContext* c) { InitializeProcessing(c); }
|
||||
|
||||
Pass::Status BlockMergePass::ProcessImpl() {
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) { return MergeBlocks(fp); };
|
||||
ProcessFunction pfn = [this](opt::Function* fp) { return MergeBlocks(fp); };
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
BlockMergePass::BlockMergePass() {}
|
||||
|
||||
Pass::Status BlockMergePass::Process(ir::IRContext* c) {
|
||||
Pass::Status BlockMergePass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -38,33 +38,33 @@ class BlockMergePass : public Pass {
|
||||
public:
|
||||
BlockMergePass();
|
||||
const char* name() const override { return "merge-blocks"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
virtual ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::kAnalysisDecorations |
|
||||
ir::IRContext::kAnalysisCombinators |
|
||||
ir::IRContext::kAnalysisNameMap;
|
||||
Status Process(opt::IRContext*) override;
|
||||
virtual opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::kAnalysisDecorations |
|
||||
opt::IRContext::kAnalysisCombinators |
|
||||
opt::IRContext::kAnalysisNameMap;
|
||||
}
|
||||
|
||||
private:
|
||||
// Kill any OpName instruction referencing |inst|, then kill |inst|.
|
||||
void KillInstAndName(ir::Instruction* inst);
|
||||
void KillInstAndName(opt::Instruction* inst);
|
||||
|
||||
// Search |func| for blocks which have a single Branch to a block
|
||||
// with no other predecessors. Merge these blocks into a single block.
|
||||
bool MergeBlocks(ir::Function* func);
|
||||
bool MergeBlocks(opt::Function* func);
|
||||
|
||||
// Returns true if |block| (or |id|) contains a merge instruction.
|
||||
bool IsHeader(ir::BasicBlock* block);
|
||||
bool IsHeader(opt::BasicBlock* block);
|
||||
bool IsHeader(uint32_t id);
|
||||
|
||||
// Returns true if |block| (or |id|) is the merge target of a merge
|
||||
// instruction.
|
||||
bool IsMerge(ir::BasicBlock* block);
|
||||
bool IsMerge(opt::BasicBlock* block);
|
||||
bool IsMerge(uint32_t id);
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
};
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace {
|
||||
spv_result_t SetSpvHeader(void* builder, spv_endianness_t, uint32_t magic,
|
||||
uint32_t version, uint32_t generator,
|
||||
uint32_t id_bound, uint32_t reserved) {
|
||||
reinterpret_cast<ir::IrLoader*>(builder)->SetModuleHeader(
|
||||
reinterpret_cast<opt::IrLoader*>(builder)->SetModuleHeader(
|
||||
magic, version, generator, id_bound, reserved);
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@ -35,7 +35,7 @@ spv_result_t SetSpvHeader(void* builder, spv_endianness_t, uint32_t magic,
|
||||
// Processes a parsed instruction for IrLoader. Meets the interface requirement
|
||||
// of spvBinaryParse().
|
||||
spv_result_t SetSpvInst(void* builder, const spv_parsed_instruction_t* inst) {
|
||||
if (reinterpret_cast<ir::IrLoader*>(builder)->AddInstruction(inst)) {
|
||||
if (reinterpret_cast<opt::IrLoader*>(builder)->AddInstruction(inst)) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
@ -43,15 +43,15 @@ spv_result_t SetSpvInst(void* builder, const spv_parsed_instruction_t* inst) {
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ir::IRContext> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const uint32_t* binary,
|
||||
const size_t size) {
|
||||
std::unique_ptr<opt::IRContext> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const uint32_t* binary,
|
||||
const size_t size) {
|
||||
auto context = spvContextCreate(env);
|
||||
SetContextMessageConsumer(context, consumer);
|
||||
|
||||
auto irContext = MakeUnique<ir::IRContext>(env, consumer);
|
||||
ir::IrLoader loader(consumer, irContext->module());
|
||||
auto irContext = MakeUnique<opt::IRContext>(env, consumer);
|
||||
opt::IrLoader loader(consumer, irContext->module());
|
||||
|
||||
spv_result_t status = spvBinaryParse(context, &loader, binary, size,
|
||||
SetSpvHeader, SetSpvInst, nullptr);
|
||||
@ -62,10 +62,10 @@ std::unique_ptr<ir::IRContext> BuildModule(spv_target_env env,
|
||||
return status == SPV_SUCCESS ? std::move(irContext) : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ir::IRContext> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const std::string& text,
|
||||
uint32_t assemble_options) {
|
||||
std::unique_ptr<opt::IRContext> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const std::string& text,
|
||||
uint32_t assemble_options) {
|
||||
SpirvTools t(env);
|
||||
t.SetMessageConsumer(consumer);
|
||||
std::vector<uint32_t> binary;
|
||||
|
@ -24,19 +24,20 @@
|
||||
|
||||
namespace spvtools {
|
||||
|
||||
// Builds an ir::Module returns the owning ir::IRContext from the given SPIR-V
|
||||
// Builds an opt::Module returns the owning opt::IRContext from the given SPIR-V
|
||||
// |binary|. |size| specifies number of words in |binary|. The |binary| will be
|
||||
// decoded according to the given target |env|. Returns nullptr if errors occur
|
||||
// and sends the errors to |consumer|.
|
||||
std::unique_ptr<ir::IRContext> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const uint32_t* binary, size_t size);
|
||||
std::unique_ptr<opt::IRContext> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const uint32_t* binary,
|
||||
size_t size);
|
||||
|
||||
// Builds an ir::Module and returns the owning ir::IRContext from the given
|
||||
// Builds an opt::Module and returns the owning opt::IRContext from the given
|
||||
// SPIR-V assembly |text|. The |text| will be encoded according to the given
|
||||
// target |env|. Returns nullptr if errors occur and sends the errors to
|
||||
// |consumer|.
|
||||
std::unique_ptr<ir::IRContext> BuildModule(
|
||||
std::unique_ptr<opt::IRContext> BuildModule(
|
||||
spv_target_env env, MessageConsumer consumer, const std::string& text,
|
||||
uint32_t assemble_options = SpirvTools::kDefaultAssembleOption);
|
||||
|
||||
|
@ -39,14 +39,14 @@ const uint32_t kVaryingSSAId = std::numeric_limits<uint32_t>::max();
|
||||
bool CCPPass::IsVaryingValue(uint32_t id) const { return id == kVaryingSSAId; }
|
||||
|
||||
SSAPropagator::PropStatus CCPPass::MarkInstructionVarying(
|
||||
ir::Instruction* instr) {
|
||||
opt::Instruction* instr) {
|
||||
assert(instr->result_id() != 0 &&
|
||||
"Instructions with no result cannot be marked varying.");
|
||||
values_[instr->result_id()] = kVaryingSSAId;
|
||||
return SSAPropagator::kVarying;
|
||||
}
|
||||
|
||||
SSAPropagator::PropStatus CCPPass::VisitPhi(ir::Instruction* phi) {
|
||||
SSAPropagator::PropStatus CCPPass::VisitPhi(opt::Instruction* phi) {
|
||||
uint32_t meet_val_id = 0;
|
||||
|
||||
// Implement the lattice meet operation. The result of this Phi instruction is
|
||||
@ -100,7 +100,7 @@ SSAPropagator::PropStatus CCPPass::VisitPhi(ir::Instruction* phi) {
|
||||
return SSAPropagator::kInteresting;
|
||||
}
|
||||
|
||||
SSAPropagator::PropStatus CCPPass::VisitAssignment(ir::Instruction* instr) {
|
||||
SSAPropagator::PropStatus CCPPass::VisitAssignment(opt::Instruction* instr) {
|
||||
assert(instr->result_id() != 0 &&
|
||||
"Expecting an instruction that produces a result");
|
||||
|
||||
@ -133,7 +133,7 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(ir::Instruction* instr) {
|
||||
}
|
||||
return it->second;
|
||||
};
|
||||
ir::Instruction* folded_inst =
|
||||
opt::Instruction* folded_inst =
|
||||
context()->get_instruction_folder().FoldInstructionToConstant(instr,
|
||||
map_func);
|
||||
if (folded_inst != nullptr) {
|
||||
@ -168,8 +168,8 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(ir::Instruction* instr) {
|
||||
return MarkInstructionVarying(instr);
|
||||
}
|
||||
|
||||
SSAPropagator::PropStatus CCPPass::VisitBranch(ir::Instruction* instr,
|
||||
ir::BasicBlock** dest_bb) const {
|
||||
SSAPropagator::PropStatus CCPPass::VisitBranch(
|
||||
opt::Instruction* instr, opt::BasicBlock** dest_bb) const {
|
||||
assert(instr->IsBranch() && "Expected a branch instruction.");
|
||||
|
||||
*dest_bb = nullptr;
|
||||
@ -250,8 +250,8 @@ SSAPropagator::PropStatus CCPPass::VisitBranch(ir::Instruction* instr,
|
||||
return SSAPropagator::kInteresting;
|
||||
}
|
||||
|
||||
SSAPropagator::PropStatus CCPPass::VisitInstruction(ir::Instruction* instr,
|
||||
ir::BasicBlock** dest_bb) {
|
||||
SSAPropagator::PropStatus CCPPass::VisitInstruction(opt::Instruction* instr,
|
||||
opt::BasicBlock** dest_bb) {
|
||||
*dest_bb = nullptr;
|
||||
if (instr->opcode() == SpvOpPhi) {
|
||||
return VisitPhi(instr);
|
||||
@ -275,14 +275,14 @@ bool CCPPass::ReplaceValues() {
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool CCPPass::PropagateConstants(ir::Function* fp) {
|
||||
bool CCPPass::PropagateConstants(opt::Function* fp) {
|
||||
// Mark function parameters as varying.
|
||||
fp->ForEachParam([this](const ir::Instruction* inst) {
|
||||
fp->ForEachParam([this](const opt::Instruction* inst) {
|
||||
values_[inst->result_id()] = kVaryingSSAId;
|
||||
});
|
||||
|
||||
const auto visit_fn = [this](ir::Instruction* instr,
|
||||
ir::BasicBlock** dest_bb) {
|
||||
const auto visit_fn = [this](opt::Instruction* instr,
|
||||
opt::BasicBlock** dest_bb) {
|
||||
return VisitInstruction(instr, dest_bb);
|
||||
};
|
||||
|
||||
@ -296,7 +296,7 @@ bool CCPPass::PropagateConstants(ir::Function* fp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCPPass::Initialize(ir::IRContext* c) {
|
||||
void CCPPass::Initialize(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
const_mgr_ = context()->get_constant_mgr();
|
||||
@ -315,11 +315,11 @@ void CCPPass::Initialize(ir::IRContext* c) {
|
||||
}
|
||||
}
|
||||
|
||||
Pass::Status CCPPass::Process(ir::IRContext* c) {
|
||||
Pass::Status CCPPass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return PropagateConstants(fp);
|
||||
};
|
||||
bool modified = ProcessReachableCallTree(pfn, context());
|
||||
|
@ -29,46 +29,46 @@ class CCPPass : public MemPass {
|
||||
public:
|
||||
CCPPass() = default;
|
||||
const char* name() const override { return "ccp"; }
|
||||
Status Process(ir::IRContext* c) override;
|
||||
virtual ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::kAnalysisDecorations |
|
||||
ir::IRContext::kAnalysisCombinators | ir::IRContext::kAnalysisCFG |
|
||||
ir::IRContext::kAnalysisDominatorAnalysis |
|
||||
ir::IRContext::kAnalysisNameMap;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
virtual opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::kAnalysisDecorations |
|
||||
opt::IRContext::kAnalysisCombinators | opt::IRContext::kAnalysisCFG |
|
||||
opt::IRContext::kAnalysisDominatorAnalysis |
|
||||
opt::IRContext::kAnalysisNameMap;
|
||||
}
|
||||
|
||||
private:
|
||||
// Initializes the pass.
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
|
||||
// Runs constant propagation on the given function |fp|. Returns true if any
|
||||
// constants were propagated and the IR modified.
|
||||
bool PropagateConstants(ir::Function* fp);
|
||||
bool PropagateConstants(opt::Function* fp);
|
||||
|
||||
// Visits a single instruction |instr|. If the instruction is a conditional
|
||||
// branch that always jumps to the same basic block, it sets the destination
|
||||
// block in |dest_bb|.
|
||||
SSAPropagator::PropStatus VisitInstruction(ir::Instruction* instr,
|
||||
ir::BasicBlock** dest_bb);
|
||||
SSAPropagator::PropStatus VisitInstruction(opt::Instruction* instr,
|
||||
opt::BasicBlock** dest_bb);
|
||||
|
||||
// Visits an OpPhi instruction |phi|. This applies the meet operator for the
|
||||
// CCP lattice. Essentially, if all the operands in |phi| have the same
|
||||
// constant value C, the result for |phi| gets assigned the value C.
|
||||
SSAPropagator::PropStatus VisitPhi(ir::Instruction* phi);
|
||||
SSAPropagator::PropStatus VisitPhi(opt::Instruction* phi);
|
||||
|
||||
// Visits an SSA assignment instruction |instr|. If the RHS of |instr| folds
|
||||
// into a constant value C, then the LHS of |instr| is assigned the value C in
|
||||
// |values_|.
|
||||
SSAPropagator::PropStatus VisitAssignment(ir::Instruction* instr);
|
||||
SSAPropagator::PropStatus VisitAssignment(opt::Instruction* instr);
|
||||
|
||||
// Visits a branch instruction |instr|. If the branch is conditional
|
||||
// (OpBranchConditional or OpSwitch), and the value of its selector is known,
|
||||
// |dest_bb| will be set to the corresponding destination block. Unconditional
|
||||
// branches always set |dest_bb| to the single destination block.
|
||||
SSAPropagator::PropStatus VisitBranch(ir::Instruction* instr,
|
||||
ir::BasicBlock** dest_bb) const;
|
||||
SSAPropagator::PropStatus VisitBranch(opt::Instruction* instr,
|
||||
opt::BasicBlock** dest_bb) const;
|
||||
|
||||
// Replaces all operands used in |fp| with the corresponding constant values
|
||||
// in |values_|. Returns true if any operands were replaced, and false
|
||||
@ -77,7 +77,7 @@ class CCPPass : public MemPass {
|
||||
|
||||
// Marks |instr| as varying by registering a varying value for its result
|
||||
// into the |values_| table. Returns SSAPropagator::kVarying.
|
||||
SSAPropagator::PropStatus MarkInstructionVarying(ir::Instruction* instr);
|
||||
SSAPropagator::PropStatus MarkInstructionVarying(opt::Instruction* instr);
|
||||
|
||||
// Returns true if |id| is the special SSA id that corresponds to a varying
|
||||
// value.
|
||||
|
@ -19,21 +19,20 @@
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
// Universal Limit of ResultID + 1
|
||||
const int kInvalidId = 0x400000;
|
||||
const int kMaxResultId = 0x400000;
|
||||
|
||||
} // namespace
|
||||
|
||||
CFG::CFG(ir::Module* module)
|
||||
CFG::CFG(opt::Module* module)
|
||||
: module_(module),
|
||||
pseudo_entry_block_(std::unique_ptr<ir::Instruction>(
|
||||
new ir::Instruction(module->context(), SpvOpLabel, 0, 0, {}))),
|
||||
pseudo_exit_block_(std::unique_ptr<ir::Instruction>(new ir::Instruction(
|
||||
module->context(), SpvOpLabel, 0, kInvalidId, {}))) {
|
||||
pseudo_entry_block_(std::unique_ptr<opt::Instruction>(
|
||||
new opt::Instruction(module->context(), SpvOpLabel, 0, 0, {}))),
|
||||
pseudo_exit_block_(std::unique_ptr<opt::Instruction>(new opt::Instruction(
|
||||
module->context(), SpvOpLabel, 0, kMaxResultId, {}))) {
|
||||
for (auto& fn : *module) {
|
||||
for (auto& blk : fn) {
|
||||
RegisterBlock(&blk);
|
||||
@ -41,7 +40,7 @@ CFG::CFG(ir::Module* module)
|
||||
}
|
||||
}
|
||||
|
||||
void CFG::AddEdges(ir::BasicBlock* blk) {
|
||||
void CFG::AddEdges(opt::BasicBlock* blk) {
|
||||
uint32_t blk_id = blk->id();
|
||||
// Force the creation of an entry, not all basic block have predecessors
|
||||
// (such as the entry blocks and some unreachables).
|
||||
@ -54,7 +53,7 @@ void CFG::AddEdges(ir::BasicBlock* blk) {
|
||||
void CFG::RemoveNonExistingEdges(uint32_t blk_id) {
|
||||
std::vector<uint32_t> updated_pred_list;
|
||||
for (uint32_t id : preds(blk_id)) {
|
||||
const ir::BasicBlock* pred_blk = block(id);
|
||||
const opt::BasicBlock* pred_blk = block(id);
|
||||
bool has_branch = false;
|
||||
pred_blk->ForEachSuccessorLabel([&has_branch, blk_id](uint32_t succ) {
|
||||
if (succ == blk_id) {
|
||||
@ -67,8 +66,8 @@ void CFG::RemoveNonExistingEdges(uint32_t blk_id) {
|
||||
label2preds_.at(blk_id) = std::move(updated_pred_list);
|
||||
}
|
||||
|
||||
void CFG::ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root,
|
||||
std::list<ir::BasicBlock*>* order) {
|
||||
void CFG::ComputeStructuredOrder(opt::Function* func, opt::BasicBlock* root,
|
||||
std::list<opt::BasicBlock*>* order) {
|
||||
assert(module_->context()->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityShader) &&
|
||||
"This only works on structured control flow");
|
||||
@ -77,16 +76,16 @@ void CFG::ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root,
|
||||
ComputeStructuredSuccessors(func);
|
||||
auto ignore_block = [](cbb_ptr) {};
|
||||
auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
|
||||
auto get_structured_successors = [this](const ir::BasicBlock* b) {
|
||||
auto get_structured_successors = [this](const opt::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));
|
||||
order->push_front(const_cast<opt::BasicBlock*>(b));
|
||||
};
|
||||
CFA<ir::BasicBlock>::DepthFirstTraversal(
|
||||
CFA<opt::BasicBlock>::DepthFirstTraversal(
|
||||
root, get_structured_successors, ignore_block, post_order, ignore_edge);
|
||||
}
|
||||
|
||||
@ -116,7 +115,7 @@ void CFG::ForEachBlockInReversePostOrder(
|
||||
}
|
||||
}
|
||||
|
||||
void CFG::ComputeStructuredSuccessors(ir::Function* func) {
|
||||
void CFG::ComputeStructuredSuccessors(opt::Function* func) {
|
||||
block2structured_succs_.clear();
|
||||
for (auto& blk : *func) {
|
||||
// If no predecessors in function, make successor to pseudo entry.
|
||||
@ -155,7 +154,7 @@ void CFG::ComputePostOrderTraversal(BasicBlock* bb, vector<BasicBlock*>* order,
|
||||
order->push_back(bb);
|
||||
}
|
||||
|
||||
BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
BasicBlock* CFG::SplitLoopHeader(opt::BasicBlock* bb) {
|
||||
assert(bb->GetLoopMergeInst() && "Expecting bb to be the header of a loop.");
|
||||
|
||||
Function* fn = bb->GetParent();
|
||||
@ -169,7 +168,7 @@ BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
|
||||
const std::vector<uint32_t>& pred = preds(bb->id());
|
||||
// Find the back edge
|
||||
ir::BasicBlock* latch_block = nullptr;
|
||||
opt::BasicBlock* latch_block = nullptr;
|
||||
Function::iterator latch_block_iter = header_it;
|
||||
while (++latch_block_iter != fn->end()) {
|
||||
// If blocks are in the proper order, then the only branch that appears
|
||||
@ -191,13 +190,13 @@ BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
++iter;
|
||||
}
|
||||
|
||||
std::unique_ptr<ir::BasicBlock> newBlock(
|
||||
std::unique_ptr<opt::BasicBlock> newBlock(
|
||||
bb->SplitBasicBlock(context, context->TakeNextId(), iter));
|
||||
|
||||
// Insert the new bb in the correct position
|
||||
auto insert_pos = header_it;
|
||||
++insert_pos;
|
||||
ir::BasicBlock* new_header = &*insert_pos.InsertBefore(std::move(newBlock));
|
||||
opt::BasicBlock* new_header = &*insert_pos.InsertBefore(std::move(newBlock));
|
||||
new_header->SetParent(fn);
|
||||
uint32_t new_header_id = new_header->id();
|
||||
context->AnalyzeDefUse(new_header->GetLabelInst());
|
||||
@ -207,7 +206,7 @@ BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
|
||||
// Update bb mappings.
|
||||
context->set_instr_block(new_header->GetLabelInst(), new_header);
|
||||
new_header->ForEachInst([new_header, context](ir::Instruction* inst) {
|
||||
new_header->ForEachInst([new_header, context](opt::Instruction* inst) {
|
||||
context->set_instr_block(inst, new_header);
|
||||
});
|
||||
|
||||
@ -235,10 +234,10 @@ BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
if (preheader_phi_ops.size() > 2) {
|
||||
opt::InstructionBuilder builder(
|
||||
context, &*bb->begin(),
|
||||
ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping);
|
||||
opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping);
|
||||
|
||||
ir::Instruction* new_phi =
|
||||
opt::Instruction* new_phi =
|
||||
builder.AddPhi(phi->type_id(), preheader_phi_ops);
|
||||
|
||||
// Add the OpPhi to the header bb.
|
||||
@ -252,7 +251,7 @@ BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
}
|
||||
|
||||
phi->RemoveFromList();
|
||||
std::unique_ptr<ir::Instruction> phi_owner(phi);
|
||||
std::unique_ptr<opt::Instruction> phi_owner(phi);
|
||||
phi->SetInOperands(std::move(header_phi_ops));
|
||||
new_header->begin()->InsertBefore(std::move(phi_owner));
|
||||
context->set_instr_block(phi, new_header);
|
||||
@ -262,11 +261,11 @@ BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
// Add a branch to the new header.
|
||||
opt::InstructionBuilder branch_builder(
|
||||
context, bb,
|
||||
ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping);
|
||||
bb->AddInstruction(MakeUnique<ir::Instruction>(
|
||||
opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping);
|
||||
bb->AddInstruction(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpBranch, 0, 0,
|
||||
std::initializer_list<ir::Operand>{
|
||||
std::initializer_list<opt::Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {new_header->id()}}}));
|
||||
context->AnalyzeUses(bb->terminator());
|
||||
context->set_instr_block(bb->terminator(), bb);
|
||||
@ -278,7 +277,7 @@ BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
*id = new_header_id;
|
||||
}
|
||||
});
|
||||
ir::Instruction* latch_branch = latch_block->terminator();
|
||||
opt::Instruction* latch_branch = latch_block->terminator();
|
||||
context->AnalyzeUses(latch_branch);
|
||||
label2preds_[new_header->id()].push_back(latch_block->id());
|
||||
|
||||
@ -289,7 +288,7 @@ BasicBlock* CFG::SplitLoopHeader(ir::BasicBlock* bb) {
|
||||
block_preds.erase(latch_pos);
|
||||
|
||||
// Update the loop descriptors
|
||||
if (context->AreAnalysesValid(ir::IRContext::kAnalysisLoopAnalysis)) {
|
||||
if (context->AreAnalysesValid(opt::IRContext::kAnalysisLoopAnalysis)) {
|
||||
LoopDescriptor* loop_desc = context->GetLoopDescriptor(bb->GetParent());
|
||||
Loop* loop = (*loop_desc)[bb->id()];
|
||||
|
||||
@ -319,5 +318,5 @@ unordered_set<BasicBlock*> CFG::FindReachableBlocks(BasicBlock* start) {
|
||||
return reachable_blocks;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -23,17 +23,17 @@
|
||||
#include <unordered_set>
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
class CFG {
|
||||
public:
|
||||
CFG(ir::Module* module);
|
||||
CFG(opt::Module* module);
|
||||
|
||||
// Return the module described by this CFG.
|
||||
ir::Module* get_module() const { return module_; }
|
||||
opt::Module* get_module() const { return module_; }
|
||||
|
||||
// Return the list of predecesors for basic block with label |blkid|.
|
||||
// TODO(dnovillo): Move this to ir::BasicBlock.
|
||||
// TODO(dnovillo): Move this to opt::BasicBlock.
|
||||
const std::vector<uint32_t>& preds(uint32_t blk_id) const {
|
||||
assert(label2preds_.count(blk_id));
|
||||
return label2preds_.at(blk_id);
|
||||
@ -41,26 +41,26 @@ class CFG {
|
||||
|
||||
// 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); }
|
||||
opt::BasicBlock* block(uint32_t blk_id) const { return id2block_.at(blk_id); }
|
||||
|
||||
// Return the pseudo entry and exit blocks.
|
||||
const ir::BasicBlock* pseudo_entry_block() const {
|
||||
const opt::BasicBlock* pseudo_entry_block() const {
|
||||
return &pseudo_entry_block_;
|
||||
}
|
||||
ir::BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; }
|
||||
opt::BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; }
|
||||
|
||||
const ir::BasicBlock* pseudo_exit_block() const {
|
||||
const opt::BasicBlock* pseudo_exit_block() const {
|
||||
return &pseudo_exit_block_;
|
||||
}
|
||||
ir::BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; }
|
||||
opt::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 {
|
||||
bool IsPseudoEntryBlock(opt::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 {
|
||||
bool IsPseudoExitBlock(opt::BasicBlock* block_ptr) const {
|
||||
return block_ptr == &pseudo_exit_block_;
|
||||
}
|
||||
|
||||
@ -68,8 +68,8 @@ class CFG {
|
||||
// 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);
|
||||
void ComputeStructuredOrder(opt::Function* func, opt::BasicBlock* root,
|
||||
std::list<opt::BasicBlock*>* order);
|
||||
|
||||
// Applies |f| to the basic block in post order starting with |bb|.
|
||||
// Note that basic blocks that cannot be reached from |bb| node will not be
|
||||
@ -85,14 +85,14 @@ class CFG {
|
||||
|
||||
// Registers |blk| as a basic block in the cfg, this also updates the
|
||||
// predecessor lists of each successor of |blk|.
|
||||
void RegisterBlock(ir::BasicBlock* blk) {
|
||||
void RegisterBlock(opt::BasicBlock* blk) {
|
||||
uint32_t blk_id = blk->id();
|
||||
id2block_[blk_id] = blk;
|
||||
AddEdges(blk);
|
||||
}
|
||||
|
||||
// Removes from the CFG any mapping for the basic block id |blk_id|.
|
||||
void ForgetBlock(const ir::BasicBlock* blk) {
|
||||
void ForgetBlock(const opt::BasicBlock* blk) {
|
||||
id2block_.erase(blk->id());
|
||||
label2preds_.erase(blk->id());
|
||||
RemoveSuccessorEdges(blk);
|
||||
@ -107,7 +107,7 @@ class CFG {
|
||||
}
|
||||
|
||||
// Registers |blk| to all of its successors.
|
||||
void AddEdges(ir::BasicBlock* blk);
|
||||
void AddEdges(opt::BasicBlock* blk);
|
||||
|
||||
// Registers the basic block id |pred_blk_id| as being a predecessor of the
|
||||
// basic block id |succ_blk_id|.
|
||||
@ -120,7 +120,7 @@ class CFG {
|
||||
void RemoveNonExistingEdges(uint32_t blk_id);
|
||||
|
||||
// Remove all edges that leave |bb|.
|
||||
void RemoveSuccessorEdges(const ir::BasicBlock* bb) {
|
||||
void RemoveSuccessorEdges(const opt::BasicBlock* bb) {
|
||||
bb->ForEachSuccessorLabel(
|
||||
[bb, this](uint32_t succ_id) { RemoveEdge(bb->id(), succ_id); });
|
||||
}
|
||||
@ -130,12 +130,12 @@ class CFG {
|
||||
// is a new block that will be the new loop header.
|
||||
//
|
||||
// Returns a pointer to the new loop header.
|
||||
BasicBlock* SplitLoopHeader(ir::BasicBlock* bb);
|
||||
BasicBlock* SplitLoopHeader(opt::BasicBlock* bb);
|
||||
|
||||
std::unordered_set<BasicBlock*> FindReachableBlocks(BasicBlock* start);
|
||||
|
||||
private:
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
using cbb_ptr = const opt::BasicBlock*;
|
||||
|
||||
// Compute structured successors for function |func|. A block's structured
|
||||
// successors are the blocks it branches to together with its declared merge
|
||||
@ -144,7 +144,7 @@ class CFG {
|
||||
// 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);
|
||||
void ComputeStructuredSuccessors(opt::Function* func);
|
||||
|
||||
// Computes the post-order traversal of the cfg starting at |bb| skipping
|
||||
// nodes in |seen|. The order of the traversal is appended to |order|, and
|
||||
@ -154,28 +154,28 @@ class CFG {
|
||||
std::unordered_set<BasicBlock*>* seen);
|
||||
|
||||
// Module for this CFG.
|
||||
ir::Module* module_;
|
||||
opt::Module* module_;
|
||||
|
||||
// Map from block to its structured successor blocks. See
|
||||
// ComputeStructuredSuccessors() for definition.
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
std::unordered_map<const opt::BasicBlock*, std::vector<opt::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
|
||||
// Extra block whose successors are all blocks with no predecessors
|
||||
// in function.
|
||||
ir::BasicBlock pseudo_entry_block_;
|
||||
opt::BasicBlock pseudo_entry_block_;
|
||||
|
||||
// Augmented CFG Exit Block.
|
||||
ir::BasicBlock pseudo_exit_block_;
|
||||
opt::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_;
|
||||
std::unordered_map<uint32_t, opt::BasicBlock*> id2block_;
|
||||
};
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_CFG_H_
|
||||
|
@ -27,13 +27,13 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
void CFGCleanupPass::Initialize(ir::IRContext* c) { InitializeProcessing(c); }
|
||||
void CFGCleanupPass::Initialize(opt::IRContext* c) { InitializeProcessing(c); }
|
||||
|
||||
Pass::Status CFGCleanupPass::Process(ir::IRContext* c) {
|
||||
Pass::Status CFGCleanupPass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) { return CFGCleanup(fp); };
|
||||
ProcessFunction pfn = [this](opt::Function* fp) { return CFGCleanup(fp); };
|
||||
bool modified = ProcessReachableCallTree(pfn, context());
|
||||
return modified ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
|
@ -26,15 +26,15 @@ class CFGCleanupPass : public MemPass {
|
||||
public:
|
||||
CFGCleanupPass() = default;
|
||||
const char* name() const override { return "cfg-cleanup"; }
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse;
|
||||
}
|
||||
|
||||
private:
|
||||
// Initialize the pass.
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -41,7 +41,7 @@ bool CommonUniformElimPass::IsNonPtrAccessChain(const SpvOp opcode) const {
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsSamplerOrImageType(
|
||||
const ir::Instruction* typeInst) const {
|
||||
const opt::Instruction* typeInst) const {
|
||||
switch (typeInst->opcode()) {
|
||||
case SpvOpTypeSampler:
|
||||
case SpvOpTypeImage:
|
||||
@ -53,7 +53,7 @@ bool CommonUniformElimPass::IsSamplerOrImageType(
|
||||
if (typeInst->opcode() != SpvOpTypeStruct) return false;
|
||||
// Return true if any member is a sampler or image
|
||||
return !typeInst->WhileEachInId([this](const uint32_t* tid) {
|
||||
const ir::Instruction* compTypeInst = get_def_use_mgr()->GetDef(*tid);
|
||||
const opt::Instruction* compTypeInst = get_def_use_mgr()->GetDef(*tid);
|
||||
if (IsSamplerOrImageType(compTypeInst)) {
|
||||
return false;
|
||||
}
|
||||
@ -62,28 +62,28 @@ bool CommonUniformElimPass::IsSamplerOrImageType(
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsSamplerOrImageVar(uint32_t varId) const {
|
||||
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
|
||||
const opt::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 opt::Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
|
||||
const uint32_t varPteTypeId =
|
||||
varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
|
||||
ir::Instruction* varPteTypeInst = get_def_use_mgr()->GetDef(varPteTypeId);
|
||||
opt::Instruction* varPteTypeInst = get_def_use_mgr()->GetDef(varPteTypeId);
|
||||
return IsSamplerOrImageType(varPteTypeInst);
|
||||
}
|
||||
|
||||
ir::Instruction* CommonUniformElimPass::GetPtr(ir::Instruction* ip,
|
||||
uint32_t* objId) {
|
||||
opt::Instruction* CommonUniformElimPass::GetPtr(opt::Instruction* ip,
|
||||
uint32_t* objId) {
|
||||
const SpvOp op = ip->opcode();
|
||||
assert(op == SpvOpStore || op == SpvOpLoad);
|
||||
*objId = ip->GetSingleWordInOperand(op == SpvOpStore ? kStorePtrIdInIdx
|
||||
: kLoadPtrIdInIdx);
|
||||
ir::Instruction* ptrInst = get_def_use_mgr()->GetDef(*objId);
|
||||
opt::Instruction* ptrInst = get_def_use_mgr()->GetDef(*objId);
|
||||
while (ptrInst->opcode() == SpvOpCopyObject) {
|
||||
*objId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
|
||||
ptrInst = get_def_use_mgr()->GetDef(*objId);
|
||||
}
|
||||
ir::Instruction* objInst = ptrInst;
|
||||
opt::Instruction* objInst = ptrInst;
|
||||
while (objInst->opcode() != SpvOpVariable &&
|
||||
objInst->opcode() != SpvOpFunctionParameter) {
|
||||
if (IsNonPtrAccessChain(objInst->opcode())) {
|
||||
@ -101,21 +101,21 @@ bool CommonUniformElimPass::IsVolatileStruct(uint32_t type_id) {
|
||||
assert(get_def_use_mgr()->GetDef(type_id)->opcode() == SpvOpTypeStruct);
|
||||
return !get_decoration_mgr()->WhileEachDecoration(
|
||||
type_id, SpvDecorationVolatile,
|
||||
[](const ir::Instruction&) { return false; });
|
||||
[](const opt::Instruction&) { return false; });
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsAccessChainToVolatileStructType(
|
||||
const ir::Instruction& AccessChainInst) {
|
||||
const opt::Instruction& AccessChainInst) {
|
||||
assert(AccessChainInst.opcode() == SpvOpAccessChain);
|
||||
|
||||
uint32_t ptr_id = AccessChainInst.GetSingleWordInOperand(0);
|
||||
const ir::Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id);
|
||||
const opt::Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id);
|
||||
uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst);
|
||||
const uint32_t num_operands = AccessChainInst.NumOperands();
|
||||
|
||||
// walk the type tree:
|
||||
for (uint32_t idx = 3; idx < num_operands; ++idx) {
|
||||
ir::Instruction* pointee_type = get_def_use_mgr()->GetDef(pointee_type_id);
|
||||
opt::Instruction* pointee_type = get_def_use_mgr()->GetDef(pointee_type_id);
|
||||
|
||||
switch (pointee_type->opcode()) {
|
||||
case SpvOpTypeMatrix:
|
||||
@ -130,7 +130,7 @@ bool CommonUniformElimPass::IsAccessChainToVolatileStructType(
|
||||
|
||||
if (idx < num_operands - 1) {
|
||||
const uint32_t index_id = AccessChainInst.GetSingleWordOperand(idx);
|
||||
const ir::Instruction* index_inst =
|
||||
const opt::Instruction* index_inst =
|
||||
get_def_use_mgr()->GetDef(index_id);
|
||||
uint32_t index_value = index_inst->GetSingleWordOperand(
|
||||
2); // TODO: replace with GetUintValueFromConstant()
|
||||
@ -144,7 +144,7 @@ bool CommonUniformElimPass::IsAccessChainToVolatileStructType(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsVolatileLoad(const ir::Instruction& loadInst) {
|
||||
bool CommonUniformElimPass::IsVolatileLoad(const opt::Instruction& loadInst) {
|
||||
assert(loadInst.opcode() == SpvOpLoad);
|
||||
// Check if this Load instruction has Volatile Memory Access flag
|
||||
if (loadInst.NumOperands() == 4) {
|
||||
@ -161,11 +161,11 @@ bool CommonUniformElimPass::IsVolatileLoad(const ir::Instruction& loadInst) {
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsUniformVar(uint32_t varId) {
|
||||
const ir::Instruction* varInst =
|
||||
const opt::Instruction* varInst =
|
||||
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 =
|
||||
const opt::Instruction* varTypeInst =
|
||||
get_def_use_mgr()->id_to_defs().find(varTypeId)->second;
|
||||
return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
|
||||
SpvStorageClassUniform ||
|
||||
@ -174,21 +174,21 @@ bool CommonUniformElimPass::IsUniformVar(uint32_t varId) {
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::HasUnsupportedDecorates(uint32_t id) const {
|
||||
return !get_def_use_mgr()->WhileEachUser(id, [this](ir::Instruction* user) {
|
||||
return !get_def_use_mgr()->WhileEachUser(id, [this](opt::Instruction* user) {
|
||||
if (IsNonTypeDecorate(user->opcode())) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::HasOnlyNamesAndDecorates(uint32_t id) const {
|
||||
return get_def_use_mgr()->WhileEachUser(id, [this](ir::Instruction* user) {
|
||||
return get_def_use_mgr()->WhileEachUser(id, [this](opt::Instruction* user) {
|
||||
SpvOp op = user->opcode();
|
||||
if (op != SpvOpName && !IsNonTypeDecorate(op)) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::DeleteIfUseless(ir::Instruction* inst) {
|
||||
void CommonUniformElimPass::DeleteIfUseless(opt::Instruction* inst) {
|
||||
const uint32_t resId = inst->result_id();
|
||||
assert(resId != 0);
|
||||
if (HasOnlyNamesAndDecorates(resId)) {
|
||||
@ -196,34 +196,34 @@ void CommonUniformElimPass::DeleteIfUseless(ir::Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
ir::Instruction* CommonUniformElimPass::ReplaceAndDeleteLoad(
|
||||
ir::Instruction* loadInst, uint32_t replId, ir::Instruction* ptrInst) {
|
||||
opt::Instruction* CommonUniformElimPass::ReplaceAndDeleteLoad(
|
||||
opt::Instruction* loadInst, uint32_t replId, opt::Instruction* ptrInst) {
|
||||
const uint32_t loadId = loadInst->result_id();
|
||||
context()->KillNamesAndDecorates(loadId);
|
||||
(void)context()->ReplaceAllUsesWith(loadId, replId);
|
||||
// remove load instruction
|
||||
ir::Instruction* next_instruction = context()->KillInst(loadInst);
|
||||
opt::Instruction* next_instruction = context()->KillInst(loadInst);
|
||||
// if access chain, see if it can be removed as well
|
||||
if (IsNonPtrAccessChain(ptrInst->opcode())) DeleteIfUseless(ptrInst);
|
||||
return next_instruction;
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::GenACLoadRepl(
|
||||
const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts,
|
||||
const opt::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts,
|
||||
uint32_t* resultId) {
|
||||
// Build and append Load
|
||||
const uint32_t ldResultId = TakeNextId();
|
||||
const uint32_t varId =
|
||||
ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
|
||||
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
|
||||
const opt::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;
|
||||
std::vector<opt::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(
|
||||
opt::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
std::initializer_list<uint32_t>{varId}));
|
||||
std::unique_ptr<opt::Instruction> newLoad(new opt::Instruction(
|
||||
context(), SpvOpLoad, varPteTypeId, ldResultId, load_in_operands));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newLoad);
|
||||
newInsts->emplace_back(std::move(newLoad));
|
||||
@ -231,34 +231,34 @@ void CommonUniformElimPass::GenACLoadRepl(
|
||||
// Build and append Extract
|
||||
const uint32_t extResultId = TakeNextId();
|
||||
const uint32_t ptrPteTypeId = GetPointeeTypeId(ptrInst);
|
||||
std::vector<ir::Operand> ext_in_opnds;
|
||||
std::vector<opt::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}));
|
||||
opt::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) {
|
||||
if (iidIdx > 0) {
|
||||
const ir::Instruction* cInst = get_def_use_mgr()->GetDef(*iid);
|
||||
const opt::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}));
|
||||
opt::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(context(), SpvOpCompositeExtract, ptrPteTypeId,
|
||||
extResultId, ext_in_opnds));
|
||||
std::unique_ptr<opt::Instruction> newExt(
|
||||
new opt::Instruction(context(), SpvOpCompositeExtract, ptrPteTypeId,
|
||||
extResultId, ext_in_opnds));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newExt);
|
||||
newInsts->emplace_back(std::move(newExt));
|
||||
*resultId = extResultId;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::IsConstantIndexAccessChain(ir::Instruction* acp) {
|
||||
bool CommonUniformElimPass::IsConstantIndexAccessChain(opt::Instruction* acp) {
|
||||
uint32_t inIdx = 0;
|
||||
return acp->WhileEachInId([&inIdx, this](uint32_t* tid) {
|
||||
if (inIdx > 0) {
|
||||
ir::Instruction* opInst = get_def_use_mgr()->GetDef(*tid);
|
||||
opt::Instruction* opInst = get_def_use_mgr()->GetDef(*tid);
|
||||
if (opInst->opcode() != SpvOpConstant) return false;
|
||||
}
|
||||
++inIdx;
|
||||
@ -266,13 +266,14 @@ bool CommonUniformElimPass::IsConstantIndexAccessChain(ir::Instruction* acp) {
|
||||
});
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) {
|
||||
bool CommonUniformElimPass::UniformAccessChainConvert(opt::Function* func) {
|
||||
bool modified = false;
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (ir::Instruction* inst = &*bi->begin(); inst; inst = inst->NextNode()) {
|
||||
for (opt::Instruction* inst = &*bi->begin(); inst;
|
||||
inst = inst->NextNode()) {
|
||||
if (inst->opcode() != SpvOpLoad) continue;
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(inst, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(inst, &varId);
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode())) continue;
|
||||
// Do not convert nested access chains
|
||||
if (ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx) != varId)
|
||||
@ -283,7 +284,7 @@ bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) {
|
||||
if (HasUnsupportedDecorates(ptrInst->result_id())) continue;
|
||||
if (IsVolatileLoad(*inst)) continue;
|
||||
if (IsAccessChainToVolatileStructType(*ptrInst)) continue;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
|
||||
std::vector<std::unique_ptr<opt::Instruction>> newInsts;
|
||||
uint32_t replId;
|
||||
GenACLoadRepl(ptrInst, &newInsts, &replId);
|
||||
inst = ReplaceAndDeleteLoad(inst, replId, ptrInst);
|
||||
@ -294,7 +295,7 @@ bool CommonUniformElimPass::UniformAccessChainConvert(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::ComputeStructuredSuccessors(ir::Function* func) {
|
||||
void CommonUniformElimPass::ComputeStructuredSuccessors(opt::Function* func) {
|
||||
block2structured_succs_.clear();
|
||||
for (auto& blk : *func) {
|
||||
// If header, make merge block first successor.
|
||||
@ -315,32 +316,32 @@ void CommonUniformElimPass::ComputeStructuredSuccessors(ir::Function* func) {
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::ComputeStructuredOrder(
|
||||
ir::Function* func, std::list<ir::BasicBlock*>* order) {
|
||||
opt::Function* func, std::list<opt::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) {
|
||||
auto get_structured_successors = [this](const opt::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));
|
||||
order->push_front(const_cast<opt::BasicBlock*>(b));
|
||||
};
|
||||
|
||||
order->clear();
|
||||
CFA<ir::BasicBlock>::DepthFirstTraversal(
|
||||
CFA<opt::BasicBlock>::DepthFirstTraversal(
|
||||
&*func->begin(), get_structured_successors, ignore_block, post_order,
|
||||
ignore_edge);
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
bool CommonUniformElimPass::CommonUniformLoadElimination(opt::Function* func) {
|
||||
// Process all blocks in structured order. This is just one way (the
|
||||
// simplest?) to keep track of the most recent block outside of control
|
||||
// flow, used to copy common instructions, guaranteed to dominate all
|
||||
// following load sites.
|
||||
std::list<ir::BasicBlock*> structuredOrder;
|
||||
std::list<opt::BasicBlock*> structuredOrder;
|
||||
ComputeStructuredOrder(func, &structuredOrder);
|
||||
uniform2load_id_.clear();
|
||||
bool modified = false;
|
||||
@ -354,7 +355,7 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
while (IsUniformLoadToBeRemoved(&*insertItr)) ++insertItr;
|
||||
uint32_t mergeBlockId = 0;
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
|
||||
ir::BasicBlock* bp = *bi;
|
||||
opt::BasicBlock* bp = *bi;
|
||||
// Check if we are exiting outermost control construct. If so, remember
|
||||
// new load insertion point. Trying to keep register pressure down.
|
||||
if (mergeBlockId == bp->id()) {
|
||||
@ -364,10 +365,11 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
// ReplaceAndDeleteLoad() can set |insertItr| as a dangling pointer.
|
||||
while (IsUniformLoadToBeRemoved(&*insertItr)) ++insertItr;
|
||||
}
|
||||
for (ir::Instruction* inst = &*bp->begin(); inst; inst = inst->NextNode()) {
|
||||
for (opt::Instruction* inst = &*bp->begin(); inst;
|
||||
inst = inst->NextNode()) {
|
||||
if (inst->opcode() != SpvOpLoad) continue;
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(inst, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(inst, &varId);
|
||||
if (ptrInst->opcode() != SpvOpVariable) continue;
|
||||
if (!IsUniformVar(varId)) continue;
|
||||
if (IsSamplerOrImageVar(varId)) continue;
|
||||
@ -385,7 +387,7 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
} else {
|
||||
// Copy load into most recent dominating block and remember it
|
||||
replId = TakeNextId();
|
||||
std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> newLoad(new opt::Instruction(
|
||||
context(), SpvOpLoad, inst->type_id(), replId,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newLoad);
|
||||
@ -406,14 +408,15 @@ bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::CommonUniformLoadElimBlock(ir::Function* func) {
|
||||
bool CommonUniformElimPass::CommonUniformLoadElimBlock(opt::Function* func) {
|
||||
bool modified = false;
|
||||
for (auto& blk : *func) {
|
||||
uniform2load_id_.clear();
|
||||
for (ir::Instruction* inst = &*blk.begin(); inst; inst = inst->NextNode()) {
|
||||
for (opt::Instruction* inst = &*blk.begin(); inst;
|
||||
inst = inst->NextNode()) {
|
||||
if (inst->opcode() != SpvOpLoad) continue;
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(inst, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(inst, &varId);
|
||||
if (ptrInst->opcode() != SpvOpVariable) continue;
|
||||
if (!IsUniformVar(varId)) continue;
|
||||
if (!IsSamplerOrImageVar(varId)) continue;
|
||||
@ -434,7 +437,7 @@ bool CommonUniformElimPass::CommonUniformLoadElimBlock(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) {
|
||||
bool CommonUniformElimPass::CommonExtractElimination(opt::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) {
|
||||
@ -457,7 +460,7 @@ bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) {
|
||||
for (auto idxItr : cItr->second) {
|
||||
if (idxItr.second.size() < 2) continue;
|
||||
uint32_t replId = TakeNextId();
|
||||
std::unique_ptr<ir::Instruction> newExtract(
|
||||
std::unique_ptr<opt::Instruction> newExtract(
|
||||
idxItr.second.front()->Clone(context()));
|
||||
newExtract->SetResultId(replId);
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newExtract);
|
||||
@ -476,7 +479,7 @@ bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool CommonUniformElimPass::EliminateCommonUniform(ir::Function* func) {
|
||||
bool CommonUniformElimPass::EliminateCommonUniform(opt::Function* func) {
|
||||
bool modified = false;
|
||||
modified |= UniformAccessChainConvert(func);
|
||||
modified |= CommonUniformLoadElimination(func);
|
||||
@ -486,7 +489,7 @@ bool CommonUniformElimPass::EliminateCommonUniform(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void CommonUniformElimPass::Initialize(ir::IRContext* c) {
|
||||
void CommonUniformElimPass::Initialize(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
// Clear collections.
|
||||
@ -525,12 +528,12 @@ Pass::Status CommonUniformElimPass::ProcessImpl() {
|
||||
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())
|
||||
for (const opt::Instruction& inst : get_module()->types_values())
|
||||
if (inst.opcode() == SpvOpTypeInt &&
|
||||
inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
|
||||
return Status::SuccessWithoutChange;
|
||||
// Process entry point functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return EliminateCommonUniform(fp);
|
||||
};
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
@ -539,7 +542,7 @@ Pass::Status CommonUniformElimPass::ProcessImpl() {
|
||||
|
||||
CommonUniformElimPass::CommonUniformElimPass() {}
|
||||
|
||||
Pass::Status CommonUniformElimPass::Process(ir::IRContext* c) {
|
||||
Pass::Status CommonUniformElimPass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -36,15 +36,15 @@ namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class CommonUniformElimPass : public Pass {
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
using cbb_ptr = const opt::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
std::function<std::vector<opt::BasicBlock*>*(const opt::BasicBlock*)>;
|
||||
|
||||
CommonUniformElimPass();
|
||||
const char* name() const override { return "eliminate-common-uniform"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
Status Process(opt::IRContext*) override;
|
||||
|
||||
private:
|
||||
// Returns true if |opcode| is a non-ptr access chain op
|
||||
@ -52,7 +52,7 @@ class CommonUniformElimPass : public Pass {
|
||||
|
||||
// Returns true if |typeInst| is a sampler or image type or a struct
|
||||
// containing one, recursively.
|
||||
bool IsSamplerOrImageType(const ir::Instruction* typeInst) const;
|
||||
bool IsSamplerOrImageType(const opt::Instruction* typeInst) const;
|
||||
|
||||
// Returns true if |varId| is a variable containing a sampler or image.
|
||||
bool IsSamplerOrImageVar(uint32_t varId) const;
|
||||
@ -60,7 +60,7 @@ class CommonUniformElimPass : public Pass {
|
||||
// Given a load or store pointed at by |ip|, return the top-most
|
||||
// non-CopyObj in its pointer operand. Also return the base pointer
|
||||
// in |objId|.
|
||||
ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* objId);
|
||||
opt::Instruction* GetPtr(opt::Instruction* ip, uint32_t* objId);
|
||||
|
||||
// Return true if variable is uniform
|
||||
bool IsUniformVar(uint32_t varId);
|
||||
@ -73,12 +73,12 @@ class CommonUniformElimPass : public Pass {
|
||||
// if the accessed variable belongs to a volatile
|
||||
// decorated object or member of a struct type
|
||||
bool IsAccessChainToVolatileStructType(
|
||||
const ir::Instruction& AccessChainInst);
|
||||
const opt::Instruction& AccessChainInst);
|
||||
|
||||
// Given an OpLoad instruction, return true if
|
||||
// OpLoad has a Volatile Memory Access flag or if
|
||||
// the resulting type is a volatile decorated struct
|
||||
bool IsVolatileLoad(const ir::Instruction& loadInst);
|
||||
bool IsVolatileLoad(const opt::Instruction& loadInst);
|
||||
|
||||
// Return true if any uses of |id| are decorate ops.
|
||||
bool HasUnsupportedDecorates(uint32_t id) const;
|
||||
@ -87,25 +87,25 @@ class CommonUniformElimPass : public Pass {
|
||||
bool HasOnlyNamesAndDecorates(uint32_t id) const;
|
||||
|
||||
// Delete inst if it has no uses. Assumes inst has a resultId.
|
||||
void DeleteIfUseless(ir::Instruction* inst);
|
||||
void DeleteIfUseless(opt::Instruction* inst);
|
||||
|
||||
// Replace all instances of load's id with replId and delete load
|
||||
// and its access chain, if any
|
||||
ir::Instruction* ReplaceAndDeleteLoad(ir::Instruction* loadInst,
|
||||
uint32_t replId,
|
||||
ir::Instruction* ptrInst);
|
||||
opt::Instruction* ReplaceAndDeleteLoad(opt::Instruction* loadInst,
|
||||
uint32_t replId,
|
||||
opt::Instruction* ptrInst);
|
||||
|
||||
// For the (constant index) access chain ptrInst, create an
|
||||
// equivalent load and extract
|
||||
void GenACLoadRepl(const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts,
|
||||
void GenACLoadRepl(const opt::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts,
|
||||
uint32_t* resultId);
|
||||
|
||||
// Return true if all indices are constant
|
||||
bool IsConstantIndexAccessChain(ir::Instruction* acp);
|
||||
bool IsConstantIndexAccessChain(opt::Instruction* acp);
|
||||
|
||||
// Convert all uniform access chain loads into load/extract.
|
||||
bool UniformAccessChainConvert(ir::Function* func);
|
||||
bool UniformAccessChainConvert(opt::Function* func);
|
||||
|
||||
// Compute structured successors for function |func|.
|
||||
// A block's structured successors are the blocks it branches to
|
||||
@ -117,7 +117,7 @@ class CommonUniformElimPass : public Pass {
|
||||
//
|
||||
// TODO(dnovillo): This pass computes structured successors slightly different
|
||||
// than the implementation in class Pass. Can this be re-factored?
|
||||
void ComputeStructuredSuccessors(ir::Function* func);
|
||||
void ComputeStructuredSuccessors(opt::Function* func);
|
||||
|
||||
// Compute structured block order for |func| into |structuredOrder|. This
|
||||
// order has the property that dominators come before all blocks they
|
||||
@ -126,24 +126,24 @@ class CommonUniformElimPass : public Pass {
|
||||
//
|
||||
// TODO(dnovillo): This pass computes structured order slightly different
|
||||
// than the implementation in class Pass. Can this be re-factored?
|
||||
void ComputeStructuredOrder(ir::Function* func,
|
||||
std::list<ir::BasicBlock*>* order);
|
||||
void ComputeStructuredOrder(opt::Function* func,
|
||||
std::list<opt::BasicBlock*>* order);
|
||||
|
||||
// Eliminate loads of uniform variables which have previously been loaded.
|
||||
// If first load is in control flow, move it to first block of function.
|
||||
// Most effective if preceded by UniformAccessChainRemoval().
|
||||
bool CommonUniformLoadElimination(ir::Function* func);
|
||||
bool CommonUniformLoadElimination(opt::Function* func);
|
||||
|
||||
// Eliminate loads of uniform sampler and image variables which have
|
||||
// previously
|
||||
// been loaded in the same block for types whose loads cannot cross blocks.
|
||||
bool CommonUniformLoadElimBlock(ir::Function* func);
|
||||
bool CommonUniformLoadElimBlock(opt::Function* func);
|
||||
|
||||
// Eliminate duplicated extracts of same id. Extract may be moved to same
|
||||
// block as the id definition. This is primarily intended for extracts
|
||||
// from uniform loads. Most effective if preceded by
|
||||
// CommonUniformLoadElimination().
|
||||
bool CommonExtractElimination(ir::Function* func);
|
||||
bool CommonExtractElimination(opt::Function* func);
|
||||
|
||||
// For function |func|, first change all uniform constant index
|
||||
// access chain loads into equivalent composite extracts. Then consolidate
|
||||
@ -157,7 +157,7 @@ class CommonUniformElimPass : public Pass {
|
||||
// is not enabled. It also currently does not support any extensions.
|
||||
//
|
||||
// This function currently only optimizes loads with a single index.
|
||||
bool EliminateCommonUniform(ir::Function* func);
|
||||
bool EliminateCommonUniform(opt::Function* func);
|
||||
|
||||
// Initialize extensions whitelist
|
||||
void InitExtensions();
|
||||
@ -172,10 +172,10 @@ class CommonUniformElimPass : public Pass {
|
||||
|
||||
// Return true if |inst| is an instruction that loads uniform variable and
|
||||
// can be replaced with other uniform load instruction.
|
||||
bool IsUniformLoadToBeRemoved(ir::Instruction* inst) {
|
||||
bool IsUniformLoadToBeRemoved(opt::Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpLoad) {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(inst, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(inst, &varId);
|
||||
if (ptrInst->opcode() == SpvOpVariable && IsUniformVar(varId) &&
|
||||
!IsSamplerOrImageVar(varId) &&
|
||||
!HasUnsupportedDecorates(inst->result_id()) && !IsVolatileLoad(*inst))
|
||||
@ -184,7 +184,7 @@ class CommonUniformElimPass : public Pass {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
// Map from uniform variable id to its common load id
|
||||
@ -193,7 +193,7 @@ class CommonUniformElimPass : public Pass {
|
||||
// Map of extract composite ids to map of indices to insts
|
||||
// TODO(greg-lunarg): Consider std::vector.
|
||||
std::unordered_map<uint32_t,
|
||||
std::unordered_map<uint32_t, std::list<ir::Instruction*>>>
|
||||
std::unordered_map<uint32_t, std::list<opt::Instruction*>>>
|
||||
comp2idx2inst_;
|
||||
|
||||
// Extensions supported by this pass.
|
||||
@ -201,7 +201,7 @@ class CommonUniformElimPass : public Pass {
|
||||
|
||||
// Map from block to its structured successor blocks. See
|
||||
// ComputeStructuredSuccessors() for definition.
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
std::unordered_map<const opt::BasicBlock*, std::vector<opt::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
};
|
||||
|
||||
|
@ -21,10 +21,10 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
using ir::Instruction;
|
||||
using ir::Operand;
|
||||
using opt::Instruction;
|
||||
using opt::Operand;
|
||||
|
||||
Pass::Status CompactIdsPass::Process(ir::IRContext* c) {
|
||||
Pass::Status CompactIdsPass::Process(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
bool modified = false;
|
||||
|
@ -26,13 +26,13 @@ namespace opt {
|
||||
class CompactIdsPass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "compact-ids"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
Status Process(opt::IRContext*) override;
|
||||
|
||||
// Return the mask of preserved Analyses.
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::kAnalysisDominatorAnalysis |
|
||||
ir::IRContext::kAnalysisLoopAnalysis;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::kAnalysisDominatorAnalysis |
|
||||
opt::IRContext::kAnalysisLoopAnalysis;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
|
||||
const ir::Instruction* insInst, const uint32_t extOffset) {
|
||||
const opt::Instruction* insInst, const uint32_t extOffset) {
|
||||
uint32_t numIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
|
||||
if (numIndices != insInst->NumInOperands() - 2) return false;
|
||||
for (uint32_t i = 0; i < numIndices; ++i)
|
||||
@ -36,7 +36,7 @@ bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
|
||||
}
|
||||
|
||||
bool ExtInsConflict(const std::vector<uint32_t>& extIndices,
|
||||
const ir::Instruction* insInst, const uint32_t extOffset) {
|
||||
const opt::Instruction* insInst, const uint32_t extOffset) {
|
||||
if (extIndices.size() - extOffset == insInst->NumInOperands() - 2)
|
||||
return false;
|
||||
uint32_t extNumIndices = static_cast<uint32_t>(extIndices.size()) - extOffset;
|
||||
|
@ -34,7 +34,7 @@ namespace opt {
|
||||
// Return true if the extract indices in |extIndices| starting at |extOffset|
|
||||
// match indices of insert |insInst|.
|
||||
bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
|
||||
const ir::Instruction* insInst, const uint32_t extOffset);
|
||||
const opt::Instruction* insInst, const uint32_t extOffset);
|
||||
|
||||
// Return true if indices in |extIndices| starting at |extOffset| and
|
||||
// indices of insert |insInst| conflict, specifically, if the insert
|
||||
@ -42,7 +42,7 @@ bool ExtInsMatch(const std::vector<uint32_t>& extIndices,
|
||||
// or less bits than the extract specifies, meaning the exact value being
|
||||
// inserted cannot be used to replace the extract.
|
||||
bool ExtInsConflict(const std::vector<uint32_t>& extIndices,
|
||||
const ir::Instruction* insInst, const uint32_t extOffset);
|
||||
const opt::Instruction* insInst, const uint32_t extOffset);
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -35,7 +35,7 @@ bool HasFloatingPoint(const analysis::Type* type) {
|
||||
|
||||
// Folds an OpcompositeExtract where input is a composite constant.
|
||||
ConstantFoldingRule FoldExtractWithConstants() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)
|
||||
-> const analysis::Constant* {
|
||||
const analysis::Constant* c = constants[kExtractCompositeIdInIdx];
|
||||
@ -47,7 +47,7 @@ ConstantFoldingRule FoldExtractWithConstants() {
|
||||
uint32_t element_index = inst->GetSingleWordInOperand(i);
|
||||
if (c->AsNullConstant()) {
|
||||
// Return Null for the return type.
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
return const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), {});
|
||||
@ -63,7 +63,7 @@ ConstantFoldingRule FoldExtractWithConstants() {
|
||||
}
|
||||
|
||||
ConstantFoldingRule FoldVectorShuffleWithConstants() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)
|
||||
-> const analysis::Constant* {
|
||||
assert(inst->opcode() == SpvOpVectorShuffle);
|
||||
@ -73,7 +73,7 @@ ConstantFoldingRule FoldVectorShuffleWithConstants() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* element_type = c1->type()->AsVector()->element_type();
|
||||
|
||||
@ -100,11 +100,11 @@ ConstantFoldingRule FoldVectorShuffleWithConstants() {
|
||||
for (uint32_t i = 2; i < inst->NumInOperands(); ++i) {
|
||||
uint32_t index = inst->GetSingleWordInOperand(i);
|
||||
if (index < c1_components.size()) {
|
||||
ir::Instruction* member_inst =
|
||||
opt::Instruction* member_inst =
|
||||
const_mgr->GetDefiningInstruction(c1_components[index]);
|
||||
ids.push_back(member_inst->result_id());
|
||||
} else {
|
||||
ir::Instruction* member_inst = const_mgr->GetDefiningInstruction(
|
||||
opt::Instruction* member_inst = const_mgr->GetDefiningInstruction(
|
||||
c2_components[index - c1_components.size()]);
|
||||
ids.push_back(member_inst->result_id());
|
||||
}
|
||||
@ -116,11 +116,11 @@ ConstantFoldingRule FoldVectorShuffleWithConstants() {
|
||||
}
|
||||
|
||||
ConstantFoldingRule FoldVectorTimesScalar() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)
|
||||
-> const analysis::Constant* {
|
||||
assert(inst->opcode() == SpvOpVectorTimesScalar);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
|
||||
@ -194,10 +194,10 @@ ConstantFoldingRule FoldVectorTimesScalar() {
|
||||
ConstantFoldingRule FoldCompositeWithConstants() {
|
||||
// Folds an OpCompositeConstruct where all of the inputs are constants to a
|
||||
// constant. A new constant is created if necessary.
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)
|
||||
-> const analysis::Constant* {
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
const analysis::Type* new_type = type_mgr->GetType(inst->type_id());
|
||||
@ -238,10 +238,10 @@ using BinaryScalarFoldingRule = std::function<const analysis::Constant*(
|
||||
// not |nullptr|, then their type is either |Float| or |Integer| or a |Vector|
|
||||
// whose element type is |Float| or |Integer|.
|
||||
ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) {
|
||||
return [scalar_rule](ir::Instruction* inst,
|
||||
return [scalar_rule](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)
|
||||
-> const analysis::Constant* {
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
|
||||
@ -288,10 +288,10 @@ ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) {
|
||||
// that |constants| contains 2 entries. If they are not |nullptr|, then their
|
||||
// type is either |Float| or a |Vector| whose element type is |Float|.
|
||||
ConstantFoldingRule FoldFPBinaryOp(BinaryScalarFoldingRule scalar_rule) {
|
||||
return [scalar_rule](ir::Instruction* inst,
|
||||
return [scalar_rule](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)
|
||||
-> const analysis::Constant* {
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
|
||||
@ -518,10 +518,10 @@ ConstantFoldingRule FoldFUnordGreaterThanEqual() {
|
||||
// Folds an OpDot where all of the inputs are constants to a
|
||||
// constant. A new constant is created if necessary.
|
||||
ConstantFoldingRule FoldOpDotWithConstants() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)
|
||||
-> const analysis::Constant* {
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
const analysis::Type* new_type = type_mgr->GetType(inst->type_id());
|
||||
@ -614,10 +614,10 @@ UnaryScalarFoldingRule FoldFNegateOp() {
|
||||
ConstantFoldingRule FoldFNegate() { return FoldFPUnaryOp(FoldFNegateOp()); }
|
||||
|
||||
ConstantFoldingRule FoldFClampFeedingCompare(uint32_t cmp_opcode) {
|
||||
return [cmp_opcode](ir::Instruction* inst,
|
||||
return [cmp_opcode](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)
|
||||
-> const analysis::Constant* {
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
|
||||
@ -627,7 +627,7 @@ ConstantFoldingRule FoldFClampFeedingCompare(uint32_t cmp_opcode) {
|
||||
|
||||
uint32_t non_const_idx = (constants[0] ? 1 : 0);
|
||||
uint32_t operand_id = inst->GetSingleWordInOperand(non_const_idx);
|
||||
ir::Instruction* operand_inst = def_use_mgr->GetDef(operand_id);
|
||||
opt::Instruction* operand_inst = def_use_mgr->GetDef(operand_id);
|
||||
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
const analysis::Type* operand_type =
|
||||
|
@ -48,7 +48,7 @@ namespace opt {
|
||||
// fold an instruction, the later rules will not be attempted.
|
||||
|
||||
using ConstantFoldingRule = std::function<const analysis::Constant*(
|
||||
ir::Instruction* inst,
|
||||
opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)>;
|
||||
|
||||
class ConstantFoldingRules {
|
||||
|
@ -102,7 +102,7 @@ int64_t Constant::GetS64() const {
|
||||
}
|
||||
}
|
||||
|
||||
ConstantManager::ConstantManager(ir::IRContext* ctx) : ctx_(ctx) {
|
||||
ConstantManager::ConstantManager(opt::IRContext* ctx) : ctx_(ctx) {
|
||||
// Populate the constant table with values from constant declarations in the
|
||||
// module. The values of each OpConstant declaration is the identity
|
||||
// assignment (i.e., each constant is its own value).
|
||||
@ -111,15 +111,15 @@ ConstantManager::ConstantManager(ir::IRContext* ctx) : ctx_(ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
Type* ConstantManager::GetType(const ir::Instruction* inst) const {
|
||||
Type* ConstantManager::GetType(const opt::Instruction* inst) const {
|
||||
return context()->get_type_mgr()->GetType(inst->type_id());
|
||||
}
|
||||
|
||||
std::vector<const Constant*> ConstantManager::GetOperandConstants(
|
||||
ir::Instruction* inst) const {
|
||||
opt::Instruction* inst) const {
|
||||
std::vector<const Constant*> constants;
|
||||
for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
|
||||
const ir::Operand* operand = &inst->GetInOperand(i);
|
||||
const opt::Operand* operand = &inst->GetInOperand(i);
|
||||
if (operand->type != SPV_OPERAND_TYPE_ID) {
|
||||
constants.push_back(nullptr);
|
||||
} else {
|
||||
@ -144,8 +144,8 @@ std::vector<const Constant*> ConstantManager::GetConstantsFromIds(
|
||||
return constants;
|
||||
}
|
||||
|
||||
ir::Instruction* ConstantManager::BuildInstructionAndAddToModule(
|
||||
const Constant* new_const, ir::Module::inst_iterator* pos,
|
||||
opt::Instruction* ConstantManager::BuildInstructionAndAddToModule(
|
||||
const Constant* new_const, opt::Module::inst_iterator* pos,
|
||||
uint32_t type_id) {
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
auto new_inst = CreateInstruction(new_id, new_const, type_id);
|
||||
@ -160,8 +160,8 @@ ir::Instruction* ConstantManager::BuildInstructionAndAddToModule(
|
||||
return new_inst_ptr;
|
||||
}
|
||||
|
||||
ir::Instruction* ConstantManager::GetDefiningInstruction(
|
||||
const Constant* c, uint32_t type_id, ir::Module::inst_iterator* pos) {
|
||||
opt::Instruction* ConstantManager::GetDefiningInstruction(
|
||||
const Constant* c, uint32_t type_id, opt::Module::inst_iterator* pos) {
|
||||
assert(type_id == 0 ||
|
||||
context()->get_type_mgr()->GetType(type_id) == c->type());
|
||||
uint32_t decl_id = FindDeclaredConstant(c);
|
||||
@ -231,7 +231,7 @@ const Constant* ConstantManager::CreateConstant(
|
||||
}
|
||||
}
|
||||
|
||||
const Constant* ConstantManager::GetConstantFromInst(ir::Instruction* inst) {
|
||||
const Constant* ConstantManager::GetConstantFromInst(opt::Instruction* inst) {
|
||||
std::vector<uint32_t> literal_words_or_ids;
|
||||
|
||||
// Collect the constant defining literals or component ids.
|
||||
@ -262,29 +262,29 @@ const Constant* ConstantManager::GetConstantFromInst(ir::Instruction* inst) {
|
||||
return GetConstant(GetType(inst), literal_words_or_ids);
|
||||
}
|
||||
|
||||
std::unique_ptr<ir::Instruction> ConstantManager::CreateInstruction(
|
||||
std::unique_ptr<opt::Instruction> ConstantManager::CreateInstruction(
|
||||
uint32_t id, const Constant* c, uint32_t type_id) const {
|
||||
uint32_t type =
|
||||
(type_id == 0) ? context()->get_type_mgr()->GetId(c->type()) : type_id;
|
||||
if (c->AsNullConstant()) {
|
||||
return MakeUnique<ir::Instruction>(context(), SpvOp::SpvOpConstantNull,
|
||||
type, id,
|
||||
std::initializer_list<ir::Operand>{});
|
||||
return MakeUnique<opt::Instruction>(context(), SpvOp::SpvOpConstantNull,
|
||||
type, id,
|
||||
std::initializer_list<opt::Operand>{});
|
||||
} else if (const BoolConstant* bc = c->AsBoolConstant()) {
|
||||
return MakeUnique<ir::Instruction>(
|
||||
return MakeUnique<opt::Instruction>(
|
||||
context(),
|
||||
bc->value() ? SpvOp::SpvOpConstantTrue : SpvOp::SpvOpConstantFalse,
|
||||
type, id, std::initializer_list<ir::Operand>{});
|
||||
type, id, std::initializer_list<opt::Operand>{});
|
||||
} else if (const IntConstant* ic = c->AsIntConstant()) {
|
||||
return MakeUnique<ir::Instruction>(
|
||||
return MakeUnique<opt::Instruction>(
|
||||
context(), SpvOp::SpvOpConstant, type, id,
|
||||
std::initializer_list<ir::Operand>{ir::Operand(
|
||||
std::initializer_list<opt::Operand>{opt::Operand(
|
||||
spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
|
||||
ic->words())});
|
||||
} else if (const FloatConstant* fc = c->AsFloatConstant()) {
|
||||
return MakeUnique<ir::Instruction>(
|
||||
return MakeUnique<opt::Instruction>(
|
||||
context(), SpvOp::SpvOpConstant, type, id,
|
||||
std::initializer_list<ir::Operand>{ir::Operand(
|
||||
std::initializer_list<opt::Operand>{opt::Operand(
|
||||
spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
|
||||
fc->words())});
|
||||
} else if (const CompositeConstant* cc = c->AsCompositeConstant()) {
|
||||
@ -294,9 +294,9 @@ std::unique_ptr<ir::Instruction> ConstantManager::CreateInstruction(
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ir::Instruction> ConstantManager::CreateCompositeInstruction(
|
||||
std::unique_ptr<opt::Instruction> ConstantManager::CreateCompositeInstruction(
|
||||
uint32_t result_id, const CompositeConstant* cc, uint32_t type_id) const {
|
||||
std::vector<ir::Operand> operands;
|
||||
std::vector<opt::Operand> operands;
|
||||
for (const Constant* component_const : cc->GetComponents()) {
|
||||
uint32_t id = FindDeclaredConstant(component_const);
|
||||
if (id == 0) {
|
||||
@ -310,8 +310,8 @@ std::unique_ptr<ir::Instruction> ConstantManager::CreateCompositeInstruction(
|
||||
}
|
||||
uint32_t type =
|
||||
(type_id == 0) ? context()->get_type_mgr()->GetId(cc->type()) : type_id;
|
||||
return MakeUnique<ir::Instruction>(context(), SpvOp::SpvOpConstantComposite,
|
||||
type, result_id, std::move(operands));
|
||||
return MakeUnique<opt::Instruction>(context(), SpvOp::SpvOpConstantComposite,
|
||||
type, result_id, std::move(operands));
|
||||
}
|
||||
|
||||
const Constant* ConstantManager::GetConstant(
|
||||
|
@ -490,9 +490,9 @@ struct ConstantEqual {
|
||||
// This class represents a pool of constants.
|
||||
class ConstantManager {
|
||||
public:
|
||||
ConstantManager(ir::IRContext* ctx);
|
||||
ConstantManager(opt::IRContext* ctx);
|
||||
|
||||
ir::IRContext* context() const { return ctx_; }
|
||||
opt::IRContext* context() const { return ctx_; }
|
||||
|
||||
// Gets or creates a unique Constant instance of type |type| and a vector of
|
||||
// constant defining words |words|. If a Constant instance existed already in
|
||||
@ -511,7 +511,7 @@ class ConstantManager {
|
||||
// Gets or creates a Constant instance to hold the constant value of the given
|
||||
// instruction. It returns a pointer to a Constant instance or nullptr if it
|
||||
// could not create the constant.
|
||||
const Constant* GetConstantFromInst(ir::Instruction* inst);
|
||||
const Constant* GetConstantFromInst(opt::Instruction* inst);
|
||||
|
||||
// Gets or creates a constant defining instruction for the given Constant |c|.
|
||||
// If |c| had already been defined, it returns a pointer to the existing
|
||||
@ -527,9 +527,9 @@ class ConstantManager {
|
||||
//
|
||||
// When |type_id| is not zero, the type of |c| must be the type returned by
|
||||
// type manager when given |type_id|.
|
||||
ir::Instruction* GetDefiningInstruction(
|
||||
opt::Instruction* GetDefiningInstruction(
|
||||
const Constant* c, uint32_t type_id = 0,
|
||||
ir::Module::inst_iterator* pos = nullptr);
|
||||
opt::Module::inst_iterator* pos = nullptr);
|
||||
|
||||
// Creates a constant defining instruction for the given Constant instance
|
||||
// and inserts the instruction at the position specified by the given
|
||||
@ -543,12 +543,12 @@ class ConstantManager {
|
||||
// |type_id| is specified, it is used as the type of the constant. Otherwise
|
||||
// the type of the constant is derived by getting an id from the type manager
|
||||
// for |c|.
|
||||
ir::Instruction* BuildInstructionAndAddToModule(
|
||||
const Constant* c, ir::Module::inst_iterator* pos, uint32_t type_id = 0);
|
||||
opt::Instruction* BuildInstructionAndAddToModule(
|
||||
const Constant* c, opt::Module::inst_iterator* pos, uint32_t type_id = 0);
|
||||
|
||||
// A helper function to get the result type of the given instruction. Returns
|
||||
// nullptr if the instruction does not have a type id (type id is 0).
|
||||
Type* GetType(const ir::Instruction* inst) const;
|
||||
Type* GetType(const opt::Instruction* inst) const;
|
||||
|
||||
// A helper function to get the collected normal constant with the given id.
|
||||
// Returns the pointer to the Constant instance in case it is found.
|
||||
@ -591,12 +591,13 @@ class ConstantManager {
|
||||
|
||||
// Returns a vector of constants representing each in operand. If an operand
|
||||
// is not constant its entry is nullptr.
|
||||
std::vector<const Constant*> GetOperandConstants(ir::Instruction* inst) const;
|
||||
std::vector<const Constant*> GetOperandConstants(
|
||||
opt::Instruction* inst) const;
|
||||
|
||||
// Records a mapping between |inst| and the constant value generated by it.
|
||||
// It returns true if a new Constant was successfully mapped, false if |inst|
|
||||
// generates no constant values.
|
||||
bool MapInst(ir::Instruction* inst) {
|
||||
bool MapInst(opt::Instruction* inst) {
|
||||
if (auto cst = GetConstantFromInst(inst)) {
|
||||
MapConstantToInst(cst, inst);
|
||||
return true;
|
||||
@ -614,7 +615,7 @@ class ConstantManager {
|
||||
|
||||
// Records a new mapping between |inst| and |const_value|. This updates the
|
||||
// two mappings |id_to_const_val_| and |const_val_to_id_|.
|
||||
void MapConstantToInst(const Constant* const_value, ir::Instruction* inst) {
|
||||
void MapConstantToInst(const Constant* const_value, opt::Instruction* inst) {
|
||||
const_val_to_id_[const_value] = inst->result_id();
|
||||
id_to_const_val_[inst->result_id()] = const_value;
|
||||
}
|
||||
@ -645,7 +646,7 @@ class ConstantManager {
|
||||
// |type_id| is specified, it is used as the type of the constant. Otherwise
|
||||
// the type of the constant is derived by getting an id from the type manager
|
||||
// for |c|.
|
||||
std::unique_ptr<ir::Instruction> CreateInstruction(
|
||||
std::unique_ptr<opt::Instruction> CreateInstruction(
|
||||
uint32_t result_id, const Constant* c, uint32_t type_id = 0) const;
|
||||
|
||||
// Creates an OpConstantComposite instruction with the given result id and
|
||||
@ -657,12 +658,12 @@ class ConstantManager {
|
||||
// |type_id| is specified, it is used as the type of the constant. Otherwise
|
||||
// the type of the constant is derived by getting an id from the type manager
|
||||
// for |c|.
|
||||
std::unique_ptr<ir::Instruction> CreateCompositeInstruction(
|
||||
std::unique_ptr<opt::Instruction> CreateCompositeInstruction(
|
||||
uint32_t result_id, const CompositeConstant* cc,
|
||||
uint32_t type_id = 0) const;
|
||||
|
||||
// IR context that owns this constant manager.
|
||||
ir::IRContext* ctx_;
|
||||
opt::IRContext* ctx_;
|
||||
|
||||
// A mapping from the result ids of Normal Constants to their
|
||||
// Constant instances. All Normal Constants in the module, either
|
||||
|
@ -25,12 +25,12 @@ const uint32_t kCompositeExtractObjectInOperand = 0;
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status CopyPropagateArrays::Process(ir::IRContext* ctx) {
|
||||
Pass::Status CopyPropagateArrays::Process(opt::IRContext* ctx) {
|
||||
InitializeProcessing(ctx);
|
||||
|
||||
bool modified = false;
|
||||
for (ir::Function& function : *get_module()) {
|
||||
ir::BasicBlock* entry_bb = &*function.begin();
|
||||
for (opt::Function& function : *get_module()) {
|
||||
opt::BasicBlock* entry_bb = &*function.begin();
|
||||
|
||||
for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable;
|
||||
++var_inst) {
|
||||
@ -39,7 +39,7 @@ Pass::Status CopyPropagateArrays::Process(ir::IRContext* ctx) {
|
||||
}
|
||||
|
||||
// Find the only store to the entire memory location, if it exists.
|
||||
ir::Instruction* store_inst = FindStoreInstruction(&*var_inst);
|
||||
opt::Instruction* store_inst = FindStoreInstruction(&*var_inst);
|
||||
|
||||
if (!store_inst) {
|
||||
continue;
|
||||
@ -60,8 +60,8 @@ Pass::Status CopyPropagateArrays::Process(ir::IRContext* ctx) {
|
||||
}
|
||||
|
||||
std::unique_ptr<CopyPropagateArrays::MemoryObject>
|
||||
CopyPropagateArrays::FindSourceObjectIfPossible(ir::Instruction* var_inst,
|
||||
ir::Instruction* store_inst) {
|
||||
CopyPropagateArrays::FindSourceObjectIfPossible(opt::Instruction* var_inst,
|
||||
opt::Instruction* store_inst) {
|
||||
assert(var_inst->opcode() == SpvOpVariable && "Expecting a variable.");
|
||||
|
||||
// Check that the variable is a composite object where |store_inst|
|
||||
@ -95,11 +95,11 @@ CopyPropagateArrays::FindSourceObjectIfPossible(ir::Instruction* var_inst,
|
||||
return source;
|
||||
}
|
||||
|
||||
ir::Instruction* CopyPropagateArrays::FindStoreInstruction(
|
||||
const ir::Instruction* var_inst) const {
|
||||
ir::Instruction* store_inst = nullptr;
|
||||
opt::Instruction* CopyPropagateArrays::FindStoreInstruction(
|
||||
const opt::Instruction* var_inst) const {
|
||||
opt::Instruction* store_inst = nullptr;
|
||||
get_def_use_mgr()->WhileEachUser(
|
||||
var_inst, [&store_inst, var_inst](ir::Instruction* use) {
|
||||
var_inst, [&store_inst, var_inst](opt::Instruction* use) {
|
||||
if (use->opcode() == SpvOpStore &&
|
||||
use->GetSingleWordInOperand(kStorePointerInOperand) ==
|
||||
var_inst->result_id()) {
|
||||
@ -115,24 +115,24 @@ ir::Instruction* CopyPropagateArrays::FindStoreInstruction(
|
||||
return store_inst;
|
||||
}
|
||||
|
||||
void CopyPropagateArrays::PropagateObject(ir::Instruction* var_inst,
|
||||
void CopyPropagateArrays::PropagateObject(opt::Instruction* var_inst,
|
||||
MemoryObject* source,
|
||||
ir::Instruction* insertion_point) {
|
||||
opt::Instruction* insertion_point) {
|
||||
assert(var_inst->opcode() == SpvOpVariable &&
|
||||
"This function propagates variables.");
|
||||
|
||||
ir::Instruction* new_access_chain =
|
||||
opt::Instruction* new_access_chain =
|
||||
BuildNewAccessChain(insertion_point, source);
|
||||
context()->KillNamesAndDecorates(var_inst);
|
||||
UpdateUses(var_inst, new_access_chain);
|
||||
}
|
||||
|
||||
ir::Instruction* CopyPropagateArrays::BuildNewAccessChain(
|
||||
ir::Instruction* insertion_point,
|
||||
opt::Instruction* CopyPropagateArrays::BuildNewAccessChain(
|
||||
opt::Instruction* insertion_point,
|
||||
CopyPropagateArrays::MemoryObject* source) const {
|
||||
InstructionBuilder builder(context(), insertion_point,
|
||||
ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping);
|
||||
opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping);
|
||||
|
||||
if (source->AccessChain().size() == 0) {
|
||||
return source->GetVariable();
|
||||
@ -143,9 +143,9 @@ ir::Instruction* CopyPropagateArrays::BuildNewAccessChain(
|
||||
source->AccessChain());
|
||||
}
|
||||
|
||||
bool CopyPropagateArrays::HasNoStores(ir::Instruction* ptr_inst) {
|
||||
bool CopyPropagateArrays::HasNoStores(opt::Instruction* ptr_inst) {
|
||||
return get_def_use_mgr()->WhileEachUser(
|
||||
ptr_inst, [this](ir::Instruction* use) {
|
||||
ptr_inst, [this](opt::Instruction* use) {
|
||||
if (use->opcode() == SpvOpLoad) {
|
||||
return true;
|
||||
} else if (use->opcode() == SpvOpAccessChain) {
|
||||
@ -162,15 +162,15 @@ bool CopyPropagateArrays::HasNoStores(ir::Instruction* ptr_inst) {
|
||||
});
|
||||
}
|
||||
|
||||
bool CopyPropagateArrays::HasValidReferencesOnly(ir::Instruction* ptr_inst,
|
||||
ir::Instruction* store_inst) {
|
||||
ir::BasicBlock* store_block = context()->get_instr_block(store_inst);
|
||||
bool CopyPropagateArrays::HasValidReferencesOnly(opt::Instruction* ptr_inst,
|
||||
opt::Instruction* store_inst) {
|
||||
opt::BasicBlock* store_block = context()->get_instr_block(store_inst);
|
||||
opt::DominatorAnalysis* dominator_analysis =
|
||||
context()->GetDominatorAnalysis(store_block->GetParent());
|
||||
|
||||
return get_def_use_mgr()->WhileEachUser(
|
||||
ptr_inst,
|
||||
[this, store_inst, dominator_analysis, ptr_inst](ir::Instruction* use) {
|
||||
[this, store_inst, dominator_analysis, ptr_inst](opt::Instruction* use) {
|
||||
if (use->opcode() == SpvOpLoad ||
|
||||
use->opcode() == SpvOpImageTexelPointer) {
|
||||
// TODO: If there are many load in the same BB as |store_inst| the
|
||||
@ -194,7 +194,7 @@ bool CopyPropagateArrays::HasValidReferencesOnly(ir::Instruction* ptr_inst,
|
||||
|
||||
std::unique_ptr<CopyPropagateArrays::MemoryObject>
|
||||
CopyPropagateArrays::GetSourceObjectIfAny(uint32_t result) {
|
||||
ir::Instruction* result_inst = context()->get_def_use_mgr()->GetDef(result);
|
||||
opt::Instruction* result_inst = context()->get_def_use_mgr()->GetDef(result);
|
||||
|
||||
switch (result_inst->opcode()) {
|
||||
case SpvOpLoad:
|
||||
@ -213,11 +213,11 @@ CopyPropagateArrays::GetSourceObjectIfAny(uint32_t result) {
|
||||
}
|
||||
|
||||
std::unique_ptr<CopyPropagateArrays::MemoryObject>
|
||||
CopyPropagateArrays::BuildMemoryObjectFromLoad(ir::Instruction* load_inst) {
|
||||
CopyPropagateArrays::BuildMemoryObjectFromLoad(opt::Instruction* load_inst) {
|
||||
std::vector<uint32_t> components_in_reverse;
|
||||
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||
|
||||
ir::Instruction* current_inst = def_use_mgr->GetDef(
|
||||
opt::Instruction* current_inst = def_use_mgr->GetDef(
|
||||
load_inst->GetSingleWordInOperand(kLoadPointerInOperand));
|
||||
|
||||
// Build the access chain for the memory object by collecting the indices used
|
||||
@ -252,7 +252,7 @@ CopyPropagateArrays::BuildMemoryObjectFromLoad(ir::Instruction* load_inst) {
|
||||
|
||||
std::unique_ptr<CopyPropagateArrays::MemoryObject>
|
||||
CopyPropagateArrays::BuildMemoryObjectFromExtract(
|
||||
ir::Instruction* extract_inst) {
|
||||
opt::Instruction* extract_inst) {
|
||||
assert(extract_inst->opcode() == SpvOpCompositeExtract &&
|
||||
"Expecting an OpCompositeExtract instruction.");
|
||||
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||
@ -283,7 +283,7 @@ CopyPropagateArrays::BuildMemoryObjectFromExtract(
|
||||
|
||||
std::unique_ptr<CopyPropagateArrays::MemoryObject>
|
||||
CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct(
|
||||
ir::Instruction* conststruct_inst) {
|
||||
opt::Instruction* conststruct_inst) {
|
||||
assert(conststruct_inst->opcode() == SpvOpCompositeConstruct &&
|
||||
"Expecting an OpCompositeConstruct instruction.");
|
||||
|
||||
@ -347,7 +347,8 @@ CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct(
|
||||
}
|
||||
|
||||
std::unique_ptr<CopyPropagateArrays::MemoryObject>
|
||||
CopyPropagateArrays::BuildMemoryObjectFromInsert(ir::Instruction* insert_inst) {
|
||||
CopyPropagateArrays::BuildMemoryObjectFromInsert(
|
||||
opt::Instruction* insert_inst) {
|
||||
assert(insert_inst->opcode() == SpvOpCompositeInsert &&
|
||||
"Expecting an OpCompositeInsert instruction.");
|
||||
|
||||
@ -406,7 +407,7 @@ CopyPropagateArrays::BuildMemoryObjectFromInsert(ir::Instruction* insert_inst) {
|
||||
|
||||
memory_object->GetParent();
|
||||
|
||||
ir::Instruction* current_insert =
|
||||
opt::Instruction* current_insert =
|
||||
def_use_mgr->GetDef(insert_inst->GetSingleWordInOperand(1));
|
||||
for (uint32_t i = number_of_elements - 1; i > 0; --i) {
|
||||
if (current_insert->opcode() != SpvOpCompositeInsert) {
|
||||
@ -468,7 +469,7 @@ bool CopyPropagateArrays::IsPointerToArrayType(uint32_t type_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CopyPropagateArrays::CanUpdateUses(ir::Instruction* original_ptr_inst,
|
||||
bool CopyPropagateArrays::CanUpdateUses(opt::Instruction* original_ptr_inst,
|
||||
uint32_t type_id) {
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||
@ -487,7 +488,7 @@ bool CopyPropagateArrays::CanUpdateUses(ir::Instruction* original_ptr_inst,
|
||||
|
||||
return def_use_mgr->WhileEachUse(
|
||||
original_ptr_inst,
|
||||
[this, type_mgr, const_mgr, type](ir::Instruction* use, uint32_t) {
|
||||
[this, type_mgr, const_mgr, type](opt::Instruction* use, uint32_t) {
|
||||
switch (use->opcode()) {
|
||||
case SpvOpLoad: {
|
||||
analysis::Pointer* pointer_type = type->AsPointer();
|
||||
@ -560,8 +561,8 @@ bool CopyPropagateArrays::CanUpdateUses(ir::Instruction* original_ptr_inst,
|
||||
}
|
||||
});
|
||||
}
|
||||
void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
|
||||
ir::Instruction* new_ptr_inst) {
|
||||
void CopyPropagateArrays::UpdateUses(opt::Instruction* original_ptr_inst,
|
||||
opt::Instruction* new_ptr_inst) {
|
||||
// TODO (s-perron): Keep the def-use manager up to date. Not done now because
|
||||
// it can cause problems for the |ForEachUse| traversals. Can be use by
|
||||
// keeping a list of instructions that need updating, and then updating them
|
||||
@ -571,14 +572,14 @@ void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
|
||||
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||
|
||||
std::vector<std::pair<ir::Instruction*, uint32_t> > uses;
|
||||
std::vector<std::pair<opt::Instruction*, uint32_t> > uses;
|
||||
def_use_mgr->ForEachUse(original_ptr_inst,
|
||||
[&uses](ir::Instruction* use, uint32_t index) {
|
||||
[&uses](opt::Instruction* use, uint32_t index) {
|
||||
uses.push_back({use, index});
|
||||
});
|
||||
|
||||
for (auto pair : uses) {
|
||||
ir::Instruction* use = pair.first;
|
||||
opt::Instruction* use = pair.first;
|
||||
uint32_t index = pair.second;
|
||||
analysis::Pointer* pointer_type = nullptr;
|
||||
switch (use->opcode()) {
|
||||
@ -671,7 +672,7 @@ void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
|
||||
// decomposing the object into the base type, which must be the same,
|
||||
// and then rebuilding them.
|
||||
if (index == 1) {
|
||||
ir::Instruction* target_pointer = def_use_mgr->GetDef(
|
||||
opt::Instruction* target_pointer = def_use_mgr->GetDef(
|
||||
use->GetSingleWordInOperand(kStorePointerInOperand));
|
||||
pointer_type =
|
||||
type_mgr->GetType(target_pointer->type_id())->AsPointer();
|
||||
@ -702,8 +703,8 @@ void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
|
||||
}
|
||||
|
||||
uint32_t CopyPropagateArrays::GenerateCopy(
|
||||
ir::Instruction* object_inst, uint32_t new_type_id,
|
||||
ir::Instruction* insertion_position) {
|
||||
opt::Instruction* object_inst, uint32_t new_type_id,
|
||||
opt::Instruction* insertion_position) {
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||
|
||||
@ -714,8 +715,8 @@ uint32_t CopyPropagateArrays::GenerateCopy(
|
||||
|
||||
opt::InstructionBuilder ir_builder(
|
||||
context(), insertion_position,
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::kAnalysisDefUse);
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::kAnalysisDefUse);
|
||||
|
||||
analysis::Type* original_type = type_mgr->GetType(original_type_id);
|
||||
analysis::Type* new_type = type_mgr->GetType(new_type_id);
|
||||
@ -735,7 +736,7 @@ uint32_t CopyPropagateArrays::GenerateCopy(
|
||||
assert(length_const->AsIntConstant());
|
||||
uint32_t array_length = length_const->AsIntConstant()->GetU32();
|
||||
for (uint32_t i = 0; i < array_length; i++) {
|
||||
ir::Instruction* extract = ir_builder.AddCompositeExtract(
|
||||
opt::Instruction* extract = ir_builder.AddCompositeExtract(
|
||||
original_element_type_id, object_inst->result_id(), {i});
|
||||
element_ids.push_back(
|
||||
GenerateCopy(extract, new_element_type_id, insertion_position));
|
||||
@ -753,7 +754,7 @@ uint32_t CopyPropagateArrays::GenerateCopy(
|
||||
new_struct_type->element_types();
|
||||
std::vector<uint32_t> element_ids;
|
||||
for (uint32_t i = 0; i < original_types.size(); i++) {
|
||||
ir::Instruction* extract = ir_builder.AddCompositeExtract(
|
||||
opt::Instruction* extract = ir_builder.AddCompositeExtract(
|
||||
type_mgr->GetId(original_types[i]), object_inst->result_id(), {i});
|
||||
element_ids.push_back(GenerateCopy(extract, type_mgr->GetId(new_types[i]),
|
||||
insertion_position));
|
||||
@ -777,7 +778,7 @@ void CopyPropagateArrays::MemoryObject::GetMember(
|
||||
}
|
||||
|
||||
uint32_t CopyPropagateArrays::MemoryObject::GetNumberOfMembers() {
|
||||
ir::IRContext* context = variable_inst_->context();
|
||||
opt::IRContext* context = variable_inst_->context();
|
||||
analysis::TypeManager* type_mgr = context->get_type_mgr();
|
||||
|
||||
const analysis::Type* type = type_mgr->GetType(variable_inst_->type_id());
|
||||
@ -804,7 +805,7 @@ uint32_t CopyPropagateArrays::MemoryObject::GetNumberOfMembers() {
|
||||
}
|
||||
|
||||
template <class iterator>
|
||||
CopyPropagateArrays::MemoryObject::MemoryObject(ir::Instruction* var_inst,
|
||||
CopyPropagateArrays::MemoryObject::MemoryObject(opt::Instruction* var_inst,
|
||||
iterator begin, iterator end)
|
||||
: variable_inst_(var_inst), access_chain_(begin, end) {}
|
||||
|
||||
|
@ -38,15 +38,15 @@ namespace opt {
|
||||
class CopyPropagateArrays : public MemPass {
|
||||
public:
|
||||
const char* name() const override { return "copy-propagate-arrays"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
Status Process(opt::IRContext*) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse | ir::IRContext::kAnalysisCFG |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::kAnalysisLoopAnalysis |
|
||||
ir::IRContext::kAnalysisDecorations |
|
||||
ir::IRContext::kAnalysisDominatorAnalysis |
|
||||
ir::IRContext::kAnalysisNameMap;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse | opt::IRContext::kAnalysisCFG |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::kAnalysisLoopAnalysis |
|
||||
opt::IRContext::kAnalysisDecorations |
|
||||
opt::IRContext::kAnalysisDominatorAnalysis |
|
||||
opt::IRContext::kAnalysisNameMap;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -62,7 +62,7 @@ class CopyPropagateArrays : public MemPass {
|
||||
// are interpreted the same way they would be in an |OpAccessChain|
|
||||
// instruction.
|
||||
template <class iterator>
|
||||
MemoryObject(ir::Instruction* var_inst, iterator begin, iterator end);
|
||||
MemoryObject(opt::Instruction* var_inst, iterator begin, iterator end);
|
||||
|
||||
// Change |this| to now point to the member identified by |access_chain|
|
||||
// (starting from the current member). The elements in |access_chain| are
|
||||
@ -87,7 +87,7 @@ class CopyPropagateArrays : public MemPass {
|
||||
uint32_t GetNumberOfMembers();
|
||||
|
||||
// Returns the owning variable that the memory object is contained in.
|
||||
ir::Instruction* GetVariable() const { return variable_inst_; }
|
||||
opt::Instruction* GetVariable() const { return variable_inst_; }
|
||||
|
||||
// Returns a vector of integers that can be used to access the specific
|
||||
// member that |this| represents starting from the owning variable. These
|
||||
@ -127,7 +127,7 @@ class CopyPropagateArrays : public MemPass {
|
||||
|
||||
private:
|
||||
// The variable that owns this memory object.
|
||||
ir::Instruction* variable_inst_;
|
||||
opt::Instruction* variable_inst_;
|
||||
|
||||
// The access chain to reach the particular member the memory object
|
||||
// represents. It should be interpreted the same way the indices in an
|
||||
@ -142,18 +142,18 @@ class CopyPropagateArrays : public MemPass {
|
||||
// and only identifies very simple cases. If no such memory object can be
|
||||
// found, the return value is |nullptr|.
|
||||
std::unique_ptr<CopyPropagateArrays::MemoryObject> FindSourceObjectIfPossible(
|
||||
ir::Instruction* var_inst, ir::Instruction* store_inst);
|
||||
opt::Instruction* var_inst, opt::Instruction* store_inst);
|
||||
|
||||
// Replaces all loads of |var_inst| with a load from |source| instead.
|
||||
// |insertion_pos| is a position where it is possible to construct the
|
||||
// address of |source| and also dominates all of the loads of |var_inst|.
|
||||
void PropagateObject(ir::Instruction* var_inst, MemoryObject* source,
|
||||
ir::Instruction* insertion_pos);
|
||||
void PropagateObject(opt::Instruction* var_inst, MemoryObject* source,
|
||||
opt::Instruction* insertion_pos);
|
||||
|
||||
// Returns true if all of the references to |ptr_inst| can be rewritten and
|
||||
// are dominated by |store_inst|.
|
||||
bool HasValidReferencesOnly(ir::Instruction* ptr_inst,
|
||||
ir::Instruction* store_inst);
|
||||
bool HasValidReferencesOnly(opt::Instruction* ptr_inst,
|
||||
opt::Instruction* store_inst);
|
||||
|
||||
// Returns a memory object that at one time was equivalent to the value in
|
||||
// |result|. If no such memory object exists, the return value is |nullptr|.
|
||||
@ -163,21 +163,21 @@ class CopyPropagateArrays : public MemPass {
|
||||
// object cannot be identified, the return value is |nullptr|. The opcode of
|
||||
// |load_inst| must be |OpLoad|.
|
||||
std::unique_ptr<MemoryObject> BuildMemoryObjectFromLoad(
|
||||
ir::Instruction* load_inst);
|
||||
opt::Instruction* load_inst);
|
||||
|
||||
// Returns the memory object that at some point was equivalent to the result
|
||||
// of |extract_inst|. If a memory object cannot be identified, the return
|
||||
// value is |nullptr|. The opcode of |extract_inst| must be
|
||||
// |OpCompositeExtract|.
|
||||
std::unique_ptr<MemoryObject> BuildMemoryObjectFromExtract(
|
||||
ir::Instruction* extract_inst);
|
||||
opt::Instruction* extract_inst);
|
||||
|
||||
// Returns the memory object that at some point was equivalent to the result
|
||||
// of |construct_inst|. If a memory object cannot be identified, the return
|
||||
// value is |nullptr|. The opcode of |constuct_inst| must be
|
||||
// |OpCompositeConstruct|.
|
||||
std::unique_ptr<MemoryObject> BuildMemoryObjectFromCompositeConstruct(
|
||||
ir::Instruction* conststruct_inst);
|
||||
opt::Instruction* conststruct_inst);
|
||||
|
||||
// Returns the memory object that at some point was equivalent to the result
|
||||
// of |insert_inst|. If a memory object cannot be identified, the return
|
||||
@ -186,43 +186,44 @@ class CopyPropagateArrays : public MemPass {
|
||||
// |OpCompositeInsert| instructions that insert the elements one at a time in
|
||||
// order from beginning to end.
|
||||
std::unique_ptr<MemoryObject> BuildMemoryObjectFromInsert(
|
||||
ir::Instruction* insert_inst);
|
||||
opt::Instruction* insert_inst);
|
||||
|
||||
// Return true if |type_id| is a pointer type whose pointee type is an array.
|
||||
bool IsPointerToArrayType(uint32_t type_id);
|
||||
|
||||
// Returns true of there are not stores using |ptr_inst| or something derived
|
||||
// from it.
|
||||
bool HasNoStores(ir::Instruction* ptr_inst);
|
||||
bool HasNoStores(opt::Instruction* ptr_inst);
|
||||
|
||||
// Creates an |OpAccessChain| instruction whose result is a pointer the memory
|
||||
// represented by |source|. The new instruction will be placed before
|
||||
// |insertion_point|. |insertion_point| must be part of a function. Returns
|
||||
// the new instruction.
|
||||
ir::Instruction* BuildNewAccessChain(ir::Instruction* insertion_point,
|
||||
MemoryObject* source) const;
|
||||
opt::Instruction* BuildNewAccessChain(opt::Instruction* insertion_point,
|
||||
MemoryObject* source) const;
|
||||
|
||||
// Rewrites all uses of |original_ptr| to use |new_pointer_inst| updating
|
||||
// types of other instructions as needed. This function should not be called
|
||||
// if |CanUpdateUses(original_ptr_inst, new_pointer_inst->type_id())| returns
|
||||
// false.
|
||||
void UpdateUses(ir::Instruction* original_ptr_inst,
|
||||
ir::Instruction* new_pointer_inst);
|
||||
void UpdateUses(opt::Instruction* original_ptr_inst,
|
||||
opt::Instruction* new_pointer_inst);
|
||||
|
||||
// Return true if |UpdateUses| is able to change all of the uses of
|
||||
// |original_ptr_inst| to |type_id| and still have valid code.
|
||||
bool CanUpdateUses(ir::Instruction* original_ptr_inst, uint32_t type_id);
|
||||
bool CanUpdateUses(opt::Instruction* original_ptr_inst, uint32_t type_id);
|
||||
|
||||
// Returns the id whose value is the same as |object_to_copy| except its type
|
||||
// is |new_type_id|. Any instructions need to generate this value will be
|
||||
// inserted before |insertion_position|.
|
||||
uint32_t GenerateCopy(ir::Instruction* object_to_copy, uint32_t new_type_id,
|
||||
ir::Instruction* insertion_position);
|
||||
uint32_t GenerateCopy(opt::Instruction* object_to_copy, uint32_t new_type_id,
|
||||
opt::Instruction* insertion_position);
|
||||
|
||||
// Returns a store to |var_inst| that writes to the entire variable, and is
|
||||
// the only store that does so. Note it does not look through OpAccessChain
|
||||
// instruction, so partial stores are not considered.
|
||||
ir::Instruction* FindStoreInstruction(const ir::Instruction* var_inst) const;
|
||||
opt::Instruction* FindStoreInstruction(
|
||||
const opt::Instruction* var_inst) const;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -34,7 +34,7 @@ const uint32_t kBranchCondFalseLabIdInIdx = 2;
|
||||
|
||||
bool DeadBranchElimPass::GetConstCondition(uint32_t condId, bool* condVal) {
|
||||
bool condIsConst;
|
||||
ir::Instruction* cInst = get_def_use_mgr()->GetDef(condId);
|
||||
opt::Instruction* cInst = get_def_use_mgr()->GetDef(condId);
|
||||
switch (cInst->opcode()) {
|
||||
case SpvOpConstantFalse: {
|
||||
*condVal = false;
|
||||
@ -56,9 +56,9 @@ bool DeadBranchElimPass::GetConstCondition(uint32_t condId, bool* condVal) {
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::GetConstInteger(uint32_t selId, uint32_t* selVal) {
|
||||
ir::Instruction* sInst = get_def_use_mgr()->GetDef(selId);
|
||||
opt::Instruction* sInst = get_def_use_mgr()->GetDef(selId);
|
||||
uint32_t typeId = sInst->type_id();
|
||||
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
|
||||
opt::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
|
||||
if (!typeInst || (typeInst->opcode() != SpvOpTypeInt)) return false;
|
||||
// TODO(greg-lunarg): Support non-32 bit ints
|
||||
if (typeInst->GetSingleWordInOperand(0) != 32) return false;
|
||||
@ -72,9 +72,9 @@ bool DeadBranchElimPass::GetConstInteger(uint32_t selId, uint32_t* selVal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeadBranchElimPass::AddBranch(uint32_t labelId, ir::BasicBlock* bp) {
|
||||
void DeadBranchElimPass::AddBranch(uint32_t labelId, opt::BasicBlock* bp) {
|
||||
assert(get_def_use_mgr()->GetDef(labelId) != nullptr);
|
||||
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> newBranch(new opt::Instruction(
|
||||
context(), SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
|
||||
context()->AnalyzeDefUse(&*newBranch);
|
||||
@ -82,18 +82,18 @@ void DeadBranchElimPass::AddBranch(uint32_t labelId, ir::BasicBlock* bp) {
|
||||
bp->AddInstruction(std::move(newBranch));
|
||||
}
|
||||
|
||||
ir::BasicBlock* DeadBranchElimPass::GetParentBlock(uint32_t id) {
|
||||
opt::BasicBlock* DeadBranchElimPass::GetParentBlock(uint32_t id) {
|
||||
return context()->get_instr_block(get_def_use_mgr()->GetDef(id));
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::MarkLiveBlocks(
|
||||
ir::Function* func, std::unordered_set<ir::BasicBlock*>* live_blocks) {
|
||||
std::unordered_set<ir::BasicBlock*> continues;
|
||||
std::vector<ir::BasicBlock*> stack;
|
||||
opt::Function* func, std::unordered_set<opt::BasicBlock*>* live_blocks) {
|
||||
std::unordered_set<opt::BasicBlock*> continues;
|
||||
std::vector<opt::BasicBlock*> stack;
|
||||
stack.push_back(&*func->begin());
|
||||
bool modified = false;
|
||||
while (!stack.empty()) {
|
||||
ir::BasicBlock* block = stack.back();
|
||||
opt::BasicBlock* block = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
// Live blocks doubles as visited set.
|
||||
@ -102,7 +102,7 @@ bool DeadBranchElimPass::MarkLiveBlocks(
|
||||
uint32_t cont_id = block->ContinueBlockIdIfAny();
|
||||
if (cont_id != 0) continues.insert(GetParentBlock(cont_id));
|
||||
|
||||
ir::Instruction* terminator = block->terminator();
|
||||
opt::Instruction* terminator = block->terminator();
|
||||
uint32_t live_lab_id = 0;
|
||||
// Check if the terminator has a single valid successor.
|
||||
if (terminator->opcode() == SpvOpBranchConditional) {
|
||||
@ -153,7 +153,7 @@ bool DeadBranchElimPass::MarkLiveBlocks(
|
||||
// Remove the merge instruction if it is a selection merge.
|
||||
AddBranch(live_lab_id, block);
|
||||
context()->KillInst(terminator);
|
||||
ir::Instruction* mergeInst = block->GetMergeInst();
|
||||
opt::Instruction* mergeInst = block->GetMergeInst();
|
||||
if (mergeInst && mergeInst->opcode() == SpvOpSelectionMerge) {
|
||||
context()->KillInst(mergeInst);
|
||||
}
|
||||
@ -171,18 +171,18 @@ bool DeadBranchElimPass::MarkLiveBlocks(
|
||||
}
|
||||
|
||||
void DeadBranchElimPass::MarkUnreachableStructuredTargets(
|
||||
const std::unordered_set<ir::BasicBlock*>& live_blocks,
|
||||
std::unordered_set<ir::BasicBlock*>* unreachable_merges,
|
||||
std::unordered_map<ir::BasicBlock*, ir::BasicBlock*>*
|
||||
const std::unordered_set<opt::BasicBlock*>& live_blocks,
|
||||
std::unordered_set<opt::BasicBlock*>* unreachable_merges,
|
||||
std::unordered_map<opt::BasicBlock*, opt::BasicBlock*>*
|
||||
unreachable_continues) {
|
||||
for (auto block : live_blocks) {
|
||||
if (auto merge_id = block->MergeBlockIdIfAny()) {
|
||||
ir::BasicBlock* merge_block = GetParentBlock(merge_id);
|
||||
opt::BasicBlock* merge_block = GetParentBlock(merge_id);
|
||||
if (!live_blocks.count(merge_block)) {
|
||||
unreachable_merges->insert(merge_block);
|
||||
}
|
||||
if (auto cont_id = block->ContinueBlockIdIfAny()) {
|
||||
ir::BasicBlock* cont_block = GetParentBlock(cont_id);
|
||||
opt::BasicBlock* cont_block = GetParentBlock(cont_id);
|
||||
if (!live_blocks.count(cont_block)) {
|
||||
(*unreachable_continues)[cont_block] = block;
|
||||
}
|
||||
@ -192,8 +192,9 @@ void DeadBranchElimPass::MarkUnreachableStructuredTargets(
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::FixPhiNodesInLiveBlocks(
|
||||
ir::Function* func, const std::unordered_set<ir::BasicBlock*>& live_blocks,
|
||||
const std::unordered_map<ir::BasicBlock*, ir::BasicBlock*>&
|
||||
opt::Function* func,
|
||||
const std::unordered_set<opt::BasicBlock*>& live_blocks,
|
||||
const std::unordered_map<opt::BasicBlock*, opt::BasicBlock*>&
|
||||
unreachable_continues) {
|
||||
bool modified = false;
|
||||
for (auto& block : *func) {
|
||||
@ -205,8 +206,8 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks(
|
||||
|
||||
bool changed = false;
|
||||
bool backedge_added = false;
|
||||
ir::Instruction* inst = &*iter;
|
||||
std::vector<ir::Operand> operands;
|
||||
opt::Instruction* inst = &*iter;
|
||||
std::vector<opt::Operand> operands;
|
||||
// Build a complete set of operands (not just input operands). Start
|
||||
// with type and result id operands.
|
||||
operands.push_back(inst->GetOperand(0u));
|
||||
@ -219,7 +220,8 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks(
|
||||
// However, if there is only one other incoming edge, the OpPhi can be
|
||||
// eliminated.
|
||||
for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) {
|
||||
ir::BasicBlock* inc = GetParentBlock(inst->GetSingleWordInOperand(i));
|
||||
opt::BasicBlock* inc =
|
||||
GetParentBlock(inst->GetSingleWordInOperand(i));
|
||||
auto cont_iter = unreachable_continues.find(inc);
|
||||
if (cont_iter != unreachable_continues.end() &&
|
||||
cont_iter->second == &block && inst->NumInOperands() > 4) {
|
||||
@ -302,9 +304,10 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks(
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::EraseDeadBlocks(
|
||||
ir::Function* func, const std::unordered_set<ir::BasicBlock*>& live_blocks,
|
||||
const std::unordered_set<ir::BasicBlock*>& unreachable_merges,
|
||||
const std::unordered_map<ir::BasicBlock*, ir::BasicBlock*>&
|
||||
opt::Function* func,
|
||||
const std::unordered_set<opt::BasicBlock*>& live_blocks,
|
||||
const std::unordered_set<opt::BasicBlock*>& unreachable_merges,
|
||||
const std::unordered_map<opt::BasicBlock*, opt::BasicBlock*>&
|
||||
unreachable_continues) {
|
||||
bool modified = false;
|
||||
for (auto ebi = func->begin(); ebi != func->end();) {
|
||||
@ -314,9 +317,9 @@ bool DeadBranchElimPass::EraseDeadBlocks(
|
||||
// Make unreachable, but leave the label.
|
||||
KillAllInsts(&*ebi, false);
|
||||
// Add unreachable terminator.
|
||||
ebi->AddInstruction(
|
||||
MakeUnique<ir::Instruction>(context(), SpvOpUnreachable, 0, 0,
|
||||
std::initializer_list<ir::Operand>{}));
|
||||
ebi->AddInstruction(MakeUnique<opt::Instruction>(
|
||||
context(), SpvOpUnreachable, 0, 0,
|
||||
std::initializer_list<opt::Operand>{}));
|
||||
context()->set_instr_block(&*ebi->tail(), &*ebi);
|
||||
modified = true;
|
||||
}
|
||||
@ -330,10 +333,10 @@ bool DeadBranchElimPass::EraseDeadBlocks(
|
||||
KillAllInsts(&*ebi, false);
|
||||
// Add unconditional branch to header.
|
||||
assert(unreachable_continues.count(&*ebi));
|
||||
ebi->AddInstruction(
|
||||
MakeUnique<ir::Instruction>(context(), SpvOpBranch, 0, 0,
|
||||
std::initializer_list<ir::Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {cont_id}}}));
|
||||
ebi->AddInstruction(MakeUnique<opt::Instruction>(
|
||||
context(), SpvOpBranch, 0, 0,
|
||||
std::initializer_list<opt::Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {cont_id}}}));
|
||||
get_def_use_mgr()->AnalyzeInstUse(&*ebi->tail());
|
||||
context()->set_instr_block(&*ebi->tail(), &*ebi);
|
||||
modified = true;
|
||||
@ -352,13 +355,13 @@ bool DeadBranchElimPass::EraseDeadBlocks(
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
bool DeadBranchElimPass::EliminateDeadBranches(opt::Function* func) {
|
||||
bool modified = false;
|
||||
std::unordered_set<ir::BasicBlock*> live_blocks;
|
||||
std::unordered_set<opt::BasicBlock*> live_blocks;
|
||||
modified |= MarkLiveBlocks(func, &live_blocks);
|
||||
|
||||
std::unordered_set<ir::BasicBlock*> unreachable_merges;
|
||||
std::unordered_map<ir::BasicBlock*, ir::BasicBlock*> unreachable_continues;
|
||||
std::unordered_set<opt::BasicBlock*> unreachable_merges;
|
||||
std::unordered_map<opt::BasicBlock*, opt::BasicBlock*> unreachable_continues;
|
||||
MarkUnreachableStructuredTargets(live_blocks, &unreachable_merges,
|
||||
&unreachable_continues);
|
||||
modified |= FixPhiNodesInLiveBlocks(func, live_blocks, unreachable_continues);
|
||||
@ -368,7 +371,7 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void DeadBranchElimPass::Initialize(ir::IRContext* c) {
|
||||
void DeadBranchElimPass::Initialize(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
}
|
||||
|
||||
@ -379,7 +382,7 @@ Pass::Status DeadBranchElimPass::ProcessImpl() {
|
||||
for (auto& ai : get_module()->annotations())
|
||||
if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return EliminateDeadBranches(fp);
|
||||
};
|
||||
bool modified = ProcessReachableCallTree(pfn, context());
|
||||
@ -388,7 +391,7 @@ Pass::Status DeadBranchElimPass::ProcessImpl() {
|
||||
|
||||
DeadBranchElimPass::DeadBranchElimPass() {}
|
||||
|
||||
Pass::Status DeadBranchElimPass::Process(ir::IRContext* module) {
|
||||
Pass::Status DeadBranchElimPass::Process(opt::IRContext* module) {
|
||||
Initialize(module);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -34,16 +34,16 @@ namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class DeadBranchElimPass : public MemPass {
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
using cbb_ptr = const opt::BasicBlock*;
|
||||
|
||||
public:
|
||||
DeadBranchElimPass();
|
||||
const char* name() const override { return "eliminate-dead-branches"; }
|
||||
Status Process(ir::IRContext* context) override;
|
||||
Status Process(opt::IRContext* context) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -56,7 +56,7 @@ class DeadBranchElimPass : public MemPass {
|
||||
bool GetConstInteger(uint32_t valId, uint32_t* value);
|
||||
|
||||
// Add branch to |labelId| to end of block |bp|.
|
||||
void AddBranch(uint32_t labelId, ir::BasicBlock* bp);
|
||||
void AddBranch(uint32_t labelId, opt::BasicBlock* bp);
|
||||
|
||||
// For function |func|, look for BranchConditionals with constant condition
|
||||
// and convert to a Branch to the indicated label. Delete resulting dead
|
||||
@ -64,21 +64,21 @@ class DeadBranchElimPass : public MemPass {
|
||||
// invalid control flow.
|
||||
// TODO(greg-lunarg): Remove remaining constant conditional branches and dead
|
||||
// blocks.
|
||||
bool EliminateDeadBranches(ir::Function* func);
|
||||
bool EliminateDeadBranches(opt::Function* func);
|
||||
|
||||
// Returns the basic block containing |id|.
|
||||
// Note: this pass only requires correct instruction block mappings for the
|
||||
// input. This pass does not preserve the block mapping, so it is not kept
|
||||
// up-to-date during processing.
|
||||
ir::BasicBlock* GetParentBlock(uint32_t id);
|
||||
opt::BasicBlock* GetParentBlock(uint32_t id);
|
||||
|
||||
// Marks live blocks reachable from the entry of |func|. Simplifies constant
|
||||
// branches and switches as it proceeds, to limit the number of live blocks.
|
||||
// It is careful not to eliminate backedges even if they are dead, but the
|
||||
// header is live. Likewise, unreachable merge blocks named in live merge
|
||||
// instruction must be retained (though they may be clobbered).
|
||||
bool MarkLiveBlocks(ir::Function* func,
|
||||
std::unordered_set<ir::BasicBlock*>* live_blocks);
|
||||
bool MarkLiveBlocks(opt::Function* func,
|
||||
std::unordered_set<opt::BasicBlock*>* live_blocks);
|
||||
|
||||
// Checks for unreachable merge and continue blocks with live headers; those
|
||||
// blocks must be retained. Continues are tracked separately so that a live
|
||||
@ -88,9 +88,9 @@ class DeadBranchElimPass : public MemPass {
|
||||
// |unreachable_continues| maps the id of an unreachable continue target to
|
||||
// the header block that declares it.
|
||||
void MarkUnreachableStructuredTargets(
|
||||
const std::unordered_set<ir::BasicBlock*>& live_blocks,
|
||||
std::unordered_set<ir::BasicBlock*>* unreachable_merges,
|
||||
std::unordered_map<ir::BasicBlock*, ir::BasicBlock*>*
|
||||
const std::unordered_set<opt::BasicBlock*>& live_blocks,
|
||||
std::unordered_set<opt::BasicBlock*>* unreachable_merges,
|
||||
std::unordered_map<opt::BasicBlock*, opt::BasicBlock*>*
|
||||
unreachable_continues);
|
||||
|
||||
// Fix phis in reachable blocks so that only live (or unremovable) incoming
|
||||
@ -106,9 +106,9 @@ class DeadBranchElimPass : public MemPass {
|
||||
// |unreachable_continues| maps continue targets that cannot be reached to
|
||||
// merge instruction that declares them.
|
||||
bool FixPhiNodesInLiveBlocks(
|
||||
ir::Function* func,
|
||||
const std::unordered_set<ir::BasicBlock*>& live_blocks,
|
||||
const std::unordered_map<ir::BasicBlock*, ir::BasicBlock*>&
|
||||
opt::Function* func,
|
||||
const std::unordered_set<opt::BasicBlock*>& live_blocks,
|
||||
const std::unordered_map<opt::BasicBlock*, opt::BasicBlock*>&
|
||||
unreachable_continues);
|
||||
|
||||
// Erases dead blocks. Any block captured in |unreachable_merges| or
|
||||
@ -123,13 +123,13 @@ class DeadBranchElimPass : public MemPass {
|
||||
// |unreachable_continues| maps continue targets that cannot be reached to
|
||||
// corresponding header block that declares them.
|
||||
bool EraseDeadBlocks(
|
||||
ir::Function* func,
|
||||
const std::unordered_set<ir::BasicBlock*>& live_blocks,
|
||||
const std::unordered_set<ir::BasicBlock*>& unreachable_merges,
|
||||
const std::unordered_map<ir::BasicBlock*, ir::BasicBlock*>&
|
||||
opt::Function* func,
|
||||
const std::unordered_set<opt::BasicBlock*>& live_blocks,
|
||||
const std::unordered_set<opt::BasicBlock*>& unreachable_merges,
|
||||
const std::unordered_map<opt::BasicBlock*, opt::BasicBlock*>&
|
||||
unreachable_continues);
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
};
|
||||
|
||||
|
@ -38,7 +38,7 @@ const uint32_t kInsertCompositeIdInIdx = 1;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
uint32_t DeadInsertElimPass::NumComponents(ir::Instruction* typeInst) {
|
||||
uint32_t DeadInsertElimPass::NumComponents(opt::Instruction* typeInst) {
|
||||
switch (typeInst->opcode()) {
|
||||
case SpvOpTypeVector: {
|
||||
return typeInst->GetSingleWordInOperand(kTypeVectorCountInIdx);
|
||||
@ -49,10 +49,10 @@ uint32_t DeadInsertElimPass::NumComponents(ir::Instruction* typeInst) {
|
||||
case SpvOpTypeArray: {
|
||||
uint32_t lenId =
|
||||
typeInst->GetSingleWordInOperand(kTypeArrayLengthIdInIdx);
|
||||
ir::Instruction* lenInst = get_def_use_mgr()->GetDef(lenId);
|
||||
opt::Instruction* lenInst = get_def_use_mgr()->GetDef(lenId);
|
||||
if (lenInst->opcode() != SpvOpConstant) return 0;
|
||||
uint32_t lenTypeId = lenInst->type_id();
|
||||
ir::Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId);
|
||||
opt::Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId);
|
||||
// TODO(greg-lunarg): Support non-32-bit array length
|
||||
if (lenTypeInst->GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
|
||||
return 0;
|
||||
@ -66,10 +66,11 @@ uint32_t DeadInsertElimPass::NumComponents(ir::Instruction* typeInst) {
|
||||
}
|
||||
|
||||
void DeadInsertElimPass::MarkInsertChain(
|
||||
ir::Instruction* insertChain, std::vector<uint32_t>* pExtIndices,
|
||||
opt::Instruction* insertChain, std::vector<uint32_t>* pExtIndices,
|
||||
uint32_t extOffset, std::unordered_set<uint32_t>* visited_phis) {
|
||||
// Not currently optimizing array inserts.
|
||||
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(insertChain->type_id());
|
||||
opt::Instruction* typeInst =
|
||||
get_def_use_mgr()->GetDef(insertChain->type_id());
|
||||
if (typeInst->opcode() == SpvOpTypeArray) return;
|
||||
// Insert chains are only composed of inserts and phis
|
||||
if (insertChain->opcode() != SpvOpCompositeInsert &&
|
||||
@ -90,7 +91,7 @@ void DeadInsertElimPass::MarkInsertChain(
|
||||
return;
|
||||
}
|
||||
}
|
||||
ir::Instruction* insInst = insertChain;
|
||||
opt::Instruction* insInst = insertChain;
|
||||
while (insInst->opcode() == SpvOpCompositeInsert) {
|
||||
// If no extract indices, mark insert and inserted object (which might
|
||||
// also be an insert chain) and continue up the chain though the input
|
||||
@ -159,12 +160,12 @@ void DeadInsertElimPass::MarkInsertChain(
|
||||
std::sort(ids.begin(), ids.end());
|
||||
auto new_end = std::unique(ids.begin(), ids.end());
|
||||
for (auto id_iter = ids.begin(); id_iter != new_end; ++id_iter) {
|
||||
ir::Instruction* pi = get_def_use_mgr()->GetDef(*id_iter);
|
||||
opt::Instruction* pi = get_def_use_mgr()->GetDef(*id_iter);
|
||||
MarkInsertChain(pi, pExtIndices, extOffset, visited_phis);
|
||||
}
|
||||
}
|
||||
|
||||
bool DeadInsertElimPass::EliminateDeadInserts(ir::Function* func) {
|
||||
bool DeadInsertElimPass::EliminateDeadInserts(opt::Function* func) {
|
||||
bool modified = false;
|
||||
bool lastmodified = true;
|
||||
// Each pass can delete dead instructions, thus potentially revealing
|
||||
@ -176,7 +177,7 @@ bool DeadInsertElimPass::EliminateDeadInserts(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool DeadInsertElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
|
||||
bool DeadInsertElimPass::EliminateDeadInsertsOnePass(opt::Function* func) {
|
||||
bool modified = false;
|
||||
liveInserts_.clear();
|
||||
visitedPhis_.clear();
|
||||
@ -185,7 +186,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
// Only process Inserts and composite Phis
|
||||
SpvOp op = ii->opcode();
|
||||
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id());
|
||||
opt::Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id());
|
||||
if (op != SpvOpCompositeInsert &&
|
||||
(op != SpvOpPhi || !spvOpcodeIsComposite(typeInst->opcode())))
|
||||
continue;
|
||||
@ -200,7 +201,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
|
||||
}
|
||||
}
|
||||
const uint32_t id = ii->result_id();
|
||||
get_def_use_mgr()->ForEachUser(id, [&ii, this](ir::Instruction* user) {
|
||||
get_def_use_mgr()->ForEachUser(id, [&ii, this](opt::Instruction* user) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpCompositeInsert:
|
||||
case SpvOpPhi:
|
||||
@ -227,7 +228,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
|
||||
}
|
||||
}
|
||||
// Find and disconnect dead inserts
|
||||
std::vector<ir::Instruction*> dead_instructions;
|
||||
std::vector<opt::Instruction*> dead_instructions;
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
if (ii->opcode() != SpvOpCompositeInsert) continue;
|
||||
@ -242,9 +243,9 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
|
||||
}
|
||||
// DCE dead inserts
|
||||
while (!dead_instructions.empty()) {
|
||||
ir::Instruction* inst = dead_instructions.back();
|
||||
opt::Instruction* inst = dead_instructions.back();
|
||||
dead_instructions.pop_back();
|
||||
DCEInst(inst, [&dead_instructions](ir::Instruction* other_inst) {
|
||||
DCEInst(inst, [&dead_instructions](opt::Instruction* other_inst) {
|
||||
auto i = std::find(dead_instructions.begin(), dead_instructions.end(),
|
||||
other_inst);
|
||||
if (i != dead_instructions.end()) {
|
||||
@ -255,13 +256,13 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void DeadInsertElimPass::Initialize(ir::IRContext* c) {
|
||||
void DeadInsertElimPass::Initialize(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
}
|
||||
|
||||
Pass::Status DeadInsertElimPass::ProcessImpl() {
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return EliminateDeadInserts(fp);
|
||||
};
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
@ -270,7 +271,7 @@ Pass::Status DeadInsertElimPass::ProcessImpl() {
|
||||
|
||||
DeadInsertElimPass::DeadInsertElimPass() {}
|
||||
|
||||
Pass::Status DeadInsertElimPass::Process(ir::IRContext* c) {
|
||||
Pass::Status DeadInsertElimPass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -37,45 +37,45 @@ class DeadInsertElimPass : public MemPass {
|
||||
public:
|
||||
DeadInsertElimPass();
|
||||
const char* name() const override { return "eliminate-dead-inserts"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
virtual ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::kAnalysisDecorations |
|
||||
ir::IRContext::kAnalysisCombinators | ir::IRContext::kAnalysisCFG |
|
||||
ir::IRContext::kAnalysisDominatorAnalysis |
|
||||
ir::IRContext::kAnalysisNameMap;
|
||||
Status Process(opt::IRContext*) override;
|
||||
virtual opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::kAnalysisDecorations |
|
||||
opt::IRContext::kAnalysisCombinators | opt::IRContext::kAnalysisCFG |
|
||||
opt::IRContext::kAnalysisDominatorAnalysis |
|
||||
opt::IRContext::kAnalysisNameMap;
|
||||
}
|
||||
|
||||
private:
|
||||
// Return the number of subcomponents in the composite type |typeId|.
|
||||
// Return 0 if not a composite type or number of components is not a
|
||||
// 32-bit constant.
|
||||
uint32_t NumComponents(ir::Instruction* typeInst);
|
||||
uint32_t NumComponents(opt::Instruction* typeInst);
|
||||
|
||||
// Mark all inserts in instruction chain ending at |insertChain| with
|
||||
// indices that intersect with extract indices |extIndices| starting with
|
||||
// index at |extOffset|. Chains are composed solely of Inserts and Phis.
|
||||
// Mark all inserts in chain if |extIndices| is nullptr.
|
||||
void MarkInsertChain(ir::Instruction* insertChain,
|
||||
void MarkInsertChain(opt::Instruction* insertChain,
|
||||
std::vector<uint32_t>* extIndices, uint32_t extOffset,
|
||||
std::unordered_set<uint32_t>* visited_phis);
|
||||
|
||||
// Perform EliminateDeadInsertsOnePass(|func|) until no modification is
|
||||
// made. Return true if modified.
|
||||
bool EliminateDeadInserts(ir::Function* func);
|
||||
bool EliminateDeadInserts(opt::Function* func);
|
||||
|
||||
// DCE all dead struct, matrix and vector inserts in |func|. An insert is
|
||||
// dead if the value it inserts is never used. Replace any reference to the
|
||||
// insert with its original composite. Return true if modified. Dead inserts
|
||||
// in dependence cycles are not currently eliminated. Dead inserts into
|
||||
// arrays are not currently eliminated.
|
||||
bool EliminateDeadInsertsOnePass(ir::Function* func);
|
||||
bool EliminateDeadInsertsOnePass(opt::Function* func);
|
||||
|
||||
// Return true if all extensions in this module are allowed by this pass.
|
||||
bool AllExtensionsSupported() const;
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
// Live inserts
|
||||
|
@ -22,7 +22,7 @@ namespace opt {
|
||||
|
||||
// This optimization removes global variables that are not needed because they
|
||||
// are definitely not accessed.
|
||||
Pass::Status DeadVariableElimination::Process(ir::IRContext* c) {
|
||||
Pass::Status DeadVariableElimination::Process(opt::IRContext* c) {
|
||||
// The algorithm will compute the reference count for every global variable.
|
||||
// Anything with a reference count of 0 will then be deleted. For variables
|
||||
// that might have references that are not explicit in this context, we use
|
||||
@ -45,7 +45,7 @@ Pass::Status DeadVariableElimination::Process(ir::IRContext* c) {
|
||||
// else, so we must keep the variable around.
|
||||
get_decoration_mgr()->ForEachDecoration(
|
||||
result_id, SpvDecorationLinkageAttributes,
|
||||
[&count](const ir::Instruction& linkage_instruction) {
|
||||
[&count](const opt::Instruction& linkage_instruction) {
|
||||
uint32_t last_operand = linkage_instruction.NumOperands() - 1;
|
||||
if (linkage_instruction.GetSingleWordOperand(last_operand) ==
|
||||
SpvLinkageTypeExport) {
|
||||
@ -58,8 +58,8 @@ Pass::Status DeadVariableElimination::Process(ir::IRContext* c) {
|
||||
// at the uses and count the number of real references.
|
||||
count = 0;
|
||||
get_def_use_mgr()->ForEachUser(
|
||||
result_id, [&count](ir::Instruction* user) {
|
||||
if (!ir::IsAnnotationInst(user->opcode()) &&
|
||||
result_id, [&count](opt::Instruction* user) {
|
||||
if (!opt::IsAnnotationInst(user->opcode()) &&
|
||||
user->opcode() != SpvOpName) {
|
||||
++count;
|
||||
}
|
||||
@ -83,14 +83,14 @@ Pass::Status DeadVariableElimination::Process(ir::IRContext* c) {
|
||||
}
|
||||
|
||||
void DeadVariableElimination::DeleteVariable(uint32_t result_id) {
|
||||
ir::Instruction* inst = get_def_use_mgr()->GetDef(result_id);
|
||||
opt::Instruction* inst = get_def_use_mgr()->GetDef(result_id);
|
||||
assert(inst->opcode() == SpvOpVariable &&
|
||||
"Should not be trying to delete anything other than an OpVariable.");
|
||||
|
||||
// Look for an initializer that references another variable. We need to know
|
||||
// if that variable can be deleted after the reference is removed.
|
||||
if (inst->NumOperands() == 4) {
|
||||
ir::Instruction* initializer =
|
||||
opt::Instruction* initializer =
|
||||
get_def_use_mgr()->GetDef(inst->GetSingleWordOperand(3));
|
||||
|
||||
// TODO: Handle OpSpecConstantOP which might be defined in terms of other
|
||||
|
@ -27,10 +27,10 @@ namespace opt {
|
||||
class DeadVariableElimination : public MemPass {
|
||||
public:
|
||||
const char* name() const override { return "dead-variable-elimination"; }
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -25,35 +25,35 @@ namespace opt {
|
||||
namespace analysis {
|
||||
|
||||
void DecorationManager::RemoveDecorationsFrom(
|
||||
uint32_t id, std::function<bool(const ir::Instruction&)> pred) {
|
||||
uint32_t id, std::function<bool(const opt::Instruction&)> pred) {
|
||||
const auto ids_iter = id_to_decoration_insts_.find(id);
|
||||
if (ids_iter == id_to_decoration_insts_.end()) return;
|
||||
|
||||
TargetData& decorations_info = ids_iter->second;
|
||||
auto context = module_->context();
|
||||
std::vector<ir::Instruction*> insts_to_kill;
|
||||
std::vector<opt::Instruction*> insts_to_kill;
|
||||
const bool is_group = !decorations_info.decorate_insts.empty();
|
||||
|
||||
// Schedule all direct decorations for removal if instructed as such by
|
||||
// |pred|.
|
||||
for (ir::Instruction* inst : decorations_info.direct_decorations)
|
||||
for (opt::Instruction* inst : decorations_info.direct_decorations)
|
||||
if (pred(*inst)) insts_to_kill.push_back(inst);
|
||||
|
||||
// For all groups being directly applied to |id|, remove |id| (and the
|
||||
// literal if |inst| is an OpGroupMemberDecorate) from the instruction
|
||||
// applying the group.
|
||||
std::unordered_set<const ir::Instruction*> indirect_decorations_to_remove;
|
||||
for (ir::Instruction* inst : decorations_info.indirect_decorations) {
|
||||
std::unordered_set<const opt::Instruction*> indirect_decorations_to_remove;
|
||||
for (opt::Instruction* inst : decorations_info.indirect_decorations) {
|
||||
assert(inst->opcode() == SpvOpGroupDecorate ||
|
||||
inst->opcode() == SpvOpGroupMemberDecorate);
|
||||
|
||||
std::vector<ir::Instruction*> group_decorations_to_keep;
|
||||
std::vector<opt::Instruction*> group_decorations_to_keep;
|
||||
const uint32_t group_id = inst->GetSingleWordInOperand(0u);
|
||||
const auto group_iter = id_to_decoration_insts_.find(group_id);
|
||||
assert(group_iter != id_to_decoration_insts_.end() &&
|
||||
"Unknown decoration group");
|
||||
const auto& group_decorations = group_iter->second.direct_decorations;
|
||||
for (ir::Instruction* decoration : group_decorations) {
|
||||
for (opt::Instruction* decoration : group_decorations) {
|
||||
if (!pred(*decoration)) group_decorations_to_keep.push_back(decoration);
|
||||
}
|
||||
|
||||
@ -97,9 +97,9 @@ void DecorationManager::RemoveDecorationsFrom(
|
||||
// If only some of the decorations should be kept, clone them and apply
|
||||
// them directly to |id|.
|
||||
if (!group_decorations_to_keep.empty()) {
|
||||
for (ir::Instruction* decoration : group_decorations_to_keep) {
|
||||
for (opt::Instruction* decoration : group_decorations_to_keep) {
|
||||
// simply clone decoration and change |group_id| to |id|
|
||||
std::unique_ptr<ir::Instruction> new_inst(
|
||||
std::unique_ptr<opt::Instruction> new_inst(
|
||||
decoration->Clone(module_->context()));
|
||||
new_inst->SetInOperand(0, {id});
|
||||
module_->AddAnnotationInst(std::move(new_inst));
|
||||
@ -113,22 +113,22 @@ void DecorationManager::RemoveDecorationsFrom(
|
||||
indirect_decorations.erase(
|
||||
std::remove_if(
|
||||
indirect_decorations.begin(), indirect_decorations.end(),
|
||||
[&indirect_decorations_to_remove](const ir::Instruction* inst) {
|
||||
[&indirect_decorations_to_remove](const opt::Instruction* inst) {
|
||||
return indirect_decorations_to_remove.count(inst);
|
||||
}),
|
||||
indirect_decorations.end());
|
||||
|
||||
for (ir::Instruction* inst : insts_to_kill) context->KillInst(inst);
|
||||
for (opt::Instruction* inst : insts_to_kill) context->KillInst(inst);
|
||||
insts_to_kill.clear();
|
||||
|
||||
// Schedule all instructions applying the group for removal if this group no
|
||||
// longer applies decorations, either directly or indirectly.
|
||||
if (is_group && decorations_info.direct_decorations.empty() &&
|
||||
decorations_info.indirect_decorations.empty()) {
|
||||
for (ir::Instruction* inst : decorations_info.decorate_insts)
|
||||
for (opt::Instruction* inst : decorations_info.decorate_insts)
|
||||
insts_to_kill.push_back(inst);
|
||||
}
|
||||
for (ir::Instruction* inst : insts_to_kill) context->KillInst(inst);
|
||||
for (opt::Instruction* inst : insts_to_kill) context->KillInst(inst);
|
||||
|
||||
if (decorations_info.direct_decorations.empty() &&
|
||||
decorations_info.indirect_decorations.empty() &&
|
||||
@ -140,20 +140,20 @@ void DecorationManager::RemoveDecorationsFrom(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ir::Instruction*> DecorationManager::GetDecorationsFor(
|
||||
std::vector<opt::Instruction*> DecorationManager::GetDecorationsFor(
|
||||
uint32_t id, bool include_linkage) {
|
||||
return InternalGetDecorationsFor<ir::Instruction*>(id, include_linkage);
|
||||
return InternalGetDecorationsFor<opt::Instruction*>(id, include_linkage);
|
||||
}
|
||||
|
||||
std::vector<const ir::Instruction*> DecorationManager::GetDecorationsFor(
|
||||
std::vector<const opt::Instruction*> DecorationManager::GetDecorationsFor(
|
||||
uint32_t id, bool include_linkage) const {
|
||||
return const_cast<DecorationManager*>(this)
|
||||
->InternalGetDecorationsFor<const ir::Instruction*>(id, include_linkage);
|
||||
->InternalGetDecorationsFor<const opt::Instruction*>(id, include_linkage);
|
||||
}
|
||||
|
||||
bool DecorationManager::HaveTheSameDecorations(uint32_t id1,
|
||||
uint32_t id2) const {
|
||||
using InstructionList = std::vector<const ir::Instruction*>;
|
||||
using InstructionList = std::vector<const opt::Instruction*>;
|
||||
using DecorationSet = std::set<std::u32string>;
|
||||
|
||||
const InstructionList decorations_for1 = GetDecorationsFor(id1, false);
|
||||
@ -167,7 +167,7 @@ bool DecorationManager::HaveTheSameDecorations(uint32_t id1,
|
||||
[](const InstructionList& decoration_list, DecorationSet* decorate_set,
|
||||
DecorationSet* decorate_id_set, DecorationSet* decorate_string_set,
|
||||
DecorationSet* member_decorate_set) {
|
||||
for (const ir::Instruction* inst : decoration_list) {
|
||||
for (const opt::Instruction* inst : decoration_list) {
|
||||
std::u32string decoration_payload;
|
||||
// Ignore the opcode and the target as we do not want them to be
|
||||
// compared.
|
||||
@ -223,8 +223,8 @@ bool DecorationManager::HaveTheSameDecorations(uint32_t id1,
|
||||
// TODO(pierremoreau): If OpDecorateId is referencing an OpConstant, one could
|
||||
// check that the constants are the same rather than just
|
||||
// looking at the constant ID.
|
||||
bool DecorationManager::AreDecorationsTheSame(const ir::Instruction* inst1,
|
||||
const ir::Instruction* inst2,
|
||||
bool DecorationManager::AreDecorationsTheSame(const opt::Instruction* inst1,
|
||||
const opt::Instruction* inst2,
|
||||
bool ignore_target) const {
|
||||
switch (inst1->opcode()) {
|
||||
case SpvOpDecorate:
|
||||
@ -250,11 +250,11 @@ void DecorationManager::AnalyzeDecorations() {
|
||||
if (!module_) return;
|
||||
|
||||
// For each group and instruction, collect all their decoration instructions.
|
||||
for (ir::Instruction& inst : module_->annotations()) {
|
||||
for (opt::Instruction& inst : module_->annotations()) {
|
||||
AddDecoration(&inst);
|
||||
}
|
||||
}
|
||||
void DecorationManager::AddDecoration(ir::Instruction* inst) {
|
||||
void DecorationManager::AddDecoration(opt::Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpDecorate:
|
||||
case SpvOpDecorateId:
|
||||
@ -295,8 +295,8 @@ std::vector<T> DecorationManager::InternalGetDecorationsFor(
|
||||
|
||||
const auto process_direct_decorations =
|
||||
[include_linkage,
|
||||
&decorations](const std::vector<ir::Instruction*>& direct_decorations) {
|
||||
for (ir::Instruction* inst : direct_decorations) {
|
||||
&decorations](const std::vector<opt::Instruction*>& direct_decorations) {
|
||||
for (opt::Instruction* inst : direct_decorations) {
|
||||
const bool is_linkage = inst->opcode() == SpvOpDecorate &&
|
||||
inst->GetSingleWordInOperand(1u) ==
|
||||
SpvDecorationLinkageAttributes;
|
||||
@ -308,7 +308,7 @@ std::vector<T> DecorationManager::InternalGetDecorationsFor(
|
||||
process_direct_decorations(ids_iter->second.direct_decorations);
|
||||
|
||||
// Process the decorations of all groups applied to |id|.
|
||||
for (const ir::Instruction* inst : target_data.indirect_decorations) {
|
||||
for (const opt::Instruction* inst : target_data.indirect_decorations) {
|
||||
const uint32_t group_id = inst->GetSingleWordInOperand(0u);
|
||||
const auto group_iter = id_to_decoration_insts_.find(group_id);
|
||||
assert(group_iter != id_to_decoration_insts_.end() && "Unknown group ID");
|
||||
@ -320,8 +320,8 @@ std::vector<T> DecorationManager::InternalGetDecorationsFor(
|
||||
|
||||
bool DecorationManager::WhileEachDecoration(
|
||||
uint32_t id, uint32_t decoration,
|
||||
std::function<bool(const ir::Instruction&)> f) {
|
||||
for (const ir::Instruction* inst : GetDecorationsFor(id, true)) {
|
||||
std::function<bool(const opt::Instruction&)> f) {
|
||||
for (const opt::Instruction* inst : GetDecorationsFor(id, true)) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpMemberDecorate:
|
||||
if (inst->GetSingleWordInOperand(2) == decoration) {
|
||||
@ -344,8 +344,8 @@ bool DecorationManager::WhileEachDecoration(
|
||||
|
||||
void DecorationManager::ForEachDecoration(
|
||||
uint32_t id, uint32_t decoration,
|
||||
std::function<void(const ir::Instruction&)> f) {
|
||||
WhileEachDecoration(id, decoration, [&f](const ir::Instruction& inst) {
|
||||
std::function<void(const opt::Instruction&)> f) {
|
||||
WhileEachDecoration(id, decoration, [&f](const opt::Instruction& inst) {
|
||||
f(inst);
|
||||
return true;
|
||||
});
|
||||
@ -355,9 +355,9 @@ void DecorationManager::CloneDecorations(uint32_t from, uint32_t to) {
|
||||
const auto decoration_list = id_to_decoration_insts_.find(from);
|
||||
if (decoration_list == id_to_decoration_insts_.end()) return;
|
||||
auto context = module_->context();
|
||||
for (ir::Instruction* inst : decoration_list->second.direct_decorations) {
|
||||
for (opt::Instruction* inst : decoration_list->second.direct_decorations) {
|
||||
// simply clone decoration and change |target-id| to |to|
|
||||
std::unique_ptr<ir::Instruction> new_inst(inst->Clone(module_->context()));
|
||||
std::unique_ptr<opt::Instruction> new_inst(inst->Clone(module_->context()));
|
||||
new_inst->SetInOperand(0, {to});
|
||||
module_->AddAnnotationInst(std::move(new_inst));
|
||||
auto decoration_iter = --module_->annotation_end();
|
||||
@ -365,15 +365,15 @@ void DecorationManager::CloneDecorations(uint32_t from, uint32_t to) {
|
||||
}
|
||||
// We need to copy the list of instructions as ForgetUses and AnalyzeUses are
|
||||
// going to modify it.
|
||||
std::vector<ir::Instruction*> indirect_decorations =
|
||||
std::vector<opt::Instruction*> indirect_decorations =
|
||||
decoration_list->second.indirect_decorations;
|
||||
for (ir::Instruction* inst : indirect_decorations) {
|
||||
for (opt::Instruction* inst : indirect_decorations) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpGroupDecorate:
|
||||
context->ForgetUses(inst);
|
||||
// add |to| to list of decorated id's
|
||||
inst->AddOperand(
|
||||
ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to}));
|
||||
opt::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to}));
|
||||
context->AnalyzeUses(inst);
|
||||
break;
|
||||
case SpvOpGroupMemberDecorate: {
|
||||
@ -381,10 +381,10 @@ void DecorationManager::CloneDecorations(uint32_t from, uint32_t to) {
|
||||
// for each (id == from), add (to, literal) as operands
|
||||
const uint32_t num_operands = inst->NumOperands();
|
||||
for (uint32_t i = 1; i < num_operands; i += 2) {
|
||||
ir::Operand op = inst->GetOperand(i);
|
||||
opt::Operand op = inst->GetOperand(i);
|
||||
if (op.words[0] == from) { // add new pair of operands: (to, literal)
|
||||
inst->AddOperand(
|
||||
ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to}));
|
||||
opt::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to}));
|
||||
op = inst->GetOperand(i + 1);
|
||||
inst->AddOperand(std::move(op));
|
||||
}
|
||||
@ -398,8 +398,8 @@ void DecorationManager::CloneDecorations(uint32_t from, uint32_t to) {
|
||||
}
|
||||
}
|
||||
|
||||
void DecorationManager::RemoveDecoration(ir::Instruction* inst) {
|
||||
const auto remove_from_container = [inst](std::vector<ir::Instruction*>& v) {
|
||||
void DecorationManager::RemoveDecoration(opt::Instruction* inst) {
|
||||
const auto remove_from_container = [inst](std::vector<opt::Instruction*>& v) {
|
||||
v.erase(std::remove(v.begin(), v.end(), inst), v.end());
|
||||
};
|
||||
|
||||
|
@ -27,11 +27,11 @@ namespace spvtools {
|
||||
namespace opt {
|
||||
namespace analysis {
|
||||
|
||||
// A class for analyzing and managing decorations in an ir::Module.
|
||||
// A class for analyzing and managing decorations in an opt::Module.
|
||||
class DecorationManager {
|
||||
public:
|
||||
// Constructs a decoration manager from the given |module|
|
||||
explicit DecorationManager(ir::Module* module) : module_(module) {
|
||||
explicit DecorationManager(opt::Module* module) : module_(module) {
|
||||
AnalyzeDecorations();
|
||||
}
|
||||
DecorationManager() = delete;
|
||||
@ -42,22 +42,22 @@ class DecorationManager {
|
||||
// removed if they have no targets left, and OpDecorationGroup will be
|
||||
// removed if the group is not applied to anyone and contains no decorations.
|
||||
void RemoveDecorationsFrom(uint32_t id,
|
||||
std::function<bool(const ir::Instruction&)> pred =
|
||||
[](const ir::Instruction&) { return true; });
|
||||
std::function<bool(const opt::Instruction&)> pred =
|
||||
[](const opt::Instruction&) { return true; });
|
||||
|
||||
// Removes all decorations from the result id of |inst|.
|
||||
//
|
||||
// NOTE: This is only meant to be called from ir_context, as only metadata
|
||||
// will be removed, and no actual instruction.
|
||||
void RemoveDecoration(ir::Instruction* inst);
|
||||
void RemoveDecoration(opt::Instruction* inst);
|
||||
|
||||
// Returns a vector of all decorations affecting |id|. If a group is applied
|
||||
// to |id|, the decorations of that group are returned rather than the group
|
||||
// decoration instruction. If |include_linkage| is not set, linkage
|
||||
// decorations won't be returned.
|
||||
std::vector<ir::Instruction*> GetDecorationsFor(uint32_t id,
|
||||
bool include_linkage);
|
||||
std::vector<const ir::Instruction*> GetDecorationsFor(
|
||||
std::vector<opt::Instruction*> GetDecorationsFor(uint32_t id,
|
||||
bool include_linkage);
|
||||
std::vector<const opt::Instruction*> GetDecorationsFor(
|
||||
uint32_t id, bool include_linkage) const;
|
||||
// Returns whether two IDs have the same decorations. Two SpvOpGroupDecorate
|
||||
// instructions that apply the same decorations but to different IDs, still
|
||||
@ -69,22 +69,22 @@ class DecorationManager {
|
||||
//
|
||||
// This is only valid for OpDecorate, OpMemberDecorate and OpDecorateId; it
|
||||
// will return false for other opcodes.
|
||||
bool AreDecorationsTheSame(const ir::Instruction* inst1,
|
||||
const ir::Instruction* inst2,
|
||||
bool AreDecorationsTheSame(const opt::Instruction* inst1,
|
||||
const opt::Instruction* inst2,
|
||||
bool ignore_target) const;
|
||||
|
||||
// |f| is run on each decoration instruction for |id| with decoration
|
||||
// |decoration|. Processed are all decorations which target |id| either
|
||||
// directly or indirectly by Decoration Groups.
|
||||
void ForEachDecoration(uint32_t id, uint32_t decoration,
|
||||
std::function<void(const ir::Instruction&)> f);
|
||||
std::function<void(const opt::Instruction&)> f);
|
||||
|
||||
// |f| is run on each decoration instruction for |id| with decoration
|
||||
// |decoration|. Processes all decoration which target |id| either directly or
|
||||
// indirectly through decoration groups. If |f| returns false, iteration is
|
||||
// terminated and this function returns false.
|
||||
bool WhileEachDecoration(uint32_t id, uint32_t decoration,
|
||||
std::function<bool(const ir::Instruction&)> f);
|
||||
std::function<bool(const opt::Instruction&)> f);
|
||||
|
||||
// Clone all decorations from one id |from|.
|
||||
// The cloned decorations are assigned to the given id |to| and are
|
||||
@ -93,7 +93,7 @@ class DecorationManager {
|
||||
void CloneDecorations(uint32_t from, uint32_t to);
|
||||
|
||||
// Informs the decoration manager of a new decoration that it needs to track.
|
||||
void AddDecoration(ir::Instruction* inst);
|
||||
void AddDecoration(opt::Instruction* inst);
|
||||
|
||||
private:
|
||||
// Analyzes the defs and uses in the given |module| and populates data
|
||||
@ -105,19 +105,19 @@ class DecorationManager {
|
||||
|
||||
// Tracks decoration information of an ID.
|
||||
struct TargetData {
|
||||
std::vector<ir::Instruction*> direct_decorations; // All decorate
|
||||
// instructions applied
|
||||
// to the tracked ID.
|
||||
std::vector<ir::Instruction*> indirect_decorations; // All instructions
|
||||
// applying a group to
|
||||
// the tracked ID.
|
||||
std::vector<ir::Instruction*> decorate_insts; // All decorate instructions
|
||||
// applying the decorations
|
||||
// of the tracked ID to
|
||||
// targets.
|
||||
// It is empty if the
|
||||
// tracked ID is not a
|
||||
// group.
|
||||
std::vector<opt::Instruction*> direct_decorations; // All decorate
|
||||
// instructions applied
|
||||
// to the tracked ID.
|
||||
std::vector<opt::Instruction*> indirect_decorations; // All instructions
|
||||
// applying a group to
|
||||
// the tracked ID.
|
||||
std::vector<opt::Instruction*> decorate_insts; // All decorate instructions
|
||||
// applying the decorations
|
||||
// of the tracked ID to
|
||||
// targets.
|
||||
// It is empty if the
|
||||
// tracked ID is not a
|
||||
// group.
|
||||
};
|
||||
|
||||
// Mapping from ids to the instructions applying a decoration to those ids.
|
||||
@ -127,7 +127,7 @@ class DecorationManager {
|
||||
// SpvOpMemberGroupDecorate).
|
||||
std::unordered_map<uint32_t, TargetData> id_to_decoration_insts_;
|
||||
// The enclosing module.
|
||||
ir::Module* module_;
|
||||
opt::Module* module_;
|
||||
};
|
||||
|
||||
} // namespace analysis
|
||||
|
@ -23,7 +23,7 @@ namespace spvtools {
|
||||
namespace opt {
|
||||
namespace analysis {
|
||||
|
||||
void DefUseManager::AnalyzeInstDef(ir::Instruction* inst) {
|
||||
void DefUseManager::AnalyzeInstDef(opt::Instruction* inst) {
|
||||
const uint32_t def_id = inst->result_id();
|
||||
if (def_id != 0) {
|
||||
auto iter = id_to_def_.find(def_id);
|
||||
@ -38,7 +38,7 @@ void DefUseManager::AnalyzeInstDef(ir::Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void DefUseManager::AnalyzeInstUse(ir::Instruction* inst) {
|
||||
void DefUseManager::AnalyzeInstUse(opt::Instruction* inst) {
|
||||
// Create entry for the given instruction. Note that the instruction may
|
||||
// not have any in-operands. In such cases, we still need a entry for those
|
||||
// instructions so this manager knows it has seen the instruction later.
|
||||
@ -57,7 +57,7 @@ void DefUseManager::AnalyzeInstUse(ir::Instruction* inst) {
|
||||
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
|
||||
case SPV_OPERAND_TYPE_SCOPE_ID: {
|
||||
uint32_t use_id = inst->GetSingleWordOperand(i);
|
||||
ir::Instruction* def = GetDef(use_id);
|
||||
opt::Instruction* def = GetDef(use_id);
|
||||
assert(def && "Definition is not registered.");
|
||||
id_to_users_.insert(UserEntry(def, inst));
|
||||
used_ids->push_back(use_id);
|
||||
@ -68,12 +68,12 @@ void DefUseManager::AnalyzeInstUse(ir::Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void DefUseManager::AnalyzeInstDefUse(ir::Instruction* inst) {
|
||||
void DefUseManager::AnalyzeInstDefUse(opt::Instruction* inst) {
|
||||
AnalyzeInstDef(inst);
|
||||
AnalyzeInstUse(inst);
|
||||
}
|
||||
|
||||
void DefUseManager::UpdateDefUse(ir::Instruction* inst) {
|
||||
void DefUseManager::UpdateDefUse(opt::Instruction* inst) {
|
||||
const uint32_t def_id = inst->result_id();
|
||||
if (def_id != 0) {
|
||||
auto iter = id_to_def_.find(def_id);
|
||||
@ -84,38 +84,38 @@ void DefUseManager::UpdateDefUse(ir::Instruction* inst) {
|
||||
AnalyzeInstUse(inst);
|
||||
}
|
||||
|
||||
ir::Instruction* DefUseManager::GetDef(uint32_t id) {
|
||||
opt::Instruction* DefUseManager::GetDef(uint32_t id) {
|
||||
auto iter = id_to_def_.find(id);
|
||||
if (iter == id_to_def_.end()) return nullptr;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
const ir::Instruction* DefUseManager::GetDef(uint32_t id) const {
|
||||
const opt::Instruction* DefUseManager::GetDef(uint32_t id) const {
|
||||
const auto iter = id_to_def_.find(id);
|
||||
if (iter == id_to_def_.end()) return nullptr;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin(
|
||||
const ir::Instruction* def) const {
|
||||
const opt::Instruction* def) const {
|
||||
return id_to_users_.lower_bound(
|
||||
UserEntry(const_cast<ir::Instruction*>(def), nullptr));
|
||||
UserEntry(const_cast<opt::Instruction*>(def), nullptr));
|
||||
}
|
||||
|
||||
bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter,
|
||||
const IdToUsersMap::const_iterator& cached_end,
|
||||
const ir::Instruction* inst) const {
|
||||
const opt::Instruction* inst) const {
|
||||
return (iter != cached_end && iter->first == inst);
|
||||
}
|
||||
|
||||
bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter,
|
||||
const ir::Instruction* inst) const {
|
||||
const opt::Instruction* inst) const {
|
||||
return UsersNotEnd(iter, id_to_users_.end(), inst);
|
||||
}
|
||||
|
||||
bool DefUseManager::WhileEachUser(
|
||||
const ir::Instruction* def,
|
||||
const std::function<bool(ir::Instruction*)>& f) const {
|
||||
const opt::Instruction* def,
|
||||
const std::function<bool(opt::Instruction*)>& f) const {
|
||||
// Ensure that |def| has been registered.
|
||||
assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) &&
|
||||
"Definition is not registered.");
|
||||
@ -129,27 +129,27 @@ bool DefUseManager::WhileEachUser(
|
||||
}
|
||||
|
||||
bool DefUseManager::WhileEachUser(
|
||||
uint32_t id, const std::function<bool(ir::Instruction*)>& f) const {
|
||||
uint32_t id, const std::function<bool(opt::Instruction*)>& f) const {
|
||||
return WhileEachUser(GetDef(id), f);
|
||||
}
|
||||
|
||||
void DefUseManager::ForEachUser(
|
||||
const ir::Instruction* def,
|
||||
const std::function<void(ir::Instruction*)>& f) const {
|
||||
WhileEachUser(def, [&f](ir::Instruction* user) {
|
||||
const opt::Instruction* def,
|
||||
const std::function<void(opt::Instruction*)>& f) const {
|
||||
WhileEachUser(def, [&f](opt::Instruction* user) {
|
||||
f(user);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void DefUseManager::ForEachUser(
|
||||
uint32_t id, const std::function<void(ir::Instruction*)>& f) const {
|
||||
uint32_t id, const std::function<void(opt::Instruction*)>& f) const {
|
||||
ForEachUser(GetDef(id), f);
|
||||
}
|
||||
|
||||
bool DefUseManager::WhileEachUse(
|
||||
const ir::Instruction* def,
|
||||
const std::function<bool(ir::Instruction*, uint32_t)>& f) const {
|
||||
const opt::Instruction* def,
|
||||
const std::function<bool(opt::Instruction*, uint32_t)>& f) const {
|
||||
// Ensure that |def| has been registered.
|
||||
assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) &&
|
||||
"Definition is not registered.");
|
||||
@ -157,9 +157,9 @@ bool DefUseManager::WhileEachUse(
|
||||
|
||||
auto end = id_to_users_.end();
|
||||
for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) {
|
||||
ir::Instruction* user = iter->second;
|
||||
opt::Instruction* user = iter->second;
|
||||
for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) {
|
||||
const ir::Operand& op = user->GetOperand(idx);
|
||||
const opt::Operand& op = user->GetOperand(idx);
|
||||
if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) {
|
||||
if (def->result_id() == op.words[0]) {
|
||||
if (!f(user, idx)) return false;
|
||||
@ -172,14 +172,14 @@ bool DefUseManager::WhileEachUse(
|
||||
|
||||
bool DefUseManager::WhileEachUse(
|
||||
uint32_t id,
|
||||
const std::function<bool(ir::Instruction*, uint32_t)>& f) const {
|
||||
const std::function<bool(opt::Instruction*, uint32_t)>& f) const {
|
||||
return WhileEachUse(GetDef(id), f);
|
||||
}
|
||||
|
||||
void DefUseManager::ForEachUse(
|
||||
const ir::Instruction* def,
|
||||
const std::function<void(ir::Instruction*, uint32_t)>& f) const {
|
||||
WhileEachUse(def, [&f](ir::Instruction* user, uint32_t index) {
|
||||
const opt::Instruction* def,
|
||||
const std::function<void(opt::Instruction*, uint32_t)>& f) const {
|
||||
WhileEachUse(def, [&f](opt::Instruction* user, uint32_t index) {
|
||||
f(user, index);
|
||||
return true;
|
||||
});
|
||||
@ -187,13 +187,13 @@ void DefUseManager::ForEachUse(
|
||||
|
||||
void DefUseManager::ForEachUse(
|
||||
uint32_t id,
|
||||
const std::function<void(ir::Instruction*, uint32_t)>& f) const {
|
||||
const std::function<void(opt::Instruction*, uint32_t)>& f) const {
|
||||
ForEachUse(GetDef(id), f);
|
||||
}
|
||||
|
||||
uint32_t DefUseManager::NumUsers(const ir::Instruction* def) const {
|
||||
uint32_t DefUseManager::NumUsers(const opt::Instruction* def) const {
|
||||
uint32_t count = 0;
|
||||
ForEachUser(def, [&count](ir::Instruction*) { ++count; });
|
||||
ForEachUser(def, [&count](opt::Instruction*) { ++count; });
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -201,9 +201,9 @@ uint32_t DefUseManager::NumUsers(uint32_t id) const {
|
||||
return NumUsers(GetDef(id));
|
||||
}
|
||||
|
||||
uint32_t DefUseManager::NumUses(const ir::Instruction* def) const {
|
||||
uint32_t DefUseManager::NumUses(const opt::Instruction* def) const {
|
||||
uint32_t count = 0;
|
||||
ForEachUse(def, [&count](ir::Instruction*, uint32_t) { ++count; });
|
||||
ForEachUse(def, [&count](opt::Instruction*, uint32_t) { ++count; });
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -211,20 +211,21 @@ uint32_t DefUseManager::NumUses(uint32_t id) const {
|
||||
return NumUses(GetDef(id));
|
||||
}
|
||||
|
||||
std::vector<ir::Instruction*> DefUseManager::GetAnnotations(uint32_t id) const {
|
||||
std::vector<ir::Instruction*> annos;
|
||||
const ir::Instruction* def = GetDef(id);
|
||||
std::vector<opt::Instruction*> DefUseManager::GetAnnotations(
|
||||
uint32_t id) const {
|
||||
std::vector<opt::Instruction*> annos;
|
||||
const opt::Instruction* def = GetDef(id);
|
||||
if (!def) return annos;
|
||||
|
||||
ForEachUser(def, [&annos](ir::Instruction* user) {
|
||||
if (ir::IsAnnotationInst(user->opcode())) {
|
||||
ForEachUser(def, [&annos](opt::Instruction* user) {
|
||||
if (opt::IsAnnotationInst(user->opcode())) {
|
||||
annos.push_back(user);
|
||||
}
|
||||
});
|
||||
return annos;
|
||||
}
|
||||
|
||||
void DefUseManager::AnalyzeDefUse(ir::Module* module) {
|
||||
void DefUseManager::AnalyzeDefUse(opt::Module* module) {
|
||||
if (!module) return;
|
||||
// Analyze all the defs before any uses to catch forward references.
|
||||
module->ForEachInst(
|
||||
@ -233,7 +234,7 @@ void DefUseManager::AnalyzeDefUse(ir::Module* module) {
|
||||
std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void DefUseManager::ClearInst(ir::Instruction* inst) {
|
||||
void DefUseManager::ClearInst(opt::Instruction* inst) {
|
||||
auto iter = inst_to_used_ids_.find(inst);
|
||||
if (iter != inst_to_used_ids_.end()) {
|
||||
EraseUseRecordsOfOperandIds(inst);
|
||||
@ -250,14 +251,14 @@ void DefUseManager::ClearInst(ir::Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void DefUseManager::EraseUseRecordsOfOperandIds(const ir::Instruction* inst) {
|
||||
void DefUseManager::EraseUseRecordsOfOperandIds(const opt::Instruction* inst) {
|
||||
// Go through all ids used by this instruction, remove this instruction's
|
||||
// uses of them.
|
||||
auto iter = inst_to_used_ids_.find(inst);
|
||||
if (iter != inst_to_used_ids_.end()) {
|
||||
for (auto use_id : iter->second) {
|
||||
id_to_users_.erase(
|
||||
UserEntry(GetDef(use_id), const_cast<ir::Instruction*>(inst)));
|
||||
UserEntry(GetDef(use_id), const_cast<opt::Instruction*>(inst)));
|
||||
}
|
||||
inst_to_used_ids_.erase(inst);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ namespace analysis {
|
||||
// * Ids referenced in OpSectionMerge & OpLoopMerge are considered as use.
|
||||
// * Ids referenced in OpPhi's in operands are considered as use.
|
||||
struct Use {
|
||||
ir::Instruction* inst; // Instruction using the id.
|
||||
opt::Instruction* inst; // Instruction using the id.
|
||||
uint32_t operand_index; // logical operand index of the id use. This can be
|
||||
// the index of result type id.
|
||||
};
|
||||
@ -58,7 +58,7 @@ inline bool operator<(const Use& lhs, const Use& rhs) {
|
||||
// Definition should never be null. User can be null, however, such an entry
|
||||
// should be used only for searching (e.g. all users of a particular definition)
|
||||
// and never stored in a container.
|
||||
using UserEntry = std::pair<ir::Instruction*, ir::Instruction*>;
|
||||
using UserEntry = std::pair<opt::Instruction*, opt::Instruction*>;
|
||||
|
||||
// Orders UserEntry for use in associative containers (i.e. less than ordering).
|
||||
//
|
||||
@ -92,17 +92,17 @@ struct UserEntryLess {
|
||||
}
|
||||
};
|
||||
|
||||
// A class for analyzing and managing defs and uses in an ir::Module.
|
||||
// A class for analyzing and managing defs and uses in an opt::Module.
|
||||
class DefUseManager {
|
||||
public:
|
||||
using IdToDefMap = std::unordered_map<uint32_t, ir::Instruction*>;
|
||||
using IdToDefMap = std::unordered_map<uint32_t, opt::Instruction*>;
|
||||
using IdToUsersMap = std::set<UserEntry, UserEntryLess>;
|
||||
|
||||
// Constructs a def-use manager from the given |module|. All internal messages
|
||||
// will be communicated to the outside via the given message |consumer|. This
|
||||
// instance only keeps a reference to the |consumer|, so the |consumer| should
|
||||
// outlive this instance.
|
||||
DefUseManager(ir::Module* module) { AnalyzeDefUse(module); }
|
||||
DefUseManager(opt::Module* module) { AnalyzeDefUse(module); }
|
||||
|
||||
DefUseManager(const DefUseManager&) = delete;
|
||||
DefUseManager(DefUseManager&&) = delete;
|
||||
@ -110,20 +110,20 @@ class DefUseManager {
|
||||
DefUseManager& operator=(DefUseManager&&) = delete;
|
||||
|
||||
// Analyzes the defs in the given |inst|.
|
||||
void AnalyzeInstDef(ir::Instruction* inst);
|
||||
void AnalyzeInstDef(opt::Instruction* inst);
|
||||
|
||||
// Analyzes the uses in the given |inst|.
|
||||
//
|
||||
// All operands of |inst| must be analyzed as defs.
|
||||
void AnalyzeInstUse(ir::Instruction* inst);
|
||||
void AnalyzeInstUse(opt::Instruction* inst);
|
||||
|
||||
// Analyzes the defs and uses in the given |inst|.
|
||||
void AnalyzeInstDefUse(ir::Instruction* inst);
|
||||
void AnalyzeInstDefUse(opt::Instruction* inst);
|
||||
|
||||
// Returns the def instruction for the given |id|. If there is no instruction
|
||||
// defining |id|, returns nullptr.
|
||||
ir::Instruction* GetDef(uint32_t id);
|
||||
const ir::Instruction* GetDef(uint32_t id) const;
|
||||
opt::Instruction* GetDef(uint32_t id);
|
||||
const opt::Instruction* GetDef(uint32_t id) const;
|
||||
|
||||
// Runs the given function |f| on each unique user instruction of |def| (or
|
||||
// |id|).
|
||||
@ -132,10 +132,10 @@ class DefUseManager {
|
||||
// only be visited once.
|
||||
//
|
||||
// |def| (or |id|) must be registered as a definition.
|
||||
void ForEachUser(const ir::Instruction* def,
|
||||
const std::function<void(ir::Instruction*)>& f) const;
|
||||
void ForEachUser(const opt::Instruction* def,
|
||||
const std::function<void(opt::Instruction*)>& f) const;
|
||||
void ForEachUser(uint32_t id,
|
||||
const std::function<void(ir::Instruction*)>& f) const;
|
||||
const std::function<void(opt::Instruction*)>& f) const;
|
||||
|
||||
// Runs the given function |f| on each unique user instruction of |def| (or
|
||||
// |id|). If |f| returns false, iteration is terminated and this function
|
||||
@ -145,10 +145,10 @@ class DefUseManager {
|
||||
// be only be visited once.
|
||||
//
|
||||
// |def| (or |id|) must be registered as a definition.
|
||||
bool WhileEachUser(const ir::Instruction* def,
|
||||
const std::function<bool(ir::Instruction*)>& f) const;
|
||||
bool WhileEachUser(const opt::Instruction* def,
|
||||
const std::function<bool(opt::Instruction*)>& f) const;
|
||||
bool WhileEachUser(uint32_t id,
|
||||
const std::function<bool(ir::Instruction*)>& f) const;
|
||||
const std::function<bool(opt::Instruction*)>& f) const;
|
||||
|
||||
// Runs the given function |f| on each unique use of |def| (or
|
||||
// |id|).
|
||||
@ -157,11 +157,11 @@ class DefUseManager {
|
||||
// visited separately.
|
||||
//
|
||||
// |def| (or |id|) must be registered as a definition.
|
||||
void ForEachUse(const ir::Instruction* def,
|
||||
const std::function<void(ir::Instruction*,
|
||||
void ForEachUse(const opt::Instruction* def,
|
||||
const std::function<void(opt::Instruction*,
|
||||
uint32_t operand_index)>& f) const;
|
||||
void ForEachUse(uint32_t id,
|
||||
const std::function<void(ir::Instruction*,
|
||||
const std::function<void(opt::Instruction*,
|
||||
uint32_t operand_index)>& f) const;
|
||||
|
||||
// Runs the given function |f| on each unique use of |def| (or
|
||||
@ -172,19 +172,19 @@ class DefUseManager {
|
||||
// visited separately.
|
||||
//
|
||||
// |def| (or |id|) must be registered as a definition.
|
||||
bool WhileEachUse(const ir::Instruction* def,
|
||||
const std::function<bool(ir::Instruction*,
|
||||
bool WhileEachUse(const opt::Instruction* def,
|
||||
const std::function<bool(opt::Instruction*,
|
||||
uint32_t operand_index)>& f) const;
|
||||
bool WhileEachUse(uint32_t id,
|
||||
const std::function<bool(ir::Instruction*,
|
||||
const std::function<bool(opt::Instruction*,
|
||||
uint32_t operand_index)>& f) const;
|
||||
|
||||
// Returns the number of users of |def| (or |id|).
|
||||
uint32_t NumUsers(const ir::Instruction* def) const;
|
||||
uint32_t NumUsers(const opt::Instruction* def) const;
|
||||
uint32_t NumUsers(uint32_t id) const;
|
||||
|
||||
// Returns the number of uses of |def| (or |id|).
|
||||
uint32_t NumUses(const ir::Instruction* def) const;
|
||||
uint32_t NumUses(const opt::Instruction* def) const;
|
||||
uint32_t NumUses(uint32_t id) const;
|
||||
|
||||
// Returns the annotation instrunctions which are a direct use of the given
|
||||
@ -192,7 +192,7 @@ class DefUseManager {
|
||||
// group(s), this function will just return the OpGroupDecorate
|
||||
// instrcution(s) which refer to the given id as an operand. The OpDecorate
|
||||
// instructions which decorate the decoration group will not be returned.
|
||||
std::vector<ir::Instruction*> GetAnnotations(uint32_t id) const;
|
||||
std::vector<opt::Instruction*> GetAnnotations(uint32_t id) const;
|
||||
|
||||
// Returns the map from ids to their def instructions.
|
||||
const IdToDefMap& id_to_defs() const { return id_to_def_; }
|
||||
@ -204,10 +204,10 @@ class DefUseManager {
|
||||
// record: |inst| uses an |id|, will be removed from the use records of |id|.
|
||||
// If |inst| defines an result id, the use record of this result id will also
|
||||
// be removed. Does nothing if |inst| was not analyzed before.
|
||||
void ClearInst(ir::Instruction* inst);
|
||||
void ClearInst(opt::Instruction* inst);
|
||||
|
||||
// Erases the records that a given instruction uses its operand ids.
|
||||
void EraseUseRecordsOfOperandIds(const ir::Instruction* inst);
|
||||
void EraseUseRecordsOfOperandIds(const opt::Instruction* inst);
|
||||
|
||||
friend bool operator==(const DefUseManager&, const DefUseManager&);
|
||||
friend bool operator!=(const DefUseManager& lhs, const DefUseManager& rhs) {
|
||||
@ -216,15 +216,15 @@ class DefUseManager {
|
||||
|
||||
// If |inst| has not already been analysed, then analyses its defintion and
|
||||
// uses.
|
||||
void UpdateDefUse(ir::Instruction* inst);
|
||||
void UpdateDefUse(opt::Instruction* inst);
|
||||
|
||||
private:
|
||||
using InstToUsedIdsMap =
|
||||
std::unordered_map<const ir::Instruction*, std::vector<uint32_t>>;
|
||||
std::unordered_map<const opt::Instruction*, std::vector<uint32_t>>;
|
||||
|
||||
// Returns the first location that {|def|, nullptr} could be inserted into the
|
||||
// users map without violating ordering.
|
||||
IdToUsersMap::const_iterator UsersBegin(const ir::Instruction* def) const;
|
||||
IdToUsersMap::const_iterator UsersBegin(const opt::Instruction* def) const;
|
||||
|
||||
// Returns true if |iter| has not reached the end of |def|'s users.
|
||||
//
|
||||
@ -233,14 +233,14 @@ class DefUseManager {
|
||||
// against |cached_end| for validity before other checks. This allows caching
|
||||
// the map's end which is a performance improvement on some platforms.
|
||||
bool UsersNotEnd(const IdToUsersMap::const_iterator& iter,
|
||||
const ir::Instruction* def) const;
|
||||
const opt::Instruction* def) const;
|
||||
bool UsersNotEnd(const IdToUsersMap::const_iterator& iter,
|
||||
const IdToUsersMap::const_iterator& cached_end,
|
||||
const ir::Instruction* def) const;
|
||||
const opt::Instruction* def) const;
|
||||
|
||||
// Analyzes the defs and uses in the given |module| and populates data
|
||||
// structures in this class. Does nothing if |module| is nullptr.
|
||||
void AnalyzeDefUse(ir::Module* module);
|
||||
void AnalyzeDefUse(opt::Module* module);
|
||||
|
||||
IdToDefMap id_to_def_; // Mapping from ids to their definitions
|
||||
IdToUsersMap id_to_users_; // Mapping from ids to their users
|
||||
|
@ -21,12 +21,12 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
ir::BasicBlock* DominatorAnalysisBase::CommonDominator(
|
||||
ir::BasicBlock* b1, ir::BasicBlock* b2) const {
|
||||
opt::BasicBlock* DominatorAnalysisBase::CommonDominator(
|
||||
opt::BasicBlock* b1, opt::BasicBlock* b2) const {
|
||||
if (!b1 || !b2) return nullptr;
|
||||
|
||||
std::unordered_set<ir::BasicBlock*> seen;
|
||||
ir::BasicBlock* block = b1;
|
||||
std::unordered_set<opt::BasicBlock*> seen;
|
||||
opt::BasicBlock* block = b1;
|
||||
while (block && seen.insert(block).second) {
|
||||
block = ImmediateDominator(block);
|
||||
}
|
||||
@ -39,8 +39,8 @@ ir::BasicBlock* DominatorAnalysisBase::CommonDominator(
|
||||
return block;
|
||||
}
|
||||
|
||||
bool DominatorAnalysisBase::Dominates(ir::Instruction* a,
|
||||
ir::Instruction* b) const {
|
||||
bool DominatorAnalysisBase::Dominates(opt::Instruction* a,
|
||||
opt::Instruction* b) const {
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
@ -49,14 +49,14 @@ bool DominatorAnalysisBase::Dominates(ir::Instruction* a,
|
||||
return true;
|
||||
}
|
||||
|
||||
ir::BasicBlock* bb_a = a->context()->get_instr_block(a);
|
||||
ir::BasicBlock* bb_b = b->context()->get_instr_block(b);
|
||||
opt::BasicBlock* bb_a = a->context()->get_instr_block(a);
|
||||
opt::BasicBlock* bb_b = b->context()->get_instr_block(b);
|
||||
|
||||
if (bb_a != bb_b) {
|
||||
return tree_.Dominates(bb_a, bb_b);
|
||||
}
|
||||
|
||||
ir::Instruction* current_inst = a;
|
||||
opt::Instruction* current_inst = a;
|
||||
while ((current_inst = current_inst->NextNode())) {
|
||||
if (current_inst == b) {
|
||||
return true;
|
||||
|
@ -30,11 +30,13 @@ class DominatorAnalysisBase {
|
||||
explicit DominatorAnalysisBase(bool is_post_dom) : tree_(is_post_dom) {}
|
||||
|
||||
// Calculates the dominator (or postdominator) tree for given function |f|.
|
||||
inline void InitializeTree(const ir::Function* f) { tree_.InitializeTree(f); }
|
||||
inline void InitializeTree(const opt::Function* f) {
|
||||
tree_.InitializeTree(f);
|
||||
}
|
||||
|
||||
// Returns true if BasicBlock |a| dominates BasicBlock |b|.
|
||||
inline bool Dominates(const ir::BasicBlock* a,
|
||||
const ir::BasicBlock* b) const {
|
||||
inline bool Dominates(const opt::BasicBlock* a,
|
||||
const opt::BasicBlock* b) const {
|
||||
if (!a || !b) return false;
|
||||
return Dominates(a->id(), b->id());
|
||||
}
|
||||
@ -46,11 +48,11 @@ class DominatorAnalysisBase {
|
||||
}
|
||||
|
||||
// Returns true if instruction |a| dominates instruction |b|.
|
||||
bool Dominates(ir::Instruction* a, ir::Instruction* b) const;
|
||||
bool Dominates(opt::Instruction* a, opt::Instruction* b) const;
|
||||
|
||||
// Returns true if BasicBlock |a| strictly dominates BasicBlock |b|.
|
||||
inline bool StrictlyDominates(const ir::BasicBlock* a,
|
||||
const ir::BasicBlock* b) const {
|
||||
inline bool StrictlyDominates(const opt::BasicBlock* a,
|
||||
const opt::BasicBlock* b) const {
|
||||
if (!a || !b) return false;
|
||||
return StrictlyDominates(a->id(), b->id());
|
||||
}
|
||||
@ -63,19 +65,20 @@ class DominatorAnalysisBase {
|
||||
|
||||
// Returns the immediate dominator of |node| or returns nullptr if it is has
|
||||
// no dominator.
|
||||
inline ir::BasicBlock* ImmediateDominator(const ir::BasicBlock* node) const {
|
||||
inline opt::BasicBlock* ImmediateDominator(
|
||||
const opt::BasicBlock* node) const {
|
||||
if (!node) return nullptr;
|
||||
return tree_.ImmediateDominator(node);
|
||||
}
|
||||
|
||||
// Returns the immediate dominator of |node_id| or returns nullptr if it is
|
||||
// has no dominator. Same as above but operates on IDs.
|
||||
inline ir::BasicBlock* ImmediateDominator(uint32_t node_id) const {
|
||||
inline opt::BasicBlock* ImmediateDominator(uint32_t node_id) const {
|
||||
return tree_.ImmediateDominator(node_id);
|
||||
}
|
||||
|
||||
// Returns true if |node| is reachable from the entry.
|
||||
inline bool IsReachable(const ir::BasicBlock* node) const {
|
||||
inline bool IsReachable(const opt::BasicBlock* node) const {
|
||||
if (!node) return false;
|
||||
return tree_.ReachableFromRoots(node->id());
|
||||
}
|
||||
@ -114,7 +117,8 @@ class DominatorAnalysisBase {
|
||||
|
||||
// Returns the most immediate basic block that dominates both |b1| and |b2|.
|
||||
// If there is no such basic block, nullptr is returned.
|
||||
ir::BasicBlock* CommonDominator(ir::BasicBlock* b1, ir::BasicBlock* b2) const;
|
||||
opt::BasicBlock* CommonDominator(opt::BasicBlock* b1,
|
||||
opt::BasicBlock* b2) const;
|
||||
|
||||
protected:
|
||||
DominatorTree tree_;
|
||||
|
@ -45,7 +45,7 @@ namespace {
|
||||
// depth first search on generic BasicBlock types. Will call post and pre order
|
||||
// user defined functions during traversal
|
||||
//
|
||||
// BBType - BasicBlock type. Will either be ir::BasicBlock or DominatorTreeNode
|
||||
// BBType - BasicBlock type. Will either be opt::BasicBlock or DominatorTreeNode
|
||||
// SuccessorLambda - Lamdba matching the signature of 'const
|
||||
// std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes
|
||||
// succeding BasicBlock A.
|
||||
@ -66,7 +66,7 @@ static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors,
|
||||
// depth first search on generic BasicBlock types. This overload is for only
|
||||
// performing user defined post order.
|
||||
//
|
||||
// BBType - BasicBlock type. Will either be ir::BasicBlock or DominatorTreeNode
|
||||
// BBType - BasicBlock type. Will either be opt::BasicBlock or DominatorTreeNode
|
||||
// SuccessorLambda - Lamdba matching the signature of 'const
|
||||
// std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes
|
||||
// succeding BasicBlock A.
|
||||
@ -84,7 +84,7 @@ static void DepthFirstSearchPostOrder(const BBType* bb,
|
||||
// Small type trait to get the function class type.
|
||||
template <typename BBType>
|
||||
struct GetFunctionClass {
|
||||
using FunctionType = ir::Function;
|
||||
using FunctionType = opt::Function;
|
||||
};
|
||||
|
||||
// Helper class to compute predecessors and successors for each Basic Block in a
|
||||
@ -98,7 +98,7 @@ struct GetFunctionClass {
|
||||
// returned by this class will be predecessors in the original CFG.
|
||||
template <typename BBType>
|
||||
class BasicBlockSuccessorHelper {
|
||||
// This should eventually become const ir::BasicBlock.
|
||||
// This should eventually become const opt::BasicBlock.
|
||||
using BasicBlock = BBType;
|
||||
using Function = typename GetFunctionClass<BBType>::FunctionType;
|
||||
|
||||
@ -219,8 +219,8 @@ bool DominatorTree::StrictlyDominates(uint32_t a, uint32_t b) const {
|
||||
return Dominates(a, b);
|
||||
}
|
||||
|
||||
bool DominatorTree::StrictlyDominates(const ir::BasicBlock* a,
|
||||
const ir::BasicBlock* b) const {
|
||||
bool DominatorTree::StrictlyDominates(const opt::BasicBlock* a,
|
||||
const opt::BasicBlock* b) const {
|
||||
return DominatorTree::StrictlyDominates(a->id(), b->id());
|
||||
}
|
||||
|
||||
@ -248,17 +248,17 @@ bool DominatorTree::Dominates(const DominatorTreeNode* a,
|
||||
a->dfs_num_post_ > b->dfs_num_post_;
|
||||
}
|
||||
|
||||
bool DominatorTree::Dominates(const ir::BasicBlock* A,
|
||||
const ir::BasicBlock* B) const {
|
||||
bool DominatorTree::Dominates(const opt::BasicBlock* A,
|
||||
const opt::BasicBlock* B) const {
|
||||
return Dominates(A->id(), B->id());
|
||||
}
|
||||
|
||||
ir::BasicBlock* DominatorTree::ImmediateDominator(
|
||||
const ir::BasicBlock* A) const {
|
||||
opt::BasicBlock* DominatorTree::ImmediateDominator(
|
||||
const opt::BasicBlock* A) const {
|
||||
return ImmediateDominator(A->id());
|
||||
}
|
||||
|
||||
ir::BasicBlock* DominatorTree::ImmediateDominator(uint32_t a) const {
|
||||
opt::BasicBlock* DominatorTree::ImmediateDominator(uint32_t a) const {
|
||||
// Check that A is a valid node in the tree.
|
||||
auto a_itr = nodes_.find(a);
|
||||
if (a_itr == nodes_.end()) return nullptr;
|
||||
@ -272,7 +272,7 @@ ir::BasicBlock* DominatorTree::ImmediateDominator(uint32_t a) const {
|
||||
return node->parent_->bb_;
|
||||
}
|
||||
|
||||
DominatorTreeNode* DominatorTree::GetOrInsertNode(ir::BasicBlock* bb) {
|
||||
DominatorTreeNode* DominatorTree::GetOrInsertNode(opt::BasicBlock* bb) {
|
||||
DominatorTreeNode* dtn = nullptr;
|
||||
|
||||
std::map<uint32_t, DominatorTreeNode>::iterator node_iter =
|
||||
@ -287,21 +287,21 @@ DominatorTreeNode* DominatorTree::GetOrInsertNode(ir::BasicBlock* bb) {
|
||||
}
|
||||
|
||||
void DominatorTree::GetDominatorEdges(
|
||||
const ir::Function* f, const ir::BasicBlock* dummy_start_node,
|
||||
std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>>* edges) {
|
||||
const opt::Function* f, const opt::BasicBlock* dummy_start_node,
|
||||
std::vector<std::pair<opt::BasicBlock*, opt::BasicBlock*>>* edges) {
|
||||
// Each time the depth first traversal calls the postorder callback
|
||||
// std::function we push that node into the postorder vector to create our
|
||||
// postorder list.
|
||||
std::vector<const ir::BasicBlock*> postorder;
|
||||
auto postorder_function = [&](const ir::BasicBlock* b) {
|
||||
std::vector<const opt::BasicBlock*> postorder;
|
||||
auto postorder_function = [&](const opt::BasicBlock* b) {
|
||||
postorder.push_back(b);
|
||||
};
|
||||
|
||||
// CFA::CalculateDominators requires std::vector<ir::BasicBlock*>
|
||||
// CFA::CalculateDominators requires std::vector<opt::BasicBlock*>
|
||||
// BB are derived from F, so we need to const cast it at some point
|
||||
// no modification is made on F.
|
||||
BasicBlockSuccessorHelper<ir::BasicBlock> helper{
|
||||
*const_cast<ir::Function*>(f), dummy_start_node, postdominator_};
|
||||
BasicBlockSuccessorHelper<opt::BasicBlock> helper{
|
||||
*const_cast<opt::Function*>(f), dummy_start_node, postdominator_};
|
||||
|
||||
// The successor function tells DepthFirstTraversal how to move to successive
|
||||
// nodes by providing an interface to get a list of successor nodes from any
|
||||
@ -318,23 +318,23 @@ void DominatorTree::GetDominatorEdges(
|
||||
DepthFirstSearchPostOrder(dummy_start_node, successor_functor,
|
||||
postorder_function);
|
||||
*edges =
|
||||
CFA<ir::BasicBlock>::CalculateDominators(postorder, predecessor_functor);
|
||||
CFA<opt::BasicBlock>::CalculateDominators(postorder, predecessor_functor);
|
||||
}
|
||||
|
||||
void DominatorTree::InitializeTree(const ir::Function* f) {
|
||||
void DominatorTree::InitializeTree(const opt::Function* f) {
|
||||
ClearTree();
|
||||
|
||||
// Skip over empty functions.
|
||||
if (f->cbegin() == f->cend()) {
|
||||
return;
|
||||
}
|
||||
const ir::CFG& cfg = *f->context()->cfg();
|
||||
const opt::CFG& cfg = *f->context()->cfg();
|
||||
|
||||
const ir::BasicBlock* dummy_start_node =
|
||||
const opt::BasicBlock* dummy_start_node =
|
||||
postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block();
|
||||
|
||||
// Get the immediate dominator for each node.
|
||||
std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>> edges;
|
||||
std::vector<std::pair<opt::BasicBlock*, opt::BasicBlock*>> edges;
|
||||
GetDominatorEdges(f, dummy_start_node, &edges);
|
||||
|
||||
// Transform the vector<pair> into the tree structure which we can use to
|
||||
|
@ -31,7 +31,7 @@ namespace opt {
|
||||
// children. It also contains two values, for the pre and post indexes in the
|
||||
// tree which are used to compare two nodes.
|
||||
struct DominatorTreeNode {
|
||||
explicit DominatorTreeNode(ir::BasicBlock* bb)
|
||||
explicit DominatorTreeNode(opt::BasicBlock* bb)
|
||||
: bb_(bb),
|
||||
parent_(nullptr),
|
||||
children_({}),
|
||||
@ -77,7 +77,7 @@ struct DominatorTreeNode {
|
||||
|
||||
inline uint32_t id() const { return bb_->id(); }
|
||||
|
||||
ir::BasicBlock* bb_;
|
||||
opt::BasicBlock* bb_;
|
||||
DominatorTreeNode* parent_;
|
||||
std::vector<DominatorTreeNode*> children_;
|
||||
|
||||
@ -158,10 +158,10 @@ class DominatorTree {
|
||||
|
||||
// Build the (post-)dominator tree for the function |f|
|
||||
// Any existing data will be overwritten
|
||||
void InitializeTree(const ir::Function* f);
|
||||
void InitializeTree(const opt::Function* f);
|
||||
|
||||
// Check if the basic block |a| dominates the basic block |b|.
|
||||
bool Dominates(const ir::BasicBlock* a, const ir::BasicBlock* b) const;
|
||||
bool Dominates(const opt::BasicBlock* a, const opt::BasicBlock* b) const;
|
||||
|
||||
// Check if the basic block id |a| dominates the basic block id |b|.
|
||||
bool Dominates(uint32_t a, uint32_t b) const;
|
||||
@ -170,8 +170,8 @@ class DominatorTree {
|
||||
bool Dominates(const DominatorTreeNode* a, const DominatorTreeNode* b) const;
|
||||
|
||||
// Check if the basic block |a| strictly dominates the basic block |b|.
|
||||
bool StrictlyDominates(const ir::BasicBlock* a,
|
||||
const ir::BasicBlock* b) const;
|
||||
bool StrictlyDominates(const opt::BasicBlock* a,
|
||||
const opt::BasicBlock* b) const;
|
||||
|
||||
// Check if the basic block id |a| strictly dominates the basic block id |b|.
|
||||
bool StrictlyDominates(uint32_t a, uint32_t b) const;
|
||||
@ -182,15 +182,15 @@ class DominatorTree {
|
||||
const DominatorTreeNode* b) const;
|
||||
|
||||
// Returns the immediate dominator of basic block |a|.
|
||||
ir::BasicBlock* ImmediateDominator(const ir::BasicBlock* A) const;
|
||||
opt::BasicBlock* ImmediateDominator(const opt::BasicBlock* A) const;
|
||||
|
||||
// Returns the immediate dominator of basic block id |a|.
|
||||
ir::BasicBlock* ImmediateDominator(uint32_t a) const;
|
||||
opt::BasicBlock* ImmediateDominator(uint32_t a) const;
|
||||
|
||||
// Returns true if the basic block |a| is reachable by this tree. A node would
|
||||
// be unreachable if it cannot be reached by traversal from the start node or
|
||||
// for a postdominator tree, cannot be reached from the exit nodes.
|
||||
inline bool ReachableFromRoots(const ir::BasicBlock* a) const {
|
||||
inline bool ReachableFromRoots(const opt::BasicBlock* a) const {
|
||||
if (!a) return false;
|
||||
return ReachableFromRoots(a->id());
|
||||
}
|
||||
@ -242,12 +242,12 @@ class DominatorTree {
|
||||
|
||||
// Returns the DominatorTreeNode associated with the basic block |bb|.
|
||||
// If the |bb| is unknown to the dominator tree, it returns null.
|
||||
inline DominatorTreeNode* GetTreeNode(ir::BasicBlock* bb) {
|
||||
inline DominatorTreeNode* GetTreeNode(opt::BasicBlock* bb) {
|
||||
return GetTreeNode(bb->id());
|
||||
}
|
||||
// Returns the DominatorTreeNode associated with the basic block |bb|.
|
||||
// If the |bb| is unknown to the dominator tree, it returns null.
|
||||
inline const DominatorTreeNode* GetTreeNode(ir::BasicBlock* bb) const {
|
||||
inline const DominatorTreeNode* GetTreeNode(opt::BasicBlock* bb) const {
|
||||
return GetTreeNode(bb->id());
|
||||
}
|
||||
|
||||
@ -272,7 +272,7 @@ class DominatorTree {
|
||||
|
||||
// Adds the basic block |bb| to the tree structure if it doesn't already
|
||||
// exist.
|
||||
DominatorTreeNode* GetOrInsertNode(ir::BasicBlock* bb);
|
||||
DominatorTreeNode* GetOrInsertNode(opt::BasicBlock* bb);
|
||||
|
||||
// Recomputes the DF numbering of the tree.
|
||||
void ResetDFNumbering();
|
||||
@ -287,8 +287,8 @@ class DominatorTree {
|
||||
// pair is its immediate dominator.
|
||||
// The root of the tree has themself as immediate dominator.
|
||||
void GetDominatorEdges(
|
||||
const ir::Function* f, const ir::BasicBlock* dummy_start_node,
|
||||
std::vector<std::pair<ir::BasicBlock*, ir::BasicBlock*>>* edges);
|
||||
const opt::Function* f, const opt::BasicBlock* dummy_start_node,
|
||||
std::vector<std::pair<opt::BasicBlock*, opt::BasicBlock*>>* edges);
|
||||
|
||||
// The roots of the tree.
|
||||
std::vector<DominatorTreeNode*> roots_;
|
||||
|
@ -26,22 +26,22 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status EliminateDeadConstantPass::Process(ir::IRContext* irContext) {
|
||||
std::unordered_set<ir::Instruction*> working_list;
|
||||
Pass::Status EliminateDeadConstantPass::Process(opt::IRContext* irContext) {
|
||||
std::unordered_set<opt::Instruction*> working_list;
|
||||
// Traverse all the instructions to get the initial set of dead constants as
|
||||
// working list and count number of real uses for constants. Uses in
|
||||
// annotation instructions do not count.
|
||||
std::unordered_map<ir::Instruction*, size_t> use_counts;
|
||||
std::vector<ir::Instruction*> constants = irContext->GetConstants();
|
||||
std::unordered_map<opt::Instruction*, size_t> use_counts;
|
||||
std::vector<opt::Instruction*> constants = irContext->GetConstants();
|
||||
for (auto* c : constants) {
|
||||
uint32_t const_id = c->result_id();
|
||||
size_t count = 0;
|
||||
irContext->get_def_use_mgr()->ForEachUse(
|
||||
const_id, [&count](ir::Instruction* user, uint32_t index) {
|
||||
const_id, [&count](opt::Instruction* user, uint32_t index) {
|
||||
(void)index;
|
||||
SpvOp op = user->opcode();
|
||||
if (!(ir::IsAnnotationInst(op) || ir::IsDebug1Inst(op) ||
|
||||
ir::IsDebug2Inst(op) || ir::IsDebug3Inst(op))) {
|
||||
if (!(opt::IsAnnotationInst(op) || opt::IsDebug1Inst(op) ||
|
||||
opt::IsDebug2Inst(op) || opt::IsDebug3Inst(op))) {
|
||||
++count;
|
||||
}
|
||||
});
|
||||
@ -53,9 +53,9 @@ Pass::Status EliminateDeadConstantPass::Process(ir::IRContext* irContext) {
|
||||
|
||||
// Start from the constants with 0 uses, back trace through the def-use chain
|
||||
// to find all dead constants.
|
||||
std::unordered_set<ir::Instruction*> dead_consts;
|
||||
std::unordered_set<opt::Instruction*> dead_consts;
|
||||
while (!working_list.empty()) {
|
||||
ir::Instruction* inst = *working_list.begin();
|
||||
opt::Instruction* inst = *working_list.begin();
|
||||
// Back propagate if the instruction contains IDs in its operands.
|
||||
switch (inst->opcode()) {
|
||||
case SpvOp::SpvOpConstantComposite:
|
||||
@ -68,7 +68,7 @@ Pass::Status EliminateDeadConstantPass::Process(ir::IRContext* irContext) {
|
||||
continue;
|
||||
}
|
||||
uint32_t operand_id = inst->GetSingleWordInOperand(i);
|
||||
ir::Instruction* def_inst =
|
||||
opt::Instruction* def_inst =
|
||||
irContext->get_def_use_mgr()->GetDef(operand_id);
|
||||
// If the use_count does not have any count for the def_inst,
|
||||
// def_inst must not be a constant, and should be ignored here.
|
||||
|
@ -26,7 +26,7 @@ namespace opt {
|
||||
class EliminateDeadConstantPass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "eliminate-dead-const"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
Status Process(opt::IRContext*) override;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -20,13 +20,13 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status EliminateDeadFunctionsPass::Process(ir::IRContext* c) {
|
||||
Pass::Status EliminateDeadFunctionsPass::Process(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
// Identify live functions first. Those that are not live
|
||||
// are dead.
|
||||
std::unordered_set<const ir::Function*> live_function_set;
|
||||
ProcessFunction mark_live = [&live_function_set](ir::Function* fp) {
|
||||
std::unordered_set<const opt::Function*> live_function_set;
|
||||
ProcessFunction mark_live = [&live_function_set](opt::Function* fp) {
|
||||
live_function_set.insert(fp);
|
||||
return false;
|
||||
};
|
||||
@ -48,10 +48,10 @@ Pass::Status EliminateDeadFunctionsPass::Process(ir::IRContext* c) {
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
void EliminateDeadFunctionsPass::EliminateFunction(ir::Function* func) {
|
||||
void EliminateDeadFunctionsPass::EliminateFunction(opt::Function* func) {
|
||||
// Remove all of the instruction in the function body
|
||||
func->ForEachInst(
|
||||
[this](ir::Instruction* inst) { context()->KillInst(inst); }, true);
|
||||
[this](opt::Instruction* inst) { context()->KillInst(inst); }, true);
|
||||
}
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -27,14 +27,14 @@ namespace opt {
|
||||
class EliminateDeadFunctionsPass : public MemPass {
|
||||
public:
|
||||
const char* name() const override { return "eliminate-dead-functions"; }
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse;
|
||||
}
|
||||
|
||||
private:
|
||||
void EliminateFunction(ir::Function* func);
|
||||
void EliminateFunction(opt::Function* func);
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -21,13 +21,13 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
void FeatureManager::Analyze(ir::Module* module) {
|
||||
void FeatureManager::Analyze(opt::Module* module) {
|
||||
AddExtensions(module);
|
||||
AddCapabilities(module);
|
||||
AddExtInstImportIds(module);
|
||||
}
|
||||
|
||||
void FeatureManager::AddExtensions(ir::Module* module) {
|
||||
void FeatureManager::AddExtensions(opt::Module* module) {
|
||||
for (auto ext : module->extensions()) {
|
||||
const std::string name =
|
||||
reinterpret_cast<const char*>(ext.GetInOperand(0u).words.data());
|
||||
@ -51,13 +51,13 @@ void FeatureManager::AddCapability(SpvCapability cap) {
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureManager::AddCapabilities(ir::Module* module) {
|
||||
for (ir::Instruction& inst : module->capabilities()) {
|
||||
void FeatureManager::AddCapabilities(opt::Module* module) {
|
||||
for (opt::Instruction& inst : module->capabilities()) {
|
||||
AddCapability(static_cast<SpvCapability>(inst.GetSingleWordInOperand(0)));
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureManager::AddExtInstImportIds(ir::Module* module) {
|
||||
void FeatureManager::AddExtInstImportIds(opt::Module* module) {
|
||||
extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450");
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ class FeatureManager {
|
||||
}
|
||||
|
||||
// Analyzes |module| and records enabled extensions and capabilities.
|
||||
void Analyze(ir::Module* module);
|
||||
void Analyze(opt::Module* module);
|
||||
|
||||
CapabilitySet* GetCapabilities() { return &capabilities_; }
|
||||
const CapabilitySet* GetCapabilities() const { return &capabilities_; }
|
||||
@ -47,17 +47,17 @@ class FeatureManager {
|
||||
|
||||
private:
|
||||
// Analyzes |module| and records enabled extensions.
|
||||
void AddExtensions(ir::Module* module);
|
||||
void AddExtensions(opt::Module* module);
|
||||
|
||||
// Adds the given |capability| and all implied capabilities into the current
|
||||
// FeatureManager.
|
||||
void AddCapability(SpvCapability capability);
|
||||
|
||||
// Analyzes |module| and records enabled capabilities.
|
||||
void AddCapabilities(ir::Module* module);
|
||||
void AddCapabilities(opt::Module* module);
|
||||
|
||||
// Analyzes |module| and records imported external instruction sets.
|
||||
void AddExtInstImportIds(ir::Module* module);
|
||||
void AddExtInstImportIds(opt::Module* module);
|
||||
|
||||
// Auxiliary object for querying SPIR-V grammar facts.
|
||||
const AssemblyGrammar& grammar_;
|
||||
|
@ -23,13 +23,13 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
using ir::Instruction;
|
||||
using ir::Operand;
|
||||
using opt::Instruction;
|
||||
using opt::Operand;
|
||||
|
||||
using Words = std::vector<uint32_t>;
|
||||
using OrderedUsesMap = std::unordered_map<uint32_t, Words>;
|
||||
|
||||
Pass::Status FlattenDecorationPass::Process(ir::IRContext* c) {
|
||||
Pass::Status FlattenDecorationPass::Process(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
bool modified = false;
|
||||
|
@ -26,7 +26,7 @@ namespace opt {
|
||||
class FlattenDecorationPass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "flatten-decoration"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
Status Process(opt::IRContext*) override;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -177,10 +177,10 @@ uint32_t InstructionFolder::OperateWords(
|
||||
}
|
||||
}
|
||||
|
||||
bool InstructionFolder::FoldInstructionInternal(ir::Instruction* inst) const {
|
||||
ir::IRContext* context = inst->context();
|
||||
bool InstructionFolder::FoldInstructionInternal(opt::Instruction* inst) const {
|
||||
opt::IRContext* context = inst->context();
|
||||
auto identity_map = [](uint32_t id) { return id; };
|
||||
ir::Instruction* folded_inst = FoldInstructionToConstant(inst, identity_map);
|
||||
opt::Instruction* folded_inst = FoldInstructionToConstant(inst, identity_map);
|
||||
if (folded_inst != nullptr) {
|
||||
inst->SetOpcode(SpvOpCopyObject);
|
||||
inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {folded_inst->result_id()}}});
|
||||
@ -230,16 +230,16 @@ uint32_t InstructionFolder::FoldScalars(
|
||||
}
|
||||
|
||||
bool InstructionFolder::FoldBinaryIntegerOpToConstant(
|
||||
ir::Instruction* inst, std::function<uint32_t(uint32_t)> id_map,
|
||||
opt::Instruction* inst, std::function<uint32_t(uint32_t)> id_map,
|
||||
uint32_t* result) const {
|
||||
SpvOp opcode = inst->opcode();
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_manger = context->get_constant_mgr();
|
||||
|
||||
uint32_t ids[2];
|
||||
const analysis::IntConstant* constants[2];
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
const ir::Operand* operand = &inst->GetInOperand(i);
|
||||
const opt::Operand* operand = &inst->GetInOperand(i);
|
||||
if (operand->type != SPV_OPERAND_TYPE_ID) {
|
||||
return false;
|
||||
}
|
||||
@ -414,16 +414,16 @@ bool InstructionFolder::FoldBinaryIntegerOpToConstant(
|
||||
}
|
||||
|
||||
bool InstructionFolder::FoldBinaryBooleanOpToConstant(
|
||||
ir::Instruction* inst, std::function<uint32_t(uint32_t)> id_map,
|
||||
opt::Instruction* inst, std::function<uint32_t(uint32_t)> id_map,
|
||||
uint32_t* result) const {
|
||||
SpvOp opcode = inst->opcode();
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_manger = context->get_constant_mgr();
|
||||
|
||||
uint32_t ids[2];
|
||||
const analysis::BoolConstant* constants[2];
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
const ir::Operand* operand = &inst->GetInOperand(i);
|
||||
const opt::Operand* operand = &inst->GetInOperand(i);
|
||||
if (operand->type != SPV_OPERAND_TYPE_ID) {
|
||||
return false;
|
||||
}
|
||||
@ -463,7 +463,7 @@ bool InstructionFolder::FoldBinaryBooleanOpToConstant(
|
||||
}
|
||||
|
||||
bool InstructionFolder::FoldIntegerOpToConstant(
|
||||
ir::Instruction* inst, std::function<uint32_t(uint32_t)> id_map,
|
||||
opt::Instruction* inst, std::function<uint32_t(uint32_t)> id_map,
|
||||
uint32_t* result) const {
|
||||
assert(IsFoldableOpcode(inst->opcode()) &&
|
||||
"Unhandled instruction opcode in FoldScalars");
|
||||
@ -572,9 +572,9 @@ bool InstructionFolder::IsFoldableConstant(
|
||||
return cst->AsNullConstant() != nullptr;
|
||||
}
|
||||
|
||||
ir::Instruction* InstructionFolder::FoldInstructionToConstant(
|
||||
ir::Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const {
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::Instruction* InstructionFolder::FoldInstructionToConstant(
|
||||
opt::Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const {
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
|
||||
if (!inst->IsFoldableByFoldScalar() &&
|
||||
@ -602,7 +602,7 @@ ir::Instruction* InstructionFolder::FoldInstructionToConstant(
|
||||
GetConstantFoldingRules().GetRulesForOpcode(inst->opcode())) {
|
||||
folded_const = rule(inst, constants);
|
||||
if (folded_const != nullptr) {
|
||||
ir::Instruction* const_inst =
|
||||
opt::Instruction* const_inst =
|
||||
const_mgr->GetDefiningInstruction(folded_const, inst->type_id());
|
||||
assert(const_inst->type_id() == inst->type_id());
|
||||
// May be a new instruction that needs to be analysed.
|
||||
@ -627,14 +627,14 @@ ir::Instruction* InstructionFolder::FoldInstructionToConstant(
|
||||
if (successful) {
|
||||
const analysis::Constant* result_const =
|
||||
const_mgr->GetConstant(const_mgr->GetType(inst), {result_val});
|
||||
ir::Instruction* folded_inst =
|
||||
opt::Instruction* folded_inst =
|
||||
const_mgr->GetDefiningInstruction(result_const, inst->type_id());
|
||||
return folded_inst;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool InstructionFolder::IsFoldableType(ir::Instruction* type_inst) const {
|
||||
bool InstructionFolder::IsFoldableType(opt::Instruction* type_inst) const {
|
||||
// Support 32-bit integers.
|
||||
if (type_inst->opcode() == SpvOpTypeInt) {
|
||||
return type_inst->GetSingleWordInOperand(0) == 32;
|
||||
@ -647,9 +647,9 @@ bool InstructionFolder::IsFoldableType(ir::Instruction* type_inst) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InstructionFolder::FoldInstruction(ir::Instruction* inst) const {
|
||||
bool InstructionFolder::FoldInstruction(opt::Instruction* inst) const {
|
||||
bool modified = false;
|
||||
ir::Instruction* folded_inst(inst);
|
||||
opt::Instruction* folded_inst(inst);
|
||||
while (folded_inst->opcode() != SpvOpCopyObject &&
|
||||
FoldInstructionInternal(&*folded_inst)) {
|
||||
modified = true;
|
||||
|
@ -66,7 +66,7 @@ class InstructionFolder {
|
||||
|
||||
// Returns true if |FoldInstructionToConstant| could fold an instruction whose
|
||||
// result type is |type_inst|.
|
||||
bool IsFoldableType(ir::Instruction* type_inst) const;
|
||||
bool IsFoldableType(opt::Instruction* type_inst) const;
|
||||
|
||||
// Tries to fold |inst| to a single constant, when the input ids to |inst|
|
||||
// have been substituted using |id_map|. Returns a pointer to the OpConstant*
|
||||
@ -77,8 +77,8 @@ class InstructionFolder {
|
||||
// can be used for things like CCP where it is known that some ids contain a
|
||||
// constant, but the instruction itself has not been updated yet. This can
|
||||
// map those ids to the appropriate constants.
|
||||
ir::Instruction* FoldInstructionToConstant(
|
||||
ir::Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const;
|
||||
opt::Instruction* FoldInstructionToConstant(
|
||||
opt::Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const;
|
||||
// Returns true if |inst| can be folded into a simpler instruction.
|
||||
// If |inst| can be simplified, |inst| is overwritten with the simplified
|
||||
// instruction reusing the same result id.
|
||||
@ -90,7 +90,7 @@ class InstructionFolder {
|
||||
// 1) An OpPhi becomes and OpCopyObject - If there are OpPhi instruction after
|
||||
// |inst| in a basic block then this is invalid. The caller must fix this
|
||||
// up.
|
||||
bool FoldInstruction(ir::Instruction* inst) const;
|
||||
bool FoldInstruction(opt::Instruction* inst) const;
|
||||
|
||||
// Return true if this opcode has a const folding rule associtated with it.
|
||||
bool HasConstFoldingRule(SpvOp opcode) const {
|
||||
@ -126,7 +126,7 @@ class InstructionFolder {
|
||||
uint32_t OperateWords(SpvOp opcode,
|
||||
const std::vector<uint32_t>& operand_words) const;
|
||||
|
||||
bool FoldInstructionInternal(ir::Instruction* inst) const;
|
||||
bool FoldInstructionInternal(opt::Instruction* inst) const;
|
||||
|
||||
// Returns true if |inst| is a binary operation that takes two integers as
|
||||
// parameters and folds to a constant that can be represented as an unsigned
|
||||
@ -134,7 +134,7 @@ class InstructionFolder {
|
||||
// folded, the resulting value is returned in |*result|. Valid result types
|
||||
// for the instruction are any integer (signed or unsigned) with 32-bits or
|
||||
// less, or a boolean value.
|
||||
bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst,
|
||||
bool FoldBinaryIntegerOpToConstant(opt::Instruction* inst,
|
||||
std::function<uint32_t(uint32_t)> id_map,
|
||||
uint32_t* result) const;
|
||||
|
||||
@ -142,7 +142,7 @@ class InstructionFolder {
|
||||
// folds
|
||||
// to a constant boolean value when the ids have been replaced using |id_map|.
|
||||
// If |inst| can be folded, the result value is returned in |*result|.
|
||||
bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst,
|
||||
bool FoldBinaryBooleanOpToConstant(opt::Instruction* inst,
|
||||
std::function<uint32_t(uint32_t)> id_map,
|
||||
uint32_t* result) const;
|
||||
|
||||
@ -150,7 +150,7 @@ class InstructionFolder {
|
||||
// substituted using id_map. If it can, the value is returned in |result|. If
|
||||
// not, |result| is unchanged. It is assumed that not all operands are
|
||||
// constant. Those cases are handled by |FoldScalar|.
|
||||
bool FoldIntegerOpToConstant(ir::Instruction* inst,
|
||||
bool FoldIntegerOpToConstant(opt::Instruction* inst,
|
||||
std::function<uint32_t(uint32_t)> id_map,
|
||||
uint32_t* result) const;
|
||||
|
||||
|
@ -27,17 +27,17 @@ namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status FoldSpecConstantOpAndCompositePass::Process(
|
||||
ir::IRContext* irContext) {
|
||||
opt::IRContext* irContext) {
|
||||
Initialize(irContext);
|
||||
return ProcessImpl(irContext);
|
||||
}
|
||||
|
||||
void FoldSpecConstantOpAndCompositePass::Initialize(ir::IRContext* irContext) {
|
||||
void FoldSpecConstantOpAndCompositePass::Initialize(opt::IRContext* irContext) {
|
||||
InitializeProcessing(irContext);
|
||||
}
|
||||
|
||||
Pass::Status FoldSpecConstantOpAndCompositePass::ProcessImpl(
|
||||
ir::IRContext* irContext) {
|
||||
opt::IRContext* irContext) {
|
||||
bool modified = false;
|
||||
// Traverse through all the constant defining instructions. For Normal
|
||||
// Constants whose values are determined and do not depend on OpUndef
|
||||
@ -59,13 +59,13 @@ Pass::Status FoldSpecConstantOpAndCompositePass::ProcessImpl(
|
||||
// the dependee Spec Constants, all its dependent constants must have been
|
||||
// processed and all its dependent Spec Constants should have been folded if
|
||||
// possible.
|
||||
ir::Module::inst_iterator next_inst = irContext->types_values_begin();
|
||||
for (ir::Module::inst_iterator inst_iter = next_inst;
|
||||
opt::Module::inst_iterator next_inst = irContext->types_values_begin();
|
||||
for (opt::Module::inst_iterator inst_iter = next_inst;
|
||||
// Need to re-evaluate the end iterator since we may modify the list of
|
||||
// instructions in this section of the module as the process goes.
|
||||
inst_iter != irContext->types_values_end(); inst_iter = next_inst) {
|
||||
++next_inst;
|
||||
ir::Instruction* inst = &*inst_iter;
|
||||
opt::Instruction* inst = &*inst_iter;
|
||||
// Collect constant values of normal constants and process the
|
||||
// OpSpecConstantOp and OpSpecConstantComposite instructions if possible.
|
||||
// The constant values will be stored in analysis::Constant instances.
|
||||
@ -121,9 +121,9 @@ Pass::Status FoldSpecConstantOpAndCompositePass::ProcessImpl(
|
||||
}
|
||||
|
||||
bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
|
||||
ir::Module::inst_iterator* pos) {
|
||||
ir::Instruction* inst = &**pos;
|
||||
ir::Instruction* folded_inst = nullptr;
|
||||
opt::Module::inst_iterator* pos) {
|
||||
opt::Instruction* inst = &**pos;
|
||||
opt::Instruction* folded_inst = nullptr;
|
||||
assert(inst->GetInOperand(0).type ==
|
||||
SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER &&
|
||||
"The first in-operand of OpSpecContantOp instruction must be of "
|
||||
@ -161,16 +161,16 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
|
||||
|
||||
uint32_t FoldSpecConstantOpAndCompositePass::GetTypeComponent(
|
||||
uint32_t typeId, uint32_t element) const {
|
||||
ir::Instruction* type = context()->get_def_use_mgr()->GetDef(typeId);
|
||||
opt::Instruction* type = context()->get_def_use_mgr()->GetDef(typeId);
|
||||
uint32_t subtype = type->GetTypeComponent(element);
|
||||
assert(subtype != 0);
|
||||
|
||||
return subtype;
|
||||
}
|
||||
|
||||
ir::Instruction* FoldSpecConstantOpAndCompositePass::DoCompositeExtract(
|
||||
ir::Module::inst_iterator* pos) {
|
||||
ir::Instruction* inst = &**pos;
|
||||
opt::Instruction* FoldSpecConstantOpAndCompositePass::DoCompositeExtract(
|
||||
opt::Module::inst_iterator* pos) {
|
||||
opt::Instruction* inst = &**pos;
|
||||
assert(inst->NumInOperands() - 1 >= 2 &&
|
||||
"OpSpecConstantOp CompositeExtract requires at least two non-type "
|
||||
"non-opcode operands.");
|
||||
@ -218,9 +218,9 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoCompositeExtract(
|
||||
current_const, pos);
|
||||
}
|
||||
|
||||
ir::Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle(
|
||||
ir::Module::inst_iterator* pos) {
|
||||
ir::Instruction* inst = &**pos;
|
||||
opt::Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle(
|
||||
opt::Module::inst_iterator* pos) {
|
||||
opt::Instruction* inst = &**pos;
|
||||
analysis::Vector* result_vec_type =
|
||||
context()->get_constant_mgr()->GetType(inst)->AsVector();
|
||||
assert(inst->NumInOperands() - 1 > 2 &&
|
||||
@ -323,9 +323,9 @@ bool IsValidTypeForComponentWiseOperation(const analysis::Type* type) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ir::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
|
||||
ir::Module::inst_iterator* pos) {
|
||||
const ir::Instruction* inst = &**pos;
|
||||
opt::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
|
||||
opt::Module::inst_iterator* pos) {
|
||||
const opt::Instruction* inst = &**pos;
|
||||
const analysis::Type* result_type =
|
||||
context()->get_constant_mgr()->GetType(inst);
|
||||
SpvOp spec_opcode = static_cast<SpvOp>(inst->GetSingleWordInOperand(0));
|
||||
@ -334,7 +334,7 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
|
||||
|
||||
if (!std::all_of(
|
||||
inst->cbegin(), inst->cend(),
|
||||
[&operands, this](const ir::Operand& o) {
|
||||
[&operands, this](const opt::Operand& o) {
|
||||
// skip the operands that is not an id.
|
||||
if (o.type != spv_operand_type_t::SPV_OPERAND_TYPE_ID) return true;
|
||||
uint32_t id = o.words.front();
|
||||
|
@ -36,19 +36,19 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
|
||||
|
||||
const char* name() const override { return "fold-spec-const-op-composite"; }
|
||||
|
||||
Status Process(ir::IRContext* irContext) override;
|
||||
Status Process(opt::IRContext* irContext) override;
|
||||
|
||||
private:
|
||||
// Initializes the type manager, def-use manager and get the maximal id used
|
||||
// in the module.
|
||||
void Initialize(ir::IRContext* irContext);
|
||||
void Initialize(opt::IRContext* irContext);
|
||||
|
||||
// The real entry of processing. Iterates through the types-constants-globals
|
||||
// section of the given module, finds the Spec Constants defined with
|
||||
// OpSpecConstantOp and OpSpecConstantComposite instructions. If the result
|
||||
// value of those spec constants can be folded, fold them to their
|
||||
// corresponding normal constants.
|
||||
Status ProcessImpl(ir::IRContext* irContext);
|
||||
Status ProcessImpl(opt::IRContext* irContext);
|
||||
|
||||
// Processes the OpSpecConstantOp instruction pointed by the given
|
||||
// instruction iterator, folds it to normal constants if possible. Returns
|
||||
@ -59,26 +59,27 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
|
||||
// folding is done successfully, the original OpSpecConstantOp instruction
|
||||
// will be changed to Nop and new folded instruction will be inserted before
|
||||
// it.
|
||||
bool ProcessOpSpecConstantOp(ir::Module::inst_iterator* pos);
|
||||
bool ProcessOpSpecConstantOp(opt::Module::inst_iterator* pos);
|
||||
|
||||
// Try to fold the OpSpecConstantOp CompositeExtract instruction pointed by
|
||||
// the given instruction iterator to a normal constant defining instruction.
|
||||
// Returns the pointer to the new constant defining instruction if succeeded.
|
||||
// Otherwise returns nullptr.
|
||||
ir::Instruction* DoCompositeExtract(ir::Module::inst_iterator* inst_iter_ptr);
|
||||
opt::Instruction* DoCompositeExtract(
|
||||
opt::Module::inst_iterator* inst_iter_ptr);
|
||||
|
||||
// Try to fold the OpSpecConstantOp VectorShuffle instruction pointed by the
|
||||
// given instruction iterator to a normal constant defining instruction.
|
||||
// Returns the pointer to the new constant defining instruction if succeeded.
|
||||
// Otherwise return nullptr.
|
||||
ir::Instruction* DoVectorShuffle(ir::Module::inst_iterator* inst_iter_ptr);
|
||||
opt::Instruction* DoVectorShuffle(opt::Module::inst_iterator* inst_iter_ptr);
|
||||
|
||||
// Try to fold the OpSpecConstantOp <component wise operations> instruction
|
||||
// pointed by the given instruction iterator to a normal constant defining
|
||||
// instruction. Returns the pointer to the new constant defining instruction
|
||||
// if succeeded, otherwise return nullptr.
|
||||
ir::Instruction* DoComponentWiseOperation(
|
||||
ir::Module::inst_iterator* inst_iter_ptr);
|
||||
opt::Instruction* DoComponentWiseOperation(
|
||||
opt::Module::inst_iterator* inst_iter_ptr);
|
||||
|
||||
// Returns the |element|'th subtype of |type|.
|
||||
//
|
||||
|
@ -75,9 +75,9 @@ const analysis::Constant* ConstInput(
|
||||
return constants[0] ? constants[0] : constants[1];
|
||||
}
|
||||
|
||||
ir::Instruction* NonConstInput(ir::IRContext* context,
|
||||
const analysis::Constant* c,
|
||||
ir::Instruction* inst) {
|
||||
opt::Instruction* NonConstInput(opt::IRContext* context,
|
||||
const analysis::Constant* c,
|
||||
opt::Instruction* inst) {
|
||||
uint32_t in_op = c ? 1u : 0u;
|
||||
return context->get_def_use_mgr()->GetDef(
|
||||
inst->GetSingleWordInOperand(in_op));
|
||||
@ -199,10 +199,10 @@ uint32_t Reciprocal(analysis::ConstantManager* const_mgr,
|
||||
|
||||
// Replaces fdiv where second operand is constant with fmul.
|
||||
FoldingRule ReciprocalFDiv() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFDiv);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
@ -244,17 +244,17 @@ FoldingRule ReciprocalFDiv() {
|
||||
|
||||
// Elides consecutive negate instructions.
|
||||
FoldingRule MergeNegateArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate);
|
||||
(void)constants;
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
ir::Instruction* op_inst =
|
||||
opt::Instruction* op_inst =
|
||||
context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u));
|
||||
if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
@ -279,18 +279,18 @@ FoldingRule MergeNegateArithmetic() {
|
||||
// -(x / 2) = x / -2
|
||||
// -(2 / x) = -2 / x
|
||||
FoldingRule MergeNegateMulDivArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate);
|
||||
(void)constants;
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
ir::Instruction* op_inst =
|
||||
opt::Instruction* op_inst =
|
||||
context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u));
|
||||
if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
@ -338,18 +338,18 @@ FoldingRule MergeNegateMulDivArithmetic() {
|
||||
// -(x - 2) = 2 - x
|
||||
// -(2 - x) = x - 2
|
||||
FoldingRule MergeNegateAddSubArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate);
|
||||
(void)constants;
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
ir::Instruction* op_inst =
|
||||
opt::Instruction* op_inst =
|
||||
context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u));
|
||||
if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
@ -571,10 +571,10 @@ uint32_t PerformOperation(analysis::ConstantManager* const_mgr, SpvOp opcode,
|
||||
// (x * 2) * 2 = x * 4
|
||||
// (2 * x) * 2 = x * 4
|
||||
FoldingRule MergeMulMulArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFMul || inst->opcode() == SpvOpIMul);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
@ -587,7 +587,7 @@ FoldingRule MergeMulMulArithmetic() {
|
||||
// Determine the constant input and the variable input in |inst|.
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (HasFloatingPoint(type) && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -624,10 +624,10 @@ FoldingRule MergeMulMulArithmetic() {
|
||||
// (y / x) * x = y
|
||||
// x * (y / x) = y
|
||||
FoldingRule MergeMulDivArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFMul);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
|
||||
@ -640,7 +640,7 @@ FoldingRule MergeMulDivArithmetic() {
|
||||
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
uint32_t op_id = inst->GetSingleWordInOperand(i);
|
||||
ir::Instruction* op_inst = def_use_mgr->GetDef(op_id);
|
||||
opt::Instruction* op_inst = def_use_mgr->GetDef(op_id);
|
||||
if (op_inst->opcode() == SpvOpFDiv) {
|
||||
if (op_inst->GetSingleWordInOperand(1) ==
|
||||
inst->GetSingleWordInOperand(1 - i)) {
|
||||
@ -654,7 +654,7 @@ FoldingRule MergeMulDivArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
|
||||
|
||||
if (other_inst->opcode() == SpvOpFDiv) {
|
||||
@ -699,10 +699,10 @@ FoldingRule MergeMulDivArithmetic() {
|
||||
// (-x) * 2 = x * -2
|
||||
// 2 * (-x) = x * -2
|
||||
FoldingRule MergeMulNegateArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFMul || inst->opcode() == SpvOpIMul);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
@ -714,7 +714,7 @@ FoldingRule MergeMulNegateArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -740,10 +740,10 @@ FoldingRule MergeMulNegateArithmetic() {
|
||||
// (4 / x) / 2 = 2 / x
|
||||
// (x / 2) / 2 = x / 4
|
||||
FoldingRule MergeDivDivArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFDiv);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
@ -754,7 +754,7 @@ FoldingRule MergeDivDivArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1 || HasZero(const_input1)) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
|
||||
|
||||
bool first_is_variable = constants[0] == nullptr;
|
||||
@ -812,10 +812,10 @@ FoldingRule MergeDivDivArithmetic() {
|
||||
// (x * y) / x = y
|
||||
// (y * x) / x = y
|
||||
FoldingRule MergeDivMulArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFDiv);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
|
||||
@ -827,7 +827,7 @@ FoldingRule MergeDivMulArithmetic() {
|
||||
if (width != 32 && width != 64) return false;
|
||||
|
||||
uint32_t op_id = inst->GetSingleWordInOperand(0);
|
||||
ir::Instruction* op_inst = def_use_mgr->GetDef(op_id);
|
||||
opt::Instruction* op_inst = def_use_mgr->GetDef(op_id);
|
||||
|
||||
if (op_inst->opcode() == SpvOpFMul) {
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
@ -843,7 +843,7 @@ FoldingRule MergeDivMulArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1 || HasZero(const_input1)) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
|
||||
|
||||
bool first_is_variable = constants[0] == nullptr;
|
||||
@ -885,11 +885,11 @@ FoldingRule MergeDivMulArithmetic() {
|
||||
// (-x) / 2 = x / -2
|
||||
// 2 / (-x) = 2 / -x
|
||||
FoldingRule MergeDivNegateArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv ||
|
||||
inst->opcode() == SpvOpUDiv);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
@ -901,7 +901,7 @@ FoldingRule MergeDivNegateArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -931,10 +931,10 @@ FoldingRule MergeDivNegateArithmetic() {
|
||||
// (-x) + 2 = 2 - x
|
||||
// 2 + (-x) = 2 - x
|
||||
FoldingRule MergeAddNegateArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
bool uses_float = HasFloatingPoint(type);
|
||||
@ -942,7 +942,7 @@ FoldingRule MergeAddNegateArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -965,10 +965,10 @@ FoldingRule MergeAddNegateArithmetic() {
|
||||
// (-x) - 2 = -2 - x
|
||||
// 2 - (-x) = x + 2
|
||||
FoldingRule MergeSubNegateArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
@ -980,7 +980,7 @@ FoldingRule MergeSubNegateArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -1014,10 +1014,10 @@ FoldingRule MergeSubNegateArithmetic() {
|
||||
// 2 + (x + 2) = x + 4
|
||||
// 2 + (2 + x) = x + 4
|
||||
FoldingRule MergeAddAddArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
@ -1029,7 +1029,7 @@ FoldingRule MergeAddAddArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -1040,7 +1040,7 @@ FoldingRule MergeAddAddArithmetic() {
|
||||
const analysis::Constant* const_input2 = ConstInput(other_constants);
|
||||
if (!const_input2) return false;
|
||||
|
||||
ir::Instruction* non_const_input =
|
||||
opt::Instruction* non_const_input =
|
||||
NonConstInput(context, other_constants[0], other_inst);
|
||||
uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(),
|
||||
const_input1, const_input2);
|
||||
@ -1062,10 +1062,10 @@ FoldingRule MergeAddAddArithmetic() {
|
||||
// 2 + (x - 2) = x + 0
|
||||
// 2 + (2 - x) = 4 - x
|
||||
FoldingRule MergeAddSubArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
@ -1077,7 +1077,7 @@ FoldingRule MergeAddSubArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -1122,10 +1122,10 @@ FoldingRule MergeAddSubArithmetic() {
|
||||
// 2 - (x + 2) = 0 - x
|
||||
// 2 - (2 + x) = 0 - x
|
||||
FoldingRule MergeSubAddArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
@ -1137,7 +1137,7 @@ FoldingRule MergeSubAddArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -1148,7 +1148,7 @@ FoldingRule MergeSubAddArithmetic() {
|
||||
const analysis::Constant* const_input2 = ConstInput(other_constants);
|
||||
if (!const_input2) return false;
|
||||
|
||||
ir::Instruction* non_const_input =
|
||||
opt::Instruction* non_const_input =
|
||||
NonConstInput(context, other_constants[0], other_inst);
|
||||
|
||||
// If the first operand of the sub is not a constant, swap the constants
|
||||
@ -1188,10 +1188,10 @@ FoldingRule MergeSubAddArithmetic() {
|
||||
// 2 - (x - 2) = 4 - x
|
||||
// 2 - (2 - x) = x + 0
|
||||
FoldingRule MergeSubSubArithmetic() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub);
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
@ -1203,7 +1203,7 @@ FoldingRule MergeSubSubArithmetic() {
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
ir::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
opt::Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
|
||||
@ -1214,7 +1214,7 @@ FoldingRule MergeSubSubArithmetic() {
|
||||
const analysis::Constant* const_input2 = ConstInput(other_constants);
|
||||
if (!const_input2) return false;
|
||||
|
||||
ir::Instruction* non_const_input =
|
||||
opt::Instruction* non_const_input =
|
||||
NonConstInput(context, other_constants[0], other_inst);
|
||||
|
||||
// Merge the constants.
|
||||
@ -1255,7 +1255,7 @@ FoldingRule MergeSubSubArithmetic() {
|
||||
}
|
||||
|
||||
FoldingRule IntMultipleBy1() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpIMul && "Wrong opcode. Should be OpIMul.");
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
@ -1281,7 +1281,7 @@ FoldingRule IntMultipleBy1() {
|
||||
}
|
||||
|
||||
FoldingRule CompositeConstructFeedingExtract() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
// If the input to an OpCompositeExtract is an OpCompositeConstruct,
|
||||
// then we can simply use the appropriate element in the construction.
|
||||
@ -1290,13 +1290,13 @@ FoldingRule CompositeConstructFeedingExtract() {
|
||||
analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr();
|
||||
analysis::TypeManager* type_mgr = inst->context()->get_type_mgr();
|
||||
uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
|
||||
ir::Instruction* cinst = def_use_mgr->GetDef(cid);
|
||||
opt::Instruction* cinst = def_use_mgr->GetDef(cid);
|
||||
|
||||
if (cinst->opcode() != SpvOpCompositeConstruct) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<ir::Operand> operands;
|
||||
std::vector<opt::Operand> operands;
|
||||
analysis::Type* composite_type = type_mgr->GetType(cinst->type_id());
|
||||
if (composite_type->AsVector() == nullptr) {
|
||||
// Get the element being extracted from the OpCompositeConstruct
|
||||
@ -1321,7 +1321,7 @@ FoldingRule CompositeConstructFeedingExtract() {
|
||||
for (uint32_t construct_index = 0;
|
||||
construct_index < cinst->NumInOperands(); ++construct_index) {
|
||||
uint32_t element_id = cinst->GetSingleWordInOperand(construct_index);
|
||||
ir::Instruction* element_def = def_use_mgr->GetDef(element_id);
|
||||
opt::Instruction* element_def = def_use_mgr->GetDef(element_id);
|
||||
analysis::Vector* element_type =
|
||||
type_mgr->GetType(element_def->type_id())->AsVector();
|
||||
if (element_type) {
|
||||
@ -1366,7 +1366,7 @@ FoldingRule CompositeExtractFeedingConstruct() {
|
||||
//
|
||||
// This is a common code pattern because of the way that scalar replacement
|
||||
// works.
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == SpvOpCompositeConstruct &&
|
||||
"Wrong opcode. Should be OpCompositeConstruct.");
|
||||
@ -1379,7 +1379,7 @@ FoldingRule CompositeExtractFeedingConstruct() {
|
||||
// - all extract from the same id.
|
||||
for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
|
||||
uint32_t element_id = inst->GetSingleWordInOperand(i);
|
||||
ir::Instruction* element_inst = def_use_mgr->GetDef(element_id);
|
||||
opt::Instruction* element_inst = def_use_mgr->GetDef(element_id);
|
||||
|
||||
if (element_inst->opcode() != SpvOpCompositeExtract) {
|
||||
return false;
|
||||
@ -1404,7 +1404,7 @@ FoldingRule CompositeExtractFeedingConstruct() {
|
||||
|
||||
// The last check it to see that the object being extracted from is the
|
||||
// correct type.
|
||||
ir::Instruction* original_inst = def_use_mgr->GetDef(original_id);
|
||||
opt::Instruction* original_inst = def_use_mgr->GetDef(original_id);
|
||||
if (original_inst->type_id() != inst->type_id()) {
|
||||
return false;
|
||||
}
|
||||
@ -1417,13 +1417,13 @@ FoldingRule CompositeExtractFeedingConstruct() {
|
||||
}
|
||||
|
||||
FoldingRule InsertFeedingExtract() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == SpvOpCompositeExtract &&
|
||||
"Wrong opcode. Should be OpCompositeExtract.");
|
||||
analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr();
|
||||
uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
|
||||
ir::Instruction* cinst = def_use_mgr->GetDef(cid);
|
||||
opt::Instruction* cinst = def_use_mgr->GetDef(cid);
|
||||
|
||||
if (cinst->opcode() != SpvOpCompositeInsert) {
|
||||
return false;
|
||||
@ -1461,7 +1461,7 @@ FoldingRule InsertFeedingExtract() {
|
||||
// Extracting an element of the value that was inserted. Extract from
|
||||
// that value directly.
|
||||
if (i + 1 == cinst->NumInOperands()) {
|
||||
std::vector<ir::Operand> operands;
|
||||
std::vector<opt::Operand> operands;
|
||||
operands.push_back(
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{cinst->GetSingleWordInOperand(kInsertObjectIdInIdx)}});
|
||||
@ -1475,7 +1475,7 @@ FoldingRule InsertFeedingExtract() {
|
||||
|
||||
// Extracting a value that is disjoint from the element being inserted.
|
||||
// Rewrite the extract to use the composite input to the insert.
|
||||
std::vector<ir::Operand> operands;
|
||||
std::vector<opt::Operand> operands;
|
||||
operands.push_back(
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{cinst->GetSingleWordInOperand(kInsertCompositeIdInIdx)}});
|
||||
@ -1492,21 +1492,21 @@ FoldingRule InsertFeedingExtract() {
|
||||
// operands of the VectorShuffle. We just need to adjust the index in the
|
||||
// extract instruction.
|
||||
FoldingRule VectorShuffleFeedingExtract() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == SpvOpCompositeExtract &&
|
||||
"Wrong opcode. Should be OpCompositeExtract.");
|
||||
analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr();
|
||||
analysis::TypeManager* type_mgr = inst->context()->get_type_mgr();
|
||||
uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
|
||||
ir::Instruction* cinst = def_use_mgr->GetDef(cid);
|
||||
opt::Instruction* cinst = def_use_mgr->GetDef(cid);
|
||||
|
||||
if (cinst->opcode() != SpvOpVectorShuffle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the size of the first vector operand of the VectorShuffle
|
||||
ir::Instruction* first_input =
|
||||
opt::Instruction* first_input =
|
||||
def_use_mgr->GetDef(cinst->GetSingleWordInOperand(0));
|
||||
analysis::Type* first_input_type =
|
||||
type_mgr->GetType(first_input->type_id());
|
||||
@ -1541,16 +1541,17 @@ FoldingRule VectorShuffleFeedingExtract() {
|
||||
// operands of the FMix.
|
||||
FoldingRule FMixFeedingExtract() {
|
||||
return
|
||||
[](ir::Instruction* inst, const std::vector<const analysis::Constant*>&) {
|
||||
[](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == SpvOpCompositeExtract &&
|
||||
"Wrong opcode. Should be OpCompositeExtract.");
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
|
||||
uint32_t composite_id =
|
||||
inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
|
||||
ir::Instruction* composite_inst = def_use_mgr->GetDef(composite_id);
|
||||
opt::Instruction* composite_inst = def_use_mgr->GetDef(composite_id);
|
||||
|
||||
if (composite_inst->opcode() != SpvOpExtInst) {
|
||||
return false;
|
||||
@ -1568,7 +1569,7 @@ FoldingRule FMixFeedingExtract() {
|
||||
|
||||
// Get the |a| for the FMix instruction.
|
||||
uint32_t a_id = composite_inst->GetSingleWordInOperand(kFMixAIdInIdx);
|
||||
std::unique_ptr<ir::Instruction> a(inst->Clone(inst->context()));
|
||||
std::unique_ptr<opt::Instruction> a(inst->Clone(inst->context()));
|
||||
a->SetInOperand(kExtractCompositeIdInIdx, {a_id});
|
||||
context->get_instruction_folder().FoldInstruction(a.get());
|
||||
|
||||
@ -1612,42 +1613,42 @@ FoldingRule FMixFeedingExtract() {
|
||||
FoldingRule RedundantPhi() {
|
||||
// An OpPhi instruction where all values are the same or the result of the phi
|
||||
// itself, can be replaced by the value itself.
|
||||
return
|
||||
[](ir::Instruction* inst, const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == SpvOpPhi && "Wrong opcode. Should be OpPhi.");
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == SpvOpPhi && "Wrong opcode. Should be OpPhi.");
|
||||
|
||||
uint32_t incoming_value = 0;
|
||||
uint32_t incoming_value = 0;
|
||||
|
||||
for (uint32_t i = 0; i < inst->NumInOperands(); i += 2) {
|
||||
uint32_t op_id = inst->GetSingleWordInOperand(i);
|
||||
if (op_id == inst->result_id()) {
|
||||
continue;
|
||||
}
|
||||
for (uint32_t i = 0; i < inst->NumInOperands(); i += 2) {
|
||||
uint32_t op_id = inst->GetSingleWordInOperand(i);
|
||||
if (op_id == inst->result_id()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (incoming_value == 0) {
|
||||
incoming_value = op_id;
|
||||
} else if (op_id != incoming_value) {
|
||||
// Found two possible value. Can't simplify.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (incoming_value == 0) {
|
||||
incoming_value = op_id;
|
||||
} else if (op_id != incoming_value) {
|
||||
// Found two possible value. Can't simplify.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (incoming_value == 0) {
|
||||
// Code looks invalid. Don't do anything.
|
||||
return false;
|
||||
}
|
||||
if (incoming_value == 0) {
|
||||
// Code looks invalid. Don't do anything.
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have a single incoming value. Simplify using that value.
|
||||
inst->SetOpcode(SpvOpCopyObject);
|
||||
inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {incoming_value}}});
|
||||
return true;
|
||||
};
|
||||
// We have a single incoming value. Simplify using that value.
|
||||
inst->SetOpcode(SpvOpCopyObject);
|
||||
inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {incoming_value}}});
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
FoldingRule RedundantSelect() {
|
||||
// An OpSelect instruction where both values are the same or the condition is
|
||||
// constant can be replaced by one of the values
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpSelect &&
|
||||
"Wrong opcode. Should be OpSelect.");
|
||||
@ -1683,7 +1684,7 @@ FoldingRule RedundantSelect() {
|
||||
return true;
|
||||
} else {
|
||||
// Convert to a vector shuffle.
|
||||
std::vector<ir::Operand> ops;
|
||||
std::vector<opt::Operand> ops;
|
||||
ops.push_back({SPV_OPERAND_TYPE_ID, {true_id}});
|
||||
ops.push_back({SPV_OPERAND_TYPE_ID, {false_id}});
|
||||
const analysis::VectorConstant* vector_const =
|
||||
@ -1763,7 +1764,7 @@ FloatConstantKind getFloatConstantKind(const analysis::Constant* constant) {
|
||||
}
|
||||
|
||||
FoldingRule RedundantFAdd() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFAdd && "Wrong opcode. Should be OpFAdd.");
|
||||
assert(constants.size() == 2);
|
||||
@ -1788,7 +1789,7 @@ FoldingRule RedundantFAdd() {
|
||||
}
|
||||
|
||||
FoldingRule RedundantFSub() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFSub && "Wrong opcode. Should be OpFSub.");
|
||||
assert(constants.size() == 2);
|
||||
@ -1819,7 +1820,7 @@ FoldingRule RedundantFSub() {
|
||||
}
|
||||
|
||||
FoldingRule RedundantFMul() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFMul && "Wrong opcode. Should be OpFMul.");
|
||||
assert(constants.size() == 2);
|
||||
@ -1852,7 +1853,7 @@ FoldingRule RedundantFMul() {
|
||||
}
|
||||
|
||||
FoldingRule RedundantFDiv() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFDiv && "Wrong opcode. Should be OpFDiv.");
|
||||
assert(constants.size() == 2);
|
||||
@ -1883,7 +1884,7 @@ FoldingRule RedundantFDiv() {
|
||||
}
|
||||
|
||||
FoldingRule RedundantFMix() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpExtInst &&
|
||||
"Wrong opcode. Should be OpExtInst.");
|
||||
@ -1920,11 +1921,11 @@ FoldingRule RedundantFMix() {
|
||||
// This rule look for a dot with a constant vector containing a single 1 and
|
||||
// the rest 0s. This is the same as doing an extract.
|
||||
FoldingRule DotProductDoingExtract() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpDot && "Wrong opcode. Should be OpDot.");
|
||||
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
|
||||
if (!inst->IsFloatingPointFoldingAllowed()) {
|
||||
@ -1976,7 +1977,7 @@ FoldingRule DotProductDoingExtract() {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<ir::Operand> operands;
|
||||
std::vector<opt::Operand> operands;
|
||||
operands.push_back(
|
||||
{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1u - i)}});
|
||||
operands.push_back(
|
||||
@ -1995,11 +1996,11 @@ FoldingRule DotProductDoingExtract() {
|
||||
// TODO: We can do something similar for OpImageWrite, but checking for volatile
|
||||
// is complicated. Waiting to see if it is needed.
|
||||
FoldingRule StoringUndef() {
|
||||
return [](ir::Instruction* inst,
|
||||
return [](opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>&) {
|
||||
assert(inst->opcode() == SpvOpStore && "Wrong opcode. Should be OpStore.");
|
||||
|
||||
ir::IRContext* context = inst->context();
|
||||
opt::IRContext* context = inst->context();
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
|
||||
// If this is a volatile store, the store cannot be removed.
|
||||
@ -2010,7 +2011,7 @@ FoldingRule StoringUndef() {
|
||||
}
|
||||
|
||||
uint32_t object_id = inst->GetSingleWordInOperand(kStoreObjectInIdx);
|
||||
ir::Instruction* object_inst = def_use_mgr->GetDef(object_id);
|
||||
opt::Instruction* object_inst = def_use_mgr->GetDef(object_id);
|
||||
if (object_inst->opcode() == SpvOpUndef) {
|
||||
inst->ToNop();
|
||||
return true;
|
||||
|
@ -52,7 +52,7 @@ namespace opt {
|
||||
// the later rules will not be attempted.
|
||||
|
||||
using FoldingRule = std::function<bool(
|
||||
ir::Instruction* inst,
|
||||
opt::Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants)>;
|
||||
|
||||
class FoldingRules {
|
||||
|
@ -18,10 +18,10 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status FreezeSpecConstantValuePass::Process(ir::IRContext* irContext) {
|
||||
Pass::Status FreezeSpecConstantValuePass::Process(opt::IRContext* irContext) {
|
||||
bool modified = false;
|
||||
irContext->module()->ForEachInst(
|
||||
[&modified, irContext](ir::Instruction* inst) {
|
||||
[&modified, irContext](opt::Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOp::SpvOpSpecConstant:
|
||||
inst->SetOpcode(SpvOp::SpvOpConstant);
|
||||
|
@ -26,7 +26,7 @@ namespace opt {
|
||||
class FreezeSpecConstantValuePass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "freeze-spec-const"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
Status Process(opt::IRContext*) override;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <sstream>
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
Function* Function::Clone(IRContext* ctx) const {
|
||||
Function* clone =
|
||||
@ -96,7 +96,7 @@ std::ostream& operator<<(std::ostream& str, const Function& func) {
|
||||
|
||||
std::string Function::PrettyPrint(uint32_t options) const {
|
||||
std::ostringstream str;
|
||||
ForEachInst([&str, options](const ir::Instruction* inst) {
|
||||
ForEachInst([&str, options](const opt::Instruction* inst) {
|
||||
str << inst->PrettyPrint(options);
|
||||
if (inst->opcode() != SpvOpFunctionEnd) {
|
||||
str << std::endl;
|
||||
@ -105,5 +105,5 @@ std::string Function::PrettyPrint(uint32_t options) const {
|
||||
return str.str();
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
class CFG;
|
||||
class IRContext;
|
||||
@ -102,7 +102,7 @@ class Function {
|
||||
|
||||
// Returns an iterator to the basic block |id|.
|
||||
iterator FindBlock(uint32_t bb_id) {
|
||||
return std::find_if(begin(), end(), [bb_id](const ir::BasicBlock& it_bb) {
|
||||
return std::find_if(begin(), end(), [bb_id](const opt::BasicBlock& it_bb) {
|
||||
return bb_id == it_bb.id();
|
||||
});
|
||||
}
|
||||
@ -122,8 +122,8 @@ class Function {
|
||||
// Returns the context of the current function.
|
||||
IRContext* context() const { return def_inst_->context(); }
|
||||
|
||||
BasicBlock* InsertBasicBlockAfter(std::unique_ptr<ir::BasicBlock>&& new_block,
|
||||
BasicBlock* position);
|
||||
BasicBlock* InsertBasicBlockAfter(
|
||||
std::unique_ptr<opt::BasicBlock>&& new_block, BasicBlock* position);
|
||||
|
||||
// Pretty-prints all the basic blocks in this function into a std::string.
|
||||
//
|
||||
@ -192,7 +192,7 @@ inline void Function::SetFunctionEnd(std::unique_ptr<Instruction> end_inst) {
|
||||
end_inst_ = std::move(end_inst);
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_CONSTRUCTS_H_
|
||||
|
@ -19,18 +19,18 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status IfConversion::Process(ir::IRContext* c) {
|
||||
Pass::Status IfConversion::Process(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
const ValueNumberTable& vn_table = *context()->GetValueNumberTable();
|
||||
bool modified = false;
|
||||
std::vector<ir::Instruction*> to_kill;
|
||||
std::vector<opt::Instruction*> to_kill;
|
||||
for (auto& func : *get_module()) {
|
||||
DominatorAnalysis* dominators = context()->GetDominatorAnalysis(&func);
|
||||
for (auto& block : func) {
|
||||
// Check if it is possible for |block| to have phis that can be
|
||||
// transformed.
|
||||
ir::BasicBlock* common = nullptr;
|
||||
opt::BasicBlock* common = nullptr;
|
||||
if (!CheckBlock(&block, dominators, &common)) continue;
|
||||
|
||||
// Get an insertion point.
|
||||
@ -41,11 +41,11 @@ Pass::Status IfConversion::Process(ir::IRContext* c) {
|
||||
|
||||
InstructionBuilder builder(
|
||||
context(), &*iter,
|
||||
ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping);
|
||||
opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping);
|
||||
block.ForEachPhiInst([this, &builder, &modified, &common, &to_kill,
|
||||
dominators, &block,
|
||||
&vn_table](ir::Instruction* phi) {
|
||||
&vn_table](opt::Instruction* phi) {
|
||||
// This phi is not compatible, but subsequent phis might be.
|
||||
if (!CheckType(phi->type_id())) return;
|
||||
|
||||
@ -59,13 +59,13 @@ Pass::Status IfConversion::Process(ir::IRContext* c) {
|
||||
// branches. If |then_block| dominates |inc0| or if the true edge
|
||||
// branches straight to this block and |common| is |inc0|, then |inc0|
|
||||
// is on the true branch. Otherwise the |inc1| is on the true branch.
|
||||
ir::BasicBlock* inc0 = GetIncomingBlock(phi, 0u);
|
||||
ir::Instruction* branch = common->terminator();
|
||||
opt::BasicBlock* inc0 = GetIncomingBlock(phi, 0u);
|
||||
opt::Instruction* branch = common->terminator();
|
||||
uint32_t condition = branch->GetSingleWordInOperand(0u);
|
||||
ir::BasicBlock* then_block =
|
||||
opt::BasicBlock* then_block =
|
||||
GetBlock(branch->GetSingleWordInOperand(1u));
|
||||
ir::Instruction* true_value = nullptr;
|
||||
ir::Instruction* false_value = nullptr;
|
||||
opt::Instruction* true_value = nullptr;
|
||||
opt::Instruction* false_value = nullptr;
|
||||
if ((then_block == &block && inc0 == common) ||
|
||||
dominators->Dominates(then_block, inc0)) {
|
||||
true_value = GetIncomingValue(phi, 0u);
|
||||
@ -75,14 +75,15 @@ Pass::Status IfConversion::Process(ir::IRContext* c) {
|
||||
false_value = GetIncomingValue(phi, 0u);
|
||||
}
|
||||
|
||||
ir::BasicBlock* true_def_block = context()->get_instr_block(true_value);
|
||||
ir::BasicBlock* false_def_block =
|
||||
opt::BasicBlock* true_def_block =
|
||||
context()->get_instr_block(true_value);
|
||||
opt::BasicBlock* false_def_block =
|
||||
context()->get_instr_block(false_value);
|
||||
|
||||
uint32_t true_vn = vn_table.GetValueNumber(true_value);
|
||||
uint32_t false_vn = vn_table.GetValueNumber(false_value);
|
||||
if (true_vn != 0 && true_vn == false_vn) {
|
||||
ir::Instruction* inst_to_use = nullptr;
|
||||
opt::Instruction* inst_to_use = nullptr;
|
||||
|
||||
// Try to pick an instruction that is not in a side node. If we can't
|
||||
// pick either the true for false branch as long as they can be
|
||||
@ -125,9 +126,9 @@ Pass::Status IfConversion::Process(ir::IRContext* c) {
|
||||
condition = SplatCondition(vec_data_ty, condition, &builder);
|
||||
}
|
||||
|
||||
ir::Instruction* select = builder.AddSelect(phi->type_id(), condition,
|
||||
true_value->result_id(),
|
||||
false_value->result_id());
|
||||
opt::Instruction* select = builder.AddSelect(phi->type_id(), condition,
|
||||
true_value->result_id(),
|
||||
false_value->result_id());
|
||||
context()->ReplaceAllUsesWith(phi->result_id(), select->result_id());
|
||||
to_kill.push_back(phi);
|
||||
modified = true;
|
||||
@ -144,18 +145,18 @@ Pass::Status IfConversion::Process(ir::IRContext* c) {
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
bool IfConversion::CheckBlock(ir::BasicBlock* block,
|
||||
bool IfConversion::CheckBlock(opt::BasicBlock* block,
|
||||
DominatorAnalysis* dominators,
|
||||
ir::BasicBlock** common) {
|
||||
opt::BasicBlock** common) {
|
||||
const std::vector<uint32_t>& preds = cfg()->preds(block->id());
|
||||
|
||||
// TODO(alan-baker): Extend to more than two predecessors
|
||||
if (preds.size() != 2) return false;
|
||||
|
||||
ir::BasicBlock* inc0 = context()->get_instr_block(preds[0]);
|
||||
opt::BasicBlock* inc0 = context()->get_instr_block(preds[0]);
|
||||
if (dominators->Dominates(block, inc0)) return false;
|
||||
|
||||
ir::BasicBlock* inc1 = context()->get_instr_block(preds[1]);
|
||||
opt::BasicBlock* inc1 = context()->get_instr_block(preds[1]);
|
||||
if (dominators->Dominates(block, inc1)) return false;
|
||||
|
||||
// All phis will have the same common dominator, so cache the result
|
||||
@ -163,15 +164,16 @@ bool IfConversion::CheckBlock(ir::BasicBlock* block,
|
||||
// any phi in this basic block.
|
||||
*common = dominators->CommonDominator(inc0, inc1);
|
||||
if (!*common || cfg()->IsPseudoEntryBlock(*common)) return false;
|
||||
ir::Instruction* branch = (*common)->terminator();
|
||||
opt::Instruction* branch = (*common)->terminator();
|
||||
if (branch->opcode() != SpvOpBranchConditional) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfConversion::CheckPhiUsers(ir::Instruction* phi, ir::BasicBlock* block) {
|
||||
bool IfConversion::CheckPhiUsers(opt::Instruction* phi,
|
||||
opt::BasicBlock* block) {
|
||||
return get_def_use_mgr()->WhileEachUser(phi, [block,
|
||||
this](ir::Instruction* user) {
|
||||
this](opt::Instruction* user) {
|
||||
if (user->opcode() == SpvOpPhi && context()->get_instr_block(user) == block)
|
||||
return false;
|
||||
return true;
|
||||
@ -194,7 +196,7 @@ uint32_t IfConversion::SplatCondition(analysis::Vector* vec_data_ty,
|
||||
}
|
||||
|
||||
bool IfConversion::CheckType(uint32_t id) {
|
||||
ir::Instruction* type = get_def_use_mgr()->GetDef(id);
|
||||
opt::Instruction* type = get_def_use_mgr()->GetDef(id);
|
||||
SpvOp op = type->opcode();
|
||||
if (spvOpcodeIsScalarType(op) || op == SpvOpTypePointer ||
|
||||
op == SpvOpTypeVector)
|
||||
@ -202,26 +204,26 @@ bool IfConversion::CheckType(uint32_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ir::BasicBlock* IfConversion::GetBlock(uint32_t id) {
|
||||
opt::BasicBlock* IfConversion::GetBlock(uint32_t id) {
|
||||
return context()->get_instr_block(get_def_use_mgr()->GetDef(id));
|
||||
}
|
||||
|
||||
ir::BasicBlock* IfConversion::GetIncomingBlock(ir::Instruction* phi,
|
||||
uint32_t predecessor) {
|
||||
opt::BasicBlock* IfConversion::GetIncomingBlock(opt::Instruction* phi,
|
||||
uint32_t predecessor) {
|
||||
uint32_t in_index = 2 * predecessor + 1;
|
||||
return GetBlock(phi->GetSingleWordInOperand(in_index));
|
||||
}
|
||||
|
||||
ir::Instruction* IfConversion::GetIncomingValue(ir::Instruction* phi,
|
||||
uint32_t predecessor) {
|
||||
opt::Instruction* IfConversion::GetIncomingValue(opt::Instruction* phi,
|
||||
uint32_t predecessor) {
|
||||
uint32_t in_index = 2 * predecessor;
|
||||
return get_def_use_mgr()->GetDef(phi->GetSingleWordInOperand(in_index));
|
||||
}
|
||||
|
||||
void IfConversion::HoistInstruction(ir::Instruction* inst,
|
||||
ir::BasicBlock* target_block,
|
||||
void IfConversion::HoistInstruction(opt::Instruction* inst,
|
||||
opt::BasicBlock* target_block,
|
||||
DominatorAnalysis* dominators) {
|
||||
ir::BasicBlock* inst_block = context()->get_instr_block(inst);
|
||||
opt::BasicBlock* inst_block = context()->get_instr_block(inst);
|
||||
if (!inst_block) {
|
||||
// This is in the header, and dominates everything.
|
||||
return;
|
||||
@ -239,23 +241,23 @@ void IfConversion::HoistInstruction(ir::Instruction* inst,
|
||||
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||
inst->ForEachInId(
|
||||
[this, target_block, def_use_mgr, dominators](uint32_t* id) {
|
||||
ir::Instruction* operand_inst = def_use_mgr->GetDef(*id);
|
||||
opt::Instruction* operand_inst = def_use_mgr->GetDef(*id);
|
||||
HoistInstruction(operand_inst, target_block, dominators);
|
||||
});
|
||||
|
||||
ir::Instruction* insertion_pos = target_block->terminator();
|
||||
opt::Instruction* insertion_pos = target_block->terminator();
|
||||
if ((insertion_pos)->PreviousNode()->opcode() == SpvOpSelectionMerge) {
|
||||
insertion_pos = insertion_pos->PreviousNode();
|
||||
}
|
||||
inst->RemoveFromList();
|
||||
insertion_pos->InsertBefore(std::unique_ptr<ir::Instruction>(inst));
|
||||
insertion_pos->InsertBefore(std::unique_ptr<opt::Instruction>(inst));
|
||||
context()->set_instr_block(inst, target_block);
|
||||
}
|
||||
|
||||
bool IfConversion::CanHoistInstruction(ir::Instruction* inst,
|
||||
ir::BasicBlock* target_block,
|
||||
bool IfConversion::CanHoistInstruction(opt::Instruction* inst,
|
||||
opt::BasicBlock* target_block,
|
||||
DominatorAnalysis* dominators) {
|
||||
ir::BasicBlock* inst_block = context()->get_instr_block(inst);
|
||||
opt::BasicBlock* inst_block = context()->get_instr_block(inst);
|
||||
if (!inst_block) {
|
||||
// This is in the header, and dominates everything.
|
||||
return true;
|
||||
@ -274,7 +276,7 @@ bool IfConversion::CanHoistInstruction(ir::Instruction* inst,
|
||||
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||
return inst->WhileEachInId(
|
||||
[this, target_block, def_use_mgr, dominators](uint32_t* id) {
|
||||
ir::Instruction* operand_inst = def_use_mgr->GetDef(*id);
|
||||
opt::Instruction* operand_inst = def_use_mgr->GetDef(*id);
|
||||
return CanHoistInstruction(operand_inst, target_block, dominators);
|
||||
});
|
||||
}
|
||||
|
@ -27,13 +27,13 @@ namespace opt {
|
||||
class IfConversion : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "if-conversion"; }
|
||||
Status Process(ir::IRContext* context) override;
|
||||
Status Process(opt::IRContext* context) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisDominatorAnalysis |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::kAnalysisCFG | ir::IRContext::kAnalysisNameMap;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisDominatorAnalysis |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::kAnalysisCFG | opt::IRContext::kAnalysisNameMap;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -42,14 +42,16 @@ class IfConversion : public Pass {
|
||||
bool CheckType(uint32_t id);
|
||||
|
||||
// Returns the basic block containing |id|.
|
||||
ir::BasicBlock* GetBlock(uint32_t id);
|
||||
opt::BasicBlock* GetBlock(uint32_t id);
|
||||
|
||||
// Returns the basic block for the |predecessor|'th index predecessor of
|
||||
// |phi|.
|
||||
ir::BasicBlock* GetIncomingBlock(ir::Instruction* phi, uint32_t predecessor);
|
||||
opt::BasicBlock* GetIncomingBlock(opt::Instruction* phi,
|
||||
uint32_t predecessor);
|
||||
|
||||
// Returns the instruction defining the |predecessor|'th index of |phi|.
|
||||
ir::Instruction* GetIncomingValue(ir::Instruction* phi, uint32_t predecessor);
|
||||
opt::Instruction* GetIncomingValue(opt::Instruction* phi,
|
||||
uint32_t predecessor);
|
||||
|
||||
// Returns the id of a OpCompositeConstruct boolean vector. The composite has
|
||||
// the same number of elements as |vec_data_ty| and each member is |cond|.
|
||||
@ -60,26 +62,27 @@ class IfConversion : public Pass {
|
||||
InstructionBuilder* builder);
|
||||
|
||||
// Returns true if none of |phi|'s users are in |block|.
|
||||
bool CheckPhiUsers(ir::Instruction* phi, ir::BasicBlock* block);
|
||||
bool CheckPhiUsers(opt::Instruction* phi, opt::BasicBlock* block);
|
||||
|
||||
// Returns |false| if |block| is not appropriate to transform. Only
|
||||
// transforms blocks with two predecessors. Neither incoming block can be
|
||||
// dominated by |block|. Both predecessors must share a common dominator that
|
||||
// is terminated by a conditional branch.
|
||||
bool CheckBlock(ir::BasicBlock* block, DominatorAnalysis* dominators,
|
||||
ir::BasicBlock** common);
|
||||
bool CheckBlock(opt::BasicBlock* block, DominatorAnalysis* dominators,
|
||||
opt::BasicBlock** common);
|
||||
|
||||
// Moves |inst| to |target_block| if it does not already dominate the block.
|
||||
// Any instructions that |inst| depends on are move if necessary. It is
|
||||
// assumed that |inst| can be hoisted to |target_block| as defined by
|
||||
// |CanHoistInstruction|. |dominators| is the dominator analysis for the
|
||||
// function that contains |target_block|.
|
||||
void HoistInstruction(ir::Instruction* inst, ir::BasicBlock* target_block,
|
||||
void HoistInstruction(opt::Instruction* inst, opt::BasicBlock* target_block,
|
||||
DominatorAnalysis* dominators);
|
||||
|
||||
// Returns true if it is legal to move |inst| and the instructions it depends
|
||||
// on to |target_block| if they do not already dominate |target_block|.
|
||||
bool CanHoistInstruction(ir::Instruction* inst, ir::BasicBlock* target_block,
|
||||
bool CanHoistInstruction(opt::Instruction* inst,
|
||||
opt::BasicBlock* target_block,
|
||||
DominatorAnalysis* dominators);
|
||||
};
|
||||
|
||||
|
@ -19,15 +19,15 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
bool InlineExhaustivePass::InlineExhaustive(ir::Function* func) {
|
||||
bool InlineExhaustivePass::InlineExhaustive(opt::Function* func) {
|
||||
bool modified = false;
|
||||
// Using block iterators here because of block erasures and insertions.
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end();) {
|
||||
if (IsInlinableFunctionCall(&*ii)) {
|
||||
// Inline call.
|
||||
std::vector<std::unique_ptr<ir::BasicBlock>> newBlocks;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newVars;
|
||||
std::vector<std::unique_ptr<opt::BasicBlock>> newBlocks;
|
||||
std::vector<std::unique_ptr<opt::Instruction>> newVars;
|
||||
GenInlineCode(&newBlocks, &newVars, ii, bi);
|
||||
// If call block is replaced with more than one block, point
|
||||
// succeeding phis at new last block.
|
||||
@ -59,11 +59,13 @@ bool InlineExhaustivePass::InlineExhaustive(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void InlineExhaustivePass::Initialize(ir::IRContext* c) { InitializeInline(c); }
|
||||
void InlineExhaustivePass::Initialize(opt::IRContext* c) {
|
||||
InitializeInline(c);
|
||||
}
|
||||
|
||||
Pass::Status InlineExhaustivePass::ProcessImpl() {
|
||||
// Attempt exhaustive inlining on each entry point function in module
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return InlineExhaustive(fp);
|
||||
};
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
@ -72,7 +74,7 @@ Pass::Status InlineExhaustivePass::ProcessImpl() {
|
||||
|
||||
InlineExhaustivePass::InlineExhaustivePass() {}
|
||||
|
||||
Pass::Status InlineExhaustivePass::Process(ir::IRContext* c) {
|
||||
Pass::Status InlineExhaustivePass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -34,16 +34,16 @@ namespace opt {
|
||||
class InlineExhaustivePass : public InlinePass {
|
||||
public:
|
||||
InlineExhaustivePass();
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
const char* name() const override { return "inline-entry-points-exhaustive"; }
|
||||
|
||||
private:
|
||||
// Exhaustively inline all function calls in func as well as in
|
||||
// all code that is inlined into func. Return true if func is modified.
|
||||
bool InlineExhaustive(ir::Function* func);
|
||||
bool InlineExhaustive(opt::Function* func);
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@ const uint32_t kTypePointerTypeIdInIdx = 1;
|
||||
} // anonymous namespace
|
||||
|
||||
bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) {
|
||||
const ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
|
||||
const opt::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
|
||||
switch (typeInst->opcode()) {
|
||||
case SpvOpTypeSampler:
|
||||
case SpvOpTypeImage:
|
||||
@ -47,14 +47,14 @@ bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) {
|
||||
});
|
||||
}
|
||||
|
||||
bool InlineOpaquePass::HasOpaqueArgsOrReturn(const ir::Instruction* callInst) {
|
||||
bool InlineOpaquePass::HasOpaqueArgsOrReturn(const opt::Instruction* callInst) {
|
||||
// Check return type
|
||||
if (IsOpaqueType(callInst->type_id())) return true;
|
||||
// Check args
|
||||
int icnt = 0;
|
||||
return !callInst->WhileEachInId([&icnt, this](const uint32_t* iid) {
|
||||
if (icnt > 0) {
|
||||
const ir::Instruction* argInst = get_def_use_mgr()->GetDef(*iid);
|
||||
const opt::Instruction* argInst = get_def_use_mgr()->GetDef(*iid);
|
||||
if (IsOpaqueType(argInst->type_id())) return false;
|
||||
}
|
||||
++icnt;
|
||||
@ -62,15 +62,15 @@ bool InlineOpaquePass::HasOpaqueArgsOrReturn(const ir::Instruction* callInst) {
|
||||
});
|
||||
}
|
||||
|
||||
bool InlineOpaquePass::InlineOpaque(ir::Function* func) {
|
||||
bool InlineOpaquePass::InlineOpaque(opt::Function* func) {
|
||||
bool modified = false;
|
||||
// Using block iterators here because of block erasures and insertions.
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end();) {
|
||||
if (IsInlinableFunctionCall(&*ii) && HasOpaqueArgsOrReturn(&*ii)) {
|
||||
// Inline call.
|
||||
std::vector<std::unique_ptr<ir::BasicBlock>> newBlocks;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newVars;
|
||||
std::vector<std::unique_ptr<opt::BasicBlock>> newBlocks;
|
||||
std::vector<std::unique_ptr<opt::Instruction>> newVars;
|
||||
GenInlineCode(&newBlocks, &newVars, ii, bi);
|
||||
// If call block is replaced with more than one block, point
|
||||
// succeeding phis at new last block.
|
||||
@ -92,18 +92,18 @@ bool InlineOpaquePass::InlineOpaque(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void InlineOpaquePass::Initialize(ir::IRContext* c) { InitializeInline(c); }
|
||||
void InlineOpaquePass::Initialize(opt::IRContext* c) { InitializeInline(c); }
|
||||
|
||||
Pass::Status InlineOpaquePass::ProcessImpl() {
|
||||
// Do opaque inlining on each function in entry point call tree
|
||||
ProcessFunction pfn = [this](ir::Function* fp) { return InlineOpaque(fp); };
|
||||
ProcessFunction pfn = [this](opt::Function* fp) { return InlineOpaque(fp); };
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
InlineOpaquePass::InlineOpaquePass() {}
|
||||
|
||||
Pass::Status InlineOpaquePass::Process(ir::IRContext* c) {
|
||||
Pass::Status InlineOpaquePass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace opt {
|
||||
class InlineOpaquePass : public InlinePass {
|
||||
public:
|
||||
InlineOpaquePass();
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
const char* name() const override { return "inline-entry-points-opaque"; }
|
||||
|
||||
@ -43,14 +43,14 @@ class InlineOpaquePass : public InlinePass {
|
||||
bool IsOpaqueType(uint32_t typeId);
|
||||
|
||||
// Return true if function call |callInst| has opaque argument or return type
|
||||
bool HasOpaqueArgsOrReturn(const ir::Instruction* callInst);
|
||||
bool HasOpaqueArgsOrReturn(const opt::Instruction* callInst);
|
||||
|
||||
// Inline all function calls in |func| that have opaque params or return
|
||||
// type. Inline similarly all code that is inlined into func. Return true
|
||||
// if func is modified.
|
||||
bool InlineOpaque(ir::Function* func);
|
||||
bool InlineOpaque(opt::Function* func);
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
};
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace opt {
|
||||
uint32_t InlinePass::AddPointerToType(uint32_t type_id,
|
||||
SpvStorageClass storage_class) {
|
||||
uint32_t resultId = TakeNextId();
|
||||
std::unique_ptr<ir::Instruction> type_inst(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> type_inst(new opt::Instruction(
|
||||
context(), SpvOpTypePointer, 0, resultId,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
{uint32_t(storage_class)}},
|
||||
@ -48,8 +48,8 @@ uint32_t InlinePass::AddPointerToType(uint32_t type_id,
|
||||
}
|
||||
|
||||
void InlinePass::AddBranch(uint32_t label_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<opt::Instruction> newBranch(new opt::Instruction(
|
||||
context(), SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}}));
|
||||
(*block_ptr)->AddInstruction(std::move(newBranch));
|
||||
@ -57,8 +57,8 @@ void InlinePass::AddBranch(uint32_t label_id,
|
||||
|
||||
void InlinePass::AddBranchCond(uint32_t cond_id, uint32_t true_id,
|
||||
uint32_t false_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<opt::Instruction> newBranch(new opt::Instruction(
|
||||
context(), SpvOpBranchConditional, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}},
|
||||
@ -67,8 +67,8 @@ void InlinePass::AddBranchCond(uint32_t cond_id, uint32_t true_id,
|
||||
}
|
||||
|
||||
void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newLoopMerge(new ir::Instruction(
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<opt::Instruction> newLoopMerge(new opt::Instruction(
|
||||
context(), SpvOpLoopMerge, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}},
|
||||
@ -77,8 +77,8 @@ void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
|
||||
}
|
||||
|
||||
void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newStore(new ir::Instruction(
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<opt::Instruction> newStore(new opt::Instruction(
|
||||
context(), SpvOpStore, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
|
||||
@ -86,16 +86,16 @@ void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id,
|
||||
}
|
||||
|
||||
void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<opt::Instruction> newLoad(new opt::Instruction(
|
||||
context(), SpvOpLoad, type_id, resultId,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}}));
|
||||
(*block_ptr)->AddInstruction(std::move(newLoad));
|
||||
}
|
||||
|
||||
std::unique_ptr<ir::Instruction> InlinePass::NewLabel(uint32_t label_id) {
|
||||
std::unique_ptr<ir::Instruction> newLabel(
|
||||
new ir::Instruction(context(), SpvOpLabel, 0, label_id, {}));
|
||||
std::unique_ptr<opt::Instruction> InlinePass::NewLabel(uint32_t label_id) {
|
||||
std::unique_ptr<opt::Instruction> newLabel(
|
||||
new opt::Instruction(context(), SpvOpLabel, 0, label_id, {}));
|
||||
return newLabel;
|
||||
}
|
||||
|
||||
@ -114,26 +114,26 @@ uint32_t InlinePass::GetFalseId() {
|
||||
}
|
||||
|
||||
void InlinePass::MapParams(
|
||||
ir::Function* calleeFn, ir::BasicBlock::iterator call_inst_itr,
|
||||
opt::Function* calleeFn, opt::BasicBlock::iterator call_inst_itr,
|
||||
std::unordered_map<uint32_t, uint32_t>* callee2caller) {
|
||||
int param_idx = 0;
|
||||
calleeFn->ForEachParam(
|
||||
[&call_inst_itr, ¶m_idx, &callee2caller](const ir::Instruction* cpi) {
|
||||
const uint32_t pid = cpi->result_id();
|
||||
(*callee2caller)[pid] = call_inst_itr->GetSingleWordOperand(
|
||||
kSpvFunctionCallArgumentId + param_idx);
|
||||
++param_idx;
|
||||
});
|
||||
calleeFn->ForEachParam([&call_inst_itr, ¶m_idx,
|
||||
&callee2caller](const opt::Instruction* cpi) {
|
||||
const uint32_t pid = cpi->result_id();
|
||||
(*callee2caller)[pid] = call_inst_itr->GetSingleWordOperand(
|
||||
kSpvFunctionCallArgumentId + param_idx);
|
||||
++param_idx;
|
||||
});
|
||||
}
|
||||
|
||||
void InlinePass::CloneAndMapLocals(
|
||||
ir::Function* calleeFn,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* new_vars,
|
||||
opt::Function* calleeFn,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* new_vars,
|
||||
std::unordered_map<uint32_t, uint32_t>* callee2caller) {
|
||||
auto callee_block_itr = calleeFn->begin();
|
||||
auto callee_var_itr = callee_block_itr->begin();
|
||||
while (callee_var_itr->opcode() == SpvOp::SpvOpVariable) {
|
||||
std::unique_ptr<ir::Instruction> var_inst(
|
||||
std::unique_ptr<opt::Instruction> var_inst(
|
||||
callee_var_itr->Clone(callee_var_itr->context()));
|
||||
uint32_t newId = TakeNextId();
|
||||
get_decoration_mgr()->CloneDecorations(callee_var_itr->result_id(), newId);
|
||||
@ -145,8 +145,8 @@ void InlinePass::CloneAndMapLocals(
|
||||
}
|
||||
|
||||
uint32_t InlinePass::CreateReturnVar(
|
||||
ir::Function* calleeFn,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* new_vars) {
|
||||
opt::Function* calleeFn,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* new_vars) {
|
||||
uint32_t returnVarId = 0;
|
||||
const uint32_t calleeTypeId = calleeFn->type_id();
|
||||
analysis::Type* calleeType = context()->get_type_mgr()->GetType(calleeTypeId);
|
||||
@ -158,7 +158,7 @@ uint32_t InlinePass::CreateReturnVar(
|
||||
returnVarTypeId = AddPointerToType(calleeTypeId, SpvStorageClassFunction);
|
||||
// Add return var to new function scope variables.
|
||||
returnVarId = TakeNextId();
|
||||
std::unique_ptr<ir::Instruction> var_inst(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> var_inst(new opt::Instruction(
|
||||
context(), SpvOpVariable, returnVarTypeId, returnVarId,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
{SpvStorageClassFunction}}}));
|
||||
@ -168,15 +168,15 @@ uint32_t InlinePass::CreateReturnVar(
|
||||
return returnVarId;
|
||||
}
|
||||
|
||||
bool InlinePass::IsSameBlockOp(const ir::Instruction* inst) const {
|
||||
bool InlinePass::IsSameBlockOp(const opt::Instruction* inst) const {
|
||||
return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage;
|
||||
}
|
||||
|
||||
void InlinePass::CloneSameBlockOps(
|
||||
std::unique_ptr<ir::Instruction>* inst,
|
||||
std::unique_ptr<opt::Instruction>* inst,
|
||||
std::unordered_map<uint32_t, uint32_t>* postCallSB,
|
||||
std::unordered_map<uint32_t, ir::Instruction*>* preCallSB,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unordered_map<uint32_t, opt::Instruction*>* preCallSB,
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr) {
|
||||
(*inst)->ForEachInId(
|
||||
[&postCallSB, &preCallSB, &block_ptr, this](uint32_t* iid) {
|
||||
const auto mapItr = (*postCallSB).find(*iid);
|
||||
@ -184,8 +184,8 @@ void InlinePass::CloneSameBlockOps(
|
||||
const auto mapItr2 = (*preCallSB).find(*iid);
|
||||
if (mapItr2 != (*preCallSB).end()) {
|
||||
// Clone pre-call same-block ops, map result id.
|
||||
const ir::Instruction* inInst = mapItr2->second;
|
||||
std::unique_ptr<ir::Instruction> sb_inst(
|
||||
const opt::Instruction* inInst = mapItr2->second;
|
||||
std::unique_ptr<opt::Instruction> sb_inst(
|
||||
inInst->Clone(inInst->context()));
|
||||
CloneSameBlockOps(&sb_inst, postCallSB, preCallSB, block_ptr);
|
||||
const uint32_t rid = sb_inst->result_id();
|
||||
@ -204,24 +204,24 @@ void InlinePass::CloneSameBlockOps(
|
||||
}
|
||||
|
||||
void InlinePass::GenInlineCode(
|
||||
std::vector<std::unique_ptr<ir::BasicBlock>>* new_blocks,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* new_vars,
|
||||
ir::BasicBlock::iterator call_inst_itr,
|
||||
ir::UptrVectorIterator<ir::BasicBlock> call_block_itr) {
|
||||
std::vector<std::unique_ptr<opt::BasicBlock>>* new_blocks,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* new_vars,
|
||||
opt::BasicBlock::iterator call_inst_itr,
|
||||
opt::UptrVectorIterator<opt::BasicBlock> call_block_itr) {
|
||||
// Map from all ids in the callee to their equivalent id in the caller
|
||||
// as callee instructions are copied into caller.
|
||||
std::unordered_map<uint32_t, uint32_t> callee2caller;
|
||||
// Pre-call same-block insts
|
||||
std::unordered_map<uint32_t, ir::Instruction*> preCallSB;
|
||||
std::unordered_map<uint32_t, opt::Instruction*> preCallSB;
|
||||
// Post-call same-block op ids
|
||||
std::unordered_map<uint32_t, uint32_t> postCallSB;
|
||||
|
||||
// Invalidate the def-use chains. They are not kept up to date while
|
||||
// inlining. However, certain calls try to keep them up-to-date if they are
|
||||
// valid. These operations can fail.
|
||||
context()->InvalidateAnalyses(ir::IRContext::kAnalysisDefUse);
|
||||
context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse);
|
||||
|
||||
ir::Function* calleeFn = id2function_[call_inst_itr->GetSingleWordOperand(
|
||||
opt::Function* calleeFn = id2function_[call_inst_itr->GetSingleWordOperand(
|
||||
kSpvFunctionCallFunctionId)];
|
||||
|
||||
// Check for multiple returns in the callee.
|
||||
@ -240,7 +240,7 @@ void InlinePass::GenInlineCode(
|
||||
|
||||
// Create set of callee result ids. Used to detect forward references
|
||||
std::unordered_set<uint32_t> callee_result_ids;
|
||||
calleeFn->ForEachInst([&callee_result_ids](const ir::Instruction* cpi) {
|
||||
calleeFn->ForEachInst([&callee_result_ids](const opt::Instruction* cpi) {
|
||||
const uint32_t rid = cpi->result_id();
|
||||
if (rid != 0) callee_result_ids.insert(rid);
|
||||
});
|
||||
@ -275,14 +275,15 @@ void InlinePass::GenInlineCode(
|
||||
// written to it. It is created when we encounter the OpLabel
|
||||
// of the first callee block. It is appended to new_blocks only when
|
||||
// it is complete.
|
||||
std::unique_ptr<ir::BasicBlock> new_blk_ptr;
|
||||
std::unique_ptr<opt::BasicBlock> new_blk_ptr;
|
||||
calleeFn->ForEachInst([&new_blocks, &callee2caller, &call_block_itr,
|
||||
&call_inst_itr, &new_blk_ptr, &prevInstWasReturn,
|
||||
&returnLabelId, &returnVarId, caller_is_loop_header,
|
||||
callee_begins_with_structured_header, &calleeTypeId,
|
||||
&multiBlocks, &postCallSB, &preCallSB, multiReturn,
|
||||
&singleTripLoopHeaderId, &singleTripLoopContinueId,
|
||||
&callee_result_ids, this](const ir::Instruction* cpi) {
|
||||
&callee_result_ids,
|
||||
this](const opt::Instruction* cpi) {
|
||||
switch (cpi->opcode()) {
|
||||
case SpvOpFunction:
|
||||
case SpvOpFunctionParameter:
|
||||
@ -305,8 +306,8 @@ void InlinePass::GenInlineCode(
|
||||
// Generate a return label so that we split the block with the function
|
||||
// call. Copy the terminator into the new block.
|
||||
if (returnLabelId == 0) returnLabelId = this->TakeNextId();
|
||||
std::unique_ptr<ir::Instruction> terminator(
|
||||
new ir::Instruction(context(), cpi->opcode(), 0, 0, {}));
|
||||
std::unique_ptr<opt::Instruction> terminator(
|
||||
new opt::Instruction(context(), cpi->opcode(), 0, 0, {}));
|
||||
new_blk_ptr->AddInstruction(std::move(terminator));
|
||||
break;
|
||||
}
|
||||
@ -337,14 +338,14 @@ void InlinePass::GenInlineCode(
|
||||
firstBlock = true;
|
||||
}
|
||||
// Create first/next block.
|
||||
new_blk_ptr.reset(new ir::BasicBlock(NewLabel(labelId)));
|
||||
new_blk_ptr.reset(new opt::BasicBlock(NewLabel(labelId)));
|
||||
if (firstBlock) {
|
||||
// Copy contents of original caller block up to call instruction.
|
||||
for (auto cii = call_block_itr->begin(); cii != call_inst_itr;
|
||||
cii = call_block_itr->begin()) {
|
||||
ir::Instruction* inst = &*cii;
|
||||
opt::Instruction* inst = &*cii;
|
||||
inst->RemoveFromList();
|
||||
std::unique_ptr<ir::Instruction> cp_inst(inst);
|
||||
std::unique_ptr<opt::Instruction> cp_inst(inst);
|
||||
// Remember same-block ops for possible regeneration.
|
||||
if (IsSameBlockOp(&*cp_inst)) {
|
||||
auto* sb_inst_ptr = cp_inst.get();
|
||||
@ -363,7 +364,7 @@ void InlinePass::GenInlineCode(
|
||||
AddBranch(guard_block_id, &new_blk_ptr);
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
// Start the next block.
|
||||
new_blk_ptr.reset(new ir::BasicBlock(NewLabel(guard_block_id)));
|
||||
new_blk_ptr.reset(new opt::BasicBlock(NewLabel(guard_block_id)));
|
||||
// Reset the mapping of the callee's entry block to point to
|
||||
// the guard block. Do this so we can fix up phis later on to
|
||||
// satisfy dominance.
|
||||
@ -385,14 +386,14 @@ void InlinePass::GenInlineCode(
|
||||
AddBranch(singleTripLoopHeaderId, &new_blk_ptr);
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
new_blk_ptr.reset(
|
||||
new ir::BasicBlock(NewLabel(singleTripLoopHeaderId)));
|
||||
new opt::BasicBlock(NewLabel(singleTripLoopHeaderId)));
|
||||
returnLabelId = this->TakeNextId();
|
||||
singleTripLoopContinueId = this->TakeNextId();
|
||||
AddLoopMerge(returnLabelId, singleTripLoopContinueId, &new_blk_ptr);
|
||||
uint32_t postHeaderId = this->TakeNextId();
|
||||
AddBranch(postHeaderId, &new_blk_ptr);
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
new_blk_ptr.reset(new ir::BasicBlock(NewLabel(postHeaderId)));
|
||||
new_blk_ptr.reset(new opt::BasicBlock(NewLabel(postHeaderId)));
|
||||
multiBlocks = true;
|
||||
// Reset the mapping of the callee's entry block to point to
|
||||
// the post-header block. Do this so we can fix up phis later
|
||||
@ -435,13 +436,13 @@ void InlinePass::GenInlineCode(
|
||||
// target block now, with a false branch back to the loop header.
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
new_blk_ptr.reset(
|
||||
new ir::BasicBlock(NewLabel(singleTripLoopContinueId)));
|
||||
new opt::BasicBlock(NewLabel(singleTripLoopContinueId)));
|
||||
AddBranchCond(GetFalseId(), singleTripLoopHeaderId, returnLabelId,
|
||||
&new_blk_ptr);
|
||||
}
|
||||
// Generate the return block.
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
new_blk_ptr.reset(new ir::BasicBlock(NewLabel(returnLabelId)));
|
||||
new_blk_ptr.reset(new opt::BasicBlock(NewLabel(returnLabelId)));
|
||||
multiBlocks = true;
|
||||
}
|
||||
// Load return value into result id of call, if it exists.
|
||||
@ -451,10 +452,10 @@ void InlinePass::GenInlineCode(
|
||||
AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr);
|
||||
}
|
||||
// Copy remaining instructions from caller block.
|
||||
for (ir::Instruction* inst = call_inst_itr->NextNode(); inst;
|
||||
for (opt::Instruction* inst = call_inst_itr->NextNode(); inst;
|
||||
inst = call_inst_itr->NextNode()) {
|
||||
inst->RemoveFromList();
|
||||
std::unique_ptr<ir::Instruction> cp_inst(inst);
|
||||
std::unique_ptr<opt::Instruction> cp_inst(inst);
|
||||
// If multiple blocks generated, regenerate any same-block
|
||||
// instruction that has not been seen in this last block.
|
||||
if (multiBlocks) {
|
||||
@ -472,7 +473,7 @@ void InlinePass::GenInlineCode(
|
||||
} break;
|
||||
default: {
|
||||
// Copy callee instruction and remap all input Ids.
|
||||
std::unique_ptr<ir::Instruction> cp_inst(cpi->Clone(context()));
|
||||
std::unique_ptr<opt::Instruction> cp_inst(cpi->Clone(context()));
|
||||
cp_inst->ForEachInId([&callee2caller, &callee_result_ids,
|
||||
this](uint32_t* iid) {
|
||||
const auto mapItr = callee2caller.find(*iid);
|
||||
@ -517,7 +518,7 @@ void InlinePass::GenInlineCode(
|
||||
auto loop_merge_itr = last->tail();
|
||||
--loop_merge_itr;
|
||||
assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
|
||||
std::unique_ptr<ir::Instruction> cp_inst(loop_merge_itr->Clone(context()));
|
||||
std::unique_ptr<opt::Instruction> cp_inst(loop_merge_itr->Clone(context()));
|
||||
if (caller_is_single_block_loop) {
|
||||
// Also, update its continue target to point to the last block.
|
||||
cp_inst->SetInOperand(kSpvLoopMergeContinueTargetIdInIdx, {last->id()});
|
||||
@ -535,7 +536,7 @@ void InlinePass::GenInlineCode(
|
||||
}
|
||||
}
|
||||
|
||||
bool InlinePass::IsInlinableFunctionCall(const ir::Instruction* inst) {
|
||||
bool InlinePass::IsInlinableFunctionCall(const opt::Instruction* inst) {
|
||||
if (inst->opcode() != SpvOp::SpvOpFunctionCall) return false;
|
||||
const uint32_t calleeFnId =
|
||||
inst->GetSingleWordOperand(kSpvFunctionCallFunctionId);
|
||||
@ -544,16 +545,16 @@ bool InlinePass::IsInlinableFunctionCall(const ir::Instruction* inst) {
|
||||
}
|
||||
|
||||
void InlinePass::UpdateSucceedingPhis(
|
||||
std::vector<std::unique_ptr<ir::BasicBlock>>& new_blocks) {
|
||||
std::vector<std::unique_ptr<opt::BasicBlock>>& new_blocks) {
|
||||
const auto firstBlk = new_blocks.begin();
|
||||
const auto lastBlk = new_blocks.end() - 1;
|
||||
const uint32_t firstId = (*firstBlk)->id();
|
||||
const uint32_t lastId = (*lastBlk)->id();
|
||||
const ir::BasicBlock& const_last_block = *lastBlk->get();
|
||||
const opt::BasicBlock& const_last_block = *lastBlk->get();
|
||||
const_last_block.ForEachSuccessorLabel(
|
||||
[&firstId, &lastId, this](const uint32_t succ) {
|
||||
ir::BasicBlock* sbp = this->id2block_[succ];
|
||||
sbp->ForEachPhiInst([&firstId, &lastId](ir::Instruction* phi) {
|
||||
opt::BasicBlock* sbp = this->id2block_[succ];
|
||||
sbp->ForEachPhiInst([&firstId, &lastId](opt::Instruction* phi) {
|
||||
phi->ForEachInId([&firstId, &lastId](uint32_t* id) {
|
||||
if (*id == firstId) *id = lastId;
|
||||
});
|
||||
@ -561,7 +562,7 @@ void InlinePass::UpdateSucceedingPhis(
|
||||
});
|
||||
}
|
||||
|
||||
bool InlinePass::HasMultipleReturns(ir::Function* func) {
|
||||
bool InlinePass::HasMultipleReturns(opt::Function* func) {
|
||||
bool seenReturn = false;
|
||||
bool multipleReturns = false;
|
||||
for (auto& blk : *func) {
|
||||
@ -579,7 +580,7 @@ bool InlinePass::HasMultipleReturns(ir::Function* func) {
|
||||
return multipleReturns;
|
||||
}
|
||||
|
||||
void InlinePass::ComputeStructuredSuccessors(ir::Function* func) {
|
||||
void InlinePass::ComputeStructuredSuccessors(opt::Function* func) {
|
||||
// If header, make merge block first successor.
|
||||
for (auto& blk : *func) {
|
||||
uint32_t mbid = blk.MergeBlockIdIfAny();
|
||||
@ -596,12 +597,12 @@ void InlinePass::ComputeStructuredSuccessors(ir::Function* func) {
|
||||
}
|
||||
|
||||
InlinePass::GetBlocksFunction InlinePass::StructuredSuccessorsFunction() {
|
||||
return [this](const ir::BasicBlock* block) {
|
||||
return [this](const opt::BasicBlock* block) {
|
||||
return &(block2structured_succs_[block]);
|
||||
};
|
||||
}
|
||||
|
||||
bool InlinePass::HasNoReturnInLoop(ir::Function* func) {
|
||||
bool InlinePass::HasNoReturnInLoop(opt::Function* func) {
|
||||
// If control not structured, do not do loop/return analysis
|
||||
// TODO: Analyze returns in non-structured control flow
|
||||
if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
|
||||
@ -612,8 +613,8 @@ bool InlinePass::HasNoReturnInLoop(ir::Function* func) {
|
||||
ComputeStructuredSuccessors(func);
|
||||
auto ignore_block = [](cbb_ptr) {};
|
||||
auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
|
||||
std::list<const ir::BasicBlock*> structuredOrder;
|
||||
CFA<ir::BasicBlock>::DepthFirstTraversal(
|
||||
std::list<const opt::BasicBlock*> structuredOrder;
|
||||
CFA<opt::BasicBlock>::DepthFirstTraversal(
|
||||
&*func->begin(), StructuredSuccessorsFunction(), ignore_block,
|
||||
[&](cbb_ptr b) { structuredOrder.push_front(b); }, ignore_edge);
|
||||
// Search for returns in loops. Only need to track outermost loop
|
||||
@ -643,7 +644,7 @@ bool InlinePass::HasNoReturnInLoop(ir::Function* func) {
|
||||
return !return_in_loop;
|
||||
}
|
||||
|
||||
void InlinePass::AnalyzeReturns(ir::Function* func) {
|
||||
void InlinePass::AnalyzeReturns(opt::Function* func) {
|
||||
// Look for multiple returns
|
||||
if (!HasMultipleReturns(func)) {
|
||||
no_return_in_loop_.insert(func->result_id());
|
||||
@ -654,7 +655,7 @@ void InlinePass::AnalyzeReturns(ir::Function* func) {
|
||||
if (HasNoReturnInLoop(func)) no_return_in_loop_.insert(func->result_id());
|
||||
}
|
||||
|
||||
bool InlinePass::IsInlinableFunction(ir::Function* func) {
|
||||
bool InlinePass::IsInlinableFunction(opt::Function* func) {
|
||||
// We can only inline a function if it has blocks.
|
||||
if (func->cbegin() == func->cend()) return false;
|
||||
// Do not inline functions with returns in loops. Currently early return
|
||||
@ -667,7 +668,7 @@ bool InlinePass::IsInlinableFunction(ir::Function* func) {
|
||||
no_return_in_loop_.cend();
|
||||
}
|
||||
|
||||
void InlinePass::InitializeInline(ir::IRContext* c) {
|
||||
void InlinePass::InitializeInline(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
false_id_ = 0;
|
||||
|
@ -32,11 +32,11 @@ namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class InlinePass : public Pass {
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
using cbb_ptr = const opt::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
std::function<std::vector<opt::BasicBlock*>*(const opt::BasicBlock*)>;
|
||||
|
||||
InlinePass();
|
||||
virtual ~InlinePass() = default;
|
||||
@ -47,60 +47,61 @@ class InlinePass : public Pass {
|
||||
uint32_t AddPointerToType(uint32_t type_id, SpvStorageClass storage_class);
|
||||
|
||||
// Add unconditional branch to labelId to end of block block_ptr.
|
||||
void AddBranch(uint32_t labelId, std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
void AddBranch(uint32_t labelId, std::unique_ptr<opt::BasicBlock>* block_ptr);
|
||||
|
||||
// Add conditional branch to end of block |block_ptr|.
|
||||
void AddBranchCond(uint32_t cond_id, uint32_t true_id, uint32_t false_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr);
|
||||
|
||||
// Add unconditional branch to labelId to end of block block_ptr.
|
||||
void AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr);
|
||||
|
||||
// Add store of valId to ptrId to end of block block_ptr.
|
||||
void AddStore(uint32_t ptrId, uint32_t valId,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr);
|
||||
|
||||
// Add load of ptrId into resultId to end of block block_ptr.
|
||||
void AddLoad(uint32_t typeId, uint32_t resultId, uint32_t ptrId,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr);
|
||||
|
||||
// Return new label.
|
||||
std::unique_ptr<ir::Instruction> NewLabel(uint32_t label_id);
|
||||
std::unique_ptr<opt::Instruction> NewLabel(uint32_t label_id);
|
||||
|
||||
// Returns the id for the boolean false value. Looks in the module first
|
||||
// and creates it if not found. Remembers it for future calls.
|
||||
uint32_t GetFalseId();
|
||||
|
||||
// Map callee params to caller args
|
||||
void MapParams(ir::Function* calleeFn, ir::BasicBlock::iterator call_inst_itr,
|
||||
void MapParams(opt::Function* calleeFn,
|
||||
opt::BasicBlock::iterator call_inst_itr,
|
||||
std::unordered_map<uint32_t, uint32_t>* callee2caller);
|
||||
|
||||
// Clone and map callee locals
|
||||
void CloneAndMapLocals(
|
||||
ir::Function* calleeFn,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* new_vars,
|
||||
opt::Function* calleeFn,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* new_vars,
|
||||
std::unordered_map<uint32_t, uint32_t>* callee2caller);
|
||||
|
||||
// Create return variable for callee clone code if needed. Return id
|
||||
// if created, otherwise 0.
|
||||
uint32_t CreateReturnVar(
|
||||
ir::Function* calleeFn,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* new_vars);
|
||||
opt::Function* calleeFn,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* new_vars);
|
||||
|
||||
// Return true if instruction must be in the same block that its result
|
||||
// is used.
|
||||
bool IsSameBlockOp(const ir::Instruction* inst) const;
|
||||
bool IsSameBlockOp(const opt::Instruction* inst) const;
|
||||
|
||||
// Clone operands which must be in same block as consumer instructions.
|
||||
// Look in preCallSB for instructions that need cloning. Look in
|
||||
// postCallSB for instructions already cloned. Add cloned instruction
|
||||
// to postCallSB.
|
||||
void CloneSameBlockOps(
|
||||
std::unique_ptr<ir::Instruction>* inst,
|
||||
std::unique_ptr<opt::Instruction>* inst,
|
||||
std::unordered_map<uint32_t, uint32_t>* postCallSB,
|
||||
std::unordered_map<uint32_t, ir::Instruction*>* preCallSB,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
std::unordered_map<uint32_t, opt::Instruction*>* preCallSB,
|
||||
std::unique_ptr<opt::BasicBlock>* block_ptr);
|
||||
|
||||
// Return in new_blocks the result of inlining the call at call_inst_itr
|
||||
// within its block at call_block_itr. The block at call_block_itr can
|
||||
@ -116,13 +117,13 @@ class InlinePass : public Pass {
|
||||
// Also return in new_vars additional OpVariable instructions required by
|
||||
// and to be inserted into the caller function after the block at
|
||||
// call_block_itr is replaced with new_blocks.
|
||||
void GenInlineCode(std::vector<std::unique_ptr<ir::BasicBlock>>* new_blocks,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* new_vars,
|
||||
ir::BasicBlock::iterator call_inst_itr,
|
||||
ir::UptrVectorIterator<ir::BasicBlock> call_block_itr);
|
||||
void GenInlineCode(std::vector<std::unique_ptr<opt::BasicBlock>>* new_blocks,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* new_vars,
|
||||
opt::BasicBlock::iterator call_inst_itr,
|
||||
opt::UptrVectorIterator<opt::BasicBlock> call_block_itr);
|
||||
|
||||
// Return true if |inst| is a function call that can be inlined.
|
||||
bool IsInlinableFunctionCall(const ir::Instruction* inst);
|
||||
bool IsInlinableFunctionCall(const opt::Instruction* inst);
|
||||
|
||||
// Compute structured successors for function |func|.
|
||||
// A block's structured successors are the blocks it branches to
|
||||
@ -131,39 +132,39 @@ class InlinePass : public Pass {
|
||||
// This assures correct depth first search in the presence of early
|
||||
// returns and kills. If the successor vector contain duplicates
|
||||
// if the merge block, they are safely ignored by DFS.
|
||||
void ComputeStructuredSuccessors(ir::Function* func);
|
||||
void ComputeStructuredSuccessors(opt::Function* func);
|
||||
|
||||
// Return function to return ordered structure successors for a given block
|
||||
// Assumes ComputeStructuredSuccessors() has been called.
|
||||
GetBlocksFunction StructuredSuccessorsFunction();
|
||||
|
||||
// Return true if |func| has multiple returns
|
||||
bool HasMultipleReturns(ir::Function* func);
|
||||
bool HasMultipleReturns(opt::Function* func);
|
||||
|
||||
// Return true if |func| has no return in a loop. The current analysis
|
||||
// requires structured control flow, so return false if control flow not
|
||||
// structured ie. module is not a shader.
|
||||
bool HasNoReturnInLoop(ir::Function* func);
|
||||
bool HasNoReturnInLoop(opt::Function* func);
|
||||
|
||||
// Find all functions with multiple returns and no returns in loops
|
||||
void AnalyzeReturns(ir::Function* func);
|
||||
void AnalyzeReturns(opt::Function* func);
|
||||
|
||||
// Return true if |func| is a function that can be inlined.
|
||||
bool IsInlinableFunction(ir::Function* func);
|
||||
bool IsInlinableFunction(opt::Function* func);
|
||||
|
||||
// Update phis in succeeding blocks to point to new last block
|
||||
void UpdateSucceedingPhis(
|
||||
std::vector<std::unique_ptr<ir::BasicBlock>>& new_blocks);
|
||||
std::vector<std::unique_ptr<opt::BasicBlock>>& new_blocks);
|
||||
|
||||
// Initialize state for optimization of |module|
|
||||
void InitializeInline(ir::IRContext* c);
|
||||
void InitializeInline(opt::IRContext* c);
|
||||
|
||||
// Map from function's result id to function.
|
||||
std::unordered_map<uint32_t, ir::Function*> id2function_;
|
||||
std::unordered_map<uint32_t, opt::Function*> id2function_;
|
||||
|
||||
// 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_;
|
||||
std::unordered_map<uint32_t, opt::BasicBlock*> id2block_;
|
||||
|
||||
// Set of ids of functions with multiple returns.
|
||||
std::set<uint32_t> multi_return_funcs_;
|
||||
@ -181,7 +182,7 @@ class InlinePass : public Pass {
|
||||
// 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*>>
|
||||
std::unordered_map<const opt::BasicBlock*, std::vector<opt::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "reflect.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
namespace {
|
||||
// Indices used to get particular operands out of instructions using InOperand.
|
||||
@ -145,7 +145,7 @@ void Instruction::ReplaceOperands(const OperandList& new_operands) {
|
||||
|
||||
bool Instruction::IsReadOnlyLoad() const {
|
||||
if (IsLoad()) {
|
||||
ir::Instruction* address_def = GetBaseAddress();
|
||||
opt::Instruction* address_def = GetBaseAddress();
|
||||
if (!address_def || address_def->opcode() != SpvOpVariable) {
|
||||
return false;
|
||||
}
|
||||
@ -161,7 +161,7 @@ Instruction* Instruction::GetBaseAddress() const {
|
||||
"GetBaseAddress should only be called on instructions that take a "
|
||||
"pointer or image.");
|
||||
uint32_t base = GetSingleWordInOperand(kLoadBaseIndex);
|
||||
ir::Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base);
|
||||
opt::Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base);
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
switch (base_inst->opcode()) {
|
||||
@ -217,7 +217,7 @@ bool Instruction::IsVulkanStorageImage() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ir::Instruction* base_type =
|
||||
opt::Instruction* base_type =
|
||||
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
|
||||
if (base_type->opcode() != SpvOpTypeImage) {
|
||||
return false;
|
||||
@ -243,7 +243,7 @@ bool Instruction::IsVulkanSampledImage() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ir::Instruction* base_type =
|
||||
opt::Instruction* base_type =
|
||||
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
|
||||
if (base_type->opcode() != SpvOpTypeImage) {
|
||||
return false;
|
||||
@ -269,7 +269,7 @@ bool Instruction::IsVulkanStorageTexelBuffer() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ir::Instruction* base_type =
|
||||
opt::Instruction* base_type =
|
||||
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
|
||||
if (base_type->opcode() != SpvOpTypeImage) {
|
||||
return false;
|
||||
@ -291,7 +291,7 @@ bool Instruction::IsVulkanStorageBuffer() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ir::Instruction* base_type =
|
||||
opt::Instruction* base_type =
|
||||
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
|
||||
|
||||
if (base_type->opcode() != SpvOpTypeStruct) {
|
||||
@ -303,13 +303,15 @@ bool Instruction::IsVulkanStorageBuffer() const {
|
||||
bool is_buffer_block = false;
|
||||
context()->get_decoration_mgr()->ForEachDecoration(
|
||||
base_type->result_id(), SpvDecorationBufferBlock,
|
||||
[&is_buffer_block](const ir::Instruction&) { is_buffer_block = true; });
|
||||
[&is_buffer_block](const opt::Instruction&) {
|
||||
is_buffer_block = true;
|
||||
});
|
||||
return is_buffer_block;
|
||||
} else if (storage_class == SpvStorageClassStorageBuffer) {
|
||||
bool is_block = false;
|
||||
context()->get_decoration_mgr()->ForEachDecoration(
|
||||
base_type->result_id(), SpvDecorationBlock,
|
||||
[&is_block](const ir::Instruction&) { is_block = true; });
|
||||
[&is_block](const opt::Instruction&) { is_block = true; });
|
||||
return is_block;
|
||||
}
|
||||
return false;
|
||||
@ -325,7 +327,7 @@ bool Instruction::IsVulkanUniformBuffer() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ir::Instruction* base_type =
|
||||
opt::Instruction* base_type =
|
||||
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
|
||||
if (base_type->opcode() != SpvOpTypeStruct) {
|
||||
return false;
|
||||
@ -334,7 +336,7 @@ bool Instruction::IsVulkanUniformBuffer() const {
|
||||
bool is_block = false;
|
||||
context()->get_decoration_mgr()->ForEachDecoration(
|
||||
base_type->result_id(), SpvDecorationBlock,
|
||||
[&is_block](const ir::Instruction&) { is_block = true; });
|
||||
[&is_block](const opt::Instruction&) { is_block = true; });
|
||||
return is_block;
|
||||
}
|
||||
|
||||
@ -414,7 +416,7 @@ bool Instruction::IsValidBasePointer() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ir::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
|
||||
opt::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
|
||||
if (type->opcode() != SpvOpTypePointer) {
|
||||
return false;
|
||||
}
|
||||
@ -429,7 +431,7 @@ bool Instruction::IsValidBasePointer() const {
|
||||
}
|
||||
|
||||
uint32_t pointee_type_id = type->GetSingleWordInOperand(1);
|
||||
ir::Instruction* pointee_type_inst =
|
||||
opt::Instruction* pointee_type_inst =
|
||||
context()->get_def_use_mgr()->GetDef(pointee_type_id);
|
||||
|
||||
if (pointee_type_inst->IsOpaqueType()) {
|
||||
@ -444,7 +446,7 @@ bool Instruction::IsValidBaseImage() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
ir::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
|
||||
opt::Instruction* type = context()->get_def_use_mgr()->GetDef(tid);
|
||||
return (type->opcode() == SpvOpTypeImage ||
|
||||
type->opcode() == SpvOpTypeSampledImage);
|
||||
}
|
||||
@ -453,13 +455,14 @@ bool Instruction::IsOpaqueType() const {
|
||||
if (opcode() == SpvOpTypeStruct) {
|
||||
bool is_opaque = false;
|
||||
ForEachInOperand([&is_opaque, this](const uint32_t* op_id) {
|
||||
ir::Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id);
|
||||
opt::Instruction* type_inst =
|
||||
context()->get_def_use_mgr()->GetDef(*op_id);
|
||||
is_opaque |= type_inst->IsOpaqueType();
|
||||
});
|
||||
return is_opaque;
|
||||
} else if (opcode() == SpvOpTypeArray) {
|
||||
uint32_t sub_type_id = GetSingleWordInOperand(0);
|
||||
ir::Instruction* sub_type_inst =
|
||||
opt::Instruction* sub_type_inst =
|
||||
context()->get_def_use_mgr()->GetDef(sub_type_id);
|
||||
return sub_type_inst->IsOpaqueType();
|
||||
} else {
|
||||
@ -491,7 +494,7 @@ bool Instruction::IsFloatingPointFoldingAllowed() const {
|
||||
bool is_nocontract = false;
|
||||
context_->get_decoration_mgr()->WhileEachDecoration(
|
||||
opcode_, SpvDecorationNoContraction,
|
||||
[&is_nocontract](const ir::Instruction&) {
|
||||
[&is_nocontract](const opt::Instruction&) {
|
||||
is_nocontract = true;
|
||||
return false;
|
||||
});
|
||||
@ -515,7 +518,7 @@ std::string Instruction::PrettyPrint(uint32_t options) const {
|
||||
options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst) {
|
||||
std::ostream& operator<<(std::ostream& str, const opt::Instruction& inst) {
|
||||
str << inst.PrettyPrint();
|
||||
return str;
|
||||
}
|
||||
@ -722,5 +725,5 @@ bool Instruction::IsOpcodeSafeToDelete() const {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
class Function;
|
||||
class IRContext;
|
||||
@ -458,7 +458,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
||||
// to provide the correct interpretation of types, constants, etc.
|
||||
//
|
||||
// Disassembly uses raw ids (not pretty printed names).
|
||||
std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst);
|
||||
std::ostream& operator<<(std::ostream& str, const opt::Instruction& inst);
|
||||
|
||||
inline bool Instruction::operator==(const Instruction& other) const {
|
||||
return unique_id() == other.unique_id();
|
||||
@ -709,7 +709,7 @@ bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); }
|
||||
bool Instruction::IsConstant() const {
|
||||
return IsCompileTimeConstantInst(opcode());
|
||||
}
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_INSTRUCTION_H_
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "instruction_list.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
InstructionList::iterator InstructionList::iterator::InsertBefore(
|
||||
std::vector<std::unique_ptr<Instruction>>&& list) {
|
||||
@ -32,5 +32,5 @@ InstructionList::iterator InstructionList::iterator::InsertBefore(
|
||||
i.get()->InsertBefore(node_);
|
||||
return iterator(i.release());
|
||||
}
|
||||
} // namespace ir
|
||||
} // namespace spvtools
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
// This class is intended to be the container for Instructions. This container
|
||||
// owns the instructions that are in it. When removing an Instruction from the
|
||||
@ -124,7 +124,7 @@ void InstructionList::clear() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_INSTRUCTION_LIST_H_
|
||||
|
@ -34,31 +34,31 @@ const uint32_t kInvalidId = std::numeric_limits<uint32_t>::max();
|
||||
// - Instruction to block analysis
|
||||
class InstructionBuilder {
|
||||
public:
|
||||
using InsertionPointTy = ir::BasicBlock::iterator;
|
||||
using InsertionPointTy = opt::BasicBlock::iterator;
|
||||
|
||||
// Creates an InstructionBuilder, all new instructions will be inserted before
|
||||
// the instruction |insert_before|.
|
||||
InstructionBuilder(
|
||||
ir::IRContext* context, ir::Instruction* insert_before,
|
||||
ir::IRContext::Analysis preserved_analyses = ir::IRContext::kAnalysisNone)
|
||||
InstructionBuilder(opt::IRContext* context, opt::Instruction* insert_before,
|
||||
opt::IRContext::Analysis preserved_analyses =
|
||||
opt::IRContext::kAnalysisNone)
|
||||
: InstructionBuilder(context, context->get_instr_block(insert_before),
|
||||
InsertionPointTy(insert_before),
|
||||
preserved_analyses) {}
|
||||
|
||||
// Creates an InstructionBuilder, all new instructions will be inserted at the
|
||||
// end of the basic block |parent_block|.
|
||||
InstructionBuilder(
|
||||
ir::IRContext* context, ir::BasicBlock* parent_block,
|
||||
ir::IRContext::Analysis preserved_analyses = ir::IRContext::kAnalysisNone)
|
||||
InstructionBuilder(opt::IRContext* context, opt::BasicBlock* parent_block,
|
||||
opt::IRContext::Analysis preserved_analyses =
|
||||
opt::IRContext::kAnalysisNone)
|
||||
: InstructionBuilder(context, parent_block, parent_block->end(),
|
||||
preserved_analyses) {}
|
||||
|
||||
// Creates a new selection merge instruction.
|
||||
// The id |merge_id| is the merge basic block id.
|
||||
ir::Instruction* AddSelectionMerge(
|
||||
opt::Instruction* AddSelectionMerge(
|
||||
uint32_t merge_id,
|
||||
uint32_t selection_control = SpvSelectionControlMaskNone) {
|
||||
std::unique_ptr<ir::Instruction> new_branch_merge(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> new_branch_merge(new opt::Instruction(
|
||||
GetContext(), SpvOpSelectionMerge, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_SELECTION_CONTROL,
|
||||
@ -69,8 +69,8 @@ class InstructionBuilder {
|
||||
// Creates a new branch instruction to |label_id|.
|
||||
// Note that the user must make sure the final basic block is
|
||||
// well formed.
|
||||
ir::Instruction* AddBranch(uint32_t label_id) {
|
||||
std::unique_ptr<ir::Instruction> new_branch(new ir::Instruction(
|
||||
opt::Instruction* AddBranch(uint32_t label_id) {
|
||||
std::unique_ptr<opt::Instruction> new_branch(new opt::Instruction(
|
||||
GetContext(), SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}}));
|
||||
return AddInstruction(std::move(new_branch));
|
||||
@ -91,14 +91,14 @@ class InstructionBuilder {
|
||||
// selection merge instruction.
|
||||
// Note that the user must make sure the final basic block is
|
||||
// well formed.
|
||||
ir::Instruction* AddConditionalBranch(
|
||||
opt::Instruction* AddConditionalBranch(
|
||||
uint32_t cond_id, uint32_t true_id, uint32_t false_id,
|
||||
uint32_t merge_id = kInvalidId,
|
||||
uint32_t selection_control = SpvSelectionControlMaskNone) {
|
||||
if (merge_id != kInvalidId) {
|
||||
AddSelectionMerge(merge_id, selection_control);
|
||||
}
|
||||
std::unique_ptr<ir::Instruction> new_branch(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> new_branch(new opt::Instruction(
|
||||
GetContext(), SpvOpBranchConditional, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}},
|
||||
@ -119,28 +119,29 @@ class InstructionBuilder {
|
||||
// selection merge instruction.
|
||||
// Note that the user must make sure the final basic block is
|
||||
// well formed.
|
||||
ir::Instruction* AddSwitch(
|
||||
opt::Instruction* AddSwitch(
|
||||
uint32_t selector_id, uint32_t default_id,
|
||||
const std::vector<std::pair<ir::Operand::OperandData, uint32_t>>& targets,
|
||||
const std::vector<std::pair<opt::Operand::OperandData, uint32_t>>&
|
||||
targets,
|
||||
uint32_t merge_id = kInvalidId,
|
||||
uint32_t selection_control = SpvSelectionControlMaskNone) {
|
||||
if (merge_id != kInvalidId) {
|
||||
AddSelectionMerge(merge_id, selection_control);
|
||||
}
|
||||
std::vector<ir::Operand> operands;
|
||||
std::vector<opt::Operand> operands;
|
||||
operands.emplace_back(
|
||||
ir::Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {selector_id}});
|
||||
opt::Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {selector_id}});
|
||||
operands.emplace_back(
|
||||
ir::Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {default_id}});
|
||||
opt::Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {default_id}});
|
||||
for (auto& target : targets) {
|
||||
operands.emplace_back(
|
||||
ir::Operand{spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
|
||||
target.first});
|
||||
operands.emplace_back(ir::Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{target.second}});
|
||||
operands.emplace_back(opt::Operand{
|
||||
spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
|
||||
target.first});
|
||||
operands.emplace_back(opt::Operand{
|
||||
spv_operand_type_t::SPV_OPERAND_TYPE_ID, {target.second}});
|
||||
}
|
||||
std::unique_ptr<ir::Instruction> new_switch(
|
||||
new ir::Instruction(GetContext(), SpvOpSwitch, 0, 0, operands));
|
||||
std::unique_ptr<opt::Instruction> new_switch(
|
||||
new opt::Instruction(GetContext(), SpvOpSwitch, 0, 0, operands));
|
||||
return AddInstruction(std::move(new_switch));
|
||||
}
|
||||
|
||||
@ -148,14 +149,14 @@ class InstructionBuilder {
|
||||
// The id |type| must be the id of the phi instruction's type.
|
||||
// The vector |incomings| must be a sequence of pairs of <definition id,
|
||||
// parent id>.
|
||||
ir::Instruction* AddPhi(uint32_t type,
|
||||
const std::vector<uint32_t>& incomings) {
|
||||
opt::Instruction* AddPhi(uint32_t type,
|
||||
const std::vector<uint32_t>& incomings) {
|
||||
assert(incomings.size() % 2 == 0 && "A sequence of pairs is expected");
|
||||
std::vector<ir::Operand> phi_ops;
|
||||
std::vector<opt::Operand> phi_ops;
|
||||
for (size_t i = 0; i < incomings.size(); i++) {
|
||||
phi_ops.push_back({SPV_OPERAND_TYPE_ID, {incomings[i]}});
|
||||
}
|
||||
std::unique_ptr<ir::Instruction> phi_inst(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> phi_inst(new opt::Instruction(
|
||||
GetContext(), SpvOpPhi, type, GetContext()->TakeNextId(), phi_ops));
|
||||
return AddInstruction(std::move(phi_inst));
|
||||
}
|
||||
@ -165,8 +166,8 @@ class InstructionBuilder {
|
||||
// |op1| and |op2| types.
|
||||
// The id |op1| is the left hand side of the operation.
|
||||
// The id |op2| is the right hand side of the operation.
|
||||
ir::Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) {
|
||||
std::unique_ptr<ir::Instruction> inst(new ir::Instruction(
|
||||
opt::Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) {
|
||||
std::unique_ptr<opt::Instruction> inst(new opt::Instruction(
|
||||
GetContext(), SpvOpIAdd, type, GetContext()->TakeNextId(),
|
||||
{{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
|
||||
return AddInstruction(std::move(inst));
|
||||
@ -176,10 +177,10 @@ class InstructionBuilder {
|
||||
// The id |op1| is the left hand side of the operation.
|
||||
// The id |op2| is the right hand side of the operation.
|
||||
// It is assumed that |op1| and |op2| have the same underlying type.
|
||||
ir::Instruction* AddULessThan(uint32_t op1, uint32_t op2) {
|
||||
opt::Instruction* AddULessThan(uint32_t op1, uint32_t op2) {
|
||||
analysis::Bool bool_type;
|
||||
uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
|
||||
std::unique_ptr<ir::Instruction> inst(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> inst(new opt::Instruction(
|
||||
GetContext(), SpvOpULessThan, type, GetContext()->TakeNextId(),
|
||||
{{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
|
||||
return AddInstruction(std::move(inst));
|
||||
@ -189,10 +190,10 @@ class InstructionBuilder {
|
||||
// The id |op1| is the left hand side of the operation.
|
||||
// The id |op2| is the right hand side of the operation.
|
||||
// It is assumed that |op1| and |op2| have the same underlying type.
|
||||
ir::Instruction* AddSLessThan(uint32_t op1, uint32_t op2) {
|
||||
opt::Instruction* AddSLessThan(uint32_t op1, uint32_t op2) {
|
||||
analysis::Bool bool_type;
|
||||
uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
|
||||
std::unique_ptr<ir::Instruction> inst(new ir::Instruction(
|
||||
std::unique_ptr<opt::Instruction> inst(new opt::Instruction(
|
||||
GetContext(), SpvOpSLessThan, type, GetContext()->TakeNextId(),
|
||||
{{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
|
||||
return AddInstruction(std::move(inst));
|
||||
@ -202,8 +203,8 @@ class InstructionBuilder {
|
||||
// |op1|. The id |op1| is the left hand side of the operation. The id |op2| is
|
||||
// the right hand side of the operation. It is assumed that |op1| and |op2|
|
||||
// have the same underlying type.
|
||||
ir::Instruction* AddLessThan(uint32_t op1, uint32_t op2) {
|
||||
ir::Instruction* op1_insn = context_->get_def_use_mgr()->GetDef(op1);
|
||||
opt::Instruction* AddLessThan(uint32_t op1, uint32_t op2) {
|
||||
opt::Instruction* op1_insn = context_->get_def_use_mgr()->GetDef(op1);
|
||||
analysis::Type* type =
|
||||
GetContext()->get_type_mgr()->GetType(op1_insn->type_id());
|
||||
analysis::Integer* int_type = type->AsInteger();
|
||||
@ -219,11 +220,11 @@ class InstructionBuilder {
|
||||
// |type| must match the types of |true_value| and |false_value|. It is up to
|
||||
// the caller to ensure that |cond| is a correct type (bool or vector of
|
||||
// bool) for |type|.
|
||||
ir::Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
std::unique_ptr<ir::Instruction> select(new ir::Instruction(
|
||||
opt::Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
std::unique_ptr<opt::Instruction> select(new opt::Instruction(
|
||||
GetContext(), SpvOpSelect, type, GetContext()->TakeNextId(),
|
||||
std::initializer_list<ir::Operand>{
|
||||
std::initializer_list<opt::Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {cond}},
|
||||
{SPV_OPERAND_TYPE_ID, {true_value}},
|
||||
{SPV_OPERAND_TYPE_ID, {false_value}}}));
|
||||
@ -232,28 +233,28 @@ class InstructionBuilder {
|
||||
|
||||
// Adds a signed int32 constant to the binary.
|
||||
// The |value| parameter is the constant value to be added.
|
||||
ir::Instruction* Add32BitSignedIntegerConstant(int32_t value) {
|
||||
opt::Instruction* Add32BitSignedIntegerConstant(int32_t value) {
|
||||
return Add32BitConstantInteger<int32_t>(value, true);
|
||||
}
|
||||
|
||||
// Create a composite construct.
|
||||
// |type| should be a composite type and the number of elements it has should
|
||||
// match the size od |ids|.
|
||||
ir::Instruction* AddCompositeConstruct(uint32_t type,
|
||||
const std::vector<uint32_t>& ids) {
|
||||
std::vector<ir::Operand> ops;
|
||||
opt::Instruction* AddCompositeConstruct(uint32_t type,
|
||||
const std::vector<uint32_t>& ids) {
|
||||
std::vector<opt::Operand> ops;
|
||||
for (auto id : ids) {
|
||||
ops.emplace_back(SPV_OPERAND_TYPE_ID,
|
||||
std::initializer_list<uint32_t>{id});
|
||||
}
|
||||
std::unique_ptr<ir::Instruction> construct(
|
||||
new ir::Instruction(GetContext(), SpvOpCompositeConstruct, type,
|
||||
GetContext()->TakeNextId(), ops));
|
||||
std::unique_ptr<opt::Instruction> construct(
|
||||
new opt::Instruction(GetContext(), SpvOpCompositeConstruct, type,
|
||||
GetContext()->TakeNextId(), ops));
|
||||
return AddInstruction(std::move(construct));
|
||||
}
|
||||
// Adds an unsigned int32 constant to the binary.
|
||||
// The |value| parameter is the constant value to be added.
|
||||
ir::Instruction* Add32BitUnsignedIntegerConstant(uint32_t value) {
|
||||
opt::Instruction* Add32BitUnsignedIntegerConstant(uint32_t value) {
|
||||
return Add32BitConstantInteger<uint32_t>(value, false);
|
||||
}
|
||||
|
||||
@ -262,7 +263,7 @@ class InstructionBuilder {
|
||||
// signed constant otherwise as an unsigned constant. If |sign| is false the
|
||||
// value must not be a negative number.
|
||||
template <typename T>
|
||||
ir::Instruction* Add32BitConstantInteger(T value, bool sign) {
|
||||
opt::Instruction* Add32BitConstantInteger(T value, bool sign) {
|
||||
// Assert that we are not trying to store a negative number in an unsigned
|
||||
// type.
|
||||
if (!sign)
|
||||
@ -293,58 +294,58 @@ class InstructionBuilder {
|
||||
return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant);
|
||||
}
|
||||
|
||||
ir::Instruction* AddCompositeExtract(
|
||||
opt::Instruction* AddCompositeExtract(
|
||||
uint32_t type, uint32_t id_of_composite,
|
||||
const std::vector<uint32_t>& index_list) {
|
||||
std::vector<ir::Operand> operands;
|
||||
std::vector<opt::Operand> operands;
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {id_of_composite}});
|
||||
|
||||
for (uint32_t index : index_list) {
|
||||
operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}});
|
||||
}
|
||||
|
||||
std::unique_ptr<ir::Instruction> new_inst(
|
||||
new ir::Instruction(GetContext(), SpvOpCompositeExtract, type,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
std::unique_ptr<opt::Instruction> new_inst(
|
||||
new opt::Instruction(GetContext(), SpvOpCompositeExtract, type,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
// Creates an unreachable instruction.
|
||||
ir::Instruction* AddUnreachable() {
|
||||
std::unique_ptr<ir::Instruction> select(
|
||||
new ir::Instruction(GetContext(), SpvOpUnreachable, 0, 0,
|
||||
std::initializer_list<ir::Operand>{}));
|
||||
opt::Instruction* AddUnreachable() {
|
||||
std::unique_ptr<opt::Instruction> select(
|
||||
new opt::Instruction(GetContext(), SpvOpUnreachable, 0, 0,
|
||||
std::initializer_list<opt::Operand>{}));
|
||||
return AddInstruction(std::move(select));
|
||||
}
|
||||
|
||||
ir::Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id,
|
||||
std::vector<uint32_t> ids) {
|
||||
std::vector<ir::Operand> operands;
|
||||
opt::Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id,
|
||||
std::vector<uint32_t> ids) {
|
||||
std::vector<opt::Operand> operands;
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}});
|
||||
|
||||
for (uint32_t index_id : ids) {
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
|
||||
}
|
||||
|
||||
std::unique_ptr<ir::Instruction> new_inst(
|
||||
new ir::Instruction(GetContext(), SpvOpAccessChain, type_id,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
std::unique_ptr<opt::Instruction> new_inst(
|
||||
new opt::Instruction(GetContext(), SpvOpAccessChain, type_id,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
ir::Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id) {
|
||||
std::vector<ir::Operand> operands;
|
||||
opt::Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id) {
|
||||
std::vector<opt::Operand> operands;
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}});
|
||||
|
||||
std::unique_ptr<ir::Instruction> new_inst(
|
||||
new ir::Instruction(GetContext(), SpvOpLoad, type_id,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
std::unique_ptr<opt::Instruction> new_inst(
|
||||
new opt::Instruction(GetContext(), SpvOpLoad, type_id,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
// Inserts the new instruction before the insertion point.
|
||||
ir::Instruction* AddInstruction(std::unique_ptr<ir::Instruction>&& insn) {
|
||||
ir::Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
|
||||
opt::Instruction* AddInstruction(std::unique_ptr<opt::Instruction>&& insn) {
|
||||
opt::Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
|
||||
UpdateInstrToBlockMapping(insn_ptr);
|
||||
UpdateDefUseMgr(insn_ptr);
|
||||
return insn_ptr;
|
||||
@ -355,65 +356,65 @@ class InstructionBuilder {
|
||||
|
||||
// Change the insertion point to insert before the instruction
|
||||
// |insert_before|.
|
||||
void SetInsertPoint(ir::Instruction* insert_before) {
|
||||
void SetInsertPoint(opt::Instruction* insert_before) {
|
||||
parent_ = context_->get_instr_block(insert_before);
|
||||
insert_before_ = InsertionPointTy(insert_before);
|
||||
}
|
||||
|
||||
// Change the insertion point to insert at the end of the basic block
|
||||
// |parent_block|.
|
||||
void SetInsertPoint(ir::BasicBlock* parent_block) {
|
||||
void SetInsertPoint(opt::BasicBlock* parent_block) {
|
||||
parent_ = parent_block;
|
||||
insert_before_ = parent_block->end();
|
||||
}
|
||||
|
||||
// Returns the context which instructions are constructed for.
|
||||
ir::IRContext* GetContext() const { return context_; }
|
||||
opt::IRContext* GetContext() const { return context_; }
|
||||
|
||||
// Returns the set of preserved analyses.
|
||||
inline ir::IRContext::Analysis GetPreservedAnalysis() const {
|
||||
inline opt::IRContext::Analysis GetPreservedAnalysis() const {
|
||||
return preserved_analyses_;
|
||||
}
|
||||
|
||||
private:
|
||||
InstructionBuilder(ir::IRContext* context, ir::BasicBlock* parent,
|
||||
InstructionBuilder(opt::IRContext* context, opt::BasicBlock* parent,
|
||||
InsertionPointTy insert_before,
|
||||
ir::IRContext::Analysis preserved_analyses)
|
||||
opt::IRContext::Analysis preserved_analyses)
|
||||
: context_(context),
|
||||
parent_(parent),
|
||||
insert_before_(insert_before),
|
||||
preserved_analyses_(preserved_analyses) {
|
||||
assert(!(preserved_analyses_ &
|
||||
~(ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping)));
|
||||
~(opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping)));
|
||||
}
|
||||
|
||||
// Returns true if the users requested to update |analysis|.
|
||||
inline bool IsAnalysisUpdateRequested(
|
||||
ir::IRContext::Analysis analysis) const {
|
||||
opt::IRContext::Analysis analysis) const {
|
||||
return preserved_analyses_ & analysis;
|
||||
}
|
||||
|
||||
// Updates the def/use manager if the user requested it. If he did not request
|
||||
// an update, this function does nothing.
|
||||
inline void UpdateDefUseMgr(ir::Instruction* insn) {
|
||||
if (IsAnalysisUpdateRequested(ir::IRContext::kAnalysisDefUse))
|
||||
inline void UpdateDefUseMgr(opt::Instruction* insn) {
|
||||
if (IsAnalysisUpdateRequested(opt::IRContext::kAnalysisDefUse))
|
||||
GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn);
|
||||
}
|
||||
|
||||
// Updates the instruction to block analysis if the user requested it. If he
|
||||
// did not request an update, this function does nothing.
|
||||
inline void UpdateInstrToBlockMapping(ir::Instruction* insn) {
|
||||
inline void UpdateInstrToBlockMapping(opt::Instruction* insn) {
|
||||
if (IsAnalysisUpdateRequested(
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping) &&
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping) &&
|
||||
parent_)
|
||||
GetContext()->set_instr_block(insn, parent_);
|
||||
}
|
||||
|
||||
ir::IRContext* context_;
|
||||
ir::BasicBlock* parent_;
|
||||
opt::IRContext* context_;
|
||||
opt::BasicBlock* parent_;
|
||||
InsertionPointTy insert_before_;
|
||||
const ir::IRContext::Analysis preserved_analyses_;
|
||||
const opt::IRContext::Analysis preserved_analyses_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <cstring>
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
|
||||
if (set & kAnalysisDefUse) {
|
||||
@ -92,7 +92,7 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
|
||||
valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
|
||||
}
|
||||
|
||||
Instruction* IRContext::KillInst(ir::Instruction* inst) {
|
||||
Instruction* IRContext::KillInst(opt::Instruction* inst) {
|
||||
if (!inst) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -114,11 +114,11 @@ Instruction* IRContext::KillInst(ir::Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
if (type_mgr_ && ir::IsTypeInst(inst->opcode())) {
|
||||
if (type_mgr_ && opt::IsTypeInst(inst->opcode())) {
|
||||
type_mgr_->RemoveId(inst->result_id());
|
||||
}
|
||||
|
||||
if (constant_mgr_ && ir::IsConstantInst(inst->opcode())) {
|
||||
if (constant_mgr_ && opt::IsConstantInst(inst->opcode())) {
|
||||
constant_mgr_->RemoveId(inst->result_id());
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ Instruction* IRContext::KillInst(ir::Instruction* inst) {
|
||||
}
|
||||
|
||||
bool IRContext::KillDef(uint32_t id) {
|
||||
ir::Instruction* def = get_def_use_mgr()->GetDef(id);
|
||||
opt::Instruction* def = get_def_use_mgr()->GetDef(id);
|
||||
if (def != nullptr) {
|
||||
KillInst(def);
|
||||
return true;
|
||||
@ -153,15 +153,15 @@ bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
|
||||
assert(get_def_use_mgr()->GetDef(after) &&
|
||||
"'after' is not a registered def.");
|
||||
|
||||
std::vector<std::pair<ir::Instruction*, uint32_t>> uses_to_update;
|
||||
std::vector<std::pair<opt::Instruction*, uint32_t>> uses_to_update;
|
||||
get_def_use_mgr()->ForEachUse(
|
||||
before, [&uses_to_update](ir::Instruction* user, uint32_t index) {
|
||||
before, [&uses_to_update](opt::Instruction* user, uint32_t index) {
|
||||
uses_to_update.emplace_back(user, index);
|
||||
});
|
||||
|
||||
ir::Instruction* prev = nullptr;
|
||||
opt::Instruction* prev = nullptr;
|
||||
for (auto p : uses_to_update) {
|
||||
ir::Instruction* user = p.first;
|
||||
opt::Instruction* user = p.first;
|
||||
uint32_t index = p.second;
|
||||
if (prev == nullptr || prev != user) {
|
||||
ForgetUses(user);
|
||||
@ -211,7 +211,7 @@ bool IRContext::IsConsistent() {
|
||||
if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
|
||||
for (auto& func : *module()) {
|
||||
for (auto& block : func) {
|
||||
if (!block.WhileEachInst([this, &block](ir::Instruction* inst) {
|
||||
if (!block.WhileEachInst([this, &block](opt::Instruction* inst) {
|
||||
if (get_instr_block(inst) != &block) {
|
||||
return false;
|
||||
}
|
||||
@ -257,18 +257,18 @@ void IRContext::AnalyzeUses(Instruction* inst) {
|
||||
}
|
||||
|
||||
void IRContext::KillNamesAndDecorates(uint32_t id) {
|
||||
std::vector<ir::Instruction*> decorations =
|
||||
std::vector<opt::Instruction*> decorations =
|
||||
get_decoration_mgr()->GetDecorationsFor(id, true);
|
||||
|
||||
for (Instruction* inst : decorations) {
|
||||
KillInst(inst);
|
||||
}
|
||||
|
||||
std::vector<ir::Instruction*> name_to_kill;
|
||||
std::vector<opt::Instruction*> name_to_kill;
|
||||
for (auto name : GetNames(id)) {
|
||||
name_to_kill.push_back(name.second);
|
||||
}
|
||||
for (ir::Instruction* name_inst : name_to_kill) {
|
||||
for (opt::Instruction* name_inst : name_to_kill) {
|
||||
KillInst(name_inst);
|
||||
}
|
||||
}
|
||||
@ -442,7 +442,7 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) {
|
||||
}
|
||||
}
|
||||
|
||||
void IRContext::AddCombinatorsForExtension(ir::Instruction* extension) {
|
||||
void IRContext::AddCombinatorsForExtension(opt::Instruction* extension) {
|
||||
assert(extension->opcode() == SpvOpExtInstImport &&
|
||||
"Expecting an import of an extension's instruction set.");
|
||||
const char* extension_name =
|
||||
@ -557,15 +557,15 @@ void IRContext::RemoveFromIdToName(const Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
ir::LoopDescriptor* IRContext::GetLoopDescriptor(const ir::Function* f) {
|
||||
opt::LoopDescriptor* IRContext::GetLoopDescriptor(const opt::Function* f) {
|
||||
if (!AreAnalysesValid(kAnalysisLoopAnalysis)) {
|
||||
ResetLoopAnalysis();
|
||||
}
|
||||
|
||||
std::unordered_map<const ir::Function*, ir::LoopDescriptor>::iterator it =
|
||||
std::unordered_map<const opt::Function*, opt::LoopDescriptor>::iterator it =
|
||||
loop_descriptors_.find(f);
|
||||
if (it == loop_descriptors_.end()) {
|
||||
return &loop_descriptors_.emplace(std::make_pair(f, ir::LoopDescriptor(f)))
|
||||
return &loop_descriptors_.emplace(std::make_pair(f, opt::LoopDescriptor(f)))
|
||||
.first->second;
|
||||
}
|
||||
|
||||
@ -573,7 +573,8 @@ ir::LoopDescriptor* IRContext::GetLoopDescriptor(const ir::Function* f) {
|
||||
}
|
||||
|
||||
// Gets the dominator analysis for function |f|.
|
||||
opt::DominatorAnalysis* IRContext::GetDominatorAnalysis(const ir::Function* f) {
|
||||
opt::DominatorAnalysis* IRContext::GetDominatorAnalysis(
|
||||
const opt::Function* f) {
|
||||
if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) {
|
||||
ResetDominatorAnalysis();
|
||||
}
|
||||
@ -587,7 +588,7 @@ opt::DominatorAnalysis* IRContext::GetDominatorAnalysis(const ir::Function* f) {
|
||||
|
||||
// Gets the postdominator analysis for function |f|.
|
||||
opt::PostDominatorAnalysis* IRContext::GetPostDominatorAnalysis(
|
||||
const ir::Function* f) {
|
||||
const opt::Function* f) {
|
||||
if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) {
|
||||
ResetDominatorAnalysis();
|
||||
}
|
||||
@ -599,13 +600,13 @@ opt::PostDominatorAnalysis* IRContext::GetPostDominatorAnalysis(
|
||||
return &post_dominator_trees_[f];
|
||||
}
|
||||
|
||||
bool ir::IRContext::CheckCFG() {
|
||||
bool opt::IRContext::CheckCFG() {
|
||||
std::unordered_map<uint32_t, std::vector<uint32_t>> real_preds;
|
||||
if (!AreAnalysesValid(kAnalysisCFG)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (ir::Function& function : *module()) {
|
||||
for (opt::Function& function : *module()) {
|
||||
for (const auto& bb : function) {
|
||||
bb.ForEachSuccessorLabel([&bb, &real_preds](const uint32_t lab_id) {
|
||||
real_preds[lab_id].push_back(bb.id());
|
||||
@ -650,5 +651,5 @@ bool ir::IRContext::CheckCFG() {
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include <unordered_set>
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
class IRContext {
|
||||
public:
|
||||
@ -126,8 +126,8 @@ class IRContext {
|
||||
inline IteratorRange<Module::const_inst_iterator> capabilities() const;
|
||||
|
||||
// Iterators for types, constants and global variables instructions.
|
||||
inline ir::Module::inst_iterator types_values_begin();
|
||||
inline ir::Module::inst_iterator types_values_end();
|
||||
inline opt::Module::inst_iterator types_values_begin();
|
||||
inline opt::Module::inst_iterator types_values_end();
|
||||
inline IteratorRange<Module::inst_iterator> types_values();
|
||||
inline IteratorRange<Module::const_inst_iterator> types_values() const;
|
||||
|
||||
@ -230,7 +230,7 @@ class IRContext {
|
||||
|
||||
// Returns the basic block for instruction |instr|. Re-builds the instruction
|
||||
// block map, if needed.
|
||||
ir::BasicBlock* get_instr_block(ir::Instruction* instr) {
|
||||
opt::BasicBlock* get_instr_block(opt::Instruction* instr) {
|
||||
if (!AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
|
||||
BuildInstrToBlockMapping();
|
||||
}
|
||||
@ -242,14 +242,14 @@ class IRContext {
|
||||
// needed.
|
||||
//
|
||||
// |id| must be a registered definition.
|
||||
ir::BasicBlock* get_instr_block(uint32_t id) {
|
||||
ir::Instruction* def = get_def_use_mgr()->GetDef(id);
|
||||
opt::BasicBlock* get_instr_block(uint32_t id) {
|
||||
opt::Instruction* def = get_def_use_mgr()->GetDef(id);
|
||||
return get_instr_block(def);
|
||||
}
|
||||
|
||||
// Sets the basic block for |inst|. Re-builds the mapping if it has become
|
||||
// invalid.
|
||||
void set_instr_block(ir::Instruction* inst, ir::BasicBlock* block) {
|
||||
void set_instr_block(opt::Instruction* inst, opt::BasicBlock* block) {
|
||||
if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
|
||||
instr_to_block_[inst] = block;
|
||||
}
|
||||
@ -337,7 +337,7 @@ class IRContext {
|
||||
//
|
||||
// Returns a pointer to the instruction after |inst| or |nullptr| if no such
|
||||
// instruction exists.
|
||||
Instruction* KillInst(ir::Instruction* inst);
|
||||
Instruction* KillInst(opt::Instruction* inst);
|
||||
|
||||
// Returns true if all of the given analyses are valid.
|
||||
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
|
||||
@ -371,7 +371,7 @@ class IRContext {
|
||||
void KillNamesAndDecorates(uint32_t id);
|
||||
|
||||
// Kill all name and decorate ops targeting the result id of |inst|.
|
||||
void KillNamesAndDecorates(ir::Instruction* inst);
|
||||
void KillNamesAndDecorates(opt::Instruction* inst);
|
||||
|
||||
// Returns the next unique id for use by an instruction.
|
||||
inline uint32_t TakeNextUniqueId() {
|
||||
@ -400,7 +400,7 @@ class IRContext {
|
||||
}
|
||||
|
||||
// Returns a pointer to the CFG for all the functions in |module_|.
|
||||
ir::CFG* cfg() {
|
||||
opt::CFG* cfg() {
|
||||
if (!AreAnalysesValid(kAnalysisCFG)) {
|
||||
BuildCFG();
|
||||
}
|
||||
@ -408,21 +408,21 @@ class IRContext {
|
||||
}
|
||||
|
||||
// Gets the loop descriptor for function |f|.
|
||||
ir::LoopDescriptor* GetLoopDescriptor(const ir::Function* f);
|
||||
opt::LoopDescriptor* GetLoopDescriptor(const opt::Function* f);
|
||||
|
||||
// Gets the dominator analysis for function |f|.
|
||||
opt::DominatorAnalysis* GetDominatorAnalysis(const ir::Function* f);
|
||||
opt::DominatorAnalysis* GetDominatorAnalysis(const opt::Function* f);
|
||||
|
||||
// Gets the postdominator analysis for function |f|.
|
||||
opt::PostDominatorAnalysis* GetPostDominatorAnalysis(const ir::Function* f);
|
||||
opt::PostDominatorAnalysis* GetPostDominatorAnalysis(const opt::Function* f);
|
||||
|
||||
// Remove the dominator tree of |f| from the cache.
|
||||
inline void RemoveDominatorAnalysis(const ir::Function* f) {
|
||||
inline void RemoveDominatorAnalysis(const opt::Function* f) {
|
||||
dominator_trees_.erase(f);
|
||||
}
|
||||
|
||||
// Remove the postdominator tree of |f| from the cache.
|
||||
inline void RemovePostDominatorAnalysis(const ir::Function* f) {
|
||||
inline void RemovePostDominatorAnalysis(const opt::Function* f) {
|
||||
post_dominator_trees_.erase(f);
|
||||
}
|
||||
|
||||
@ -462,7 +462,7 @@ class IRContext {
|
||||
instr_to_block_.clear();
|
||||
for (auto& fn : *module_) {
|
||||
for (auto& block : fn) {
|
||||
block.ForEachInst([this, &block](ir::Instruction* inst) {
|
||||
block.ForEachInst([this, &block](opt::Instruction* inst) {
|
||||
instr_to_block_[inst] = █
|
||||
});
|
||||
}
|
||||
@ -476,7 +476,7 @@ class IRContext {
|
||||
}
|
||||
|
||||
void BuildCFG() {
|
||||
cfg_.reset(new ir::CFG(module()));
|
||||
cfg_.reset(new opt::CFG(module()));
|
||||
valid_analyses_ = valid_analyses_ | kAnalysisCFG;
|
||||
}
|
||||
|
||||
@ -528,7 +528,7 @@ class IRContext {
|
||||
void AddCombinatorsForCapability(uint32_t capability);
|
||||
|
||||
// Add the combinator opcode for the given extension to combinator_ops_.
|
||||
void AddCombinatorsForExtension(ir::Instruction* extension);
|
||||
void AddCombinatorsForExtension(opt::Instruction* extension);
|
||||
|
||||
// Remove |inst| from |id_to_name_| if it is in map.
|
||||
void RemoveFromIdToName(const Instruction* inst);
|
||||
@ -569,7 +569,7 @@ class IRContext {
|
||||
//
|
||||
// NOTE: Do not traverse this map. Ever. Use the function and basic block
|
||||
// iterators to traverse instructions.
|
||||
std::unordered_map<ir::Instruction*, ir::BasicBlock*> instr_to_block_;
|
||||
std::unordered_map<opt::Instruction*, opt::BasicBlock*> instr_to_block_;
|
||||
|
||||
// A bitset indicating which analyes are currently valid.
|
||||
Analysis valid_analyses_;
|
||||
@ -579,16 +579,17 @@ class IRContext {
|
||||
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> combinator_ops_;
|
||||
|
||||
// The CFG for all the functions in |module_|.
|
||||
std::unique_ptr<ir::CFG> cfg_;
|
||||
std::unique_ptr<opt::CFG> cfg_;
|
||||
|
||||
// Each function in the module will create its own dominator tree. We cache
|
||||
// the result so it doesn't need to be rebuilt each time.
|
||||
std::map<const ir::Function*, opt::DominatorAnalysis> dominator_trees_;
|
||||
std::map<const ir::Function*, opt::PostDominatorAnalysis>
|
||||
std::map<const opt::Function*, opt::DominatorAnalysis> dominator_trees_;
|
||||
std::map<const opt::Function*, opt::PostDominatorAnalysis>
|
||||
post_dominator_trees_;
|
||||
|
||||
// Cache of loop descriptors for each function.
|
||||
std::unordered_map<const ir::Function*, ir::LoopDescriptor> loop_descriptors_;
|
||||
std::unordered_map<const opt::Function*, opt::LoopDescriptor>
|
||||
loop_descriptors_;
|
||||
|
||||
// Constant manager for |module_|.
|
||||
std::unique_ptr<opt::analysis::ConstantManager> constant_mgr_;
|
||||
@ -610,27 +611,27 @@ class IRContext {
|
||||
std::unique_ptr<opt::InstructionFolder> inst_folder_;
|
||||
};
|
||||
|
||||
inline ir::IRContext::Analysis operator|(ir::IRContext::Analysis lhs,
|
||||
ir::IRContext::Analysis rhs) {
|
||||
return static_cast<ir::IRContext::Analysis>(static_cast<int>(lhs) |
|
||||
static_cast<int>(rhs));
|
||||
inline opt::IRContext::Analysis operator|(opt::IRContext::Analysis lhs,
|
||||
opt::IRContext::Analysis rhs) {
|
||||
return static_cast<opt::IRContext::Analysis>(static_cast<int>(lhs) |
|
||||
static_cast<int>(rhs));
|
||||
}
|
||||
|
||||
inline ir::IRContext::Analysis& operator|=(ir::IRContext::Analysis& lhs,
|
||||
ir::IRContext::Analysis rhs) {
|
||||
lhs = static_cast<ir::IRContext::Analysis>(static_cast<int>(lhs) |
|
||||
static_cast<int>(rhs));
|
||||
inline opt::IRContext::Analysis& operator|=(opt::IRContext::Analysis& lhs,
|
||||
opt::IRContext::Analysis rhs) {
|
||||
lhs = static_cast<opt::IRContext::Analysis>(static_cast<int>(lhs) |
|
||||
static_cast<int>(rhs));
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline ir::IRContext::Analysis operator<<(ir::IRContext::Analysis a,
|
||||
int shift) {
|
||||
return static_cast<ir::IRContext::Analysis>(static_cast<int>(a) << shift);
|
||||
inline opt::IRContext::Analysis operator<<(opt::IRContext::Analysis a,
|
||||
int shift) {
|
||||
return static_cast<opt::IRContext::Analysis>(static_cast<int>(a) << shift);
|
||||
}
|
||||
|
||||
inline ir::IRContext::Analysis& operator<<=(ir::IRContext::Analysis& a,
|
||||
int shift) {
|
||||
a = static_cast<ir::IRContext::Analysis>(static_cast<int>(a) << shift);
|
||||
inline opt::IRContext::Analysis& operator<<=(opt::IRContext::Analysis& a,
|
||||
int shift) {
|
||||
a = static_cast<opt::IRContext::Analysis>(static_cast<int>(a) << shift);
|
||||
return a;
|
||||
}
|
||||
|
||||
@ -674,11 +675,11 @@ IteratorRange<Module::const_inst_iterator> IRContext::capabilities() const {
|
||||
return ((const Module*)module())->capabilities();
|
||||
}
|
||||
|
||||
ir::Module::inst_iterator IRContext::types_values_begin() {
|
||||
opt::Module::inst_iterator IRContext::types_values_begin() {
|
||||
return module()->types_values_begin();
|
||||
}
|
||||
|
||||
ir::Module::inst_iterator IRContext::types_values_end() {
|
||||
opt::Module::inst_iterator IRContext::types_values_end() {
|
||||
return module()->types_values_end();
|
||||
}
|
||||
|
||||
@ -849,7 +850,7 @@ IRContext::GetNames(uint32_t id) {
|
||||
return make_range(std::move(result.first), std::move(result.second));
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_IR_CONTEXT_H
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "reflect.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
IrLoader::IrLoader(const MessageConsumer& consumer, Module* m)
|
||||
: consumer_(consumer),
|
||||
@ -157,5 +157,5 @@ void IrLoader::EndModule() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
// Loader class for constructing SPIR-V in-memory IR representation. Methods in
|
||||
// this class are designed to work with the interface for spvBinaryParse() in
|
||||
@ -78,7 +78,7 @@ class IrLoader {
|
||||
std::vector<Instruction> dbg_line_info_;
|
||||
};
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_IR_LOADER_H_
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <vector>
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
// An ad hoc iterator class for std::vector<std::unique_ptr<|ValueType|>>. The
|
||||
// purpose of this iterator class is to provide transparent access to those
|
||||
@ -351,7 +351,7 @@ inline
|
||||
return UptrVectorIterator(container_, container_->begin() + index);
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_ITERATOR_H_
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status LICMPass::Process(ir::IRContext* c) {
|
||||
Pass::Status LICMPass::Process(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
bool modified = false;
|
||||
|
||||
@ -35,21 +35,21 @@ Pass::Status LICMPass::Process(ir::IRContext* c) {
|
||||
|
||||
bool LICMPass::ProcessIRContext() {
|
||||
bool modified = false;
|
||||
ir::Module* module = get_module();
|
||||
opt::Module* module = get_module();
|
||||
|
||||
// Process each function in the module
|
||||
for (ir::Function& f : *module) {
|
||||
for (opt::Function& f : *module) {
|
||||
modified |= ProcessFunction(&f);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool LICMPass::ProcessFunction(ir::Function* f) {
|
||||
bool LICMPass::ProcessFunction(opt::Function* f) {
|
||||
bool modified = false;
|
||||
ir::LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f);
|
||||
opt::LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f);
|
||||
|
||||
// Process each loop in the function
|
||||
for (ir::Loop& loop : *loop_descriptor) {
|
||||
for (opt::Loop& loop : *loop_descriptor) {
|
||||
// Ignore nested loops, as we will process them in order in ProcessLoop
|
||||
if (loop.IsNested()) {
|
||||
continue;
|
||||
@ -59,19 +59,19 @@ bool LICMPass::ProcessFunction(ir::Function* f) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool LICMPass::ProcessLoop(ir::Loop* loop, ir::Function* f) {
|
||||
bool LICMPass::ProcessLoop(opt::Loop* loop, opt::Function* f) {
|
||||
bool modified = false;
|
||||
|
||||
// Process all nested loops first
|
||||
for (ir::Loop* nested_loop : *loop) {
|
||||
for (opt::Loop* nested_loop : *loop) {
|
||||
modified |= ProcessLoop(nested_loop, f);
|
||||
}
|
||||
|
||||
std::vector<ir::BasicBlock*> loop_bbs{};
|
||||
std::vector<opt::BasicBlock*> loop_bbs{};
|
||||
modified |= AnalyseAndHoistFromBB(loop, f, loop->GetHeaderBlock(), &loop_bbs);
|
||||
|
||||
for (size_t i = 0; i < loop_bbs.size(); ++i) {
|
||||
ir::BasicBlock* bb = loop_bbs[i];
|
||||
opt::BasicBlock* bb = loop_bbs[i];
|
||||
// do not delete the element
|
||||
modified |= AnalyseAndHoistFromBB(loop, f, bb, &loop_bbs);
|
||||
}
|
||||
@ -79,12 +79,12 @@ bool LICMPass::ProcessLoop(ir::Loop* loop, ir::Function* f) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool LICMPass::AnalyseAndHoistFromBB(ir::Loop* loop, ir::Function* f,
|
||||
ir::BasicBlock* bb,
|
||||
std::vector<ir::BasicBlock*>* loop_bbs) {
|
||||
bool LICMPass::AnalyseAndHoistFromBB(opt::Loop* loop, opt::Function* f,
|
||||
opt::BasicBlock* bb,
|
||||
std::vector<opt::BasicBlock*>* loop_bbs) {
|
||||
bool modified = false;
|
||||
std::function<void(ir::Instruction*)> hoist_inst =
|
||||
[this, &loop, &modified](ir::Instruction* inst) {
|
||||
std::function<void(opt::Instruction*)> hoist_inst =
|
||||
[this, &loop, &modified](opt::Instruction* inst) {
|
||||
if (loop->ShouldHoistInstruction(this->context(), inst)) {
|
||||
HoistInstruction(loop, inst);
|
||||
modified = true;
|
||||
@ -108,14 +108,14 @@ bool LICMPass::AnalyseAndHoistFromBB(ir::Loop* loop, ir::Function* f,
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool LICMPass::IsImmediatelyContainedInLoop(ir::Loop* loop, ir::Function* f,
|
||||
ir::BasicBlock* bb) {
|
||||
ir::LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f);
|
||||
bool LICMPass::IsImmediatelyContainedInLoop(opt::Loop* loop, opt::Function* f,
|
||||
opt::BasicBlock* bb) {
|
||||
opt::LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f);
|
||||
return loop == (*loop_descriptor)[bb->id()];
|
||||
}
|
||||
|
||||
void LICMPass::HoistInstruction(ir::Loop* loop, ir::Instruction* inst) {
|
||||
ir::BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock();
|
||||
void LICMPass::HoistInstruction(opt::Loop* loop, opt::Instruction* inst) {
|
||||
opt::BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock();
|
||||
inst->InsertBefore(std::move(&(*pre_header_bb->tail())));
|
||||
context()->set_instr_block(inst, pre_header_bb);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class LICMPass : public Pass {
|
||||
LICMPass() {}
|
||||
|
||||
const char* name() const override { return "loop-invariant-code-motion"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
Status Process(opt::IRContext*) override;
|
||||
|
||||
private:
|
||||
// Searches the IRContext for functions and processes each, moving invariants
|
||||
@ -40,26 +40,26 @@ class LICMPass : public Pass {
|
||||
|
||||
// Checks the function for loops, calling ProcessLoop on each one found.
|
||||
// Returns true if a change was made to the function, false otherwise.
|
||||
bool ProcessFunction(ir::Function* f);
|
||||
bool ProcessFunction(opt::Function* f);
|
||||
|
||||
// Checks for invariants in the loop and attempts to move them to the loops
|
||||
// preheader. Works from inner loop to outer when nested loops are found.
|
||||
// Returns true if a change was made to the loop, false otherwise.
|
||||
bool ProcessLoop(ir::Loop* loop, ir::Function* f);
|
||||
bool ProcessLoop(opt::Loop* loop, opt::Function* f);
|
||||
|
||||
// Analyses each instruction in |bb|, hoisting invariants to |pre_header_bb|.
|
||||
// Each child of |bb| wrt to |dom_tree| is pushed to |loop_bbs|
|
||||
bool AnalyseAndHoistFromBB(ir::Loop* loop, ir::Function* f,
|
||||
ir::BasicBlock* bb,
|
||||
std::vector<ir::BasicBlock*>* loop_bbs);
|
||||
bool AnalyseAndHoistFromBB(opt::Loop* loop, opt::Function* f,
|
||||
opt::BasicBlock* bb,
|
||||
std::vector<opt::BasicBlock*>* loop_bbs);
|
||||
|
||||
// Returns true if |bb| is immediately contained in |loop|
|
||||
bool IsImmediatelyContainedInLoop(ir::Loop* loop, ir::Function* f,
|
||||
ir::BasicBlock* bb);
|
||||
bool IsImmediatelyContainedInLoop(opt::Loop* loop, opt::Function* f,
|
||||
opt::BasicBlock* bb);
|
||||
|
||||
// Move the instruction to the given BasicBlock
|
||||
// This method will update the instruction to block mapping for the context
|
||||
void HoistInstruction(ir::Loop* loop, ir::Instruction* inst);
|
||||
void HoistInstruction(opt::Loop* loop, opt::Instruction* inst);
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -33,20 +33,20 @@ const uint32_t kTypeIntWidthInIdx = 0;
|
||||
|
||||
void LocalAccessChainConvertPass::BuildAndAppendInst(
|
||||
SpvOp opcode, uint32_t typeId, uint32_t resultId,
|
||||
const std::vector<ir::Operand>& in_opnds,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts) {
|
||||
std::unique_ptr<ir::Instruction> newInst(
|
||||
new ir::Instruction(context(), opcode, typeId, resultId, in_opnds));
|
||||
const std::vector<opt::Operand>& in_opnds,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts) {
|
||||
std::unique_ptr<opt::Instruction> newInst(
|
||||
new opt::Instruction(context(), opcode, typeId, resultId, in_opnds));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newInst);
|
||||
newInsts->emplace_back(std::move(newInst));
|
||||
}
|
||||
|
||||
uint32_t LocalAccessChainConvertPass::BuildAndAppendVarLoad(
|
||||
const ir::Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts) {
|
||||
const opt::Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts) {
|
||||
const uint32_t ldResultId = TakeNextId();
|
||||
*varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
|
||||
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(*varId);
|
||||
const opt::Instruction* varInst = get_def_use_mgr()->GetDef(*varId);
|
||||
assert(varInst->opcode() == SpvOpVariable);
|
||||
*varPteTypeId = GetPointeeTypeId(varInst);
|
||||
BuildAndAppendInst(SpvOpLoad, *varPteTypeId, ldResultId,
|
||||
@ -56,11 +56,11 @@ uint32_t LocalAccessChainConvertPass::BuildAndAppendVarLoad(
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::AppendConstantOperands(
|
||||
const ir::Instruction* ptrInst, std::vector<ir::Operand>* in_opnds) {
|
||||
const opt::Instruction* ptrInst, std::vector<opt::Operand>* in_opnds) {
|
||||
uint32_t iidIdx = 0;
|
||||
ptrInst->ForEachInId([&iidIdx, &in_opnds, this](const uint32_t* iid) {
|
||||
if (iidIdx > 0) {
|
||||
const ir::Instruction* cInst = get_def_use_mgr()->GetDef(*iid);
|
||||
const opt::Instruction* cInst = get_def_use_mgr()->GetDef(*iid);
|
||||
uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx);
|
||||
in_opnds->push_back(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {val}});
|
||||
@ -70,8 +70,8 @@ void LocalAccessChainConvertPass::AppendConstantOperands(
|
||||
}
|
||||
|
||||
uint32_t LocalAccessChainConvertPass::GenAccessChainLoadReplacement(
|
||||
const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts) {
|
||||
const opt::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts) {
|
||||
// Build and append load of variable in ptrInst
|
||||
uint32_t varId;
|
||||
uint32_t varPteTypeId;
|
||||
@ -81,7 +81,7 @@ uint32_t LocalAccessChainConvertPass::GenAccessChainLoadReplacement(
|
||||
// Build and append Extract
|
||||
const uint32_t extResultId = TakeNextId();
|
||||
const uint32_t ptrPteTypeId = GetPointeeTypeId(ptrInst);
|
||||
std::vector<ir::Operand> ext_in_opnds = {
|
||||
std::vector<opt::Operand> ext_in_opnds = {
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}};
|
||||
AppendConstantOperands(ptrInst, &ext_in_opnds);
|
||||
BuildAndAppendInst(SpvOpCompositeExtract, ptrPteTypeId, extResultId,
|
||||
@ -90,8 +90,8 @@ uint32_t LocalAccessChainConvertPass::GenAccessChainLoadReplacement(
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
const ir::Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts) {
|
||||
const opt::Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts) {
|
||||
// Build and append load of variable in ptrInst
|
||||
uint32_t varId;
|
||||
uint32_t varPteTypeId;
|
||||
@ -100,7 +100,7 @@ void LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
|
||||
// Build and append Insert
|
||||
const uint32_t insResultId = TakeNextId();
|
||||
std::vector<ir::Operand> ins_in_opnds = {
|
||||
std::vector<opt::Operand> ins_in_opnds = {
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}};
|
||||
AppendConstantOperands(ptrInst, &ins_in_opnds);
|
||||
@ -115,11 +115,11 @@ void LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
}
|
||||
|
||||
bool LocalAccessChainConvertPass::IsConstantIndexAccessChain(
|
||||
const ir::Instruction* acp) const {
|
||||
const opt::Instruction* acp) const {
|
||||
uint32_t inIdx = 0;
|
||||
return acp->WhileEachInId([&inIdx, this](const uint32_t* tid) {
|
||||
if (inIdx > 0) {
|
||||
ir::Instruction* opInst = get_def_use_mgr()->GetDef(*tid);
|
||||
opt::Instruction* opInst = get_def_use_mgr()->GetDef(*tid);
|
||||
if (opInst->opcode() != SpvOpConstant) return false;
|
||||
}
|
||||
++inIdx;
|
||||
@ -129,7 +129,7 @@ bool LocalAccessChainConvertPass::IsConstantIndexAccessChain(
|
||||
|
||||
bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) {
|
||||
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
|
||||
if (get_def_use_mgr()->WhileEachUser(ptrId, [this](ir::Instruction* user) {
|
||||
if (get_def_use_mgr()->WhileEachUser(ptrId, [this](opt::Instruction* user) {
|
||||
SpvOp op = user->opcode();
|
||||
if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
|
||||
if (!HasOnlySupportedRefs(user->result_id())) {
|
||||
@ -147,14 +147,14 @@ bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::FindTargetVars(ir::Function* func) {
|
||||
void LocalAccessChainConvertPass::FindTargetVars(opt::Function* func) {
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
switch (ii->opcode()) {
|
||||
case SpvOpStore:
|
||||
case SpvOpLoad: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId)) break;
|
||||
const SpvOp op = ptrInst->opcode();
|
||||
// Rule out variables with non-supported refs eg function calls
|
||||
@ -185,21 +185,22 @@ void LocalAccessChainConvertPass::FindTargetVars(ir::Function* func) {
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalAccessChainConvertPass::ConvertLocalAccessChains(ir::Function* func) {
|
||||
bool LocalAccessChainConvertPass::ConvertLocalAccessChains(
|
||||
opt::Function* func) {
|
||||
FindTargetVars(func);
|
||||
// Replace access chains of all targeted variables with equivalent
|
||||
// extract and insert sequences
|
||||
bool modified = false;
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
std::vector<ir::Instruction*> dead_instructions;
|
||||
std::vector<opt::Instruction*> dead_instructions;
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
switch (ii->opcode()) {
|
||||
case SpvOpLoad: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode())) break;
|
||||
if (!IsTargetVar(varId)) break;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
|
||||
std::vector<std::unique_ptr<opt::Instruction>> newInsts;
|
||||
uint32_t replId = GenAccessChainLoadReplacement(ptrInst, &newInsts);
|
||||
context()->KillNamesAndDecorates(&*ii);
|
||||
context()->ReplaceAllUsesWith(ii->result_id(), replId);
|
||||
@ -211,10 +212,10 @@ bool LocalAccessChainConvertPass::ConvertLocalAccessChains(ir::Function* func) {
|
||||
} break;
|
||||
case SpvOpStore: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode())) break;
|
||||
if (!IsTargetVar(varId)) break;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
|
||||
std::vector<std::unique_ptr<opt::Instruction>> newInsts;
|
||||
uint32_t valId = ii->GetSingleWordInOperand(kStoreValIdInIdx);
|
||||
GenAccessChainStoreReplacement(ptrInst, valId, &newInsts);
|
||||
dead_instructions.push_back(&*ii);
|
||||
@ -230,9 +231,9 @@ bool LocalAccessChainConvertPass::ConvertLocalAccessChains(ir::Function* func) {
|
||||
}
|
||||
|
||||
while (!dead_instructions.empty()) {
|
||||
ir::Instruction* inst = dead_instructions.back();
|
||||
opt::Instruction* inst = dead_instructions.back();
|
||||
dead_instructions.pop_back();
|
||||
DCEInst(inst, [&dead_instructions](ir::Instruction* other_inst) {
|
||||
DCEInst(inst, [&dead_instructions](opt::Instruction* other_inst) {
|
||||
auto i = std::find(dead_instructions.begin(), dead_instructions.end(),
|
||||
other_inst);
|
||||
if (i != dead_instructions.end()) {
|
||||
@ -244,7 +245,7 @@ bool LocalAccessChainConvertPass::ConvertLocalAccessChains(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::Initialize(ir::IRContext* c) {
|
||||
void LocalAccessChainConvertPass::Initialize(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
// Initialize Target Variable Caches
|
||||
@ -272,7 +273,7 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const {
|
||||
Pass::Status LocalAccessChainConvertPass::ProcessImpl() {
|
||||
// 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())
|
||||
for (const opt::Instruction& inst : get_module()->types_values())
|
||||
if (inst.opcode() == SpvOpTypeInt &&
|
||||
inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
|
||||
return Status::SuccessWithoutChange;
|
||||
@ -284,7 +285,7 @@ Pass::Status LocalAccessChainConvertPass::ProcessImpl() {
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return ConvertLocalAccessChains(fp);
|
||||
};
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
@ -293,7 +294,7 @@ Pass::Status LocalAccessChainConvertPass::ProcessImpl() {
|
||||
|
||||
LocalAccessChainConvertPass::LocalAccessChainConvertPass() {}
|
||||
|
||||
Pass::Status LocalAccessChainConvertPass::Process(ir::IRContext* c) {
|
||||
Pass::Status LocalAccessChainConvertPass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -37,13 +37,13 @@ class LocalAccessChainConvertPass : public MemPass {
|
||||
public:
|
||||
LocalAccessChainConvertPass();
|
||||
const char* name() const override { return "convert-local-access-chains"; }
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse;
|
||||
}
|
||||
|
||||
using ProcessFunction = std::function<bool(ir::Function*)>;
|
||||
using ProcessFunction = std::function<bool(opt::Function*)>;
|
||||
|
||||
private:
|
||||
// Return true if all refs through |ptrId| are only loads or stores and
|
||||
@ -55,42 +55,42 @@ class LocalAccessChainConvertPass : public MemPass {
|
||||
// Search |func| and cache function scope variables of target type that are
|
||||
// not accessed with non-constant-index access chains. Also cache non-target
|
||||
// variables.
|
||||
void FindTargetVars(ir::Function* func);
|
||||
void FindTargetVars(opt::Function* func);
|
||||
|
||||
// Build instruction from |opcode|, |typeId|, |resultId|, and |in_opnds|.
|
||||
// Append to |newInsts|.
|
||||
void BuildAndAppendInst(
|
||||
SpvOp opcode, uint32_t typeId, uint32_t resultId,
|
||||
const std::vector<ir::Operand>& in_opnds,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
const std::vector<opt::Operand>& in_opnds,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts);
|
||||
|
||||
// Build load of variable in |ptrInst| and append to |newInsts|.
|
||||
// Return var in |varId| and its pointee type in |varPteTypeId|.
|
||||
uint32_t BuildAndAppendVarLoad(
|
||||
const ir::Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
const opt::Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts);
|
||||
|
||||
// Append literal integer operands to |in_opnds| corresponding to constant
|
||||
// integer operands from access chain |ptrInst|. Assumes all indices in
|
||||
// access chains are OpConstant.
|
||||
void AppendConstantOperands(const ir::Instruction* ptrInst,
|
||||
std::vector<ir::Operand>* in_opnds);
|
||||
void AppendConstantOperands(const opt::Instruction* ptrInst,
|
||||
std::vector<opt::Operand>* in_opnds);
|
||||
|
||||
// Create a load/insert/store equivalent to a store of
|
||||
// |valId| through (constant index) access chaing |ptrInst|.
|
||||
// Append to |newInsts|.
|
||||
void GenAccessChainStoreReplacement(
|
||||
const ir::Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
const opt::Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts);
|
||||
|
||||
// For the (constant index) access chain |ptrInst|, create an
|
||||
// equivalent load and extract. Append to |newInsts|.
|
||||
uint32_t GenAccessChainLoadReplacement(
|
||||
const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
const opt::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<opt::Instruction>>* newInsts);
|
||||
|
||||
// Return true if all indices of access chain |acp| are OpConstant integers
|
||||
bool IsConstantIndexAccessChain(const ir::Instruction* acp) const;
|
||||
bool IsConstantIndexAccessChain(const opt::Instruction* acp) const;
|
||||
|
||||
// Identify all function scope variables of target type which are
|
||||
// accessed only with loads, stores and access chains with constant
|
||||
@ -101,7 +101,7 @@ class LocalAccessChainConvertPass : public MemPass {
|
||||
//
|
||||
// Nested access chains and pointer access chains are not currently
|
||||
// converted.
|
||||
bool ConvertLocalAccessChains(ir::Function* func);
|
||||
bool ConvertLocalAccessChains(opt::Function* func);
|
||||
|
||||
// Initialize extensions whitelist
|
||||
void InitExtensions();
|
||||
@ -109,7 +109,7 @@ class LocalAccessChainConvertPass : public MemPass {
|
||||
// Return true if all extensions in this module are allowed by this pass.
|
||||
bool AllExtensionsSupported() const;
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
// Variables with only supported references, ie. loads and stores using
|
||||
|
@ -19,7 +19,7 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status LocalRedundancyEliminationPass::Process(ir::IRContext* c) {
|
||||
Pass::Status LocalRedundancyEliminationPass::Process(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
bool modified = false;
|
||||
@ -39,11 +39,12 @@ Pass::Status LocalRedundancyEliminationPass::Process(ir::IRContext* c) {
|
||||
}
|
||||
|
||||
bool LocalRedundancyEliminationPass::EliminateRedundanciesInBB(
|
||||
ir::BasicBlock* block, const ValueNumberTable& vnTable,
|
||||
opt::BasicBlock* block, const ValueNumberTable& vnTable,
|
||||
std::map<uint32_t, uint32_t>* value_to_ids) {
|
||||
bool modified = false;
|
||||
|
||||
auto func = [this, &vnTable, &modified, value_to_ids](ir::Instruction* inst) {
|
||||
auto func = [this, &vnTable, &modified,
|
||||
value_to_ids](opt::Instruction* inst) {
|
||||
if (inst->result_id() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -32,14 +32,14 @@ namespace opt {
|
||||
class LocalRedundancyEliminationPass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "local-redundancy-elimination"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
virtual ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::kAnalysisDecorations |
|
||||
ir::IRContext::kAnalysisCombinators | ir::IRContext::kAnalysisCFG |
|
||||
ir::IRContext::kAnalysisDominatorAnalysis |
|
||||
ir::IRContext::kAnalysisNameMap;
|
||||
Status Process(opt::IRContext*) override;
|
||||
virtual opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::kAnalysisDecorations |
|
||||
opt::IRContext::kAnalysisCombinators | opt::IRContext::kAnalysisCFG |
|
||||
opt::IRContext::kAnalysisDominatorAnalysis |
|
||||
opt::IRContext::kAnalysisNameMap;
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -54,7 +54,7 @@ class LocalRedundancyEliminationPass : public Pass {
|
||||
// dominates |bb|.
|
||||
//
|
||||
// Returns true if the module is changed.
|
||||
bool EliminateRedundanciesInBB(ir::BasicBlock* block,
|
||||
bool EliminateRedundanciesInBB(opt::BasicBlock* block,
|
||||
const ValueNumberTable& vnTable,
|
||||
std::map<uint32_t, uint32_t>* value_to_ids);
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ const uint32_t kStoreValIdInIdx = 1;
|
||||
|
||||
bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
|
||||
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
|
||||
if (get_def_use_mgr()->WhileEachUser(ptrId, [this](ir::Instruction* user) {
|
||||
if (get_def_use_mgr()->WhileEachUser(ptrId, [this](opt::Instruction* user) {
|
||||
SpvOp op = user->opcode();
|
||||
if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
|
||||
if (!HasOnlySupportedRefs(user->result_id())) {
|
||||
@ -48,12 +48,12 @@ bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
|
||||
}
|
||||
|
||||
bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
|
||||
ir::Function* func) {
|
||||
opt::Function* func) {
|
||||
// Perform local store/load, load/load and store/store elimination
|
||||
// on each block
|
||||
bool modified = false;
|
||||
std::vector<ir::Instruction*> instructions_to_kill;
|
||||
std::unordered_set<ir::Instruction*> instructions_to_save;
|
||||
std::vector<opt::Instruction*> instructions_to_kill;
|
||||
std::unordered_set<opt::Instruction*> instructions_to_save;
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
var2store_.clear();
|
||||
var2load_.clear();
|
||||
@ -64,7 +64,7 @@ bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
|
||||
case SpvOpStore: {
|
||||
// Verify store variable is target type
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId)) continue;
|
||||
if (!HasOnlySupportedRefs(varId)) continue;
|
||||
// If a store to the whole variable, remember it for succeeding
|
||||
@ -106,7 +106,7 @@ bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
|
||||
case SpvOpLoad: {
|
||||
// Verify store variable is target type
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
opt::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId)) continue;
|
||||
if (!HasOnlySupportedRefs(varId)) continue;
|
||||
uint32_t replId = 0;
|
||||
@ -151,14 +151,14 @@ bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
|
||||
}
|
||||
}
|
||||
|
||||
for (ir::Instruction* inst : instructions_to_kill) {
|
||||
for (opt::Instruction* inst : instructions_to_kill) {
|
||||
context()->KillInst(inst);
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
void LocalSingleBlockLoadStoreElimPass::Initialize(ir::IRContext* c) {
|
||||
void LocalSingleBlockLoadStoreElimPass::Initialize(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
// Initialize Target Type Caches
|
||||
@ -196,7 +196,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
|
||||
// return unmodified.
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return LocalSingleBlockLoadStoreElim(fp);
|
||||
};
|
||||
|
||||
@ -206,7 +206,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
|
||||
|
||||
LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElimPass() {}
|
||||
|
||||
Pass::Status LocalSingleBlockLoadStoreElimPass::Process(ir::IRContext* c) {
|
||||
Pass::Status LocalSingleBlockLoadStoreElimPass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ class LocalSingleBlockLoadStoreElimPass : public MemPass {
|
||||
public:
|
||||
LocalSingleBlockLoadStoreElimPass();
|
||||
const char* name() const override { return "eliminate-local-single-block"; }
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -58,7 +58,7 @@ class LocalSingleBlockLoadStoreElimPass : public MemPass {
|
||||
// load id with previous id and delete load. Finally, check if
|
||||
// remaining stores are useless, and delete store and variable
|
||||
// where possible. Assumes logical addressing.
|
||||
bool LocalSingleBlockLoadStoreElim(ir::Function* func);
|
||||
bool LocalSingleBlockLoadStoreElim(opt::Function* func);
|
||||
|
||||
// Initialize extensions whitelist
|
||||
void InitExtensions();
|
||||
@ -66,7 +66,7 @@ class LocalSingleBlockLoadStoreElimPass : public MemPass {
|
||||
// Return true if all extensions in this module are supported by this pass.
|
||||
bool AllExtensionsSupported() const;
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
// Map from function scope variable to a store of that variable in the
|
||||
@ -74,14 +74,14 @@ class LocalSingleBlockLoadStoreElimPass : public MemPass {
|
||||
// at the start of each block and incrementally updated as the block
|
||||
// is scanned. The stores are candidates for elimination. The map is
|
||||
// conservatively cleared when a function call is encountered.
|
||||
std::unordered_map<uint32_t, ir::Instruction*> var2store_;
|
||||
std::unordered_map<uint32_t, opt::Instruction*> var2store_;
|
||||
|
||||
// Map from function scope variable to a load of that variable in the
|
||||
// current block whose value is currently valid. This map is cleared
|
||||
// at the start of each block and incrementally updated as the block
|
||||
// is scanned. The stores are candidates for elimination. The map is
|
||||
// conservatively cleared when a function call is encountered.
|
||||
std::unordered_map<uint32_t, ir::Instruction*> var2load_;
|
||||
std::unordered_map<uint32_t, opt::Instruction*> var2load_;
|
||||
|
||||
// Set of variables whose most recent store in the current block cannot be
|
||||
// deleted, for example, if there is a load of the variable which is
|
||||
|
@ -30,12 +30,12 @@ const uint32_t kVariableInitIdInIdx = 1;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool LocalSingleStoreElimPass::LocalSingleStoreElim(ir::Function* func) {
|
||||
bool LocalSingleStoreElimPass::LocalSingleStoreElim(opt::Function* func) {
|
||||
bool modified = false;
|
||||
|
||||
// Check all function scope variables in |func|.
|
||||
ir::BasicBlock* entry_block = &*func->begin();
|
||||
for (ir::Instruction& inst : *entry_block) {
|
||||
opt::BasicBlock* entry_block = &*func->begin();
|
||||
for (opt::Instruction& inst : *entry_block) {
|
||||
if (inst.opcode() != SpvOpVariable) {
|
||||
break;
|
||||
}
|
||||
@ -45,7 +45,7 @@ bool LocalSingleStoreElimPass::LocalSingleStoreElim(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void LocalSingleStoreElimPass::Initialize(ir::IRContext* irContext) {
|
||||
void LocalSingleStoreElimPass::Initialize(opt::IRContext* irContext) {
|
||||
InitializeProcessing(irContext);
|
||||
InitExtensionWhiteList();
|
||||
}
|
||||
@ -69,7 +69,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return LocalSingleStoreElim(fp);
|
||||
};
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
@ -78,7 +78,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
|
||||
|
||||
LocalSingleStoreElimPass::LocalSingleStoreElimPass() {}
|
||||
|
||||
Pass::Status LocalSingleStoreElimPass::Process(ir::IRContext* irContext) {
|
||||
Pass::Status LocalSingleStoreElimPass::Process(opt::IRContext* irContext) {
|
||||
Initialize(irContext);
|
||||
return ProcessImpl();
|
||||
}
|
||||
@ -120,11 +120,11 @@ void LocalSingleStoreElimPass::InitExtensionWhiteList() {
|
||||
"SPV_EXT_descriptor_indexing",
|
||||
});
|
||||
}
|
||||
bool LocalSingleStoreElimPass::ProcessVariable(ir::Instruction* var_inst) {
|
||||
vector<ir::Instruction*> users;
|
||||
bool LocalSingleStoreElimPass::ProcessVariable(opt::Instruction* var_inst) {
|
||||
vector<opt::Instruction*> users;
|
||||
FindUses(var_inst, &users);
|
||||
|
||||
ir::Instruction* store_inst = FindSingleStoreAndCheckUses(var_inst, users);
|
||||
opt::Instruction* store_inst = FindSingleStoreAndCheckUses(var_inst, users);
|
||||
|
||||
if (store_inst == nullptr) {
|
||||
return false;
|
||||
@ -133,17 +133,17 @@ bool LocalSingleStoreElimPass::ProcessVariable(ir::Instruction* var_inst) {
|
||||
return RewriteLoads(store_inst, users);
|
||||
}
|
||||
|
||||
ir::Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
|
||||
ir::Instruction* var_inst, const vector<ir::Instruction*>& users) const {
|
||||
opt::Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
|
||||
opt::Instruction* var_inst, const vector<opt::Instruction*>& users) const {
|
||||
// Make sure there is exactly 1 store.
|
||||
ir::Instruction* store_inst = nullptr;
|
||||
opt::Instruction* store_inst = nullptr;
|
||||
|
||||
// If |var_inst| has an initializer, then that will count as a store.
|
||||
if (var_inst->NumInOperands() > 1) {
|
||||
store_inst = var_inst;
|
||||
}
|
||||
|
||||
for (ir::Instruction* user : users) {
|
||||
for (opt::Instruction* user : users) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpStore:
|
||||
// Since we are in the relaxed addressing mode, the use has to be the
|
||||
@ -182,10 +182,10 @@ ir::Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
|
||||
}
|
||||
|
||||
void LocalSingleStoreElimPass::FindUses(
|
||||
const ir::Instruction* var_inst,
|
||||
std::vector<ir::Instruction*>* users) const {
|
||||
const opt::Instruction* var_inst,
|
||||
std::vector<opt::Instruction*>* users) const {
|
||||
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||
def_use_mgr->ForEachUser(var_inst, [users, this](ir::Instruction* user) {
|
||||
def_use_mgr->ForEachUser(var_inst, [users, this](opt::Instruction* user) {
|
||||
users->push_back(user);
|
||||
if (user->opcode() == SpvOpCopyObject) {
|
||||
FindUses(user, users);
|
||||
@ -193,9 +193,9 @@ void LocalSingleStoreElimPass::FindUses(
|
||||
});
|
||||
}
|
||||
|
||||
bool LocalSingleStoreElimPass::FeedsAStore(ir::Instruction* inst) const {
|
||||
bool LocalSingleStoreElimPass::FeedsAStore(opt::Instruction* inst) const {
|
||||
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||
return !def_use_mgr->WhileEachUser(inst, [this](ir::Instruction* user) {
|
||||
return !def_use_mgr->WhileEachUser(inst, [this](opt::Instruction* user) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpStore:
|
||||
return false;
|
||||
@ -216,8 +216,8 @@ bool LocalSingleStoreElimPass::FeedsAStore(ir::Instruction* inst) const {
|
||||
}
|
||||
|
||||
bool LocalSingleStoreElimPass::RewriteLoads(
|
||||
ir::Instruction* store_inst, const std::vector<ir::Instruction*>& uses) {
|
||||
ir::BasicBlock* store_block = context()->get_instr_block(store_inst);
|
||||
opt::Instruction* store_inst, const std::vector<opt::Instruction*>& uses) {
|
||||
opt::BasicBlock* store_block = context()->get_instr_block(store_inst);
|
||||
opt::DominatorAnalysis* dominator_analysis =
|
||||
context()->GetDominatorAnalysis(store_block->GetParent());
|
||||
|
||||
@ -227,9 +227,9 @@ bool LocalSingleStoreElimPass::RewriteLoads(
|
||||
else
|
||||
stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx);
|
||||
|
||||
std::vector<ir::Instruction*> uses_in_store_block;
|
||||
std::vector<opt::Instruction*> uses_in_store_block;
|
||||
bool modified = false;
|
||||
for (ir::Instruction* use : uses) {
|
||||
for (opt::Instruction* use : uses) {
|
||||
if (use->opcode() == SpvOpLoad) {
|
||||
if (dominator_analysis->Dominates(store_inst, use)) {
|
||||
modified = true;
|
||||
|
@ -34,16 +34,16 @@ namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class LocalSingleStoreElimPass : public Pass {
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
using cbb_ptr = const opt::BasicBlock*;
|
||||
|
||||
public:
|
||||
LocalSingleStoreElimPass();
|
||||
const char* name() const override { return "eliminate-local-single-store"; }
|
||||
Status Process(ir::IRContext* irContext) override;
|
||||
Status Process(opt::IRContext* irContext) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -51,7 +51,7 @@ class LocalSingleStoreElimPass : public Pass {
|
||||
// with a single non-access-chain store in |func|. Replace all their
|
||||
// non-access-chain loads with the value that is stored and eliminate
|
||||
// any resulting dead code.
|
||||
bool LocalSingleStoreElim(ir::Function* func);
|
||||
bool LocalSingleStoreElim(opt::Function* func);
|
||||
|
||||
// Initialize extensions whitelist
|
||||
void InitExtensionWhiteList();
|
||||
@ -59,37 +59,37 @@ class LocalSingleStoreElimPass : public Pass {
|
||||
// Return true if all extensions in this module are allowed by this pass.
|
||||
bool AllExtensionsSupported() const;
|
||||
|
||||
void Initialize(ir::IRContext* irContext);
|
||||
void Initialize(opt::IRContext* irContext);
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
// If there is a single store to |var_inst|, and it covers the entire
|
||||
// variable, then replace all of the loads of the entire variable that are
|
||||
// dominated by the store by the value that was stored. Returns true if the
|
||||
// module was changed.
|
||||
bool ProcessVariable(ir::Instruction* var_inst);
|
||||
bool ProcessVariable(opt::Instruction* var_inst);
|
||||
|
||||
// Collects all of the uses of |var_inst| into |uses|. This looks through
|
||||
// OpObjectCopy's that copy the address of the variable, and collects those
|
||||
// uses as well.
|
||||
void FindUses(const ir::Instruction* var_inst,
|
||||
std::vector<ir::Instruction*>* uses) const;
|
||||
void FindUses(const opt::Instruction* var_inst,
|
||||
std::vector<opt::Instruction*>* uses) const;
|
||||
|
||||
// Returns a store to |var_inst| if
|
||||
// - it is a store to the entire variable,
|
||||
// - and there are no other instructions that may modify |var_inst|.
|
||||
ir::Instruction* FindSingleStoreAndCheckUses(
|
||||
ir::Instruction* var_inst,
|
||||
const std::vector<ir::Instruction*>& users) const;
|
||||
opt::Instruction* FindSingleStoreAndCheckUses(
|
||||
opt::Instruction* var_inst,
|
||||
const std::vector<opt::Instruction*>& users) const;
|
||||
|
||||
// Returns true if the address that results from |inst| may be used as a base
|
||||
// address in a store instruction or may be used to compute the base address
|
||||
// of a store instruction.
|
||||
bool FeedsAStore(ir::Instruction* inst) const;
|
||||
bool FeedsAStore(opt::Instruction* inst) const;
|
||||
|
||||
// Replaces all of the loads in |uses| by the value stored in |store_inst|.
|
||||
// The load instructions are then killed.
|
||||
bool RewriteLoads(ir::Instruction* store_inst,
|
||||
const std::vector<ir::Instruction*>& uses);
|
||||
bool RewriteLoads(opt::Instruction* store_inst,
|
||||
const std::vector<opt::Instruction*>& uses);
|
||||
|
||||
// Extensions supported by this pass.
|
||||
std::unordered_set<std::string> extensions_whitelist_;
|
||||
|
@ -23,7 +23,7 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
void LocalMultiStoreElimPass::Initialize(ir::IRContext* c) {
|
||||
void LocalMultiStoreElimPass::Initialize(opt::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
|
||||
// Initialize extension whitelist
|
||||
@ -54,7 +54,7 @@ Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
ProcessFunction pfn = [this](opt::Function* fp) {
|
||||
return SSARewriter(this).RewriteFunctionIntoSSA(fp);
|
||||
};
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
@ -63,7 +63,7 @@ Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
|
||||
|
||||
LocalMultiStoreElimPass::LocalMultiStoreElimPass() {}
|
||||
|
||||
Pass::Status LocalMultiStoreElimPass::Process(ir::IRContext* c) {
|
||||
Pass::Status LocalMultiStoreElimPass::Process(opt::IRContext* c) {
|
||||
Initialize(c);
|
||||
return ProcessImpl();
|
||||
}
|
||||
|
@ -34,19 +34,19 @@ namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class LocalMultiStoreElimPass : public MemPass {
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
using cbb_ptr = const opt::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
std::function<std::vector<opt::BasicBlock*>*(const opt::BasicBlock*)>;
|
||||
|
||||
LocalMultiStoreElimPass();
|
||||
const char* name() const override { return "eliminate-local-multi-store"; }
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping;
|
||||
opt::IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return opt::IRContext::kAnalysisDefUse |
|
||||
opt::IRContext::kAnalysisInstrToBlockMapping;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -56,7 +56,7 @@ class LocalMultiStoreElimPass : public MemPass {
|
||||
// Return true if all extensions in this module are allowed by this pass.
|
||||
bool AllExtensionsSupported() const;
|
||||
|
||||
void Initialize(ir::IRContext* c);
|
||||
void Initialize(opt::IRContext* c);
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
// Extensions supported by this pass.
|
||||
|
@ -181,15 +181,15 @@ bool NormalizeAndCompareFractions(int64_t numerator_0, int64_t denominator_0,
|
||||
|
||||
} // namespace
|
||||
|
||||
bool LoopDependenceAnalysis::GetDependence(const ir::Instruction* source,
|
||||
const ir::Instruction* destination,
|
||||
bool LoopDependenceAnalysis::GetDependence(const opt::Instruction* source,
|
||||
const opt::Instruction* destination,
|
||||
DistanceVector* distance_vector) {
|
||||
// Start off by finding and marking all the loops in |loops_| that are
|
||||
// irrelevant to the dependence analysis.
|
||||
MarkUnsusedDistanceEntriesAsIrrelevant(source, destination, distance_vector);
|
||||
|
||||
ir::Instruction* source_access_chain = GetOperandDefinition(source, 0);
|
||||
ir::Instruction* destination_access_chain =
|
||||
opt::Instruction* source_access_chain = GetOperandDefinition(source, 0);
|
||||
opt::Instruction* destination_access_chain =
|
||||
GetOperandDefinition(destination, 0);
|
||||
|
||||
auto num_access_chains =
|
||||
@ -234,8 +234,8 @@ bool LoopDependenceAnalysis::GetDependence(const ir::Instruction* source,
|
||||
|
||||
// If the access chains aren't collecting from the same structure there is no
|
||||
// dependence.
|
||||
ir::Instruction* source_array = GetOperandDefinition(source_access_chain, 0);
|
||||
ir::Instruction* destination_array =
|
||||
opt::Instruction* source_array = GetOperandDefinition(source_access_chain, 0);
|
||||
opt::Instruction* destination_array =
|
||||
GetOperandDefinition(destination_access_chain, 0);
|
||||
|
||||
// Nested access chains are not supported yet, bail out.
|
||||
@ -254,8 +254,8 @@ bool LoopDependenceAnalysis::GetDependence(const ir::Instruction* source,
|
||||
|
||||
// To handle multiple subscripts we must get every operand in the access
|
||||
// chains past the first.
|
||||
std::vector<ir::Instruction*> source_subscripts = GetSubscripts(source);
|
||||
std::vector<ir::Instruction*> destination_subscripts =
|
||||
std::vector<opt::Instruction*> source_subscripts = GetSubscripts(source);
|
||||
std::vector<opt::Instruction*> destination_subscripts =
|
||||
GetSubscripts(destination);
|
||||
|
||||
auto sets_of_subscripts =
|
||||
@ -263,7 +263,7 @@ bool LoopDependenceAnalysis::GetDependence(const ir::Instruction* source,
|
||||
|
||||
auto first_coupled = std::partition(
|
||||
std::begin(sets_of_subscripts), std::end(sets_of_subscripts),
|
||||
[](const std::set<std::pair<ir::Instruction*, ir::Instruction*>>& set) {
|
||||
[](const std::set<std::pair<opt::Instruction*, opt::Instruction*>>& set) {
|
||||
return set.size() == 1;
|
||||
});
|
||||
|
||||
@ -284,7 +284,7 @@ bool LoopDependenceAnalysis::GetDependence(const ir::Instruction* source,
|
||||
// Check the loops are in a form we support.
|
||||
auto subscript_pair = std::make_pair(source_node, destination_node);
|
||||
|
||||
const ir::Loop* loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
const opt::Loop* loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
if (loop) {
|
||||
if (!IsSupportedLoop(loop)) {
|
||||
PrintDebug(
|
||||
@ -371,9 +371,9 @@ bool LoopDependenceAnalysis::GetDependence(const ir::Instruction* source,
|
||||
for (const auto& subscript : coupled_subscripts) {
|
||||
auto loops = CollectLoops(std::get<0>(subscript), std::get<1>(subscript));
|
||||
|
||||
auto is_subscript_supported =
|
||||
std::all_of(std::begin(loops), std::end(loops),
|
||||
[this](const ir::Loop* l) { return IsSupportedLoop(l); });
|
||||
auto is_subscript_supported = std::all_of(
|
||||
std::begin(loops), std::end(loops),
|
||||
[this](const opt::Loop* l) { return IsSupportedLoop(l); });
|
||||
|
||||
supported = supported && is_subscript_supported;
|
||||
}
|
||||
@ -541,7 +541,7 @@ bool LoopDependenceAnalysis::StrongSIVTest(SENode* source, SENode* destination,
|
||||
// Build an SENode for distance.
|
||||
std::pair<SENode*, SENode*> subscript_pair =
|
||||
std::make_pair(source, destination);
|
||||
const ir::Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
const opt::Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
SENode* source_constant_term =
|
||||
GetConstantTerm(subscript_loop, source->AsSERecurrentNode());
|
||||
SENode* destination_constant_term =
|
||||
@ -676,7 +676,7 @@ bool LoopDependenceAnalysis::SymbolicStrongSIVTest(
|
||||
// outwith the bounds.
|
||||
std::pair<SENode*, SENode*> subscript_pair =
|
||||
std::make_pair(source, destination);
|
||||
const ir::Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
const opt::Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
if (IsProvablyOutsideOfLoopBounds(subscript_loop, source_destination_delta,
|
||||
coefficient)) {
|
||||
PrintDebug(
|
||||
@ -701,7 +701,7 @@ bool LoopDependenceAnalysis::WeakZeroSourceSIVTest(
|
||||
PrintDebug("Performing WeakZeroSourceSIVTest.");
|
||||
std::pair<SENode*, SENode*> subscript_pair =
|
||||
std::make_pair(source, destination);
|
||||
const ir::Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
const opt::Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
// Build an SENode for distance.
|
||||
SENode* destination_constant_term =
|
||||
GetConstantTerm(subscript_loop, destination);
|
||||
@ -855,7 +855,7 @@ bool LoopDependenceAnalysis::WeakZeroDestinationSIVTest(
|
||||
// Build an SENode for distance.
|
||||
std::pair<SENode*, SENode*> subscript_pair =
|
||||
std::make_pair(source, destination);
|
||||
const ir::Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
const opt::Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
SENode* source_constant_term = GetConstantTerm(subscript_loop, source);
|
||||
SENode* delta = scalar_evolution_.SimplifyExpression(
|
||||
scalar_evolution_.CreateSubtraction(destination, source_constant_term));
|
||||
@ -1115,10 +1115,10 @@ bool LoopDependenceAnalysis::GCDMIVTest(
|
||||
}
|
||||
|
||||
using PartitionedSubscripts =
|
||||
std::vector<std::set<std::pair<ir::Instruction*, ir::Instruction*>>>;
|
||||
std::vector<std::set<std::pair<opt::Instruction*, opt::Instruction*>>>;
|
||||
PartitionedSubscripts LoopDependenceAnalysis::PartitionSubscripts(
|
||||
const std::vector<ir::Instruction*>& source_subscripts,
|
||||
const std::vector<ir::Instruction*>& destination_subscripts) {
|
||||
const std::vector<opt::Instruction*>& source_subscripts,
|
||||
const std::vector<opt::Instruction*>& destination_subscripts) {
|
||||
PartitionedSubscripts partitions{};
|
||||
|
||||
auto num_subscripts = source_subscripts.size();
|
||||
@ -1139,7 +1139,7 @@ PartitionedSubscripts LoopDependenceAnalysis::PartitionSubscripts(
|
||||
auto it = std::find_if(
|
||||
current_partition.begin(), current_partition.end(),
|
||||
[loop,
|
||||
this](const std::pair<ir::Instruction*, ir::Instruction*>& elem)
|
||||
this](const std::pair<opt::Instruction*, opt::Instruction*>& elem)
|
||||
-> bool {
|
||||
auto source_recurrences =
|
||||
scalar_evolution_.AnalyzeInstruction(std::get<0>(elem))
|
||||
@ -1177,7 +1177,7 @@ PartitionedSubscripts LoopDependenceAnalysis::PartitionSubscripts(
|
||||
partitions.erase(
|
||||
std::remove_if(
|
||||
partitions.begin(), partitions.end(),
|
||||
[](const std::set<std::pair<ir::Instruction*, ir::Instruction*>>&
|
||||
[](const std::set<std::pair<opt::Instruction*, opt::Instruction*>>&
|
||||
partition) { return partition.empty(); }),
|
||||
partitions.end());
|
||||
|
||||
|
@ -167,7 +167,7 @@ class DependenceEmpty;
|
||||
|
||||
class Constraint {
|
||||
public:
|
||||
explicit Constraint(const ir::Loop* loop) : loop_(loop) {}
|
||||
explicit Constraint(const opt::Loop* loop) : loop_(loop) {}
|
||||
enum ConstraintType { Line, Distance, Point, None, Empty };
|
||||
|
||||
virtual ConstraintType GetType() const = 0;
|
||||
@ -175,7 +175,7 @@ class Constraint {
|
||||
virtual ~Constraint() {}
|
||||
|
||||
// Get the loop this constraint belongs to.
|
||||
const ir::Loop* GetLoop() const { return loop_; }
|
||||
const opt::Loop* GetLoop() const { return loop_; }
|
||||
|
||||
bool operator==(const Constraint& other) const;
|
||||
|
||||
@ -192,12 +192,12 @@ class Constraint {
|
||||
#undef DeclareCastMethod
|
||||
|
||||
protected:
|
||||
const ir::Loop* loop_;
|
||||
const opt::Loop* loop_;
|
||||
};
|
||||
|
||||
class DependenceLine : public Constraint {
|
||||
public:
|
||||
DependenceLine(SENode* a, SENode* b, SENode* c, const ir::Loop* loop)
|
||||
DependenceLine(SENode* a, SENode* b, SENode* c, const opt::Loop* loop)
|
||||
: Constraint(loop), a_(a), b_(b), c_(c) {}
|
||||
|
||||
ConstraintType GetType() const final { return Line; }
|
||||
@ -217,7 +217,7 @@ class DependenceLine : public Constraint {
|
||||
|
||||
class DependenceDistance : public Constraint {
|
||||
public:
|
||||
DependenceDistance(SENode* distance, const ir::Loop* loop)
|
||||
DependenceDistance(SENode* distance, const opt::Loop* loop)
|
||||
: Constraint(loop), distance_(distance) {}
|
||||
|
||||
ConstraintType GetType() const final { return Distance; }
|
||||
@ -233,7 +233,7 @@ class DependenceDistance : public Constraint {
|
||||
|
||||
class DependencePoint : public Constraint {
|
||||
public:
|
||||
DependencePoint(SENode* source, SENode* destination, const ir::Loop* loop)
|
||||
DependencePoint(SENode* source, SENode* destination, const opt::Loop* loop)
|
||||
: Constraint(loop), source_(source), destination_(destination) {}
|
||||
|
||||
ConstraintType GetType() const final { return Point; }
|
||||
@ -294,8 +294,8 @@ class DependenceEmpty : public Constraint {
|
||||
// is present in the pair.
|
||||
class LoopDependenceAnalysis {
|
||||
public:
|
||||
LoopDependenceAnalysis(ir::IRContext* context,
|
||||
std::vector<const ir::Loop*> loops)
|
||||
LoopDependenceAnalysis(opt::IRContext* context,
|
||||
std::vector<const opt::Loop*> loops)
|
||||
: context_(context),
|
||||
loops_(loops),
|
||||
scalar_evolution_(context),
|
||||
@ -308,8 +308,8 @@ class LoopDependenceAnalysis {
|
||||
// Any direction and distance information found will be stored in
|
||||
// |distance_vector|.
|
||||
// Returns true if independence is found, false otherwise.
|
||||
bool GetDependence(const ir::Instruction* source,
|
||||
const ir::Instruction* destination,
|
||||
bool GetDependence(const opt::Instruction* source,
|
||||
const opt::Instruction* destination,
|
||||
DistanceVector* distance_vector);
|
||||
|
||||
// Returns true if |subscript_pair| represents a Zero Index Variable pair
|
||||
@ -326,11 +326,11 @@ class LoopDependenceAnalysis {
|
||||
|
||||
// Finds the lower bound of |loop| as an SENode* and returns the result.
|
||||
// The lower bound is the starting value of the loops induction variable
|
||||
SENode* GetLowerBound(const ir::Loop* loop);
|
||||
SENode* GetLowerBound(const opt::Loop* loop);
|
||||
|
||||
// Finds the upper bound of |loop| as an SENode* and returns the result.
|
||||
// The upper bound is the last value before the loop exit condition is met.
|
||||
SENode* GetUpperBound(const ir::Loop* loop);
|
||||
SENode* GetUpperBound(const opt::Loop* loop);
|
||||
|
||||
// Returns true if |value| is between |bound_one| and |bound_two| (inclusive).
|
||||
bool IsWithinBounds(int64_t value, int64_t bound_one, int64_t bound_two);
|
||||
@ -338,32 +338,32 @@ class LoopDependenceAnalysis {
|
||||
// Finds the bounds of |loop| as upper_bound - lower_bound and returns the
|
||||
// resulting SENode.
|
||||
// If the operations can not be completed a nullptr is returned.
|
||||
SENode* GetTripCount(const ir::Loop* loop);
|
||||
SENode* GetTripCount(const opt::Loop* loop);
|
||||
|
||||
// Returns the SENode* produced by building an SENode from the result of
|
||||
// calling GetInductionInitValue on |loop|.
|
||||
// If the operation can not be completed a nullptr is returned.
|
||||
SENode* GetFirstTripInductionNode(const ir::Loop* loop);
|
||||
SENode* GetFirstTripInductionNode(const opt::Loop* loop);
|
||||
|
||||
// Returns the SENode* produced by building an SENode from the result of
|
||||
// GetFirstTripInductionNode + (GetTripCount - 1) * induction_coefficient.
|
||||
// If the operation can not be completed a nullptr is returned.
|
||||
SENode* GetFinalTripInductionNode(const ir::Loop* loop,
|
||||
SENode* GetFinalTripInductionNode(const opt::Loop* loop,
|
||||
SENode* induction_coefficient);
|
||||
|
||||
// Returns all the distinct loops that appear in |nodes|.
|
||||
std::set<const ir::Loop*> CollectLoops(
|
||||
std::set<const opt::Loop*> CollectLoops(
|
||||
const std::vector<SERecurrentNode*>& nodes);
|
||||
|
||||
// Returns all the distinct loops that appear in |source| and |destination|.
|
||||
std::set<const ir::Loop*> CollectLoops(SENode* source, SENode* destination);
|
||||
std::set<const opt::Loop*> CollectLoops(SENode* source, SENode* destination);
|
||||
|
||||
// Returns true if |distance| is provably outside the loop bounds.
|
||||
// |coefficient| must be an SENode representing the coefficient of the
|
||||
// induction variable of |loop|.
|
||||
// This method is able to handle some symbolic cases which IsWithinBounds
|
||||
// can't handle.
|
||||
bool IsProvablyOutsideOfLoopBounds(const ir::Loop* loop, SENode* distance,
|
||||
bool IsProvablyOutsideOfLoopBounds(const opt::Loop* loop, SENode* distance,
|
||||
SENode* coefficient);
|
||||
|
||||
// Sets the ostream for debug information for the analysis.
|
||||
@ -393,14 +393,14 @@ class LoopDependenceAnalysis {
|
||||
// Returns the partitioning of subscript pairs. Sets of size 1 indicates an
|
||||
// independent subscript-pair and others indicate coupled sets.
|
||||
using PartitionedSubscripts =
|
||||
std::vector<std::set<std::pair<ir::Instruction*, ir::Instruction*>>>;
|
||||
std::vector<std::set<std::pair<opt::Instruction*, opt::Instruction*>>>;
|
||||
PartitionedSubscripts PartitionSubscripts(
|
||||
const std::vector<ir::Instruction*>& source_subscripts,
|
||||
const std::vector<ir::Instruction*>& destination_subscripts);
|
||||
const std::vector<opt::Instruction*>& source_subscripts,
|
||||
const std::vector<opt::Instruction*>& destination_subscripts);
|
||||
|
||||
// Returns the ir::Loop* matching the loop for |subscript_pair|.
|
||||
// Returns the opt::Loop* matching the loop for |subscript_pair|.
|
||||
// |subscript_pair| must be an SIV pair.
|
||||
const ir::Loop* GetLoopForSubscriptPair(
|
||||
const opt::Loop* GetLoopForSubscriptPair(
|
||||
const std::pair<SENode*, SENode*>& subscript_pair);
|
||||
|
||||
// Returns the DistanceEntry matching the loop for |subscript_pair|.
|
||||
@ -410,13 +410,13 @@ class LoopDependenceAnalysis {
|
||||
DistanceVector* distance_vector);
|
||||
|
||||
// Returns the DistanceEntry matching |loop|.
|
||||
DistanceEntry* GetDistanceEntryForLoop(const ir::Loop* loop,
|
||||
DistanceEntry* GetDistanceEntryForLoop(const opt::Loop* loop,
|
||||
DistanceVector* distance_vector);
|
||||
|
||||
// Returns a vector of Instruction* which form the subscripts of the array
|
||||
// access defined by the access chain |instruction|.
|
||||
std::vector<ir::Instruction*> GetSubscripts(
|
||||
const ir::Instruction* instruction);
|
||||
std::vector<opt::Instruction*> GetSubscripts(
|
||||
const opt::Instruction* instruction);
|
||||
|
||||
// Delta test as described in Figure 3 of 'Practical Dependence
|
||||
// Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91.
|
||||
@ -441,18 +441,18 @@ class LoopDependenceAnalysis {
|
||||
// analysis.
|
||||
// A loop is supported if it has a single induction variable and that
|
||||
// induction variable has a step of +1 or -1 per loop iteration.
|
||||
bool CheckSupportedLoops(std::vector<const ir::Loop*> loops);
|
||||
bool CheckSupportedLoops(std::vector<const opt::Loop*> loops);
|
||||
|
||||
// Returns true if |loop| is in a form supported by this analysis.
|
||||
// A loop is supported if it has a single induction variable and that
|
||||
// induction variable has a step of +1 or -1 per loop iteration.
|
||||
bool IsSupportedLoop(const ir::Loop* loop);
|
||||
bool IsSupportedLoop(const opt::Loop* loop);
|
||||
|
||||
private:
|
||||
ir::IRContext* context_;
|
||||
opt::IRContext* context_;
|
||||
|
||||
// The loop nest we are analysing the dependence of.
|
||||
std::vector<const ir::Loop*> loops_;
|
||||
std::vector<const opt::Loop*> loops_;
|
||||
|
||||
// The ScalarEvolutionAnalysis used by this analysis to store and perform much
|
||||
// of its logic.
|
||||
@ -514,8 +514,8 @@ class LoopDependenceAnalysis {
|
||||
|
||||
// Uses the def_use_mgr to get the instruction referenced by
|
||||
// SingleWordInOperand(|id|) when called on |instruction|.
|
||||
ir::Instruction* GetOperandDefinition(const ir::Instruction* instruction,
|
||||
int id);
|
||||
opt::Instruction* GetOperandDefinition(const opt::Instruction* instruction,
|
||||
int id);
|
||||
|
||||
// Perform the GCD test if both, the source and the destination nodes, are in
|
||||
// the form a0*i0 + a1*i1 + ... an*in + c.
|
||||
@ -533,13 +533,13 @@ class LoopDependenceAnalysis {
|
||||
// Takes the offset from the induction variable and subtracts the lower bound
|
||||
// from it to get the constant term added to the induction.
|
||||
// Returns the resuting constant term, or nullptr if it could not be produced.
|
||||
SENode* GetConstantTerm(const ir::Loop* loop, SERecurrentNode* induction);
|
||||
SENode* GetConstantTerm(const opt::Loop* loop, SERecurrentNode* induction);
|
||||
|
||||
// Marks all the distance entries in |distance_vector| that were relate to
|
||||
// loops in |loops_| but were not used in any subscripts as irrelevant to the
|
||||
// to the dependence test.
|
||||
void MarkUnsusedDistanceEntriesAsIrrelevant(
|
||||
const ir::Instruction* source, const ir::Instruction* destination,
|
||||
const opt::Instruction* source, const opt::Instruction* destination,
|
||||
DistanceVector* distance_vector);
|
||||
|
||||
// Converts |value| to a std::string and returns the result.
|
||||
|
@ -47,12 +47,12 @@ bool LoopDependenceAnalysis::IsMIV(
|
||||
1;
|
||||
}
|
||||
|
||||
SENode* LoopDependenceAnalysis::GetLowerBound(const ir::Loop* loop) {
|
||||
ir::Instruction* cond_inst = loop->GetConditionInst();
|
||||
SENode* LoopDependenceAnalysis::GetLowerBound(const opt::Loop* loop) {
|
||||
opt::Instruction* cond_inst = loop->GetConditionInst();
|
||||
if (!cond_inst) {
|
||||
return nullptr;
|
||||
}
|
||||
ir::Instruction* lower_inst = GetOperandDefinition(cond_inst, 0);
|
||||
opt::Instruction* lower_inst = GetOperandDefinition(cond_inst, 0);
|
||||
switch (cond_inst->opcode()) {
|
||||
case SpvOpULessThan:
|
||||
case SpvOpSLessThan:
|
||||
@ -79,12 +79,12 @@ SENode* LoopDependenceAnalysis::GetLowerBound(const ir::Loop* loop) {
|
||||
}
|
||||
}
|
||||
|
||||
SENode* LoopDependenceAnalysis::GetUpperBound(const ir::Loop* loop) {
|
||||
ir::Instruction* cond_inst = loop->GetConditionInst();
|
||||
SENode* LoopDependenceAnalysis::GetUpperBound(const opt::Loop* loop) {
|
||||
opt::Instruction* cond_inst = loop->GetConditionInst();
|
||||
if (!cond_inst) {
|
||||
return nullptr;
|
||||
}
|
||||
ir::Instruction* upper_inst = GetOperandDefinition(cond_inst, 1);
|
||||
opt::Instruction* upper_inst = GetOperandDefinition(cond_inst, 1);
|
||||
switch (cond_inst->opcode()) {
|
||||
case SpvOpULessThan:
|
||||
case SpvOpSLessThan: {
|
||||
@ -135,7 +135,7 @@ bool LoopDependenceAnalysis::IsWithinBounds(int64_t value, int64_t bound_one,
|
||||
}
|
||||
|
||||
bool LoopDependenceAnalysis::IsProvablyOutsideOfLoopBounds(
|
||||
const ir::Loop* loop, SENode* distance, SENode* coefficient) {
|
||||
const opt::Loop* loop, SENode* distance, SENode* coefficient) {
|
||||
// We test to see if we can reduce the coefficient to an integral constant.
|
||||
SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode();
|
||||
if (!coefficient_constant) {
|
||||
@ -196,7 +196,7 @@ bool LoopDependenceAnalysis::IsProvablyOutsideOfLoopBounds(
|
||||
return false;
|
||||
}
|
||||
|
||||
const ir::Loop* LoopDependenceAnalysis::GetLoopForSubscriptPair(
|
||||
const opt::Loop* LoopDependenceAnalysis::GetLoopForSubscriptPair(
|
||||
const std::pair<SENode*, SENode*>& subscript_pair) {
|
||||
// Collect all the SERecurrentNodes.
|
||||
std::vector<SERecurrentNode*> source_nodes =
|
||||
@ -205,7 +205,7 @@ const ir::Loop* LoopDependenceAnalysis::GetLoopForSubscriptPair(
|
||||
std::get<1>(subscript_pair)->CollectRecurrentNodes();
|
||||
|
||||
// Collect all the loops stored by the SERecurrentNodes.
|
||||
std::unordered_set<const ir::Loop*> loops{};
|
||||
std::unordered_set<const opt::Loop*> loops{};
|
||||
for (auto source_nodes_it = source_nodes.begin();
|
||||
source_nodes_it != source_nodes.end(); ++source_nodes_it) {
|
||||
loops.insert((*source_nodes_it)->GetLoop());
|
||||
@ -226,7 +226,7 @@ const ir::Loop* LoopDependenceAnalysis::GetLoopForSubscriptPair(
|
||||
}
|
||||
|
||||
DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForLoop(
|
||||
const ir::Loop* loop, DistanceVector* distance_vector) {
|
||||
const opt::Loop* loop, DistanceVector* distance_vector) {
|
||||
if (!loop) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -245,22 +245,22 @@ DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForLoop(
|
||||
DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForSubscriptPair(
|
||||
const std::pair<SENode*, SENode*>& subscript_pair,
|
||||
DistanceVector* distance_vector) {
|
||||
const ir::Loop* loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
const opt::Loop* loop = GetLoopForSubscriptPair(subscript_pair);
|
||||
|
||||
return GetDistanceEntryForLoop(loop, distance_vector);
|
||||
}
|
||||
|
||||
SENode* LoopDependenceAnalysis::GetTripCount(const ir::Loop* loop) {
|
||||
ir::BasicBlock* condition_block = loop->FindConditionBlock();
|
||||
SENode* LoopDependenceAnalysis::GetTripCount(const opt::Loop* loop) {
|
||||
opt::BasicBlock* condition_block = loop->FindConditionBlock();
|
||||
if (!condition_block) {
|
||||
return nullptr;
|
||||
}
|
||||
ir::Instruction* induction_instr =
|
||||
opt::Instruction* induction_instr =
|
||||
loop->FindConditionVariable(condition_block);
|
||||
if (!induction_instr) {
|
||||
return nullptr;
|
||||
}
|
||||
ir::Instruction* cond_instr = loop->GetConditionInst();
|
||||
opt::Instruction* cond_instr = loop->GetConditionInst();
|
||||
if (!cond_instr) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -281,12 +281,12 @@ SENode* LoopDependenceAnalysis::GetTripCount(const ir::Loop* loop) {
|
||||
}
|
||||
|
||||
SENode* LoopDependenceAnalysis::GetFirstTripInductionNode(
|
||||
const ir::Loop* loop) {
|
||||
ir::BasicBlock* condition_block = loop->FindConditionBlock();
|
||||
const opt::Loop* loop) {
|
||||
opt::BasicBlock* condition_block = loop->FindConditionBlock();
|
||||
if (!condition_block) {
|
||||
return nullptr;
|
||||
}
|
||||
ir::Instruction* induction_instr =
|
||||
opt::Instruction* induction_instr =
|
||||
loop->FindConditionVariable(condition_block);
|
||||
if (!induction_instr) {
|
||||
return nullptr;
|
||||
@ -302,7 +302,7 @@ SENode* LoopDependenceAnalysis::GetFirstTripInductionNode(
|
||||
}
|
||||
|
||||
SENode* LoopDependenceAnalysis::GetFinalTripInductionNode(
|
||||
const ir::Loop* loop, SENode* induction_coefficient) {
|
||||
const opt::Loop* loop, SENode* induction_coefficient) {
|
||||
SENode* first_trip_induction_node = GetFirstTripInductionNode(loop);
|
||||
if (!first_trip_induction_node) {
|
||||
return nullptr;
|
||||
@ -319,12 +319,12 @@ SENode* LoopDependenceAnalysis::GetFinalTripInductionNode(
|
||||
scalar_evolution_.CreateMultiplyNode(trip_count, induction_coefficient)));
|
||||
}
|
||||
|
||||
std::set<const ir::Loop*> LoopDependenceAnalysis::CollectLoops(
|
||||
std::set<const opt::Loop*> LoopDependenceAnalysis::CollectLoops(
|
||||
const std::vector<SERecurrentNode*>& recurrent_nodes) {
|
||||
// We don't handle loops with more than one induction variable. Therefore we
|
||||
// can identify the number of induction variables by collecting all of the
|
||||
// loops the collected recurrent nodes belong to.
|
||||
std::set<const ir::Loop*> loops{};
|
||||
std::set<const opt::Loop*> loops{};
|
||||
for (auto recurrent_nodes_it = recurrent_nodes.begin();
|
||||
recurrent_nodes_it != recurrent_nodes.end(); ++recurrent_nodes_it) {
|
||||
loops.insert((*recurrent_nodes_it)->GetLoop());
|
||||
@ -343,23 +343,24 @@ int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* node) {
|
||||
// We don't handle loops with more than one induction variable. Therefore we
|
||||
// can identify the number of induction variables by collecting all of the
|
||||
// loops the collected recurrent nodes belong to.
|
||||
std::set<const ir::Loop*> loops = CollectLoops(recurrent_nodes);
|
||||
std::set<const opt::Loop*> loops = CollectLoops(recurrent_nodes);
|
||||
|
||||
return static_cast<int64_t>(loops.size());
|
||||
}
|
||||
|
||||
std::set<const ir::Loop*> LoopDependenceAnalysis::CollectLoops(
|
||||
std::set<const opt::Loop*> LoopDependenceAnalysis::CollectLoops(
|
||||
SENode* source, SENode* destination) {
|
||||
if (!source || !destination) {
|
||||
return std::set<const ir::Loop*>{};
|
||||
return std::set<const opt::Loop*>{};
|
||||
}
|
||||
|
||||
std::vector<SERecurrentNode*> source_nodes = source->CollectRecurrentNodes();
|
||||
std::vector<SERecurrentNode*> destination_nodes =
|
||||
destination->CollectRecurrentNodes();
|
||||
|
||||
std::set<const ir::Loop*> loops = CollectLoops(source_nodes);
|
||||
std::set<const ir::Loop*> destination_loops = CollectLoops(destination_nodes);
|
||||
std::set<const opt::Loop*> loops = CollectLoops(source_nodes);
|
||||
std::set<const opt::Loop*> destination_loops =
|
||||
CollectLoops(destination_nodes);
|
||||
|
||||
loops.insert(std::begin(destination_loops), std::end(destination_loops));
|
||||
|
||||
@ -372,22 +373,22 @@ int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* source,
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::set<const ir::Loop*> loops = CollectLoops(source, destination);
|
||||
std::set<const opt::Loop*> loops = CollectLoops(source, destination);
|
||||
|
||||
return static_cast<int64_t>(loops.size());
|
||||
}
|
||||
|
||||
ir::Instruction* LoopDependenceAnalysis::GetOperandDefinition(
|
||||
const ir::Instruction* instruction, int id) {
|
||||
opt::Instruction* LoopDependenceAnalysis::GetOperandDefinition(
|
||||
const opt::Instruction* instruction, int id) {
|
||||
return context_->get_def_use_mgr()->GetDef(
|
||||
instruction->GetSingleWordInOperand(id));
|
||||
}
|
||||
|
||||
std::vector<ir::Instruction*> LoopDependenceAnalysis::GetSubscripts(
|
||||
const ir::Instruction* instruction) {
|
||||
ir::Instruction* access_chain = GetOperandDefinition(instruction, 0);
|
||||
std::vector<opt::Instruction*> LoopDependenceAnalysis::GetSubscripts(
|
||||
const opt::Instruction* instruction) {
|
||||
opt::Instruction* access_chain = GetOperandDefinition(instruction, 0);
|
||||
|
||||
std::vector<ir::Instruction*> subscripts;
|
||||
std::vector<opt::Instruction*> subscripts;
|
||||
|
||||
for (auto i = 1u; i < access_chain->NumInOperandWords(); ++i) {
|
||||
subscripts.push_back(GetOperandDefinition(access_chain, i));
|
||||
@ -396,7 +397,7 @@ std::vector<ir::Instruction*> LoopDependenceAnalysis::GetSubscripts(
|
||||
return subscripts;
|
||||
}
|
||||
|
||||
SENode* LoopDependenceAnalysis::GetConstantTerm(const ir::Loop* loop,
|
||||
SENode* LoopDependenceAnalysis::GetConstantTerm(const opt::Loop* loop,
|
||||
SERecurrentNode* induction) {
|
||||
SENode* offset = induction->GetOffset();
|
||||
SENode* lower_bound = GetLowerBound(loop);
|
||||
@ -409,7 +410,7 @@ SENode* LoopDependenceAnalysis::GetConstantTerm(const ir::Loop* loop,
|
||||
}
|
||||
|
||||
bool LoopDependenceAnalysis::CheckSupportedLoops(
|
||||
std::vector<const ir::Loop*> loops) {
|
||||
std::vector<const opt::Loop*> loops) {
|
||||
for (auto loop : loops) {
|
||||
if (!IsSupportedLoop(loop)) {
|
||||
return false;
|
||||
@ -419,15 +420,15 @@ bool LoopDependenceAnalysis::CheckSupportedLoops(
|
||||
}
|
||||
|
||||
void LoopDependenceAnalysis::MarkUnsusedDistanceEntriesAsIrrelevant(
|
||||
const ir::Instruction* source, const ir::Instruction* destination,
|
||||
const opt::Instruction* source, const opt::Instruction* destination,
|
||||
DistanceVector* distance_vector) {
|
||||
std::vector<ir::Instruction*> source_subscripts = GetSubscripts(source);
|
||||
std::vector<ir::Instruction*> destination_subscripts =
|
||||
std::vector<opt::Instruction*> source_subscripts = GetSubscripts(source);
|
||||
std::vector<opt::Instruction*> destination_subscripts =
|
||||
GetSubscripts(destination);
|
||||
|
||||
std::set<const ir::Loop*> used_loops{};
|
||||
std::set<const opt::Loop*> used_loops{};
|
||||
|
||||
for (ir::Instruction* source_inst : source_subscripts) {
|
||||
for (opt::Instruction* source_inst : source_subscripts) {
|
||||
SENode* source_node = scalar_evolution_.SimplifyExpression(
|
||||
scalar_evolution_.AnalyzeInstruction(source_inst));
|
||||
std::vector<SERecurrentNode*> recurrent_nodes =
|
||||
@ -437,7 +438,7 @@ void LoopDependenceAnalysis::MarkUnsusedDistanceEntriesAsIrrelevant(
|
||||
}
|
||||
}
|
||||
|
||||
for (ir::Instruction* destination_inst : destination_subscripts) {
|
||||
for (opt::Instruction* destination_inst : destination_subscripts) {
|
||||
SENode* destination_node = scalar_evolution_.SimplifyExpression(
|
||||
scalar_evolution_.AnalyzeInstruction(destination_inst));
|
||||
std::vector<SERecurrentNode*> recurrent_nodes =
|
||||
@ -455,13 +456,13 @@ void LoopDependenceAnalysis::MarkUnsusedDistanceEntriesAsIrrelevant(
|
||||
}
|
||||
}
|
||||
|
||||
bool LoopDependenceAnalysis::IsSupportedLoop(const ir::Loop* loop) {
|
||||
std::vector<ir::Instruction*> inductions{};
|
||||
bool LoopDependenceAnalysis::IsSupportedLoop(const opt::Loop* loop) {
|
||||
std::vector<opt::Instruction*> inductions{};
|
||||
loop->GetInductionVariables(inductions);
|
||||
if (inductions.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
ir::Instruction* induction = inductions[0];
|
||||
opt::Instruction* induction = inductions[0];
|
||||
SENode* induction_node = scalar_evolution_.SimplifyExpression(
|
||||
scalar_evolution_.AnalyzeInstruction(induction));
|
||||
if (!induction_node->AsSERecurrentNode()) {
|
||||
|
@ -29,16 +29,16 @@
|
||||
#include "opt/tree_iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
namespace opt {
|
||||
|
||||
// Takes in a phi instruction |induction| and the loop |header| and returns the
|
||||
// step operation of the loop.
|
||||
ir::Instruction* Loop::GetInductionStepOperation(
|
||||
const ir::Instruction* induction) const {
|
||||
opt::Instruction* Loop::GetInductionStepOperation(
|
||||
const opt::Instruction* induction) const {
|
||||
// Induction must be a phi instruction.
|
||||
assert(induction->opcode() == SpvOpPhi);
|
||||
|
||||
ir::Instruction* step = nullptr;
|
||||
opt::Instruction* step = nullptr;
|
||||
|
||||
opt::analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr();
|
||||
|
||||
@ -46,7 +46,7 @@ ir::Instruction* Loop::GetInductionStepOperation(
|
||||
for (uint32_t operand_id = 1; operand_id < induction->NumInOperands();
|
||||
operand_id += 2) {
|
||||
// Incoming edge.
|
||||
ir::BasicBlock* incoming_block =
|
||||
opt::BasicBlock* incoming_block =
|
||||
context_->cfg()->block(induction->GetSingleWordInOperand(operand_id));
|
||||
|
||||
// Check if the block is dominated by header, and thus coming from within
|
||||
@ -142,17 +142,17 @@ int64_t Loop::GetResidualConditionValue(SpvOp condition, int64_t initial_value,
|
||||
return remainder;
|
||||
}
|
||||
|
||||
ir::Instruction* Loop::GetConditionInst() const {
|
||||
ir::BasicBlock* condition_block = FindConditionBlock();
|
||||
opt::Instruction* Loop::GetConditionInst() const {
|
||||
opt::BasicBlock* condition_block = FindConditionBlock();
|
||||
if (!condition_block) {
|
||||
return nullptr;
|
||||
}
|
||||
ir::Instruction* branch_conditional = &*condition_block->tail();
|
||||
opt::Instruction* branch_conditional = &*condition_block->tail();
|
||||
if (!branch_conditional ||
|
||||
branch_conditional->opcode() != SpvOpBranchConditional) {
|
||||
return nullptr;
|
||||
}
|
||||
ir::Instruction* condition_inst = context_->get_def_use_mgr()->GetDef(
|
||||
opt::Instruction* condition_inst = context_->get_def_use_mgr()->GetDef(
|
||||
branch_conditional->GetSingleWordInOperand(0));
|
||||
if (IsSupportedCondition(condition_inst->opcode())) {
|
||||
return condition_inst;
|
||||
@ -164,14 +164,14 @@ ir::Instruction* Loop::GetConditionInst() const {
|
||||
// Extract the initial value from the |induction| OpPhi instruction and store it
|
||||
// in |value|. If the function couldn't find the initial value of |induction|
|
||||
// return false.
|
||||
bool Loop::GetInductionInitValue(const ir::Instruction* induction,
|
||||
bool Loop::GetInductionInitValue(const opt::Instruction* induction,
|
||||
int64_t* value) const {
|
||||
ir::Instruction* constant_instruction = nullptr;
|
||||
opt::Instruction* constant_instruction = nullptr;
|
||||
opt::analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr();
|
||||
|
||||
for (uint32_t operand_id = 0; operand_id < induction->NumInOperands();
|
||||
operand_id += 2) {
|
||||
ir::BasicBlock* bb = context_->cfg()->block(
|
||||
opt::BasicBlock* bb = context_->cfg()->block(
|
||||
induction->GetSingleWordInOperand(operand_id + 1));
|
||||
|
||||
if (!IsInsideLoop(bb)) {
|
||||
@ -327,8 +327,8 @@ void Loop::SetPreHeaderBlock(BasicBlock* preheader) {
|
||||
loop_preheader_ = preheader;
|
||||
}
|
||||
|
||||
ir::BasicBlock* Loop::FindLatchBlock() {
|
||||
ir::CFG* cfg = context_->cfg();
|
||||
opt::BasicBlock* Loop::FindLatchBlock() {
|
||||
opt::CFG* cfg = context_->cfg();
|
||||
|
||||
opt::DominatorAnalysis* dominator_analysis =
|
||||
context_->GetDominatorAnalysis(loop_header_->GetParent());
|
||||
@ -350,7 +350,7 @@ ir::BasicBlock* Loop::FindLatchBlock() {
|
||||
}
|
||||
|
||||
void Loop::GetExitBlocks(std::unordered_set<uint32_t>* exit_blocks) const {
|
||||
ir::CFG* cfg = context_->cfg();
|
||||
opt::CFG* cfg = context_->cfg();
|
||||
exit_blocks->clear();
|
||||
|
||||
for (uint32_t bb_id : GetBlocks()) {
|
||||
@ -366,13 +366,13 @@ void Loop::GetExitBlocks(std::unordered_set<uint32_t>* exit_blocks) const {
|
||||
void Loop::GetMergingBlocks(
|
||||
std::unordered_set<uint32_t>* merging_blocks) const {
|
||||
assert(GetMergeBlock() && "This loop is not structured");
|
||||
ir::CFG* cfg = context_->cfg();
|
||||
opt::CFG* cfg = context_->cfg();
|
||||
merging_blocks->clear();
|
||||
|
||||
std::stack<const ir::BasicBlock*> to_visit;
|
||||
std::stack<const opt::BasicBlock*> to_visit;
|
||||
to_visit.push(GetMergeBlock());
|
||||
while (!to_visit.empty()) {
|
||||
const ir::BasicBlock* bb = to_visit.top();
|
||||
const opt::BasicBlock* bb = to_visit.top();
|
||||
to_visit.pop();
|
||||
merging_blocks->insert(bb->id());
|
||||
for (uint32_t pred_id : cfg->preds(bb->id())) {
|
||||
@ -386,7 +386,7 @@ void Loop::GetMergingBlocks(
|
||||
namespace {
|
||||
|
||||
static inline bool IsBasicBlockSafeToClone(IRContext* context, BasicBlock* bb) {
|
||||
for (ir::Instruction& inst : *bb) {
|
||||
for (opt::Instruction& inst : *bb) {
|
||||
if (!inst.IsBranch() && !context->IsCombinatorInstruction(&inst))
|
||||
return false;
|
||||
}
|
||||
@ -397,7 +397,7 @@ static inline bool IsBasicBlockSafeToClone(IRContext* context, BasicBlock* bb) {
|
||||
} // namespace
|
||||
|
||||
bool Loop::IsSafeToClone() const {
|
||||
ir::CFG& cfg = *context_->cfg();
|
||||
opt::CFG& cfg = *context_->cfg();
|
||||
|
||||
for (uint32_t bb_id : GetBlocks()) {
|
||||
BasicBlock* bb = cfg.block(bb_id);
|
||||
@ -421,14 +421,14 @@ bool Loop::IsSafeToClone() const {
|
||||
}
|
||||
|
||||
bool Loop::IsLCSSA() const {
|
||||
ir::CFG* cfg = context_->cfg();
|
||||
opt::CFG* cfg = context_->cfg();
|
||||
opt::analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
|
||||
|
||||
std::unordered_set<uint32_t> exit_blocks;
|
||||
GetExitBlocks(&exit_blocks);
|
||||
|
||||
// Declare ir_context so we can capture context_ in the below lambda
|
||||
ir::IRContext* ir_context = context_;
|
||||
opt::IRContext* ir_context = context_;
|
||||
|
||||
for (uint32_t bb_id : GetBlocks()) {
|
||||
for (Instruction& insn : *cfg->block(bb_id)) {
|
||||
@ -437,7 +437,7 @@ bool Loop::IsLCSSA() const {
|
||||
// - In an exit block and in a phi instruction.
|
||||
if (!def_use_mgr->WhileEachUser(
|
||||
&insn,
|
||||
[&exit_blocks, ir_context, this](ir::Instruction* use) -> bool {
|
||||
[&exit_blocks, ir_context, this](opt::Instruction* use) -> bool {
|
||||
BasicBlock* parent = ir_context->get_instr_block(use);
|
||||
assert(parent && "Invalid analysis");
|
||||
if (IsInsideLoop(parent)) return true;
|
||||
@ -472,9 +472,9 @@ bool Loop::AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst) {
|
||||
}
|
||||
|
||||
void Loop::ComputeLoopStructuredOrder(
|
||||
std::vector<ir::BasicBlock*>* ordered_loop_blocks, bool include_pre_header,
|
||||
std::vector<opt::BasicBlock*>* ordered_loop_blocks, bool include_pre_header,
|
||||
bool include_merge) const {
|
||||
ir::CFG& cfg = *context_->cfg();
|
||||
opt::CFG& cfg = *context_->cfg();
|
||||
|
||||
// Reserve the memory: all blocks in the loop + extra if needed.
|
||||
ordered_loop_blocks->reserve(GetBlocks().size() + include_pre_header +
|
||||
@ -508,7 +508,7 @@ void LoopDescriptor::PopulateList(const Function* f) {
|
||||
// instructions.
|
||||
opt::DominatorTree& dom_tree = dom_analysis->GetDomTree();
|
||||
for (opt::DominatorTreeNode& node :
|
||||
ir::make_range(dom_tree.post_begin(), dom_tree.post_end())) {
|
||||
opt::make_range(dom_tree.post_begin(), dom_tree.post_end())) {
|
||||
Instruction* merge_inst = node.bb_->GetLoopMergeInst();
|
||||
if (merge_inst) {
|
||||
bool all_backedge_unreachable = true;
|
||||
@ -578,14 +578,14 @@ void LoopDescriptor::PopulateList(const Function* f) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ir::Loop*> LoopDescriptor::GetLoopsInBinaryLayoutOrder() {
|
||||
std::vector<opt::Loop*> LoopDescriptor::GetLoopsInBinaryLayoutOrder() {
|
||||
std::vector<uint32_t> ids{};
|
||||
|
||||
for (size_t i = 0; i < NumLoops(); ++i) {
|
||||
ids.push_back(GetLoopByIndex(i).GetHeaderBlock()->id());
|
||||
}
|
||||
|
||||
std::vector<ir::Loop*> loops{};
|
||||
std::vector<opt::Loop*> loops{};
|
||||
if (!ids.empty()) {
|
||||
auto function = GetLoopByIndex(0).GetHeaderBlock()->GetParent();
|
||||
for (const auto& block : *function) {
|
||||
@ -601,11 +601,11 @@ std::vector<ir::Loop*> LoopDescriptor::GetLoopsInBinaryLayoutOrder() {
|
||||
return loops;
|
||||
}
|
||||
|
||||
ir::BasicBlock* Loop::FindConditionBlock() const {
|
||||
opt::BasicBlock* Loop::FindConditionBlock() const {
|
||||
if (!loop_merge_) {
|
||||
return nullptr;
|
||||
}
|
||||
ir::BasicBlock* condition_block = nullptr;
|
||||
opt::BasicBlock* condition_block = nullptr;
|
||||
|
||||
uint32_t in_loop_pred = 0;
|
||||
for (uint32_t p : context_->cfg()->preds(loop_merge_->id())) {
|
||||
@ -622,11 +622,11 @@ ir::BasicBlock* Loop::FindConditionBlock() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ir::BasicBlock* bb = context_->cfg()->block(in_loop_pred);
|
||||
opt::BasicBlock* bb = context_->cfg()->block(in_loop_pred);
|
||||
|
||||
if (!bb) return nullptr;
|
||||
|
||||
const ir::Instruction& branch = *bb->ctail();
|
||||
const opt::Instruction& branch = *bb->ctail();
|
||||
|
||||
// Make sure the branch is a conditional branch.
|
||||
if (branch.opcode() != SpvOpBranchConditional) return nullptr;
|
||||
@ -640,8 +640,8 @@ ir::BasicBlock* Loop::FindConditionBlock() const {
|
||||
return condition_block;
|
||||
}
|
||||
|
||||
bool Loop::FindNumberOfIterations(const ir::Instruction* induction,
|
||||
const ir::Instruction* branch_inst,
|
||||
bool Loop::FindNumberOfIterations(const opt::Instruction* induction,
|
||||
const opt::Instruction* branch_inst,
|
||||
size_t* iterations_out,
|
||||
int64_t* step_value_out,
|
||||
int64_t* init_value_out) const {
|
||||
@ -649,7 +649,7 @@ bool Loop::FindNumberOfIterations(const ir::Instruction* induction,
|
||||
opt::analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr();
|
||||
|
||||
// Condition instruction from the OpConditionalBranch.
|
||||
ir::Instruction* condition =
|
||||
opt::Instruction* condition =
|
||||
def_use_manager->GetDef(branch_inst->GetSingleWordOperand(0));
|
||||
|
||||
assert(IsSupportedCondition(condition->opcode()));
|
||||
@ -680,7 +680,7 @@ bool Loop::FindNumberOfIterations(const ir::Instruction* induction,
|
||||
}
|
||||
|
||||
// Find the instruction which is stepping through the loop.
|
||||
ir::Instruction* step_inst = GetInductionStepOperation(induction);
|
||||
opt::Instruction* step_inst = GetInductionStepOperation(induction);
|
||||
if (!step_inst) return false;
|
||||
|
||||
// Find the constant value used by the condition variable.
|
||||
@ -833,20 +833,20 @@ int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value,
|
||||
|
||||
// Returns the list of induction variables within the loop.
|
||||
void Loop::GetInductionVariables(
|
||||
std::vector<ir::Instruction*>& induction_variables) const {
|
||||
for (ir::Instruction& inst : *loop_header_) {
|
||||
std::vector<opt::Instruction*>& induction_variables) const {
|
||||
for (opt::Instruction& inst : *loop_header_) {
|
||||
if (inst.opcode() == SpvOp::SpvOpPhi) {
|
||||
induction_variables.push_back(&inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ir::Instruction* Loop::FindConditionVariable(
|
||||
const ir::BasicBlock* condition_block) const {
|
||||
opt::Instruction* Loop::FindConditionVariable(
|
||||
const opt::BasicBlock* condition_block) const {
|
||||
// Find the branch instruction.
|
||||
const ir::Instruction& branch_inst = *condition_block->ctail();
|
||||
const opt::Instruction& branch_inst = *condition_block->ctail();
|
||||
|
||||
ir::Instruction* induction = nullptr;
|
||||
opt::Instruction* induction = nullptr;
|
||||
// Verify that the branch instruction is a conditional branch.
|
||||
if (branch_inst.opcode() == SpvOp::SpvOpBranchConditional) {
|
||||
// From the branch instruction find the branch condition.
|
||||
@ -854,13 +854,13 @@ ir::Instruction* Loop::FindConditionVariable(
|
||||
|
||||
// Find the instruction representing the condition used in the conditional
|
||||
// branch.
|
||||
ir::Instruction* condition =
|
||||
opt::Instruction* condition =
|
||||
def_use_manager->GetDef(branch_inst.GetSingleWordOperand(0));
|
||||
|
||||
// Ensure that the condition is a less than operation.
|
||||
if (condition && IsSupportedCondition(condition->opcode())) {
|
||||
// The left hand side operand of the operation.
|
||||
ir::Instruction* variable_inst =
|
||||
opt::Instruction* variable_inst =
|
||||
def_use_manager->GetDef(condition->GetSingleWordOperand(2));
|
||||
|
||||
// Make sure the variable instruction used is a phi.
|
||||
@ -924,7 +924,7 @@ bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
|
||||
// maintain the state of the loop descriptor class.
|
||||
void LoopDescriptor::PostModificationCleanup() {
|
||||
LoopContainerType loops_to_remove_;
|
||||
for (ir::Loop* loop : loops_) {
|
||||
for (opt::Loop* loop : loops_) {
|
||||
if (loop->IsMarkedForRemoval()) {
|
||||
loops_to_remove_.push_back(loop);
|
||||
if (loop->HasParent()) {
|
||||
@ -933,13 +933,13 @@ void LoopDescriptor::PostModificationCleanup() {
|
||||
}
|
||||
}
|
||||
|
||||
for (ir::Loop* loop : loops_to_remove_) {
|
||||
for (opt::Loop* loop : loops_to_remove_) {
|
||||
loops_.erase(std::find(loops_.begin(), loops_.end(), loop));
|
||||
}
|
||||
|
||||
for (auto& pair : loops_to_add_) {
|
||||
ir::Loop* parent = pair.first;
|
||||
ir::Loop* loop = pair.second;
|
||||
opt::Loop* parent = pair.first;
|
||||
opt::Loop* loop = pair.second;
|
||||
|
||||
if (parent) {
|
||||
loop->SetParent(nullptr);
|
||||
@ -964,12 +964,12 @@ void LoopDescriptor::ClearLoops() {
|
||||
}
|
||||
|
||||
// Adds a new loop nest to the descriptor set.
|
||||
ir::Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<ir::Loop> new_loop) {
|
||||
ir::Loop* loop = new_loop.release();
|
||||
opt::Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<opt::Loop> new_loop) {
|
||||
opt::Loop* loop = new_loop.release();
|
||||
if (!loop->HasParent()) dummy_top_loop_.nested_loops_.push_back(loop);
|
||||
// Iterate from inner to outer most loop, adding basic block to loop mapping
|
||||
// as we go.
|
||||
for (ir::Loop& current_loop :
|
||||
for (opt::Loop& current_loop :
|
||||
make_range(iterator::begin(loop), iterator::end(nullptr))) {
|
||||
loops_.push_back(¤t_loop);
|
||||
for (uint32_t bb_id : current_loop.GetBlocks())
|
||||
@ -979,18 +979,18 @@ ir::Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<ir::Loop> new_loop) {
|
||||
return loop;
|
||||
}
|
||||
|
||||
void LoopDescriptor::RemoveLoop(ir::Loop* loop) {
|
||||
ir::Loop* parent = loop->GetParent() ? loop->GetParent() : &dummy_top_loop_;
|
||||
void LoopDescriptor::RemoveLoop(opt::Loop* loop) {
|
||||
opt::Loop* parent = loop->GetParent() ? loop->GetParent() : &dummy_top_loop_;
|
||||
parent->nested_loops_.erase(std::find(parent->nested_loops_.begin(),
|
||||
parent->nested_loops_.end(), loop));
|
||||
std::for_each(
|
||||
loop->nested_loops_.begin(), loop->nested_loops_.end(),
|
||||
[loop](ir::Loop* sub_loop) { sub_loop->SetParent(loop->GetParent()); });
|
||||
[loop](opt::Loop* sub_loop) { sub_loop->SetParent(loop->GetParent()); });
|
||||
parent->nested_loops_.insert(parent->nested_loops_.end(),
|
||||
loop->nested_loops_.begin(),
|
||||
loop->nested_loops_.end());
|
||||
for (uint32_t bb_id : loop->GetBlocks()) {
|
||||
ir::Loop* l = FindLoopForBasicBlock(bb_id);
|
||||
opt::Loop* l = FindLoopForBasicBlock(bb_id);
|
||||
if (l == loop) {
|
||||
SetBasicBlockToLoop(bb_id, l->GetParent());
|
||||
} else {
|
||||
@ -1005,5 +1005,5 @@ void LoopDescriptor::RemoveLoop(ir::Loop* loop) {
|
||||
loops_.erase(it);
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -24,15 +24,13 @@
|
||||
#include <vector>
|
||||
|
||||
#include "opt/basic_block.h"
|
||||
#include "opt/dominator_analysis.h"
|
||||
#include "opt/module.h"
|
||||
#include "opt/tree_iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
class DominatorAnalysis;
|
||||
struct DominatorTreeNode;
|
||||
} // namespace opt
|
||||
namespace ir {
|
||||
|
||||
class IRContext;
|
||||
class CFG;
|
||||
class LoopDescriptor;
|
||||
@ -79,7 +77,7 @@ class Loop {
|
||||
inline void UpdateLoopMergeInst() {
|
||||
assert(GetHeaderBlock()->GetLoopMergeInst() &&
|
||||
"The loop is not structured");
|
||||
ir::Instruction* merge_inst = GetHeaderBlock()->GetLoopMergeInst();
|
||||
opt::Instruction* merge_inst = GetHeaderBlock()->GetLoopMergeInst();
|
||||
merge_inst->SetInOperand(0, {GetMergeBlock()->id()});
|
||||
}
|
||||
|
||||
@ -234,20 +232,21 @@ class Loop {
|
||||
}
|
||||
|
||||
// Returns the list of induction variables within the loop.
|
||||
void GetInductionVariables(std::vector<ir::Instruction*>& inductions) const;
|
||||
void GetInductionVariables(std::vector<opt::Instruction*>& inductions) const;
|
||||
|
||||
// This function uses the |condition| to find the induction variable which is
|
||||
// used by the loop condition within the loop. This only works if the loop is
|
||||
// bound by a single condition and single induction variable.
|
||||
ir::Instruction* FindConditionVariable(const ir::BasicBlock* condition) const;
|
||||
opt::Instruction* FindConditionVariable(
|
||||
const opt::BasicBlock* condition) const;
|
||||
|
||||
// Returns the number of iterations within a loop when given the |induction|
|
||||
// variable and the loop |condition| check. It stores the found number of
|
||||
// iterations in the output parameter |iterations| and optionally, the step
|
||||
// value in |step_value| and the initial value of the induction variable in
|
||||
// |init_value|.
|
||||
bool FindNumberOfIterations(const ir::Instruction* induction,
|
||||
const ir::Instruction* condition,
|
||||
bool FindNumberOfIterations(const opt::Instruction* induction,
|
||||
const opt::Instruction* condition,
|
||||
size_t* iterations,
|
||||
int64_t* step_amount = nullptr,
|
||||
int64_t* init_value = nullptr) const;
|
||||
@ -264,7 +263,7 @@ class Loop {
|
||||
|
||||
// Finds the conditional block with a branch to the merge and continue blocks
|
||||
// within the loop body.
|
||||
ir::BasicBlock* FindConditionBlock() const;
|
||||
opt::BasicBlock* FindConditionBlock() const;
|
||||
|
||||
// Remove the child loop form this loop.
|
||||
inline void RemoveChildLoop(Loop* loop) {
|
||||
@ -308,13 +307,13 @@ class Loop {
|
||||
// Extract the initial value from the |induction| variable and store it in
|
||||
// |value|. If the function couldn't find the initial value of |induction|
|
||||
// return false.
|
||||
bool GetInductionInitValue(const ir::Instruction* induction,
|
||||
bool GetInductionInitValue(const opt::Instruction* induction,
|
||||
int64_t* value) const;
|
||||
|
||||
// Takes in a phi instruction |induction| and the loop |header| and returns
|
||||
// the step operation of the loop.
|
||||
ir::Instruction* GetInductionStepOperation(
|
||||
const ir::Instruction* induction) const;
|
||||
opt::Instruction* GetInductionStepOperation(
|
||||
const opt::Instruction* induction) const;
|
||||
|
||||
// Returns true if we can deduce the number of loop iterations in the step
|
||||
// operation |step|. IsSupportedCondition must also be true for the condition
|
||||
@ -332,7 +331,7 @@ class Loop {
|
||||
// exist. If |include_merge| is true, the merge block will also be included at
|
||||
// the end of the list if it exist.
|
||||
void ComputeLoopStructuredOrder(
|
||||
std::vector<ir::BasicBlock*>* ordered_loop_blocks,
|
||||
std::vector<opt::BasicBlock*>* ordered_loop_blocks,
|
||||
bool include_pre_header = false, bool include_merge = false) const;
|
||||
|
||||
// Given the loop |condition|, |initial_value|, |step_value|, the trip count
|
||||
@ -346,7 +345,7 @@ class Loop {
|
||||
|
||||
// Returns the condition instruction for entry into the loop
|
||||
// Returns nullptr if it can't be found.
|
||||
ir::Instruction* GetConditionInst() const;
|
||||
opt::Instruction* GetConditionInst() const;
|
||||
|
||||
// Returns the context associated this loop.
|
||||
IRContext* GetContext() const { return context_; }
|
||||
@ -355,7 +354,7 @@ class Loop {
|
||||
// which is also dominated by the loop continue block. This block is the latch
|
||||
// block. The specification mandates that this block should exist, therefore
|
||||
// this function will assert if it is not found.
|
||||
ir::BasicBlock* FindLatchBlock();
|
||||
opt::BasicBlock* FindLatchBlock();
|
||||
|
||||
private:
|
||||
IRContext* context_;
|
||||
@ -459,7 +458,7 @@ class LoopDescriptor {
|
||||
|
||||
// Returns the loops in |this| in the order their headers appear in the
|
||||
// binary.
|
||||
std::vector<ir::Loop*> GetLoopsInBinaryLayoutOrder();
|
||||
std::vector<opt::Loop*> GetLoopsInBinaryLayoutOrder();
|
||||
|
||||
// Returns the inner most loop that contains the basic block id |block_id|.
|
||||
inline Loop* operator[](uint32_t block_id) const {
|
||||
@ -502,7 +501,7 @@ class LoopDescriptor {
|
||||
|
||||
// Mark the loop |loop_to_add| as needing to be added when the user calls
|
||||
// PostModificationCleanup. |parent| may be null.
|
||||
inline void AddLoop(ir::Loop* loop_to_add, ir::Loop* parent) {
|
||||
inline void AddLoop(opt::Loop* loop_to_add, opt::Loop* parent) {
|
||||
loops_to_add_.emplace_back(std::make_pair(parent, loop_to_add));
|
||||
}
|
||||
|
||||
@ -521,12 +520,12 @@ class LoopDescriptor {
|
||||
|
||||
// Adds the loop |new_loop| and all its nested loops to the descriptor set.
|
||||
// The object takes ownership of all the loops.
|
||||
ir::Loop* AddLoopNest(std::unique_ptr<ir::Loop> new_loop);
|
||||
opt::Loop* AddLoopNest(std::unique_ptr<opt::Loop> new_loop);
|
||||
|
||||
// Remove the loop |loop|.
|
||||
void RemoveLoop(ir::Loop* loop);
|
||||
void RemoveLoop(opt::Loop* loop);
|
||||
|
||||
void SetAsTopLoop(ir::Loop* loop) {
|
||||
void SetAsTopLoop(opt::Loop* loop) {
|
||||
assert(std::find(dummy_top_loop_.begin(), dummy_top_loop_.end(), loop) ==
|
||||
dummy_top_loop_.end() &&
|
||||
"already registered");
|
||||
@ -569,7 +568,7 @@ class LoopDescriptor {
|
||||
LoopsToAddContainerType loops_to_add_;
|
||||
};
|
||||
|
||||
} // namespace ir
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_LOOP_DESCRIPTORS_H_
|
||||
|
@ -55,7 +55,7 @@ namespace opt {
|
||||
|
||||
class LoopFissionImpl {
|
||||
public:
|
||||
LoopFissionImpl(ir::IRContext* context, ir::Loop* loop)
|
||||
LoopFissionImpl(opt::IRContext* context, opt::Loop* loop)
|
||||
: context_(context), loop_(loop), load_used_in_condition_(false) {}
|
||||
|
||||
// Group each instruction in the loop into sets of instructions related by
|
||||
@ -69,37 +69,37 @@ class LoopFissionImpl {
|
||||
bool CanPerformSplit();
|
||||
|
||||
// Split the loop and return a pointer to the new loop.
|
||||
ir::Loop* SplitLoop();
|
||||
opt::Loop* SplitLoop();
|
||||
|
||||
// Checks if |inst| is safe to move. We can only move instructions which don't
|
||||
// have any side effects and OpLoads and OpStores.
|
||||
bool MovableInstruction(const ir::Instruction& inst) const;
|
||||
bool MovableInstruction(const opt::Instruction& inst) const;
|
||||
|
||||
private:
|
||||
// Traverse the def use chain of |inst| and add the users and uses of |inst|
|
||||
// which are in the same loop to the |returned_set|.
|
||||
void TraverseUseDef(ir::Instruction* inst,
|
||||
std::set<ir::Instruction*>* returned_set,
|
||||
void TraverseUseDef(opt::Instruction* inst,
|
||||
std::set<opt::Instruction*>* returned_set,
|
||||
bool ignore_phi_users = false, bool report_loads = false);
|
||||
|
||||
// We group the instructions in the block into two different groups, the
|
||||
// instructions to be kept in the original loop and the ones to be cloned into
|
||||
// the new loop. As the cloned loop is attached to the preheader it will be
|
||||
// the first loop and the second loop will be the original.
|
||||
std::set<ir::Instruction*> cloned_loop_instructions_;
|
||||
std::set<ir::Instruction*> original_loop_instructions_;
|
||||
std::set<opt::Instruction*> cloned_loop_instructions_;
|
||||
std::set<opt::Instruction*> original_loop_instructions_;
|
||||
|
||||
// We need a set of all the instructions to be seen so we can break any
|
||||
// recursion and also so we can ignore certain instructions by preemptively
|
||||
// adding them to this set.
|
||||
std::set<ir::Instruction*> seen_instructions_;
|
||||
std::set<opt::Instruction*> seen_instructions_;
|
||||
|
||||
// A map of instructions to their relative position in the function.
|
||||
std::map<ir::Instruction*, size_t> instruction_order_;
|
||||
std::map<opt::Instruction*, size_t> instruction_order_;
|
||||
|
||||
ir::IRContext* context_;
|
||||
opt::IRContext* context_;
|
||||
|
||||
ir::Loop* loop_;
|
||||
opt::Loop* loop_;
|
||||
|
||||
// This is set to true by TraverseUseDef when traversing the instructions
|
||||
// related to the loop condition and any if conditions should any of those
|
||||
@ -107,27 +107,27 @@ class LoopFissionImpl {
|
||||
bool load_used_in_condition_;
|
||||
};
|
||||
|
||||
bool LoopFissionImpl::MovableInstruction(const ir::Instruction& inst) const {
|
||||
bool LoopFissionImpl::MovableInstruction(const opt::Instruction& inst) const {
|
||||
return inst.opcode() == SpvOp::SpvOpLoad ||
|
||||
inst.opcode() == SpvOp::SpvOpStore ||
|
||||
inst.opcode() == SpvOp::SpvOpSelectionMerge ||
|
||||
inst.opcode() == SpvOp::SpvOpPhi || inst.IsOpcodeCodeMotionSafe();
|
||||
}
|
||||
|
||||
void LoopFissionImpl::TraverseUseDef(ir::Instruction* inst,
|
||||
std::set<ir::Instruction*>* returned_set,
|
||||
void LoopFissionImpl::TraverseUseDef(opt::Instruction* inst,
|
||||
std::set<opt::Instruction*>* returned_set,
|
||||
bool ignore_phi_users, bool report_loads) {
|
||||
assert(returned_set && "Set to be returned cannot be null.");
|
||||
|
||||
opt::analysis::DefUseManager* def_use = context_->get_def_use_mgr();
|
||||
std::set<ir::Instruction*>& inst_set = *returned_set;
|
||||
std::set<opt::Instruction*>& inst_set = *returned_set;
|
||||
|
||||
// We create this functor to traverse the use def chain to build the
|
||||
// grouping of related instructions. The lambda captures the std::function
|
||||
// to allow it to recurse.
|
||||
std::function<void(ir::Instruction*)> traverser_functor;
|
||||
std::function<void(opt::Instruction*)> traverser_functor;
|
||||
traverser_functor = [this, def_use, &inst_set, &traverser_functor,
|
||||
ignore_phi_users, report_loads](ir::Instruction* user) {
|
||||
ignore_phi_users, report_loads](opt::Instruction* user) {
|
||||
// If we've seen the instruction before or it is not inside the loop end the
|
||||
// traversal.
|
||||
if (!user || seen_instructions_.count(user) != 0 ||
|
||||
@ -171,7 +171,7 @@ void LoopFissionImpl::TraverseUseDef(ir::Instruction* inst,
|
||||
def_use->ForEachUser(user, traverser_functor);
|
||||
|
||||
// Wrapper functor for the use traversal.
|
||||
auto traverse_use = [&traverser_functor](ir::Instruction* use, uint32_t) {
|
||||
auto traverse_use = [&traverser_functor](opt::Instruction* use, uint32_t) {
|
||||
traverser_functor(use);
|
||||
};
|
||||
def_use->ForEachUse(user, traverse_use);
|
||||
@ -184,33 +184,33 @@ void LoopFissionImpl::TraverseUseDef(ir::Instruction* inst,
|
||||
}
|
||||
|
||||
bool LoopFissionImpl::GroupInstructionsByUseDef() {
|
||||
std::vector<std::set<ir::Instruction*>> sets{};
|
||||
std::vector<std::set<opt::Instruction*>> sets{};
|
||||
|
||||
// We want to ignore all the instructions stemming from the loop condition
|
||||
// instruction.
|
||||
ir::BasicBlock* condition_block = loop_->FindConditionBlock();
|
||||
opt::BasicBlock* condition_block = loop_->FindConditionBlock();
|
||||
|
||||
if (!condition_block) return false;
|
||||
ir::Instruction* condition = &*condition_block->tail();
|
||||
opt::Instruction* condition = &*condition_block->tail();
|
||||
|
||||
// We iterate over the blocks via iterating over all the blocks in the
|
||||
// function, we do this so we are iterating in the same order which the blocks
|
||||
// appear in the binary.
|
||||
ir::Function& function = *loop_->GetHeaderBlock()->GetParent();
|
||||
opt::Function& function = *loop_->GetHeaderBlock()->GetParent();
|
||||
|
||||
// Create a temporary set to ignore certain groups of instructions within the
|
||||
// loop. We don't want any instructions related to control flow to be removed
|
||||
// from either loop only instructions within the control flow bodies.
|
||||
std::set<ir::Instruction*> instructions_to_ignore{};
|
||||
std::set<opt::Instruction*> instructions_to_ignore{};
|
||||
TraverseUseDef(condition, &instructions_to_ignore, true, true);
|
||||
|
||||
// Traverse control flow instructions to ensure they are added to the
|
||||
// seen_instructions_ set and will be ignored when it it called with actual
|
||||
// sets.
|
||||
for (ir::BasicBlock& block : function) {
|
||||
for (opt::BasicBlock& block : function) {
|
||||
if (!loop_->IsInsideLoop(block.id())) continue;
|
||||
|
||||
for (ir::Instruction& inst : block) {
|
||||
for (opt::Instruction& inst : block) {
|
||||
// Ignore all instructions related to control flow.
|
||||
if (inst.opcode() == SpvOp::SpvOpSelectionMerge || inst.IsBranch()) {
|
||||
TraverseUseDef(&inst, &instructions_to_ignore, true, true);
|
||||
@ -220,12 +220,12 @@ bool LoopFissionImpl::GroupInstructionsByUseDef() {
|
||||
|
||||
// Traverse the instructions and generate the sets, automatically ignoring any
|
||||
// instructions in instructions_to_ignore.
|
||||
for (ir::BasicBlock& block : function) {
|
||||
for (opt::BasicBlock& block : function) {
|
||||
if (!loop_->IsInsideLoop(block.id()) ||
|
||||
loop_->GetHeaderBlock()->id() == block.id())
|
||||
continue;
|
||||
|
||||
for (ir::Instruction& inst : block) {
|
||||
for (opt::Instruction& inst : block) {
|
||||
// Record the order that each load/store is seen.
|
||||
if (inst.opcode() == SpvOp::SpvOpLoad ||
|
||||
inst.opcode() == SpvOp::SpvOpStore) {
|
||||
@ -238,7 +238,7 @@ bool LoopFissionImpl::GroupInstructionsByUseDef() {
|
||||
}
|
||||
|
||||
// Build the set.
|
||||
std::set<ir::Instruction*> inst_set{};
|
||||
std::set<opt::Instruction*> inst_set{};
|
||||
TraverseUseDef(&inst, &inst_set);
|
||||
if (!inst_set.empty()) sets.push_back(std::move(inst_set));
|
||||
}
|
||||
@ -273,8 +273,8 @@ bool LoopFissionImpl::CanPerformSplit() {
|
||||
|
||||
// Build a list of all parent loops of this loop. Loop dependence analysis
|
||||
// needs this structure.
|
||||
std::vector<const ir::Loop*> loops;
|
||||
ir::Loop* parent_loop = loop_;
|
||||
std::vector<const opt::Loop*> loops;
|
||||
opt::Loop* parent_loop = loop_;
|
||||
while (parent_loop) {
|
||||
loops.push_back(parent_loop);
|
||||
parent_loop = parent_loop->GetParent();
|
||||
@ -283,13 +283,13 @@ bool LoopFissionImpl::CanPerformSplit() {
|
||||
LoopDependenceAnalysis analysis{context_, loops};
|
||||
|
||||
// A list of all the stores in the cloned loop.
|
||||
std::vector<ir::Instruction*> set_one_stores{};
|
||||
std::vector<opt::Instruction*> set_one_stores{};
|
||||
|
||||
// A list of all the loads in the cloned loop.
|
||||
std::vector<ir::Instruction*> set_one_loads{};
|
||||
std::vector<opt::Instruction*> set_one_loads{};
|
||||
|
||||
// Populate the above lists.
|
||||
for (ir::Instruction* inst : cloned_loop_instructions_) {
|
||||
for (opt::Instruction* inst : cloned_loop_instructions_) {
|
||||
if (inst->opcode() == SpvOp::SpvOpStore) {
|
||||
set_one_stores.push_back(inst);
|
||||
} else if (inst->opcode() == SpvOp::SpvOpLoad) {
|
||||
@ -307,7 +307,7 @@ bool LoopFissionImpl::CanPerformSplit() {
|
||||
|
||||
// Check the dependencies between loads in the cloned loop and stores in the
|
||||
// original and vice versa.
|
||||
for (ir::Instruction* inst : original_loop_instructions_) {
|
||||
for (opt::Instruction* inst : original_loop_instructions_) {
|
||||
// If we find any instruction which we can't move (such as a barrier),
|
||||
// return false.
|
||||
if (!MovableInstruction(*inst)) return false;
|
||||
@ -315,7 +315,7 @@ bool LoopFissionImpl::CanPerformSplit() {
|
||||
// Look at the dependency between the loads in the original and stores in
|
||||
// the cloned loops.
|
||||
if (inst->opcode() == SpvOp::SpvOpLoad) {
|
||||
for (ir::Instruction* store : set_one_stores) {
|
||||
for (opt::Instruction* store : set_one_stores) {
|
||||
DistanceVector vec{loop_depth};
|
||||
|
||||
// If the store actually should appear after the load, return false.
|
||||
@ -333,7 +333,7 @@ bool LoopFissionImpl::CanPerformSplit() {
|
||||
}
|
||||
}
|
||||
} else if (inst->opcode() == SpvOp::SpvOpStore) {
|
||||
for (ir::Instruction* load : set_one_loads) {
|
||||
for (opt::Instruction* load : set_one_loads) {
|
||||
DistanceVector vec{loop_depth};
|
||||
|
||||
// If the load actually should appear after the store, return false.
|
||||
@ -355,30 +355,30 @@ bool LoopFissionImpl::CanPerformSplit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
ir::Loop* LoopFissionImpl::SplitLoop() {
|
||||
opt::Loop* LoopFissionImpl::SplitLoop() {
|
||||
// Clone the loop.
|
||||
LoopUtils util{context_, loop_};
|
||||
LoopUtils::LoopCloningResult clone_results;
|
||||
ir::Loop* cloned_loop = util.CloneAndAttachLoopToHeader(&clone_results);
|
||||
opt::Loop* cloned_loop = util.CloneAndAttachLoopToHeader(&clone_results);
|
||||
|
||||
// Update the OpLoopMerge in the cloned loop.
|
||||
cloned_loop->UpdateLoopMergeInst();
|
||||
|
||||
// Add the loop_ to the module.
|
||||
ir::Function::iterator it =
|
||||
opt::Function::iterator it =
|
||||
util.GetFunction()->FindBlock(loop_->GetOrCreatePreHeaderBlock()->id());
|
||||
util.GetFunction()->AddBasicBlocks(clone_results.cloned_bb_.begin(),
|
||||
clone_results.cloned_bb_.end(), ++it);
|
||||
loop_->SetPreHeaderBlock(cloned_loop->GetMergeBlock());
|
||||
|
||||
std::vector<ir::Instruction*> instructions_to_kill{};
|
||||
std::vector<opt::Instruction*> instructions_to_kill{};
|
||||
|
||||
// Kill all the instructions which should appear in the cloned loop but not in
|
||||
// the original loop.
|
||||
for (uint32_t id : loop_->GetBlocks()) {
|
||||
ir::BasicBlock* block = context_->cfg()->block(id);
|
||||
opt::BasicBlock* block = context_->cfg()->block(id);
|
||||
|
||||
for (ir::Instruction& inst : *block) {
|
||||
for (opt::Instruction& inst : *block) {
|
||||
// If the instruction appears in the cloned loop instruction group, kill
|
||||
// it.
|
||||
if (cloned_loop_instructions_.count(&inst) == 1 &&
|
||||
@ -395,9 +395,9 @@ ir::Loop* LoopFissionImpl::SplitLoop() {
|
||||
// Kill all instructions which should appear in the original loop and not in
|
||||
// the cloned loop.
|
||||
for (uint32_t id : cloned_loop->GetBlocks()) {
|
||||
ir::BasicBlock* block = context_->cfg()->block(id);
|
||||
for (ir::Instruction& inst : *block) {
|
||||
ir::Instruction* old_inst = clone_results.ptr_map_[&inst];
|
||||
opt::BasicBlock* block = context_->cfg()->block(id);
|
||||
for (opt::Instruction& inst : *block) {
|
||||
opt::Instruction* old_inst = clone_results.ptr_map_[&inst];
|
||||
// If the instruction belongs to the original loop instruction group, kill
|
||||
// it.
|
||||
if (cloned_loop_instructions_.count(old_inst) == 0 &&
|
||||
@ -407,7 +407,7 @@ ir::Loop* LoopFissionImpl::SplitLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
for (ir::Instruction* i : instructions_to_kill) {
|
||||
for (opt::Instruction* i : instructions_to_kill) {
|
||||
context_->KillInst(i);
|
||||
}
|
||||
|
||||
@ -433,37 +433,38 @@ LoopFissionPass::LoopFissionPass() : split_multiple_times_(false) {
|
||||
};
|
||||
}
|
||||
|
||||
bool LoopFissionPass::ShouldSplitLoop(const ir::Loop& loop, ir::IRContext* c) {
|
||||
bool LoopFissionPass::ShouldSplitLoop(const opt::Loop& loop,
|
||||
opt::IRContext* c) {
|
||||
LivenessAnalysis* analysis = c->GetLivenessAnalysis();
|
||||
|
||||
RegisterLiveness::RegionRegisterLiveness liveness{};
|
||||
|
||||
ir::Function* function = loop.GetHeaderBlock()->GetParent();
|
||||
opt::Function* function = loop.GetHeaderBlock()->GetParent();
|
||||
analysis->Get(function)->ComputeLoopRegisterPressure(loop, &liveness);
|
||||
|
||||
return split_criteria_(liveness);
|
||||
}
|
||||
|
||||
Pass::Status LoopFissionPass::Process(ir::IRContext* c) {
|
||||
Pass::Status LoopFissionPass::Process(opt::IRContext* c) {
|
||||
bool changed = false;
|
||||
|
||||
for (ir::Function& f : *c->module()) {
|
||||
for (opt::Function& f : *c->module()) {
|
||||
// We collect all the inner most loops in the function and run the loop
|
||||
// splitting util on each. The reason we do this is to allow us to iterate
|
||||
// over each, as creating new loops will invalidate the the loop iterator.
|
||||
std::vector<ir::Loop*> inner_most_loops{};
|
||||
ir::LoopDescriptor& loop_descriptor = *c->GetLoopDescriptor(&f);
|
||||
for (ir::Loop& loop : loop_descriptor) {
|
||||
std::vector<opt::Loop*> inner_most_loops{};
|
||||
opt::LoopDescriptor& loop_descriptor = *c->GetLoopDescriptor(&f);
|
||||
for (opt::Loop& loop : loop_descriptor) {
|
||||
if (!loop.HasChildren() && ShouldSplitLoop(loop, c)) {
|
||||
inner_most_loops.push_back(&loop);
|
||||
}
|
||||
}
|
||||
|
||||
// List of new loops which meet the criteria to be split again.
|
||||
std::vector<ir::Loop*> new_loops_to_split{};
|
||||
std::vector<opt::Loop*> new_loops_to_split{};
|
||||
|
||||
while (!inner_most_loops.empty()) {
|
||||
for (ir::Loop* loop : inner_most_loops) {
|
||||
for (opt::Loop* loop : inner_most_loops) {
|
||||
LoopFissionImpl impl{c, loop};
|
||||
|
||||
// Group the instructions in the loop into two different sets of related
|
||||
@ -474,9 +475,9 @@ Pass::Status LoopFissionPass::Process(ir::IRContext* c) {
|
||||
}
|
||||
|
||||
if (impl.CanPerformSplit()) {
|
||||
ir::Loop* second_loop = impl.SplitLoop();
|
||||
opt::Loop* second_loop = impl.SplitLoop();
|
||||
changed = true;
|
||||
c->InvalidateAnalysesExceptFor(ir::IRContext::kAnalysisLoopAnalysis);
|
||||
c->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisLoopAnalysis);
|
||||
|
||||
// If the newly created loop meets the criteria to be split, split it
|
||||
// again.
|
||||
|
@ -57,10 +57,10 @@ class LoopFissionPass : public Pass {
|
||||
|
||||
const char* name() const override { return "Loop Fission"; }
|
||||
|
||||
Pass::Status Process(ir::IRContext* context) override;
|
||||
Pass::Status Process(opt::IRContext* context) override;
|
||||
|
||||
// Checks if |loop| meets the register pressure criteria to be split.
|
||||
bool ShouldSplitLoop(const ir::Loop& loop, ir::IRContext* context);
|
||||
bool ShouldSplitLoop(const opt::Loop& loop, opt::IRContext* context);
|
||||
|
||||
private:
|
||||
// Functor to run in ShouldSplitLoop to determine if the register pressure
|
||||
|
@ -27,7 +27,7 @@ namespace opt {
|
||||
namespace {
|
||||
|
||||
// Append all the loops nested in |loop| to |loops|.
|
||||
void CollectChildren(ir::Loop* loop, std::vector<const ir::Loop*>* loops) {
|
||||
void CollectChildren(opt::Loop* loop, std::vector<const opt::Loop*>* loops) {
|
||||
for (auto child : *loop) {
|
||||
loops->push_back(child);
|
||||
if (child->NumImmediateChildren() != 0) {
|
||||
@ -37,10 +37,10 @@ void CollectChildren(ir::Loop* loop, std::vector<const ir::Loop*>* loops) {
|
||||
}
|
||||
|
||||
// Return the set of locations accessed by |stores| and |loads|.
|
||||
std::set<ir::Instruction*> GetLocationsAccessed(
|
||||
const std::map<ir::Instruction*, std::vector<ir::Instruction*>>& stores,
|
||||
const std::map<ir::Instruction*, std::vector<ir::Instruction*>>& loads) {
|
||||
std::set<ir::Instruction*> locations{};
|
||||
std::set<opt::Instruction*> GetLocationsAccessed(
|
||||
const std::map<opt::Instruction*, std::vector<opt::Instruction*>>& stores,
|
||||
const std::map<opt::Instruction*, std::vector<opt::Instruction*>>& loads) {
|
||||
std::set<opt::Instruction*> locations{};
|
||||
|
||||
for (const auto& kv : stores) {
|
||||
locations.insert(std::get<0>(kv));
|
||||
@ -56,8 +56,8 @@ std::set<ir::Instruction*> GetLocationsAccessed(
|
||||
// Append all dependences from |sources| to |destinations| to |dependences|.
|
||||
void GetDependences(std::vector<DistanceVector>* dependences,
|
||||
LoopDependenceAnalysis* analysis,
|
||||
const std::vector<ir::Instruction*>& sources,
|
||||
const std::vector<ir::Instruction*>& destinations,
|
||||
const std::vector<opt::Instruction*>& sources,
|
||||
const std::vector<opt::Instruction*>& destinations,
|
||||
size_t num_entries) {
|
||||
for (auto source : sources) {
|
||||
for (auto destination : destinations) {
|
||||
@ -70,8 +70,8 @@ void GetDependences(std::vector<DistanceVector>* dependences,
|
||||
}
|
||||
|
||||
// Apped all instructions in |block| to |instructions|.
|
||||
void AddInstructionsInBlock(std::vector<ir::Instruction*>* instructions,
|
||||
ir::BasicBlock* block) {
|
||||
void AddInstructionsInBlock(std::vector<opt::Instruction*>* instructions,
|
||||
opt::BasicBlock* block) {
|
||||
for (auto& inst : *block) {
|
||||
instructions->push_back(&inst);
|
||||
}
|
||||
@ -82,12 +82,12 @@ void AddInstructionsInBlock(std::vector<ir::Instruction*>* instructions,
|
||||
} // namespace
|
||||
|
||||
bool LoopFusion::UsedInContinueOrConditionBlock(
|
||||
ir::Instruction* phi_instruction, ir::Loop* loop) {
|
||||
opt::Instruction* phi_instruction, opt::Loop* loop) {
|
||||
auto condition_block = loop->FindConditionBlock()->id();
|
||||
auto continue_block = loop->GetContinueBlock()->id();
|
||||
auto not_used = context_->get_def_use_mgr()->WhileEachUser(
|
||||
phi_instruction,
|
||||
[this, condition_block, continue_block](ir::Instruction* instruction) {
|
||||
[this, condition_block, continue_block](opt::Instruction* instruction) {
|
||||
auto block_id = context_->get_instr_block(instruction)->id();
|
||||
return block_id != condition_block && block_id != continue_block;
|
||||
});
|
||||
@ -96,10 +96,10 @@ bool LoopFusion::UsedInContinueOrConditionBlock(
|
||||
}
|
||||
|
||||
void LoopFusion::RemoveIfNotUsedContinueOrConditionBlock(
|
||||
std::vector<ir::Instruction*>* instructions, ir::Loop* loop) {
|
||||
std::vector<opt::Instruction*>* instructions, opt::Loop* loop) {
|
||||
instructions->erase(
|
||||
std::remove_if(std::begin(*instructions), std::end(*instructions),
|
||||
[this, loop](ir::Instruction* instruction) {
|
||||
[this, loop](opt::Instruction* instruction) {
|
||||
return !UsedInContinueOrConditionBlock(instruction,
|
||||
loop);
|
||||
}),
|
||||
@ -132,7 +132,7 @@ bool LoopFusion::AreCompatible() {
|
||||
|
||||
// |GetInductionVariables| returns all OpPhi in the header. Check that both
|
||||
// loops have exactly one that is used in the continue and condition blocks.
|
||||
std::vector<ir::Instruction*> inductions_0{}, inductions_1{};
|
||||
std::vector<opt::Instruction*> inductions_0{}, inductions_1{};
|
||||
loop_0_->GetInductionVariables(inductions_0);
|
||||
RemoveIfNotUsedContinueOrConditionBlock(&inductions_0, loop_0_);
|
||||
|
||||
@ -169,7 +169,7 @@ bool LoopFusion::AreCompatible() {
|
||||
|
||||
auto pre_header_1 = loop_1_->GetPreHeaderBlock();
|
||||
|
||||
std::vector<ir::BasicBlock*> block_to_check{};
|
||||
std::vector<opt::BasicBlock*> block_to_check{};
|
||||
block_to_check.push_back(pre_header_1);
|
||||
|
||||
if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) {
|
||||
@ -208,7 +208,7 @@ bool LoopFusion::AreCompatible() {
|
||||
auto is_used = false;
|
||||
context_->get_def_use_mgr()->ForEachUse(
|
||||
inst.GetSingleWordInOperand(0),
|
||||
[&is_used](ir::Instruction* use_inst, uint32_t) {
|
||||
[&is_used](opt::Instruction* use_inst, uint32_t) {
|
||||
if (use_inst->opcode() == SpvOpLoad) {
|
||||
is_used = true;
|
||||
}
|
||||
@ -230,7 +230,7 @@ bool LoopFusion::AreCompatible() {
|
||||
return true;
|
||||
} // namespace opt
|
||||
|
||||
bool LoopFusion::ContainsBarriersOrFunctionCalls(ir::Loop* loop) {
|
||||
bool LoopFusion::ContainsBarriersOrFunctionCalls(opt::Loop* loop) {
|
||||
for (const auto& block : loop->GetBlocks()) {
|
||||
for (const auto& inst : *containing_function_->FindBlock(block)) {
|
||||
auto opcode = inst.opcode();
|
||||
@ -336,9 +336,9 @@ bool LoopFusion::CheckStep() {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<ir::Instruction*, std::vector<ir::Instruction*>>
|
||||
LoopFusion::LocationToMemOps(const std::vector<ir::Instruction*>& mem_ops) {
|
||||
std::map<ir::Instruction*, std::vector<ir::Instruction*>> location_map{};
|
||||
std::map<opt::Instruction*, std::vector<opt::Instruction*>>
|
||||
LoopFusion::LocationToMemOps(const std::vector<opt::Instruction*>& mem_ops) {
|
||||
std::map<opt::Instruction*, std::vector<opt::Instruction*>> location_map{};
|
||||
|
||||
for (auto instruction : mem_ops) {
|
||||
auto access_location = context_->get_def_use_mgr()->GetDef(
|
||||
@ -355,10 +355,10 @@ LoopFusion::LocationToMemOps(const std::vector<ir::Instruction*>& mem_ops) {
|
||||
return location_map;
|
||||
}
|
||||
|
||||
std::pair<std::vector<ir::Instruction*>, std::vector<ir::Instruction*>>
|
||||
LoopFusion::GetLoadsAndStoresInLoop(ir::Loop* loop) {
|
||||
std::vector<ir::Instruction*> loads{};
|
||||
std::vector<ir::Instruction*> stores{};
|
||||
std::pair<std::vector<opt::Instruction*>, std::vector<opt::Instruction*>>
|
||||
LoopFusion::GetLoadsAndStoresInLoop(opt::Loop* loop) {
|
||||
std::vector<opt::Instruction*> loads{};
|
||||
std::vector<opt::Instruction*> stores{};
|
||||
|
||||
for (auto block_id : loop->GetBlocks()) {
|
||||
if (block_id == loop->GetContinueBlock()->id()) {
|
||||
@ -377,9 +377,9 @@ LoopFusion::GetLoadsAndStoresInLoop(ir::Loop* loop) {
|
||||
return std::make_pair(loads, stores);
|
||||
}
|
||||
|
||||
bool LoopFusion::IsUsedInLoop(ir::Instruction* instruction, ir::Loop* loop) {
|
||||
bool LoopFusion::IsUsedInLoop(opt::Instruction* instruction, opt::Loop* loop) {
|
||||
auto not_used = context_->get_def_use_mgr()->WhileEachUser(
|
||||
instruction, [this, loop](ir::Instruction* user) {
|
||||
instruction, [this, loop](opt::Instruction* user) {
|
||||
auto block_id = context_->get_instr_block(user)->id();
|
||||
return !loop->IsInsideLoop(block_id);
|
||||
});
|
||||
@ -397,7 +397,7 @@ bool LoopFusion::IsLegal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<ir::Instruction*> phi_instructions{};
|
||||
std::vector<opt::Instruction*> phi_instructions{};
|
||||
loop_0_->GetInductionVariables(phi_instructions);
|
||||
|
||||
// Check no OpPhi in |loop_0_| is used in |loop_1_|.
|
||||
@ -410,7 +410,7 @@ bool LoopFusion::IsLegal() {
|
||||
// Check no LCSSA OpPhi in merge block of |loop_0_| is used in |loop_1_|.
|
||||
auto phi_used = false;
|
||||
loop_0_->GetMergeBlock()->ForEachPhiInst(
|
||||
[this, &phi_used](ir::Instruction* phi_instruction) {
|
||||
[this, &phi_used](opt::Instruction* phi_instruction) {
|
||||
phi_used |= IsUsedInLoop(phi_instruction, loop_1_);
|
||||
});
|
||||
|
||||
@ -433,7 +433,7 @@ bool LoopFusion::IsLegal() {
|
||||
auto locations_0 = GetLocationsAccessed(store_locs_0, load_locs_0);
|
||||
auto locations_1 = GetLocationsAccessed(store_locs_1, load_locs_1);
|
||||
|
||||
std::vector<ir::Instruction*> potential_clashes{};
|
||||
std::vector<opt::Instruction*> potential_clashes{};
|
||||
|
||||
std::set_intersection(std::begin(locations_0), std::end(locations_0),
|
||||
std::begin(locations_1), std::end(locations_1),
|
||||
@ -445,7 +445,7 @@ bool LoopFusion::IsLegal() {
|
||||
}
|
||||
|
||||
// Find variables that have at least one store.
|
||||
std::vector<ir::Instruction*> potential_clashes_with_stores{};
|
||||
std::vector<opt::Instruction*> potential_clashes_with_stores{};
|
||||
for (auto location : potential_clashes) {
|
||||
if (store_locs_0.find(location) != std::end(store_locs_0) ||
|
||||
store_locs_1.find(location) != std::end(store_locs_1)) {
|
||||
@ -463,7 +463,7 @@ bool LoopFusion::IsLegal() {
|
||||
// distance.
|
||||
|
||||
// Find all the loops in this loop nest for the dependency analysis.
|
||||
std::vector<const ir::Loop*> loops{};
|
||||
std::vector<const opt::Loop*> loops{};
|
||||
|
||||
// Find the parents.
|
||||
for (auto current_loop = loop_0_; current_loop != nullptr;
|
||||
@ -519,7 +519,7 @@ bool LoopFusion::IsLegal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReplacePhiParentWith(ir::Instruction* inst, uint32_t orig_block,
|
||||
void ReplacePhiParentWith(opt::Instruction* inst, uint32_t orig_block,
|
||||
uint32_t new_block) {
|
||||
if (inst->GetSingleWordInOperand(1) == orig_block) {
|
||||
inst->SetInOperand(1, {new_block});
|
||||
@ -555,7 +555,7 @@ void LoopFusion::Fuse() {
|
||||
|
||||
// Update merge block id in the header of |loop_0_| to the merge block of
|
||||
// |loop_1_|.
|
||||
loop_0_->GetHeaderBlock()->ForEachInst([this](ir::Instruction* inst) {
|
||||
loop_0_->GetHeaderBlock()->ForEachInst([this](opt::Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpLoopMerge) {
|
||||
inst->SetInOperand(0, {loop_1_->GetMergeBlock()->id()});
|
||||
}
|
||||
@ -563,7 +563,7 @@ void LoopFusion::Fuse() {
|
||||
|
||||
// Update condition branch target in |loop_0_| to the merge block of
|
||||
// |loop_1_|.
|
||||
condition_block_of_0->ForEachInst([this](ir::Instruction* inst) {
|
||||
condition_block_of_0->ForEachInst([this](opt::Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpBranchConditional) {
|
||||
auto loop_0_merge_block_id = loop_0_->GetMergeBlock()->id();
|
||||
|
||||
@ -577,7 +577,7 @@ void LoopFusion::Fuse() {
|
||||
|
||||
// Move OpPhi instructions not corresponding to the induction variable from
|
||||
// the header of |loop_1_| to the header of |loop_0_|.
|
||||
std::vector<ir::Instruction*> instructions_to_move{};
|
||||
std::vector<opt::Instruction*> instructions_to_move{};
|
||||
for (auto& instruction : *loop_1_->GetHeaderBlock()) {
|
||||
if (instruction.opcode() == SpvOpPhi && &instruction != induction_1_) {
|
||||
instructions_to_move.push_back(&instruction);
|
||||
@ -590,7 +590,7 @@ void LoopFusion::Fuse() {
|
||||
}
|
||||
|
||||
// Update the OpPhi parents to the correct blocks in |loop_0_|.
|
||||
loop_0_->GetHeaderBlock()->ForEachPhiInst([this](ir::Instruction* i) {
|
||||
loop_0_->GetHeaderBlock()->ForEachPhiInst([this](opt::Instruction* i) {
|
||||
ReplacePhiParentWith(i, loop_1_->GetPreHeaderBlock()->id(),
|
||||
loop_0_->GetPreHeaderBlock()->id());
|
||||
|
||||
@ -611,14 +611,14 @@ void LoopFusion::Fuse() {
|
||||
|
||||
// Replace LCSSA OpPhi in merge block of |loop_0_|.
|
||||
loop_0_->GetMergeBlock()->ForEachPhiInst(
|
||||
[this](ir::Instruction* instruction) {
|
||||
[this](opt::Instruction* instruction) {
|
||||
context_->ReplaceAllUsesWith(instruction->result_id(),
|
||||
instruction->GetSingleWordInOperand(0));
|
||||
});
|
||||
|
||||
// Update LCSSA OpPhi in merge block of |loop_1_|.
|
||||
loop_1_->GetMergeBlock()->ForEachPhiInst(
|
||||
[condition_block_of_0](ir::Instruction* instruction) {
|
||||
[condition_block_of_0](opt::Instruction* instruction) {
|
||||
instruction->SetInOperand(1, {condition_block_of_0->id()});
|
||||
});
|
||||
|
||||
@ -627,7 +627,7 @@ void LoopFusion::Fuse() {
|
||||
|
||||
// Gather all instructions to be killed from |loop_1_| (induction variable
|
||||
// initialisation, header, condition and continue blocks).
|
||||
std::vector<ir::Instruction*> instr_to_delete{};
|
||||
std::vector<opt::Instruction*> instr_to_delete{};
|
||||
AddInstructionsInBlock(&instr_to_delete, loop_1_->GetPreHeaderBlock());
|
||||
AddInstructionsInBlock(&instr_to_delete, loop_1_->GetHeaderBlock());
|
||||
AddInstructionsInBlock(&instr_to_delete, loop_1_->FindConditionBlock());
|
||||
@ -673,7 +673,7 @@ void LoopFusion::Fuse() {
|
||||
auto ld = context_->GetLoopDescriptor(containing_function_);
|
||||
|
||||
// Create a copy, so the iterator wouldn't be invalidated.
|
||||
std::vector<ir::Loop*> loops_to_add_remove{};
|
||||
std::vector<opt::Loop*> loops_to_add_remove{};
|
||||
for (auto child_loop : *loop_1_) {
|
||||
loops_to_add_remove.push_back(child_loop);
|
||||
}
|
||||
@ -722,10 +722,10 @@ void LoopFusion::Fuse() {
|
||||
|
||||
// Invalidate analyses.
|
||||
context_->InvalidateAnalysesExceptFor(
|
||||
ir::IRContext::Analysis::kAnalysisInstrToBlockMapping |
|
||||
ir::IRContext::Analysis::kAnalysisLoopAnalysis |
|
||||
ir::IRContext::Analysis::kAnalysisDefUse |
|
||||
ir::IRContext::Analysis::kAnalysisCFG);
|
||||
opt::IRContext::Analysis::kAnalysisInstrToBlockMapping |
|
||||
opt::IRContext::Analysis::kAnalysisLoopAnalysis |
|
||||
opt::IRContext::Analysis::kAnalysisDefUse |
|
||||
opt::IRContext::Analysis::kAnalysisCFG);
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
|
@ -29,7 +29,7 @@ namespace opt {
|
||||
|
||||
class LoopFusion {
|
||||
public:
|
||||
LoopFusion(ir::IRContext* context, ir::Loop* loop_0, ir::Loop* loop_1)
|
||||
LoopFusion(opt::IRContext* context, opt::Loop* loop_0, opt::Loop* loop_1)
|
||||
: context_(context),
|
||||
loop_0_(loop_0),
|
||||
loop_1_(loop_1),
|
||||
@ -70,42 +70,42 @@ class LoopFusion {
|
||||
|
||||
// Returns |true| if |instruction| is used in the continue or condition block
|
||||
// of |loop|.
|
||||
bool UsedInContinueOrConditionBlock(ir::Instruction* instruction,
|
||||
ir::Loop* loop);
|
||||
bool UsedInContinueOrConditionBlock(opt::Instruction* instruction,
|
||||
opt::Loop* loop);
|
||||
|
||||
// Remove entries in |instructions| that are not used in the continue or
|
||||
// condition block of |loop|.
|
||||
void RemoveIfNotUsedContinueOrConditionBlock(
|
||||
std::vector<ir::Instruction*>* instructions, ir::Loop* loop);
|
||||
std::vector<opt::Instruction*>* instructions, opt::Loop* loop);
|
||||
|
||||
// Returns |true| if |instruction| is used in |loop|.
|
||||
bool IsUsedInLoop(ir::Instruction* instruction, ir::Loop* loop);
|
||||
bool IsUsedInLoop(opt::Instruction* instruction, opt::Loop* loop);
|
||||
|
||||
// Returns |true| if |loop| has at least one barrier or function call.
|
||||
bool ContainsBarriersOrFunctionCalls(ir::Loop* loop);
|
||||
bool ContainsBarriersOrFunctionCalls(opt::Loop* loop);
|
||||
|
||||
// Get all instructions in the |loop| (except in the latch block) that have
|
||||
// the opcode |opcode|.
|
||||
std::pair<std::vector<ir::Instruction*>, std::vector<ir::Instruction*>>
|
||||
GetLoadsAndStoresInLoop(ir::Loop* loop);
|
||||
std::pair<std::vector<opt::Instruction*>, std::vector<opt::Instruction*>>
|
||||
GetLoadsAndStoresInLoop(opt::Loop* loop);
|
||||
|
||||
// Given a vector of memory operations (OpLoad/OpStore), constructs a map from
|
||||
// variables to the loads/stores that those variables.
|
||||
std::map<ir::Instruction*, std::vector<ir::Instruction*>> LocationToMemOps(
|
||||
const std::vector<ir::Instruction*>& mem_ops);
|
||||
std::map<opt::Instruction*, std::vector<opt::Instruction*>> LocationToMemOps(
|
||||
const std::vector<opt::Instruction*>& mem_ops);
|
||||
|
||||
ir::IRContext* context_;
|
||||
opt::IRContext* context_;
|
||||
|
||||
// The original loops to be fused.
|
||||
ir::Loop* loop_0_;
|
||||
ir::Loop* loop_1_;
|
||||
opt::Loop* loop_0_;
|
||||
opt::Loop* loop_1_;
|
||||
|
||||
// The function that contains |loop_0_| and |loop_1_|.
|
||||
ir::Function* containing_function_ = nullptr;
|
||||
opt::Function* containing_function_ = nullptr;
|
||||
|
||||
// The induction variables for |loop_0_| and |loop_1_|.
|
||||
ir::Instruction* induction_0_ = nullptr;
|
||||
ir::Instruction* induction_1_ = nullptr;
|
||||
opt::Instruction* induction_0_ = nullptr;
|
||||
opt::Instruction* induction_1_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -22,21 +22,21 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status LoopFusionPass::Process(ir::IRContext* c) {
|
||||
Pass::Status LoopFusionPass::Process(opt::IRContext* c) {
|
||||
bool modified = false;
|
||||
ir::Module* module = c->module();
|
||||
opt::Module* module = c->module();
|
||||
|
||||
// Process each function in the module
|
||||
for (ir::Function& f : *module) {
|
||||
for (opt::Function& f : *module) {
|
||||
modified |= ProcessFunction(&f);
|
||||
}
|
||||
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
bool LoopFusionPass::ProcessFunction(ir::Function* function) {
|
||||
bool LoopFusionPass::ProcessFunction(opt::Function* function) {
|
||||
auto c = function->context();
|
||||
ir::LoopDescriptor& ld = *c->GetLoopDescriptor(function);
|
||||
opt::LoopDescriptor& ld = *c->GetLoopDescriptor(function);
|
||||
|
||||
// If a loop doesn't have a preheader needs then it needs to be created. Make
|
||||
// sure to return Status::SuccessWithChange in that case.
|
||||
|
@ -34,12 +34,12 @@ class LoopFusionPass : public Pass {
|
||||
// Processes the given |module|. Returns Status::Failure if errors occur when
|
||||
// processing. Returns the corresponding Status::Success if processing is
|
||||
// succesful to indicate whether changes have been made to the modue.
|
||||
Status Process(ir::IRContext* c) override;
|
||||
Status Process(opt::IRContext* c) override;
|
||||
|
||||
private:
|
||||
// Fuse loops in |function| if compatible, legal and the fused loop won't use
|
||||
// too many registers.
|
||||
bool ProcessFunction(ir::Function* function);
|
||||
bool ProcessFunction(opt::Function* function);
|
||||
|
||||
// The maximum number of registers a fused loop is allowed to use.
|
||||
size_t max_registers_per_loop_;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user