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