validate_scopes.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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/spirv_target_env.h"
  16. #include "source/val/instruction.h"
  17. #include "source/val/validation_state.h"
  18. namespace spvtools {
  19. namespace val {
  20. bool IsValidScope(uint32_t scope) {
  21. // Deliberately avoid a default case so we have to update the list when the
  22. // scopes list changes.
  23. switch (static_cast<spv::Scope>(scope)) {
  24. case spv::Scope::CrossDevice:
  25. case spv::Scope::Device:
  26. case spv::Scope::Workgroup:
  27. case spv::Scope::Subgroup:
  28. case spv::Scope::Invocation:
  29. case spv::Scope::QueueFamilyKHR:
  30. case spv::Scope::ShaderCallKHR:
  31. return true;
  32. case spv::Scope::Max:
  33. break;
  34. }
  35. return false;
  36. }
  37. spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst,
  38. uint32_t scope) {
  39. spv::Op 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) << ": expected scope to be a 32-bit int";
  46. }
  47. if (!is_const_int32) {
  48. if (_.HasCapability(spv::Capability::Shader) &&
  49. !_.HasCapability(spv::Capability::CooperativeMatrixNV)) {
  50. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  51. << "Scope ids must be OpConstant when Shader capability is "
  52. << "present";
  53. }
  54. if (_.HasCapability(spv::Capability::Shader) &&
  55. _.HasCapability(spv::Capability::CooperativeMatrixNV) &&
  56. !spvOpcodeIsConstant(_.GetIdOpcode(scope))) {
  57. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  58. << "Scope ids must be constant or specialization constant when "
  59. << "CooperativeMatrixNV capability is present";
  60. }
  61. }
  62. if (is_const_int32 && !IsValidScope(value)) {
  63. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  64. << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
  65. }
  66. return SPV_SUCCESS;
  67. }
  68. spv_result_t ValidateExecutionScope(ValidationState_t& _,
  69. const Instruction* inst, uint32_t scope) {
  70. spv::Op opcode = inst->opcode();
  71. bool is_int32 = false, is_const_int32 = false;
  72. uint32_t tmp_value = 0;
  73. std::tie(is_int32, is_const_int32, tmp_value) = _.EvalInt32IfConst(scope);
  74. if (auto error = ValidateScope(_, inst, scope)) {
  75. return error;
  76. }
  77. if (!is_const_int32) {
  78. return SPV_SUCCESS;
  79. }
  80. spv::Scope value = spv::Scope(tmp_value);
  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. (opcode != spv::Op::OpGroupNonUniformQuadAllKHR) &&
  88. (opcode != spv::Op::OpGroupNonUniformQuadAnyKHR)) &&
  89. (value != spv::Scope::Subgroup)) {
  90. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  91. << _.VkErrorID(4642) << spvOpcodeString(opcode)
  92. << ": in Vulkan environment Execution scope is limited to "
  93. << "Subgroup";
  94. }
  95. }
  96. // OpControlBarrier must only use Subgroup execution scope for a subset of
  97. // execution models.
  98. if (opcode == spv::Op::OpControlBarrier && value != spv::Scope::Subgroup) {
  99. std::string errorVUID = _.VkErrorID(4682);
  100. _.function(inst->function()->id())
  101. ->RegisterExecutionModelLimitation([errorVUID](
  102. spv::ExecutionModel model,
  103. std::string* message) {
  104. if (model == spv::ExecutionModel::Fragment ||
  105. model == spv::ExecutionModel::Vertex ||
  106. model == spv::ExecutionModel::Geometry ||
  107. model == spv::ExecutionModel::TessellationEvaluation ||
  108. model == spv::ExecutionModel::RayGenerationKHR ||
  109. model == spv::ExecutionModel::IntersectionKHR ||
  110. model == spv::ExecutionModel::AnyHitKHR ||
  111. model == spv::ExecutionModel::ClosestHitKHR ||
  112. model == spv::ExecutionModel::MissKHR) {
  113. if (message) {
  114. *message =
  115. errorVUID +
  116. "in Vulkan environment, OpControlBarrier execution scope "
  117. "must be Subgroup for Fragment, Vertex, Geometry, "
  118. "TessellationEvaluation, RayGeneration, Intersection, "
  119. "AnyHit, ClosestHit, and Miss execution models";
  120. }
  121. return false;
  122. }
  123. return true;
  124. });
  125. }
  126. // Only subset of execution models support Workgroup.
  127. if (value == spv::Scope::Workgroup) {
  128. std::string errorVUID = _.VkErrorID(4637);
  129. _.function(inst->function()->id())
  130. ->RegisterExecutionModelLimitation(
  131. [errorVUID](spv::ExecutionModel model, std::string* message) {
  132. if (model != spv::ExecutionModel::TaskNV &&
  133. model != spv::ExecutionModel::MeshNV &&
  134. model != spv::ExecutionModel::TaskEXT &&
  135. model != spv::ExecutionModel::MeshEXT &&
  136. model != spv::ExecutionModel::TessellationControl &&
  137. model != spv::ExecutionModel::GLCompute) {
  138. if (message) {
  139. *message =
  140. errorVUID +
  141. "in Vulkan environment, Workgroup execution scope is "
  142. "only for TaskNV, MeshNV, TaskEXT, MeshEXT, "
  143. "TessellationControl, and GLCompute execution models";
  144. }
  145. return false;
  146. }
  147. return true;
  148. });
  149. }
  150. // Vulkan generic rules
  151. // Scope for execution must be limited to Workgroup or Subgroup
  152. if (value != spv::Scope::Workgroup && value != spv::Scope::Subgroup) {
  153. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  154. << _.VkErrorID(4636) << spvOpcodeString(opcode)
  155. << ": in Vulkan environment Execution Scope is limited to "
  156. << "Workgroup and Subgroup";
  157. }
  158. }
  159. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  160. // General SPIRV rules
  161. // Scope for execution must be limited to Workgroup or Subgroup for
  162. // non-uniform operations
  163. if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
  164. opcode != spv::Op::OpGroupNonUniformQuadAllKHR &&
  165. opcode != spv::Op::OpGroupNonUniformQuadAnyKHR &&
  166. value != spv::Scope::Subgroup && value != spv::Scope::Workgroup) {
  167. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  168. << spvOpcodeString(opcode)
  169. << ": Execution scope is limited to Subgroup or Workgroup";
  170. }
  171. return SPV_SUCCESS;
  172. }
  173. spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
  174. uint32_t scope) {
  175. const spv::Op opcode = inst->opcode();
  176. bool is_int32 = false, is_const_int32 = false;
  177. uint32_t tmp_value = 0;
  178. std::tie(is_int32, is_const_int32, tmp_value) = _.EvalInt32IfConst(scope);
  179. if (auto error = ValidateScope(_, inst, scope)) {
  180. return error;
  181. }
  182. if (!is_const_int32) {
  183. return SPV_SUCCESS;
  184. }
  185. spv::Scope value = spv::Scope(tmp_value);
  186. if (value == spv::Scope::QueueFamilyKHR) {
  187. if (_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
  188. return SPV_SUCCESS;
  189. } else {
  190. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  191. << spvOpcodeString(opcode)
  192. << ": Memory Scope QueueFamilyKHR requires capability "
  193. << "VulkanMemoryModelKHR";
  194. }
  195. }
  196. if (value == spv::Scope::Device &&
  197. _.HasCapability(spv::Capability::VulkanMemoryModelKHR) &&
  198. !_.HasCapability(spv::Capability::VulkanMemoryModelDeviceScopeKHR)) {
  199. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  200. << "Use of device scope with VulkanKHR memory model requires the "
  201. << "VulkanMemoryModelDeviceScopeKHR capability";
  202. }
  203. // Vulkan Specific rules
  204. if (spvIsVulkanEnv(_.context()->target_env)) {
  205. if (value != spv::Scope::Device && value != spv::Scope::Workgroup &&
  206. value != spv::Scope::Subgroup && value != spv::Scope::Invocation &&
  207. value != spv::Scope::ShaderCallKHR &&
  208. value != spv::Scope::QueueFamily) {
  209. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  210. << _.VkErrorID(4638) << spvOpcodeString(opcode)
  211. << ": in Vulkan environment Memory Scope is limited to Device, "
  212. "QueueFamily, Workgroup, ShaderCallKHR, Subgroup, or "
  213. "Invocation";
  214. } else if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
  215. value == spv::Scope::Subgroup &&
  216. !_.HasCapability(spv::Capability::SubgroupBallotKHR) &&
  217. !_.HasCapability(spv::Capability::SubgroupVoteKHR)) {
  218. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  219. << _.VkErrorID(7951) << spvOpcodeString(opcode)
  220. << ": in Vulkan 1.0 environment Memory Scope is can not be "
  221. "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR "
  222. "declared";
  223. }
  224. if (value == spv::Scope::ShaderCallKHR) {
  225. std::string errorVUID = _.VkErrorID(4640);
  226. _.function(inst->function()->id())
  227. ->RegisterExecutionModelLimitation(
  228. [errorVUID](spv::ExecutionModel model, std::string* message) {
  229. if (model != spv::ExecutionModel::RayGenerationKHR &&
  230. model != spv::ExecutionModel::IntersectionKHR &&
  231. model != spv::ExecutionModel::AnyHitKHR &&
  232. model != spv::ExecutionModel::ClosestHitKHR &&
  233. model != spv::ExecutionModel::MissKHR &&
  234. model != spv::ExecutionModel::CallableKHR) {
  235. if (message) {
  236. *message =
  237. errorVUID +
  238. "ShaderCallKHR Memory Scope requires a ray tracing "
  239. "execution model";
  240. }
  241. return false;
  242. }
  243. return true;
  244. });
  245. }
  246. if (value == spv::Scope::Workgroup) {
  247. std::string errorVUID = _.VkErrorID(7321);
  248. _.function(inst->function()->id())
  249. ->RegisterExecutionModelLimitation(
  250. [errorVUID](spv::ExecutionModel model, std::string* message) {
  251. if (model != spv::ExecutionModel::GLCompute &&
  252. model != spv::ExecutionModel::TessellationControl &&
  253. model != spv::ExecutionModel::TaskNV &&
  254. model != spv::ExecutionModel::MeshNV &&
  255. model != spv::ExecutionModel::TaskEXT &&
  256. model != spv::ExecutionModel::MeshEXT) {
  257. if (message) {
  258. *message = errorVUID +
  259. "Workgroup Memory Scope is limited to MeshNV, "
  260. "TaskNV, MeshEXT, TaskEXT, TessellationControl, "
  261. "and GLCompute execution model";
  262. }
  263. return false;
  264. }
  265. return true;
  266. });
  267. if (_.memory_model() == spv::MemoryModel::GLSL450) {
  268. errorVUID = _.VkErrorID(7320);
  269. _.function(inst->function()->id())
  270. ->RegisterExecutionModelLimitation(
  271. [errorVUID](spv::ExecutionModel model, std::string* message) {
  272. if (model == spv::ExecutionModel::TessellationControl) {
  273. if (message) {
  274. *message =
  275. errorVUID +
  276. "Workgroup Memory Scope can't be used with "
  277. "TessellationControl using GLSL450 Memory Model";
  278. }
  279. return false;
  280. }
  281. return true;
  282. });
  283. }
  284. }
  285. }
  286. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  287. return SPV_SUCCESS;
  288. }
  289. } // namespace val
  290. } // namespace spvtools