validate_annotation.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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/opcode.h"
  15. #include "source/spirv_target_env.h"
  16. #include "source/val/instruction.h"
  17. #include "source/val/validate.h"
  18. #include "source/val/validation_state.h"
  19. namespace spvtools {
  20. namespace val {
  21. namespace {
  22. bool IsValidWebGPUDecoration(uint32_t decoration) {
  23. switch (decoration) {
  24. case SpvDecorationSpecId:
  25. case SpvDecorationBlock:
  26. case SpvDecorationRowMajor:
  27. case SpvDecorationColMajor:
  28. case SpvDecorationArrayStride:
  29. case SpvDecorationMatrixStride:
  30. case SpvDecorationBuiltIn:
  31. case SpvDecorationNoPerspective:
  32. case SpvDecorationFlat:
  33. case SpvDecorationCentroid:
  34. case SpvDecorationRestrict:
  35. case SpvDecorationAliased:
  36. case SpvDecorationNonWritable:
  37. case SpvDecorationNonReadable:
  38. case SpvDecorationUniform:
  39. case SpvDecorationLocation:
  40. case SpvDecorationComponent:
  41. case SpvDecorationIndex:
  42. case SpvDecorationBinding:
  43. case SpvDecorationDescriptorSet:
  44. case SpvDecorationOffset:
  45. case SpvDecorationNoContraction:
  46. return true;
  47. default:
  48. return false;
  49. }
  50. }
  51. std::string LogStringForDecoration(uint32_t decoration) {
  52. switch (decoration) {
  53. case SpvDecorationRelaxedPrecision:
  54. return "RelaxedPrecision";
  55. case SpvDecorationSpecId:
  56. return "SpecId";
  57. case SpvDecorationBlock:
  58. return "Block";
  59. case SpvDecorationBufferBlock:
  60. return "BufferBlock";
  61. case SpvDecorationRowMajor:
  62. return "RowMajor";
  63. case SpvDecorationColMajor:
  64. return "ColMajor";
  65. case SpvDecorationArrayStride:
  66. return "ArrayStride";
  67. case SpvDecorationMatrixStride:
  68. return "MatrixStride";
  69. case SpvDecorationGLSLShared:
  70. return "GLSLShared";
  71. case SpvDecorationGLSLPacked:
  72. return "GLSLPacked";
  73. case SpvDecorationCPacked:
  74. return "CPacked";
  75. case SpvDecorationBuiltIn:
  76. return "BuiltIn";
  77. case SpvDecorationNoPerspective:
  78. return "NoPerspective";
  79. case SpvDecorationFlat:
  80. return "Flat";
  81. case SpvDecorationPatch:
  82. return "Patch";
  83. case SpvDecorationCentroid:
  84. return "Centroid";
  85. case SpvDecorationSample:
  86. return "Sample";
  87. case SpvDecorationInvariant:
  88. return "Invariant";
  89. case SpvDecorationRestrict:
  90. return "Restrict";
  91. case SpvDecorationAliased:
  92. return "Aliased";
  93. case SpvDecorationVolatile:
  94. return "Volatile";
  95. case SpvDecorationConstant:
  96. return "Constant";
  97. case SpvDecorationCoherent:
  98. return "Coherent";
  99. case SpvDecorationNonWritable:
  100. return "NonWritable";
  101. case SpvDecorationNonReadable:
  102. return "NonReadable";
  103. case SpvDecorationUniform:
  104. return "Uniform";
  105. case SpvDecorationSaturatedConversion:
  106. return "SaturatedConversion";
  107. case SpvDecorationStream:
  108. return "Stream";
  109. case SpvDecorationLocation:
  110. return "Location";
  111. case SpvDecorationComponent:
  112. return "Component";
  113. case SpvDecorationIndex:
  114. return "Index";
  115. case SpvDecorationBinding:
  116. return "Binding";
  117. case SpvDecorationDescriptorSet:
  118. return "DescriptorSet";
  119. case SpvDecorationOffset:
  120. return "Offset";
  121. case SpvDecorationXfbBuffer:
  122. return "XfbBuffer";
  123. case SpvDecorationXfbStride:
  124. return "XfbStride";
  125. case SpvDecorationFuncParamAttr:
  126. return "FuncParamAttr";
  127. case SpvDecorationFPRoundingMode:
  128. return "FPRoundingMode";
  129. case SpvDecorationFPFastMathMode:
  130. return "FPFastMathMode";
  131. case SpvDecorationLinkageAttributes:
  132. return "LinkageAttributes";
  133. case SpvDecorationNoContraction:
  134. return "NoContraction";
  135. case SpvDecorationInputAttachmentIndex:
  136. return "InputAttachmentIndex";
  137. case SpvDecorationAlignment:
  138. return "Alignment";
  139. case SpvDecorationMaxByteOffset:
  140. return "MaxByteOffset";
  141. case SpvDecorationAlignmentId:
  142. return "AlignmentId";
  143. case SpvDecorationMaxByteOffsetId:
  144. return "MaxByteOffsetId";
  145. case SpvDecorationNoSignedWrap:
  146. return "NoSignedWrap";
  147. case SpvDecorationNoUnsignedWrap:
  148. return "NoUnsignedWrap";
  149. case SpvDecorationExplicitInterpAMD:
  150. return "ExplicitInterpAMD";
  151. case SpvDecorationOverrideCoverageNV:
  152. return "OverrideCoverageNV";
  153. case SpvDecorationPassthroughNV:
  154. return "PassthroughNV";
  155. case SpvDecorationViewportRelativeNV:
  156. return "ViewportRelativeNV";
  157. case SpvDecorationSecondaryViewportRelativeNV:
  158. return "SecondaryViewportRelativeNV";
  159. case SpvDecorationPerPrimitiveNV:
  160. return "PerPrimitiveNV";
  161. case SpvDecorationPerViewNV:
  162. return "PerViewNV";
  163. case SpvDecorationPerTaskNV:
  164. return "PerTaskNV";
  165. case SpvDecorationPerVertexNV:
  166. return "PerVertexNV";
  167. case SpvDecorationNonUniformEXT:
  168. return "NonUniformEXT";
  169. case SpvDecorationRestrictPointerEXT:
  170. return "RestrictPointerEXT";
  171. case SpvDecorationAliasedPointerEXT:
  172. return "AliasedPointerEXT";
  173. case SpvDecorationHlslCounterBufferGOOGLE:
  174. return "HlslCounterBufferGOOGLE";
  175. case SpvDecorationHlslSemanticGOOGLE:
  176. return "HlslSemanticGOOGLE";
  177. default:
  178. break;
  179. }
  180. return "Unknown";
  181. }
  182. // Returns true if the decoration takes ID parameters.
  183. // TODO(dneto): This can be generated from the grammar.
  184. bool DecorationTakesIdParameters(uint32_t type) {
  185. switch (static_cast<SpvDecoration>(type)) {
  186. case SpvDecorationUniformId:
  187. case SpvDecorationAlignmentId:
  188. case SpvDecorationMaxByteOffsetId:
  189. case SpvDecorationHlslCounterBufferGOOGLE:
  190. return true;
  191. default:
  192. break;
  193. }
  194. return false;
  195. }
  196. spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
  197. const auto decoration = inst->GetOperandAs<uint32_t>(1);
  198. if (decoration == SpvDecorationSpecId) {
  199. const auto target_id = inst->GetOperandAs<uint32_t>(0);
  200. const auto target = _.FindDef(target_id);
  201. if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) {
  202. return _.diag(SPV_ERROR_INVALID_ID, inst)
  203. << "OpDecorate SpecId decoration target <id> '"
  204. << _.getIdName(target_id)
  205. << "' is not a scalar specialization constant.";
  206. }
  207. }
  208. if (spvIsWebGPUEnv(_.context()->target_env) &&
  209. !IsValidWebGPUDecoration(decoration)) {
  210. return _.diag(SPV_ERROR_INVALID_ID, inst)
  211. << "OpDecorate decoration '" << LogStringForDecoration(decoration)
  212. << "' is not valid for the WebGPU execution environment.";
  213. }
  214. if (DecorationTakesIdParameters(decoration)) {
  215. return _.diag(SPV_ERROR_INVALID_ID, inst)
  216. << "Decorations taking ID parameters may not be used with "
  217. "OpDecorateId";
  218. }
  219. // TODO: Add validations for all decorations.
  220. return SPV_SUCCESS;
  221. }
  222. spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
  223. const auto decoration = inst->GetOperandAs<uint32_t>(1);
  224. if (!DecorationTakesIdParameters(decoration)) {
  225. return _.diag(SPV_ERROR_INVALID_ID, inst)
  226. << "Decorations that don't take ID parameters may not be used with "
  227. "OpDecorateId";
  228. }
  229. // TODO: Add validations for these decorations.
  230. // UniformId is covered elsewhere.
  231. return SPV_SUCCESS;
  232. }
  233. spv_result_t ValidateMemberDecorate(ValidationState_t& _,
  234. const Instruction* inst) {
  235. const auto struct_type_id = inst->GetOperandAs<uint32_t>(0);
  236. const auto struct_type = _.FindDef(struct_type_id);
  237. if (!struct_type || SpvOpTypeStruct != struct_type->opcode()) {
  238. return _.diag(SPV_ERROR_INVALID_ID, inst)
  239. << "OpMemberDecorate Structure type <id> '"
  240. << _.getIdName(struct_type_id) << "' is not a struct type.";
  241. }
  242. const auto member = inst->GetOperandAs<uint32_t>(1);
  243. const auto member_count =
  244. static_cast<uint32_t>(struct_type->words().size() - 2);
  245. if (member_count <= member) {
  246. return _.diag(SPV_ERROR_INVALID_ID, inst)
  247. << "Index " << member
  248. << " provided in OpMemberDecorate for struct <id> "
  249. << _.getIdName(struct_type_id)
  250. << " is out of bounds. The structure has " << member_count
  251. << " members. Largest valid index is " << member_count - 1 << ".";
  252. }
  253. const auto decoration = inst->GetOperandAs<uint32_t>(2);
  254. if (spvIsWebGPUEnv(_.context()->target_env) &&
  255. !IsValidWebGPUDecoration(decoration)) {
  256. return _.diag(SPV_ERROR_INVALID_ID, inst)
  257. << "OpMemberDecorate decoration '" << _.getIdName(decoration)
  258. << "' is not valid for the WebGPU execution environment.";
  259. }
  260. return SPV_SUCCESS;
  261. }
  262. spv_result_t ValidateDecorationGroup(ValidationState_t& _,
  263. const Instruction* inst) {
  264. if (spvIsWebGPUEnv(_.context()->target_env)) {
  265. return _.diag(SPV_ERROR_INVALID_BINARY, inst)
  266. << "OpDecorationGroup is not allowed in the WebGPU execution "
  267. << "environment.";
  268. }
  269. const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
  270. const auto decoration_group = _.FindDef(decoration_group_id);
  271. for (auto pair : decoration_group->uses()) {
  272. auto use = pair.first;
  273. if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate &&
  274. use->opcode() != SpvOpGroupMemberDecorate &&
  275. use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId) {
  276. return _.diag(SPV_ERROR_INVALID_ID, inst)
  277. << "Result id of OpDecorationGroup can only "
  278. << "be targeted by OpName, OpGroupDecorate, "
  279. << "OpDecorate, OpDecorateId, and OpGroupMemberDecorate";
  280. }
  281. }
  282. return SPV_SUCCESS;
  283. }
  284. spv_result_t ValidateGroupDecorate(ValidationState_t& _,
  285. const Instruction* inst) {
  286. if (spvIsWebGPUEnv(_.context()->target_env)) {
  287. return _.diag(SPV_ERROR_INVALID_BINARY, inst)
  288. << "OpGroupDecorate is not allowed in the WebGPU execution "
  289. << "environment.";
  290. }
  291. const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
  292. auto decoration_group = _.FindDef(decoration_group_id);
  293. if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) {
  294. return _.diag(SPV_ERROR_INVALID_ID, inst)
  295. << "OpGroupDecorate Decoration group <id> '"
  296. << _.getIdName(decoration_group_id)
  297. << "' is not a decoration group.";
  298. }
  299. for (unsigned i = 1; i < inst->operands().size(); ++i) {
  300. auto target_id = inst->GetOperandAs<uint32_t>(i);
  301. auto target = _.FindDef(target_id);
  302. if (!target || target->opcode() == SpvOpDecorationGroup) {
  303. return _.diag(SPV_ERROR_INVALID_ID, inst)
  304. << "OpGroupDecorate may not target OpDecorationGroup <id> '"
  305. << _.getIdName(target_id) << "'";
  306. }
  307. }
  308. return SPV_SUCCESS;
  309. }
  310. spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _,
  311. const Instruction* inst) {
  312. if (spvIsWebGPUEnv(_.context()->target_env)) {
  313. return _.diag(SPV_ERROR_INVALID_BINARY, inst)
  314. << "OpGroupMemberDecorate is not allowed in the WebGPU execution "
  315. << "environment.";
  316. }
  317. const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
  318. const auto decoration_group = _.FindDef(decoration_group_id);
  319. if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) {
  320. return _.diag(SPV_ERROR_INVALID_ID, inst)
  321. << "OpGroupMemberDecorate Decoration group <id> '"
  322. << _.getIdName(decoration_group_id)
  323. << "' is not a decoration group.";
  324. }
  325. // Grammar checks ensures that the number of arguments to this instruction
  326. // is an odd number: 1 decoration group + (id,literal) pairs.
  327. for (size_t i = 1; i + 1 < inst->operands().size(); i += 2) {
  328. const uint32_t struct_id = inst->GetOperandAs<uint32_t>(i);
  329. const uint32_t index = inst->GetOperandAs<uint32_t>(i + 1);
  330. auto struct_instr = _.FindDef(struct_id);
  331. if (!struct_instr || SpvOpTypeStruct != struct_instr->opcode()) {
  332. return _.diag(SPV_ERROR_INVALID_ID, inst)
  333. << "OpGroupMemberDecorate Structure type <id> '"
  334. << _.getIdName(struct_id) << "' is not a struct type.";
  335. }
  336. const uint32_t num_struct_members =
  337. static_cast<uint32_t>(struct_instr->words().size() - 2);
  338. if (index >= num_struct_members) {
  339. return _.diag(SPV_ERROR_INVALID_ID, inst)
  340. << "Index " << index
  341. << " provided in OpGroupMemberDecorate for struct <id> "
  342. << _.getIdName(struct_id)
  343. << " is out of bounds. The structure has " << num_struct_members
  344. << " members. Largest valid index is " << num_struct_members - 1
  345. << ".";
  346. }
  347. }
  348. return SPV_SUCCESS;
  349. }
  350. // Registers necessary decoration(s) for the appropriate IDs based on the
  351. // instruction.
  352. spv_result_t RegisterDecorations(ValidationState_t& _,
  353. const Instruction* inst) {
  354. switch (inst->opcode()) {
  355. case SpvOpDecorate:
  356. case SpvOpDecorateId: {
  357. const uint32_t target_id = inst->word(1);
  358. const SpvDecoration dec_type = static_cast<SpvDecoration>(inst->word(2));
  359. std::vector<uint32_t> dec_params;
  360. if (inst->words().size() > 3) {
  361. dec_params.insert(dec_params.end(), inst->words().begin() + 3,
  362. inst->words().end());
  363. }
  364. _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params));
  365. break;
  366. }
  367. case SpvOpMemberDecorate: {
  368. const uint32_t struct_id = inst->word(1);
  369. const uint32_t index = inst->word(2);
  370. const SpvDecoration dec_type = static_cast<SpvDecoration>(inst->word(3));
  371. std::vector<uint32_t> dec_params;
  372. if (inst->words().size() > 4) {
  373. dec_params.insert(dec_params.end(), inst->words().begin() + 4,
  374. inst->words().end());
  375. }
  376. _.RegisterDecorationForId(struct_id,
  377. Decoration(dec_type, dec_params, index));
  378. break;
  379. }
  380. case SpvOpDecorationGroup: {
  381. // We don't need to do anything right now. Assigning decorations to groups
  382. // will be taken care of via OpGroupDecorate.
  383. break;
  384. }
  385. case SpvOpGroupDecorate: {
  386. // Word 1 is the group <id>. All subsequent words are target <id>s that
  387. // are going to be decorated with the decorations.
  388. const uint32_t decoration_group_id = inst->word(1);
  389. std::vector<Decoration>& group_decorations =
  390. _.id_decorations(decoration_group_id);
  391. for (size_t i = 2; i < inst->words().size(); ++i) {
  392. const uint32_t target_id = inst->word(i);
  393. _.RegisterDecorationsForId(target_id, group_decorations.begin(),
  394. group_decorations.end());
  395. }
  396. break;
  397. }
  398. case SpvOpGroupMemberDecorate: {
  399. // Word 1 is the Decoration Group <id> followed by (struct<id>,literal)
  400. // pairs. All decorations of the group should be applied to all the struct
  401. // members that are specified in the instructions.
  402. const uint32_t decoration_group_id = inst->word(1);
  403. std::vector<Decoration>& group_decorations =
  404. _.id_decorations(decoration_group_id);
  405. // Grammar checks ensures that the number of arguments to this instruction
  406. // is an odd number: 1 decoration group + (id,literal) pairs.
  407. for (size_t i = 2; i + 1 < inst->words().size(); i = i + 2) {
  408. const uint32_t struct_id = inst->word(i);
  409. const uint32_t index = inst->word(i + 1);
  410. // ID validation phase ensures this is in fact a struct instruction and
  411. // that the index is not out of bound.
  412. _.RegisterDecorationsForStructMember(struct_id, index,
  413. group_decorations.begin(),
  414. group_decorations.end());
  415. }
  416. break;
  417. }
  418. default:
  419. break;
  420. }
  421. return SPV_SUCCESS;
  422. }
  423. } // namespace
  424. spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) {
  425. switch (inst->opcode()) {
  426. case SpvOpDecorate:
  427. if (auto error = ValidateDecorate(_, inst)) return error;
  428. break;
  429. case SpvOpDecorateId:
  430. if (auto error = ValidateDecorateId(_, inst)) return error;
  431. break;
  432. // TODO(dneto): SpvOpDecorateStringGOOGLE
  433. // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253
  434. case SpvOpMemberDecorate:
  435. if (auto error = ValidateMemberDecorate(_, inst)) return error;
  436. break;
  437. case SpvOpDecorationGroup:
  438. if (auto error = ValidateDecorationGroup(_, inst)) return error;
  439. break;
  440. case SpvOpGroupDecorate:
  441. if (auto error = ValidateGroupDecorate(_, inst)) return error;
  442. break;
  443. case SpvOpGroupMemberDecorate:
  444. if (auto error = ValidateGroupMemberDecorate(_, inst)) return error;
  445. break;
  446. default:
  447. break;
  448. }
  449. // In order to validate decoration rules, we need to know all the decorations
  450. // that are applied to any given <id>.
  451. RegisterDecorations(_, inst);
  452. return SPV_SUCCESS;
  453. }
  454. } // namespace val
  455. } // namespace spvtools