Home | History | Annotate | Download | only in val
      1 // Copyright (c) 2018 Google LLC.
      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 #include "source/val/validate_scopes.h"
     16 
     17 #include "source/diagnostic.h"
     18 #include "source/spirv_target_env.h"
     19 #include "source/val/instruction.h"
     20 #include "source/val/validation_state.h"
     21 
     22 namespace spvtools {
     23 namespace val {
     24 
     25 spv_result_t ValidateExecutionScope(ValidationState_t& _,
     26                                     const Instruction* inst, uint32_t scope) {
     27   SpvOp opcode = inst->opcode();
     28   bool is_int32 = false, is_const_int32 = false;
     29   uint32_t value = 0;
     30   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
     31 
     32   if (!is_int32) {
     33     return _.diag(SPV_ERROR_INVALID_DATA, inst)
     34            << spvOpcodeString(opcode)
     35            << ": expected Execution Scope to be a 32-bit int";
     36   }
     37 
     38   if (!is_const_int32) {
     39     if (_.HasCapability(SpvCapabilityShader)) {
     40       return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Scope ids must be "
     41                                                      "OpConstant when Shader "
     42                                                      "capability is present";
     43     }
     44     return SPV_SUCCESS;
     45   }
     46 
     47   // Vulkan specific rules
     48   if (spvIsVulkanEnv(_.context()->target_env)) {
     49     // Vulkan 1.1 specific rules
     50     if (_.context()->target_env != SPV_ENV_VULKAN_1_0) {
     51       // Scope for Non Uniform Group Operations must be limited to Subgroup
     52       if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
     53           value != SpvScopeSubgroup) {
     54         return _.diag(SPV_ERROR_INVALID_DATA, inst)
     55                << spvOpcodeString(opcode)
     56                << ": in Vulkan environment Execution scope is limited to "
     57                   "Subgroup";
     58       }
     59     }
     60 
     61     // If OpControlBarrier is used in fragment, vertex, tessellation evaluation,
     62     // or geometry stages, the execution Scope must be Subgroup.
     63     if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) {
     64       _.function(inst->function()->id())
     65           ->RegisterExecutionModelLimitation([](SpvExecutionModel model,
     66                                                 std::string* message) {
     67             if (model == SpvExecutionModelFragment ||
     68                 model == SpvExecutionModelVertex ||
     69                 model == SpvExecutionModelGeometry ||
     70                 model == SpvExecutionModelTessellationEvaluation) {
     71               if (message) {
     72                 *message =
     73                     "in Vulkan evironment, OpControlBarrier execution scope "
     74                     "must be Subgroup for Fragment, Vertex, Geometry and "
     75                     "TessellationEvaluation execution models";
     76               }
     77               return false;
     78             }
     79             return true;
     80           });
     81     }
     82 
     83     // Vulkan generic rules
     84     // Scope for execution must be limited to Workgroup or Subgroup
     85     if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
     86       return _.diag(SPV_ERROR_INVALID_DATA, inst)
     87              << spvOpcodeString(opcode)
     88              << ": in Vulkan environment Execution Scope is limited to "
     89                 "Workgroup and Subgroup";
     90     }
     91   }
     92 
     93   // WebGPU Specific rules
     94   if (spvIsWebGPUEnv(_.context()->target_env)) {
     95     // Scope for execution must be limited to Workgroup or Subgroup
     96     if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
     97       return _.diag(SPV_ERROR_INVALID_DATA, inst)
     98              << spvOpcodeString(opcode)
     99              << ": in WebGPU environment Execution Scope is limited to "
    100                 "Workgroup and Subgroup";
    101     }
    102   }
    103 
    104   // TODO(atgoo (at) github.com) Add checks for OpenCL and OpenGL environments.
    105 
    106   // General SPIRV rules
    107   // Scope for execution must be limited to Workgroup or Subgroup for
    108   // non-uniform operations
    109   if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
    110       value != SpvScopeSubgroup && value != SpvScopeWorkgroup) {
    111     return _.diag(SPV_ERROR_INVALID_DATA, inst)
    112            << spvOpcodeString(opcode)
    113            << ": Execution scope is limited to Subgroup or Workgroup";
    114   }
    115 
    116   return SPV_SUCCESS;
    117 }
    118 
    119 spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
    120                                  uint32_t scope) {
    121   const SpvOp opcode = inst->opcode();
    122   bool is_int32 = false, is_const_int32 = false;
    123   uint32_t value = 0;
    124   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
    125 
    126   if (!is_int32) {
    127     return _.diag(SPV_ERROR_INVALID_DATA, inst)
    128            << spvOpcodeString(opcode)
    129            << ": expected Memory Scope to be a 32-bit int";
    130   }
    131 
    132   if (!is_const_int32) {
    133     if (_.HasCapability(SpvCapabilityShader)) {
    134       return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Scope ids must be "
    135                                                      "OpConstant when Shader "
    136                                                      "capability is present";
    137     }
    138     return SPV_SUCCESS;
    139   }
    140 
    141   if (value == SpvScopeQueueFamilyKHR) {
    142     if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
    143       return SPV_SUCCESS;
    144     } else {
    145       return _.diag(SPV_ERROR_INVALID_DATA, inst)
    146              << spvOpcodeString(opcode)
    147              << ": Memory Scope QueueFamilyKHR requires capability "
    148                 "VulkanMemoryModelKHR";
    149     }
    150   }
    151 
    152   if (value == SpvScopeDevice &&
    153       _.HasCapability(SpvCapabilityVulkanMemoryModelKHR) &&
    154       !_.HasCapability(SpvCapabilityVulkanMemoryModelDeviceScopeKHR)) {
    155     return _.diag(SPV_ERROR_INVALID_DATA, inst)
    156            << "Use of device scope with VulkanKHR memory model requires the "
    157               "VulkanMemoryModelDeviceScopeKHR capability";
    158   }
    159 
    160   // Vulkan Specific rules
    161   if (spvIsVulkanEnv(_.context()->target_env)) {
    162     if (value == SpvScopeCrossDevice) {
    163       return _.diag(SPV_ERROR_INVALID_DATA, inst)
    164              << spvOpcodeString(opcode)
    165              << ": in Vulkan environment, Memory Scope cannot be CrossDevice";
    166     }
    167     // Vulkan 1.0 specifc rules
    168     if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
    169         value != SpvScopeDevice && value != SpvScopeWorkgroup &&
    170         value != SpvScopeInvocation) {
    171       return _.diag(SPV_ERROR_INVALID_DATA, inst)
    172              << spvOpcodeString(opcode)
    173              << ": in Vulkan 1.0 environment Memory Scope is limited to "
    174                 "Device, "
    175                 "Workgroup and Invocation";
    176     }
    177     // Vulkan 1.1 specifc rules
    178     if (_.context()->target_env == SPV_ENV_VULKAN_1_1 &&
    179         value != SpvScopeDevice && value != SpvScopeWorkgroup &&
    180         value != SpvScopeSubgroup && value != SpvScopeInvocation) {
    181       return _.diag(SPV_ERROR_INVALID_DATA, inst)
    182              << spvOpcodeString(opcode)
    183              << ": in Vulkan 1.1 environment Memory Scope is limited to "
    184                 "Device, "
    185                 "Workgroup and Invocation";
    186     }
    187   }
    188 
    189   // TODO(atgoo (at) github.com) Add checks for OpenCL and OpenGL environments.
    190 
    191   return SPV_SUCCESS;
    192 }
    193 
    194 }  // namespace val
    195 }  // namespace spvtools
    196