validate_id.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // Copyright (c) 2015-2016 The Khronos Group 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. #include "source/val/validate.h"
  15. #include <cassert>
  16. #include <algorithm>
  17. #include <iostream>
  18. #include <iterator>
  19. #include <stack>
  20. #include <string>
  21. #include <unordered_set>
  22. #include <utility>
  23. #include <vector>
  24. #include "source/diagnostic.h"
  25. #include "source/instruction.h"
  26. #include "source/opcode.h"
  27. #include "source/operand.h"
  28. #include "source/spirv_validator_options.h"
  29. #include "source/val/function.h"
  30. #include "source/val/validation_state.h"
  31. #include "spirv-tools/libspirv.h"
  32. namespace spvtools {
  33. namespace val {
  34. spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst) {
  35. for (auto& operand : inst->operands()) {
  36. const spv_operand_type_t& type = operand.type;
  37. const uint32_t operand_id = inst->word(operand.offset);
  38. if (spvIsIdType(type) && type != SPV_OPERAND_TYPE_RESULT_ID) {
  39. if (auto def = _.FindDef(operand_id))
  40. def->RegisterUse(inst, operand.offset);
  41. }
  42. }
  43. return SPV_SUCCESS;
  44. }
  45. /// This function checks all ID definitions dominate their use in the CFG.
  46. ///
  47. /// This function will iterate over all ID definitions that are defined in the
  48. /// functions of a module and make sure that the definitions appear in a
  49. /// block that dominates their use.
  50. ///
  51. /// NOTE: This function does NOT check module scoped functions which are
  52. /// checked during the initial binary parse in the IdPass below
  53. spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _) {
  54. std::vector<const Instruction*> phi_instructions;
  55. std::unordered_set<uint32_t> phi_ids;
  56. for (const auto& inst : _.ordered_instructions()) {
  57. if (inst.id() == 0) continue;
  58. if (const Function* func = inst.function()) {
  59. if (const BasicBlock* block = inst.block()) {
  60. // If the Id is defined within a block then make sure all references to
  61. // that Id appear in a blocks that are dominated by the defining block
  62. for (auto& use_index_pair : inst.uses()) {
  63. const Instruction* use = use_index_pair.first;
  64. if (const BasicBlock* use_block = use->block()) {
  65. if (use_block->reachable() == false) continue;
  66. if (use->opcode() == SpvOpPhi) {
  67. if (phi_ids.insert(use->id()).second) {
  68. phi_instructions.push_back(use);
  69. }
  70. } else if (!block->dominates(*use->block())) {
  71. return _.diag(SPV_ERROR_INVALID_ID, use_block->label())
  72. << "ID " << _.getIdName(inst.id()) << " defined in block "
  73. << _.getIdName(block->id())
  74. << " does not dominate its use in block "
  75. << _.getIdName(use_block->id());
  76. }
  77. }
  78. }
  79. } else {
  80. // If the Ids defined within a function but not in a block(i.e. function
  81. // parameters, block ids), then make sure all references to that Id
  82. // appear within the same function
  83. for (auto use : inst.uses()) {
  84. const Instruction* user = use.first;
  85. if (user->function() && user->function() != func) {
  86. return _.diag(SPV_ERROR_INVALID_ID, _.FindDef(func->id()))
  87. << "ID " << _.getIdName(inst.id()) << " used in function "
  88. << _.getIdName(user->function()->id())
  89. << " is used outside of it's defining function "
  90. << _.getIdName(func->id());
  91. }
  92. }
  93. }
  94. }
  95. // NOTE: Ids defined outside of functions must appear before they are used
  96. // This check is being performed in the IdPass function
  97. }
  98. // Check all OpPhi parent blocks are dominated by the variable's defining
  99. // blocks
  100. for (const Instruction* phi : phi_instructions) {
  101. if (phi->block()->reachable() == false) continue;
  102. for (size_t i = 3; i < phi->operands().size(); i += 2) {
  103. const Instruction* variable = _.FindDef(phi->word(i));
  104. const BasicBlock* parent =
  105. phi->function()->GetBlock(phi->word(i + 1)).first;
  106. if (variable->block() && parent->reachable() &&
  107. !variable->block()->dominates(*parent)) {
  108. return _.diag(SPV_ERROR_INVALID_ID, phi)
  109. << "In OpPhi instruction " << _.getIdName(phi->id()) << ", ID "
  110. << _.getIdName(variable->id())
  111. << " definition does not dominate its parent "
  112. << _.getIdName(parent->id());
  113. }
  114. }
  115. }
  116. return SPV_SUCCESS;
  117. }
  118. // Performs SSA validation on the IDs of an instruction. The
  119. // can_have_forward_declared_ids functor should return true if the
  120. // instruction operand's ID can be forward referenced.
  121. spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
  122. auto can_have_forward_declared_ids =
  123. inst->opcode() == SpvOpExtInst &&
  124. spvExtInstIsDebugInfo(inst->ext_inst_type())
  125. ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
  126. inst->ext_inst_type(), inst->word(4))
  127. : spvOperandCanBeForwardDeclaredFunction(inst->opcode());
  128. // Keep track of a result id defined by this instruction. 0 means it
  129. // does not define an id.
  130. uint32_t result_id = 0;
  131. for (unsigned i = 0; i < inst->operands().size(); i++) {
  132. const spv_parsed_operand_t& operand = inst->operand(i);
  133. const spv_operand_type_t& type = operand.type;
  134. // We only care about Id operands, which are a single word.
  135. const uint32_t operand_word = inst->word(operand.offset);
  136. auto ret = SPV_ERROR_INTERNAL;
  137. switch (type) {
  138. case SPV_OPERAND_TYPE_RESULT_ID:
  139. // NOTE: Multiple Id definitions are being checked by the binary parser.
  140. //
  141. // Defer undefined-forward-reference removal until after we've analyzed
  142. // the remaining operands to this instruction. Deferral only matters
  143. // for OpPhi since it's the only case where it defines its own forward
  144. // reference. Other instructions that can have forward references
  145. // either don't define a value or the forward reference is to a function
  146. // Id (and hence defined outside of a function body).
  147. result_id = operand_word;
  148. // NOTE: The result Id is added (in RegisterInstruction) *after* all of
  149. // the other Ids have been checked to avoid premature use in the same
  150. // instruction.
  151. ret = SPV_SUCCESS;
  152. break;
  153. case SPV_OPERAND_TYPE_ID:
  154. case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
  155. case SPV_OPERAND_TYPE_SCOPE_ID:
  156. if (const auto def = _.FindDef(operand_word)) {
  157. const auto opcode = inst->opcode();
  158. if (spvOpcodeGeneratesType(def->opcode()) &&
  159. !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
  160. !inst->IsDebugInfo() && !inst->IsNonSemantic() &&
  161. !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction &&
  162. opcode != SpvOpCooperativeMatrixLengthNV &&
  163. !(opcode == SpvOpSpecConstantOp &&
  164. inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
  165. return _.diag(SPV_ERROR_INVALID_ID, inst)
  166. << "Operand " << _.getIdName(operand_word)
  167. << " cannot be a type";
  168. } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
  169. !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() &&
  170. !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
  171. !spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi &&
  172. opcode != SpvOpExtInst && opcode != SpvOpExtInstImport &&
  173. opcode != SpvOpSelectionMerge &&
  174. opcode != SpvOpLoopMerge && opcode != SpvOpFunction &&
  175. opcode != SpvOpCooperativeMatrixLengthNV &&
  176. !(opcode == SpvOpSpecConstantOp &&
  177. inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
  178. return _.diag(SPV_ERROR_INVALID_ID, inst)
  179. << "Operand " << _.getIdName(operand_word)
  180. << " requires a type";
  181. } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) {
  182. return _.diag(SPV_ERROR_INVALID_ID, inst)
  183. << "Operand " << _.getIdName(operand_word)
  184. << " in semantic instruction cannot be a non-semantic "
  185. "instruction";
  186. } else {
  187. ret = SPV_SUCCESS;
  188. }
  189. } else if (can_have_forward_declared_ids(i)) {
  190. if (spvOpcodeGeneratesType(inst->opcode()) &&
  191. !_.IsForwardPointer(operand_word)) {
  192. ret = _.diag(SPV_ERROR_INVALID_ID, inst)
  193. << "Operand " << _.getIdName(operand_word)
  194. << " requires a previous definition";
  195. } else {
  196. ret = _.ForwardDeclareId(operand_word);
  197. }
  198. } else {
  199. ret = _.diag(SPV_ERROR_INVALID_ID, inst)
  200. << "ID " << _.getIdName(operand_word)
  201. << " has not been defined";
  202. }
  203. break;
  204. case SPV_OPERAND_TYPE_TYPE_ID:
  205. if (_.IsDefinedId(operand_word)) {
  206. auto* def = _.FindDef(operand_word);
  207. if (!spvOpcodeGeneratesType(def->opcode())) {
  208. ret = _.diag(SPV_ERROR_INVALID_ID, inst)
  209. << "ID " << _.getIdName(operand_word) << " is not a type id";
  210. } else {
  211. ret = SPV_SUCCESS;
  212. }
  213. } else {
  214. ret = _.diag(SPV_ERROR_INVALID_ID, inst)
  215. << "ID " << _.getIdName(operand_word)
  216. << " has not been defined";
  217. }
  218. break;
  219. default:
  220. ret = SPV_SUCCESS;
  221. break;
  222. }
  223. if (SPV_SUCCESS != ret) return ret;
  224. }
  225. if (result_id) _.RemoveIfForwardDeclared(result_id);
  226. return SPV_SUCCESS;
  227. }
  228. } // namespace val
  229. } // namespace spvtools