Store all enabled capabilities in the feature manger.
authorSteven Perron <stevenperron@google.com>
Tue, 19 Dec 2017 19:18:13 +0000 (14:18 -0500)
committerDavid Neto <dneto@google.com>
Thu, 21 Dec 2017 16:14:53 +0000 (11:14 -0500)
In order to keep track of all of the implicit capabilities as well as
the explicit ones, we will add them all to the feature manager.  That is
the object that needs to be queried when checking if a capability is
enabled.

The name of the "HasCapability" function in the module was changed to
make it more obvious that it does not check for implied capabilities.

Keep an spv_context and AssemblyGrammar in IRContext

25 files changed:
source/link/linker.cpp
source/opt/aggressive_dead_code_elim_pass.cpp
source/opt/build_module.cpp
source/opt/cfg.cpp
source/opt/common_uniform_elim_pass.cpp
source/opt/dead_branch_elim_pass.cpp
source/opt/feature_manager.cpp
source/opt/feature_manager.h
source/opt/inline_pass.cpp
source/opt/instruction.cpp
source/opt/ir_context.h
source/opt/local_single_block_elim_pass.cpp
source/opt/local_single_store_elim_pass.cpp
source/opt/local_ssa_elim_pass.cpp
source/opt/mem_pass.cpp
source/opt/merge_return_pass.cpp
source/opt/module.cpp
source/opt/module.h
source/opt/private_to_local_pass.cpp
test/opt/decoration_manager_test.cpp
test/opt/def_use_test.cpp
test/opt/feature_manager_test.cpp
test/opt/instruction_test.cpp
test/opt/ir_context_test.cpp
test/opt/pass_manager_test.cpp

index 9ea9f96..fbb1b06 100644 (file)
@@ -225,7 +225,7 @@ spv_result_t Linker::Link(const uint32_t* const* binaries,
   ir::ModuleHeader header;
   res = GenerateHeader(consumer, modules, max_id_bound, &header);
   if (res != SPV_SUCCESS) return res;
-  IRContext linked_context(consumer);
+  IRContext linked_context(impl_->context->target_env, consumer);
   linked_context.module()->SetHeader(header);
 
   // Phase 3: Merge all the binaries into a single one.
index 05b8331..101649b 100644 (file)
@@ -467,12 +467,12 @@ void AggressiveDCEPass::Initialize(ir::IRContext* c) {
 Pass::Status AggressiveDCEPass::ProcessImpl() {
   // Current functionality assumes shader capability
   // TODO(greg-lunarg): Handle additional capabilities
-  if (!get_module()->HasCapability(SpvCapabilityShader))
+  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
     return Status::SuccessWithoutChange;
   // Current functionality assumes relaxed logical addressing (see
   // instruction.h)
   // TODO(greg-lunarg): Handle non-logical addressing
-  if (get_module()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
     return Status::SuccessWithoutChange;
   // If any extensions in the module are not explicitly supported,
   // return unmodified.
index 7978646..f558767 100644 (file)
@@ -51,7 +51,7 @@ std::unique_ptr<ir::IRContext> BuildModule(spv_target_env env,
   auto context = spvContextCreate(env);
   libspirv::SetContextMessageConsumer(context, consumer);
 
-  auto irContext = MakeUnique<ir::IRContext>(consumer);
+  auto irContext = MakeUnique<ir::IRContext>(env, consumer);
   ir::IrLoader loader(consumer, irContext->module());
 
   spv_result_t status = spvBinaryParse(context, &loader, binary, size,
index bf46236..d67b21e 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "cfg.h"
 #include "cfa.h"
+#include "ir_context.h"
 #include "module.h"
 
 namespace spvtools {
@@ -45,7 +46,8 @@ CFG::CFG(ir::Module* module)
 
 void CFG::ComputeStructuredOrder(ir::Function* func, ir::BasicBlock* root,
                                  std::list<ir::BasicBlock*>* order) {
-  assert(module_->HasCapability(SpvCapabilityShader) &&
+  assert(module_->context()->get_feature_mgr()->HasCapability(
+             SpvCapabilityShader) &&
          "This only works on structured control flow");
 
   // Compute structured successors and do DFS.
index 8f4c95b..fac8257 100644 (file)
@@ -515,11 +515,11 @@ bool CommonUniformElimPass::AllExtensionsSupported() const {
 Pass::Status CommonUniformElimPass::ProcessImpl() {
   // Assumes all control flow structured.
   // TODO(greg-lunarg): Do SSA rewrite for non-structured control flow
-  if (!get_module()->HasCapability(SpvCapabilityShader))
+  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
     return Status::SuccessWithoutChange;
   // Assumes logical addressing only
   // TODO(greg-lunarg): Add support for physical addressing
-  if (get_module()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
     return Status::SuccessWithoutChange;
   // Do not process if any disallowed extensions are enabled
   if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
index 52199ba..e165c51 100644 (file)
@@ -348,7 +348,7 @@ bool DeadBranchElimPass::AllExtensionsSupported() const {
 Pass::Status DeadBranchElimPass::ProcessImpl() {
   // Current functionality assumes structured control flow.
   // TODO(greg-lunarg): Handle non-structured control-flow.
-  if (!get_module()->HasCapability(SpvCapabilityShader))
+  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
     return Status::SuccessWithoutChange;
   // Do not process if module contains OpGroupDecorate. Additional
   // support required in KillNamesAndDecorates().
index 5bd43e9..ebb1dd5 100644 (file)
@@ -13,6 +13,8 @@
 // limitations under the License.
 
 #include "feature_manager.h"
+#include <queue>
+#include <stack>
 
 #include "enum_string_mapping.h"
 
@@ -20,6 +22,11 @@ namespace spvtools {
 namespace opt {
 
 void FeatureManager::Analyze(ir::Module* module) {
+  AddExtensions(module);
+  AddCapabilities(module);
+}
+
+void FeatureManager::AddExtensions(ir::Module* module) {
   for (auto ext : module->extensions()) {
     const std::string name =
         reinterpret_cast<const char*>(ext.GetInOperand(0u).words.data());
@@ -30,5 +37,24 @@ void FeatureManager::Analyze(ir::Module* module) {
   }
 }
 
+void FeatureManager::AddCapability(SpvCapability cap) {
+  if (capabilities_.Contains(cap)) return;
+
+  capabilities_.Add(cap);
+
+  spv_operand_desc desc = {};
+  if (SPV_SUCCESS ==
+      grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) {
+    libspirv::CapabilitySet(desc->numCapabilities, desc->capabilities)
+        .ForEach([this](SpvCapability c) { AddCapability(c); });
+  }
+}
+
+void FeatureManager::AddCapabilities(ir::Module* module) {
+  for (ir::Instruction& inst : module->capabilities()) {
+    AddCapability(static_cast<SpvCapability>(inst.GetSingleWordInOperand(0)));
+  }
+}
+
 }  // namespace opt
 }  // namespace spvtools
index 874bf50..615234f 100644 (file)
@@ -15,6 +15,7 @@
 #ifndef LIBSPIRV_OPT_FEATURE_MANAGER_H_
 #define LIBSPIRV_OPT_FEATURE_MANAGER_H_
 
+#include "assembly_grammar.h"
 #include "extensions.h"
 #include "module.h"
 
@@ -24,19 +25,41 @@ namespace opt {
 // Tracks features enabled by a module. The IRContext has a FeatureManager.
 class FeatureManager {
  public:
-  FeatureManager() = default;
+  explicit FeatureManager(const libspirv::AssemblyGrammar& grammar)
+      : grammar_(grammar) {}
 
   // Returns true if |ext| is an enabled extension in the module.
   bool HasExtension(libspirv::Extension ext) const {
     return extensions_.Contains(ext);
   }
 
-  // Analyzes |module| and records enabled extensions.
+  // Returns true if |cap| is an enabled capability in the module.
+  bool HasCapability(SpvCapability cap) const {
+    return capabilities_.Contains(cap);
+  }
+
+  // Analyzes |module| and records enabled extensions and capabilities.
   void Analyze(ir::Module* module);
 
  private:
+  // Analyzes |module| and records enabled extensions.
+  void AddExtensions(ir::Module* module);
+
+  // Adds the given |capability| and all implied capabilities into the current
+  // FeatureManager.
+  void AddCapability(SpvCapability capability);
+
+  // Analyzes |module| and records enabled capabilities.
+  void AddCapabilities(ir::Module* module);
+
+  // Auxiliary object for querying SPIR-V grammar facts.
+  const libspirv::AssemblyGrammar& grammar_;
+
   // The enabled extensions.
   libspirv::ExtensionSet extensions_;
+
+  // The enabled capabilities.
+  libspirv::CapabilitySet capabilities_;
 };
 
 }  // namespace opt
index 558469a..8e4808d 100644 (file)
@@ -575,7 +575,8 @@ InlinePass::GetBlocksFunction InlinePass::StructuredSuccessorsFunction() {
 bool InlinePass::HasNoReturnInLoop(ir::Function* func) {
   // If control not structured, do not do loop/return analysis
   // TODO: Analyze returns in non-structured control flow
-  if (!get_module()->HasCapability(SpvCapabilityShader)) return false;
+  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
+    return false;
   // Compute structured block order. This order has the property
   // that dominators are before all blocks they dominate and merge blocks
   // are after all blocks that are in the control constructs of their header.
index 83c548c..684514c 100644 (file)
@@ -189,7 +189,7 @@ Instruction* Instruction::GetBaseAddress() const {
 }
 
 bool Instruction::IsReadOnlyVariable() const {
-  if (context()->module()->HasCapability(SpvCapabilityShader))
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
     return IsReadOnlyVariableShaders();
   else
     return IsReadOnlyVariableKernel();
@@ -407,7 +407,7 @@ bool Instruction::IsValidBasePointer() const {
     return false;
   }
 
-  if (context()->module()->HasCapability(SpvCapabilityAddresses)) {
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses)) {
     // TODO: The rules here could be more restrictive.
     return true;
   }
index 3437992..67f5c0e 100644 (file)
@@ -15,6 +15,7 @@
 #ifndef SPIRV_TOOLS_IR_CONTEXT_H
 #define SPIRV_TOOLS_IR_CONTEXT_H
 
+#include "assembly_grammar.h"
 #include "cfg.h"
 #include "constants.h"
 #include "decoration_manager.h"
@@ -63,28 +64,38 @@ class IRContext {
   friend inline Analysis& operator<<=(Analysis& a, int shift);
 
   // Creates an |IRContext| that contains an owned |Module|
-  IRContext(spvtools::MessageConsumer c)
-      : unique_id_(0),
+  IRContext(spv_target_env env, spvtools::MessageConsumer c)
+      : syntax_context_(spvContextCreate(env)),
+        grammar_(syntax_context_),
+        unique_id_(0),
         module_(new Module()),
         consumer_(std::move(c)),
         def_use_mgr_(nullptr),
         valid_analyses_(kAnalysisNone),
         constant_mgr_(nullptr),
         type_mgr_(nullptr) {
+    libspirv::SetContextMessageConsumer(syntax_context_, consumer_);
     module_->SetContext(this);
   }
 
-  IRContext(std::unique_ptr<Module>&& m, spvtools::MessageConsumer c)
-      : unique_id_(0),
+  IRContext(spv_target_env env, std::unique_ptr<Module>&& m,
+            spvtools::MessageConsumer c)
+      : syntax_context_(spvContextCreate(env)),
+        grammar_(syntax_context_),
+        unique_id_(0),
         module_(std::move(m)),
         consumer_(std::move(c)),
         def_use_mgr_(nullptr),
         valid_analyses_(kAnalysisNone),
         constant_mgr_(nullptr),
         type_mgr_(nullptr) {
+    libspirv::SetContextMessageConsumer(syntax_context_, consumer_);
     module_->SetContext(this);
     InitializeCombinators();
   }
+
+  ~IRContext() { spvContextDestroy(syntax_context_); }
+
   Module* module() const { return module_.get(); }
 
   // Returns a vector of pointers to constant-creation instructions in this
@@ -414,7 +425,7 @@ class IRContext {
 
   // Analyzes the features in the owned module. Builds the manager if required.
   void AnalyzeFeatures() {
-    feature_mgr_.reset(new opt::FeatureManager());
+    feature_mgr_.reset(new opt::FeatureManager(grammar_));
     feature_mgr_->Analyze(module());
   }
 
@@ -428,6 +439,13 @@ class IRContext {
   // Add the combinator opcode for the given extension to combinator_ops_.
   void AddCombinatorsForExtension(ir::Instruction* extension);
 
+  // The SPIR-V syntax context containing grammar tables for opcodes and
+  // operands.
+  spv_context syntax_context_;
+
+  // Auxiliary object for querying SPIR-V grammar facts.
+  libspirv::AssemblyGrammar grammar_;
+
   // An unique identifier for instructions in |module_|. Can be used to order
   // instructions in a container.
   //
index 392e4ab..6bb8ffd 100644 (file)
@@ -189,7 +189,7 @@ bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
 
 Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
   // Assumes relaxed logical addressing only (see instruction.h).
-  if (get_module()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
     return Status::SuccessWithoutChange;
   // Do not process if module contains OpGroupDecorate. Additional
   // support required in KillNamesAndDecorates().
index 67cf516..c59a9dd 100644 (file)
@@ -308,7 +308,7 @@ bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
 
 Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
   // Assumes relaxed logical addressing only (see instruction.h)
-  if (get_module()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
     return Status::SuccessWithoutChange;
   // Do not process if module contains OpGroupDecorate. Additional
   // support required in KillNamesAndDecorates().
index af84ebf..a1f6a17 100644 (file)
@@ -77,11 +77,11 @@ bool LocalMultiStoreElimPass::AllExtensionsSupported() const {
 Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
   // Assumes all control flow structured.
   // TODO(greg-lunarg): Do SSA rewrite for non-structured control flow
-  if (!get_module()->HasCapability(SpvCapabilityShader))
+  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
     return Status::SuccessWithoutChange;
   // Assumes relaxed logical addressing only (see instruction.h)
   // TODO(greg-lunarg): Add support for physical addressing
-  if (get_module()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
     return Status::SuccessWithoutChange;
   // Do not process if module contains OpGroupDecorate. Additional
   // support required in KillNamesAndDecorates().
index f8aa31a..88bd4bc 100644 (file)
@@ -564,7 +564,7 @@ Pass::Status MemPass::InsertPhiInstructions(ir::Function* func) {
   // TODO(dnovillo) the current Phi placement mechanism assumes structured
   // control-flow. This should be generalized
   // (https://github.com/KhronosGroup/SPIRV-Tools/issues/893).
-  assert(get_module()->HasCapability(SpvCapabilityShader) &&
+  assert(context()->get_feature_mgr()->HasCapability(SpvCapabilityShader) &&
          "This only works on structured control flow");
 
   // Initialize the data structures used to insert Phi instructions.
index f8cf25c..c5e533d 100644 (file)
@@ -25,7 +25,7 @@ Pass::Status MergeReturnPass::Process(ir::IRContext* irContext) {
 
   // TODO (alanbaker): Support structured control flow. Bail out in the
   // meantime.
-  if (get_module()->HasCapability(SpvCapabilityShader))
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
     return Status::SuccessWithoutChange;
 
   bool modified = false;
index aaf0e20..ba31384 100644 (file)
@@ -140,7 +140,7 @@ uint32_t Module::ComputeIdBound() const {
   return highest + 1;
 }
 
-bool Module::HasCapability(uint32_t cap) {
+bool Module::HasExplicitCapability(uint32_t cap) {
   for (auto& ci : capabilities_) {
     uint32_t tcap = ci.GetSingleWordOperand(0);
     if (tcap == cap) {
index b8418bf..99ed3a2 100644 (file)
@@ -239,7 +239,7 @@ class Module {
   uint32_t ComputeIdBound() const;
 
   // Returns true if module has capability |cap|
-  bool HasCapability(uint32_t cap);
+  bool HasExplicitCapability(uint32_t cap);
 
   // Returns id for OpExtInst instruction for extension |extstr|.
   // Returns 0 if not found.
index d4daf8d..3cdf13f 100644 (file)
@@ -30,7 +30,7 @@ Pass::Status PrivateToLocalPass::Process(ir::IRContext* c) {
 
   // Private variables require the shader capability.  If this is not a shader,
   // there is no work to do.
-  if (get_module()->HasCapability(SpvCapabilityAddresses))
+  if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
     return Status::SuccessWithoutChange;
 
   std::vector<std::pair<ir::Instruction*, ir::Function*>> variables_to_move;
index 7e5e4f2..ca2b0a8 100644 (file)
@@ -112,7 +112,7 @@ class DecorationManagerTest : public ::testing::Test {
 };
 
 TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffOpcodes) {
-  spvtools::ir::IRContext ir_context(GetConsumer());
+  spvtools::ir::IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
   Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
@@ -126,7 +126,7 @@ TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffOpcodes) {
 }
 
 TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffDeco) {
-  spvtools::ir::IRContext ir_context(GetConsumer());
+  spvtools::ir::IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
   Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
@@ -141,7 +141,7 @@ TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffDeco) {
 }
 
 TEST_F(DecorationManagerTest, ComparingSameDecorationsOnDiffTargetAllowed) {
-  spvtools::ir::IRContext ir_context(GetConsumer());
+  spvtools::ir::IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
   Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
@@ -156,7 +156,7 @@ TEST_F(DecorationManagerTest, ComparingSameDecorationsOnDiffTargetAllowed) {
 }
 
 TEST_F(DecorationManagerTest, ComparingSameDecorationsOnDiffTargetDisallowed) {
-  spvtools::ir::IRContext ir_context(GetConsumer());
+  spvtools::ir::IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpDecorate %1 Constant
   Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
@@ -171,7 +171,7 @@ TEST_F(DecorationManagerTest, ComparingSameDecorationsOnDiffTargetDisallowed) {
 }
 
 TEST_F(DecorationManagerTest, ComparingMemberDecorationsOnSameTypeDiffMember) {
-  spvtools::ir::IRContext ir_context(GetConsumer());
+  spvtools::ir::IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpMemberDecorate %1 0 Constant
   Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
@@ -189,7 +189,7 @@ TEST_F(DecorationManagerTest, ComparingMemberDecorationsOnSameTypeDiffMember) {
 
 TEST_F(DecorationManagerTest,
        ComparingSameMemberDecorationsOnDiffTargetAllowed) {
-  spvtools::ir::IRContext ir_context(GetConsumer());
+  spvtools::ir::IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpMemberDecorate %1 0 Constant
   Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
@@ -207,7 +207,7 @@ TEST_F(DecorationManagerTest,
 
 TEST_F(DecorationManagerTest,
        ComparingSameMemberDecorationsOnDiffTargetDisallowed) {
-  spvtools::ir::IRContext ir_context(GetConsumer());
+  spvtools::ir::IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
   // OpMemberDecorate %1 0 Constant
   Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u,
                     {{SPV_OPERAND_TYPE_ID, {1u}},
index fd1a9ec..a72f79f 100644 (file)
@@ -1368,7 +1368,7 @@ INSTANTIATE_TEST_CASE_P(
 using AnalyzeInstDefUse = ::testing::Test;
 
 TEST(AnalyzeInstDefUse, UseWithNoResultId) {
-  ir::IRContext context(nullptr);
+  ir::IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
 
   // Analyze the instructions.
   opt::analysis::DefUseManager manager(context.module());
index 264df1f..f67388a 100644 (file)
@@ -33,7 +33,7 @@ OpMemoryModel Logical GLSL450
       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
   ASSERT_NE(context, nullptr);
 
-  ASSERT_FALSE(context->get_feature_mgr()->HasExtension(
+  EXPECT_FALSE(context->get_feature_mgr()->HasExtension(
       libspirv::Extension::kSPV_KHR_variable_pointers));
 }
 
@@ -48,7 +48,7 @@ OpExtension "SPV_KHR_variable_pointers"
       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
   ASSERT_NE(context, nullptr);
 
-  ASSERT_TRUE(context->get_feature_mgr()->HasExtension(
+  EXPECT_TRUE(context->get_feature_mgr()->HasExtension(
       libspirv::Extension::kSPV_KHR_variable_pointers));
 }
 
@@ -63,7 +63,7 @@ OpExtension "SPV_KHR_variable_pointers"
       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
   ASSERT_NE(context, nullptr);
 
-  ASSERT_FALSE(context->get_feature_mgr()->HasExtension(
+  EXPECT_FALSE(context->get_feature_mgr()->HasExtension(
       libspirv::Extension::kSPV_KHR_storage_buffer_storage_class));
 }
 
@@ -79,8 +79,56 @@ OpExtension "SPV_KHR_storage_buffer_storage_class"
       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
   ASSERT_NE(context, nullptr);
 
-  ASSERT_TRUE(context->get_feature_mgr()->HasExtension(
+  EXPECT_TRUE(context->get_feature_mgr()->HasExtension(
       libspirv::Extension::kSPV_KHR_variable_pointers));
-  ASSERT_TRUE(context->get_feature_mgr()->HasExtension(
+  EXPECT_TRUE(context->get_feature_mgr()->HasExtension(
       libspirv::Extension::kSPV_KHR_storage_buffer_storage_class));
 }
+
+// Test capability checks.
+TEST_F(FeatureManagerTest, ExplicitlyPresent1) {
+  const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+  )";
+
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+  ASSERT_NE(context, nullptr);
+
+  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader));
+  EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel));
+}
+
+TEST_F(FeatureManagerTest, ExplicitlyPresent2) {
+  const std::string text = R"(
+OpCapability Kernel
+OpMemoryModel Logical GLSL450
+  )";
+
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+  ASSERT_NE(context, nullptr);
+
+  EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader));
+  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel));
+}
+
+TEST_F(FeatureManagerTest, ImplicitlyPresent) {
+  const std::string text = R"(
+OpCapability Tessellation
+OpMemoryModel Logical GLSL450
+  )";
+
+  std::unique_ptr<ir::IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+  ASSERT_NE(context, nullptr);
+
+  // Check multiple levels of indirection.  Tessellation implies Shader, which
+  // implies Matrix.
+  EXPECT_TRUE(
+      context->get_feature_mgr()->HasCapability(SpvCapabilityTessellation));
+  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader));
+  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityMatrix));
+  EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel));
+}
index aa457ca..a39fa1b 100644 (file)
@@ -46,7 +46,7 @@ TEST(InstructionTest, CreateTrivial) {
 }
 
 TEST(InstructionTest, CreateWithOpcodeAndNoOperands) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, SpvOpReturn);
   EXPECT_EQ(SpvOpReturn, inst.opcode());
   EXPECT_EQ(0u, inst.type_id());
@@ -127,7 +127,7 @@ spv_parsed_instruction_t kSampleControlBarrierInstruction = {
     3};
 
 TEST(InstructionTest, CreateWithOpcodeAndOperands) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, kSampleParsedInstruction);
   EXPECT_EQ(SpvOpTypeInt, inst.opcode());
   EXPECT_EQ(0u, inst.type_id());
@@ -138,7 +138,7 @@ TEST(InstructionTest, CreateWithOpcodeAndOperands) {
 }
 
 TEST(InstructionTest, GetOperand) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, kSampleParsedInstruction);
   EXPECT_THAT(inst.GetOperand(0).words, Eq(std::vector<uint32_t>{44}));
   EXPECT_THAT(inst.GetOperand(1).words, Eq(std::vector<uint32_t>{32}));
@@ -146,14 +146,14 @@ TEST(InstructionTest, GetOperand) {
 }
 
 TEST(InstructionTest, GetInOperand) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, kSampleParsedInstruction);
   EXPECT_THAT(inst.GetInOperand(0).words, Eq(std::vector<uint32_t>{32}));
   EXPECT_THAT(inst.GetInOperand(1).words, Eq(std::vector<uint32_t>{1}));
 }
 
 TEST(InstructionTest, OperandConstIterators) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, kSampleParsedInstruction);
   // Spot check iteration across operands.
   auto cbegin = inst.cbegin();
@@ -180,7 +180,7 @@ TEST(InstructionTest, OperandConstIterators) {
 }
 
 TEST(InstructionTest, OperandIterators) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, kSampleParsedInstruction);
   // Spot check iteration across operands, with mutable iterators.
   auto begin = inst.begin();
@@ -211,7 +211,7 @@ TEST(InstructionTest, OperandIterators) {
 }
 
 TEST(InstructionTest, ForInIdStandardIdTypes) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, kSampleAccessChainInstruction);
 
   std::vector<uint32_t> ids;
@@ -224,7 +224,7 @@ TEST(InstructionTest, ForInIdStandardIdTypes) {
 }
 
 TEST(InstructionTest, ForInIdNonstandardIdTypes) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context, kSampleControlBarrierInstruction);
 
   std::vector<uint32_t> ids;
@@ -237,14 +237,14 @@ TEST(InstructionTest, ForInIdNonstandardIdTypes) {
 }
 
 TEST(InstructionTest, UniqueIds) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst1(&context);
   Instruction inst2(&context);
   EXPECT_NE(inst1.unique_id(), inst2.unique_id());
 }
 
 TEST(InstructionTest, CloneUniqueIdDifferent) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&context);
   std::unique_ptr<Instruction> clone(inst.Clone(&context));
   EXPECT_EQ(inst.context(), clone->context());
@@ -252,8 +252,8 @@ TEST(InstructionTest, CloneUniqueIdDifferent) {
 }
 
 TEST(InstructionTest, CloneDifferentContext) {
-  IRContext c1(nullptr);
-  IRContext c2(nullptr);
+  IRContext c1(SPV_ENV_UNIVERSAL_1_2, nullptr);
+  IRContext c2(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&c1);
   std::unique_ptr<Instruction> clone(inst.Clone(&c2));
   EXPECT_EQ(&c1, inst.context());
@@ -262,8 +262,8 @@ TEST(InstructionTest, CloneDifferentContext) {
 }
 
 TEST(InstructionTest, CloneDifferentContextDifferentUniqueId) {
-  IRContext c1(nullptr);
-  IRContext c2(nullptr);
+  IRContext c1(SPV_ENV_UNIVERSAL_1_2, nullptr);
+  IRContext c2(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction inst(&c1);
   Instruction other(&c2);
   std::unique_ptr<Instruction> clone(inst.Clone(&c2));
@@ -272,7 +272,7 @@ TEST(InstructionTest, CloneDifferentContextDifferentUniqueId) {
 }
 
 TEST(InstructionTest, EqualsEqualsOperator) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction i1(&context);
   Instruction i2(&context);
   std::unique_ptr<Instruction> clone(i1.Clone(&context));
@@ -283,7 +283,7 @@ TEST(InstructionTest, EqualsEqualsOperator) {
 }
 
 TEST(InstructionTest, LessThanOperator) {
-  IRContext context(nullptr);
+  IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
   Instruction i1(&context);
   Instruction i2(&context);
   std::unique_ptr<Instruction> clone(i1.Clone(&context));
index 4b5c642..ad851ed 100644 (file)
@@ -62,7 +62,8 @@ using IRContextTest = PassTest<::testing::Test>;
 
 TEST_F(IRContextTest, IndividualValidAfterBuild) {
   std::unique_ptr<ir::Module> module(new ir::Module());
-  IRContext localContext(std::move(module), spvtools::MessageConsumer());
+  IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
+                         spvtools::MessageConsumer());
 
   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
        i <<= 1) {
@@ -73,7 +74,8 @@ TEST_F(IRContextTest, IndividualValidAfterBuild) {
 
 TEST_F(IRContextTest, AllValidAfterBuild) {
   std::unique_ptr<ir::Module> module = MakeUnique<ir::Module>();
-  IRContext localContext(std::move(module), spvtools::MessageConsumer());
+  IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
+                         spvtools::MessageConsumer());
 
   Analysis built_analyses = IRContext::kAnalysisNone;
   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
@@ -86,7 +88,8 @@ TEST_F(IRContextTest, AllValidAfterBuild) {
 
 TEST_F(IRContextTest, AllValidAfterPassNoChange) {
   std::unique_ptr<ir::Module> module = MakeUnique<ir::Module>();
-  IRContext localContext(std::move(module), spvtools::MessageConsumer());
+  IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
+                         spvtools::MessageConsumer());
 
   Analysis built_analyses = IRContext::kAnalysisNone;
   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
@@ -103,7 +106,8 @@ TEST_F(IRContextTest, AllValidAfterPassNoChange) {
 
 TEST_F(IRContextTest, NoneValidAfterPassWithChange) {
   std::unique_ptr<ir::Module> module = MakeUnique<ir::Module>();
-  IRContext localContext(std::move(module), spvtools::MessageConsumer());
+  IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
+                         spvtools::MessageConsumer());
 
   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
        i <<= 1) {
@@ -121,7 +125,8 @@ TEST_F(IRContextTest, NoneValidAfterPassWithChange) {
 
 TEST_F(IRContextTest, AllPreservedAfterPassWithChange) {
   std::unique_ptr<ir::Module> module = MakeUnique<ir::Module>();
-  IRContext localContext(std::move(module), spvtools::MessageConsumer());
+  IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
+                         spvtools::MessageConsumer());
 
   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
        i <<= 1) {
@@ -139,7 +144,8 @@ TEST_F(IRContextTest, AllPreservedAfterPassWithChange) {
 
 TEST_F(IRContextTest, PreserveFirstOnlyAfterPassWithChange) {
   std::unique_ptr<ir::Module> module = MakeUnique<ir::Module>();
-  IRContext localContext(std::move(module), spvtools::MessageConsumer());
+  IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
+                         spvtools::MessageConsumer());
 
   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
        i <<= 1) {
@@ -201,7 +207,7 @@ TEST_F(IRContextTest, KillMemberName) {
 
 TEST_F(IRContextTest, TakeNextUniqueIdIncrementing) {
   const uint32_t NUM_TESTS = 1000;
-  IRContext localContext(nullptr);
+  IRContext localContext(SPV_ENV_UNIVERSAL_1_2, nullptr);
   for (uint32_t i = 1; i < NUM_TESTS; ++i)
     EXPECT_EQ(i, localContext.TakeNextUniqueId());
 }
index 4d5bcce..2066eff 100644 (file)
@@ -154,7 +154,8 @@ class AppendTypeVoidInstPass : public opt::Pass {
 TEST(PassManager, RecomputeIdBoundAutomatically) {
   opt::PassManager manager;
   std::unique_ptr<ir::Module> module(new ir::Module());
-  ir::IRContext context(std::move(module), manager.consumer());
+  ir::IRContext context(SPV_ENV_UNIVERSAL_1_2, std::move(module),
+                        manager.consumer());
   EXPECT_THAT(GetIdBound(*context.module()), Eq(0u));
 
   manager.Run(&context);