validate_atomics.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. // Copyright (c) 2017 Google Inc.
  2. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
  3. // reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. // Validates correctness of atomic SPIR-V instructions.
  17. #include "source/val/validate.h"
  18. #include "source/diagnostic.h"
  19. #include "source/opcode.h"
  20. #include "source/spirv_target_env.h"
  21. #include "source/util/bitutils.h"
  22. #include "source/val/instruction.h"
  23. #include "source/val/validate_memory_semantics.h"
  24. #include "source/val/validate_scopes.h"
  25. #include "source/val/validation_state.h"
  26. namespace {
  27. bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) {
  28. switch (storage_class) {
  29. case SpvStorageClassUniform:
  30. case SpvStorageClassStorageBuffer:
  31. case SpvStorageClassWorkgroup:
  32. case SpvStorageClassCrossWorkgroup:
  33. case SpvStorageClassGeneric:
  34. case SpvStorageClassAtomicCounter:
  35. case SpvStorageClassImage:
  36. case SpvStorageClassFunction:
  37. case SpvStorageClassPhysicalStorageBufferEXT:
  38. return true;
  39. break;
  40. default:
  41. return false;
  42. }
  43. }
  44. bool HasReturnType(uint32_t opcode) {
  45. switch (opcode) {
  46. case SpvOpAtomicStore:
  47. case SpvOpAtomicFlagClear:
  48. return false;
  49. break;
  50. default:
  51. return true;
  52. }
  53. }
  54. bool HasOnlyFloatReturnType(uint32_t opcode) {
  55. switch (opcode) {
  56. case SpvOpAtomicFAddEXT:
  57. case SpvOpAtomicFMinEXT:
  58. case SpvOpAtomicFMaxEXT:
  59. return true;
  60. break;
  61. default:
  62. return false;
  63. }
  64. }
  65. bool HasOnlyIntReturnType(uint32_t opcode) {
  66. switch (opcode) {
  67. case SpvOpAtomicCompareExchange:
  68. case SpvOpAtomicCompareExchangeWeak:
  69. case SpvOpAtomicIIncrement:
  70. case SpvOpAtomicIDecrement:
  71. case SpvOpAtomicIAdd:
  72. case SpvOpAtomicISub:
  73. case SpvOpAtomicSMin:
  74. case SpvOpAtomicUMin:
  75. case SpvOpAtomicSMax:
  76. case SpvOpAtomicUMax:
  77. case SpvOpAtomicAnd:
  78. case SpvOpAtomicOr:
  79. case SpvOpAtomicXor:
  80. return true;
  81. break;
  82. default:
  83. return false;
  84. }
  85. }
  86. bool HasIntOrFloatReturnType(uint32_t opcode) {
  87. switch (opcode) {
  88. case SpvOpAtomicLoad:
  89. case SpvOpAtomicExchange:
  90. return true;
  91. break;
  92. default:
  93. return false;
  94. }
  95. }
  96. bool HasOnlyBoolReturnType(uint32_t opcode) {
  97. switch (opcode) {
  98. case SpvOpAtomicFlagTestAndSet:
  99. return true;
  100. break;
  101. default:
  102. return false;
  103. }
  104. }
  105. } // namespace
  106. namespace spvtools {
  107. namespace val {
  108. // Validates correctness of atomic instructions.
  109. spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
  110. const SpvOp opcode = inst->opcode();
  111. switch (opcode) {
  112. case SpvOpAtomicLoad:
  113. case SpvOpAtomicStore:
  114. case SpvOpAtomicExchange:
  115. case SpvOpAtomicFAddEXT:
  116. case SpvOpAtomicCompareExchange:
  117. case SpvOpAtomicCompareExchangeWeak:
  118. case SpvOpAtomicIIncrement:
  119. case SpvOpAtomicIDecrement:
  120. case SpvOpAtomicIAdd:
  121. case SpvOpAtomicISub:
  122. case SpvOpAtomicSMin:
  123. case SpvOpAtomicUMin:
  124. case SpvOpAtomicFMinEXT:
  125. case SpvOpAtomicSMax:
  126. case SpvOpAtomicUMax:
  127. case SpvOpAtomicFMaxEXT:
  128. case SpvOpAtomicAnd:
  129. case SpvOpAtomicOr:
  130. case SpvOpAtomicXor:
  131. case SpvOpAtomicFlagTestAndSet:
  132. case SpvOpAtomicFlagClear: {
  133. const uint32_t result_type = inst->type_id();
  134. // All current atomics only are scalar result
  135. // Validate return type first so can just check if pointer type is same
  136. // (if applicable)
  137. if (HasReturnType(opcode)) {
  138. if (HasOnlyFloatReturnType(opcode) &&
  139. !_.IsFloatScalarType(result_type)) {
  140. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  141. << spvOpcodeString(opcode)
  142. << ": expected Result Type to be float scalar type";
  143. } else if (HasOnlyIntReturnType(opcode) &&
  144. !_.IsIntScalarType(result_type)) {
  145. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  146. << spvOpcodeString(opcode)
  147. << ": expected Result Type to be integer scalar type";
  148. } else if (HasIntOrFloatReturnType(opcode) &&
  149. !_.IsFloatScalarType(result_type) &&
  150. !_.IsIntScalarType(result_type)) {
  151. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  152. << spvOpcodeString(opcode)
  153. << ": expected Result Type to be integer or float scalar type";
  154. } else if (HasOnlyBoolReturnType(opcode) &&
  155. !_.IsBoolScalarType(result_type)) {
  156. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  157. << spvOpcodeString(opcode)
  158. << ": expected Result Type to be bool scalar type";
  159. }
  160. }
  161. uint32_t operand_index = HasReturnType(opcode) ? 2 : 0;
  162. const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++);
  163. uint32_t data_type = 0;
  164. uint32_t storage_class = 0;
  165. if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
  166. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  167. << spvOpcodeString(opcode)
  168. << ": expected Pointer to be of type OpTypePointer";
  169. }
  170. // Can't use result_type because OpAtomicStore doesn't have a result
  171. if ( _.IsIntScalarType(data_type) &&_.GetBitWidth(data_type) == 64 &&
  172. !_.HasCapability(SpvCapabilityInt64Atomics)) {
  173. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  174. << spvOpcodeString(opcode)
  175. << ": 64-bit atomics require the Int64Atomics capability";
  176. }
  177. // Validate storage class against universal rules
  178. if (!IsStorageClassAllowedByUniversalRules(storage_class)) {
  179. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  180. << spvOpcodeString(opcode)
  181. << ": storage class forbidden by universal validation rules.";
  182. }
  183. // Then Shader rules
  184. if (_.HasCapability(SpvCapabilityShader)) {
  185. // Vulkan environment rule
  186. if (spvIsVulkanEnv(_.context()->target_env)) {
  187. if ((storage_class != SpvStorageClassUniform) &&
  188. (storage_class != SpvStorageClassStorageBuffer) &&
  189. (storage_class != SpvStorageClassWorkgroup) &&
  190. (storage_class != SpvStorageClassImage) &&
  191. (storage_class != SpvStorageClassPhysicalStorageBuffer)) {
  192. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  193. << _.VkErrorID(4686) << spvOpcodeString(opcode)
  194. << ": Vulkan spec only allows storage classes for atomic to "
  195. "be: Uniform, Workgroup, Image, StorageBuffer, or "
  196. "PhysicalStorageBuffer.";
  197. }
  198. } else if (storage_class == SpvStorageClassFunction) {
  199. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  200. << spvOpcodeString(opcode)
  201. << ": Function storage class forbidden when the Shader "
  202. "capability is declared.";
  203. }
  204. if (opcode == SpvOpAtomicFAddEXT) {
  205. // result type being float checked already
  206. if ((_.GetBitWidth(result_type) == 16) &&
  207. (!_.HasCapability(SpvCapabilityAtomicFloat16AddEXT))) {
  208. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  209. << spvOpcodeString(opcode)
  210. << ": float add atomics require the AtomicFloat32AddEXT "
  211. "capability";
  212. }
  213. if ((_.GetBitWidth(result_type) == 32) &&
  214. (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) {
  215. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  216. << spvOpcodeString(opcode)
  217. << ": float add atomics require the AtomicFloat32AddEXT "
  218. "capability";
  219. }
  220. if ((_.GetBitWidth(result_type) == 64) &&
  221. (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) {
  222. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  223. << spvOpcodeString(opcode)
  224. << ": float add atomics require the AtomicFloat64AddEXT "
  225. "capability";
  226. }
  227. } else if (opcode == SpvOpAtomicFMinEXT ||
  228. opcode == SpvOpAtomicFMaxEXT) {
  229. if ((_.GetBitWidth(result_type) == 16) &&
  230. (!_.HasCapability(SpvCapabilityAtomicFloat16MinMaxEXT))) {
  231. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  232. << spvOpcodeString(opcode)
  233. << ": float min/max atomics require the "
  234. "AtomicFloat16MinMaxEXT capability";
  235. }
  236. if ((_.GetBitWidth(result_type) == 32) &&
  237. (!_.HasCapability(SpvCapabilityAtomicFloat32MinMaxEXT))) {
  238. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  239. << spvOpcodeString(opcode)
  240. << ": float min/max atomics require the "
  241. "AtomicFloat32MinMaxEXT capability";
  242. }
  243. if ((_.GetBitWidth(result_type) == 64) &&
  244. (!_.HasCapability(SpvCapabilityAtomicFloat64MinMaxEXT))) {
  245. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  246. << spvOpcodeString(opcode)
  247. << ": float min/max atomics require the "
  248. "AtomicFloat64MinMaxEXT capability";
  249. }
  250. }
  251. }
  252. // And finally OpenCL environment rules
  253. if (spvIsOpenCLEnv(_.context()->target_env)) {
  254. if ((storage_class != SpvStorageClassFunction) &&
  255. (storage_class != SpvStorageClassWorkgroup) &&
  256. (storage_class != SpvStorageClassCrossWorkgroup) &&
  257. (storage_class != SpvStorageClassGeneric)) {
  258. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  259. << spvOpcodeString(opcode)
  260. << ": storage class must be Function, Workgroup, "
  261. "CrossWorkGroup or Generic in the OpenCL environment.";
  262. }
  263. if (_.context()->target_env == SPV_ENV_OPENCL_1_2) {
  264. if (storage_class == SpvStorageClassGeneric) {
  265. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  266. << "Storage class cannot be Generic in OpenCL 1.2 "
  267. "environment";
  268. }
  269. }
  270. }
  271. // If result and pointer type are different, need to do special check here
  272. if (opcode == SpvOpAtomicFlagTestAndSet ||
  273. opcode == SpvOpAtomicFlagClear) {
  274. if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
  275. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  276. << spvOpcodeString(opcode)
  277. << ": expected Pointer to point to a value of 32-bit integer "
  278. "type";
  279. }
  280. } else if (opcode == SpvOpAtomicStore) {
  281. if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {
  282. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  283. << spvOpcodeString(opcode)
  284. << ": expected Pointer to be a pointer to integer or float "
  285. << "scalar type";
  286. }
  287. } else if (data_type != result_type) {
  288. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  289. << spvOpcodeString(opcode)
  290. << ": expected Pointer to point to a value of type Result "
  291. "Type";
  292. }
  293. auto memory_scope = inst->GetOperandAs<const uint32_t>(operand_index++);
  294. if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
  295. return error;
  296. }
  297. const auto equal_semantics_index = operand_index++;
  298. if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index,
  299. memory_scope))
  300. return error;
  301. if (opcode == SpvOpAtomicCompareExchange ||
  302. opcode == SpvOpAtomicCompareExchangeWeak) {
  303. const auto unequal_semantics_index = operand_index++;
  304. if (auto error = ValidateMemorySemantics(
  305. _, inst, unequal_semantics_index, memory_scope))
  306. return error;
  307. // Volatile bits must match for equal and unequal semantics. Previous
  308. // checks guarantee they are 32-bit constants, but we need to recheck
  309. // whether they are evaluatable constants.
  310. bool is_int32 = false;
  311. bool is_equal_const = false;
  312. bool is_unequal_const = false;
  313. uint32_t equal_value = 0;
  314. uint32_t unequal_value = 0;
  315. std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst(
  316. inst->GetOperandAs<uint32_t>(equal_semantics_index));
  317. std::tie(is_int32, is_unequal_const, unequal_value) =
  318. _.EvalInt32IfConst(
  319. inst->GetOperandAs<uint32_t>(unequal_semantics_index));
  320. if (is_equal_const && is_unequal_const &&
  321. ((equal_value & SpvMemorySemanticsVolatileMask) ^
  322. (unequal_value & SpvMemorySemanticsVolatileMask))) {
  323. return _.diag(SPV_ERROR_INVALID_ID, inst)
  324. << "Volatile mask setting must match for Equal and Unequal "
  325. "memory semantics";
  326. }
  327. }
  328. if (opcode == SpvOpAtomicStore) {
  329. const uint32_t value_type = _.GetOperandTypeId(inst, 3);
  330. if (value_type != data_type) {
  331. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  332. << spvOpcodeString(opcode)
  333. << ": expected Value type and the type pointed to by "
  334. "Pointer to be the same";
  335. }
  336. } else if (opcode != SpvOpAtomicLoad && opcode != SpvOpAtomicIIncrement &&
  337. opcode != SpvOpAtomicIDecrement &&
  338. opcode != SpvOpAtomicFlagTestAndSet &&
  339. opcode != SpvOpAtomicFlagClear) {
  340. const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);
  341. if (value_type != result_type) {
  342. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  343. << spvOpcodeString(opcode)
  344. << ": expected Value to be of type Result Type";
  345. }
  346. }
  347. if (opcode == SpvOpAtomicCompareExchange ||
  348. opcode == SpvOpAtomicCompareExchangeWeak) {
  349. const uint32_t comparator_type =
  350. _.GetOperandTypeId(inst, operand_index++);
  351. if (comparator_type != result_type) {
  352. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  353. << spvOpcodeString(opcode)
  354. << ": expected Comparator to be of type Result Type";
  355. }
  356. }
  357. break;
  358. }
  359. default:
  360. break;
  361. }
  362. return SPV_SUCCESS;
  363. }
  364. } // namespace val
  365. } // namespace spvtools