Adding additional functionality to ADCE.

Modified ADCE to remove dead globals.
* Entry point and execution mode instructions are marked as alive
* Reachable functions and their parameters are marked as alive
* Instruction deletion now deferred until the end of the pass
* Eliminated dead insts set, added IsDead to calculate that value
instead
* Ported applicable dead variable elimination tests
* Ported dead constant elim tests

Added dead function elimination to ADCE
* ported dead function elim tests

Added handling of decoration groups in ADCE
* Uses a custom sorter to traverse decorations in a specific order
* Simplifies necessary checks

Updated -O and -Os pass lists.
This commit is contained in:
Alan Baker 2017-12-18 12:13:10 -05:00
parent d54a286c75
commit 3a054e1ddc
6 changed files with 1940 additions and 260 deletions

View File

@ -385,7 +385,7 @@ Optimizer::PassToken CreateInsertExtractElimPass();
Optimizer::PassToken CreateCommonUniformElimPass();
// Create aggressive dead code elimination pass
// This pass eliminates unused code from functions. In addition,
// This pass eliminates unused code from the module. In addition,
// it detects and eliminates code which may have spurious uses but which do
// not contribute to the output of the function. The most common cause of
// such code sequences is summations in loops whose result is no longer used
@ -393,8 +393,9 @@ Optimizer::PassToken CreateCommonUniformElimPass();
// time cost over standard dead code elimination.
//
// This pass only processes entry point functions. It also only processes
// shaders with relaxed logical addressing (see opt/instruction.h). It currently
// will not process functions with function calls.
// shaders with relaxed logical addressing (see opt/instruction.h). It
// currently will not process functions with function calls. Unreachable
// functions are deleted.
//
// This pass will be made more effective by first running passes that remove
// dead control flow and inlines function calls.

View File

@ -19,6 +19,7 @@
#include "cfa.h"
#include "iterator.h"
#include "latest_version_glsl_std_450_header.h"
#include "reflect.h"
#include <stack>
@ -33,6 +34,57 @@ const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
// Sorting functor to present annotation instructions in an easy-to-process
// order. The functor orders by opcode first and falls back on unique id
// ordering if both instructions have the same opcode.
//
// Desired priority:
// SpvOpGroupDecorate
// SpvOpGroupMemberDecorate
// SpvOpDecorate
// SpvOpMemberDecorate
// SpvOpDecorateId
// SpvOpDecorationGroup
struct DecorationLess {
bool operator()(const ir::Instruction* lhs,
const ir::Instruction* rhs) const {
assert(lhs && rhs);
SpvOp lhsOp = lhs->opcode();
SpvOp rhsOp = rhs->opcode();
if (lhsOp != rhsOp) {
// OpGroupDecorate and OpGroupMember decorate are highest priority to
// eliminate dead targets early and simplify subsequent checks.
if (lhsOp == SpvOpGroupDecorate && rhsOp != SpvOpGroupDecorate)
return true;
if (rhsOp == SpvOpGroupDecorate && lhsOp != SpvOpGroupDecorate)
return false;
if (lhsOp == SpvOpGroupMemberDecorate &&
rhsOp != SpvOpGroupMemberDecorate)
return true;
if (rhsOp == SpvOpGroupMemberDecorate &&
lhsOp != SpvOpGroupMemberDecorate)
return false;
if (lhsOp == SpvOpDecorate && rhsOp != SpvOpDecorate) return true;
if (rhsOp == SpvOpDecorate && lhsOp != SpvOpDecorate) return false;
if (lhsOp == SpvOpMemberDecorate && rhsOp != SpvOpMemberDecorate)
return true;
if (rhsOp == SpvOpMemberDecorate && lhsOp != SpvOpMemberDecorate)
return false;
if (lhsOp == SpvOpDecorateId && rhsOp != SpvOpDecorateId) return true;
if (rhsOp == SpvOpDecorateId && lhsOp != SpvOpDecorateId) return false;
// OpDecorationGroup is lowest priority to ensure use/def chains remain
// usable for instructions that target this group.
if (lhsOp == SpvOpDecorationGroup && rhsOp != SpvOpDecorationGroup)
return true;
if (rhsOp == SpvOpDecorationGroup && lhsOp != SpvOpDecorationGroup)
return false;
}
// Fall back to maintain total ordering (compare unique ids).
return *lhs < *rhs;
}
};
} // namespace
bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
@ -82,13 +134,32 @@ bool AggressiveDCEPass::AllExtensionsSupported() const {
return true;
}
bool AggressiveDCEPass::IsDead(ir::Instruction* inst) {
if (IsLive(inst)) return false;
if (inst->IsBranch() &&
!IsStructuredIfOrLoopHeader(context()->get_instr_block(inst), nullptr,
nullptr, nullptr))
return false;
return true;
}
bool AggressiveDCEPass::IsTargetDead(ir::Instruction* inst) {
const uint32_t tId = inst->GetSingleWordInOperand(0);
const ir::Instruction* tInst = get_def_use_mgr()->GetDef(tId);
if (dead_insts_.find(tInst) != dead_insts_.end()) {
return true;
ir::Instruction* tInst = get_def_use_mgr()->GetDef(tId);
if (ir::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) {
if (user->opcode() == SpvOpGroupDecorate ||
user->opcode() == SpvOpGroupMemberDecorate)
dead = false;
});
return dead;
}
return false;
return IsDead(tInst);
}
void AggressiveDCEPass::ProcessLoad(uint32_t varId) {
@ -106,6 +177,7 @@ bool AggressiveDCEPass::IsStructuredIfOrLoopHeader(ir::BasicBlock* bp,
ir::Instruction** mergeInst,
ir::Instruction** branchInst,
uint32_t* mergeBlockId) {
if (!bp) return false;
ir::Instruction* mi = bp->GetMergeInst();
if (mi == nullptr) return false;
ir::Instruction* bri = &*bp->tail();
@ -158,13 +230,6 @@ void AggressiveDCEPass::ComputeBlock2HeaderMaps(
}
}
void AggressiveDCEPass::ComputeInst2BlockMap(ir::Function* func) {
for (auto& blk : *func) {
blk.ForEachInst(
[&blk, this](ir::Instruction* ip) { inst2block_[ip] = &blk; });
}
}
void AggressiveDCEPass::AddBranch(uint32_t labelId, ir::BasicBlock* bp) {
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
context(), SpvOpBranch, 0, 0,
@ -185,7 +250,7 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
if (op != SpvOpBranchConditional && op != SpvOpBranch) return;
ir::Instruction* branchInst = user;
while (true) {
ir::BasicBlock* blk = inst2block_[branchInst];
ir::BasicBlock* blk = context()->get_instr_block(branchInst);
ir::Instruction* hdrBranch = block2headerBranch_[blk];
if (hdrBranch == nullptr) return;
ir::Instruction* hdrMerge = branch2merge_[hdrBranch];
@ -218,7 +283,7 @@ 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 = inst2block_[user];
ir::BasicBlock* blk = context()->get_instr_block(user);
ir::Instruction* hdrBranch = block2headerBranch_[blk];
if (hdrBranch == nullptr) return;
ir::Instruction* hdrMerge = branch2merge_[hdrBranch];
@ -234,8 +299,14 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
}
bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
// Compute map from instruction to block
ComputeInst2BlockMap(func);
// Mark function parameters as live.
AddToWorklist(&func->DefInst());
func->ForEachParam(
[this](const ir::Instruction* param) {
AddToWorklist(const_cast<ir::Instruction*>(param));
},
false);
// Compute map from block to controlling conditional branch
std::list<ir::BasicBlock*> structuredOrder;
cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
@ -320,12 +391,6 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
// If privates are not like local, add their stores to worklist
if (!private_like_local_)
for (auto& ps : private_stores_) AddToWorklist(ps);
// Add OpGroupDecorates to worklist because they are a pain to remove
// ids from.
// TODO(greg-lunarg): Handle dead ids in OpGroupDecorate
for (auto& ai : get_module()->annotations()) {
if (ai.opcode() == SpvOpGroupDecorate) AddToWorklist(&ai);
}
// Perform closure on live instruction set.
while (!worklist_.empty()) {
ir::Instruction* liveInst = worklist_.front();
@ -338,11 +403,14 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return;
if (!IsLive(inInst)) AddToWorklist(inInst);
});
if (liveInst->type_id() != 0) {
AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id()));
}
// If in a structured if or loop construct, add the controlling
// 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 = inst2block_[liveInst];
ir::BasicBlock* blk = context()->get_instr_block(liveInst);
ir::Instruction* branchInst = block2headerBranch_[blk];
if (branchInst != nullptr && !IsLive(branchInst)) {
AddToWorklist(branchInst);
@ -376,67 +444,19 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
}
worklist_.pop();
}
// Mark all non-live instructions dead except non-structured branches, which
// now should be considered live unless their block is deleted.
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
if (IsLive(&*ii)) continue;
// TODO(greg-lunarg
// https://github.com/KhronosGroup/SPIRV-Tools/issues/1021) This should be
// using ii->IsBranch(), but this code does not handle OpSwitch
// instructions yet.
if ((ii->opcode() == SpvOpBranch ||
ii->opcode() == SpvOpBranchConditional) &&
!IsStructuredIfOrLoopHeader(*bi, nullptr, nullptr, nullptr))
continue;
dead_insts_.insert(&*ii);
}
}
// Remove debug and annotation statements referencing dead instructions.
// This must be done before killing the instructions, otherwise there are
// dead objects in the def/use database.
ir::Instruction* instruction = &*get_module()->debug2_begin();
while (instruction) {
if (instruction->opcode() != SpvOpName) {
instruction = instruction->NextNode();
continue;
}
if (IsTargetDead(instruction)) {
instruction = context()->KillInst(instruction);
modified = true;
} else {
instruction = instruction->NextNode();
}
}
instruction = &*get_module()->annotation_begin();
while (instruction) {
if (instruction->opcode() != SpvOpDecorate &&
instruction->opcode() != SpvOpDecorateId) {
instruction = instruction->NextNode();
continue;
}
if (IsTargetDead(instruction)) {
instruction = context()->KillInst(instruction);
modified = true;
} else {
instruction = instruction->NextNode();
}
}
// 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) {
if (dead_insts_.find(inst) == dead_insts_.end()) return;
if (!IsDead(inst)) return;
if (inst->opcode() == SpvOpLabel) return;
// If dead instruction is selection merge, remember merge block
// for new branch at end of block
if (inst->opcode() == SpvOpSelectionMerge ||
inst->opcode() == SpvOpLoopMerge)
mergeBlockId = inst->GetSingleWordInOperand(0);
context()->KillInst(inst);
to_kill_.push_back(inst);
modified = true;
});
// If a structured if or loop was deleted, add a branch to its merge
@ -450,8 +470,6 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
++bi;
}
}
// Cleanup all CFG including all unreachable blocks
CFGCleanup(func);
return modified;
}
@ -463,12 +481,20 @@ void AggressiveDCEPass::Initialize(ir::IRContext* c) {
worklist_ = std::queue<ir::Instruction*>{};
live_insts_.clear();
live_local_vars_.clear();
dead_insts_.clear();
// Initialize extensions whitelist
InitExtensions();
}
void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
for (auto& exec : get_module()->execution_modes()) {
AddToWorklist(&exec);
}
for (auto& entry : get_module()->entry_points()) {
AddToWorklist(&entry);
}
}
Pass::Status AggressiveDCEPass::ProcessImpl() {
// Current functionality assumes shader capability
// TODO(greg-lunarg): Handle additional capabilities
@ -482,12 +508,158 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
// If any extensions in the module are not explicitly supported,
// return unmodified.
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
// Process all entry point functions
// Eliminate Dead functions.
bool modified = EliminateDeadFunctions();
InitializeModuleScopeLiveInstructions();
// Process all entry point functions.
ProcessFunction pfn = [this](ir::Function* fp) { return AggressiveDCE(fp); };
bool modified = ProcessEntryPointCallTree(pfn, get_module());
modified |= ProcessEntryPointCallTree(pfn, get_module());
// Process module-level instructions. Now that all live instructions have
// been marked, it is safe to remove dead global values.
modified |= ProcessGlobalValues();
// Kill all dead instructions.
for (auto inst : to_kill_) {
context()->KillInst(inst);
}
// Cleanup all CFG including all unreachable blocks.
ProcessFunction cleanup = [this](ir::Function* f) { return CFGCleanup(f); };
modified |= ProcessEntryPointCallTree(cleanup, get_module());
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
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) {
live_function_set.insert(fp);
return false;
};
ProcessEntryPointCallTree(mark_live, get_module());
bool modified = false;
for (auto funcIter = get_module()->begin();
funcIter != get_module()->end();) {
if (live_function_set.count(&*funcIter) == 0) {
modified = true;
EliminateFunction(&*funcIter);
funcIter = funcIter.Erase();
} else {
++funcIter;
}
}
return modified;
}
void AggressiveDCEPass::EliminateFunction(ir::Function* func) {
// Remove all of the instruction in the function body
func->ForEachInst(
[this](ir::Instruction* inst) { context()->KillInst(inst); }, true);
}
bool AggressiveDCEPass::ProcessGlobalValues() {
// Remove debug and annotation statements referencing dead instructions.
// 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();
while (instruction) {
if (instruction->opcode() != SpvOpName) {
instruction = instruction->NextNode();
continue;
}
if (IsTargetDead(instruction)) {
instruction = context()->KillInst(instruction);
modified = true;
} else {
instruction = instruction->NextNode();
}
}
// 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;
for (auto& inst : get_module()->annotations()) annotations.push_back(&inst);
std::sort(annotations.begin(), annotations.end(), DecorationLess());
for (auto annotation : annotations) {
switch (annotation->opcode()) {
case SpvOpDecorate:
case SpvOpMemberDecorate:
case SpvOpDecorateId:
if (IsTargetDead(annotation)) context()->KillInst(annotation);
break;
case SpvOpGroupDecorate: {
// Go through the targets of this group decorate. Remove each dead
// target. If all targets are dead, remove this decoration.
bool dead = true;
for (uint32_t i = 1; i < annotation->NumOperands();) {
ir::Instruction* opInst =
get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
if (IsDead(opInst)) {
// Don't increment |i|.
annotation->RemoveOperand(i);
} else {
i++;
dead = false;
}
}
if (dead) context()->KillInst(annotation);
break;
}
case SpvOpGroupMemberDecorate: {
// Go through the targets of this group member decorate. Remove each
// dead target (and member index). If all targets are dead, remove this
// decoration.
bool dead = true;
for (uint32_t i = 1; i < annotation->NumOperands();) {
ir::Instruction* opInst =
get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
if (IsDead(opInst)) {
// Don't increment |i|.
annotation->RemoveOperand(i + 1);
annotation->RemoveOperand(i);
} else {
i += 2;
dead = false;
}
}
if (dead) context()->KillInst(annotation);
break;
}
case SpvOpDecorationGroup:
// By the time we hit decoration groups we've checked everything that
// can target them. So if they have no uses they must be dead.
if (get_def_use_mgr()->NumUsers(annotation) == 0)
context()->KillInst(annotation);
break;
default:
assert(false);
break;
}
}
// Since ADCE is disabled for non-shaders, we don't check for export linkage
// attributes here.
for (auto& val : get_module()->types_values()) {
if (IsDead(&val)) {
to_kill_.push_back(&val);
}
}
return modified;
}
AggressiveDCEPass::AggressiveDCEPass() {}
Pass::Status AggressiveDCEPass::Process(ir::IRContext* c) {

View File

@ -55,18 +55,25 @@ class AggressiveDCEPass : public MemPass {
// Return true if |varId| is variable of function storage class or is
// private variable and privates can be optimized like locals (see
// privates_like_local_)
// privates_like_local_).
bool IsLocalVar(uint32_t varId);
// Return true if |inst| is marked live
bool IsLive(ir::Instruction* inst) {
// Return true if |inst| is marked live.
bool IsLive(const ir::Instruction* inst) const {
return live_insts_.find(inst) != live_insts_.end();
}
// Returns true if |inst| is dead.
bool IsDead(ir::Instruction* inst);
// Adds entry points and execution modes to the worklist for processing with
// the first function.
void InitializeModuleScopeLiveInstructions();
// Add |inst| to worklist_ and live_insts_.
void AddToWorklist(ir::Instruction* inst) {
worklist_.push(inst);
live_insts_.insert(inst);
worklist_.push(inst);
}
// Add all store instruction which use |ptrId|, directly or indirectly,
@ -79,8 +86,9 @@ class AggressiveDCEPass : public MemPass {
// Return true if all extensions in this module are supported by this pass.
bool AllExtensionsSupported() const;
// Returns true if |inst| is dead. An instruction is dead if its result id
// is used in decoration or debug instructions only.
// 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);
// If |varId| is local, mark all stores of varId as live.
@ -89,6 +97,8 @@ class AggressiveDCEPass : public MemPass {
// If |bp| is structured if or loop header block, return true and set
// |mergeInst| to the merge instruction, |branchInst| to the conditional
// branch and |mergeBlockId| to the 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 IsStructuredIfOrLoopHeader(ir::BasicBlock* bp,
ir::Instruction** mergeInst,
ir::Instruction** branchInst,
@ -98,9 +108,6 @@ class AggressiveDCEPass : public MemPass {
// to order blocks.
void ComputeBlock2HeaderMaps(std::list<ir::BasicBlock*>& structuredOrder);
// Initialize inst2block_ for |func|.
void ComputeInst2BlockMap(ir::Function* func);
// Add branch to |labelId| to end of block |bp|.
void AddBranch(uint32_t labelId, ir::BasicBlock* bp);
@ -108,10 +115,20 @@ class AggressiveDCEPass : public MemPass {
// |mergeInst| to worklist if not already live
void AddBreaksAndContinuesToWorklist(ir::Instruction* mergeInst);
// Eliminates dead debug2 and annotation instructions. Marks dead globals for
// removal (e.g. types, constants and variables).
bool ProcessGlobalValues();
// Erases functions that are unreachable from the entry points of the module.
bool EliminateDeadFunctions();
// Removes |func| from the module and deletes all its instructions.
void EliminateFunction(ir::Function* func);
// For function |func|, mark all Stores to non-function-scope variables
// and block terminating instructions as live. Recursively mark the values
// they use. When complete, delete any non-live instructions. Return true
// if the function has been modified.
// they use. When complete, mark any non-live instructions to be deleted.
// Returns true if the function has been modified.
//
// Note: This function does not delete useless control structures. All
// existing control structures will remain. This can leave not-insignificant
@ -147,17 +164,6 @@ class AggressiveDCEPass : public MemPass {
// Map from branch to its associated merge instruction, if any
std::unordered_map<ir::Instruction*, ir::Instruction*> branch2merge_;
// Map from instruction containing block
std::unordered_map<ir::Instruction*, ir::BasicBlock*> inst2block_;
// Map from block's label id to block.
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
// Map from block to its structured successor blocks. See
// ComputeStructuredSuccessors() for definition.
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
block2structured_succs_;
// Store instructions to variables of private storage
std::vector<ir::Instruction*> private_stores_;
@ -167,8 +173,9 @@ class AggressiveDCEPass : public MemPass {
// Live Local Variables
std::unordered_set<uint32_t> live_local_vars_;
// Dead instructions. Use for debug cleanup.
std::unordered_set<const ir::Instruction*> dead_insts_;
// List of instructions to delete. Deletion is delayed until debug and
// annotation instructions are processed.
std::vector<ir::Instruction*> to_kill_;
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;

View File

@ -118,7 +118,7 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
return RegisterPass(CreateRemoveDuplicatesPass())
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
.RegisterPass(CreateEliminateDeadFunctionsPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateScalarReplacementPass())
.RegisterPass(CreateLocalAccessChainConvertPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
@ -134,14 +134,14 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
.RegisterPass(CreateCFGCleanupPass())
// Currently exposing driver bugs resulting in crashes (#946)
// .RegisterPass(CreateCommonUniformElimPass())
.RegisterPass(CreateDeadVariableEliminationPass());
.RegisterPass(CreateAggressiveDCEPass());
}
Optimizer& Optimizer::RegisterSizePasses() {
return RegisterPass(CreateRemoveDuplicatesPass())
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
.RegisterPass(CreateEliminateDeadFunctionsPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateLocalAccessChainConvertPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
@ -156,7 +156,7 @@ Optimizer& Optimizer::RegisterSizePasses() {
.RegisterPass(CreateCFGCleanupPass())
// Currently exposing driver bugs resulting in crashes (#946)
// .RegisterPass(CreateCommonUniformElimPass())
.RegisterPass(CreateDeadVariableEliminationPass());
.RegisterPass(CreateAggressiveDCEPass());
}
bool Optimizer::Run(const uint32_t* original_binary,

File diff suppressed because it is too large Load Diff

View File

@ -154,9 +154,18 @@ class AssemblyBuilder {
return *this;
}
// Pre-pends string to the preamble of the module. Useful for EFFCEE checks.
AssemblyBuilder& PrependPreamble(const std::vector<std::string>& preamble) {
preamble_.insert(preamble_.end(), preamble.begin(), preamble.end());
return *this;
}
// Get the SPIR-V assembly code as string.
std::string GetCode() const {
std::ostringstream ss;
for (const auto& line : preamble_) {
ss << line << std::endl;
}
for (const auto& line : global_preamble_) {
ss << line << std::endl;
}
@ -230,6 +239,8 @@ class AssemblyBuilder {
}
uint32_t spec_id_counter_;
// User-defined preamble.
std::vector<std::string> preamble_;
// The vector that contains common preambles shared across all test SPIR-V
// code.
std::vector<std::string> global_preamble_;