summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.h
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.h')
-rw-r--r--3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.h256
1 files changed, 241 insertions, 15 deletions
diff --git a/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.h b/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.h
index a9d892fa2fb..45bf1290e00 100644
--- a/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.h
+++ b/3rdparty/bgfx/3rdparty/spirv-tools/source/opt/ir_context.h
@@ -20,6 +20,7 @@
#include <limits>
#include <map>
#include <memory>
+#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility>
@@ -37,6 +38,7 @@
#include "source/opt/module.h"
#include "source/opt/register_pressure.h"
#include "source/opt/scalar_analysis.h"
+#include "source/opt/struct_cfg_analysis.h"
#include "source/opt/type_manager.h"
#include "source/opt/value_number_table.h"
#include "source/util/make_unique.h"
@@ -71,9 +73,16 @@ class IRContext {
kAnalysisScalarEvolution = 1 << 8,
kAnalysisRegisterPressure = 1 << 9,
kAnalysisValueNumberTable = 1 << 10,
- kAnalysisEnd = 1 << 11
+ kAnalysisStructuredCFG = 1 << 11,
+ kAnalysisBuiltinVarId = 1 << 12,
+ kAnalysisIdToFuncMapping = 1 << 13,
+ kAnalysisConstants = 1 << 14,
+ kAnalysisTypes = 1 << 15,
+ kAnalysisEnd = 1 << 16
};
+ using ProcessFunction = std::function<bool(Function*)>;
+
friend inline Analysis operator|(Analysis lhs, Analysis rhs);
friend inline Analysis& operator|=(Analysis& lhs, Analysis rhs);
friend inline Analysis operator<<(Analysis a, int shift);
@@ -90,7 +99,10 @@ class IRContext {
valid_analyses_(kAnalysisNone),
constant_mgr_(nullptr),
type_mgr_(nullptr),
- id_to_name_(nullptr) {
+ id_to_name_(nullptr),
+ max_id_bound_(kDefaultMaxIdBound),
+ preserve_bindings_(false),
+ preserve_spec_constants_(false) {
SetContextMessageConsumer(syntax_context_, consumer_);
module_->SetContext(this);
}
@@ -104,7 +116,10 @@ class IRContext {
def_use_mgr_(nullptr),
valid_analyses_(kAnalysisNone),
type_mgr_(nullptr),
- id_to_name_(nullptr) {
+ id_to_name_(nullptr),
+ max_id_bound_(kDefaultMaxIdBound),
+ preserve_bindings_(false),
+ preserve_spec_constants_(false) {
SetContextMessageConsumer(syntax_context_, consumer_);
module_->SetContext(this);
InitializeCombinators();
@@ -172,14 +187,16 @@ class IRContext {
inline IteratorRange<Module::inst_iterator> debugs3();
inline IteratorRange<Module::const_inst_iterator> debugs3() const;
- // Clears all debug instructions (excluding OpLine & OpNoLine).
- inline void debug_clear();
+ // Add |capability| to the module, if it is not already enabled.
+ inline void AddCapability(SpvCapability capability);
// Appends a capability instruction to this module.
inline void AddCapability(std::unique_ptr<Instruction>&& c);
// Appends an extension instruction to this module.
+ inline void AddExtension(const std::string& ext_name);
inline void AddExtension(std::unique_ptr<Instruction>&& e);
// Appends an extended instruction set instruction to this module.
+ inline void AddExtInstImport(const std::string& name);
inline void AddExtInstImport(std::unique_ptr<Instruction>&& e);
// Set the memory model for this module.
inline void SetMemoryModel(std::unique_ptr<Instruction>&& m);
@@ -225,6 +242,15 @@ class IRContext {
return vn_table_.get();
}
+ // Returns a pointer to a StructuredCFGAnalysis. If the analysis is invalid,
+ // it is rebuilt first.
+ StructuredCFGAnalysis* GetStructuredCFGAnalysis() {
+ if (!AreAnalysesValid(kAnalysisStructuredCFG)) {
+ BuildStructuredCFGAnalysis();
+ }
+ return struct_cfg_analysis_.get();
+ }
+
// Returns a pointer to a liveness analysis. If the liveness analysis is
// invalid, it is rebuilt first.
LivenessAnalysis* GetLivenessAnalysis() {
@@ -274,8 +300,9 @@ class IRContext {
// created yet, it creates one. NOTE: Once created, the constant manager
// remains active and it is never re-built.
analysis::ConstantManager* get_constant_mgr() {
- if (!constant_mgr_)
- constant_mgr_ = MakeUnique<analysis::ConstantManager>(this);
+ if (!AreAnalysesValid(kAnalysisConstants)) {
+ BuildConstantManager();
+ }
return constant_mgr_.get();
}
@@ -283,8 +310,9 @@ class IRContext {
// yet, it creates one. NOTE: Once created, the type manager remains active it
// is never re-built.
analysis::TypeManager* get_type_mgr() {
- if (!type_mgr_)
- type_mgr_ = MakeUnique<analysis::TypeManager>(consumer(), this);
+ if (!AreAnalysesValid(kAnalysisTypes)) {
+ BuildTypeManager();
+ }
return type_mgr_.get();
}
@@ -356,6 +384,15 @@ class IRContext {
// |before| and |after| must be registered definitions in the DefUseManager.
bool ReplaceAllUsesWith(uint32_t before, uint32_t after);
+ // Replace all uses of |before| id with |after| id if those uses
+ // (instruction, operand pair) return true for |predicate|. Returns true if
+ // any replacement happens. This method does not kill the definition of the
+ // |before| id. If |after| is the same as |before|, does nothing and return
+ // false.
+ bool ReplaceAllUsesWithPredicate(
+ uint32_t before, uint32_t after,
+ const std::function<bool(Instruction*, uint32_t)>& predicate);
+
// Returns true if all of the analyses that are suppose to be valid are
// actually valid.
bool IsConsistent();
@@ -432,8 +469,18 @@ class IRContext {
post_dominator_trees_.erase(f);
}
- // Return the next available SSA id and increment it.
- inline uint32_t TakeNextId() { return module()->TakeNextIdBound(); }
+ // Return the next available SSA id and increment it. Returns 0 if the
+ // maximum SSA id has been reached.
+ inline uint32_t TakeNextId() {
+ uint32_t next_id = module()->TakeNextIdBound();
+ if (next_id == 0) {
+ if (consumer()) {
+ std::string message = "ID overflow. Try running compact-ids.";
+ consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
+ }
+ }
+ return next_id;
+ }
FeatureManager* get_feature_mgr() {
if (!feature_mgr_.get()) {
@@ -442,6 +489,8 @@ class IRContext {
return feature_mgr_.get();
}
+ void ResetFeatureManager() { feature_mgr_.reset(nullptr); }
+
// Returns the grammar for this context.
const AssemblyGrammar& grammar() const { return grammar_; }
@@ -456,6 +505,65 @@ class IRContext {
return *inst_folder_;
}
+ uint32_t max_id_bound() const { return max_id_bound_; }
+ void set_max_id_bound(uint32_t new_bound) { max_id_bound_ = new_bound; }
+
+ bool preserve_bindings() const { return preserve_bindings_; }
+ void set_preserve_bindings(bool should_preserve_bindings) {
+ preserve_bindings_ = should_preserve_bindings;
+ }
+
+ bool preserve_spec_constants() const { return preserve_spec_constants_; }
+ void set_preserve_spec_constants(bool should_preserve_spec_constants) {
+ preserve_spec_constants_ = should_preserve_spec_constants;
+ }
+
+ // Return id of input variable only decorated with |builtin|, if in module.
+ // Create variable and return its id otherwise. If builtin not currently
+ // supported, return 0.
+ uint32_t GetBuiltinInputVarId(uint32_t builtin);
+
+ // Returns the function whose id is |id|, if one exists. Returns |nullptr|
+ // otherwise.
+ Function* GetFunction(uint32_t id) {
+ if (!AreAnalysesValid(kAnalysisIdToFuncMapping)) {
+ BuildIdToFuncMapping();
+ }
+ auto entry = id_to_func_.find(id);
+ return (entry != id_to_func_.end()) ? entry->second : nullptr;
+ }
+
+ Function* GetFunction(Instruction* inst) {
+ if (inst->opcode() != SpvOpFunction) {
+ return nullptr;
+ }
+ return GetFunction(inst->result_id());
+ }
+
+ // Add to |todo| all ids of functions called directly from |func|.
+ void AddCalls(const Function* func, std::queue<uint32_t>* todo);
+
+ // Applies |pfn| to every function in the call trees that are rooted at the
+ // entry points. Returns true if any call |pfn| returns true. By convention
+ // |pfn| should return true if it modified the module.
+ bool ProcessEntryPointCallTree(ProcessFunction& pfn);
+
+ // Applies |pfn| to every function in the call trees rooted at the entry
+ // points and exported functions. Returns true if any call |pfn| returns
+ // true. By convention |pfn| should return true if it modified the module.
+ bool ProcessReachableCallTree(ProcessFunction& pfn);
+
+ // Applies |pfn| to every function in the call trees rooted at the elements of
+ // |roots|. Returns true if any call to |pfn| returns true. By convention
+ // |pfn| should return true if it modified the module. After returning
+ // |roots| will be empty.
+ bool ProcessCallTreeFromRoots(ProcessFunction& pfn,
+ std::queue<uint32_t>* roots);
+
+ // Emmits a error message to the message consumer indicating the error
+ // described by |message| occurred in |inst|.
+ void EmitErrorMessage(std::string message, Instruction* inst);
+
private:
// Builds the def-use manager from scratch, even if it was already valid.
void BuildDefUseManager() {
@@ -476,6 +584,15 @@ class IRContext {
valid_analyses_ = valid_analyses_ | kAnalysisInstrToBlockMapping;
}
+ // Builds the instruction-function map for the whole module.
+ void BuildIdToFuncMapping() {
+ id_to_func_.clear();
+ for (auto& fn : *module_) {
+ id_to_func_[fn.result_id()] = &fn;
+ }
+ valid_analyses_ = valid_analyses_ | kAnalysisIdToFuncMapping;
+ }
+
void BuildDecorationManager() {
decoration_mgr_ = MakeUnique<analysis::DecorationManager>(module());
valid_analyses_ = valid_analyses_ | kAnalysisDecorations;
@@ -504,6 +621,27 @@ class IRContext {
valid_analyses_ = valid_analyses_ | kAnalysisValueNumberTable;
}
+ // Builds the structured CFG analysis from scratch, even if it was already
+ // valid.
+ void BuildStructuredCFGAnalysis() {
+ struct_cfg_analysis_ = MakeUnique<StructuredCFGAnalysis>(this);
+ valid_analyses_ = valid_analyses_ | kAnalysisStructuredCFG;
+ }
+
+ // Builds the constant manager from scratch, even if it was already
+ // valid.
+ void BuildConstantManager() {
+ constant_mgr_ = MakeUnique<analysis::ConstantManager>(this);
+ valid_analyses_ = valid_analyses_ | kAnalysisConstants;
+ }
+
+ // Builds the type manager from scratch, even if it was already
+ // valid.
+ void BuildTypeManager() {
+ type_mgr_ = MakeUnique<analysis::TypeManager>(consumer(), this);
+ valid_analyses_ = valid_analyses_ | kAnalysisTypes;
+ }
+
// Removes all computed dominator and post-dominator trees. This will force
// the context to rebuild the trees on demand.
void ResetDominatorAnalysis() {
@@ -520,6 +658,13 @@ class IRContext {
valid_analyses_ = valid_analyses_ | kAnalysisLoopAnalysis;
}
+ // Removes all computed loop descriptors.
+ void ResetBuiltinAnalysis() {
+ // Clear the cache.
+ builtin_var_id_map_.clear();
+ valid_analyses_ = valid_analyses_ | kAnalysisBuiltinVarId;
+ }
+
// Analyzes the features in the owned module. Builds the manager if required.
void AnalyzeFeatures() {
feature_mgr_ = MakeUnique<FeatureManager>(grammar_);
@@ -543,6 +688,13 @@ class IRContext {
// true if the cfg is invalidated.
bool CheckCFG();
+ // Return id of input variable only decorated with |builtin|, if in module.
+ // Return 0 otherwise.
+ uint32_t FindBuiltinInputVar(uint32_t builtin);
+
+ // Add |var_id| to all entry points in module.
+ void AddVarToEntryPoints(uint32_t var_id);
+
// The SPIR-V syntax context containing grammar tables for opcodes and
// operands.
spv_context syntax_context_;
@@ -570,13 +722,20 @@ class IRContext {
std::unique_ptr<analysis::DecorationManager> decoration_mgr_;
std::unique_ptr<FeatureManager> feature_mgr_;
- // A map from instructions the the basic block they belong to. This mapping is
+ // A map from instructions to the basic block they belong to. This mapping is
// built on-demand when get_instr_block() is called.
//
// NOTE: Do not traverse this map. Ever. Use the function and basic block
// iterators to traverse instructions.
std::unordered_map<Instruction*, BasicBlock*> instr_to_block_;
+ // A map from ids to the function they define. This mapping is
+ // built on-demand when GetFunction() is called.
+ //
+ // NOTE: Do not traverse this map. Ever. Use the function and basic block
+ // iterators to traverse instructions.
+ std::unordered_map<uint32_t, Function*> id_to_func_;
+
// A bitset indicating which analyes are currently valid.
Analysis valid_analyses_;
@@ -584,6 +743,10 @@ class IRContext {
// without side-effect.
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> combinator_ops_;
+ // Opcodes of shader capability core executable instructions
+ // without side-effect.
+ std::unordered_map<uint32_t, uint32_t> builtin_var_id_map_;
+
// The CFG for all the functions in |module_|.
std::unique_ptr<CFG> cfg_;
@@ -613,6 +776,18 @@ class IRContext {
std::unique_ptr<ValueNumberTable> vn_table_;
std::unique_ptr<InstructionFolder> inst_folder_;
+
+ std::unique_ptr<StructuredCFGAnalysis> struct_cfg_analysis_;
+
+ // The maximum legal value for the id bound.
+ uint32_t max_id_bound_;
+
+ // Whether all bindings within |module_| should be preserved.
+ bool preserve_bindings_;
+
+ // Whether all specialization constants within |module_|
+ // should be preserved.
+ bool preserve_spec_constants_;
};
inline IRContext::Analysis operator|(IRContext::Analysis lhs,
@@ -750,20 +925,68 @@ IteratorRange<Module::const_inst_iterator> IRContext::debugs3() const {
return ((const Module*)module_.get())->debugs3();
}
-void IRContext::debug_clear() { module_->debug_clear(); }
+void IRContext::AddCapability(SpvCapability capability) {
+ if (!get_feature_mgr()->HasCapability(capability)) {
+ std::unique_ptr<Instruction> capability_inst(new Instruction(
+ this, SpvOpCapability, 0, 0,
+ {{SPV_OPERAND_TYPE_CAPABILITY, {static_cast<uint32_t>(capability)}}}));
+ AddCapability(std::move(capability_inst));
+ }
+}
void IRContext::AddCapability(std::unique_ptr<Instruction>&& c) {
AddCombinatorsForCapability(c->GetSingleWordInOperand(0));
+ if (feature_mgr_ != nullptr) {
+ feature_mgr_->AddCapability(
+ static_cast<SpvCapability>(c->GetSingleWordInOperand(0)));
+ }
+ if (AreAnalysesValid(kAnalysisDefUse)) {
+ get_def_use_mgr()->AnalyzeInstDefUse(c.get());
+ }
module()->AddCapability(std::move(c));
}
+void IRContext::AddExtension(const std::string& ext_name) {
+ const auto num_chars = ext_name.size();
+ // Compute num words, accommodate the terminating null character.
+ const auto num_words = (num_chars + 1 + 3) / 4;
+ std::vector<uint32_t> ext_words(num_words, 0u);
+ std::memcpy(ext_words.data(), ext_name.data(), num_chars);
+ AddExtension(std::unique_ptr<Instruction>(
+ new Instruction(this, SpvOpExtension, 0u, 0u,
+ {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
+}
+
void IRContext::AddExtension(std::unique_ptr<Instruction>&& e) {
+ if (AreAnalysesValid(kAnalysisDefUse)) {
+ get_def_use_mgr()->AnalyzeInstDefUse(e.get());
+ }
+ if (feature_mgr_ != nullptr) {
+ feature_mgr_->AddExtension(&*e);
+ }
module()->AddExtension(std::move(e));
}
+void IRContext::AddExtInstImport(const std::string& name) {
+ const auto num_chars = name.size();
+ // Compute num words, accommodate the terminating null character.
+ const auto num_words = (num_chars + 1 + 3) / 4;
+ std::vector<uint32_t> ext_words(num_words, 0u);
+ std::memcpy(ext_words.data(), name.data(), num_chars);
+ AddExtInstImport(std::unique_ptr<Instruction>(
+ new Instruction(this, SpvOpExtInstImport, 0u, TakeNextId(),
+ {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
+}
+
void IRContext::AddExtInstImport(std::unique_ptr<Instruction>&& e) {
AddCombinatorsForExtension(e.get());
+ if (AreAnalysesValid(kAnalysisDefUse)) {
+ get_def_use_mgr()->AnalyzeInstDefUse(e.get());
+ }
module()->AddExtInstImport(std::move(e));
+ if (feature_mgr_ != nullptr) {
+ feature_mgr_->AddExtInstImportIds(module());
+ }
}
void IRContext::SetMemoryModel(std::unique_ptr<Instruction>&& m) {
@@ -799,6 +1022,9 @@ void IRContext::AddAnnotationInst(std::unique_ptr<Instruction>&& a) {
if (AreAnalysesValid(kAnalysisDecorations)) {
get_decoration_mgr()->AddDecoration(a.get());
}
+ if (AreAnalysesValid(kAnalysisDefUse)) {
+ get_def_use_mgr()->AnalyzeInstDefUse(a.get());
+ }
module()->AddAnnotationInst(std::move(a));
}
@@ -810,10 +1036,10 @@ void IRContext::AddType(std::unique_ptr<Instruction>&& t) {
}
void IRContext::AddGlobalValue(std::unique_ptr<Instruction>&& v) {
- module()->AddGlobalValue(std::move(v));
if (AreAnalysesValid(kAnalysisDefUse)) {
- get_def_use_mgr()->AnalyzeInstDef(&*(--types_values_end()));
+ get_def_use_mgr()->AnalyzeInstDefUse(&*v);
}
+ module()->AddGlobalValue(std::move(v));
}
void IRContext::AddFunction(std::unique_ptr<Function>&& f) {