validate_scopes.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. case SpvScopeShaderCallKHR:
  32. return true;
  33. case SpvScopeMax:
  34. break;
  35. }
  36. return false;
  37. }
  38. spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst,
  39. uint32_t scope) {
  40. SpvOp opcode = inst->opcode();
  41. bool is_int32 = false, is_const_int32 = false;
  42. uint32_t value = 0;
  43. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
  44. if (!is_int32) {
  45. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  46. << spvOpcodeString(opcode) << ": expected 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. }
  63. if (is_const_int32 && !IsValidScope(value)) {
  64. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  65. << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
  66. }
  67. return SPV_SUCCESS;
  68. }
  69. spv_result_t ValidateExecutionScope(ValidationState_t& _,
  70. const Instruction* inst, uint32_t scope) {
  71. SpvOp opcode = inst->opcode();
  72. bool is_int32 = false, is_const_int32 = false;
  73. uint32_t value = 0;
  74. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
  75. if (auto error = ValidateScope(_, inst, scope)) {
  76. return error;
  77. }
  78. if (!is_const_int32) {
  79. return SPV_SUCCESS;
  80. }
  81. // Vulkan specific rules
  82. if (spvIsVulkanEnv(_.context()->target_env)) {
  83. // Vulkan 1.1 specific rules
  84. if (_.context()->target_env != SPV_ENV_VULKAN_1_0) {
  85. // Scope for Non Uniform Group Operations must be limited to Subgroup
  86. if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
  87. value != SpvScopeSubgroup) {
  88. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  89. << spvOpcodeString(opcode)
  90. << ": in Vulkan environment Execution scope is limited to "
  91. << "Subgroup";
  92. }
  93. }
  94. // If OpControlBarrier is used in fragment, vertex, tessellation evaluation,
  95. // or geometry stages, the execution Scope must be Subgroup.
  96. if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) {
  97. _.function(inst->function()->id())
  98. ->RegisterExecutionModelLimitation([](SpvExecutionModel model,
  99. std::string* message) {
  100. if (model == SpvExecutionModelFragment ||
  101. model == SpvExecutionModelVertex ||
  102. model == SpvExecutionModelGeometry ||
  103. model == SpvExecutionModelTessellationEvaluation) {
  104. if (message) {
  105. *message =
  106. "in Vulkan evironment, OpControlBarrier execution scope "
  107. "must be Subgroup for Fragment, Vertex, Geometry and "
  108. "TessellationEvaluation execution models";
  109. }
  110. return false;
  111. }
  112. return true;
  113. });
  114. }
  115. // Vulkan generic rules
  116. // Scope for execution must be limited to Workgroup or Subgroup
  117. if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
  118. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  119. << spvOpcodeString(opcode)
  120. << ": in Vulkan environment Execution Scope is limited to "
  121. << "Workgroup and Subgroup";
  122. }
  123. }
  124. // WebGPU Specific rules
  125. if (spvIsWebGPUEnv(_.context()->target_env)) {
  126. if (value != SpvScopeWorkgroup) {
  127. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  128. << spvOpcodeString(opcode)
  129. << ": in WebGPU environment Execution Scope is limited to "
  130. << "Workgroup";
  131. } else {
  132. _.function(inst->function()->id())
  133. ->RegisterExecutionModelLimitation(
  134. [](SpvExecutionModel model, std::string* message) {
  135. if (model != SpvExecutionModelGLCompute) {
  136. if (message) {
  137. *message =
  138. ": in WebGPU environment, Workgroup Execution Scope is "
  139. "limited to GLCompute execution model";
  140. }
  141. return false;
  142. }
  143. return true;
  144. });
  145. }
  146. }
  147. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  148. // General SPIRV rules
  149. // Scope for execution must be limited to Workgroup or Subgroup for
  150. // non-uniform operations
  151. if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
  152. value != SpvScopeSubgroup && value != SpvScopeWorkgroup) {
  153. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  154. << spvOpcodeString(opcode)
  155. << ": Execution scope is limited to Subgroup or Workgroup";
  156. }
  157. return SPV_SUCCESS;
  158. }
  159. spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
  160. uint32_t scope) {
  161. const SpvOp opcode = inst->opcode();
  162. bool is_int32 = false, is_const_int32 = false;
  163. uint32_t value = 0;
  164. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
  165. if (auto error = ValidateScope(_, inst, scope)) {
  166. return error;
  167. }
  168. if (!is_const_int32) {
  169. return SPV_SUCCESS;
  170. }
  171. if (value == SpvScopeQueueFamilyKHR) {
  172. if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
  173. return SPV_SUCCESS;
  174. } else {
  175. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  176. << spvOpcodeString(opcode)
  177. << ": Memory Scope QueueFamilyKHR requires capability "
  178. << "VulkanMemoryModelKHR";
  179. }
  180. }
  181. if (value == SpvScopeDevice &&
  182. _.HasCapability(SpvCapabilityVulkanMemoryModelKHR) &&
  183. !_.HasCapability(SpvCapabilityVulkanMemoryModelDeviceScopeKHR)) {
  184. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  185. << "Use of device scope with VulkanKHR memory model requires the "
  186. << "VulkanMemoryModelDeviceScopeKHR capability";
  187. }
  188. // Vulkan Specific rules
  189. if (spvIsVulkanEnv(_.context()->target_env)) {
  190. if (value == SpvScopeCrossDevice) {
  191. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  192. << spvOpcodeString(opcode)
  193. << ": in Vulkan environment, Memory Scope cannot be CrossDevice";
  194. }
  195. // Vulkan 1.0 specifc rules
  196. if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
  197. value != SpvScopeDevice && value != SpvScopeWorkgroup &&
  198. value != SpvScopeInvocation) {
  199. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  200. << spvOpcodeString(opcode)
  201. << ": in Vulkan 1.0 environment Memory Scope is limited to "
  202. << "Device, Workgroup and Invocation";
  203. }
  204. // Vulkan 1.1 specifc rules
  205. if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 ||
  206. _.context()->target_env == SPV_ENV_VULKAN_1_2) &&
  207. value != SpvScopeDevice && value != SpvScopeWorkgroup &&
  208. value != SpvScopeSubgroup && value != SpvScopeInvocation &&
  209. value != SpvScopeShaderCallKHR) {
  210. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  211. << spvOpcodeString(opcode)
  212. << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited "
  213. << "to Device, Workgroup, Invocation, and ShaderCall";
  214. }
  215. if (value == SpvScopeShaderCallKHR) {
  216. _.function(inst->function()->id())
  217. ->RegisterExecutionModelLimitation(
  218. [](SpvExecutionModel model, std::string* message) {
  219. if (model != SpvExecutionModelRayGenerationKHR &&
  220. model != SpvExecutionModelIntersectionKHR &&
  221. model != SpvExecutionModelAnyHitKHR &&
  222. model != SpvExecutionModelClosestHitKHR &&
  223. model != SpvExecutionModelMissKHR &&
  224. model != SpvExecutionModelCallableKHR) {
  225. if (message) {
  226. *message =
  227. "ShaderCallKHR Memory Scope requires a ray tracing "
  228. "execution model";
  229. }
  230. return false;
  231. }
  232. return true;
  233. });
  234. }
  235. }
  236. // WebGPU specific rules
  237. if (spvIsWebGPUEnv(_.context()->target_env)) {
  238. switch (inst->opcode()) {
  239. case SpvOpControlBarrier:
  240. if (value != SpvScopeWorkgroup) {
  241. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  242. << spvOpcodeString(opcode)
  243. << ": in WebGPU environment Memory Scope is limited to "
  244. << "Workgroup for OpControlBarrier";
  245. }
  246. break;
  247. case SpvOpMemoryBarrier:
  248. if (value != SpvScopeWorkgroup) {
  249. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  250. << spvOpcodeString(opcode)
  251. << ": in WebGPU environment Memory Scope is limited to "
  252. << "Workgroup for OpMemoryBarrier";
  253. }
  254. break;
  255. default:
  256. if (spvOpcodeIsAtomicOp(inst->opcode())) {
  257. if (value != SpvScopeQueueFamilyKHR) {
  258. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  259. << spvOpcodeString(opcode)
  260. << ": in WebGPU environment Memory Scope is limited to "
  261. << "QueueFamilyKHR for OpAtomic* operations";
  262. }
  263. }
  264. if (value != SpvScopeWorkgroup && value != SpvScopeInvocation &&
  265. value != SpvScopeQueueFamilyKHR) {
  266. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  267. << spvOpcodeString(opcode)
  268. << ": in WebGPU environment Memory Scope is limited to "
  269. << "Workgroup, Invocation, and QueueFamilyKHR";
  270. }
  271. break;
  272. }
  273. if (value == SpvScopeWorkgroup) {
  274. _.function(inst->function()->id())
  275. ->RegisterExecutionModelLimitation(
  276. [](SpvExecutionModel model, std::string* message) {
  277. if (model != SpvExecutionModelGLCompute) {
  278. if (message) {
  279. *message =
  280. ": in WebGPU environment, Workgroup Memory Scope is "
  281. "limited to GLCompute execution model";
  282. }
  283. return false;
  284. }
  285. return true;
  286. });
  287. }
  288. }
  289. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  290. return SPV_SUCCESS;
  291. }
  292. } // namespace val
  293. } // namespace spvtools