validate_atomics.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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 "source/val/validate.h"
  16. #include "source/diagnostic.h"
  17. #include "source/opcode.h"
  18. #include "source/spirv_target_env.h"
  19. #include "source/util/bitutils.h"
  20. #include "source/val/instruction.h"
  21. #include "source/val/validate_memory_semantics.h"
  22. #include "source/val/validate_scopes.h"
  23. #include "source/val/validation_state.h"
  24. namespace {
  25. bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) {
  26. switch (storage_class) {
  27. case SpvStorageClassUniform:
  28. case SpvStorageClassStorageBuffer:
  29. case SpvStorageClassWorkgroup:
  30. case SpvStorageClassCrossWorkgroup:
  31. case SpvStorageClassGeneric:
  32. case SpvStorageClassAtomicCounter:
  33. case SpvStorageClassImage:
  34. case SpvStorageClassFunction:
  35. case SpvStorageClassPhysicalStorageBufferEXT:
  36. return true;
  37. break;
  38. default:
  39. return false;
  40. }
  41. }
  42. } // namespace
  43. namespace spvtools {
  44. namespace val {
  45. // Validates correctness of atomic instructions.
  46. spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
  47. const SpvOp opcode = inst->opcode();
  48. const uint32_t result_type = inst->type_id();
  49. switch (opcode) {
  50. case SpvOpAtomicLoad:
  51. case SpvOpAtomicStore:
  52. case SpvOpAtomicExchange:
  53. case SpvOpAtomicCompareExchange:
  54. case SpvOpAtomicCompareExchangeWeak:
  55. case SpvOpAtomicIIncrement:
  56. case SpvOpAtomicIDecrement:
  57. case SpvOpAtomicIAdd:
  58. case SpvOpAtomicISub:
  59. case SpvOpAtomicSMin:
  60. case SpvOpAtomicUMin:
  61. case SpvOpAtomicSMax:
  62. case SpvOpAtomicUMax:
  63. case SpvOpAtomicAnd:
  64. case SpvOpAtomicOr:
  65. case SpvOpAtomicXor:
  66. case SpvOpAtomicFlagTestAndSet:
  67. case SpvOpAtomicFlagClear: {
  68. if (_.HasCapability(SpvCapabilityKernel) &&
  69. (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicExchange ||
  70. opcode == SpvOpAtomicCompareExchange)) {
  71. if (!_.IsFloatScalarType(result_type) &&
  72. !_.IsIntScalarType(result_type)) {
  73. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  74. << spvOpcodeString(opcode)
  75. << ": expected Result Type to be int or float scalar type";
  76. }
  77. } else if (opcode == SpvOpAtomicFlagTestAndSet) {
  78. if (!_.IsBoolScalarType(result_type)) {
  79. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  80. << spvOpcodeString(opcode)
  81. << ": expected Result Type to be bool scalar type";
  82. }
  83. } else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) {
  84. assert(result_type == 0);
  85. } else {
  86. if (!_.IsIntScalarType(result_type)) {
  87. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  88. << spvOpcodeString(opcode)
  89. << ": expected Result Type to be int scalar type";
  90. }
  91. if (spvIsVulkanEnv(_.context()->target_env) &&
  92. _.GetBitWidth(result_type) != 32) {
  93. switch (opcode) {
  94. case SpvOpAtomicSMin:
  95. case SpvOpAtomicUMin:
  96. case SpvOpAtomicSMax:
  97. case SpvOpAtomicUMax:
  98. case SpvOpAtomicAnd:
  99. case SpvOpAtomicOr:
  100. case SpvOpAtomicXor:
  101. case SpvOpAtomicIAdd:
  102. case SpvOpAtomicLoad:
  103. case SpvOpAtomicStore:
  104. case SpvOpAtomicExchange:
  105. case SpvOpAtomicCompareExchange: {
  106. if (_.GetBitWidth(result_type) == 64 &&
  107. !_.HasCapability(SpvCapabilityInt64Atomics))
  108. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  109. << spvOpcodeString(opcode)
  110. << ": 64-bit atomics require the Int64Atomics "
  111. "capability";
  112. } break;
  113. default:
  114. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  115. << spvOpcodeString(opcode)
  116. << ": according to the Vulkan spec atomic Result Type "
  117. "needs "
  118. "to be a 32-bit int scalar type";
  119. }
  120. }
  121. }
  122. uint32_t operand_index =
  123. opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore ? 0 : 2;
  124. const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++);
  125. uint32_t data_type = 0;
  126. uint32_t storage_class = 0;
  127. if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
  128. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  129. << spvOpcodeString(opcode)
  130. << ": expected Pointer to be of type OpTypePointer";
  131. }
  132. // Validate storage class against universal rules
  133. if (!IsStorageClassAllowedByUniversalRules(storage_class)) {
  134. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  135. << spvOpcodeString(opcode)
  136. << ": storage class forbidden by universal validation rules.";
  137. }
  138. // Then Shader rules
  139. if (_.HasCapability(SpvCapabilityShader)) {
  140. if (storage_class == SpvStorageClassFunction) {
  141. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  142. << spvOpcodeString(opcode)
  143. << ": Function storage class forbidden when the Shader "
  144. "capability is declared.";
  145. }
  146. }
  147. // And finally OpenCL environment rules
  148. if (spvIsOpenCLEnv(_.context()->target_env)) {
  149. if ((storage_class != SpvStorageClassFunction) &&
  150. (storage_class != SpvStorageClassWorkgroup) &&
  151. (storage_class != SpvStorageClassCrossWorkgroup) &&
  152. (storage_class != SpvStorageClassGeneric)) {
  153. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  154. << spvOpcodeString(opcode)
  155. << ": storage class must be Function, Workgroup, "
  156. "CrossWorkGroup or Generic in the OpenCL environment.";
  157. }
  158. if (_.context()->target_env == SPV_ENV_OPENCL_1_2) {
  159. if (storage_class == SpvStorageClassGeneric) {
  160. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  161. << "Storage class cannot be Generic in OpenCL 1.2 "
  162. "environment";
  163. }
  164. }
  165. }
  166. if (opcode == SpvOpAtomicFlagTestAndSet ||
  167. opcode == SpvOpAtomicFlagClear) {
  168. if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
  169. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  170. << spvOpcodeString(opcode)
  171. << ": expected Pointer to point to a value of 32-bit int type";
  172. }
  173. } else if (opcode == SpvOpAtomicStore) {
  174. if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {
  175. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  176. << spvOpcodeString(opcode)
  177. << ": expected Pointer to be a pointer to int or float "
  178. << "scalar type";
  179. }
  180. } else {
  181. if (data_type != result_type) {
  182. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  183. << spvOpcodeString(opcode)
  184. << ": expected Pointer to point to a value of type Result "
  185. "Type";
  186. }
  187. }
  188. auto memory_scope = inst->GetOperandAs<const uint32_t>(operand_index++);
  189. if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
  190. return error;
  191. }
  192. const auto equal_semantics_index = operand_index++;
  193. if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index))
  194. return error;
  195. if (opcode == SpvOpAtomicCompareExchange ||
  196. opcode == SpvOpAtomicCompareExchangeWeak) {
  197. const auto unequal_semantics_index = operand_index++;
  198. if (auto error =
  199. ValidateMemorySemantics(_, inst, unequal_semantics_index))
  200. return error;
  201. // Volatile bits must match for equal and unequal semantics. Previous
  202. // checks guarantee they are 32-bit constants, but we need to recheck
  203. // whether they are evaluatable constants.
  204. bool is_int32 = false;
  205. bool is_equal_const = false;
  206. bool is_unequal_const = false;
  207. uint32_t equal_value = 0;
  208. uint32_t unequal_value = 0;
  209. std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst(
  210. inst->GetOperandAs<uint32_t>(equal_semantics_index));
  211. std::tie(is_int32, is_unequal_const, unequal_value) =
  212. _.EvalInt32IfConst(
  213. inst->GetOperandAs<uint32_t>(unequal_semantics_index));
  214. if (is_equal_const && is_unequal_const &&
  215. ((equal_value & SpvMemorySemanticsVolatileMask) ^
  216. (unequal_value & SpvMemorySemanticsVolatileMask))) {
  217. return _.diag(SPV_ERROR_INVALID_ID, inst)
  218. << "Volatile mask setting must match for Equal and Unequal "
  219. "memory semantics";
  220. }
  221. }
  222. if (opcode == SpvOpAtomicStore) {
  223. const uint32_t value_type = _.GetOperandTypeId(inst, 3);
  224. if (value_type != data_type) {
  225. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  226. << spvOpcodeString(opcode)
  227. << ": expected Value type and the type pointed to by "
  228. "Pointer to be the same";
  229. }
  230. } else if (opcode != SpvOpAtomicLoad && opcode != SpvOpAtomicIIncrement &&
  231. opcode != SpvOpAtomicIDecrement &&
  232. opcode != SpvOpAtomicFlagTestAndSet &&
  233. opcode != SpvOpAtomicFlagClear) {
  234. const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);
  235. if (value_type != result_type) {
  236. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  237. << spvOpcodeString(opcode)
  238. << ": expected Value to be of type Result Type";
  239. }
  240. }
  241. if (opcode == SpvOpAtomicCompareExchange ||
  242. opcode == SpvOpAtomicCompareExchangeWeak) {
  243. const uint32_t comparator_type =
  244. _.GetOperandTypeId(inst, operand_index++);
  245. if (comparator_type != result_type) {
  246. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  247. << spvOpcodeString(opcode)
  248. << ": expected Comparator to be of type Result Type";
  249. }
  250. }
  251. break;
  252. }
  253. default:
  254. break;
  255. }
  256. return SPV_SUCCESS;
  257. }
  258. } // namespace val
  259. } // namespace spvtools