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