mirror of
https://gitee.com/openharmony/third_party_spirv-tools
synced 2024-11-23 07:20:28 +00:00
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:
parent
039c12f096
commit
efe12ff5a1
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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:
|
||||
|
@ -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_.
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -107,6 +107,7 @@ Pass::Status Pass::Run(ir::IRContext* ctx) {
|
||||
if (status == Status::SuccessWithChange) {
|
||||
ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses());
|
||||
}
|
||||
assert(ctx->IsConsistent());
|
||||
return status;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user