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)
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);
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);
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.
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);
}
}
+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
// 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.
// 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>>;
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
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);
};
}
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);
// 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
// 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_;
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:
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_.
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
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();
i += 2;
}
+ context()->ForgetUses(phi);
phi->ReplaceOperands(keep_operands);
+ context()->AnalyzeUses(phi);
}
void MemPass::RemoveBlock(ir::Function::iterator* bi) {
if (status == Status::SuccessWithChange) {
ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses());
}
+ assert(ctx->IsConsistent());
return status;
}