validate_scopes.cpp 8.2 KB

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