validate_scopes.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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. bool IsValidScope(uint32_t scope) {
  22. // Deliberately avoid a default case so we have to update the list when the
  23. // scopes list changes.
  24. switch (static_cast<SpvScope>(scope)) {
  25. case SpvScopeCrossDevice:
  26. case SpvScopeDevice:
  27. case SpvScopeWorkgroup:
  28. case SpvScopeSubgroup:
  29. case SpvScopeInvocation:
  30. case SpvScopeQueueFamilyKHR:
  31. return true;
  32. case SpvScopeMax:
  33. break;
  34. }
  35. return false;
  36. }
  37. spv_result_t ValidateExecutionScope(ValidationState_t& _,
  38. const Instruction* inst, uint32_t scope) {
  39. SpvOp opcode = inst->opcode();
  40. bool is_int32 = false, is_const_int32 = false;
  41. uint32_t value = 0;
  42. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
  43. if (!is_int32) {
  44. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  45. << spvOpcodeString(opcode)
  46. << ": expected Execution Scope to be a 32-bit int";
  47. }
  48. if (!is_const_int32) {
  49. if (_.HasCapability(SpvCapabilityShader) &&
  50. !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
  51. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  52. << "Scope ids must be OpConstant when Shader capability is "
  53. << "present";
  54. }
  55. if (_.HasCapability(SpvCapabilityShader) &&
  56. _.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
  57. !spvOpcodeIsConstant(_.GetIdOpcode(scope))) {
  58. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  59. << "Scope ids must be constant or specialization constant when "
  60. << "CooperativeMatrixNV capability is present";
  61. }
  62. return SPV_SUCCESS;
  63. }
  64. if (is_const_int32 && !IsValidScope(value)) {
  65. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  66. << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
  67. }
  68. // Vulkan specific rules
  69. if (spvIsVulkanEnv(_.context()->target_env)) {
  70. // Vulkan 1.1 specific rules
  71. if (_.context()->target_env != SPV_ENV_VULKAN_1_0) {
  72. // Scope for Non Uniform Group Operations must be limited to Subgroup
  73. if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
  74. value != SpvScopeSubgroup) {
  75. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  76. << spvOpcodeString(opcode)
  77. << ": in Vulkan environment Execution scope is limited to "
  78. << "Subgroup";
  79. }
  80. }
  81. // If OpControlBarrier is used in fragment, vertex, tessellation evaluation,
  82. // or geometry stages, the execution Scope must be Subgroup.
  83. if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) {
  84. _.function(inst->function()->id())
  85. ->RegisterExecutionModelLimitation([](SpvExecutionModel model,
  86. std::string* message) {
  87. if (model == SpvExecutionModelFragment ||
  88. model == SpvExecutionModelVertex ||
  89. model == SpvExecutionModelGeometry ||
  90. model == SpvExecutionModelTessellationEvaluation) {
  91. if (message) {
  92. *message =
  93. "in Vulkan evironment, OpControlBarrier execution scope "
  94. "must be Subgroup for Fragment, Vertex, Geometry and "
  95. "TessellationEvaluation execution models";
  96. }
  97. return false;
  98. }
  99. return true;
  100. });
  101. }
  102. // Vulkan generic rules
  103. // Scope for execution must be limited to Workgroup or Subgroup
  104. if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
  105. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  106. << spvOpcodeString(opcode)
  107. << ": in Vulkan environment Execution Scope is limited to "
  108. << "Workgroup and Subgroup";
  109. }
  110. }
  111. // WebGPU Specific rules
  112. if (spvIsWebGPUEnv(_.context()->target_env)) {
  113. if (value != SpvScopeWorkgroup) {
  114. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  115. << spvOpcodeString(opcode)
  116. << ": in WebGPU environment Execution Scope is limited to "
  117. << "Workgroup";
  118. }
  119. }
  120. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  121. // General SPIRV rules
  122. // Scope for execution must be limited to Workgroup or Subgroup for
  123. // non-uniform operations
  124. if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
  125. value != SpvScopeSubgroup && value != SpvScopeWorkgroup) {
  126. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  127. << spvOpcodeString(opcode)
  128. << ": Execution scope is limited to Subgroup or Workgroup";
  129. }
  130. return SPV_SUCCESS;
  131. }
  132. spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
  133. uint32_t scope) {
  134. const SpvOp opcode = inst->opcode();
  135. bool is_int32 = false, is_const_int32 = false;
  136. uint32_t value = 0;
  137. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
  138. if (!is_int32) {
  139. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  140. << spvOpcodeString(opcode)
  141. << ": expected Memory Scope to be a 32-bit int";
  142. }
  143. if (!is_const_int32) {
  144. if (_.HasCapability(SpvCapabilityShader) &&
  145. !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
  146. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  147. << "Scope ids must be OpConstant when Shader capability is "
  148. << "present";
  149. }
  150. if (_.HasCapability(SpvCapabilityShader) &&
  151. _.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
  152. !spvOpcodeIsConstant(_.GetIdOpcode(scope))) {
  153. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  154. << "Scope ids must be constant or specialization constant when "
  155. << "CooperativeMatrixNV capability is present";
  156. }
  157. return SPV_SUCCESS;
  158. }
  159. if (is_const_int32 && !IsValidScope(value)) {
  160. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  161. << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
  162. }
  163. if (value == SpvScopeQueueFamilyKHR) {
  164. if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
  165. return SPV_SUCCESS;
  166. } else {
  167. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  168. << spvOpcodeString(opcode)
  169. << ": Memory Scope QueueFamilyKHR requires capability "
  170. << "VulkanMemoryModelKHR";
  171. }
  172. }
  173. if (value == SpvScopeDevice &&
  174. _.HasCapability(SpvCapabilityVulkanMemoryModelKHR) &&
  175. !_.HasCapability(SpvCapabilityVulkanMemoryModelDeviceScopeKHR)) {
  176. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  177. << "Use of device scope with VulkanKHR memory model requires the "
  178. << "VulkanMemoryModelDeviceScopeKHR capability";
  179. }
  180. // Vulkan Specific rules
  181. if (spvIsVulkanEnv(_.context()->target_env)) {
  182. if (value == SpvScopeCrossDevice) {
  183. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  184. << spvOpcodeString(opcode)
  185. << ": in Vulkan environment, Memory Scope cannot be CrossDevice";
  186. }
  187. // Vulkan 1.0 specifc rules
  188. if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
  189. value != SpvScopeDevice && value != SpvScopeWorkgroup &&
  190. value != SpvScopeInvocation) {
  191. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  192. << spvOpcodeString(opcode)
  193. << ": in Vulkan 1.0 environment Memory Scope is limited to "
  194. << "Device, Workgroup and Invocation";
  195. }
  196. // Vulkan 1.1 specifc rules
  197. if (_.context()->target_env == SPV_ENV_VULKAN_1_1 &&
  198. value != SpvScopeDevice && value != SpvScopeWorkgroup &&
  199. value != SpvScopeSubgroup && value != SpvScopeInvocation) {
  200. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  201. << spvOpcodeString(opcode)
  202. << ": in Vulkan 1.1 environment Memory Scope is limited to "
  203. << "Device, Workgroup and Invocation";
  204. }
  205. }
  206. // WebGPU specific rules
  207. if (spvIsWebGPUEnv(_.context()->target_env)) {
  208. switch (inst->opcode()) {
  209. case SpvOpControlBarrier:
  210. if (value != SpvScopeWorkgroup) {
  211. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  212. << spvOpcodeString(opcode)
  213. << ": in WebGPU environment Memory Scope is limited to "
  214. << "Workgroup for OpControlBarrier";
  215. }
  216. break;
  217. case SpvOpMemoryBarrier:
  218. if (value != SpvScopeWorkgroup) {
  219. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  220. << spvOpcodeString(opcode)
  221. << ": in WebGPU environment Memory Scope is limited to "
  222. << "Workgroup for OpMemoryBarrier";
  223. }
  224. break;
  225. default:
  226. if (spvOpcodeIsAtomicOp(inst->opcode())) {
  227. if (value != SpvScopeQueueFamilyKHR) {
  228. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  229. << spvOpcodeString(opcode)
  230. << ": in WebGPU environment Memory Scope is limited to "
  231. << "QueueFamilyKHR for OpAtomic* operations";
  232. }
  233. }
  234. if (value != SpvScopeWorkgroup && value != SpvScopeInvocation &&
  235. value != SpvScopeQueueFamilyKHR) {
  236. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  237. << spvOpcodeString(opcode)
  238. << ": in WebGPU environment Memory Scope is limited to "
  239. << "Workgroup, Invocation, and QueueFamilyKHR";
  240. }
  241. break;
  242. }
  243. }
  244. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  245. return SPV_SUCCESS;
  246. }
  247. } // namespace val
  248. } // namespace spvtools