Use AssemblyGrammar for capability validation.
authorDejan Mircevski <deki@google.com>
Sun, 31 Jan 2016 04:32:09 +0000 (23:32 -0500)
committerDavid Neto <dneto@google.com>
Tue, 2 Feb 2016 16:40:05 +0000 (11:40 -0500)
Also:
- ForEach() for spv_capability_mask_t.
- Add capability min/max constants.
- Move max definition from validate_types.cpp to spirv_definition.h.

CMakeLists.txt
include/libspirv/libspirv.h
source/spirv_definition.h
source/validate.cpp
source/validate.h
source/validate_instruction.cpp
source/validate_types.cpp
test/Validate.Capability.cpp
test/ValidationState.cpp [new file with mode: 0644]

index 6535acd..cfea291 100644 (file)
@@ -239,6 +239,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
       ${CMAKE_CURRENT_SOURCE_DIR}/test/Validate.Storage.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/test/Validate.SSA.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/test/ValidateID.cpp
+      ${CMAKE_CURRENT_SOURCE_DIR}/test/ValidationState.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/test/main.cpp)
 
     add_executable(UnitSPIRV ${TEST_SOURCES})
index 1646e56..5dc3a6e 100644 (file)
@@ -48,7 +48,7 @@ extern "C" {
 
 // Helpers
 
-#define spvIsInBitfield(value, bitfield) (value == (value & bitfield))
+#define spvIsInBitfield(value, bitfield) ((value) == ((value) & bitfield))
 
 #define SPV_BIT(shift) (1 << (shift))
 
index 221ef13..3872e76 100644 (file)
 #ifndef LIBSPIRV_SPIRV_DEFINITION_H_
 #define LIBSPIRV_SPIRV_DEFINITION_H_
 
+#include <cstdint>
+
+#include "libspirv/libspirv.h"
+
 // A bit mask representing a set of capabilities.
 // Currently there are 57 distinct capabilities, so 64 bits
 // should be enough.
@@ -39,6 +43,22 @@ typedef uint64_t spv_capability_mask_t;
 #define SPV_CAPABILITY_AS_MASK(capability) \
   (spv_capability_mask_t(1) << (capability))
 
+// Min/max capability IDs.
+enum {
+  kCapabilitiesMinValue = SpvCapabilityMatrix,
+  kCapabilitiesMaxValue = SpvCapabilityStorageImageWriteWithoutFormat
+};
+
+// Applies f to every capability present in a mask.
+namespace libspirv {
+template <typename Functor>
+inline void ForEach(spv_capability_mask_t capabilities, Functor f) {
+  for (int cap = kCapabilitiesMinValue; cap <= kCapabilitiesMaxValue; ++cap)
+    if (spvIsInBitfield(SPV_CAPABILITY_AS_MASK(cap), capabilities))
+      f(static_cast<SpvCapability>(cap));
+}
+}  // end namespace libspirv
+
 typedef struct spv_header_t {
   uint32_t magic;
   uint32_t version;
index f2b5f26..f06c462 100644 (file)
@@ -322,7 +322,7 @@ spv_result_t spvValidate(const spv_const_context context,
 
   // NOTE: Parse the module and perform inline validation checks. These
   // checks do not require the the knowledge of the whole module.
-  ValidationState_t vstate(pDiagnostic, options);
+  ValidationState_t vstate(pDiagnostic, options, context);
   spvCheckReturn(spvBinaryParse(context, &vstate, binary->code,
                                 binary->wordCount, setHeader,
                                 ProcessInstruction, pDiagnostic));
index 4374410..c87aa08 100644 (file)
 
 #include "libspirv/libspirv.h"
 
+#include "assembly_grammar.h"
 #include "binary.h"
 #include "diagnostic.h"
 #include "instruction.h"
+#include "spirv_definition.h"
 #include "table.h"
 
 // Structures
@@ -171,7 +173,8 @@ class Functions {
 
 class ValidationState_t {
  public:
-  ValidationState_t(spv_diagnostic* diagnostic, uint32_t options);
+  ValidationState_t(spv_diagnostic* diagnostic, uint32_t options,
+                    const spv_const_context context);
 
   // Forward declares the id in the module
   spv_result_t forwardDeclareId(uint32_t id);
@@ -266,8 +269,14 @@ class ValidationState_t {
   // Registers the capability and its dependent capabilities
   void registerCapability(SpvCapability cap);
 
-  // Returns true if the capabillity is enabled in the module
-  bool hasCapability(SpvCapability cap);
+  // Returns true if the capability is enabled in the module.
+  bool hasCapability(SpvCapability cap) const;
+
+  // Returns true if any of the capabilities are enabled.  Always true for
+  // capabilities==0.
+  bool HasAnyOf(spv_capability_mask_t capabilities) const;
+
+  AssemblyGrammar& grammar() { return grammar_; }
 
  private:
   spv_diagnostic* diagnostic_;
@@ -298,6 +307,7 @@ class ValidationState_t {
   // IDs that are entry points, ie, arguments to OpEntryPoint.
   std::vector<uint32_t> entry_points_;
 
+  AssemblyGrammar grammar_;
 };
 
 }  // namespace libspirv
@@ -342,7 +352,7 @@ spv_result_t spvValidateIDs(const spv_instruction_t* pInstructions,
                             const spv_ext_inst_table extInstTable,
                             spv_position position, spv_diagnostic* pDiagnostic);
 
-#define spvCheckReturn(expression)                      \
+#define spvCheckReturn(expression) \
   if (spv_result_t error = (expression)) return error;
 
 #endif  // LIBSPIRV_VALIDATE_H_
index f89e64b..c1a9574 100644 (file)
 // Performs validation on instructions that appear inside of a SPIR-V block.
 
 #include <cassert>
+#include <sstream>
+#include <string>
+
+#include "diagnostic.h"
+#include "opcode.h"
+#include "spirv_definition.h"
 #include "validate_passes.h"
 
+using libspirv::AssemblyGrammar;
+using libspirv::DiagnosticStream;
 using libspirv::ValidationState_t;
 
 namespace {
 
-#define STORAGE_CLASS_CASE(CLASS, CAPABILITY)                                \
-  case SpvStorageClass##CLASS:                                               \
-    if (_.hasCapability(SpvCapability##CAPABILITY) == false) {               \
-      return _.diag(SPV_ERROR_INVALID_CAPABILITY)                            \
-             << #CLASS " storage class requires " #CAPABILITY " capability"; \
-    }                                                                        \
-    break
-
-spv_result_t StorageClassCapabilityCheck(ValidationState_t& _,
-                                         SpvStorageClass storage_class) {
-  switch (storage_class) {
-    STORAGE_CLASS_CASE(Input, Shader);
-    STORAGE_CLASS_CASE(Uniform, Shader);
-    STORAGE_CLASS_CASE(Output, Shader);
-    STORAGE_CLASS_CASE(Private, Shader);
-    STORAGE_CLASS_CASE(Generic, Kernel);
-    STORAGE_CLASS_CASE(PushConstant, Shader);
-    STORAGE_CLASS_CASE(AtomicCounter, AtomicStorage);
-    default:
-      // No capabilities are required for UniformConstant, WorkgroupLocal,
-      // WorkgroupGlobal, Function, and Image
-      break;
-  }
-  return SPV_SUCCESS;
-}
-#undef VARIABLE_STORAGE_CASE
-
-#define DECORATION_CASE(DECORATION, CAPABILITY)                                \
-  case SpvDecoration##DECORATION:                                              \
-    if (_.hasCapability(SpvCapability##CAPABILITY) == false) {                 \
-      return _.diag(SPV_ERROR_INVALID_CAPABILITY)                              \
-             << #DECORATION " decoration requires " #CAPABILITY " capability"; \
-    }                                                                          \
-    break
-
-#define BUILTIN_CASE(BUILTIN, CAPABILITY)                                \
-  case SpvBuiltIn##BUILTIN:                                              \
-    if (_.hasCapability(SpvCapability##CAPABILITY) == false) {           \
-      return _.diag(SPV_ERROR_INVALID_CAPABILITY)                        \
-             << #BUILTIN " builtin requires " #CAPABILITY " capability"; \
-    }                                                                    \
-    break
-
-#define BUILTIN_CASE2(BUILTIN, CAPABILITY1, CAPABILITY2)                       \
-  case SpvBuiltIn##BUILTIN:                                                    \
-    if (_.hasCapability(SpvCapability##CAPABILITY1) == false &&                \
-        _.hasCapability(SpvCapability##CAPABILITY2) == false) {                \
-      return _.diag(SPV_ERROR_INVALID_CAPABILITY)                              \
-             << #BUILTIN " builtin requires " #CAPABILITY1 " or " #CAPABILITY2 \
-                         " capabilities";                                      \
-    }                                                                          \
-    break
-
-spv_result_t DecorationCapabilityCheck(ValidationState_t& _,
-                                       SpvDecoration decoration,
-                                       SpvBuiltIn optional_builtin) {
-  switch (decoration) {
-    DECORATION_CASE(RelaxedPrecision, Shader);
-    DECORATION_CASE(SpecId, Shader);
-    DECORATION_CASE(Block, Shader);
-    DECORATION_CASE(BufferBlock, Shader);
-    DECORATION_CASE(RowMajor, Matrix);
-    DECORATION_CASE(ColMajor, Matrix);
-    DECORATION_CASE(ArrayStride, Shader);
-    DECORATION_CASE(MatrixStride, Shader);
-    DECORATION_CASE(GLSLShared, Shader);
-    DECORATION_CASE(GLSLPacked, Shader);
-    DECORATION_CASE(CPacked, Kernel);
-    DECORATION_CASE(NoPerspective, Shader);
-    DECORATION_CASE(Flat, Shader);
-    DECORATION_CASE(Patch, Tessellation);
-    DECORATION_CASE(Centroid, Shader);
-    DECORATION_CASE(Sample, Shader);
-    DECORATION_CASE(Invariant, Shader);
-    DECORATION_CASE(Constant, Kernel);
-    DECORATION_CASE(Uniform, Shader);
-    DECORATION_CASE(SaturatedConversion, Kernel);
-    DECORATION_CASE(Stream, GeometryStreams);
-    DECORATION_CASE(Location, Shader);
-    DECORATION_CASE(Component, Shader);
-    DECORATION_CASE(Index, Shader);
-    DECORATION_CASE(Binding, Shader);
-    DECORATION_CASE(DescriptorSet, Shader);
-    DECORATION_CASE(XfbBuffer, TransformFeedback);
-    DECORATION_CASE(XfbStride, TransformFeedback);
-    DECORATION_CASE(FuncParamAttr, Kernel);
-    DECORATION_CASE(FPRoundingMode, Kernel);
-    DECORATION_CASE(FPFastMathMode, Kernel);
-    DECORATION_CASE(LinkageAttributes, Linkage);
-    DECORATION_CASE(NoContraction, Shader);
-    DECORATION_CASE(Alignment, Kernel);
-    DECORATION_CASE(InputAttachmentIndex, InputAttachment);
-    case SpvDecorationBuiltIn:
-      switch (optional_builtin) {
-        BUILTIN_CASE(Position, Shader);
-        BUILTIN_CASE(PointSize, Shader);
-        BUILTIN_CASE(ClipDistance, ClipDistance);
-        BUILTIN_CASE(CullDistance, CullDistance);
-        BUILTIN_CASE(VertexId, Shader);
-        BUILTIN_CASE(InstanceId, Shader);
-        BUILTIN_CASE2(PrimitiveId, Geometry, Tessellation);
-        BUILTIN_CASE2(InvocationId, Geometry, Tessellation);
-        BUILTIN_CASE(Layer, Geometry);
-        case SpvBuiltInViewportIndex:
-          assert(
-              false &&
-              "UNHANDLED");  // TODO(umar): missing SpvCapabilityMultiViewport
-          // BUILTIN_CASE(ViewportIndex, MultiViewport);
-          BUILTIN_CASE(TessLevelOuter, Tessellation);
-          BUILTIN_CASE(TessLevelInner, Tessellation);
-          BUILTIN_CASE(TessCoord, Tessellation);
-          BUILTIN_CASE(PatchVertices, Tessellation);
-          BUILTIN_CASE(FragCoord, Shader);
-          BUILTIN_CASE(PointCoord, Shader);
-          BUILTIN_CASE(FrontFacing, Shader);
-          BUILTIN_CASE(SampleId, SampleRateShading);
-          BUILTIN_CASE(SamplePosition, SampleRateShading);
-          BUILTIN_CASE(SampleMask, SampleRateShading);
-          BUILTIN_CASE(FragDepth, Shader);
-          BUILTIN_CASE(HelperInvocation, Shader);
-          BUILTIN_CASE(WorkDim, Kernel);
-          BUILTIN_CASE(GlobalSize, Kernel);
-          BUILTIN_CASE(EnqueuedWorkgroupSize, Kernel);
-          BUILTIN_CASE(GlobalOffset, Kernel);
-          BUILTIN_CASE(GlobalLinearId, Kernel);
-          BUILTIN_CASE(SubgroupSize, Kernel);
-          BUILTIN_CASE(SubgroupMaxSize, Kernel);
-          BUILTIN_CASE(NumSubgroups, Kernel);
-          BUILTIN_CASE(NumEnqueuedSubgroups, Kernel);
-          BUILTIN_CASE(SubgroupId, Kernel);
-          BUILTIN_CASE(SubgroupLocalInvocationId, Kernel);
-          BUILTIN_CASE(VertexIndex, Shader);
-          BUILTIN_CASE(InstanceIndex, Shader);
-        case SpvBuiltInNumWorkgroups:
-        case SpvBuiltInWorkgroupSize:
-        case SpvBuiltInWorkgroupId:
-        case SpvBuiltInLocalInvocationId:
-        case SpvBuiltInGlobalInvocationId:
-        case SpvBuiltInLocalInvocationIndex:
-          break;
-      }
-    default:
-      // No capabilities are required for Restrict, Aliased, BuiltIn, Volatile,
-      // Coherent, NonWritable, NonReadable, and Offset
-      break;
-  }
-#undef DECORATION_CASE
-#undef BUILTIN_CASE
-#undef BUILTIN_CASE2
-
-  return SPV_SUCCESS;
-}
-
-#define EXECUTION_MODEL_CASE(MODEL, CAPABILITY)                                \
-  case SpvExecutionModel##MODEL:                                               \
-    if (_.hasCapability(SpvCapability##CAPABILITY) == false) {                 \
-      return _.diag(SPV_ERROR_INVALID_CAPABILITY)                              \
-             << #MODEL " execution model requires " #CAPABILITY " capability"; \
-    }                                                                          \
-    break
-
-spv_result_t ExecutionModelCapabilityCheck(ValidationState_t& _,
-                                           SpvExecutionModel execution_model) {
-  switch (execution_model) {
-    EXECUTION_MODEL_CASE(Vertex, Shader);
-    EXECUTION_MODEL_CASE(TessellationControl, Tessellation);
-    EXECUTION_MODEL_CASE(TessellationEvaluation, Tessellation);
-    EXECUTION_MODEL_CASE(Geometry, Geometry);
-    EXECUTION_MODEL_CASE(Fragment, Shader);
-    EXECUTION_MODEL_CASE(GLCompute, Shader);
-    EXECUTION_MODEL_CASE(Kernel, Kernel);
-  }
-#undef EXECUTION_MODEL_CASE
-  return SPV_SUCCESS;
+std::string ToString(spv_capability_mask_t mask,
+                     const AssemblyGrammar& grammar) {
+  std::stringstream ss;
+  libspirv::ForEach(mask, [&grammar, &ss](SpvCapability cap) {
+    spv_operand_desc desc;
+    if (SPV_SUCCESS ==
+        grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc))
+      ss << desc->name << " ";
+    else
+      ss << cap << " ";
+  });
+  return ss.str();
 }
 
-spv_result_t AddressingAndMemoryModelCapabilityCheck(
-    ValidationState_t& _, SpvAddressingModel addressing_model,
-    SpvMemoryModel memory_model) {
-  switch (addressing_model) {
-    case SpvAddressingModelPhysical32:
-    case SpvAddressingModelPhysical64:
-      if (_.hasCapability(SpvCapabilityAddresses) == false) {
-        return _.diag(SPV_ERROR_INVALID_CAPABILITY)
-               << "Physical32 and Physical64 addressing models require the "
-                  "Addresses capability";
-      }
-      break;
-    case SpvAddressingModelLogical:
-      break;
-  }
-
-  switch (memory_model) {
-    case SpvMemoryModelSimple:
-    case SpvMemoryModelGLSL450:
-      if (_.hasCapability(SpvCapabilityShader) == false) {
-        return _.diag(SPV_ERROR_INVALID_CAPABILITY)
-               << "Simple and GLSL450 memory models require the Shader "
-                  "capability";
-      }
-      break;
-    case SpvMemoryModelOpenCL:
-      if (_.hasCapability(SpvCapabilityKernel) == false) {
-        return _.diag(SPV_ERROR_INVALID_CAPABILITY)
-               << "OpenCL memory model requires the Kernel capability";
-      }
-      break;
-  }
-  return SPV_SUCCESS;
+// Reports a missing-capability error to _'s diagnostic stream and returns
+// SPV_ERROR_INVALID_CAPABILITY.
+spv_result_t CapabilityError(ValidationState_t& _, int which_operand,
+                             SpvOp opcode,
+                             const std::string& required_capabilities) {
+  return _.diag(SPV_ERROR_INVALID_CAPABILITY)
+         << "Operand " << which_operand << " of " << spvOpcodeString(opcode)
+         << " requires one of these capabilities: " << required_capabilities;
 }
 
-#define EXECUTION_MODE_CASE(MODE, CAPABILITY)                      \
-  case SpvExecutionMode##MODE:                                     \
-    if (_.hasCapability(SpvCapability##CAPABILITY) == false) {     \
-      return _.diag(SPV_ERROR_INVALID_CAPABILITY)                  \
-             << #MODE " mode requires " #CAPABILITY " capability"; \
-    }                                                              \
-    break
-#define EXECUTION_MODE_CASE2(MODE, CAPABILITY1, CAPABILITY2)                   \
-  case SpvExecutionMode##MODE:                                                 \
-    if (_.hasCapability(SpvCapability##CAPABILITY1) == false &&                \
-        _.hasCapability(SpvCapability##CAPABILITY2) == false) {                \
-      return _.diag(SPV_ERROR_INVALID_CAPABILITY) << #MODE                     \
-             " mode requires " #CAPABILITY1 " or " #CAPABILITY2 " capability"; \
-    }                                                                          \
-    break
-spv_result_t ExecutionModeCapabilityCheck(ValidationState_t& _,
-                                          SpvExecutionMode execution_mode) {
-  switch (execution_mode) {
-    EXECUTION_MODE_CASE(Invocations, Geometry);
-    EXECUTION_MODE_CASE(SpacingEqual, Tessellation);
-    EXECUTION_MODE_CASE(SpacingFractionalEven, Tessellation);
-    EXECUTION_MODE_CASE(SpacingFractionalOdd, Tessellation);
-    EXECUTION_MODE_CASE(VertexOrderCw, Tessellation);
-    EXECUTION_MODE_CASE(VertexOrderCcw, Tessellation);
-    EXECUTION_MODE_CASE(PixelCenterInteger, Shader);
-    EXECUTION_MODE_CASE(OriginUpperLeft, Shader);
-    EXECUTION_MODE_CASE(OriginLowerLeft, Shader);
-    EXECUTION_MODE_CASE(EarlyFragmentTests, Shader);
-    EXECUTION_MODE_CASE(PointMode, Tessellation);
-    EXECUTION_MODE_CASE(Xfb, TransformFeedback);
-    EXECUTION_MODE_CASE(DepthReplacing, Shader);
-    EXECUTION_MODE_CASE(DepthGreater, Shader);
-    EXECUTION_MODE_CASE(DepthLess, Shader);
-    EXECUTION_MODE_CASE(DepthUnchanged, Shader);
-    EXECUTION_MODE_CASE(LocalSizeHint, Kernel);
-    EXECUTION_MODE_CASE(InputPoints, Geometry);
-    EXECUTION_MODE_CASE(InputLines, Geometry);
-    EXECUTION_MODE_CASE(InputLinesAdjacency, Geometry);
-    EXECUTION_MODE_CASE2(Triangles, Geometry, Tessellation);
-    EXECUTION_MODE_CASE(InputTrianglesAdjacency, Geometry);
-    EXECUTION_MODE_CASE(Quads, Tessellation);
-    EXECUTION_MODE_CASE(Isolines, Tessellation);
-    EXECUTION_MODE_CASE2(OutputVertices, Geometry, Tessellation);
-    EXECUTION_MODE_CASE(OutputPoints, Geometry);
-    EXECUTION_MODE_CASE(OutputLineStrip, Geometry);
-    EXECUTION_MODE_CASE(OutputTriangleStrip, Geometry);
-    EXECUTION_MODE_CASE(VecTypeHint, Kernel);
-    EXECUTION_MODE_CASE(ContractionOff, Kernel);
-    case SpvExecutionModeLocalSize:
-      break;
-  }
-#undef EXECUTION_MODE_CASE
-#undef EXECUTION_MODE_CASE2
-  return SPV_SUCCESS;
+// Returns an operand's required capabilities.
+spv_capability_mask_t RequiredCapabilities(const AssemblyGrammar& grammar,
+                                           spv_operand_type_t type,
+                                           uint32_t operand) {
+  spv_operand_desc operand_desc;
+  if (SPV_SUCCESS == grammar.lookupOperand(type, operand, &operand_desc))
+    return operand_desc->capabilities;
+  else
+    return 0;
 }
 
-#define DIM_CASE(DIM, CAPABILITY)                                   \
-  case SpvDim##DIM:                                                 \
-    if (_.hasCapability(SpvCapability##CAPABILITY) == false) {      \
-      return _.diag(SPV_ERROR_INVALID_CAPABILITY)                   \
-             << "Dim " #DIM " requires " #CAPABILITY " capability"; \
-    }                                                               \
-    break
+}  // namespace anonymous
 
-spv_result_t DimCapabilityCheck(ValidationState_t& _, SpvDim dim) {
-  switch (dim) {
-    DIM_CASE(1D, Sampled1D);
-    DIM_CASE(Cube, Shader);
-    DIM_CASE(Rect, SampledRect);
-    DIM_CASE(Buffer, SampledBuffer);
-    DIM_CASE(SubpassData, InputAttachment);
-    case SpvDim2D:
-    case SpvDim3D:
-      break;
-  }
-#undef DIM_CASE
-  return SPV_SUCCESS;
-}
+namespace libspirv {
 
-spv_result_t SamplerAddressingModeCapabilityCheck(
-    ValidationState_t& _, SpvSamplerAddressingMode sampler_addressing_mode) {
-  std::string mode;
-  switch (sampler_addressing_mode) {
-    case SpvSamplerAddressingModeNone:
-      mode = "None";
-      break;
-    case SpvSamplerAddressingModeClampToEdge:
-      mode = "ClampToEdge";
-      break;
-    case SpvSamplerAddressingModeClamp:
-      mode = "Clamp";
-      break;
-    case SpvSamplerAddressingModeRepeat:
-      mode = "Repeat";
-      break;
-    case SpvSamplerAddressingModeRepeatMirrored:
-      mode = "RepeatMirrored";
-      break;
-  }
-  if (_.hasCapability(SpvCapabilityKernel) == false) {
+spv_result_t CapCheck(ValidationState_t& _,
+                      const spv_parsed_instruction_t* inst) {
+  spv_opcode_desc opcode_desc;
+  if (SPV_SUCCESS == _.grammar().lookupOpcode(inst->opcode, &opcode_desc) &&
+      !_.HasAnyOf(opcode_desc->capabilities))
     return _.diag(SPV_ERROR_INVALID_CAPABILITY)
-           << mode + " sample address mode requires Kernel capability";
+           << "Opcode " << spvOpcodeString(inst->opcode)
+           << " requires one of these capabilities: "
+           << ToString(opcode_desc->capabilities, _.grammar());
+  for (int i = 0; i < inst->num_operands; ++i) {
+    const auto& operand = inst->operands[i];
+    const auto word = inst->words[operand.offset];
+    auto caps = RequiredCapabilities(_.grammar(), operand.type, word);
+    if (!_.HasAnyOf(caps))
+      return CapabilityError(_, i + 1, inst->opcode,
+                             ToString(caps, _.grammar()));
+    if (operand.type == SPV_OPERAND_TYPE_IMAGE)
+      for (auto individual_operand :
+           {SpvImageOperandsBiasMask, SpvImageOperandsLodMask,
+            SpvImageOperandsGradMask, SpvImageOperandsConstOffsetMask,
+            SpvImageOperandsOffsetMask, SpvImageOperandsConstOffsetsMask,
+            SpvImageOperandsSampleMask, SpvImageOperandsMinLodMask})
+        if (word & individual_operand) {
+          caps = RequiredCapabilities(_.grammar(), SPV_OPERAND_TYPE_IMAGE,
+                                      individual_operand);
+          if (!_.HasAnyOf(caps))
+            return CapabilityError(_, i + 1, inst->opcode,
+                                   ToString(caps, _.grammar()));
+        }
   }
   return SPV_SUCCESS;
 }
-}
-
-namespace libspirv {
 
 // clang-format off
 spv_result_t InstructionPass(ValidationState_t& _,
                              const spv_parsed_instruction_t* inst) {
   if (_.is_enabled(SPV_VALIDATE_INSTRUCTION_BIT)) {
-    SpvOp opcode = inst->opcode;
-    switch (opcode) {
-      case SpvOpNop: break;
-      case SpvOpSourceContinued: break;
-      case SpvOpSource: break;
-      case SpvOpSourceExtension: break;
-      case SpvOpName: break;
-      case SpvOpMemberName: break;
-      case SpvOpString: break;
-      case SpvOpLine: break;
-      case SpvOpNoLine: break;
-      case SpvOpDecorate: {
-        SpvDecoration decoration =
-          static_cast<SpvDecoration>(inst->words[inst->operands[1].offset]);
-        SpvBuiltIn builtin = static_cast<SpvBuiltIn>(0);
-        if(decoration == SpvDecorationBuiltIn) {
-          builtin = static_cast<SpvBuiltIn>(inst->words[inst->operands[2].offset]);
-        }
-        spvCheckReturn(DecorationCapabilityCheck(_, decoration, builtin));
-      } break;
-
-      case SpvOpMemberDecorate: {
-        SpvDecoration decoration =
-          static_cast<SpvDecoration>(inst->words[inst->operands[2].offset]);
-        SpvBuiltIn builtin = static_cast<SpvBuiltIn>(0);
-        if(decoration == SpvDecorationBuiltIn) {
-          builtin = static_cast<SpvBuiltIn>(inst->words[inst->operands[3].offset]);
-        }
-        spvCheckReturn(DecorationCapabilityCheck(_, decoration, builtin));
-      } break;
-
-      case SpvOpDecorationGroup: break;
-      case SpvOpGroupDecorate: break;
-      case SpvOpGroupMemberDecorate: break;
-      case SpvOpExtension: break;
-      case SpvOpExtInstImport: break;
-      case SpvOpExtInst: break;
-      case SpvOpMemoryModel: {
-        SpvAddressingModel addressing_model =
-          static_cast<SpvAddressingModel>(inst->words[inst->operands[0].offset]);
-        SpvMemoryModel memory_model =
-          static_cast<SpvMemoryModel>(inst->words[inst->operands[1].offset]);
-        spvCheckReturn(AddressingAndMemoryModelCapabilityCheck(_,
-                                               addressing_model, memory_model));
-      } break;
-
-      case SpvOpEntryPoint: {
-        SpvExecutionModel execution_model =
-          static_cast<SpvExecutionModel>(inst->words[inst->operands[0].offset]);
-        spvCheckReturn(ExecutionModelCapabilityCheck(_, execution_model));
-      } break;
-
-      case SpvOpExecutionMode: {
-        SpvExecutionMode execution_mode =
-          static_cast<SpvExecutionMode>(inst->words[inst->operands[1].offset]);
-        spvCheckReturn(ExecutionModeCapabilityCheck(_, execution_mode));
-      } break;
-
-      case SpvOpCapability:
+    if (inst->opcode == SpvOpCapability)
         _.registerCapability(
             static_cast<SpvCapability>(inst->words[inst->operands[0].offset]));
-        break;
-
-      case SpvOpTypeVoid: break;
-      case SpvOpTypeBool: break;
-      case SpvOpTypeInt: break;
-      case SpvOpTypeFloat: break;
-      case SpvOpTypeVector: break;
-      case SpvOpTypeMatrix:
-        if (_.hasCapability(SpvCapabilityMatrix) == false) {
-          return _.diag(SPV_ERROR_INVALID_CAPABILITY)
-                 << "Matrix type requires Matrix capability";
-        }
-        break;
-      case SpvOpTypeImage: {
-        if (_.hasCapability(SpvCapabilityImageBasic) == false) {
-          return _.diag(SPV_ERROR_INVALID_CAPABILITY)
-            << "TypeImage requires the ImageBasic capability";
-        }
-        SpvDim dim =
-          static_cast<SpvDim>(inst->words[inst->operands[2].offset]);
-        spvCheckReturn(DimCapabilityCheck(_, dim));
-
-      } break;
-
-      case SpvOpTypeSampler: break;
-      case SpvOpTypeSampledImage: break;
-      case SpvOpTypeArray: break;
-      case SpvOpTypeRuntimeArray: break;
-      case SpvOpTypeStruct: break;
-      case SpvOpTypeOpaque: break;
-      case SpvOpTypePointer: {
-        const SpvStorageClass storage_class =
-            static_cast<SpvStorageClass>(inst->words[inst->operands[1].offset]);
-        spvCheckReturn(StorageClassCapabilityCheck(_, storage_class));
-      } break;
-
-      case SpvOpTypeFunction: break;
-      case SpvOpTypeEvent: break;
-      case SpvOpTypeDeviceEvent: break;
-      case SpvOpTypeReserveId: break;
-      case SpvOpTypeQueue: break;
-      case SpvOpTypePipe: break;
-      case SpvOpTypeForwardPointer: {
-        const SpvStorageClass storage_class =
-            static_cast<SpvStorageClass>(inst->words[inst->operands[1].offset]);
-        spvCheckReturn(StorageClassCapabilityCheck(_, storage_class));
-      } break;
-
-      case SpvOpUndef: break;
-      case SpvOpConstantTrue: break;
-      case SpvOpConstantFalse: break;
-      case SpvOpConstant: break;
-      case SpvOpConstantComposite: break;
-      case SpvOpConstantSampler: {
-        if (_.hasCapability(SpvCapabilityLiteralSampler) == false) {
-        return _.diag(SPV_ERROR_INVALID_CAPABILITY)
-          << "ConstantSampler requires the LiteralSampler capability";
-        }
-        const SpvSamplerAddressingMode sampler_addressing_mode =
-          static_cast<SpvSamplerAddressingMode>(inst->words[inst->operands[1].offset]);
-        spvCheckReturn(SamplerAddressingModeCapabilityCheck(_, sampler_addressing_mode));
-      } break;
-
-      case SpvOpConstantNull: break;
-      case SpvOpSpecConstantTrue: break;
-      case SpvOpSpecConstantFalse: break;
-      case SpvOpSpecConstant: break;
-      case SpvOpSpecConstantComposite: break;
-      case SpvOpSpecConstantOp: break;
-      case SpvOpVariable: {
-        const SpvStorageClass storage_class =
+    if (inst->opcode == SpvOpVariable) {
+        const auto storage_class =
             static_cast<SpvStorageClass>(inst->words[inst->operands[2].offset]);
         if (storage_class == SpvStorageClassGeneric)
           return _.diag(SPV_ERROR_INVALID_BINARY)
               << "OpVariable storage class cannot be Generic";
-        spvCheckReturn(StorageClassCapabilityCheck(_, storage_class));
-
         if (_.getLayoutSection() == kLayoutFunctionDefinitions) {
           if (storage_class != SpvStorageClassFunction) {
             return _.diag(SPV_ERROR_INVALID_LAYOUT)
@@ -508,254 +139,10 @@ spv_result_t InstructionPass(ValidationState_t& _,
                       "outside of a function";
           }
         }
-      } break;
-
-      case SpvOpImageTexelPointer: break;
-      case SpvOpLoad: break;
-      case SpvOpStore: break;
-      case SpvOpCopyMemory: break;
-      case SpvOpCopyMemorySized: break;
-      case SpvOpAccessChain: break;
-      case SpvOpInBoundsAccessChain: break;
-      case SpvOpPtrAccessChain: break;
-      case SpvOpArrayLength: break;
-      case SpvOpGenericPtrMemSemantics: break;
-      case SpvOpInBoundsPtrAccessChain: break;
-      case SpvOpFunction: break;
-      case SpvOpFunctionParameter: break;
-      case SpvOpFunctionEnd: break;
-      case SpvOpFunctionCall: break;
-      case SpvOpSampledImage: break;
-      case SpvOpImageSampleImplicitLod: break;
-      case SpvOpImageSampleExplicitLod: break;
-      case SpvOpImageSampleDrefImplicitLod: break;
-      case SpvOpImageSampleDrefExplicitLod: break;
-      case SpvOpImageSampleProjImplicitLod: break;
-      case SpvOpImageSampleProjExplicitLod: break;
-      case SpvOpImageSampleProjDrefImplicitLod: break;
-      case SpvOpImageSampleProjDrefExplicitLod: break;
-      case SpvOpImageFetch: break;
-      case SpvOpImageGather: break;
-      case SpvOpImageDrefGather: break;
-      case SpvOpImageRead: break;
-      case SpvOpImageWrite: break;
-      case SpvOpImage: break;
-      case SpvOpImageQueryFormat: break;
-      case SpvOpImageQueryOrder: break;
-      case SpvOpImageQuerySizeLod: break;
-      case SpvOpImageQuerySize: break;
-      case SpvOpImageQueryLod: break;
-      case SpvOpImageQueryLevels: break;
-      case SpvOpImageQuerySamples: break;
-      case SpvOpImageSparseSampleImplicitLod: break;
-      case SpvOpImageSparseSampleExplicitLod: break;
-      case SpvOpImageSparseSampleDrefImplicitLod: break;
-      case SpvOpImageSparseSampleDrefExplicitLod: break;
-      case SpvOpImageSparseSampleProjImplicitLod: break;
-      case SpvOpImageSparseSampleProjExplicitLod: break;
-      case SpvOpImageSparseSampleProjDrefImplicitLod: break;
-      case SpvOpImageSparseSampleProjDrefExplicitLod: break;
-      case SpvOpImageSparseFetch: break;
-      case SpvOpImageSparseGather: break;
-      case SpvOpImageSparseDrefGather: break;
-      case SpvOpImageSparseTexelsResident: break;
-      case SpvOpConvertFToU: break;
-      case SpvOpConvertFToS: break;
-      case SpvOpConvertSToF: break;
-      case SpvOpConvertUToF: break;
-      case SpvOpUConvert: break;
-      case SpvOpSConvert: break;
-      case SpvOpFConvert: break;
-      case SpvOpQuantizeToF16: break;
-      case SpvOpConvertPtrToU: break;
-      case SpvOpSatConvertSToU: break;
-      case SpvOpSatConvertUToS: break;
-      case SpvOpConvertUToPtr: break;
-      case SpvOpPtrCastToGeneric: break;
-      case SpvOpGenericCastToPtr: break;
-      case SpvOpGenericCastToPtrExplicit: {
-        const SpvStorageClass storage_class =
-            static_cast<SpvStorageClass>(inst->words[inst->operands[3].offset]);
-        spvCheckReturn(StorageClassCapabilityCheck(_, storage_class));
-      } break;
-
-      case SpvOpBitcast: break;
-      case SpvOpVectorExtractDynamic: break;
-      case SpvOpVectorInsertDynamic: break;
-      case SpvOpVectorShuffle: break;
-      case SpvOpCompositeConstruct: break;
-      case SpvOpCompositeExtract: break;
-      case SpvOpCompositeInsert: break;
-      case SpvOpCopyObject: break;
-      case SpvOpTranspose: break;
-      case SpvOpSNegate: break;
-      case SpvOpFNegate: break;
-      case SpvOpIAdd: break;
-      case SpvOpFAdd: break;
-      case SpvOpISub: break;
-      case SpvOpFSub: break;
-      case SpvOpIMul: break;
-      case SpvOpFMul: break;
-      case SpvOpUDiv: break;
-      case SpvOpSDiv: break;
-      case SpvOpFDiv: break;
-      case SpvOpUMod: break;
-      case SpvOpSRem: break;
-      case SpvOpSMod: break;
-      case SpvOpFRem: break;
-      case SpvOpFMod: break;
-      case SpvOpVectorTimesScalar: break;
-      case SpvOpMatrixTimesScalar: break;
-      case SpvOpVectorTimesMatrix: break;
-      case SpvOpMatrixTimesVector: break;
-      case SpvOpMatrixTimesMatrix: break;
-      case SpvOpOuterProduct: break;
-      case SpvOpDot: break;
-      case SpvOpIAddCarry: break;
-      case SpvOpISubBorrow: break;
-      case SpvOpUMulExtended: break;
-      case SpvOpSMulExtended: break; break;
-      case SpvOpShiftRightLogical: break;
-      case SpvOpShiftRightArithmetic: break;
-      case SpvOpShiftLeftLogical: break;
-      case SpvOpBitwiseOr: break;
-      case SpvOpBitwiseXor: break;
-      case SpvOpBitwiseAnd: break;
-      case SpvOpNot: break;
-      case SpvOpBitFieldInsert: break;
-      case SpvOpBitFieldSExtract: break;
-      case SpvOpBitFieldUExtract: break;
-      case SpvOpBitReverse: break;
-      case SpvOpBitCount: break;
-      case SpvOpAny: break;
-      case SpvOpAll: break;
-      case SpvOpIsNan: break;
-      case SpvOpIsInf: break;
-      case SpvOpIsFinite: break;
-      case SpvOpIsNormal: break;
-      case SpvOpSignBitSet: break;
-      case SpvOpLessOrGreater: break;
-      case SpvOpOrdered: break;
-      case SpvOpUnordered: break;
-      case SpvOpLogicalEqual: break;
-      case SpvOpLogicalNotEqual: break;
-      case SpvOpLogicalOr: break;
-      case SpvOpLogicalAnd: break;
-      case SpvOpLogicalNot: break;
-      case SpvOpSelect: break;
-      case SpvOpIEqual: break;
-      case SpvOpINotEqual: break;
-      case SpvOpUGreaterThan: break;
-      case SpvOpSGreaterThan: break;
-      case SpvOpUGreaterThanEqual: break;
-      case SpvOpSGreaterThanEqual: break;
-      case SpvOpULessThan: break;
-      case SpvOpSLessThan: break;
-      case SpvOpULessThanEqual: break;
-      case SpvOpSLessThanEqual: break;
-      case SpvOpFOrdEqual: break;
-      case SpvOpFUnordEqual: break;
-      case SpvOpFOrdNotEqual: break;
-      case SpvOpFUnordNotEqual: break;
-      case SpvOpFOrdLessThan: break;
-      case SpvOpFUnordLessThan: break;
-      case SpvOpFOrdGreaterThan: break;
-      case SpvOpFUnordGreaterThan: break;
-      case SpvOpFOrdLessThanEqual: break;
-      case SpvOpFUnordLessThanEqual: break;
-      case SpvOpFOrdGreaterThanEqual: break;
-      case SpvOpFUnordGreaterThanEqual: break;
-      case SpvOpDPdx: break;
-      case SpvOpDPdy: break;
-      case SpvOpFwidth: break;
-      case SpvOpDPdxFine: break;
-      case SpvOpDPdyFine: break;
-      case SpvOpFwidthFine: break;
-      case SpvOpDPdxCoarse: break;
-      case SpvOpDPdyCoarse: break;
-      case SpvOpFwidthCoarse: break;
-      case SpvOpPhi: break;
-      case SpvOpLoopMerge: break;
-      case SpvOpSelectionMerge: break;
-      case SpvOpLabel: break;
-      case SpvOpBranch: break;
-      case SpvOpBranchConditional: break;
-      case SpvOpSwitch: break;
-      case SpvOpKill: break;
-      case SpvOpReturn: break;
-      case SpvOpReturnValue: break;
-      case SpvOpUnreachable: break;
-      case SpvOpLifetimeStart: break;
-      case SpvOpLifetimeStop: break;
-      case SpvOpAtomicLoad: break;
-      case SpvOpAtomicStore: break;
-      case SpvOpAtomicExchange: break;
-      case SpvOpAtomicCompareExchange: break;
-      case SpvOpAtomicCompareExchangeWeak: break;
-      case SpvOpAtomicIIncrement: break;
-      case SpvOpAtomicIDecrement: break;
-      case SpvOpAtomicIAdd: break;
-      case SpvOpAtomicISub: break;
-      case SpvOpAtomicSMin: break;
-      case SpvOpAtomicUMin: break;
-      case SpvOpAtomicSMax: break;
-      case SpvOpAtomicUMax: break;
-      case SpvOpAtomicAnd: break;
-      case SpvOpAtomicOr: break;
-      case SpvOpAtomicXor: break;
-      case SpvOpAtomicFlagTestAndSet: break;
-      case SpvOpAtomicFlagClear: break;
-      case SpvOpEmitVertex: break;
-      case SpvOpEndPrimitive: break;
-      case SpvOpEmitStreamVertex: break;
-      case SpvOpEndStreamPrimitive: break;
-      case SpvOpControlBarrier: break;
-      case SpvOpMemoryBarrier: break;
-      case SpvOpGroupAsyncCopy: break;
-      case SpvOpGroupWaitEvents: break;
-      case SpvOpGroupAll: break;
-      case SpvOpGroupAny: break;
-      case SpvOpGroupBroadcast: break;
-      case SpvOpGroupIAdd: break;
-      case SpvOpGroupFAdd: break;
-      case SpvOpGroupFMin: break;
-      case SpvOpGroupUMin: break;
-      case SpvOpGroupSMin: break;
-      case SpvOpGroupFMax: break;
-      case SpvOpGroupUMax: break;
-      case SpvOpGroupSMax: break;
-      case SpvOpEnqueueMarker: break;
-      case SpvOpEnqueueKernel: break;
-      case SpvOpGetKernelNDrangeSubGroupCount: break;
-      case SpvOpGetKernelNDrangeMaxSubGroupSize: break;
-      case SpvOpGetKernelWorkGroupSize: break;
-      case SpvOpGetKernelPreferredWorkGroupSizeMultiple: break;
-      case SpvOpRetainEvent: break;
-      case SpvOpReleaseEvent: break;
-      case SpvOpCreateUserEvent: break;
-      case SpvOpIsValidEvent: break;
-      case SpvOpSetUserEventStatus: break;
-      case SpvOpCaptureEventProfilingInfo: break;
-      case SpvOpGetDefaultQueue: break;
-      case SpvOpBuildNDRange: break;
-      case SpvOpReadPipe: break;
-      case SpvOpWritePipe: break;
-      case SpvOpReservedReadPipe: break;
-      case SpvOpReservedWritePipe: break;
-      case SpvOpReserveReadPipePackets: break;
-      case SpvOpReserveWritePipePackets: break;
-      case SpvOpCommitReadPipe: break;
-      case SpvOpCommitWritePipe: break;
-      case SpvOpIsValidReserveId: break;
-      case SpvOpGetNumPipePackets: break;
-      case SpvOpGetMaxPipePackets: break;
-      case SpvOpGroupReserveReadPipePackets: break;
-      case SpvOpGroupReserveWritePipePackets: break;
-      case SpvOpGroupCommitReadPipe: break;
-      case SpvOpGroupCommitWritePipe: break;
     }
+    return CapCheck(_, inst);
   }
   return SPV_SUCCESS;
 }
 // clang-format on
-}
+}  // namespace libspirv
index 1efb8a9..39fcdcf 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "headers/spirv.h"
 
+#include "spirv_definition.h"
 #include "validate.h"
 
 using std::find;
@@ -204,16 +205,13 @@ bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
   return out;
 }
 
-// This variable is the maximum ID of capabilities.
-static const size_t kCapabilitiesMaxValue =
-    SpvCapabilityStorageImageWriteWithoutFormat;
-
 }  // anonymous namespace
 
 namespace libspirv {
 
 ValidationState_t::ValidationState_t(spv_diagnostic* diagnostic,
-                                     uint32_t options)
+                                     uint32_t options,
+                                     const spv_const_context context)
     : diagnostic_(diagnostic),
       instruction_counter_(0),
       unresolved_forward_ids_{},
@@ -221,7 +219,8 @@ ValidationState_t::ValidationState_t(spv_diagnostic* diagnostic,
       operand_names_{},
       current_layout_section_(kLayoutCapabilities),
       module_functions_(*this),
-      module_capabilities_(kCapabilitiesMaxValue + 1, false) {}
+      module_capabilities_(kCapabilitiesMaxValue + 1, false),
+      grammar_(context) {}
 
 spv_result_t ValidationState_t::forwardDeclareId(uint32_t id) {
   unresolved_forward_ids_.insert(id);
@@ -301,85 +300,29 @@ bool ValidationState_t::in_block() const {
   return module_functions_.in_block();
 }
 
-// Returns the dependant capability of the input capibility. If the capability
-// does not have a capability then the same capability is returned.
-// clang-format off
-SpvCapability
-getDependantCapability(SpvCapability cap) {
-  SpvCapability out;
-  switch(cap) {
-    case SpvCapabilityShader:                            out = SpvCapabilityMatrix;           break;
-    case SpvCapabilityGeometry:                          out = SpvCapabilityShader;           break;
-    case SpvCapabilityTessellation:                      out = SpvCapabilityShader;           break;
-    case SpvCapabilityVector16:                          out = SpvCapabilityKernel;           break;
-    case SpvCapabilityFloat16Buffer:                     out = SpvCapabilityKernel;           break;
-    case SpvCapabilityFloat16:                           out = SpvCapabilityFloat16Buffer;    break;
-    case SpvCapabilityInt64Atomics:                      out = SpvCapabilityInt64;            break;
-    case SpvCapabilityImageBasic:                        out = SpvCapabilityKernel;           break;
-    case SpvCapabilityImageReadWrite:                    out = SpvCapabilityImageBasic;       break;
-    case SpvCapabilityImageMipmap:                       out = SpvCapabilityImageBasic;       break;
-    //case SpvCapabilityImageSRGBWrite:                                                       break;
-    case SpvCapabilityPipes:                             out = SpvCapabilityKernel;           break;
-    case SpvCapabilityDeviceEnqueue:                     out = SpvCapabilityKernel;           break;
-    case SpvCapabilityLiteralSampler:                    out = SpvCapabilityKernel;           break;
-    case SpvCapabilityAtomicStorage:                     out = SpvCapabilityShader;           break;
-    case SpvCapabilityTessellationPointSize:             out = SpvCapabilityTessellation;     break;
-    case SpvCapabilityGeometryPointSize:                 out = SpvCapabilityGeometry;         break;
-    case SpvCapabilityImageGatherExtended:               out = SpvCapabilityShader;           break;
-    case SpvCapabilityStorageImageMultisample:           out = SpvCapabilityShader;           break;
-    case SpvCapabilityUniformBufferArrayDynamicIndexing: out = SpvCapabilityShader;           break;
-    case SpvCapabilitySampledImageArrayDynamicIndexing:  out = SpvCapabilityShader;           break;
-    case SpvCapabilityStorageBufferArrayDynamicIndexing: out = SpvCapabilityShader;           break;
-    case SpvCapabilityStorageImageArrayDynamicIndexing:  out = SpvCapabilityShader;           break;
-    case SpvCapabilityClipDistance:                      out = SpvCapabilityShader;           break;
-    case SpvCapabilityCullDistance:                      out = SpvCapabilityShader;           break;
-    case SpvCapabilityImageCubeArray:                    out = SpvCapabilitySampledCubeArray; break;
-    case SpvCapabilitySampleRateShading:                 out = SpvCapabilityShader;           break;
-    case SpvCapabilityImageRect:                         out = SpvCapabilitySampledRect;      break;
-    case SpvCapabilitySampledRect:                       out = SpvCapabilityShader;           break;
-    case SpvCapabilityGenericPointer:                    out = SpvCapabilityAddresses;        break;
-    case SpvCapabilityInt8:                              out = SpvCapabilityKernel;           break;
-    case SpvCapabilityInputAttachment:                   out = SpvCapabilityShader;           break;
-    case SpvCapabilitySparseResidency:                   out = SpvCapabilityShader;           break;
-    case SpvCapabilityMinLod:                            out = SpvCapabilityShader;           break;
-    case SpvCapabilitySampled1D:                         out = SpvCapabilityShader;           break;
-    case SpvCapabilityImage1D:                           out = SpvCapabilitySampled1D;        break;
-    case SpvCapabilitySampledCubeArray:                  out = SpvCapabilityShader;           break;
-    case SpvCapabilitySampledBuffer:                     out = SpvCapabilityShader;           break;
-    case SpvCapabilityImageBuffer:                       out = SpvCapabilitySampledBuffer;    break;
-    case SpvCapabilityImageMSArray:                      out = SpvCapabilityShader;           break;
-    case SpvCapabilityStorageImageExtendedFormats:       out = SpvCapabilityShader;           break;
-    case SpvCapabilityImageQuery:                        out = SpvCapabilityShader;           break;
-    case SpvCapabilityDerivativeControl:                 out = SpvCapabilityShader;           break;
-    case SpvCapabilityInterpolationFunction:             out = SpvCapabilityShader;           break;
-    case SpvCapabilityTransformFeedback:                 out = SpvCapabilityShader;           break;
-    case SpvCapabilityGeometryStreams:                   out = SpvCapabilityGeometry;         break;
-    case SpvCapabilityStorageImageReadWithoutFormat:     out = SpvCapabilityShader;           break;
-    case SpvCapabilityStorageImageWriteWithoutFormat:    out = SpvCapabilityShader;           break;
-    default:
-      out = cap;
-      break;
-  }
-  return out;
-}
-// clang-format on
-
 void ValidationState_t::registerCapability(SpvCapability cap) {
-  SpvCapability capability = cap;
-  // Set dependant capabilities
-  do {
-    module_capabilities_[capability] = true;
-    capability = getDependantCapability(capability);
-  } while (getDependantCapability(capability) != capability);
-
-  // Set Base capability
-  module_capabilities_[capability] = true;
+  module_capabilities_[cap] = true;
+  spv_operand_desc desc;
+  if (SPV_SUCCESS ==
+      grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc))
+    libspirv::ForEach(desc->capabilities,
+                      [this](SpvCapability c) { registerCapability(c); });
 }
 
-bool ValidationState_t::hasCapability(SpvCapability cap) {
+bool ValidationState_t::hasCapability(SpvCapability cap) const {
   return module_capabilities_[cap];
 }
 
+bool ValidationState_t::HasAnyOf(spv_capability_mask_t capabilities) const {
+  if (!capabilities)
+    return true;  // No capabilities requested: trivially satisfied.
+  bool found = false;
+  libspirv::ForEach(capabilities, [&found, this](SpvCapability c) {
+    found |= hasCapability(c);
+  });
+  return found;
+}
+
 Functions::Functions(ValidationState_t& module)
     : module_(module), in_function_(false), in_block_(false) {}
 
index f32bd56..4aeeb2a 100644 (file)
 #include "UnitSPIRV.h"
 #include "ValidateFixtures.h"
 
-#include <functional>
 #include <sstream>
 #include <string>
+#include <tuple>
 #include <utility>
 
-using std::function;
-using std::ostream;
-using std::ostream_iterator;
+namespace {
+
 using std::pair;
 using std::make_pair;
 using std::stringstream;
 using std::string;
-using std::tie;
 using std::tuple;
 using std::vector;
 
-using ::testing::HasSubstr;
+using testing::Combine;
+using testing::Values;
+using testing::ValuesIn;
 
 using ValidateCapability =
     spvtest::ValidateBase<tuple<string, pair<string, vector<string>>>,
@@ -269,20 +269,19 @@ const vector<string>& SampledRectDependencies() {
   "SampledRect",
   "ImageRect"};
   return *r;
-};
+}
 
 const vector<string>& SampledBufferDependencies() {
   static const auto r = new vector<string>{
   "SampledBuffer",
   "ImageBuffer"};
   return *r;
-};
+}
 
-INSTANTIATE_TEST_CASE_P(ExecutionModel,
-                        ValidateCapability,
-                        ::testing::Combine(
-                        testing::ValuesIn(AllCapabilities()),
-                        testing::Values(
+INSTANTIATE_TEST_CASE_P(ExecutionModel, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair("OpEntryPoint Vertex %func \"shader\" %var1 %var2\n",                 ShaderDependencies()),
 make_pair("OpEntryPoint TessellationControl %func \"shader\" %var1 %var2\n",    TessellationDependencies()),
 make_pair("OpEntryPoint TessellationEvaluation %func \"shader\" %var1 %var2\n", TessellationDependencies()),
@@ -292,11 +291,10 @@ make_pair("OpEntryPoint GLCompute %func \"shader\" %var1 %var2\n",
 make_pair("OpEntryPoint Kernel %func \"shader\" %var1 %var2\n",                 KernelDependencies())
                                                            )));
 
-INSTANTIATE_TEST_CASE_P(AddressingAndMemoryModel,
-                        ValidateCapability,
-                        ::testing::Combine(
-                        testing::ValuesIn(AllCapabilities()),
-                        testing::Values(
+INSTANTIATE_TEST_CASE_P(AddressingAndMemoryModel, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair(" OpCapability Shader"
           " OpMemoryModel Logical Simple",     AllCapabilities()),
 make_pair(" OpCapability Shader"
@@ -317,11 +315,10 @@ make_pair(" OpCapability Kernel"
           " OpMemoryModel Physical64 OpenCL",  AddressesDependencies())
                                                            )));
 
-INSTANTIATE_TEST_CASE_P(ExecutionMode,
-                        ValidateCapability,
-                        ::testing::Combine(
-                        testing::ValuesIn(AllCapabilities()),
-                        testing::Values(
+INSTANTIATE_TEST_CASE_P(ExecutionMode, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair("OpExecutionMode %func Invocations 42",          GeometryDependencies()),
 make_pair("OpExecutionMode %func SpacingEqual",            TessellationDependencies()),
 make_pair("OpExecutionMode %func SpacingFractionalEven",   TessellationDependencies()),
@@ -355,11 +352,10 @@ make_pair("OpExecutionMode %func VecTypeHint 2",           KernelDependencies())
 make_pair("OpExecutionMode %func ContractionOff",          KernelDependencies())
 )));
 
-INSTANTIATE_TEST_CASE_P(StorageClass,
-                        ValidateCapability,
-                        ::testing::Combine(
-testing::ValuesIn(AllCapabilities()),
-testing::Values(
+INSTANTIATE_TEST_CASE_P(StorageClass, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair(" %intt = OpTypeInt 32 0\n"
           " %ptrt = OpTypePointer UniformConstant %intt\n"
           " %var = OpVariable %ptrt UniformConstant\n",             AllCapabilities()),
@@ -392,11 +388,10 @@ make_pair(" %intt = OpTypeInt 32 0\n"
           " %var = OpVariable %ptrt Image\n",                       AllCapabilities())
   )));
 
-INSTANTIATE_TEST_CASE_P(Dim,
-                        ValidateCapability,
-                        ::testing::Combine(
-                        testing::ValuesIn(AllCapabilities()),
-                        testing::Values(
+INSTANTIATE_TEST_CASE_P(Dim, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair(" OpCapability ImageBasic"
           " %voidt = OpTypeVoid"
           " %imgt = OpTypeImage %voidt 1D 0 0 0 0 Unknown",       Sampled1DDependencies()),
@@ -422,11 +417,10 @@ make_pair(" OpCapability ImageBasic"
 
 // NOTE: All Sampler Address Modes require kernel capabilities but the
 // OpConstantSampler requires LiteralSampler which depends on Kernel
-INSTANTIATE_TEST_CASE_P(SamplerAddressingMode,
-                        ValidateCapability,
-                        ::testing::Combine(
-                        testing::ValuesIn(AllCapabilities()),
-                        testing::Values(
+INSTANTIATE_TEST_CASE_P(SamplerAddressingMode, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair(" %samplert = OpTypeSampler"
           " %sampler = OpConstantSampler %samplert None 1 Nearest",           vector<string>{"LiteralSampler"}),
 make_pair(" %samplert = OpTypeSampler"
@@ -450,11 +444,10 @@ make_pair(" %samplert = OpTypeSampler"
 //TODO(umar): Access Qualifier
 //TODO(umar): Function Parameter Attribute
 
-INSTANTIATE_TEST_CASE_P(Decoration,
-                        ValidateCapability,
-                        ::testing::Combine(
-                        testing::ValuesIn(AllCapabilities()),
-                        testing::Values(
+INSTANTIATE_TEST_CASE_P(Decoration, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair("OpDecorate %intt RelaxedPrecision\n",                    ShaderDependencies()),
 make_pair("OpDecorate %intt SpecId 1\n",                            ShaderDependencies()),
 make_pair("OpDecorate %intt Block\n",                               ShaderDependencies()),
@@ -500,21 +493,20 @@ make_pair("OpDecorate %intt Alignment 4\n",                         KernelDepend
   )));
 
 
-INSTANTIATE_TEST_CASE_P(BuiltIn,
-                        ValidateCapability,
-                        ::testing::Combine(
-                        testing::ValuesIn(AllCapabilities()),
-                        testing::Values(
+INSTANTIATE_TEST_CASE_P(BuiltIn, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair("OpDecorate %intt BuiltIn Position\n",                  ShaderDependencies()),
 make_pair("OpDecorate %intt BuiltIn PointSize\n",                 ShaderDependencies()),
-make_pair("OpDecorate %intt BuiltIn ClipDistance\n",              vector<string>{"ClipDistance"}),
-make_pair("OpDecorate %intt BuiltIn CullDistance\n",              vector<string>{"CullDistance"}),
+make_pair("OpDecorate %intt BuiltIn ClipDistance\n",              ShaderDependencies()),
+make_pair("OpDecorate %intt BuiltIn CullDistance\n",              ShaderDependencies()),
 make_pair("OpDecorate %intt BuiltIn VertexId\n",                  ShaderDependencies()),
 make_pair("OpDecorate %intt BuiltIn InstanceId\n",                ShaderDependencies()),
 make_pair("OpDecorate %intt BuiltIn PrimitiveId\n",               GeometryTessellationDependencies()),
 make_pair("OpDecorate %intt BuiltIn InvocationId\n",              GeometryTessellationDependencies()),
 make_pair("OpDecorate %intt BuiltIn Layer\n",                     GeometryDependencies()),
-//make_pair("OpDecorate %intt BuiltIn ViewPortIndex\n",           vector<string>{"MultiViewport"}),
+make_pair("OpDecorate %intt BuiltIn ViewportIndex\n",             GeometryDependencies()),
 make_pair("OpDecorate %intt BuiltIn TessLevelOuter\n",            TessellationDependencies()),
 make_pair("OpDecorate %intt BuiltIn TessLevelInner\n",            TessellationDependencies()),
 make_pair("OpDecorate %intt BuiltIn TessCoord\n",                 TessellationDependencies()),
@@ -522,30 +514,43 @@ make_pair("OpDecorate %intt BuiltIn PatchVertices\n",             TessellationDe
 make_pair("OpDecorate %intt BuiltIn FragCoord\n",                 ShaderDependencies()),
 make_pair("OpDecorate %intt BuiltIn PointCoord\n",                ShaderDependencies()),
 make_pair("OpDecorate %intt BuiltIn FrontFacing\n",               ShaderDependencies()),
-make_pair("OpDecorate %intt BuiltIn SampleId\n",                  vector<string>{"SampleRateShading"}),
-make_pair("OpDecorate %intt BuiltIn SamplePosition\n",            vector<string>{"SampleRateShading"}),
-make_pair("OpDecorate %intt BuiltIn SampleMask\n",                vector<string>{"SampleRateShading"}),
+make_pair("OpDecorate %intt BuiltIn SampleId\n",                  ShaderDependencies()),
+make_pair("OpDecorate %intt BuiltIn SamplePosition\n",            ShaderDependencies()),
+make_pair("OpDecorate %intt BuiltIn SampleMask\n",                ShaderDependencies()),
 make_pair("OpDecorate %intt BuiltIn FragDepth\n",                 ShaderDependencies()),
 make_pair("OpDecorate %intt BuiltIn HelperInvocation\n",          ShaderDependencies()),
-make_pair("OpDecorate %intt BuiltIn NumWorkgroups\n",             AllCapabilities()),
-make_pair("OpDecorate %intt BuiltIn WorkgroupSize\n",             AllCapabilities()),
-make_pair("OpDecorate %intt BuiltIn WorkgroupId\n",               AllCapabilities()),
-make_pair("OpDecorate %intt BuiltIn LocalInvocationId\n",         AllCapabilities()),
-make_pair("OpDecorate %intt BuiltIn GlobalInvocationId\n",        AllCapabilities()),
-make_pair("OpDecorate %intt BuiltIn LocalInvocationIndex",        AllCapabilities()),
-make_pair("OpDecorate %intt BuiltIn WorkDim\n",                   KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn GlobalSize\n",                KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn EnqueuedWorkgroupSize\n",     KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn GlobalOffset\n",              KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn GlobalLinearId\n",            KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn SubgroupSize\n",              KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn SubgroupMaxSize\n",           KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn NumSubgroups\n",              KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn NumEnqueuedSubgroups\n",      KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn SubgroupId\n",                KernelDependencies()),
-make_pair("OpDecorate %intt BuiltIn SubgroupLocalInvocationId\n", KernelDependencies()),
 make_pair("OpDecorate %intt BuiltIn VertexIndex\n",               ShaderDependencies()),
-make_pair("OpDecorate %intt BuiltIn InstanceIndex\n",             ShaderDependencies())
+make_pair("OpDecorate %intt BuiltIn InstanceIndex\n",             ShaderDependencies()),
+// Though the remaining builtins don't require Shader, the BuiltIn keyword
+// itself currently does require it.
+make_pair("OpDecorate %intt BuiltIn NumWorkgroups\n",             ShaderDependencies()),
+make_pair("OpDecorate %intt BuiltIn WorkgroupSize\n",             ShaderDependencies()),
+make_pair("OpDecorate %intt BuiltIn WorkgroupId\n",               ShaderDependencies()),
+make_pair("OpDecorate %intt BuiltIn LocalInvocationId\n",         ShaderDependencies()),
+make_pair("OpDecorate %intt BuiltIn GlobalInvocationId\n",        ShaderDependencies()),
+make_pair("OpDecorate %intt BuiltIn LocalInvocationIndex",        ShaderDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn WorkDim\n",                   KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn GlobalSize\n",                KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn EnqueuedWorkgroupSize\n",     KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn GlobalOffset\n",              KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn GlobalLinearId\n",            KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn SubgroupSize\n",              KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn SubgroupMaxSize\n",           KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn NumSubgroups\n",              KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn NumEnqueuedSubgroups\n",      KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn SubgroupId\n",                KernelDependencies()),
+make_pair("OpCapability Shader\n"
+          "OpDecorate %intt BuiltIn SubgroupLocalInvocationId\n", KernelDependencies())
                                                            )));
 
 // TODO(umar): Selection Control
@@ -558,17 +563,62 @@ make_pair("OpDecorate %intt BuiltIn InstanceIndex\n",             ShaderDependen
 // TODO(umar): Kernel Enqueue Flags
 // TODO(umar): Kernel Profiling Flags
 
-INSTANTIATE_TEST_CASE_P(MatrixOp,
-                        ValidateCapability,
-                        ::testing::Combine(
-                        testing::ValuesIn(AllCapabilities()),
-                        testing::Values(
+INSTANTIATE_TEST_CASE_P(MatrixOp, ValidateCapability,
+                        Combine(
+                            ValuesIn(AllCapabilities()),
+                            Values(
 make_pair(
           "%intt     = OpTypeInt 32 1\n"
           "%vec3     = OpTypeVector %intt 3\n"
           "%mat33    = OpTypeMatrix %vec3 3\n", MatrixDependencies()))));
 // clang-format on
 
+// Creates assembly containing an OpImageFetch instruction using operands for
+// the image-operands part.  The assembly defines constants %fzero and %izero
+// that can be used for operands where IDs are required.  The assembly is valid,
+// apart from not declaring any capabilities required by the operands.
+string ImageOperandsTemplate(const string& operands) {
+  stringstream ss;
+  // clang-format off
+  ss << R"(
+OpCapability Kernel
+OpMemoryModel Logical OpenCL
+
+%i32 = OpTypeInt 32 1
+%f32 = OpTypeFloat 32
+%v4i32 = OpTypeVector %i32 4
+%timg = OpTypeImage %i32 2D 0 0 0 0 Unknown
+%pimg = OpTypePointer UniformConstant %timg
+%tfun = OpTypeFunction %i32
+
+%vimg = OpVariable %pimg UniformConstant
+%izero = OpConstant %i32 0
+%fzero = OpConstant %f32 0.
+
+%main = OpFunction %i32 None %tfun
+%lbl = OpLabel
+%img = OpLoad %timg %vimg
+%r1 = OpImageFetch %v4i32 %img %izero )" << operands << R"(
+OpReturnValue %izero
+OpFunctionEnd
+)";
+  // clang-format on
+  return ss.str();
+}
+
+INSTANTIATE_TEST_CASE_P(
+    TwoImageOperandsMask, ValidateCapability,
+    Combine(
+        ValuesIn(AllCapabilities()),
+        Values(make_pair(ImageOperandsTemplate("Bias|Lod %fzero %fzero"),
+                         ShaderDependencies()),
+               make_pair(ImageOperandsTemplate("Lod|Offset %fzero %izero"),
+                         vector<string>{"ImageGatherExtended"}),
+               make_pair(ImageOperandsTemplate("Sample|MinLod %izero %fzero"),
+                         vector<string>{"MinLod"}),
+               make_pair(ImageOperandsTemplate("Lod|Sample %fzero %izero"),
+                         AllCapabilities()))));
+
 // TODO(umar): Instruction capability checks
 
 TEST_P(ValidateCapability, Capability) {
@@ -596,3 +646,5 @@ TEST_P(ValidateCapability, Capability) {
   CompileSuccessfully(ss.str());
   ASSERT_EQ(res, ValidateInstructions());
 }
+
+}  // namespace anonymous
diff --git a/test/ValidationState.cpp b/test/ValidationState.cpp
new file mode 100644 (file)
index 0000000..fc164b9
--- /dev/null
@@ -0,0 +1,101 @@
+// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2016 Google
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+// Unit tests for ValidationState_t.
+
+#include <gtest/gtest.h>
+#include <vector>
+
+#include "headers/spirv.h"
+
+#include "source/validate.h"
+
+namespace {
+using libspirv::ValidationState_t;
+using std::vector;
+
+// A test with a ValidationState_t member transparently.
+class ValidationStateTest : public testing::Test {
+ public:
+  ValidationStateTest()
+      : context_(spvContextCreate()), state_(&diag_, 0, context_) {}
+
+ protected:
+  spv_diagnostic diag_;
+  spv_context context_;
+  ValidationState_t state_;
+};
+
+// A test of ValidationState_t::HasAnyOf().
+class ValidationState_HasAnyOfTest : public ValidationStateTest {
+ protected:
+  spv_capability_mask_t mask(const vector<SpvCapability>& capabilities) {
+    spv_capability_mask_t m = 0;
+    for (auto c : capabilities) m |= SPV_CAPABILITY_AS_MASK(c);
+    return m;
+  }
+};
+
+TEST_F(ValidationState_HasAnyOfTest, EmptyMask) {
+  EXPECT_TRUE(state_.HasAnyOf(0));
+  state_.registerCapability(SpvCapabilityMatrix);
+  EXPECT_TRUE(state_.HasAnyOf(0));
+  state_.registerCapability(SpvCapabilityImageMipmap);
+  EXPECT_TRUE(state_.HasAnyOf(0));
+  state_.registerCapability(SpvCapabilityPipes);
+  EXPECT_TRUE(state_.HasAnyOf(0));
+  state_.registerCapability(SpvCapabilityStorageImageArrayDynamicIndexing);
+  EXPECT_TRUE(state_.HasAnyOf(0));
+  state_.registerCapability(SpvCapabilityClipDistance);
+  EXPECT_TRUE(state_.HasAnyOf(0));
+  state_.registerCapability(SpvCapabilityStorageImageWriteWithoutFormat);
+  EXPECT_TRUE(state_.HasAnyOf(0));
+}
+
+TEST_F(ValidationState_HasAnyOfTest, SingleCapMask) {
+  EXPECT_FALSE(state_.HasAnyOf(mask({SpvCapabilityMatrix})));
+  EXPECT_FALSE(state_.HasAnyOf(mask({SpvCapabilityImageMipmap})));
+  state_.registerCapability(SpvCapabilityMatrix);
+  EXPECT_TRUE(state_.HasAnyOf(mask({SpvCapabilityMatrix})));
+  EXPECT_FALSE(state_.HasAnyOf(mask({SpvCapabilityImageMipmap})));
+  state_.registerCapability(SpvCapabilityImageMipmap);
+  EXPECT_TRUE(state_.HasAnyOf(mask({SpvCapabilityMatrix})));
+  EXPECT_TRUE(state_.HasAnyOf(mask({SpvCapabilityImageMipmap})));
+}
+
+TEST_F(ValidationState_HasAnyOfTest, MultiCapMask) {
+  const auto mask1 = mask({SpvCapabilitySampledRect, SpvCapabilityImageBuffer});
+  const auto mask2 = mask({SpvCapabilityStorageImageWriteWithoutFormat,
+                           SpvCapabilityStorageImageReadWithoutFormat,
+                           SpvCapabilityGeometryStreams});
+  EXPECT_FALSE(state_.HasAnyOf(mask1));
+  EXPECT_FALSE(state_.HasAnyOf(mask2));
+  state_.registerCapability(SpvCapabilityImageBuffer);
+  EXPECT_TRUE(state_.HasAnyOf(mask1));
+  EXPECT_FALSE(state_.HasAnyOf(mask2));
+}
+}