Have all MemPasses preserve the def-use manager.

Originally the passes that extended from MemPass were those that are
of the def-use manager.  I am assuming they would be able to preserve
it because of that.

Added a check to verify consistency of the IRContext. The IRContext
relies on the pass to tell it if something is invalidated.
It is possible that the pass lied.  To help identify those situations,
we will check if the valid analyses are correct after each pass.

This will be enabled by default for the debug build, and disabled in the
production build.  It can be disabled in the debug build by adding
"-DSPIRV_CHECK_CONTEXT=OFF" to the cmake command.
This commit is contained in:
Steven Perron 2017-11-09 11:24:41 -05:00
parent 039c12f096
commit efe12ff5a1
16 changed files with 133 additions and 2 deletions

View File

@ -173,6 +173,13 @@ if ("${SPIRV_SKIP_EXECUTABLES}")
set(SPIRV_SKIP_TESTS ON)
endif()
# Defaults to ON. The checks can be time consuming.
# Turn off if they take too long.
option(SPIRV_CHECK_CONTEXT "In a debug build, check if the IR context is in a valid state." ON)
if (${SPIRV_CHECK_CONTEXT})
add_definitions(-DSPIRV_CHECK_CONTEXT)
endif()
add_subdirectory(external)
add_subdirectory(source)

View File

@ -44,6 +44,10 @@ class AggressiveDCEPass : public MemPass {
const char* name() const override { return "eliminate-dead-code-aggressive"; }
Status Process(ir::IRContext* c) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
private:
// Return true if |varId| is variable of |storageClass|.
bool IsVarOfStorage(uint32_t varId, uint32_t storageClass);

View File

@ -28,6 +28,10 @@ class CFGCleanupPass : public MemPass {
const char* name() const override { return "cfg-cleanup"; }
Status Process(ir::IRContext* c) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
private:
// Initialize the pass.
void Initialize(ir::IRContext* c);

View File

@ -44,6 +44,10 @@ class DeadBranchElimPass : public MemPass {
const char* name() const override { return "eliminate-dead-branches"; }
Status Process(ir::IRContext* context) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
private:
// If |condId| is boolean constant, return conditional value in |condVal| and
// return true, otherwise return false.

View File

@ -29,6 +29,10 @@ class DeadVariableElimination : public MemPass {
const char* name() const override { return "dead-variable-elimination"; }
Status Process(ir::IRContext* c) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
private:
// Deletes the OpVariable instruction who result id is |result_id|.
void DeleteVariable(uint32_t result_id);

View File

@ -151,6 +151,30 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const ir::Instruction* inst) {
}
}
bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) {
if (lhs.id_to_def_ != rhs.id_to_def_) {
return false;
}
for (auto use : lhs.id_to_uses_) {
auto rhs_iter = rhs.id_to_uses_.find(use.first);
if (rhs_iter == rhs.id_to_uses_.end()) {
return false;
}
use.second.sort();
UseList rhs_uselist = rhs_iter->second;
rhs_uselist.sort();
if (use.second != rhs_uselist) {
return false;
}
}
if (lhs.inst_to_used_ids_ != lhs.inst_to_used_ids_) {
return false;
}
return true;
}
} // namespace analysis
} // namespace opt
} // namespace spvtools

View File

@ -37,6 +37,22 @@ struct Use {
// the index of result type id.
};
inline bool operator==(const Use& lhs, const Use& rhs) {
return lhs.inst == rhs.inst && lhs.operand_index == rhs.operand_index;
}
inline bool operator!=(const Use& lhs, const Use& rhs) {
return !(lhs == rhs);
}
inline bool operator<(const Use& lhs, const Use& rhs) {
if (lhs.inst < rhs.inst)
return true;
if (lhs.inst > rhs.inst)
return false;
return lhs.operand_index < rhs.operand_index;
}
using UseList = std::list<Use>;
// A class for analyzing and managing defs and uses in an ir::Module.
@ -95,6 +111,11 @@ class DefUseManager {
// Erases the records that a given instruction uses its operand ids.
void EraseUseRecordsOfOperandIds(const ir::Instruction* inst);
friend bool operator==(const DefUseManager&, const DefUseManager&);
friend bool operator!=(const DefUseManager& lhs, const DefUseManager& rhs) {
return !(lhs == rhs);
}
private:
using InstToUsedIdsMap =
std::unordered_map<const ir::Instruction*, std::vector<uint32_t>>;
@ -107,6 +128,7 @@ class DefUseManager {
IdToUsesMap id_to_uses_; // Mapping from ids to their uses
// Mapping from instructions to the ids used in the instruction.
InstToUsedIdsMap inst_to_used_ids_;
};
} // namespace analysis

View File

@ -29,6 +29,10 @@ class EliminateDeadFunctionsPass : public MemPass {
const char* name() const override { return "eliminate-dead-functions"; }
Status Process(ir::IRContext* c) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
private:
void EliminateFunction(ir::Function* func);
};

View File

@ -64,7 +64,7 @@ bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
}
for (opt::analysis::Use& use : uses_to_update) {
get_def_use_mgr()->EraseUseRecordsOfOperandIds(use.inst);
ForgetUses(use.inst);
const uint32_t type_result_id_count =
(use.inst->result_id() != 0) + (use.inst->type_id() != 0);
@ -88,9 +88,35 @@ bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
// Make the modification in the instruction.
use.inst->SetInOperand(in_operand_pos, {after});
}
get_def_use_mgr()->AnalyzeInstUse(use.inst);
AnalyzeUses(use.inst);
}
return true;
}
bool IRContext::IsConsistent() {
#ifndef SPIRV_CHECK_CONTEXT
return true;
#endif
if (AreAnalysesValid(kAnalysisDefUse)) {
opt::analysis::DefUseManager new_def_use(module());
if (*get_def_use_mgr() != new_def_use) {
return false;
}
}
return true;
}
void spvtools::ir::IRContext::ForgetUses(Instruction* inst) {
if (AreAnalysesValid(kAnalysisDefUse)) {
get_def_use_mgr()->EraseUseRecordsOfOperandIds(inst);
}
}
void IRContext::AnalyzeUses(Instruction* inst) {
if (AreAnalysesValid(kAnalysisDefUse)) {
get_def_use_mgr()->AnalyzeInstUse(inst);
}
}
} // namespace ir
} // namespace spvtools

View File

@ -188,6 +188,19 @@ class IRContext {
// false.
bool ReplaceAllUsesWith(uint32_t before, uint32_t after);
// Returns true if all of the analyses that are suppose to be valid are
// actually valid.
bool IsConsistent();
// Informs the IRContext that the uses of |inst| are going to change, and that
// is should forget everything it know about the current uses. Any valid
// analyses will be updated accordingly.
void ForgetUses(Instruction* inst);
// The IRContext will look at the uses of |inst| and update any valid analyses
// will be updated accordingly.
void AnalyzeUses(Instruction* inst);
private:
std::unique_ptr<Module> module_;
spvtools::MessageConsumer consumer_;

View File

@ -39,6 +39,10 @@ class LocalAccessChainConvertPass : public MemPass {
const char* name() const override { return "convert-local-access-chains"; }
Status Process(ir::IRContext* c) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
using ProcessFunction = std::function<bool(ir::Function*)>;
private:

View File

@ -39,6 +39,10 @@ class LocalSingleBlockLoadStoreElimPass : public MemPass {
const char* name() const override { return "eliminate-local-single-block"; }
Status Process(ir::IRContext* c) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
private:
// Return true if all uses of |varId| are only through supported reference
// operations ie. loads and store. Also cache in supported_ref_ptrs_.

View File

@ -40,6 +40,10 @@ class LocalSingleStoreElimPass : public MemPass {
LocalSingleStoreElimPass();
const char* name() const override { return "eliminate-local-single-store"; }
Status Process(ir::IRContext* irContext) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
private:
// Return true if all refs through |ptrId| are only loads or stores and

View File

@ -44,6 +44,10 @@ class LocalMultiStoreElimPass : public MemPass {
const char* name() const override { return "eliminate-local-multi-store"; }
Status Process(ir::IRContext* c) override;
ir::IRContext::Analysis GetPreservedAnalyses() override {
return ir::IRContext::kAnalysisDefUse;
}
private:
// Initialize extensions whitelist
void InitExtensions();

View File

@ -755,7 +755,9 @@ void MemPass::RemovePhiOperands(
i += 2;
}
context()->ForgetUses(phi);
phi->ReplaceOperands(keep_operands);
context()->AnalyzeUses(phi);
}
void MemPass::RemoveBlock(ir::Function::iterator* bi) {

View File

@ -107,6 +107,7 @@ Pass::Status Pass::Run(ir::IRContext* ctx) {
if (status == Status::SuccessWithChange) {
ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses());
}
assert(ctx->IsConsistent());
return status;
}