validate_atomics.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. // Copyright (c) 2017 Google Inc.
  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 atomic SPIR-V instructions.
  15. #include "validate.h"
  16. #include "diagnostic.h"
  17. #include "opcode.h"
  18. #include "spirv_target_env.h"
  19. #include "util/bitutils.h"
  20. #include "val/instruction.h"
  21. #include "val/validation_state.h"
  22. namespace libspirv {
  23. // Validates Memory Scope operand.
  24. spv_result_t ValidateMemoryScope(ValidationState_t& _,
  25. const spv_parsed_instruction_t* inst,
  26. uint32_t id) {
  27. const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
  28. bool is_int32 = false, is_const_int32 = false;
  29. uint32_t value = 0;
  30. std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
  31. if (!is_int32) {
  32. return _.diag(SPV_ERROR_INVALID_DATA)
  33. << spvOpcodeString(opcode) << ": expected Scope to be 32-bit int";
  34. }
  35. if (!is_const_int32) {
  36. return SPV_SUCCESS;
  37. }
  38. #if 0
  39. // TODO([email protected]): this check fails Vulkan CTS, reenable once fixed.
  40. if (spvIsVulkanEnv(_.context()->target_env)) {
  41. if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&
  42. value != SpvScopeInvocation) {
  43. return _.diag(SPV_ERROR_INVALID_DATA)
  44. << spvOpcodeString(opcode)
  45. << ": in Vulkan environment memory scope is limited to Device, "
  46. "Workgroup and Invocation";
  47. }
  48. }
  49. #endif
  50. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  51. return SPV_SUCCESS;
  52. }
  53. // Validates a Memory Semantics operand.
  54. spv_result_t ValidateMemorySemantics(ValidationState_t& _,
  55. const spv_parsed_instruction_t* inst,
  56. uint32_t operand_index) {
  57. const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
  58. bool is_int32 = false, is_const_int32 = false;
  59. uint32_t flags = 0;
  60. const uint32_t memory_semantics_id =
  61. inst->words[inst->operands[operand_index].offset];
  62. std::tie(is_int32, is_const_int32, flags) =
  63. _.EvalInt32IfConst(memory_semantics_id);
  64. if (!is_int32) {
  65. return _.diag(SPV_ERROR_INVALID_DATA)
  66. << spvOpcodeString(opcode)
  67. << ": expected Memory Semantics to be 32-bit int";
  68. }
  69. if (!is_const_int32) {
  70. return SPV_SUCCESS;
  71. }
  72. if (spvutils::CountSetBits(
  73. flags &
  74. (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
  75. SpvMemorySemanticsAcquireReleaseMask |
  76. SpvMemorySemanticsSequentiallyConsistentMask)) > 1) {
  77. return _.diag(SPV_ERROR_INVALID_DATA)
  78. << spvOpcodeString(opcode)
  79. << ": no more than one of the following Memory Semantics bits can "
  80. "be set at the same time: Acquire, Release, AcquireRelease or "
  81. "SequentiallyConsistent";
  82. }
  83. if (flags & SpvMemorySemanticsUniformMemoryMask &&
  84. !_.HasCapability(SpvCapabilityShader)) {
  85. return _.diag(SPV_ERROR_INVALID_DATA)
  86. << spvOpcodeString(opcode)
  87. << ": Memory Semantics UniformMemory requires capability Shader";
  88. }
  89. if (flags & SpvMemorySemanticsAtomicCounterMemoryMask &&
  90. !_.HasCapability(SpvCapabilityAtomicStorage)) {
  91. return _.diag(SPV_ERROR_INVALID_DATA)
  92. << spvOpcodeString(opcode)
  93. << ": Memory Semantics UniformMemory requires capability "
  94. "AtomicStorage";
  95. }
  96. if (opcode == SpvOpAtomicFlagClear &&
  97. (flags & SpvMemorySemanticsAcquireMask ||
  98. flags & SpvMemorySemanticsAcquireReleaseMask)) {
  99. return _.diag(SPV_ERROR_INVALID_DATA)
  100. << "Memory Semantics Acquire and AcquireRelease cannot be used with "
  101. << spvOpcodeString(opcode);
  102. }
  103. if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 &&
  104. (flags & SpvMemorySemanticsReleaseMask ||
  105. flags & SpvMemorySemanticsAcquireReleaseMask)) {
  106. return _.diag(SPV_ERROR_INVALID_DATA)
  107. << spvOpcodeString(opcode)
  108. << ": Memory Semantics Release and AcquireRelease cannot be used "
  109. "for operand Unequal";
  110. }
  111. if (spvIsVulkanEnv(_.context()->target_env)) {
  112. if (opcode == SpvOpAtomicLoad &&
  113. (flags & SpvMemorySemanticsReleaseMask ||
  114. flags & SpvMemorySemanticsAcquireReleaseMask ||
  115. flags & SpvMemorySemanticsSequentiallyConsistentMask)) {
  116. return _.diag(SPV_ERROR_INVALID_DATA)
  117. << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "
  118. "Release, AcquireRelease and SequentiallyConsistent";
  119. }
  120. if (opcode == SpvOpAtomicStore &&
  121. (flags & SpvMemorySemanticsAcquireMask ||
  122. flags & SpvMemorySemanticsAcquireReleaseMask ||
  123. flags & SpvMemorySemanticsSequentiallyConsistentMask)) {
  124. return _.diag(SPV_ERROR_INVALID_DATA)
  125. << "Vulkan spec disallows OpAtomicStore with Memory Semantics "
  126. "Acquire, AcquireRelease and SequentiallyConsistent";
  127. }
  128. }
  129. // TODO([email protected]) Add checks for OpenCL and OpenGL environments.
  130. return SPV_SUCCESS;
  131. }
  132. // Validates correctness of atomic instructions.
  133. spv_result_t AtomicsPass(ValidationState_t& _,
  134. const spv_parsed_instruction_t* inst) {
  135. const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
  136. const uint32_t result_type = inst->type_id;
  137. switch (opcode) {
  138. case SpvOpAtomicLoad:
  139. case SpvOpAtomicStore:
  140. case SpvOpAtomicExchange:
  141. case SpvOpAtomicCompareExchange:
  142. case SpvOpAtomicCompareExchangeWeak:
  143. case SpvOpAtomicIIncrement:
  144. case SpvOpAtomicIDecrement:
  145. case SpvOpAtomicIAdd:
  146. case SpvOpAtomicISub:
  147. case SpvOpAtomicSMin:
  148. case SpvOpAtomicUMin:
  149. case SpvOpAtomicSMax:
  150. case SpvOpAtomicUMax:
  151. case SpvOpAtomicAnd:
  152. case SpvOpAtomicOr:
  153. case SpvOpAtomicXor:
  154. case SpvOpAtomicFlagTestAndSet:
  155. case SpvOpAtomicFlagClear: {
  156. if (_.HasCapability(SpvCapabilityKernel) &&
  157. (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicExchange ||
  158. opcode == SpvOpAtomicCompareExchange)) {
  159. if (!_.IsFloatScalarType(result_type) &&
  160. !_.IsIntScalarType(result_type)) {
  161. return _.diag(SPV_ERROR_INVALID_DATA)
  162. << spvOpcodeString(opcode)
  163. << ": expected Result Type to be int or float scalar type";
  164. }
  165. } else if (opcode == SpvOpAtomicFlagTestAndSet) {
  166. if (!_.IsBoolScalarType(result_type)) {
  167. return _.diag(SPV_ERROR_INVALID_DATA)
  168. << spvOpcodeString(opcode)
  169. << ": expected Result Type to be bool scalar type";
  170. }
  171. } else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) {
  172. assert(result_type == 0);
  173. } else {
  174. if (!_.IsIntScalarType(result_type)) {
  175. return _.diag(SPV_ERROR_INVALID_DATA)
  176. << spvOpcodeString(opcode)
  177. << ": expected Result Type to be int scalar type";
  178. }
  179. if (spvIsVulkanEnv(_.context()->target_env) &&
  180. _.GetBitWidth(result_type) != 32) {
  181. return _.diag(SPV_ERROR_INVALID_DATA)
  182. << spvOpcodeString(opcode)
  183. << ": according to the Vulkan spec atomic Result Type needs "
  184. "to be a 32-bit int scalar type";
  185. }
  186. }
  187. uint32_t operand_index =
  188. opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore ? 0 : 2;
  189. const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++);
  190. uint32_t data_type = 0;
  191. uint32_t storage_class = 0;
  192. if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
  193. return _.diag(SPV_ERROR_INVALID_DATA)
  194. << spvOpcodeString(opcode)
  195. << ": expected Pointer to be of type OpTypePointer";
  196. }
  197. switch (storage_class) {
  198. case SpvStorageClassUniform:
  199. case SpvStorageClassWorkgroup:
  200. case SpvStorageClassCrossWorkgroup:
  201. case SpvStorageClassGeneric:
  202. case SpvStorageClassAtomicCounter:
  203. case SpvStorageClassImage:
  204. case SpvStorageClassStorageBuffer:
  205. break;
  206. default:
  207. return _.diag(SPV_ERROR_INVALID_DATA)
  208. << spvOpcodeString(opcode)
  209. << ": expected Pointer Storage Class to be Uniform, "
  210. "Workgroup, CrossWorkgroup, Generic, AtomicCounter, Image "
  211. "or StorageBuffer";
  212. }
  213. if (opcode == SpvOpAtomicFlagTestAndSet ||
  214. opcode == SpvOpAtomicFlagClear) {
  215. if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
  216. return _.diag(SPV_ERROR_INVALID_DATA)
  217. << spvOpcodeString(opcode)
  218. << ": expected Pointer to point to a value of 32-bit int type";
  219. }
  220. } else if (opcode == SpvOpAtomicStore) {
  221. if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {
  222. return _.diag(SPV_ERROR_INVALID_DATA)
  223. << spvOpcodeString(opcode)
  224. << ": expected Pointer to be a pointer to int or float "
  225. << "scalar type";
  226. }
  227. } else {
  228. if (data_type != result_type) {
  229. return _.diag(SPV_ERROR_INVALID_DATA)
  230. << spvOpcodeString(opcode)
  231. << ": expected Pointer to point to a value of type Result "
  232. "Type";
  233. }
  234. }
  235. const uint32_t memory_scope =
  236. inst->words[inst->operands[operand_index++].offset];
  237. if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
  238. return error;
  239. }
  240. if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
  241. return error;
  242. if (opcode == SpvOpAtomicCompareExchange ||
  243. opcode == SpvOpAtomicCompareExchangeWeak) {
  244. if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
  245. return error;
  246. }
  247. if (opcode == SpvOpAtomicStore) {
  248. const uint32_t value_type = _.GetOperandTypeId(inst, 3);
  249. if (value_type != data_type) {
  250. return _.diag(SPV_ERROR_INVALID_DATA)
  251. << spvOpcodeString(opcode)
  252. << ": expected Value type and the type pointed to by Pointer "
  253. "to"
  254. << " be the same";
  255. }
  256. } else if (opcode != SpvOpAtomicLoad && opcode != SpvOpAtomicIIncrement &&
  257. opcode != SpvOpAtomicIDecrement &&
  258. opcode != SpvOpAtomicFlagTestAndSet &&
  259. opcode != SpvOpAtomicFlagClear) {
  260. const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);
  261. if (value_type != result_type) {
  262. return _.diag(SPV_ERROR_INVALID_DATA)
  263. << spvOpcodeString(opcode)
  264. << ": expected Value to be of type Result Type";
  265. }
  266. }
  267. if (opcode == SpvOpAtomicCompareExchange ||
  268. opcode == SpvOpAtomicCompareExchangeWeak) {
  269. const uint32_t comparator_type =
  270. _.GetOperandTypeId(inst, operand_index++);
  271. if (comparator_type != result_type) {
  272. return _.diag(SPV_ERROR_INVALID_DATA)
  273. << spvOpcodeString(opcode)
  274. << ": expected Comparator to be of type Result Type";
  275. }
  276. }
  277. break;
  278. }
  279. default:
  280. break;
  281. }
  282. return SPV_SUCCESS;
  283. }
  284. } // namespace libspirv