fuzzer_util.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  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 <algorithm>
  15. #include <unordered_set>
  16. #include "source/fuzz/fuzzer_util.h"
  17. #include "source/opt/build_module.h"
  18. namespace spvtools {
  19. namespace fuzz {
  20. namespace fuzzerutil {
  21. bool IsFreshId(opt::IRContext* context, uint32_t id) {
  22. return !context->get_def_use_mgr()->GetDef(id);
  23. }
  24. void UpdateModuleIdBound(opt::IRContext* context, uint32_t id) {
  25. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the
  26. // case where the maximum id bound is reached.
  27. context->module()->SetIdBound(
  28. std::max(context->module()->id_bound(), id + 1));
  29. }
  30. opt::BasicBlock* MaybeFindBlock(opt::IRContext* context,
  31. uint32_t maybe_block_id) {
  32. auto inst = context->get_def_use_mgr()->GetDef(maybe_block_id);
  33. if (inst == nullptr) {
  34. // No instruction defining this id was found.
  35. return nullptr;
  36. }
  37. if (inst->opcode() != SpvOpLabel) {
  38. // The instruction defining the id is not a label, so it cannot be a block
  39. // id.
  40. return nullptr;
  41. }
  42. return context->cfg()->block(maybe_block_id);
  43. }
  44. bool PhiIdsOkForNewEdge(
  45. opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
  46. const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
  47. if (bb_from->IsSuccessor(bb_to)) {
  48. // There is already an edge from |from_block| to |to_block|, so there is
  49. // no need to extend OpPhi instructions. Do not allow phi ids to be
  50. // present. This might turn out to be too strict; perhaps it would be OK
  51. // just to ignore the ids in this case.
  52. return phi_ids.empty();
  53. }
  54. // The edge would add a previously non-existent edge from |from_block| to
  55. // |to_block|, so we go through the given phi ids and check that they exactly
  56. // match the OpPhi instructions in |to_block|.
  57. uint32_t phi_index = 0;
  58. // An explicit loop, rather than applying a lambda to each OpPhi in |bb_to|,
  59. // makes sense here because we need to increment |phi_index| for each OpPhi
  60. // instruction.
  61. for (auto& inst : *bb_to) {
  62. if (inst.opcode() != SpvOpPhi) {
  63. // The OpPhi instructions all occur at the start of the block; if we find
  64. // a non-OpPhi then we have seen them all.
  65. break;
  66. }
  67. if (phi_index == static_cast<uint32_t>(phi_ids.size())) {
  68. // Not enough phi ids have been provided to account for the OpPhi
  69. // instructions.
  70. return false;
  71. }
  72. // Look for an instruction defining the next phi id.
  73. opt::Instruction* phi_extension =
  74. context->get_def_use_mgr()->GetDef(phi_ids[phi_index]);
  75. if (!phi_extension) {
  76. // The id given to extend this OpPhi does not exist.
  77. return false;
  78. }
  79. if (phi_extension->type_id() != inst.type_id()) {
  80. // The instruction given to extend this OpPhi either does not have a type
  81. // or its type does not match that of the OpPhi.
  82. return false;
  83. }
  84. if (context->get_instr_block(phi_extension)) {
  85. // The instruction defining the phi id has an associated block (i.e., it
  86. // is not a global value). Check whether its definition dominates the
  87. // exit of |from_block|.
  88. auto dominator_analysis =
  89. context->GetDominatorAnalysis(bb_from->GetParent());
  90. if (!dominator_analysis->Dominates(phi_extension,
  91. bb_from->terminator())) {
  92. // The given id is no good as its definition does not dominate the exit
  93. // of |from_block|
  94. return false;
  95. }
  96. }
  97. phi_index++;
  98. }
  99. // We allow some of the ids provided for extending OpPhi instructions to be
  100. // unused. Their presence does no harm, and requiring a perfect match may
  101. // make transformations less likely to cleanly apply.
  102. return true;
  103. }
  104. uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value) {
  105. opt::analysis::Bool bool_type;
  106. auto registered_bool_type =
  107. context->get_type_mgr()->GetRegisteredType(&bool_type);
  108. if (!registered_bool_type) {
  109. return 0;
  110. }
  111. opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
  112. value);
  113. return context->get_constant_mgr()->FindDeclaredConstant(
  114. &bool_constant, context->get_type_mgr()->GetId(&bool_type));
  115. }
  116. void AddUnreachableEdgeAndUpdateOpPhis(
  117. opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
  118. bool condition_value,
  119. const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
  120. assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
  121. "Precondition on phi_ids is not satisfied");
  122. assert(bb_from->terminator()->opcode() == SpvOpBranch &&
  123. "Precondition on terminator of bb_from is not satisfied");
  124. // Get the id of the boolean constant to be used as the condition.
  125. uint32_t bool_id = MaybeGetBoolConstantId(context, condition_value);
  126. assert(
  127. bool_id &&
  128. "Precondition that condition value must be available is not satisfied");
  129. const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
  130. auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
  131. // Add the dead branch, by turning OpBranch into OpBranchConditional, and
  132. // ordering the targets depending on whether the given boolean corresponds to
  133. // true or false.
  134. bb_from->terminator()->SetOpcode(SpvOpBranchConditional);
  135. bb_from->terminator()->SetInOperands(
  136. {{SPV_OPERAND_TYPE_ID, {bool_id}},
  137. {SPV_OPERAND_TYPE_ID, {condition_value ? successor : bb_to->id()}},
  138. {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to->id() : successor}}});
  139. // Update OpPhi instructions in the target block if this branch adds a
  140. // previously non-existent edge from source to target.
  141. if (!from_to_edge_already_exists) {
  142. uint32_t phi_index = 0;
  143. for (auto& inst : *bb_to) {
  144. if (inst.opcode() != SpvOpPhi) {
  145. break;
  146. }
  147. assert(phi_index < static_cast<uint32_t>(phi_ids.size()) &&
  148. "There should be at least one phi id per OpPhi instruction.");
  149. inst.AddOperand({SPV_OPERAND_TYPE_ID, {phi_ids[phi_index]}});
  150. inst.AddOperand({SPV_OPERAND_TYPE_ID, {bb_from->id()}});
  151. phi_index++;
  152. }
  153. }
  154. }
  155. bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
  156. uint32_t maybe_loop_header_id) {
  157. // We deem a block to be part of a loop's continue construct if the loop's
  158. // continue target dominates the block.
  159. auto containing_construct_block = context->cfg()->block(maybe_loop_header_id);
  160. if (containing_construct_block->IsLoopHeader()) {
  161. auto continue_target = containing_construct_block->ContinueBlockId();
  162. if (context->GetDominatorAnalysis(containing_construct_block->GetParent())
  163. ->Dominates(continue_target, block_id)) {
  164. return true;
  165. }
  166. }
  167. return false;
  168. }
  169. opt::BasicBlock::iterator GetIteratorForInstruction(
  170. opt::BasicBlock* block, const opt::Instruction* inst) {
  171. for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) {
  172. if (inst == &*inst_it) {
  173. return inst_it;
  174. }
  175. }
  176. return block->end();
  177. }
  178. bool BlockIsReachableInItsFunction(opt::IRContext* context,
  179. opt::BasicBlock* bb) {
  180. auto enclosing_function = bb->GetParent();
  181. return context->GetDominatorAnalysis(enclosing_function)
  182. ->Dominates(enclosing_function->entry().get(), bb);
  183. }
  184. bool CanInsertOpcodeBeforeInstruction(
  185. SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) {
  186. if (instruction_in_block->PreviousNode() &&
  187. (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge ||
  188. instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
  189. // We cannot insert directly after a merge instruction.
  190. return false;
  191. }
  192. if (opcode != SpvOpVariable &&
  193. instruction_in_block->opcode() == SpvOpVariable) {
  194. // We cannot insert a non-OpVariable instruction directly before a
  195. // variable; variables in a function must be contiguous in the entry block.
  196. return false;
  197. }
  198. // We cannot insert a non-OpPhi instruction directly before an OpPhi, because
  199. // OpPhi instructions need to be contiguous at the start of a block.
  200. return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
  201. }
  202. bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
  203. if (inst->opcode() == SpvOpSampledImage) {
  204. // The SPIR-V data rules say that only very specific instructions may
  205. // may consume the result id of an OpSampledImage, and this excludes the
  206. // instructions that are used for making synonyms.
  207. return false;
  208. }
  209. if (!inst->HasResultId()) {
  210. // We can only make a synonym of an instruction that generates an id.
  211. return false;
  212. }
  213. if (!inst->type_id()) {
  214. // We can only make a synonym of an instruction that has a type.
  215. return false;
  216. }
  217. auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id());
  218. if (type_inst->opcode() == SpvOpTypePointer) {
  219. switch (inst->opcode()) {
  220. case SpvOpConstantNull:
  221. case SpvOpUndef:
  222. // We disallow making synonyms of null or undefined pointers. This is
  223. // to provide the property that if the original shader exhibited no bad
  224. // pointer accesses, the transformed shader will not either.
  225. return false;
  226. default:
  227. break;
  228. }
  229. }
  230. // We do not make synonyms of objects that have decorations: if the synonym is
  231. // not decorated analogously, using the original object vs. its synonymous
  232. // form may not be equivalent.
  233. return ir_context->get_decoration_mgr()
  234. ->GetDecorationsFor(inst->result_id(), true)
  235. .empty();
  236. }
  237. bool IsCompositeType(const opt::analysis::Type* type) {
  238. return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() ||
  239. type->AsVector());
  240. }
  241. std::vector<uint32_t> RepeatedFieldToVector(
  242. const google::protobuf::RepeatedField<uint32_t>& repeated_field) {
  243. std::vector<uint32_t> result;
  244. for (auto i : repeated_field) {
  245. result.push_back(i);
  246. }
  247. return result;
  248. }
  249. uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context,
  250. uint32_t base_object_type_id,
  251. uint32_t index) {
  252. auto should_be_composite_type =
  253. context->get_def_use_mgr()->GetDef(base_object_type_id);
  254. assert(should_be_composite_type && "The type should exist.");
  255. switch (should_be_composite_type->opcode()) {
  256. case SpvOpTypeArray: {
  257. auto array_length = GetArraySize(*should_be_composite_type, context);
  258. if (array_length == 0 || index >= array_length) {
  259. return 0;
  260. }
  261. return should_be_composite_type->GetSingleWordInOperand(0);
  262. }
  263. case SpvOpTypeMatrix:
  264. case SpvOpTypeVector: {
  265. auto count = should_be_composite_type->GetSingleWordInOperand(1);
  266. if (index >= count) {
  267. return 0;
  268. }
  269. return should_be_composite_type->GetSingleWordInOperand(0);
  270. }
  271. case SpvOpTypeStruct: {
  272. if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
  273. return 0;
  274. }
  275. return should_be_composite_type->GetSingleWordInOperand(index);
  276. }
  277. default:
  278. return 0;
  279. }
  280. }
  281. uint32_t WalkCompositeTypeIndices(
  282. opt::IRContext* context, uint32_t base_object_type_id,
  283. const google::protobuf::RepeatedField<google::protobuf::uint32>& indices) {
  284. uint32_t sub_object_type_id = base_object_type_id;
  285. for (auto index : indices) {
  286. sub_object_type_id =
  287. WalkOneCompositeTypeIndex(context, sub_object_type_id, index);
  288. if (!sub_object_type_id) {
  289. return 0;
  290. }
  291. }
  292. return sub_object_type_id;
  293. }
  294. uint32_t GetNumberOfStructMembers(
  295. const opt::Instruction& struct_type_instruction) {
  296. assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
  297. "An OpTypeStruct instruction is required here.");
  298. return struct_type_instruction.NumInOperands();
  299. }
  300. uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
  301. opt::IRContext* context) {
  302. auto array_length_constant =
  303. context->get_constant_mgr()
  304. ->GetConstantFromInst(context->get_def_use_mgr()->GetDef(
  305. array_type_instruction.GetSingleWordInOperand(1)))
  306. ->AsIntConstant();
  307. if (array_length_constant->words().size() != 1) {
  308. return 0;
  309. }
  310. return array_length_constant->GetU32();
  311. }
  312. bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
  313. std::vector<uint32_t> binary;
  314. context->module()->ToBinary(&binary, false);
  315. SpirvTools tools(context->grammar().target_env());
  316. return tools.Validate(binary.data(), binary.size(), validator_options);
  317. }
  318. std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
  319. std::vector<uint32_t> binary;
  320. context->module()->ToBinary(&binary, false);
  321. return BuildModule(context->grammar().target_env(), nullptr, binary.data(),
  322. binary.size());
  323. }
  324. bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id) {
  325. auto type = ir_context->get_type_mgr()->GetType(id);
  326. return type && !type->AsFunction();
  327. }
  328. bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id) {
  329. bool result = false;
  330. ir_context->get_def_use_mgr()->WhileEachUse(
  331. block_id,
  332. [&result](const opt::Instruction* use_instruction,
  333. uint32_t /*unused*/) -> bool {
  334. switch (use_instruction->opcode()) {
  335. case SpvOpLoopMerge:
  336. case SpvOpSelectionMerge:
  337. result = true;
  338. return false;
  339. default:
  340. return true;
  341. }
  342. });
  343. return result;
  344. }
  345. uint32_t FindFunctionType(opt::IRContext* ir_context,
  346. const std::vector<uint32_t>& type_ids) {
  347. // Look through the existing types for a match.
  348. for (auto& type_or_value : ir_context->types_values()) {
  349. if (type_or_value.opcode() != SpvOpTypeFunction) {
  350. // We are only interested in function types.
  351. continue;
  352. }
  353. if (type_or_value.NumInOperands() != type_ids.size()) {
  354. // Not a match: different numbers of arguments.
  355. continue;
  356. }
  357. // Check whether the return type and argument types match.
  358. bool input_operands_match = true;
  359. for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
  360. if (type_ids[i] != type_or_value.GetSingleWordInOperand(i)) {
  361. input_operands_match = false;
  362. break;
  363. }
  364. }
  365. if (input_operands_match) {
  366. // Everything matches.
  367. return type_or_value.result_id();
  368. }
  369. }
  370. // No match was found.
  371. return 0;
  372. }
  373. opt::Instruction* GetFunctionType(opt::IRContext* context,
  374. const opt::Function* function) {
  375. uint32_t type_id = function->DefInst().GetSingleWordInOperand(1);
  376. return context->get_def_use_mgr()->GetDef(type_id);
  377. }
  378. opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
  379. for (auto& function : *ir_context->module()) {
  380. if (function.result_id() == function_id) {
  381. return &function;
  382. }
  383. }
  384. return nullptr;
  385. }
  386. bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) {
  387. for (auto& entry_point : context->module()->entry_points()) {
  388. if (entry_point.GetSingleWordInOperand(1) == function_id) {
  389. return true;
  390. }
  391. }
  392. return false;
  393. }
  394. bool IdIsAvailableAtUse(opt::IRContext* context,
  395. opt::Instruction* use_instruction,
  396. uint32_t use_input_operand_index, uint32_t id) {
  397. auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
  398. auto enclosing_function =
  399. context->get_instr_block(use_instruction)->GetParent();
  400. // If the id a function parameter, it needs to be associated with the
  401. // function containing the use.
  402. if (defining_instruction->opcode() == SpvOpFunctionParameter) {
  403. return InstructionIsFunctionParameter(defining_instruction,
  404. enclosing_function);
  405. }
  406. if (!context->get_instr_block(id)) {
  407. // The id must be at global scope.
  408. return true;
  409. }
  410. if (defining_instruction == use_instruction) {
  411. // It is not OK for a definition to use itself.
  412. return false;
  413. }
  414. auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
  415. if (use_instruction->opcode() == SpvOpPhi) {
  416. // In the case where the use is an operand to OpPhi, it is actually the
  417. // *parent* block associated with the operand that must be dominated by
  418. // the synonym.
  419. auto parent_block =
  420. use_instruction->GetSingleWordInOperand(use_input_operand_index + 1);
  421. return dominator_analysis->Dominates(
  422. context->get_instr_block(defining_instruction)->id(), parent_block);
  423. }
  424. return dominator_analysis->Dominates(defining_instruction, use_instruction);
  425. }
  426. bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
  427. opt::Instruction* instruction,
  428. uint32_t id) {
  429. auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
  430. auto enclosing_function = context->get_instr_block(instruction)->GetParent();
  431. // If the id a function parameter, it needs to be associated with the
  432. // function containing the instruction.
  433. if (defining_instruction->opcode() == SpvOpFunctionParameter) {
  434. return InstructionIsFunctionParameter(defining_instruction,
  435. enclosing_function);
  436. }
  437. if (!context->get_instr_block(id)) {
  438. // The id is at global scope.
  439. return true;
  440. }
  441. if (defining_instruction == instruction) {
  442. // The instruction is not available right before its own definition.
  443. return false;
  444. }
  445. return context->GetDominatorAnalysis(enclosing_function)
  446. ->Dominates(defining_instruction, instruction);
  447. }
  448. bool InstructionIsFunctionParameter(opt::Instruction* instruction,
  449. opt::Function* function) {
  450. if (instruction->opcode() != SpvOpFunctionParameter) {
  451. return false;
  452. }
  453. bool found_parameter = false;
  454. function->ForEachParam(
  455. [instruction, &found_parameter](opt::Instruction* param) {
  456. if (param == instruction) {
  457. found_parameter = true;
  458. }
  459. });
  460. return found_parameter;
  461. }
  462. uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id) {
  463. return context->get_def_use_mgr()->GetDef(result_id)->type_id();
  464. }
  465. uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst) {
  466. assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
  467. "Precondition: |pointer_type_inst| must be OpTypePointer.");
  468. return pointer_type_inst->GetSingleWordInOperand(1);
  469. }
  470. uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context,
  471. uint32_t pointer_type_id) {
  472. return GetPointeeTypeIdFromPointerType(
  473. context->get_def_use_mgr()->GetDef(pointer_type_id));
  474. }
  475. SpvStorageClass GetStorageClassFromPointerType(
  476. opt::Instruction* pointer_type_inst) {
  477. assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
  478. "Precondition: |pointer_type_inst| must be OpTypePointer.");
  479. return static_cast<SpvStorageClass>(
  480. pointer_type_inst->GetSingleWordInOperand(0));
  481. }
  482. SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
  483. uint32_t pointer_type_id) {
  484. return GetStorageClassFromPointerType(
  485. context->get_def_use_mgr()->GetDef(pointer_type_id));
  486. }
  487. uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
  488. SpvStorageClass storage_class) {
  489. for (auto& inst : context->types_values()) {
  490. switch (inst.opcode()) {
  491. case SpvOpTypePointer:
  492. if (inst.GetSingleWordInOperand(0) == storage_class &&
  493. inst.GetSingleWordInOperand(1) == pointee_type_id) {
  494. return inst.result_id();
  495. }
  496. break;
  497. default:
  498. break;
  499. }
  500. }
  501. return 0;
  502. }
  503. bool IsNullConstantSupported(const opt::analysis::Type& type) {
  504. return type.AsBool() || type.AsInteger() || type.AsFloat() ||
  505. type.AsMatrix() || type.AsVector() || type.AsArray() ||
  506. type.AsStruct() || type.AsPointer() || type.AsEvent() ||
  507. type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
  508. }
  509. bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
  510. const opt::IRContext* ir_context) {
  511. // TODO(afd): We capture the universal environments for which this requirement
  512. // holds. The check should be refined on demand for other target
  513. // environments.
  514. switch (ir_context->grammar().target_env()) {
  515. case SPV_ENV_UNIVERSAL_1_0:
  516. case SPV_ENV_UNIVERSAL_1_1:
  517. case SPV_ENV_UNIVERSAL_1_2:
  518. case SPV_ENV_UNIVERSAL_1_3:
  519. return false;
  520. default:
  521. return true;
  522. }
  523. }
  524. void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) {
  525. if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(context)) {
  526. // Conservatively add this global to the interface of every entry point in
  527. // the module. This means that the global is available for other
  528. // transformations to use.
  529. //
  530. // A downside of this is that the global will be in the interface even if it
  531. // ends up never being used.
  532. //
  533. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
  534. // this if a more thorough approach to entry point interfaces is taken.
  535. for (auto& entry_point : context->module()->entry_points()) {
  536. entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {id}});
  537. }
  538. }
  539. }
  540. void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
  541. uint32_t type_id, SpvStorageClass storage_class,
  542. uint32_t initializer_id) {
  543. // Check various preconditions.
  544. assert((storage_class == SpvStorageClassPrivate ||
  545. storage_class == SpvStorageClassWorkgroup) &&
  546. "Variable's storage class must be either Private or Workgroup");
  547. auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
  548. (void)type_inst; // Variable becomes unused in release mode.
  549. assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
  550. GetStorageClassFromPointerType(type_inst) == storage_class &&
  551. "Variable's type is invalid");
  552. if (storage_class == SpvStorageClassWorkgroup) {
  553. assert(initializer_id == 0);
  554. }
  555. if (initializer_id != 0) {
  556. const auto* constant_inst =
  557. context->get_def_use_mgr()->GetDef(initializer_id);
  558. (void)constant_inst; // Variable becomes unused in release mode.
  559. assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
  560. GetPointeeTypeIdFromPointerType(type_inst) ==
  561. constant_inst->type_id() &&
  562. "Initializer is invalid");
  563. }
  564. opt::Instruction::OperandList operands = {
  565. {SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast<uint32_t>(storage_class)}}};
  566. if (initializer_id) {
  567. operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}});
  568. }
  569. context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
  570. context, SpvOpVariable, type_id, result_id, std::move(operands)));
  571. AddVariableIdToEntryPointInterfaces(context, result_id);
  572. }
  573. void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
  574. uint32_t type_id, uint32_t function_id,
  575. uint32_t initializer_id) {
  576. // Check various preconditions.
  577. auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
  578. (void)type_inst; // Variable becomes unused in release mode.
  579. assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
  580. GetStorageClassFromPointerType(type_inst) == SpvStorageClassFunction &&
  581. "Variable's type is invalid");
  582. const auto* constant_inst =
  583. context->get_def_use_mgr()->GetDef(initializer_id);
  584. (void)constant_inst; // Variable becomes unused in release mode.
  585. assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
  586. GetPointeeTypeIdFromPointerType(type_inst) ==
  587. constant_inst->type_id() &&
  588. "Initializer is invalid");
  589. auto* function = FindFunction(context, function_id);
  590. assert(function && "Function id is invalid");
  591. function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
  592. context, SpvOpVariable, type_id, result_id,
  593. opt::Instruction::OperandList{
  594. {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
  595. {SPV_OPERAND_TYPE_ID, {initializer_id}}}));
  596. }
  597. bool HasDuplicates(const std::vector<uint32_t>& arr) {
  598. return std::unordered_set<uint32_t>(arr.begin(), arr.end()).size() !=
  599. arr.size();
  600. }
  601. bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
  602. uint32_t hi) {
  603. if (arr.empty()) {
  604. return lo > hi;
  605. }
  606. if (HasDuplicates(arr)) {
  607. return false;
  608. }
  609. auto min_max = std::minmax_element(arr.begin(), arr.end());
  610. return arr.size() == hi - lo + 1 && *min_max.first == lo &&
  611. *min_max.second == hi;
  612. }
  613. std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
  614. uint32_t function_id) {
  615. auto* function = FindFunction(ir_context, function_id);
  616. assert(function && "|function_id| is invalid");
  617. std::vector<opt::Instruction*> result;
  618. function->ForEachParam(
  619. [&result](opt::Instruction* inst) { result.push_back(inst); });
  620. return result;
  621. }
  622. } // namespace fuzzerutil
  623. } // namespace fuzz
  624. } // namespace spvtools