set_spec_constant_default_value_pass.cpp
optimizer.cpp
mem_pass.cpp
+ pass.cpp
pass_manager.cpp
strip_debug_info_pass.cpp
types.cpp
namespace {
-const uint32_t kEntryPointFunctionIdInIdx = 1;
const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kExtInstSetIdInIndx = 0;
const uint32_t kExtInstInstructionInIndx = 1;
bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- assert(varInst->opcode() == SpvOpVariable);
+ const SpvOp op = varInst->opcode();
+ if (op != SpvOpVariable && op != SpvOpFunctionParameter)
+ return false;
const uint32_t varTypeId = varInst->type_id();
const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
+ if (varTypeInst->opcode() != SpvOpTypePointer)
+ return false;
return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
SpvStorageClassFunction;
}
} break;
case SpvOpLoad:
break;
- // Assume it stores eg frexp, modf, function call
+ // If default, assume it stores eg frexp, modf, function call
case SpvOpStore:
default: {
if (live_insts_.find(u.inst) == live_insts_.end())
worklist_.push(&inst);
} break;
default: {
- // eg. control flow, function call, atomics
+ // eg. control flow, function call, atomics, function param,
+ // function return
// TODO(greg-lunarg): function calls live only if write to non-local
if (!IsCombinator(op))
worklist_.push(&inst);
ProcessLoad(varId);
});
}
+ // If function parameter, treat as if it's result id is loaded from
+ else if (liveInst->opcode() == SpvOpFunctionParameter) {
+ ProcessLoad(liveInst->result_id());
+ }
worklist_.pop();
}
// Mark all non-live instructions dead
void AggressiveDCEPass::Initialize(ir::Module* module) {
module_ = module;
- // Initialize id-to-function map
- id2function_.clear();
- for (auto& fn : *module_)
- id2function_[fn.result_id()] = &fn;
-
// Clear collections
worklist_ = std::queue<ir::Instruction*>{};
live_insts_.clear();
// Initialize combinator whitelists
InitCombinatorSets();
// Process all entry point functions
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
- modified = AggressiveDCE(fn) || modified;
- }
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return AggressiveDCE(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
// to the live instruction worklist.
void AddStores(uint32_t ptrId);
- // Return true if variable with |varId| is function scope
+ // Return true if object with |varId| is function scope variable or
+ // function parameter with pointer type.
bool IsLocalVar(uint32_t varId);
// Initialize combinator data structures
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Live Instruction Worklist. An instruction is added to this list
// if it might have a side effect, either directly or indirectly.
// If we don't know, then add it to this list. Instructions are
namespace spvtools {
namespace opt {
-namespace {
-
-const int kEntryPointFunctionIdInIdx = 1;
-
-} // anonymous namespace
-
bool BlockMergePass::IsLoopHeader(ir::BasicBlock* block_ptr) {
auto iItr = block_ptr->tail();
if (iItr == block_ptr->begin())
module_ = module;
- // Initialize function and block maps
- id2function_.clear();
- for (auto& fn : *module_)
- id2function_[fn.result_id()] = &fn;
-
// TODO(greg-lunarg): Reuse def/use from previous passes
def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module_));
// Do not process if any disallowed extensions are enabled
if (!AllExtensionsSupported())
return Status::SuccessWithoutChange;
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
- modified = MergeBlocks(fn) || modified;
- }
+ // Process all entry point functions.
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return MergeBlocks(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
// Def-Uses for the module we are processing
std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
};
namespace {
-const uint32_t kEntryPointFunctionIdInIdx = 1;
const uint32_t kAccessChainPtrIdInIdx = 0;
const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kTypePointerTypeIdInIdx = 1;
}
ir::Instruction* CommonUniformElimPass::GetPtr(
- ir::Instruction* ip, uint32_t* varId) {
+ ir::Instruction* ip, uint32_t* objId) {
const SpvOp op = ip->opcode();
assert(op == SpvOpStore || op == SpvOpLoad);
- *varId = ip->GetSingleWordInOperand(
+ *objId = ip->GetSingleWordInOperand(
op == SpvOpStore ? kStorePtrIdInIdx : kLoadPtrIdInIdx);
- ir::Instruction* ptrInst = def_use_mgr_->GetDef(*varId);
+ ir::Instruction* ptrInst = def_use_mgr_->GetDef(*objId);
while (ptrInst->opcode() == SpvOpCopyObject) {
- *varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
- ptrInst = def_use_mgr_->GetDef(*varId);
+ *objId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
+ ptrInst = def_use_mgr_->GetDef(*objId);
}
- ir::Instruction* varInst = ptrInst;
- while (varInst->opcode() != SpvOpVariable) {
- if (IsNonPtrAccessChain(varInst->opcode())) {
- *varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
+ ir::Instruction* objInst = ptrInst;
+ while (objInst->opcode() != SpvOpVariable &&
+ objInst->opcode() != SpvOpFunctionParameter) {
+ if (IsNonPtrAccessChain(objInst->opcode())) {
+ *objId = objInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
}
else {
- assert(varInst->opcode() == SpvOpCopyObject);
- *varId = varInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
+ assert(objInst->opcode() == SpvOpCopyObject);
+ *objId = objInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
}
- varInst = def_use_mgr_->GetDef(*varId);
+ objInst = def_use_mgr_->GetDef(*objId);
}
return ptrInst;
}
bool CommonUniformElimPass::IsUniformVar(uint32_t varId) {
const ir::Instruction* varInst =
def_use_mgr_->id_to_defs().find(varId)->second;
- assert(varInst->opcode() == SpvOpVariable);
+ if (varInst->opcode() != SpvOpVariable)
+ return false;
const uint32_t varTypeId = varInst->type_id();
const ir::Instruction* varTypeInst =
def_use_mgr_->id_to_defs().find(varTypeId)->second;
module_ = module;
// Initialize function and block maps
- id2function_.clear();
id2block_.clear();
- for (auto& fn : *module_) {
- id2function_[fn.result_id()] = &fn;
+ for (auto& fn : *module_)
for (auto& blk : fn)
id2block_[blk.id()] = &blk;
- }
// Clear collections
block2structured_succs_.clear();
inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
return Status::SuccessWithoutChange;
// Process entry point functions
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
- modified = EliminateCommonUniform(fn) || modified;
- }
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return EliminateCommonUniform(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
FinalizeNextId(module_);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
// Return true if |block_ptr| is loop header block
bool IsLoopHeader(ir::BasicBlock* block_ptr);
- // Given a load or store pointed at by |ip|, return the pointer
- // instruction. Also return the variable's id in |varId|.
- ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* varId);
+ // 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);
// Return true if variable is uniform
bool IsUniformVar(uint32_t varId);
// Def-Uses for the module we are processing
std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Map from block's label id to block.
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
namespace {
-const uint32_t kEntryPointFunctionIdInIdx = 1;
const uint32_t kBranchCondConditionalIdInIdx = 0;
const uint32_t kBranchCondTrueLabIdInIdx = 1;
const uint32_t kBranchCondFalseLabIdInIdx = 2;
module_ = module;
// Initialize function and block maps
- id2function_.clear();
id2block_.clear();
block2structured_succs_.clear();
- for (auto& fn : *module_) {
- // Initialize function and block maps.
- id2function_[fn.result_id()] = &fn;
- for (auto& blk : fn) {
+
+ // Initialize block map
+ for (auto& fn : *module_)
+ for (auto& blk : fn)
id2block_[blk.id()] = &blk;
- }
- }
// TODO(greg-lunarg): Reuse def/use from previous passes
def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module_));
// Collect all named and decorated ids
FindNamedOrDecoratedIds();
// Process all entry point functions
- bool modified = false;
- for (const auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
- modified = EliminateDeadBranches(fn) || modified;
- }
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return EliminateDeadBranches(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Map from block's label id to block.
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
namespace spvtools {
namespace opt {
-namespace {
-
-const int kEntryPointFunctionIdInIdx = 1;
-
-} // anonymous namespace
-
bool InlineExhaustivePass::InlineExhaustive(ir::Function* func) {
bool modified = false;
// Using block iterators here because of block erasures and insertions.
};
Pass::Status InlineExhaustivePass::ProcessImpl() {
- // Do exhaustive inlining on each entry point function in module
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordOperand(kEntryPointFunctionIdInIdx)];
- modified = InlineExhaustive(fn) || modified;
- }
-
+ // Attempt exhaustive inlining on each entry point function in module
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return InlineExhaustive(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
FinalizeNextId(module_);
-
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
#include "iterator.h"
-static const int kSpvEntryPointFunctionId = 1;
-static const int kSpvExtractCompositeId = 0;
-static const int kSpvInsertObjectId = 0;
-static const int kSpvInsertCompositeId = 1;
-
namespace spvtools {
namespace opt {
+namespace {
+
+const uint32_t kExtractCompositeIdInIdx = 0;
+const uint32_t kInsertObjectIdInIdx = 0;
+const uint32_t kInsertCompositeIdInIdx = 1;
+
+} // anonymous namespace
+
bool InsertExtractElimPass::ExtInsMatch(const ir::Instruction* extInst,
const ir::Instruction* insInst) const {
if (extInst->NumInOperands() != insInst->NumInOperands() - 1)
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
switch (ii->opcode()) {
case SpvOpCompositeExtract: {
- uint32_t cid = ii->GetSingleWordInOperand(kSpvExtractCompositeId);
+ uint32_t cid = ii->GetSingleWordInOperand(kExtractCompositeIdInIdx);
ir::Instruction* cinst = def_use_mgr_->GetDef(cid);
uint32_t replId = 0;
while (cinst->opcode() == SpvOpCompositeInsert) {
if (ExtInsConflict(&*ii, cinst))
break;
if (ExtInsMatch(&*ii, cinst)) {
- replId = cinst->GetSingleWordInOperand(kSpvInsertObjectId);
+ replId = cinst->GetSingleWordInOperand(kInsertObjectIdInIdx);
break;
}
- cid = cinst->GetSingleWordInOperand(kSpvInsertCompositeId);
+ cid = cinst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
cinst = def_use_mgr_->GetDef(cid);
}
if (replId != 0) {
module_ = module;
- // Initialize function and block maps
- id2function_.clear();
- for (auto& fn : *module_)
- id2function_[fn.result_id()] = &fn;
-
// Do def/use on whole module
def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module_));
if (!AllExtensionsSupported())
return Status::SuccessWithoutChange;
// Process all entry point functions.
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordOperand(kSpvEntryPointFunctionId)];
- modified = EliminateInsertExtract(fn) || modified;
- }
-
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return EliminateInsertExtract(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
// Def-Uses for the module we are processing
std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
};
namespace {
-const uint32_t kEntryPointFunctionIdInIdx = 1;
const uint32_t kStoreValIdInIdx = 1;
const uint32_t kAccessChainPtrIdInIdx = 0;
const uint32_t kTypePointerTypeIdInIdx = 1;
module_ = module;
- // Initialize function and block maps
- id2function_.clear();
- for (auto& fn : *module_)
- id2function_[fn.result_id()] = &fn;
-
// Initialize Target Variable Caches
seen_target_vars_.clear();
seen_non_target_vars_.clear();
if (inst.opcode() == SpvOpTypeInt &&
inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
return Status::SuccessWithoutChange;
-
// Do not process if module contains OpGroupDecorate. Additional
// support required in KillNamesAndDecorates().
// TODO(greg-lunarg): Add support for OpGroupDecorate
// Collect all named and decorated ids
FindNamedOrDecoratedIds();
// Process all entry point functions.
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
- modified = ConvertLocalAccessChains(fn) || modified;
- }
-
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return ConvertLocalAccessChains(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
FinalizeNextId(module_);
-
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
const char* name() const override { return "convert-local-access-chains"; }
Status Process(ir::Module*) override;
+ using ProcessFunction = std::function<bool(ir::Function*)>;
+
private:
// Return true if all refs through |ptrId| are only loads or stores and
// cache ptrId in supported_ref_ptrs_.
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Variables with only supported references, ie. loads and stores using
// variable directly or through non-ptr access chains.
std::unordered_set<uint32_t> supported_ref_ptrs_;
namespace {
-const uint32_t kEntryPointFunctionIdInIdx = 1;
const uint32_t kStoreValIdInIdx = 1;
} // anonymous namespace
module_ = module;
- // Initialize function and block maps
- id2function_.clear();
- for (auto& fn : *module_)
- id2function_[fn.result_id()] = &fn;
-
// Initialize Target Type Caches
seen_target_vars_.clear();
seen_non_target_vars_.clear();
// Collect all named and decorated ids
FindNamedOrDecoratedIds();
// Process all entry point functions
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
- modified = LocalSingleBlockLoadStoreElim(fn) || modified;
- }
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return LocalSingleBlockLoadStoreElim(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
FinalizeNextId(module_);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Map from function scope variable to a store 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
namespace {
-const uint32_t kEntryPointFunctionIdInIdx = 1;
const uint32_t kStoreValIdInIdx = 1;
} // anonymous namespace
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
if (non_ssa_vars_.find(varId) != non_ssa_vars_.end())
continue;
- if (!HasOnlySupportedRefs(varId)) {
- non_ssa_vars_.insert(varId);
- continue;
- }
if (ptrInst->opcode() != SpvOpVariable) {
non_ssa_vars_.insert(varId);
ssa_var2store_.erase(varId);
non_ssa_vars_.insert(varId);
continue;
}
+ if (!HasOnlySupportedRefs(varId)) {
+ non_ssa_vars_.insert(varId);
+ continue;
+ }
// Ignore variables with multiple stores
if (ssa_var2store_.find(varId) != ssa_var2store_.end()) {
non_ssa_vars_.insert(varId);
module_ = module;
// Initialize function and block maps
- id2function_.clear();
label2block_.clear();
for (auto& fn : *module_) {
- id2function_[fn.result_id()] = &fn;
for (auto& blk : fn) {
uint32_t bid = blk.id();
label2block_[bid] = &blk;
// Collect all named and decorated ids
FindNamedOrDecoratedIds();
// Process all entry point functions
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
- modified = LocalSingleStoreElim(fn) || modified;
- }
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return LocalSingleStoreElim(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
FinalizeNextId(module_);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Map from block's label id to block
std::unordered_map<uint32_t, ir::BasicBlock*> label2block_;
namespace {
-const uint32_t kEntryPointFunctionIdInIdx = 1;
const uint32_t kStoreValIdInIdx = 1;
const uint32_t kTypePointerTypeIdInIdx = 1;
const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module_));
// Initialize function and block maps
- id2function_.clear();
id2block_.clear();
block2structured_succs_.clear();
- for (auto& fn : *module_) {
- id2function_[fn.result_id()] = &fn;
+ for (auto& fn : *module_)
for (auto& blk : fn)
id2block_[blk.id()] = &blk;
- }
// Clear collections
seen_target_vars_.clear();
// Collect all named and decorated ids
FindNamedOrDecoratedIds();
// Process functions
- bool modified = false;
- for (auto& e : module_->entry_points()) {
- ir::Function* fn =
- id2function_[e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)];
- modified = EliminateMultiStoreLocal(fn) || modified;
- }
+ ProcessFunction pfn = [this](ir::Function* fp) {
+ return EliminateMultiStoreLocal(fp);
+ };
+ bool modified = ProcessEntryPointCallTree(pfn, module_);
FinalizeNextId(module_);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
void Initialize(ir::Module* module);
Pass::Status ProcessImpl();
- // Map from function's result id to function
- std::unordered_map<uint32_t, ir::Function*> id2function_;
-
// Map from block's label id to block.
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
ptrInst = def_use_mgr_->GetDef(varId);
}
const SpvOp op = ptrInst->opcode();
- return op == SpvOpVariable || IsNonPtrAccessChain(op);
+ if (op == SpvOpVariable || IsNonPtrAccessChain(op))
+ return true;
+ if (op != SpvOpFunctionParameter)
+ return false;
+ const uint32_t varTypeId = ptrInst->type_id();
+ const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
+ return varTypeInst->opcode() == SpvOpTypePointer;
}
ir::Instruction* MemPass::GetPtr(
ptrInst = def_use_mgr_->GetDef(*varId);
}
ir::Instruction* varInst = ptrInst;
- while (varInst->opcode() != SpvOpVariable) {
+ while (varInst->opcode() != SpvOpVariable &&
+ varInst->opcode() != SpvOpFunctionParameter) {
if (IsNonPtrAccessChain(varInst->opcode())) {
*varId = varInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
}
}
bool MemPass::IsLiveVar(uint32_t varId) const {
- // non-function scope vars are live
const ir::Instruction* varInst = def_use_mgr_->GetDef(varId);
- assert(varInst->opcode() == SpvOpVariable);
+ // assume live if not a variable eg. function parameter
+ if (varInst->opcode() != SpvOpVariable)
+ return true;
+ // non-function scope vars are live
const uint32_t varTypeId = varInst->type_id();
const ir::Instruction* varTypeInst = def_use_mgr_->GetDef(varTypeId);
if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
bool IsNonPtrAccessChain(const SpvOp opcode) const;
// Given the id |ptrId|, return true if the top-most non-CopyObj is
- // a pointer.
+ // a variable, a non-ptr access chain or a parameter of pointer type.
bool IsPtr(uint32_t ptrId);
// Given the id of a pointer |ptrId|, return the top-most non-CopyObj.
--- /dev/null
+// Copyright (c) 2017 The Khronos Group Inc.
+// Copyright (c) 2017 Valve Corporation
+// Copyright (c) 2017 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "pass.h"
+
+#include "iterator.h"
+
+namespace spvtools {
+namespace opt {
+
+namespace {
+
+const uint32_t kEntryPointFunctionIdInIdx = 1;
+
+} // namespace anonymous
+
+void Pass::AddCalls(ir::Function* func,
+ std::queue<uint32_t>* todo) {
+ for (auto bi = func->begin(); bi != func->end(); ++bi)
+ for (auto ii = bi->begin(); ii != bi->end(); ++ii)
+ if (ii->opcode() == SpvOpFunctionCall)
+ todo->push(ii->GetSingleWordInOperand(0));
+}
+
+bool Pass::ProcessEntryPointCallTree(
+ ProcessFunction& pfn, ir::Module* module) {
+ // Map from function's result id to function
+ std::unordered_map<uint32_t, ir::Function*> id2function;
+ for (auto& fn : *module)
+ id2function[fn.result_id()] = &fn;
+ // Process call tree
+ bool modified = false;
+ std::queue<uint32_t> todo;
+ std::unordered_set<uint32_t> done;
+ for (auto& e : module->entry_points())
+ todo.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx));
+ while (!todo.empty()) {
+ const uint32_t fi = todo.front();
+ if (done.find(fi) == done.end()) {
+ ir::Function* fn = id2function[fi];
+ modified = pfn(fn) || modified;
+ done.insert(fi);
+ AddCalls(fn, &todo);
+ }
+ todo.pop();
+ }
+ return modified;
+}
+
+} // namespace opt
+} // namespace spvtools
+
#ifndef LIBSPIRV_OPT_PASS_H_
#define LIBSPIRV_OPT_PASS_H_
+#include <algorithm>
+#include <map>
+#include <queue>
+#include <unordered_map>
+#include <unordered_set>
+
#include <utility>
#include "module.h"
SuccessWithoutChange = 0x11,
};
+ using ProcessFunction = std::function<bool(ir::Function*)>;
+
// Constructs a new pass.
//
// The constructed instance will have an empty message consumer, which just
// Returns the reference to the message consumer for this pass.
const MessageConsumer& consumer() const { return consumer_; }
+ // Add to |todo| all ids of functions called in |func|.
+ void AddCalls(ir::Function* func, std::queue<uint32_t>* todo);
+
+ //
+ bool ProcessEntryPointCallTree(ProcessFunction& pfn, ir::Module* module);
+
// 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 are made to the module.
defs_before + func_before, defs_after + func_after, true, true);
}
+TEST_F(AggressiveDCETest, NoParamElim) {
+ // This demonstrates that unused parameters are not eliminated, but
+ // dead uses of them are.
+ // #version 140
+ //
+ // in vec4 BaseColor;
+ //
+ // vec4 foo(vec4 v1, vec4 v2)
+ // {
+ // vec4 t = -v1;
+ // return v2;
+ // }
+ //
+ // void main()
+ // {
+ // vec4 dead;
+ // gl_FragColor = foo(dead, BaseColor);
+ // }
+
+ const std::string defs_before =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %foo_vf4_vf4_ "foo(vf4;vf4;"
+OpName %v1 "v1"
+OpName %v2 "v2"
+OpName %t "t"
+OpName %gl_FragColor "gl_FragColor"
+OpName %dead "dead"
+OpName %BaseColor "BaseColor"
+OpName %param "param"
+OpName %param_0 "param"
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%17 = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%main = OpFunction %void None %13
+%20 = OpLabel
+%dead = OpVariable %_ptr_Function_v4float Function
+%param = OpVariable %_ptr_Function_v4float Function
+%param_0 = OpVariable %_ptr_Function_v4float Function
+%21 = OpLoad %v4float %dead
+OpStore %param %21
+%22 = OpLoad %v4float %BaseColor
+OpStore %param_0 %22
+%23 = OpFunctionCall %v4float %foo_vf4_vf4_ %param %param_0
+OpStore %gl_FragColor %23
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string defs_after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %foo_vf4_vf4_ "foo(vf4;vf4;"
+OpName %v1 "v1"
+OpName %v2 "v2"
+OpName %gl_FragColor "gl_FragColor"
+OpName %dead "dead"
+OpName %BaseColor "BaseColor"
+OpName %param "param"
+OpName %param_0 "param"
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%17 = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%main = OpFunction %void None %13
+%20 = OpLabel
+%dead = OpVariable %_ptr_Function_v4float Function
+%param = OpVariable %_ptr_Function_v4float Function
+%param_0 = OpVariable %_ptr_Function_v4float Function
+%21 = OpLoad %v4float %dead
+OpStore %param %21
+%22 = OpLoad %v4float %BaseColor
+OpStore %param_0 %22
+%23 = OpFunctionCall %v4float %foo_vf4_vf4_ %param %param_0
+OpStore %gl_FragColor %23
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string func_before =
+ R"(%foo_vf4_vf4_ = OpFunction %v4float None %17
+%v1 = OpFunctionParameter %_ptr_Function_v4float
+%v2 = OpFunctionParameter %_ptr_Function_v4float
+%24 = OpLabel
+%t = OpVariable %_ptr_Function_v4float Function
+%25 = OpLoad %v4float %v1
+%26 = OpFNegate %v4float %25
+OpStore %t %26
+%27 = OpLoad %v4float %v2
+OpReturnValue %27
+OpFunctionEnd
+)";
+
+ const std::string func_after =
+ R"(%foo_vf4_vf4_ = OpFunction %v4float None %17
+%v1 = OpFunctionParameter %_ptr_Function_v4float
+%v2 = OpFunctionParameter %_ptr_Function_v4float
+%24 = OpLabel
+%27 = OpLoad %v4float %v2
+OpReturnValue %27
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::AggressiveDCEPass>(
+ defs_before + func_before, defs_after + func_after, true, true);
+}
+
TEST_F(AggressiveDCETest, ElimOpaque) {
// SPIR-V not representable from GLSL; not generatable from HLSL
// for the moment.
)";
const std::string func_after =
- R"(%main = OpFunction %void None %9
+ R"(%main = OpFunction %void None %9
%25 = OpLabel
%26 = OpLoad %v2float %texCoords
%29 = OpLoad %15 %sampler15
defs_before + func_before, defs_after + func_after, true, true);
}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Check that logical addressing required
predefs_before + before, predefs_after + after, true, true);
}
+TEST_F(LocalSingleBlockLoadStoreElimTest, PositiveAndNegativeCallTree) {
+ // Note that the call tree function bar is optimized, but foo is not
+ //
+ // #version 140
+ //
+ // in vec4 BaseColor;
+ //
+ // vec4 foo(vec4 v1)
+ // {
+ // vec4 t = v1;
+ // return t;
+ // }
+ //
+ // vec4 bar(vec4 v1)
+ // {
+ // vec4 t = v1;
+ // return t;
+ // }
+ //
+ // void main()
+ // {
+ // gl_FragColor = bar(BaseColor);
+ // }
+
+ const std::string predefs_before =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %foo_vf4_ "foo(vf4;"
+OpName %v1 "v1"
+OpName %bar_vf4_ "bar(vf4;"
+OpName %v1_0 "v1"
+OpName %t "t"
+OpName %t_0 "t"
+OpName %gl_FragColor "gl_FragColor"
+OpName %BaseColor "BaseColor"
+OpName %param "param"
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%17 = OpTypeFunction %v4float %_ptr_Function_v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%main = OpFunction %void None %13
+%20 = OpLabel
+%param = OpVariable %_ptr_Function_v4float Function
+%21 = OpLoad %v4float %BaseColor
+OpStore %param %21
+%22 = OpFunctionCall %v4float %bar_vf4_ %param
+OpStore %gl_FragColor %22
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string predefs_after =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %foo_vf4_ "foo(vf4;"
+OpName %v1 "v1"
+OpName %bar_vf4_ "bar(vf4;"
+OpName %v1_0 "v1"
+OpName %t "t"
+OpName %gl_FragColor "gl_FragColor"
+OpName %BaseColor "BaseColor"
+OpName %param "param"
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%17 = OpTypeFunction %v4float %_ptr_Function_v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%main = OpFunction %void None %13
+%20 = OpLabel
+%param = OpVariable %_ptr_Function_v4float Function
+%21 = OpLoad %v4float %BaseColor
+OpStore %param %21
+%22 = OpFunctionCall %v4float %bar_vf4_ %param
+OpStore %gl_FragColor %22
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string before =
+ R"(%foo_vf4_ = OpFunction %v4float None %17
+%v1 = OpFunctionParameter %_ptr_Function_v4float
+%23 = OpLabel
+%t = OpVariable %_ptr_Function_v4float Function
+%24 = OpLoad %v4float %v1
+OpStore %t %24
+%25 = OpLoad %v4float %t
+OpReturnValue %25
+OpFunctionEnd
+%bar_vf4_ = OpFunction %v4float None %17
+%v1_0 = OpFunctionParameter %_ptr_Function_v4float
+%26 = OpLabel
+%t_0 = OpVariable %_ptr_Function_v4float Function
+%27 = OpLoad %v4float %v1_0
+OpStore %t_0 %27
+%28 = OpLoad %v4float %t_0
+OpReturnValue %28
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(%foo_vf4_ = OpFunction %v4float None %17
+%v1 = OpFunctionParameter %_ptr_Function_v4float
+%23 = OpLabel
+%t = OpVariable %_ptr_Function_v4float Function
+%24 = OpLoad %v4float %v1
+OpStore %t %24
+%25 = OpLoad %v4float %t
+OpReturnValue %25
+OpFunctionEnd
+%bar_vf4_ = OpFunction %v4float None %17
+%v1_0 = OpFunctionParameter %_ptr_Function_v4float
+%26 = OpLabel
+%27 = OpLoad %v4float %v1_0
+OpReturnValue %27
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::LocalSingleBlockLoadStoreElimPass>(
+ predefs_before + before, predefs_after + after, true, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Other target variable types