Home | History | Annotate | Download | only in val
      1 // Copyright (c) 2017 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // Validates OpCapability instruction.
     16 
     17 #include "source/val/validate.h"
     18 
     19 #include <cassert>
     20 #include <string>
     21 #include <unordered_set>
     22 
     23 #include "source/diagnostic.h"
     24 #include "source/opcode.h"
     25 #include "source/val/instruction.h"
     26 #include "source/val/validation_state.h"
     27 
     28 namespace spvtools {
     29 namespace val {
     30 namespace {
     31 
     32 bool IsSupportGuaranteedVulkan_1_0(uint32_t capability) {
     33   switch (capability) {
     34     case SpvCapabilityMatrix:
     35     case SpvCapabilityShader:
     36     case SpvCapabilityInputAttachment:
     37     case SpvCapabilitySampled1D:
     38     case SpvCapabilityImage1D:
     39     case SpvCapabilitySampledBuffer:
     40     case SpvCapabilityImageBuffer:
     41     case SpvCapabilityImageQuery:
     42     case SpvCapabilityDerivativeControl:
     43       return true;
     44   }
     45   return false;
     46 }
     47 
     48 bool IsSupportGuaranteedVulkan_1_1(uint32_t capability) {
     49   if (IsSupportGuaranteedVulkan_1_0(capability)) return true;
     50   switch (capability) {
     51     case SpvCapabilityDeviceGroup:
     52     case SpvCapabilityMultiView:
     53       return true;
     54   }
     55   return false;
     56 }
     57 
     58 bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
     59   switch (capability) {
     60     case SpvCapabilityGeometry:
     61     case SpvCapabilityTessellation:
     62     case SpvCapabilityFloat64:
     63     case SpvCapabilityInt64:
     64     case SpvCapabilityInt16:
     65     case SpvCapabilityTessellationPointSize:
     66     case SpvCapabilityGeometryPointSize:
     67     case SpvCapabilityImageGatherExtended:
     68     case SpvCapabilityStorageImageMultisample:
     69     case SpvCapabilityUniformBufferArrayDynamicIndexing:
     70     case SpvCapabilitySampledImageArrayDynamicIndexing:
     71     case SpvCapabilityStorageBufferArrayDynamicIndexing:
     72     case SpvCapabilityStorageImageArrayDynamicIndexing:
     73     case SpvCapabilityClipDistance:
     74     case SpvCapabilityCullDistance:
     75     case SpvCapabilityImageCubeArray:
     76     case SpvCapabilitySampleRateShading:
     77     case SpvCapabilitySparseResidency:
     78     case SpvCapabilityMinLod:
     79     case SpvCapabilitySampledCubeArray:
     80     case SpvCapabilityImageMSArray:
     81     case SpvCapabilityStorageImageExtendedFormats:
     82     case SpvCapabilityInterpolationFunction:
     83     case SpvCapabilityStorageImageReadWithoutFormat:
     84     case SpvCapabilityStorageImageWriteWithoutFormat:
     85     case SpvCapabilityMultiViewport:
     86     case SpvCapabilityInt64Atomics:
     87     case SpvCapabilityTransformFeedback:
     88     case SpvCapabilityGeometryStreams:
     89     case SpvCapabilityFloat16:
     90     case SpvCapabilityInt8:
     91       return true;
     92   }
     93   return false;
     94 }
     95 
     96 bool IsSupportOptionalVulkan_1_1(uint32_t capability) {
     97   if (IsSupportOptionalVulkan_1_0(capability)) return true;
     98 
     99   switch (capability) {
    100     case SpvCapabilityGroupNonUniform:
    101     case SpvCapabilityGroupNonUniformVote:
    102     case SpvCapabilityGroupNonUniformArithmetic:
    103     case SpvCapabilityGroupNonUniformBallot:
    104     case SpvCapabilityGroupNonUniformShuffle:
    105     case SpvCapabilityGroupNonUniformShuffleRelative:
    106     case SpvCapabilityGroupNonUniformClustered:
    107     case SpvCapabilityGroupNonUniformQuad:
    108     case SpvCapabilityDrawParameters:
    109     // Alias SpvCapabilityStorageBuffer16BitAccess.
    110     case SpvCapabilityStorageUniformBufferBlock16:
    111     // Alias SpvCapabilityUniformAndStorageBuffer16BitAccess.
    112     case SpvCapabilityStorageUniform16:
    113     case SpvCapabilityStoragePushConstant16:
    114     case SpvCapabilityStorageInputOutput16:
    115     case SpvCapabilityDeviceGroup:
    116     case SpvCapabilityMultiView:
    117     case SpvCapabilityVariablePointersStorageBuffer:
    118     case SpvCapabilityVariablePointers:
    119       return true;
    120   }
    121   return false;
    122 }
    123 
    124 bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
    125   switch (capability) {
    126     case SpvCapabilityAddresses:
    127     case SpvCapabilityFloat16Buffer:
    128     case SpvCapabilityGroups:
    129     case SpvCapabilityInt16:
    130     case SpvCapabilityInt8:
    131     case SpvCapabilityKernel:
    132     case SpvCapabilityLinkage:
    133     case SpvCapabilityVector16:
    134       return true;
    135     case SpvCapabilityInt64:
    136       return !embedded_profile;
    137     case SpvCapabilityPipes:
    138       return embedded_profile;
    139   }
    140   return false;
    141 }
    142 
    143 bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
    144   if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true;
    145 
    146   switch (capability) {
    147     case SpvCapabilityDeviceEnqueue:
    148     case SpvCapabilityGenericPointer:
    149     case SpvCapabilityPipes:
    150       return true;
    151   }
    152   return false;
    153 }
    154 
    155 bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) {
    156   if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true;
    157 
    158   switch (capability) {
    159     case SpvCapabilitySubgroupDispatch:
    160     case SpvCapabilityPipeStorage:
    161       return true;
    162   }
    163   return false;
    164 }
    165 
    166 bool IsSupportOptionalOpenCL_1_2(uint32_t capability) {
    167   switch (capability) {
    168     case SpvCapabilityImageBasic:
    169     case SpvCapabilityFloat64:
    170       return true;
    171   }
    172   return false;
    173 }
    174 
    175 // Checks if |capability| was enabled by extension.
    176 bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
    177   spv_operand_desc operand_desc = nullptr;
    178   _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
    179                             &operand_desc);
    180 
    181   // operand_desc is expected to be not null, otherwise validator would have
    182   // failed at an earlier stage. This 'assert' is 'just in case'.
    183   assert(operand_desc);
    184 
    185   ExtensionSet operand_exts(operand_desc->numExtensions,
    186                             operand_desc->extensions);
    187   if (operand_exts.IsEmpty()) return false;
    188 
    189   return _.HasAnyOfExtensions(operand_exts);
    190 }
    191 
    192 bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _,
    193                                      uint32_t capability) {
    194   if (_.HasCapability(SpvCapabilityImageBasic)) {
    195     switch (capability) {
    196       case SpvCapabilityLiteralSampler:
    197       case SpvCapabilitySampled1D:
    198       case SpvCapabilityImage1D:
    199       case SpvCapabilitySampledBuffer:
    200       case SpvCapabilityImageBuffer:
    201         return true;
    202     }
    203     return false;
    204   }
    205   return false;
    206 }
    207 
    208 bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _,
    209                                      uint32_t capability) {
    210   if (_.HasCapability(SpvCapabilityImageBasic)) {
    211     switch (capability) {
    212       case SpvCapabilityImageReadWrite:
    213       case SpvCapabilityLiteralSampler:
    214       case SpvCapabilitySampled1D:
    215       case SpvCapabilityImage1D:
    216       case SpvCapabilitySampledBuffer:
    217       case SpvCapabilityImageBuffer:
    218         return true;
    219     }
    220     return false;
    221   }
    222   return false;
    223 }
    224 
    225 bool IsSupportGuaranteedWebGPU(uint32_t capability) {
    226   switch (capability) {
    227     case SpvCapabilityMatrix:
    228     case SpvCapabilityShader:
    229     case SpvCapabilitySampled1D:
    230     case SpvCapabilityImage1D:
    231     case SpvCapabilityDerivativeControl:
    232     case SpvCapabilityImageQuery:
    233       return true;
    234   }
    235   return false;
    236 }
    237 
    238 }  // namespace
    239 
    240 // Validates that capability declarations use operands allowed in the current
    241 // context.
    242 spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) {
    243   if (inst->opcode() != SpvOpCapability) return SPV_SUCCESS;
    244 
    245   assert(inst->operands().size() == 1);
    246 
    247   const spv_parsed_operand_t& operand = inst->operand(0);
    248 
    249   assert(operand.num_words == 1);
    250   assert(operand.offset < inst->words().size());
    251 
    252   const uint32_t capability = inst->word(operand.offset);
    253   const auto capability_str = [&_, capability]() {
    254     spv_operand_desc desc = nullptr;
    255     if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
    256                                   &desc) != SPV_SUCCESS ||
    257         !desc) {
    258       return std::string("Unknown");
    259     }
    260     return std::string(desc->name);
    261   };
    262 
    263   const auto env = _.context()->target_env;
    264   const bool opencl_embedded = env == SPV_ENV_OPENCL_EMBEDDED_1_2 ||
    265                                env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
    266                                env == SPV_ENV_OPENCL_EMBEDDED_2_1 ||
    267                                env == SPV_ENV_OPENCL_EMBEDDED_2_2;
    268   const std::string opencl_profile = opencl_embedded ? "Embedded" : "Full";
    269   if (env == SPV_ENV_VULKAN_1_0) {
    270     if (!IsSupportGuaranteedVulkan_1_0(capability) &&
    271         !IsSupportOptionalVulkan_1_0(capability) &&
    272         !IsEnabledByExtension(_, capability)) {
    273       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
    274              << "Capability " << capability_str()
    275              << " is not allowed by Vulkan 1.0 specification"
    276              << " (or requires extension)";
    277     }
    278   } else if (env == SPV_ENV_VULKAN_1_1) {
    279     if (!IsSupportGuaranteedVulkan_1_1(capability) &&
    280         !IsSupportOptionalVulkan_1_1(capability) &&
    281         !IsEnabledByExtension(_, capability)) {
    282       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
    283              << "Capability " << capability_str()
    284              << " is not allowed by Vulkan 1.1 specification"
    285              << " (or requires extension)";
    286     }
    287   } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) {
    288     if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) &&
    289         !IsSupportOptionalOpenCL_1_2(capability) &&
    290         !IsEnabledByExtension(_, capability) &&
    291         !IsEnabledByCapabilityOpenCL_1_2(_, capability)) {
    292       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
    293              << "Capability " << capability_str()
    294              << " is not allowed by OpenCL 1.2 " << opencl_profile
    295              << " Profile specification"
    296              << " (or requires extension or capability)";
    297     }
    298   } else if (env == SPV_ENV_OPENCL_2_0 || env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
    299              env == SPV_ENV_OPENCL_2_1 || env == SPV_ENV_OPENCL_EMBEDDED_2_1) {
    300     if (!IsSupportGuaranteedOpenCL_2_0(capability, opencl_embedded) &&
    301         !IsSupportOptionalOpenCL_1_2(capability) &&
    302         !IsEnabledByExtension(_, capability) &&
    303         !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
    304       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
    305              << "Capability " << capability_str()
    306              << " is not allowed by OpenCL 2.0/2.1 " << opencl_profile
    307              << " Profile specification"
    308              << " (or requires extension or capability)";
    309     }
    310   } else if (env == SPV_ENV_OPENCL_2_2 || env == SPV_ENV_OPENCL_EMBEDDED_2_2) {
    311     if (!IsSupportGuaranteedOpenCL_2_2(capability, opencl_embedded) &&
    312         !IsSupportOptionalOpenCL_1_2(capability) &&
    313         !IsEnabledByExtension(_, capability) &&
    314         !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
    315       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
    316              << "Capability " << capability_str()
    317              << " is not allowed by OpenCL 2.2 " << opencl_profile
    318              << " Profile specification"
    319              << " (or requires extension or capability)";
    320     }
    321   } else if (env == SPV_ENV_WEBGPU_0) {
    322     if (!IsSupportGuaranteedWebGPU(capability) &&
    323         !IsEnabledByExtension(_, capability)) {
    324       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
    325              << "Capability " << capability_str()
    326              << " is not allowed by WebGPU specification"
    327              << " (or requires extension)";
    328     }
    329   }
    330 
    331   return SPV_SUCCESS;
    332 }
    333 
    334 }  // namespace val
    335 }  // namespace spvtools
    336