fuzzer_util.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright (c) 2019 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/fuzz/fuzzer_util.h"
  15. #include "source/opt/build_module.h"
  16. namespace spvtools {
  17. namespace fuzz {
  18. namespace fuzzerutil {
  19. bool IsFreshId(opt::IRContext* context, uint32_t id) {
  20. return !context->get_def_use_mgr()->GetDef(id);
  21. }
  22. void UpdateModuleIdBound(opt::IRContext* context, uint32_t id) {
  23. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the
  24. // case where the maximum id bound is reached.
  25. context->module()->SetIdBound(
  26. std::max(context->module()->id_bound(), id + 1));
  27. }
  28. opt::BasicBlock* MaybeFindBlock(opt::IRContext* context,
  29. uint32_t maybe_block_id) {
  30. auto inst = context->get_def_use_mgr()->GetDef(maybe_block_id);
  31. if (inst == nullptr) {
  32. // No instruction defining this id was found.
  33. return nullptr;
  34. }
  35. if (inst->opcode() != SpvOpLabel) {
  36. // The instruction defining the id is not a label, so it cannot be a block
  37. // id.
  38. return nullptr;
  39. }
  40. return context->cfg()->block(maybe_block_id);
  41. }
  42. bool PhiIdsOkForNewEdge(
  43. opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
  44. const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
  45. if (bb_from->IsSuccessor(bb_to)) {
  46. // There is already an edge from |from_block| to |to_block|, so there is
  47. // no need to extend OpPhi instructions. Do not allow phi ids to be
  48. // present. This might turn out to be too strict; perhaps it would be OK
  49. // just to ignore the ids in this case.
  50. return phi_ids.empty();
  51. }
  52. // The edge would add a previously non-existent edge from |from_block| to
  53. // |to_block|, so we go through the given phi ids and check that they exactly
  54. // match the OpPhi instructions in |to_block|.
  55. uint32_t phi_index = 0;
  56. // An explicit loop, rather than applying a lambda to each OpPhi in |bb_to|,
  57. // makes sense here because we need to increment |phi_index| for each OpPhi
  58. // instruction.
  59. for (auto& inst : *bb_to) {
  60. if (inst.opcode() != SpvOpPhi) {
  61. // The OpPhi instructions all occur at the start of the block; if we find
  62. // a non-OpPhi then we have seen them all.
  63. break;
  64. }
  65. if (phi_index == static_cast<uint32_t>(phi_ids.size())) {
  66. // Not enough phi ids have been provided to account for the OpPhi
  67. // instructions.
  68. return false;
  69. }
  70. // Look for an instruction defining the next phi id.
  71. opt::Instruction* phi_extension =
  72. context->get_def_use_mgr()->GetDef(phi_ids[phi_index]);
  73. if (!phi_extension) {
  74. // The id given to extend this OpPhi does not exist.
  75. return false;
  76. }
  77. if (phi_extension->type_id() != inst.type_id()) {
  78. // The instruction given to extend this OpPhi either does not have a type
  79. // or its type does not match that of the OpPhi.
  80. return false;
  81. }
  82. if (context->get_instr_block(phi_extension)) {
  83. // The instruction defining the phi id has an associated block (i.e., it
  84. // is not a global value). Check whether its definition dominates the
  85. // exit of |from_block|.
  86. auto dominator_analysis =
  87. context->GetDominatorAnalysis(bb_from->GetParent());
  88. if (!dominator_analysis->Dominates(phi_extension,
  89. bb_from->terminator())) {
  90. // The given id is no good as its definition does not dominate the exit
  91. // of |from_block|
  92. return false;
  93. }
  94. }
  95. phi_index++;
  96. }
  97. // Return false if not all of the ids for extending OpPhi instructions are
  98. // needed. This might turn out to be stricter than necessary; perhaps it would
  99. // be OK just to not use the ids in this case.
  100. return phi_index == static_cast<uint32_t>(phi_ids.size());
  101. }
  102. void AddUnreachableEdgeAndUpdateOpPhis(
  103. opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
  104. bool condition_value,
  105. const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
  106. assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
  107. "Precondition on phi_ids is not satisfied");
  108. assert(bb_from->terminator()->opcode() == SpvOpBranch &&
  109. "Precondition on terminator of bb_from is not satisfied");
  110. // Get the id of the boolean constant to be used as the condition.
  111. opt::analysis::Bool bool_type;
  112. opt::analysis::BoolConstant bool_constant(
  113. context->get_type_mgr()->GetRegisteredType(&bool_type)->AsBool(),
  114. condition_value);
  115. uint32_t bool_id = context->get_constant_mgr()->FindDeclaredConstant(
  116. &bool_constant, context->get_type_mgr()->GetId(&bool_type));
  117. const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
  118. auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
  119. // Add the dead branch, by turning OpBranch into OpBranchConditional, and
  120. // ordering the targets depending on whether the given boolean corresponds to
  121. // true or false.
  122. bb_from->terminator()->SetOpcode(SpvOpBranchConditional);
  123. bb_from->terminator()->SetInOperands(
  124. {{SPV_OPERAND_TYPE_ID, {bool_id}},
  125. {SPV_OPERAND_TYPE_ID, {condition_value ? successor : bb_to->id()}},
  126. {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to->id() : successor}}});
  127. // Update OpPhi instructions in the target block if this branch adds a
  128. // previously non-existent edge from source to target.
  129. if (!from_to_edge_already_exists) {
  130. uint32_t phi_index = 0;
  131. for (auto& inst : *bb_to) {
  132. if (inst.opcode() != SpvOpPhi) {
  133. break;
  134. }
  135. assert(phi_index < static_cast<uint32_t>(phi_ids.size()) &&
  136. "There should be exactly one phi id per OpPhi instruction.");
  137. inst.AddOperand({SPV_OPERAND_TYPE_ID, {phi_ids[phi_index]}});
  138. inst.AddOperand({SPV_OPERAND_TYPE_ID, {bb_from->id()}});
  139. phi_index++;
  140. }
  141. assert(phi_index == static_cast<uint32_t>(phi_ids.size()) &&
  142. "There should be exactly one phi id per OpPhi instruction.");
  143. }
  144. }
  145. bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
  146. uint32_t maybe_loop_header_id) {
  147. // We deem a block to be part of a loop's continue construct if the loop's
  148. // continue target dominates the block.
  149. auto containing_construct_block = context->cfg()->block(maybe_loop_header_id);
  150. if (containing_construct_block->IsLoopHeader()) {
  151. auto continue_target = containing_construct_block->ContinueBlockId();
  152. if (context->GetDominatorAnalysis(containing_construct_block->GetParent())
  153. ->Dominates(continue_target, block_id)) {
  154. return true;
  155. }
  156. }
  157. return false;
  158. }
  159. opt::BasicBlock::iterator GetIteratorForInstruction(
  160. opt::BasicBlock* block, const opt::Instruction* inst) {
  161. for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) {
  162. if (inst == &*inst_it) {
  163. return inst_it;
  164. }
  165. }
  166. return block->end();
  167. }
  168. bool BlockIsReachableInItsFunction(opt::IRContext* context,
  169. opt::BasicBlock* bb) {
  170. auto enclosing_function = bb->GetParent();
  171. return context->GetDominatorAnalysis(enclosing_function)
  172. ->Dominates(enclosing_function->entry().get(), bb);
  173. }
  174. bool CanInsertOpcodeBeforeInstruction(
  175. SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) {
  176. if (instruction_in_block->PreviousNode() &&
  177. (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge ||
  178. instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
  179. // We cannot insert directly after a merge instruction.
  180. return false;
  181. }
  182. if (opcode != SpvOpVariable &&
  183. instruction_in_block->opcode() == SpvOpVariable) {
  184. // We cannot insert a non-OpVariable instruction directly before a
  185. // variable; variables in a function must be contiguous in the entry block.
  186. return false;
  187. }
  188. // We cannot insert a non-OpPhi instruction directly before an OpPhi, because
  189. // OpPhi instructions need to be contiguous at the start of a block.
  190. return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
  191. }
  192. bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
  193. if (!inst->HasResultId()) {
  194. // We can only make a synonym of an instruction that generates an id.
  195. return false;
  196. }
  197. if (!inst->type_id()) {
  198. // We can only make a synonym of an instruction that has a type.
  199. return false;
  200. }
  201. // We do not make synonyms of objects that have decorations: if the synonym is
  202. // not decorated analogously, using the original object vs. its synonymous
  203. // form may not be equivalent.
  204. return ir_context->get_decoration_mgr()
  205. ->GetDecorationsFor(inst->result_id(), true)
  206. .empty();
  207. }
  208. bool IsCompositeType(const opt::analysis::Type* type) {
  209. return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() ||
  210. type->AsVector());
  211. }
  212. std::vector<uint32_t> RepeatedFieldToVector(
  213. const google::protobuf::RepeatedField<uint32_t>& repeated_field) {
  214. std::vector<uint32_t> result;
  215. for (auto i : repeated_field) {
  216. result.push_back(i);
  217. }
  218. return result;
  219. }
  220. uint32_t WalkCompositeTypeIndices(
  221. opt::IRContext* context, uint32_t base_object_type_id,
  222. const google::protobuf::RepeatedField<google::protobuf::uint32>& indices) {
  223. uint32_t sub_object_type_id = base_object_type_id;
  224. for (auto index : indices) {
  225. auto should_be_composite_type =
  226. context->get_def_use_mgr()->GetDef(sub_object_type_id);
  227. assert(should_be_composite_type && "The type should exist.");
  228. if (SpvOpTypeArray == should_be_composite_type->opcode()) {
  229. auto array_length = GetArraySize(*should_be_composite_type, context);
  230. if (array_length == 0 || index >= array_length) {
  231. return 0;
  232. }
  233. sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
  234. } else if (SpvOpTypeMatrix == should_be_composite_type->opcode()) {
  235. auto matrix_column_count =
  236. should_be_composite_type->GetSingleWordInOperand(1);
  237. if (index >= matrix_column_count) {
  238. return 0;
  239. }
  240. sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
  241. } else if (SpvOpTypeStruct == should_be_composite_type->opcode()) {
  242. if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
  243. return 0;
  244. }
  245. sub_object_type_id =
  246. should_be_composite_type->GetSingleWordInOperand(index);
  247. } else if (SpvOpTypeVector == should_be_composite_type->opcode()) {
  248. auto vector_length = should_be_composite_type->GetSingleWordInOperand(1);
  249. if (index >= vector_length) {
  250. return 0;
  251. }
  252. sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
  253. } else {
  254. return 0;
  255. }
  256. }
  257. return sub_object_type_id;
  258. }
  259. uint32_t GetNumberOfStructMembers(
  260. const opt::Instruction& struct_type_instruction) {
  261. assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
  262. "An OpTypeStruct instruction is required here.");
  263. return struct_type_instruction.NumInOperands();
  264. }
  265. uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
  266. opt::IRContext* context) {
  267. auto array_length_constant =
  268. context->get_constant_mgr()
  269. ->GetConstantFromInst(context->get_def_use_mgr()->GetDef(
  270. array_type_instruction.GetSingleWordInOperand(1)))
  271. ->AsIntConstant();
  272. if (array_length_constant->words().size() != 1) {
  273. return 0;
  274. }
  275. return array_length_constant->GetU32();
  276. }
  277. bool IsValid(opt::IRContext* context) {
  278. std::vector<uint32_t> binary;
  279. context->module()->ToBinary(&binary, false);
  280. return SpirvTools(context->grammar().target_env()).Validate(binary);
  281. }
  282. std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
  283. std::vector<uint32_t> binary;
  284. context->module()->ToBinary(&binary, false);
  285. return BuildModule(context->grammar().target_env(), nullptr, binary.data(),
  286. binary.size());
  287. }
  288. } // namespace fuzzerutil
  289. } // namespace fuzz
  290. } // namespace spvtools