Support OpenCL 1.2 and 2.0 target environments
authorPierre Moreau <dev@pmoreau.org>
Wed, 29 Nov 2017 23:49:23 +0000 (00:49 +0100)
committerDavid Neto <dneto@google.com>
Tue, 12 Dec 2017 16:35:39 +0000 (11:35 -0500)
include: Add target environment enums for OpenCL 1.2 and 2.0

Validator: Validate OpenCL capabilities

Update validate capabilities to handle embedded profiles

Add test for OpenCL capabilities validation

Update messages to mention the OpenCL profile used

Re-format val_capability_test.cpp

include/spirv-tools/libspirv.h
source/ext_inst.cpp
source/opcode.cpp
source/operand.cpp
source/spirv_target_env.cpp
source/table.cpp
source/validate_capability.cpp
test/target_env_test.cpp
test/unit_spirv.h
test/val/val_capability_test.cpp

index 9119ca3..cc4d08a 100644 (file)
@@ -374,8 +374,8 @@ typedef enum {
   SPV_ENV_UNIVERSAL_1_0,  // SPIR-V 1.0 latest revision, no other restrictions.
   SPV_ENV_VULKAN_1_0,     // Vulkan 1.0 latest revision.
   SPV_ENV_UNIVERSAL_1_1,  // SPIR-V 1.1 latest revision, no other restrictions.
-  SPV_ENV_OPENCL_2_1,     // OpenCL 2.1 latest revision.
-  SPV_ENV_OPENCL_2_2,     // OpenCL 2.2 latest revision.
+  SPV_ENV_OPENCL_2_1,     // OpenCL Full Profile 2.1 latest revision.
+  SPV_ENV_OPENCL_2_2,     // OpenCL Full Profile 2.2 latest revision.
   SPV_ENV_OPENGL_4_0,     // OpenGL 4.0 plus GL_ARB_gl_spirv, latest revisions.
   SPV_ENV_OPENGL_4_1,     // OpenGL 4.1 plus GL_ARB_gl_spirv, latest revisions.
   SPV_ENV_OPENGL_4_2,     // OpenGL 4.2 plus GL_ARB_gl_spirv, latest revisions.
@@ -383,6 +383,16 @@ typedef enum {
   // There is no variant for OpenGL 4.4.
   SPV_ENV_OPENGL_4_5,     // OpenGL 4.5 plus GL_ARB_gl_spirv, latest revisions.
   SPV_ENV_UNIVERSAL_1_2,  // SPIR-V 1.2, latest revision, no other restrictions.
+  SPV_ENV_OPENCL_1_2,     // OpenCL Full Profile 1.2 plus cl_khr_il_program,
+                          // latest revision.
+  SPV_ENV_OPENCL_EMBEDDED_1_2,  // OpenCL Embedded Profile 1.2 plus
+                                // cl_khr_il_program, latest revision.
+  SPV_ENV_OPENCL_2_0,  // OpenCL Full Profile 2.0 plus cl_khr_il_program,
+                       // latest revision.
+  SPV_ENV_OPENCL_EMBEDDED_2_0,  // OpenCL Embedded Profile 2.0 plus
+                                // cl_khr_il_program, latest revision.
+  SPV_ENV_OPENCL_EMBEDDED_2_1,  // OpenCL Embedded Profile 2.1 latest revision.
+  SPV_ENV_OPENCL_EMBEDDED_2_2,  // OpenCL Embedded Profile 2.2 latest revision.
 } spv_target_env;
 
 // SPIR-V Validator can be parameterized with the following Universal Limits.
index 7942c47..fc8a486 100644 (file)
@@ -59,8 +59,14 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
     case SPV_ENV_VULKAN_1_0:
     case SPV_ENV_UNIVERSAL_1_1:
     case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_OPENCL_1_2:
+    case SPV_ENV_OPENCL_EMBEDDED_1_2:
+    case SPV_ENV_OPENCL_2_0:
+    case SPV_ENV_OPENCL_EMBEDDED_2_0:
     case SPV_ENV_OPENCL_2_1:
+    case SPV_ENV_OPENCL_EMBEDDED_2_1:
     case SPV_ENV_OPENCL_2_2:
+    case SPV_ENV_OPENCL_EMBEDDED_2_2:
     case SPV_ENV_OPENGL_4_0:
     case SPV_ENV_OPENGL_4_1:
     case SPV_ENV_OPENGL_4_2:
index a9ab1e6..7a84046 100644 (file)
@@ -91,7 +91,12 @@ spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable,
   switch (env) {
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_OPENCL_1_2:
+    case SPV_ENV_OPENCL_EMBEDDED_1_2:
+    case SPV_ENV_OPENCL_2_0:
+    case SPV_ENV_OPENCL_EMBEDDED_2_0:
     case SPV_ENV_OPENCL_2_1:
+    case SPV_ENV_OPENCL_EMBEDDED_2_1:
     case SPV_ENV_OPENGL_4_0:
     case SPV_ENV_OPENGL_4_1:
     case SPV_ENV_OPENGL_4_2:
@@ -104,6 +109,7 @@ spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable,
       return SPV_SUCCESS;
     case SPV_ENV_UNIVERSAL_1_2:
     case SPV_ENV_OPENCL_2_2:
+    case SPV_ENV_OPENCL_EMBEDDED_2_2:
       *pInstTable = &kTable_1_2;
       return SPV_SUCCESS;
   }
index d406199..9751767 100644 (file)
@@ -41,7 +41,12 @@ spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable,
   switch (env) {
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_OPENCL_1_2:
+    case SPV_ENV_OPENCL_EMBEDDED_1_2:
+    case SPV_ENV_OPENCL_2_0:
+    case SPV_ENV_OPENCL_EMBEDDED_2_0:
     case SPV_ENV_OPENCL_2_1:
+    case SPV_ENV_OPENCL_EMBEDDED_2_1:
     case SPV_ENV_OPENGL_4_0:
     case SPV_ENV_OPENGL_4_1:
     case SPV_ENV_OPENGL_4_2:
@@ -54,6 +59,7 @@ spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable,
       return SPV_SUCCESS;
     case SPV_ENV_UNIVERSAL_1_2:
     case SPV_ENV_OPENCL_2_2:
+    case SPV_ENV_OPENCL_EMBEDDED_2_2:
       *pOperandTable = &kTable_1_2;
       return SPV_SUCCESS;
   }
index ed47f52..bfe5c76 100644 (file)
@@ -26,10 +26,22 @@ const char* spvTargetEnvDescription(spv_target_env env) {
       return "SPIR-V 1.0 (under Vulkan 1.0 semantics)";
     case SPV_ENV_UNIVERSAL_1_1:
       return "SPIR-V 1.1";
+    case SPV_ENV_OPENCL_1_2:
+      return "SPIR-V 1.0 (under OpenCL 1.2 Full Profile semantics)";
+    case SPV_ENV_OPENCL_EMBEDDED_1_2:
+      return "SPIR-V 1.0 (under OpenCL 1.2 Embedded Profile semantics)";
+    case SPV_ENV_OPENCL_2_0:
+      return "SPIR-V 1.0 (under OpenCL 2.0 Full Profile semantics)";
+    case SPV_ENV_OPENCL_EMBEDDED_2_0:
+      return "SPIR-V 1.0 (under OpenCL 2.0 Embedded Profile semantics)";
     case SPV_ENV_OPENCL_2_1:
-      return "SPIR-V 1.0 (under OpenCL 2.1 semantics)";
+      return "SPIR-V 1.0 (under OpenCL 2.1 Full Profile semantics)";
+    case SPV_ENV_OPENCL_EMBEDDED_2_1:
+      return "SPIR-V 1.0 (under OpenCL 2.1 Embedded Profile semantics)";
     case SPV_ENV_OPENCL_2_2:
-      return "SPIR-V 1.1 (under OpenCL 2.2 semantics)";
+      return "SPIR-V 1.1 (under OpenCL 2.2 Full Profile semantics)";
+    case SPV_ENV_OPENCL_EMBEDDED_2_2:
+      return "SPIR-V 1.1 (under OpenCL 2.2 Embedded Profile semantics)";
     case SPV_ENV_OPENGL_4_0:
       return "SPIR-V 1.0 (under OpenCL 4.0 semantics)";
     case SPV_ENV_OPENGL_4_1:
@@ -51,7 +63,12 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
   switch (env) {
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_OPENCL_1_2:
+    case SPV_ENV_OPENCL_EMBEDDED_1_2:
+    case SPV_ENV_OPENCL_2_0:
+    case SPV_ENV_OPENCL_EMBEDDED_2_0:
     case SPV_ENV_OPENCL_2_1:
+    case SPV_ENV_OPENCL_EMBEDDED_2_1:
     case SPV_ENV_OPENGL_4_0:
     case SPV_ENV_OPENGL_4_1:
     case SPV_ENV_OPENGL_4_2:
@@ -62,6 +79,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
       return SPV_SPIRV_VERSION_WORD(1, 1);
     case SPV_ENV_UNIVERSAL_1_2:
     case SPV_ENV_OPENCL_2_2:
+    case SPV_ENV_OPENCL_EMBEDDED_2_2:
       return SPV_SPIRV_VERSION_WORD(1, 2);
   }
   assert(0 && "Unhandled SPIR-V target environment");
@@ -84,9 +102,27 @@ bool spvParseTargetEnv(const char* s, spv_target_env* env) {
   } else if (match("spv1.2")) {
     if (env) *env = SPV_ENV_UNIVERSAL_1_2;
     return true;
+  } else if (match("opencl1.2embedded")) {
+    if (env) *env = SPV_ENV_OPENCL_EMBEDDED_1_2;
+    return true;
+  } else if (match("opencl1.2")) {
+    if (env) *env = SPV_ENV_OPENCL_1_2;
+    return true;
+  } else if (match("opencl2.0embedded")) {
+    if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_0;
+    return true;
+  } else if (match("opencl2.0")) {
+    if (env) *env = SPV_ENV_OPENCL_2_0;
+    return true;
+  } else if (match("opencl2.1embedded")) {
+    if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_1;
+    return true;
   } else if (match("opencl2.1")) {
     if (env) *env = SPV_ENV_OPENCL_2_1;
     return true;
+  } else if (match("opencl2.2embedded")) {
+    if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_2;
+    return true;
   } else if (match("opencl2.2")) {
     if (env) *env = SPV_ENV_OPENCL_2_2;
     return true;
index 8054051..92296ec 100644 (file)
@@ -21,8 +21,14 @@ spv_context spvContextCreate(spv_target_env env) {
     case SPV_ENV_UNIVERSAL_1_0:
     case SPV_ENV_VULKAN_1_0:
     case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_OPENCL_1_2:
+    case SPV_ENV_OPENCL_EMBEDDED_1_2:
+    case SPV_ENV_OPENCL_2_0:
+    case SPV_ENV_OPENCL_EMBEDDED_2_0:
     case SPV_ENV_OPENCL_2_1:
+    case SPV_ENV_OPENCL_EMBEDDED_2_1:
     case SPV_ENV_OPENCL_2_2:
+    case SPV_ENV_OPENCL_EMBEDDED_2_2:
     case SPV_ENV_OPENGL_4_0:
     case SPV_ENV_OPENGL_4_1:
     case SPV_ENV_OPENGL_4_2:
index a3341ab..02dad8b 100644 (file)
@@ -77,6 +77,57 @@ bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
   return false;
 }
 
+bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
+  switch (capability) {
+    case SpvCapabilityAddresses:
+    case SpvCapabilityFloat16Buffer:
+    case SpvCapabilityGroups:
+    case SpvCapabilityInt16:
+    case SpvCapabilityInt8:
+    case SpvCapabilityKernel:
+    case SpvCapabilityLinkage:
+    case SpvCapabilityVector16:
+      return true;
+    case SpvCapabilityInt64:
+      return !embedded_profile;
+    case SpvCapabilityPipes:
+      return embedded_profile;
+  }
+  return false;
+}
+
+bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
+  if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true;
+
+  switch (capability) {
+    case SpvCapabilityDeviceEnqueue:
+    case SpvCapabilityGenericPointer:
+    case SpvCapabilityPipes:
+      return true;
+  }
+  return false;
+}
+
+bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) {
+  if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true;
+
+  switch (capability) {
+    case SpvCapabilitySubgroupDispatch:
+    case SpvCapabilityPipeStorage:
+      return true;
+  }
+  return false;
+}
+
+bool IsSupportOptionalOpenCL_1_2(uint32_t capability) {
+  switch (capability) {
+    case SpvCapabilityImageBasic:
+    case SpvCapabilityFloat64:
+      return true;
+  }
+  return false;
+}
+
 // Checks if |capability| was enabled by extension.
 bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
   spv_operand_desc operand_desc = nullptr;
@@ -94,6 +145,39 @@ bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
   return _.HasAnyOfExtensions(operand_exts);
 }
 
+bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _,
+                                     uint32_t capability) {
+  if (_.HasCapability(SpvCapabilityImageBasic)) {
+    switch (capability) {
+      case SpvCapabilityLiteralSampler:
+      case SpvCapabilitySampled1D:
+      case SpvCapabilityImage1D:
+      case SpvCapabilitySampledBuffer:
+      case SpvCapabilityImageBuffer:
+        return true;
+    }
+    return false;
+  }
+  return false;
+}
+
+bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _,
+                                     uint32_t capability) {
+  if (_.HasCapability(SpvCapabilityImageBasic)) {
+    switch (capability) {
+      case SpvCapabilityImageReadWrite:
+      case SpvCapabilityLiteralSampler:
+      case SpvCapabilitySampled1D:
+      case SpvCapabilityImage1D:
+      case SpvCapabilitySampledBuffer:
+      case SpvCapabilityImageBuffer:
+        return true;
+    }
+    return false;
+  }
+  return false;
+}
+
 }  // namespace
 
 // Validates that capability declarations use operands allowed in the current
@@ -113,6 +197,11 @@ spv_result_t CapabilityPass(ValidationState_t& _,
   const uint32_t capability = inst->words[operand.offset];
 
   const auto env = _.context()->target_env;
+  const bool opencl_embedded = env == SPV_ENV_OPENCL_EMBEDDED_1_2 ||
+                               env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
+                               env == SPV_ENV_OPENCL_EMBEDDED_2_1 ||
+                               env == SPV_ENV_OPENCL_EMBEDDED_2_2;
+  const std::string opencl_profile = opencl_embedded ? "Embedded" : "Full";
   if (env == SPV_ENV_VULKAN_1_0) {
     if (!IsSupportGuaranteedVulkan_1_0(capability) &&
         !IsSupportOptionalVulkan_1_0(capability) &&
@@ -122,6 +211,40 @@ spv_result_t CapabilityPass(ValidationState_t& _,
              << " is not allowed by Vulkan 1.0 specification"
              << " (or requires extension)";
     }
+  } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) {
+    if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) &&
+        !IsSupportOptionalOpenCL_1_2(capability) &&
+        !IsEnabledByExtension(_, capability) &&
+        !IsEnabledByCapabilityOpenCL_1_2(_, capability)) {
+      return _.diag(SPV_ERROR_INVALID_CAPABILITY)
+             << "Capability value " << capability
+             << " is not allowed by OpenCL 1.2 " << opencl_profile
+             << " Profile specification"
+             << " (or requires extension or capability)";
+    }
+  } else if (env == SPV_ENV_OPENCL_2_0 || env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
+             env == SPV_ENV_OPENCL_2_1 || env == SPV_ENV_OPENCL_EMBEDDED_2_1) {
+    if (!IsSupportGuaranteedOpenCL_2_0(capability, opencl_embedded) &&
+        !IsSupportOptionalOpenCL_1_2(capability) &&
+        !IsEnabledByExtension(_, capability) &&
+        !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
+      return _.diag(SPV_ERROR_INVALID_CAPABILITY)
+             << "Capability value " << capability
+             << " is not allowed by OpenCL 2.0/2.1 " << opencl_profile
+             << " Profile specification"
+             << " (or requires extension or capability)";
+    }
+  } else if (env == SPV_ENV_OPENCL_2_2 || env == SPV_ENV_OPENCL_EMBEDDED_2_2) {
+    if (!IsSupportGuaranteedOpenCL_2_2(capability, opencl_embedded) &&
+        !IsSupportOptionalOpenCL_1_2(capability) &&
+        !IsEnabledByExtension(_, capability) &&
+        !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
+      return _.diag(SPV_ERROR_INVALID_CAPABILITY)
+             << "Capability value " << capability
+             << " is not allowed by OpenCL 2.2 " << opencl_profile
+             << " Profile specification"
+             << " (or requires extension or capability)";
+    }
   }
 
   return SPV_SUCCESS;
index f47195b..f67a461 100644 (file)
@@ -49,7 +49,7 @@ INSTANTIATE_TEST_CASE_P(AllTargetEnvs, TargetEnvTest,
 
 TEST(GetContextTest, InvalidTargetEnvProducesNull) {
   // Use a value beyond the last valid enum value.
-  spv_context context = spvContextCreate(static_cast<spv_target_env>(15));
+  spv_context context = spvContextCreate(static_cast<spv_target_env>(17));
   EXPECT_EQ(context, nullptr);
 }
 
@@ -69,22 +69,29 @@ TEST_P(TargetParseTest, InvalidTargetEnvProducesNull) {
   EXPECT_THAT(env, Eq(GetParam().env));
 }
 
-INSTANTIATE_TEST_CASE_P(TargetParsing, TargetParseTest,
-                        ValuesIn(std::vector<ParseCase>{
-                            {"spv1.0", true, SPV_ENV_UNIVERSAL_1_0},
-                            {"spv1.1", true, SPV_ENV_UNIVERSAL_1_1},
-                            {"spv1.2", true, SPV_ENV_UNIVERSAL_1_2},
-                            {"vulkan1.0", true, SPV_ENV_VULKAN_1_0},
-                            {"opencl2.1", true, SPV_ENV_OPENCL_2_1},
-                            {"opencl2.2", true, SPV_ENV_OPENCL_2_2},
-                            {"opengl4.0", true, SPV_ENV_OPENGL_4_0},
-                            {"opengl4.1", true, SPV_ENV_OPENGL_4_1},
-                            {"opengl4.2", true, SPV_ENV_OPENGL_4_2},
-                            {"opengl4.3", true, SPV_ENV_OPENGL_4_3},
-                            {"opengl4.5", true, SPV_ENV_OPENGL_4_5},
-                            {nullptr, false, SPV_ENV_UNIVERSAL_1_0},
-                            {"", false, SPV_ENV_UNIVERSAL_1_0},
-                            {"abc", false, SPV_ENV_UNIVERSAL_1_0},
-                        }));
+INSTANTIATE_TEST_CASE_P(
+    TargetParsing, TargetParseTest,
+    ValuesIn(std::vector<ParseCase>{
+        {"spv1.0", true, SPV_ENV_UNIVERSAL_1_0},
+        {"spv1.1", true, SPV_ENV_UNIVERSAL_1_1},
+        {"spv1.2", true, SPV_ENV_UNIVERSAL_1_2},
+        {"vulkan1.0", true, SPV_ENV_VULKAN_1_0},
+        {"opencl2.1", true, SPV_ENV_OPENCL_2_1},
+        {"opencl2.2", true, SPV_ENV_OPENCL_2_2},
+        {"opengl4.0", true, SPV_ENV_OPENGL_4_0},
+        {"opengl4.1", true, SPV_ENV_OPENGL_4_1},
+        {"opengl4.2", true, SPV_ENV_OPENGL_4_2},
+        {"opengl4.3", true, SPV_ENV_OPENGL_4_3},
+        {"opengl4.5", true, SPV_ENV_OPENGL_4_5},
+        {"opencl1.2", true, SPV_ENV_OPENCL_1_2},
+        {"opencl1.2embedded", true, SPV_ENV_OPENCL_EMBEDDED_1_2},
+        {"opencl2.0", true, SPV_ENV_OPENCL_2_0},
+        {"opencl2.0embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_0},
+        {"opencl2.1embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_1},
+        {"opencl2.2embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_2},
+        {nullptr, false, SPV_ENV_UNIVERSAL_1_0},
+        {"", false, SPV_ENV_UNIVERSAL_1_0},
+        {"abc", false, SPV_ENV_UNIVERSAL_1_0},
+    }));
 
 }  // anonymous namespace
index 416d6e5..c848fe9 100644 (file)
@@ -208,10 +208,15 @@ inline std::string MakeLongUTF8String(size_t num_4_byte_chars) {
 
 // Returns a vector of all valid target environment enums.
 inline std::vector<spv_target_env> AllTargetEnvironments() {
-  return {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENCL_2_1,
-          SPV_ENV_OPENCL_2_2,    SPV_ENV_VULKAN_1_0,    SPV_ENV_OPENGL_4_0,
-          SPV_ENV_OPENGL_4_1,    SPV_ENV_OPENGL_4_2,    SPV_ENV_OPENGL_4_3,
-          SPV_ENV_OPENGL_4_5,    SPV_ENV_UNIVERSAL_1_2};
+  return {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
+          SPV_ENV_OPENCL_1_2,    SPV_ENV_OPENCL_EMBEDDED_1_2,
+          SPV_ENV_OPENCL_2_0,    SPV_ENV_OPENCL_EMBEDDED_2_0,
+          SPV_ENV_OPENCL_2_1,    SPV_ENV_OPENCL_EMBEDDED_2_1,
+          SPV_ENV_OPENCL_2_2,    SPV_ENV_OPENCL_EMBEDDED_2_2,
+          SPV_ENV_VULKAN_1_0,    SPV_ENV_OPENGL_4_0,
+          SPV_ENV_OPENGL_4_1,    SPV_ENV_OPENGL_4_2,
+          SPV_ENV_OPENGL_4_3,    SPV_ENV_OPENGL_4_5,
+          SPV_ENV_UNIVERSAL_1_2};
 }
 
 // Returns the capabilities in a CapabilitySet as an ordered vector.
index 28df365..29a718f 100644 (file)
@@ -1626,4 +1626,340 @@ OpDecorate %intt BuiltIn PointSize
               HasSubstr("Capability value 4427 is not allowed by Vulkan 1.0"));
 }
 
+TEST_F(ValidateCapability, NonOpenCL12FullCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Pipes
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)";
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 17 is not allowed by OpenCL 1.2 Full Profile"));
+}
+
+TEST_F(ValidateCapability, OpenCL12FullEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability ImageBasic
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_1_2));
+}
+
+TEST_F(ValidateCapability, OpenCL12FullNotEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 43 is not allowed by OpenCL 1.2 Full Profile"));
+}
+
+TEST_F(ValidateCapability, NonOpenCL12EmbeddedCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Int64
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)";
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 11 is not allowed by OpenCL 1.2 Embedded Profile"));
+}
+
+TEST_F(ValidateCapability, OpenCL12EmbeddedEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability ImageBasic
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_1_2));
+}
+
+TEST_F(ValidateCapability, OpenCL12EmbeddedNotEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 43 is not allowed by OpenCL 1.2 Embedded Profile"));
+}
+
+TEST_F(ValidateCapability, OpenCL20FullCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Pipes
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)";
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_0));
+}
+
+TEST_F(ValidateCapability, NonOpenCL20FullCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Matrix
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)";
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_2_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 0 is not allowed by OpenCL 2.0/2.1 Full Profile"));
+}
+
+TEST_F(ValidateCapability, OpenCL20FullEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability ImageBasic
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_0));
+}
+
+TEST_F(ValidateCapability, OpenCL20FullNotEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_2_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 43 is not allowed by OpenCL 2.0/2.1 Full Profile"));
+}
+
+TEST_F(ValidateCapability, NonOpenCL20EmbeddedCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Int64
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)";
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Capability value 11 is not allowed by OpenCL 2.0/2.1 "
+                        "Embedded Profile"));
+}
+
+TEST_F(ValidateCapability, OpenCL20EmbeddedEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability ImageBasic
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_0));
+}
+
+TEST_F(ValidateCapability, OpenCL20EmbeddedNotEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Capability value 43 is not allowed by OpenCL 2.0/2.1 "
+                        "Embedded Profile"));
+}
+
+TEST_F(ValidateCapability, OpenCL22FullCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability PipeStorage
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)";
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_2));
+}
+
+TEST_F(ValidateCapability, NonOpenCL22FullCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Matrix
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)";
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_2_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 0 is not allowed by OpenCL 2.2 Full Profile"));
+}
+
+TEST_F(ValidateCapability, OpenCL22FullEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability ImageBasic
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_2));
+}
+
+TEST_F(ValidateCapability, OpenCL22FullNotEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_2_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 43 is not allowed by OpenCL 2.2 Full Profile"));
+}
+
+TEST_F(ValidateCapability, NonOpenCL22EmbeddedCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Int64
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)";
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 11 is not allowed by OpenCL 2.2 Embedded Profile"));
+}
+
+TEST_F(ValidateCapability, OpenCL22EmbeddedEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability ImageBasic
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_2));
+}
+
+TEST_F(ValidateCapability, OpenCL22EmbeddedNotEnabledByCapability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Sampled1D
+OpMemoryModel Physical64 OpenCL
+%u32    = OpTypeInt 32 0
+)" + string(kVoidFVoid);
+
+  CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Capability value 43 is not allowed by OpenCL 2.2 Embedded Profile"));
+}
+
 }  // namespace