validate_barriers.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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. // Validates correctness of barrier SPIR-V instructions.
  15. #include "validate.h"
  16. #include "diagnostic.h"
  17. #include "opcode.h"
  18. #include "spirv_constant.h"
  19. #include "spirv_target_env.h"
  20. #include "util/bitutils.h"
  21. #include "val/instruction.h"
  22. #include "val/validation_state.h"
  23. namespace libspirv {
  24. namespace {
  25. // Validates Execution Scope operand.
  26. spv_result_t ValidateExecutionScope(ValidationState_t& _,
  27. const spv_parsed_instruction_t* inst,
  28. uint32_t id) {
  29. const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
  30. bool is_int32 = false, is_const_int32 = false;
  31. uint32_t value = 0;
  32. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
  33. if (!is_int32) {
  34. return _.diag(SPV_ERROR_INVALID_DATA)
  35. << spvOpcodeString(opcode)
  36. << ": expected Execution Scope to be a 32-bit int";
  37. }
  38. if (!is_const_int32) {
  39. return SPV_SUCCESS;
  40. }
  41. if (spvIsVulkanEnv(_.context()->target_env)) {
  42. if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
  43. return _.diag(SPV_ERROR_INVALID_DATA)
  44. << spvOpcodeString(opcode)
  45. << ": in Vulkan environment Execution Scope is limited to "
  46. "Workgroup and Subgroup";
  47. }
  48. }
  49. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  50. return SPV_SUCCESS;
  51. }
  52. // Validates Memory Scope operand.
  53. spv_result_t ValidateMemoryScope(ValidationState_t& _,
  54. const spv_parsed_instruction_t* inst,
  55. uint32_t id) {
  56. const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
  57. bool is_int32 = false, is_const_int32 = false;
  58. uint32_t value = 0;
  59. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
  60. if (!is_int32) {
  61. return _.diag(SPV_ERROR_INVALID_DATA)
  62. << spvOpcodeString(opcode)
  63. << ": expected Memory Scope to be a 32-bit int";
  64. }
  65. if (!is_const_int32) {
  66. return SPV_SUCCESS;
  67. }
  68. if (spvIsVulkanEnv(_.context()->target_env)) {
  69. if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&
  70. value != SpvScopeInvocation) {
  71. return _.diag(SPV_ERROR_INVALID_DATA)
  72. << spvOpcodeString(opcode)
  73. << ": in Vulkan environment Memory Scope is limited to Device, "
  74. "Workgroup and Invocation";
  75. }
  76. }
  77. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  78. return SPV_SUCCESS;
  79. }
  80. // Validates Memory Semantics operand.
  81. spv_result_t ValidateMemorySemantics(ValidationState_t& _,
  82. const spv_parsed_instruction_t* inst,
  83. uint32_t id) {
  84. const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
  85. bool is_int32 = false, is_const_int32 = false;
  86. uint32_t value = 0;
  87. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
  88. if (!is_int32) {
  89. return _.diag(SPV_ERROR_INVALID_DATA)
  90. << spvOpcodeString(opcode)
  91. << ": expected Memory Semantics to be a 32-bit int";
  92. }
  93. if (!is_const_int32) {
  94. return SPV_SUCCESS;
  95. }
  96. const size_t num_memory_order_set_bits = spvutils::CountSetBits(
  97. value & (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
  98. SpvMemorySemanticsAcquireReleaseMask |
  99. SpvMemorySemanticsSequentiallyConsistentMask));
  100. if (num_memory_order_set_bits > 1) {
  101. return _.diag(SPV_ERROR_INVALID_DATA)
  102. << spvOpcodeString(opcode)
  103. << ": Memory Semantics can have at most one of the following bits "
  104. "set: Acquire, Release, AcquireRelease or SequentiallyConsistent";
  105. }
  106. if (spvIsVulkanEnv(_.context()->target_env)) {
  107. const bool includes_storage_class =
  108. value & (SpvMemorySemanticsUniformMemoryMask |
  109. SpvMemorySemanticsWorkgroupMemoryMask |
  110. SpvMemorySemanticsImageMemoryMask);
  111. if (opcode == SpvOpMemoryBarrier && !num_memory_order_set_bits) {
  112. return _.diag(SPV_ERROR_INVALID_DATA)
  113. << spvOpcodeString(opcode)
  114. << ": Vulkan specification requires Memory Semantics to have one "
  115. "of the following bits set: Acquire, Release, AcquireRelease "
  116. "or SequentiallyConsistent";
  117. }
  118. if (opcode == SpvOpMemoryBarrier && !includes_storage_class) {
  119. return _.diag(SPV_ERROR_INVALID_DATA)
  120. << spvOpcodeString(opcode)
  121. << ": expected Memory Semantics to include a Vulkan-supported "
  122. "storage class";
  123. }
  124. #if 0
  125. // TODO([email protected]): this check fails Vulkan CTS, reenable once fixed.
  126. if (opcode == SpvOpControlBarrier && value && !includes_storage_class) {
  127. return _.diag(SPV_ERROR_INVALID_DATA)
  128. << spvOpcodeString(opcode)
  129. << ": expected Memory Semantics to include a Vulkan-supported "
  130. "storage class if Memory Semantics is not None";
  131. }
  132. #endif
  133. }
  134. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  135. return SPV_SUCCESS;
  136. }
  137. } // anonymous namespace
  138. // Validates correctness of barrier instructions.
  139. spv_result_t BarriersPass(ValidationState_t& _,
  140. const spv_parsed_instruction_t* inst) {
  141. const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
  142. const uint32_t result_type = inst->type_id;
  143. switch (opcode) {
  144. case SpvOpControlBarrier: {
  145. if (spvVersionForTargetEnv(_.context()->target_env) <
  146. SPV_SPIRV_VERSION_WORD(1, 3)) {
  147. _.current_function().RegisterExecutionModelLimitation(
  148. [](SpvExecutionModel model, std::string* message) {
  149. if (model != SpvExecutionModelTessellationControl &&
  150. model != SpvExecutionModelGLCompute &&
  151. model != SpvExecutionModelKernel) {
  152. if (message) {
  153. *message =
  154. "OpControlBarrier requires one of the following "
  155. "Execution "
  156. "Models: TessellationControl, GLCompute or Kernel";
  157. }
  158. return false;
  159. }
  160. return true;
  161. });
  162. }
  163. const uint32_t execution_scope = inst->words[1];
  164. const uint32_t memory_scope = inst->words[2];
  165. const uint32_t memory_semantics = inst->words[3];
  166. if (auto error = ValidateExecutionScope(_, inst, execution_scope)) {
  167. return error;
  168. }
  169. if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
  170. return error;
  171. }
  172. if (auto error = ValidateMemorySemantics(_, inst, memory_semantics)) {
  173. return error;
  174. }
  175. break;
  176. }
  177. case SpvOpMemoryBarrier: {
  178. const uint32_t memory_scope = inst->words[1];
  179. const uint32_t memory_semantics = inst->words[2];
  180. if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
  181. return error;
  182. }
  183. if (auto error = ValidateMemorySemantics(_, inst, memory_semantics)) {
  184. return error;
  185. }
  186. break;
  187. }
  188. case SpvOpNamedBarrierInitialize: {
  189. if (_.GetIdOpcode(result_type) != SpvOpTypeNamedBarrier) {
  190. return _.diag(SPV_ERROR_INVALID_DATA)
  191. << spvOpcodeString(opcode)
  192. << ": expected Result Type to be OpTypeNamedBarrier";
  193. }
  194. const uint32_t subgroup_count_type = _.GetOperandTypeId(inst, 2);
  195. if (!_.IsIntScalarType(subgroup_count_type) ||
  196. _.GetBitWidth(subgroup_count_type) != 32) {
  197. return _.diag(SPV_ERROR_INVALID_DATA)
  198. << spvOpcodeString(opcode)
  199. << ": expected Subgroup Count to be a 32-bit int";
  200. }
  201. break;
  202. }
  203. case SpvOpMemoryNamedBarrier: {
  204. const uint32_t named_barrier_type = _.GetOperandTypeId(inst, 0);
  205. if (_.GetIdOpcode(named_barrier_type) != SpvOpTypeNamedBarrier) {
  206. return _.diag(SPV_ERROR_INVALID_DATA)
  207. << spvOpcodeString(opcode)
  208. << ": expected Named Barrier to be of type OpTypeNamedBarrier";
  209. }
  210. const uint32_t memory_scope = inst->words[2];
  211. const uint32_t memory_semantics = inst->words[3];
  212. if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
  213. return error;
  214. }
  215. if (auto error = ValidateMemorySemantics(_, inst, memory_semantics)) {
  216. return error;
  217. }
  218. break;
  219. }
  220. default:
  221. break;
  222. }
  223. return SPV_SUCCESS;
  224. }
  225. } // namespace libspirv